class AdLint::Cc1::StatementInterpreter

Public Class Methods

new(owner) click to toggle source
Calls superclass method AdLint::Cc1::SubInterpreter::new
# File lib/adlint/cc1/interp.rb, line 971
def initialize(owner)
  super(owner, Statement)

  # NOTE: All effective controlling expressions in the executing
  #       iteration-statements.
  @effective_ctrlexpr_stack = []
end

Public Instance Methods

visit_break_statement(node) click to toggle source
# File lib/adlint/cc1/interp.rb, line 1238
def visit_break_statement(node)
  checkpoint(node.location)

  node.executed = true
  BreakEvent.of_break.throw
end
visit_c99_for_statement(node) click to toggle source
# File lib/adlint/cc1/interp.rb, line 1197
def visit_c99_for_statement(node)
  checkpoint(node.location)

  scoped_eval do
    interpret(node.declaration)

    node.executed = true
    notify_c99_for_stmt_started(node)

    if ctrlexpr_val = interpret_for_ctrlexpr(node)
      notify_c99_for_ctrlexpr_evaled(node, ctrlexpr_val)
    else
      ctrlexpr_val = scalar_value_of_true
    end

    case
    when ctrlexpr_val.test_must_be_true.true?
      interpret_for_body_statement(node, true)
    when ctrlexpr_val.test_may_be_true.true?
      interpret_for_body_statement(node, false)
    end
  end
ensure
  notify_c99_for_stmt_ended(node)
end
visit_case_labeled_statement(node) click to toggle source
# File lib/adlint/cc1/interp.rb, line 989
def visit_case_labeled_statement(node)
  checkpoint(node.location)

  node.executed = true
  ctrlexpr = node.expression
  ctrlexpr_var = object_to_variable(interpret(ctrlexpr, QUIET), ctrlexpr)
  notify_case_ctrlexpr_evaled(node, ctrlexpr_var)

  interpret(node.statement)
end
visit_compound_statement(node) click to toggle source
# File lib/adlint/cc1/interp.rb, line 1007
def visit_compound_statement(node)
  checkpoint(node.location)

  node.executed = true
  scoped_eval do
    begin
      notify_block_started(node)
      node.block_items.each { |item| interpret(item) }
    ensure
      notify_block_ended(node)
    end
  end
end
visit_continue_statement(node) click to toggle source
# File lib/adlint/cc1/interp.rb, line 1231
def visit_continue_statement(node)
  checkpoint(node.location)

  node.executed = true
  BreakEvent.of_continue.throw
end
visit_default_labeled_statement(node) click to toggle source
# File lib/adlint/cc1/interp.rb, line 1000
def visit_default_labeled_statement(node)
  checkpoint(node.location)

  node.executed = true
  interpret(node.statement)
end
visit_do_statement(node) click to toggle source
# File lib/adlint/cc1/interp.rb, line 1145
def visit_do_statement(node)
  checkpoint(node.location)

  node.executed = true
  notify_do_stmt_started(node)

  widen_varying_variable_value_domain(node)

  orig_ctrlexpr, * = node.deduct_controlling_expression

  begin
    enter_iteration_statement(orig_ctrlexpr)
    branch_opts = [ITERATION, NARROWING, FINAL, IMPLICIT_COND, COMPLETE]
    branched_eval(nil, *branch_opts) { interpret(node.statement) }
  ensure
    leave_iteration_statement(orig_ctrlexpr)
  end

  ctrlexpr_obj = interpret(node.expression)
  ctrlexpr_var = object_to_variable(ctrlexpr_obj, node.expression)
  ctrlexpr_val = value_of(ctrlexpr_var)
  notify_variable_value_referred(node.expression, ctrlexpr_var)
  notify_sequence_point_reached(SequencePoint.new(node.expression))
  notify_do_ctrlexpr_evaled(node, ctrlexpr_val)
ensure
  notify_do_stmt_ended(node)
end
visit_expression_statement(node) click to toggle source
# File lib/adlint/cc1/interp.rb, line 1021
def visit_expression_statement(node)
  checkpoint(node.location)

  node.executed = true
  notify_expression_stmt_started(node)

  interpret(node.expression) if node.expression
ensure
  notify_expression_stmt_ended(node)
end
visit_for_statement(node) click to toggle source
# File lib/adlint/cc1/interp.rb, line 1173
def visit_for_statement(node)
  checkpoint(node.location)

  node.executed = true
  notify_for_stmt_started(node)

  node.initial_statement.accept(self)

  if ctrlexpr_val = interpret_for_ctrlexpr(node)
    notify_for_ctrlexpr_evaled(node, ctrlexpr_val)
  else
    ctrlexpr_val = scalar_value_of_true
  end

  case
  when ctrlexpr_val.test_must_be_true.true?
    interpret_for_body_statement(node, true)
  when ctrlexpr_val.test_may_be_true.true?
    interpret_for_body_statement(node, false)
  end
ensure
  notify_for_stmt_ended(node)
end
visit_generic_labeled_statement(node) click to toggle source
# File lib/adlint/cc1/interp.rb, line 979
def visit_generic_labeled_statement(node)
  checkpoint(node.location)

  node.executed = true
  notify_label_defined(node)

  uninitialize_block_local_variables(node)
  interpret(node.statement)
end
visit_goto_statement(node) click to toggle source
# File lib/adlint/cc1/interp.rb, line 1223
def visit_goto_statement(node)
  checkpoint(node.location)

  # TODO: Must implement goto semantics.
  node.executed = true
  notify_goto_stmt_evaled(node, node.identifier.value)
end
visit_if_else_statement(node) click to toggle source
# File lib/adlint/cc1/interp.rb, line 1066
def visit_if_else_statement(node)
  checkpoint(node.location)

  node.executed = true

  orig_ctrlexpr = node.expression
  if orig_ctrlexpr == effective_ctrlexpr
    ctrlexpr_val = scalar_value_of_arbitrary
    ctrlexpr = nil
  else
    ctrlexpr_obj = interpret(orig_ctrlexpr)
    ctrlexpr_var = object_to_variable(ctrlexpr_obj, orig_ctrlexpr)
    ctrlexpr_val = value_of(ctrlexpr_var)
    notify_variable_value_referred(orig_ctrlexpr, ctrlexpr_var)
    notify_sequence_point_reached(SequencePoint.new(orig_ctrlexpr))
    ctrlexpr = orig_ctrlexpr.to_normalized_logical
  end
  notify_if_else_ctrlexpr_evaled(node, ctrlexpr_val)

  case
  when ctrlexpr_val.test_must_be_true.true?
    branched_eval(ctrlexpr, NARROWING, FINAL, IMPLICIT_COND, COMPLETE) do
      interpret(node.then_statement)
    end
    return
  when ctrlexpr_val.test_may_be_true.true?
    branched_eval(ctrlexpr, NARROWING, IMPLICIT_COND) do
      interpret(node.then_statement)
    end
  end

  case node.else_statement
  when IfStatement, IfElseStatement
    interpret(node.else_statement)
  else
    branched_eval(nil, NARROWING, COMPLEMENTAL, FINAL, COMPLETE) do
      interpret(node.else_statement)
    end
  end
end
visit_if_statement(node) click to toggle source
# File lib/adlint/cc1/interp.rb, line 1032
def visit_if_statement(node)
  checkpoint(node.location)

  node.executed = true

  orig_ctrlexpr = node.expression
  if orig_ctrlexpr == effective_ctrlexpr
    ctrlexpr_val = scalar_value_of_arbitrary
    ctrlexpr = nil
  else
    ctrlexpr_obj = interpret(orig_ctrlexpr)
    ctrlexpr_var = object_to_variable(ctrlexpr_obj, orig_ctrlexpr)
    ctrlexpr_val = value_of(ctrlexpr_var)
    notify_variable_value_referred(orig_ctrlexpr, ctrlexpr_var)
    notify_sequence_point_reached(SequencePoint.new(orig_ctrlexpr))
    ctrlexpr = orig_ctrlexpr.to_normalized_logical
  end
  notify_if_ctrlexpr_evaled(node, ctrlexpr_val)

  case
  when ctrlexpr_val.test_must_be_true.true?
    branched_eval(ctrlexpr, NARROWING, FINAL, IMPLICIT_COND, COMPLETE) do
      interpret(node.statement)
    end
  when ctrlexpr_val.test_may_be_true.true?
    branched_eval(ctrlexpr, NARROWING, FINAL, IMPLICIT_COND) do
      interpret(node.statement)
    end
  else
    # NOTE: To end the current branch group of else-if sequence.
    branched_eval(nil, NARROWING, FINAL) {}
  end
end
visit_return_statement(node) click to toggle source
# File lib/adlint/cc1/interp.rb, line 1245
def visit_return_statement(node)
  checkpoint(node.location)

  node.executed = true

  unless node.expression
    notify_return_stmt_evaled(node, nil)
    BreakEvent.of_return.throw
  end

  var = object_to_variable(interpret(node.expression), node.expression)
  notify_variable_value_referred(node.expression, var)

  if active_fun = interpreter._active_function and
      ret_type = active_fun.type.return_type
    if var.type.same_as?(ret_type)
      conved = var
    else
      conved = do_conversion(var, ret_type) || create_tmpvar(ret_type)
      notify_implicit_conv_performed(node.expression, var, conved)
    end
  else
    conved = var
  end

  notify_sequence_point_reached(SequencePoint.new(node))
  notify_return_stmt_evaled(node, var)
  BreakEvent.of_return.throw
end
visit_while_statement(node) click to toggle source
# File lib/adlint/cc1/interp.rb, line 1107
def visit_while_statement(node)
  checkpoint(node.location)

  node.executed = true
  notify_while_stmt_started(node)

  ctrlexpr_obj = interpret_iteration_ctrlexpr(node, node.expression)
  ctrlexpr_var = object_to_variable(ctrlexpr_obj, node.expression)
  ctrlexpr_val = value_of(ctrlexpr_var)

  notify_variable_value_referred(node.expression, ctrlexpr_var)
  notify_sequence_point_reached(SequencePoint.new(node.expression))
  notify_while_ctrlexpr_evaled(node, ctrlexpr_val)

  orig_ctrlexpr, ctrlexpr = node.deduct_controlling_expression

  case
  when ctrlexpr_val.test_must_be_true.true?
    begin
      enter_iteration_statement(orig_ctrlexpr)
      branch_opts = [ITERATION, NARROWING, FINAL, IMPLICIT_COND, COMPLETE]
      branched_eval(ctrlexpr, *branch_opts) { interpret(node.statement) }
    ensure
      leave_iteration_statement(orig_ctrlexpr)
    end
  when ctrlexpr_val.test_may_be_true.true?
    begin
      enter_iteration_statement(orig_ctrlexpr)
      branch_opts = [ITERATION, NARROWING, FINAL, IMPLICIT_COND]
      branched_eval(ctrlexpr, *branch_opts) { interpret(node.statement) }
    ensure
      leave_iteration_statement(orig_ctrlexpr)
    end
  end
ensure
  notify_while_stmt_ended(node)
end

Private Instance Methods

deduct_ctrl_var_path_by_compound_assignment_expr(var, expr) click to toggle source
# File lib/adlint/cc1/interp.rb, line 1432
def deduct_ctrl_var_path_by_compound_assignment_expr(var, expr)
  return nil unless expr.lhs_operand.identifier.value == var.name

  case expr.operator.type
  when "+="
    :increase
  when "-="
    :decrease
  else
    nil
  end
end
deduct_ctrl_var_path_by_simple_assignment_expr(var, expr) click to toggle source
# File lib/adlint/cc1/interp.rb, line 1401
def deduct_ctrl_var_path_by_simple_assignment_expr(var, expr)
  return nil unless expr.lhs_operand.identifier.value == var.name

  additive_exprs = collect_additive_expressions(expr.rhs_operand)
  histogram = additive_exprs.map { |additive_expr|
    if additive_expr.lhs_operand.kind_of?(ObjectSpecifier)
      lhs_name = additive_expr.lhs_operand.identifier.value
    end
    if additive_expr.rhs_operand.kind_of?(ObjectSpecifier)
      rhs_name = additive_expr.rhs_operand.identifier.value
    end

    next nil unless lhs_name == var.name || rhs_name == var.name

    case additive_expr.operator.type
    when "+"
      :increase
    when "-"
      :decrease
    else
      nil
    end
  }.compact.each_with_object(Hash.new(0)) { |dir, hash| hash[dir] += 1 }

  if histogram.empty?
    nil
  else
    histogram[:decrease] <= histogram[:increase] ? :increase : :decrease
  end
end
deduct_variable_varying_path(var, iter_stmt) click to toggle source
# File lib/adlint/cc1/interp.rb, line 1378
def deduct_variable_varying_path(var, iter_stmt)
  histogram = iter_stmt.varying_expressions.map { |expr|
    case expr
    when SimpleAssignmentExpression
      deduct_ctrl_var_path_by_simple_assignment_expr(var, expr)
    when CompoundAssignmentExpression
      deduct_ctrl_var_path_by_compound_assignment_expr(var, expr)
    when PrefixIncrementExpression, PostfixIncrementExpression
      expr.operand.identifier.value == var.name ? :increase : nil
    when PrefixDecrementExpression, PostfixDecrementExpression
      expr.operand.identifier.value == var.name ? :decrease : nil
    else
      nil
    end
  }.compact.each_with_object(Hash.new(0)) { |dir, hash| hash[dir] += 1 }

  if histogram.empty?
    nil
  else
    histogram[:decrease] <= histogram[:increase] ? :increase : :decrease
  end
end
effective_ctrlexpr() click to toggle source
# File lib/adlint/cc1/interp.rb, line 1445
def effective_ctrlexpr
  @effective_ctrlexpr_stack.last
end
enter_iteration_statement(effective_ctrlexpr) click to toggle source
# File lib/adlint/cc1/interp.rb, line 1449
def enter_iteration_statement(effective_ctrlexpr)
  @effective_ctrlexpr_stack.push(effective_ctrlexpr)
end
interpret_for_body_statement(node, complete) click to toggle source
# File lib/adlint/cc1/interp.rb, line 1295
def interpret_for_body_statement(node, complete)
  if complete
    branch_opts = [ITERATION, NARROWING, FINAL, IMPLICIT_COND, COMPLETE]
  else
    branch_opts = [ITERATION, NARROWING, FINAL, IMPLICIT_COND]
  end

  orig_ctrlexpr, ctrlexpr = node.deduct_controlling_expression
  enter_iteration_statement(orig_ctrlexpr)

  branched_eval(ctrlexpr, *branch_opts) do
    interpret(node.body_statement)
    interpret(node.expression) if node.expression

    if explicit_ctrlexpr = node.condition_statement.expression
      # NOTE: To avoid that value of the controlling variable is marked
      #       as updated at end of the for-statement.  Value of the
      #       controlling variable is referred by the controlling
      #       expression at the last iteration.
      # FIXME: This re-interpretation of the controlling expression may
      #        cause duplicative warning messages.
      # FIXME: This re-interpretation of the controlling expression always
      #        causes "logical-expression must be false" warnings about a
      #        one-time-for-loop.  To avoid this, now, workarounds are in
      #        builtin code checks W0609 and W0610.
      var = object_to_variable(interpret(explicit_ctrlexpr),
                               explicit_ctrlexpr)
      notify_variable_value_referred(explicit_ctrlexpr, var)
      notify_sequence_point_reached(SequencePoint.new(explicit_ctrlexpr))
    end
  end
ensure
  leave_iteration_statement(orig_ctrlexpr)
end
interpret_for_ctrlexpr(node) click to toggle source
# File lib/adlint/cc1/interp.rb, line 1276
def interpret_for_ctrlexpr(node)
  node.condition_statement.executed = true

  if ctrlexpr = node.condition_statement.expression
    ctrlexpr_obj = interpret(ctrlexpr, QUIET)
    ctrlexpr_var = object_to_variable(ctrlexpr_obj, ctrlexpr)
    ctrlexpr_val = value_of(ctrlexpr_var)

    ctrlexpr_obj = interpret_iteration_ctrlexpr(node, ctrlexpr)
    ctrlexpr_var = object_to_variable(ctrlexpr_obj, ctrlexpr)
    notify_variable_value_referred(ctrlexpr, ctrlexpr_var)
    notify_sequence_point_reached(SequencePoint.new(ctrlexpr))
  else
    widen_varying_variable_value_domain(node)
  end

  ctrlexpr_val
end
interpret_iteration_ctrlexpr(iter_stmt, ctrlexpr) click to toggle source
# File lib/adlint/cc1/interp.rb, line 1351
def interpret_iteration_ctrlexpr(iter_stmt, ctrlexpr)
  environment.enter_branch(FINAL).execute(interpreter, nil) do
    widen_varying_variable_value_domain(iter_stmt)
  end
  interpret(ctrlexpr)
ensure
  environment.leave_branch_group
  environment.leave_branch
end
leave_iteration_statement(effective_ctrlexpr) click to toggle source
# File lib/adlint/cc1/interp.rb, line 1453
def leave_iteration_statement(effective_ctrlexpr)
  @effective_ctrlexpr_stack.pop
end
uninitialize_block_local_variables(generic_labeled_stmt) click to toggle source
# File lib/adlint/cc1/interp.rb, line 1330
def uninitialize_block_local_variables(generic_labeled_stmt)
  related_goto_stmts = generic_labeled_stmt.referrers
  return if related_goto_stmts.empty?

  local_variables.each do |var|
    var_def = var.declarations_and_definitions.first

    anterior_goto_stmts = related_goto_stmts.select { |goto_stmt|
      goto_stmt.location.line_no < var_def.location.line_no
    }

    unless anterior_goto_stmts.empty?
      var.value.enter_versioning_group
      var.value.begin_versioning
      var.uninitialize!
      var.value.end_versioning
      var.value.leave_versioning_group(true)
    end
  end
end
widen_varying_variable_value_domain(iter_stmt) click to toggle source
# File lib/adlint/cc1/interp.rb, line 1361
def widen_varying_variable_value_domain(iter_stmt)
  iter_stmt.varying_variable_names.each do |name|
    if var = variable_named(name)
      widened_sval = var.type.arbitrary_value

      case deduct_variable_varying_path(var, iter_stmt)
      when :increase
        widened_sval.narrow_domain!(Operator::GE, var.value)
      when :decrease
        widened_sval.narrow_domain!(Operator::LE, var.value)
      end

      var.widen_value_domain!(Operator::EQ, widened_sval)
    end
  end
end