Beispiel #1
0
  /**
   * create a CAstNode l representing a loop that traverses the prototype chain from receiver
   * searching for the constant property element. update nodeMap to map root to an expression that
   * reads the property from the right node.
   *
   * @param root
   * @param receiver
   * @param element
   * @param context
   * @param nodeMap
   * @return
   */
  private CAstNode makeConstRead(
      CAstNode root,
      CAstNode receiver,
      CAstNode element,
      RewriteContext context,
      Map<Pair<CAstNode, ExpanderKey>, CAstNode> nodeMap) {
    CAstNode get, result;
    String receiverTemp = TEMP_NAME + (readTempCounter++);
    String elt = (String) element.getValue();
    if (elt.equals("prototype") || elt.equals("__proto__")) {
      result =
          Ast.makeNode(
              CAstNode.BLOCK_EXPR,
              get = Ast.makeNode(CAstNode.OBJECT_REF, receiver, Ast.makeConstant(elt)));
    } else {

      if (context.inAssignment()) {
        context.setAssign(
            Ast.makeNode(CAstNode.VAR, Ast.makeConstant(receiverTemp)), Ast.makeConstant(elt));
      }

      result =
          Ast.makeNode(
              CAstNode.BLOCK_EXPR,
              // declare loop variable and initialize to the receiver
              Ast.makeNode(
                  CAstNode.DECL_STMT,
                  Ast.makeConstant(new InternalCAstSymbol(receiverTemp, false, false)),
                  receiver),
              Ast.makeNode(
                  CAstNode.LOOP,
                  // while the desired property of the loop variable is not
                  // defined...
                  Ast.makeNode(
                      CAstNode.UNARY_EXPR,
                      CAstOperator.OP_NOT,
                      Ast.makeNode(
                          CAstNode.IS_DEFINED_EXPR,
                          Ast.makeNode(CAstNode.VAR, Ast.makeConstant(receiverTemp)),
                          Ast.makeConstant(elt))),
                  // set the loop variable to be its prototype
                  Ast.makeNode(
                      CAstNode.ASSIGN,
                      Ast.makeNode(CAstNode.VAR, Ast.makeConstant(receiverTemp)),
                      Ast.makeNode(
                          CAstNode.OBJECT_REF,
                          Ast.makeNode(CAstNode.VAR, Ast.makeConstant(receiverTemp)),
                          Ast.makeConstant("__proto__")))),
              get =
                  Ast.makeNode(
                      CAstNode.OBJECT_REF,
                      Ast.makeNode(CAstNode.VAR, Ast.makeConstant(receiverTemp)),
                      Ast.makeConstant(elt)));
    }

    nodeMap.put(Pair.make(root, context.key()), result);
    nodeMap.put(Pair.make(root, ExpanderKey.EXTRA), get);

    return result;
  }
  private IMethod makeObjectCall(IClass cls, int nargs) {
    assert nargs == 0;

    Object key = Pair.make(cls, new Integer(nargs));
    if (constructors.containsKey(key)) return constructors.get(key);
    else return record(key, makeNullaryObjectConstructor(cls));
  }
  private IMethod makeFunctionObjectConstructor(IClass cls, int nargs) {
    JSInstructionFactory insts =
        (JSInstructionFactory) cls.getClassLoader().getInstructionFactory();
    Object key = Pair.make(cls, new Integer(nargs));
    if (constructors.containsKey(key)) return constructors.get(key);

    MethodReference ref = JavaScriptMethods.makeCtorReference(cls.getReference());
    JavaScriptSummary S = new JavaScriptSummary(ref, nargs + 1);
    S.addStatement(insts.GetInstruction(nargs + 4, 1, "prototype"));
    S.getNextProgramCounter();

    S.addStatement(
        insts.NewInstruction(
            nargs + 5, NewSiteReference.make(S.getNextProgramCounter(), JavaScriptTypes.Object)));

    S.addStatement(insts.SetPrototype(nargs + 5, nargs + 4));
    // S.addStatement(insts.PutInstruction(nargs + 5, nargs + 4, "__proto__"));
    S.getNextProgramCounter();

    CallSiteReference cs = new JSCallSiteReference(S.getNextProgramCounter());
    int[] args = new int[nargs + 1];
    args[0] = nargs + 5;
    for (int i = 0; i < nargs; i++) args[i + 1] = i + 2;
    S.addStatement(insts.Invoke(1, nargs + 7, args, nargs + 8, cs));

    S.addStatement(insts.ReturnInstruction(nargs + 7, false));
    S.getNextProgramCounter();

    S.addStatement(insts.ReturnInstruction(nargs + 5, false));
    S.getNextProgramCounter();

    // S.addConstant(nargs + 9, new ConstantValue("__proto__"));

    return record(key, new JavaScriptConstructor(ref, S, cls));
  }
 /**
  * builds a call graph, and sets the corresponding heap model for analysis
  *
  * @param scope
  * @param cha
  * @param options
  * @return
  * @throws CancelException
  * @throws IllegalArgumentException
  */
 private static Pair<CallGraph, PointerAnalysis> buildCallGraph(
     AnalysisScope scope, ClassHierarchy cha, AnalysisOptions options)
     throws IllegalArgumentException, CancelException {
   CallGraph retCG = null;
   PointerAnalysis retPA = null;
   final AnalysisCache cache = new AnalysisCache();
   CallGraphBuilder builder;
   if (CHEAP_CG) {
     builder = Util.makeZeroCFABuilder(options, cache, cha, scope);
     // we want vanilla 0-1 CFA, which has one abstract loc per allocation
     heapModel = Util.makeVanillaZeroOneCFABuilder(options, cache, cha, scope);
   } else {
     builder = Util.makeZeroOneContainerCFABuilder(options, cache, cha, scope);
     heapModel = (HeapModel) builder;
   }
   ProgressMaster master = ProgressMaster.make(new NullProgressMonitor(), 360000, false);
   master.beginTask("runSolver", 1);
   try {
     retCG = builder.makeCallGraph(options, master);
     retPA = builder.getPointerAnalysis();
   } catch (CallGraphBuilderCancelException e) {
     System.err.println("TIMED OUT!!");
     retCG = e.getPartialCallGraph();
     retPA = e.getPartialPointerAnalysis();
   }
   return Pair.make(retCG, retPA);
 }
  private IMethod makeNumberCall(IClass cls, int nargs) {
    assert nargs == 0 || nargs == 1;

    Object key = Pair.make(cls, new Integer(nargs));
    if (constructors.containsKey(key)) return constructors.get(key);
    else return record(key, (nargs == 0) ? makeNullaryNumberCall(cls) : makeUnaryNumberCall(cls));
  }
 List<String> mapExternalVarForProject() {
   List<String> map = new LinkedList<String>();
   PlanNode predecessor = planNode.getPredecessor(wrapper.getPlan());
   if (predecessor != null) {
     String predecessorCTE = wrapper.getPlanNodeCTE(predecessor, false);
     Set<Variable> predecessorVars = predecessor.getAvailableVariables();
     if (predecessorVars != null) {
       Set<Variable> iriBoundVariables = wrapper.getIRIBoundVariables();
       for (Variable v : predecessorVars) {
         if (projectedInPrimary.contains(v)) continue;
         projectedInPrimary.add(v);
         String vPredName = wrapper.getPlanNodeVarMapping(predecessor, v.getName());
         map.add(predecessorCTE + "." + vPredName + " AS " + v.getName());
         String vType = null;
         if (!iriBoundVariables.contains(v)) {
           String vPredType =
               predecessorCTE + "." + v.getName() + Constants.TYP_COLUMN_SUFFIX_IN_SPARQL_RS;
           map.add(vPredType + " AS " + v.getName() + Constants.TYP_COLUMN_SUFFIX_IN_SPARQL_RS);
           vType = vPredType;
         }
         varMap.put(v.getName(), Pair.make(predecessorCTE + "." + vPredName, vType));
       }
     }
   }
   return map;
 }
Beispiel #7
0
 /*
  * @see com.ibm.wala.ssa.SSAPiNodePolicy#getPi(com.ibm.wala.ssa.SSAConditionalBranchInstruction, com.ibm.wala.ssa.SSAInstruction,
  * com.ibm.wala.ssa.SSAInstruction, com.ibm.wala.ssa.SymbolTable)
  */
 @Override
 public Pair<Integer, SSAInstruction> getPi(
     SSAConditionalBranchInstruction cond,
     SSAInstruction def1,
     SSAInstruction def2,
     SymbolTable symbolTable) {
   if (def1 instanceof SSAInstanceofInstruction) {
     if (symbolTable.isBooleanOrZeroOneConstant(cond.getUse(1))) {
       return Pair.make(def1.getUse(0), def1);
     }
   }
   if (def2 instanceof SSAInstanceofInstruction) {
     if (symbolTable.isBooleanOrZeroOneConstant(cond.getUse(0))) {
       return Pair.make(def2.getUse(0), def2);
     }
   }
   return null;
 }
 private IMethod makeArrayConstructor(IClass cls, int nargs) {
   Object key = Pair.make(cls, new Integer(nargs));
   if (constructors.containsKey(key)) return constructors.get(key);
   else
     return record(
         key,
         (nargs == 1)
             ? makeArrayLengthConstructor(cls)
             : makeArrayContentsConstructor(cls, nargs));
 }
 private List<String> getEntrySQLConstraint() {
   List<String> entrySQLConstraint = new LinkedList<String>();
   QueryTriple qt = planNode.getTriple();
   QueryTripleTerm entryTerm = null;
   AccessMethod am = planNode.getMethod();
   boolean hasSqlType = false;
   if (AccessMethodType.isDirectAccess(am.getType())) {
     entryTerm = qt.getSubject();
   } else {
     entryTerm = qt.getObject();
     hasSqlType = true;
   }
   if (entryTerm.isVariable()) {
     Variable entryVariable = entryTerm.getVariable();
     PlanNode predecessor = planNode.getPredecessor(wrapper.getPlan());
     boolean typConstraint = false;
     if (hasSqlType && wrapper.getIRIBoundVariables().contains(entryVariable)) {
       entrySQLConstraint.add(
           getTypeConstraintForIRIs(tTableColumnPrefix + Constants.NAME_COLUMN_PREFIX_TYPE));
     } else if (hasSqlType && !wrapper.getIRIBoundVariables().contains(entryVariable)) {
       typConstraint = true;
     }
     boolean entryConstraintWithPredecessor =
         getPredecessorConstraint(
             entrySQLConstraint,
             entryVariable,
             predecessor,
             tTableColumnPrefix + Constants.NAME_COLUMN_ENTRY,
             tTableColumnPrefix + Constants.NAME_COLUMN_PREFIX_TYPE,
             typConstraint);
     if (!entryConstraintWithPredecessor) {
       // Check for entry constraint for variable with the same name on different positions
       if (varMap.containsKey(entryVariable.getName())) {
         entrySQLConstraint.add(
             tTableColumnPrefix
                 + Constants.NAME_COLUMN_ENTRY
                 + " = "
                 + varMap.get(entryVariable.getName()).fst);
       }
     }
     String entryType =
         (typConstraint) ? tTableColumnPrefix + Constants.NAME_COLUMN_PREFIX_TYPE : null;
     varMap.put(
         entryVariable.getName(),
         Pair.make(tTableColumnPrefix + Constants.NAME_COLUMN_ENTRY, entryType));
   } else {
     super.addConstantEntrySQLConstraint(
         entryTerm,
         entrySQLConstraint,
         hasSqlType,
         tTableColumnPrefix + Constants.NAME_COLUMN_ENTRY);
   }
   return entrySQLConstraint;
 }
 List<String> getValueSQLConstraint() {
   List<String> valueSQLConstraint = new LinkedList<String>();
   QueryTriple qt = planNode.getTriple();
   QueryTripleTerm valueTerm = null;
   AccessMethod am = planNode.getMethod();
   boolean hasSqlType = false;
   if (AccessMethodType.isDirectAccess(am.getType())) {
     valueTerm = qt.getObject();
     hasSqlType = true;
   } else {
     valueTerm = qt.getSubject();
   }
   if (valueTerm.isVariable()) {
     Variable valueVariable = valueTerm.getVariable();
     PlanNode predecessor = planNode.getPredecessor(wrapper.getPlan());
     boolean typConstraint = false;
     if (hasSqlType && wrapper.getIRIBoundVariables().contains(valueVariable)) {
       valueSQLConstraint.add(
           getTypeConstraintForIRIs(hashColumnExpression(Constants.NAME_COLUMN_PREFIX_TYPE)));
     }
     if (hasSqlType && !wrapper.getIRIBoundVariables().contains(valueVariable)) {
       typConstraint = true;
     }
     boolean hasValueConstrintWithPredecessor =
         getPredecessorConstraint(
             valueSQLConstraint,
             valueVariable,
             predecessor,
             hashColumnExpression(Constants.NAME_COLUMN_PREFIX_VALUE),
             hashColumnExpression(Constants.NAME_COLUMN_PREFIX_TYPE),
             typConstraint);
     if (!hasValueConstrintWithPredecessor) {
       if (varMap.containsKey(valueVariable.getName())) {
         // Check for value constraint for variable with the same name on different positions
         valueSQLConstraint.add(
             hashColumnExpression(Constants.NAME_COLUMN_PREFIX_VALUE)
                 + " = "
                 + varMap.get(valueVariable.getName()).fst);
       }
     }
     String valueType =
         (typConstraint) ? hashColumnExpression(Constants.NAME_COLUMN_PREFIX_TYPE) : null;
     varMap.put(
         valueVariable.getName(),
         Pair.make(hashColumnExpression(Constants.NAME_COLUMN_PREFIX_VALUE), valueType));
   } else {
     valueSQLConstraint.add(
         hashColumnExpression(Constants.NAME_COLUMN_PREFIX_VALUE)
             + " = '"
             + valueTerm.toSqlDataString()
             + "'");
   }
   return valueSQLConstraint;
 }
Beispiel #11
0
  /**
   * similar to makeConstRead(), but desired property is some expression instead of a constant
   *
   * @see #makeConstRead(CAstNode, CAstNode, CAstNode, RewriteContext, Map)
   */
  private CAstNode makeVarRead(
      CAstNode root,
      CAstNode receiver,
      CAstNode element,
      RewriteContext context,
      Map<Pair<CAstNode, ExpanderKey>, CAstNode> nodeMap) {
    String receiverTemp = TEMP_NAME + (readTempCounter++);
    String elementTemp = TEMP_NAME + (readTempCounter++);

    if (context.inAssignment()) {
      context.setAssign(
          Ast.makeNode(CAstNode.VAR, Ast.makeConstant(receiverTemp)),
          Ast.makeNode(CAstNode.VAR, Ast.makeConstant(elementTemp)));
    }

    CAstNode get;
    CAstNode result =
        Ast.makeNode(
            CAstNode.BLOCK_EXPR,
            Ast.makeNode(
                CAstNode.DECL_STMT,
                Ast.makeConstant(new InternalCAstSymbol(receiverTemp, false, false)),
                receiver),
            Ast.makeNode(
                CAstNode.DECL_STMT,
                Ast.makeConstant(new InternalCAstSymbol(elementTemp, false, false)),
                element),
            Ast.makeNode(
                CAstNode.LOOP,
                Ast.makeNode(
                    CAstNode.UNARY_EXPR,
                    CAstOperator.OP_NOT,
                    Ast.makeNode(
                        CAstNode.IS_DEFINED_EXPR,
                        Ast.makeNode(CAstNode.VAR, Ast.makeConstant(receiverTemp)),
                        Ast.makeNode(CAstNode.VAR, Ast.makeConstant(elementTemp)))),
                Ast.makeNode(
                    CAstNode.ASSIGN,
                    Ast.makeNode(CAstNode.VAR, Ast.makeConstant(receiverTemp)),
                    Ast.makeNode(
                        CAstNode.OBJECT_REF,
                        Ast.makeNode(CAstNode.VAR, Ast.makeConstant(receiverTemp)),
                        Ast.makeConstant("__proto__")))),
            get =
                Ast.makeNode(
                    CAstNode.OBJECT_REF,
                    Ast.makeNode(CAstNode.VAR, Ast.makeConstant(receiverTemp)),
                    Ast.makeNode(CAstNode.VAR, Ast.makeConstant(elementTemp))));

    nodeMap.put(Pair.make(root, context.key()), get);

    return result;
  }
  private IMethod makeObjectConstructor(IClass cls, int nargs) {
    if (nargs == 0 || nargs == 1) {

      Object key = Pair.make(cls, new Integer(nargs));
      if (constructors.containsKey(key)) return constructors.get(key);
      else
        return record(
            key,
            (nargs == 0) ? makeNullaryObjectConstructor(cls) : makeUnaryObjectConstructor(cls));

    } else {
      // not a legal call, likely the result of analysis imprecision
      return null;
    }
  }
  private IMethod makeValueConstructor(IClass cls, int nargs, Object value) {
    if (nargs == 0 || nargs == 1) {

      Object key = Pair.make(cls, new Integer(nargs));
      if (constructors.containsKey(key)) return constructors.get(key);
      else
        return record(
            key,
            (nargs == 0)
                ? makeNullaryValueConstructor(cls, value)
                : makeUnaryValueConstructor(cls));
    } else {
      // not a legal call, likely due to dataflow imprecision
      return null;
    }
  }
 /**
  * collect the putstatic instructions in the call graph as {@link PathEdge} seeds for the
  * analysis
  */
 private Collection<PathEdge<BasicBlockInContext<IExplodedBasicBlock>>> collectInitialSeeds() {
   Collection<PathEdge<BasicBlockInContext<IExplodedBasicBlock>>> result = HashSetFactory.make();
   for (BasicBlockInContext<IExplodedBasicBlock> bb : supergraph) {
     IExplodedBasicBlock ebb = bb.getDelegate();
     SSAInstruction instruction = ebb.getInstruction();
     if (instruction instanceof SSAPutInstruction) {
       SSAPutInstruction putInstr = (SSAPutInstruction) instruction;
       if (putInstr.isStatic()) {
         final CGNode cgNode = bb.getNode();
         Pair<CGNode, Integer> fact = Pair.make(cgNode, ebb.getFirstInstructionIndex());
         int factNum = domain.add(fact);
         BasicBlockInContext<IExplodedBasicBlock> fakeEntry = getFakeEntry(cgNode);
         // note that the fact number used for the source of this path edge doesn't really matter
         result.add(PathEdge.createPathEdge(fakeEntry, factNum, bb, factNum));
       }
     }
   }
   return result;
 }
  private IMethod makeFunctionConstructor(IClass receiver, IClass cls) {
    JSInstructionFactory insts =
        (JSInstructionFactory) cls.getClassLoader().getInstructionFactory();
    Pair<IClass, IClass> tableKey = Pair.make(receiver, cls);
    if (constructors.containsKey(tableKey)) return constructors.get(tableKey);

    MethodReference ref = JavaScriptMethods.makeCtorReference(receiver.getReference());
    JavaScriptSummary S = new JavaScriptSummary(ref, 1);

    S.addStatement(insts.GetInstruction(4, 1, "prototype"));
    S.getNextProgramCounter();

    S.addStatement(
        insts.NewInstruction(
            5, NewSiteReference.make(S.getNextProgramCounter(), cls.getReference())));

    S.addStatement(
        insts.NewInstruction(
            7, NewSiteReference.make(S.getNextProgramCounter(), JavaScriptTypes.Object)));

    S.addStatement(insts.SetPrototype(5, 4));
    // S.addStatement(insts.PutInstruction(5, 4, "__proto__"));
    S.getNextProgramCounter();

    S.addStatement(insts.PutInstruction(5, 7, "prototype"));
    S.getNextProgramCounter();

    S.addStatement(insts.PutInstruction(7, 5, "constructor"));
    S.getNextProgramCounter();

    // TODO we need to set v7.__proto__ to Object.prototype
    S.addStatement(insts.ReturnInstruction(5, false));
    S.getNextProgramCounter();

    // S.addConstant(8, new ConstantValue("__proto__"));

    if (receiver != cls)
      return record(
          tableKey,
          new JavaScriptConstructor(ref, S, receiver, "(" + cls.getReference().getName() + ")"));
    else return record(tableKey, new JavaScriptConstructor(ref, S, receiver));
  }
  private static List<Pair<CGNode, SSACheckCastInstruction>> findFailingCasts(
      CallGraph cg, DemandRefinementPointsTo dmp) {
    final IClassHierarchy cha = dmp.getClassHierarchy();
    List<Pair<CGNode, SSACheckCastInstruction>> failing =
        new ArrayList<Pair<CGNode, SSACheckCastInstruction>>();

    int numSafe = 0, numMightFail = 0;
    outer:
    for (Iterator<? extends CGNode> nodeIter = cg.iterator(); nodeIter.hasNext(); ) {
      CGNode node = nodeIter.next();
      TypeReference declaringClass = node.getMethod().getReference().getDeclaringClass();
      // skip library classes
      if (declaringClass.getClassLoader().equals(ClassLoaderReference.Primordial)) {
        continue;
      }
      IR ir = node.getIR();
      if (ir == null) continue;
      SSAInstruction[] instrs = ir.getInstructions();
      for (int i = 0; i < instrs.length; i++) {
        if (numSafe + numMightFail > MAX_CASTS) break outer;
        SSAInstruction instruction = instrs[i];
        if (instruction instanceof SSACheckCastInstruction) {
          SSACheckCastInstruction castInstr = (SSACheckCastInstruction) instruction;
          final TypeReference[] declaredResultTypes = castInstr.getDeclaredResultTypes();

          boolean primOnly = true;
          for (TypeReference t : declaredResultTypes) {
            if (!t.isPrimitiveType()) {
              primOnly = false;
            }
          }
          if (primOnly) {
            continue;
          }

          System.err.println("CHECKING " + castInstr + " in " + node.getMethod());
          PointerKey castedPk = heapModel.getPointerKeyForLocal(node, castInstr.getUse(0));
          Predicate<InstanceKey> castPred =
              new Predicate<InstanceKey>() {

                @Override
                public boolean test(InstanceKey ik) {
                  TypeReference ikTypeRef = ik.getConcreteType().getReference();
                  for (TypeReference t : declaredResultTypes) {
                    if (cha.isAssignableFrom(cha.lookupClass(t), cha.lookupClass(ikTypeRef))) {
                      return true;
                    }
                  }
                  return false;
                }
              };
          long startTime = System.currentTimeMillis();
          Pair<PointsToResult, Collection<InstanceKey>> queryResult =
              dmp.getPointsTo(castedPk, castPred);
          long runningTime = System.currentTimeMillis() - startTime;
          System.err.println("running time: " + runningTime + "ms");
          final FieldRefinePolicy fieldRefinePolicy =
              dmp.getRefinementPolicy().getFieldRefinePolicy();
          switch (queryResult.fst) {
            case SUCCESS:
              System.err.println("SAFE: " + castInstr + " in " + node.getMethod());
              if (fieldRefinePolicy instanceof ManualFieldPolicy) {
                ManualFieldPolicy hackedFieldPolicy = (ManualFieldPolicy) fieldRefinePolicy;
                System.err.println(hackedFieldPolicy.getHistory());
              }
              System.err.println("TRAVERSED " + dmp.getNumNodesTraversed() + " nodes");
              numSafe++;
              break;
            case NOMOREREFINE:
              if (queryResult.snd != null) {
                System.err.println(
                    "MIGHT FAIL: no more refinement possible for "
                        + castInstr
                        + " in "
                        + node.getMethod());
              } else {
                System.err.println(
                    "MIGHT FAIL: exceeded budget for " + castInstr + " in " + node.getMethod());
              }
              failing.add(Pair.make(node, castInstr));
              numMightFail++;
              break;
            case BUDGETEXCEEDED:
              System.err.println(
                  "MIGHT FAIL: exceeded budget for " + castInstr + " in " + node.getMethod());
              failing.add(Pair.make(node, castInstr));
              numMightFail++;
              break;
            default:
              Assertions.UNREACHABLE();
          }
        }
      }
      // break outer;
    }
    System.err.println("TOTAL SAFE: " + numSafe);
    System.err.println("TOTAL MIGHT FAIL: " + numMightFail);
    return failing;
  }
Beispiel #17
0
  @Override
  protected CAstNode copyNodes(
      CAstNode root,
      final CAstControlFlowMap cfg,
      RewriteContext context,
      Map<Pair<CAstNode, ExpanderKey>, CAstNode> nodeMap) {
    int kind = root.getKind();

    if (kind == CAstNode.OBJECT_REF && context.inRead()) {
      // if we see a property access (OBJECT_REF) in a read context, transform
      // to a loop traversing the prototype chain
      CAstNode readLoop;
      CAstNode receiver = copyNodes(root.getChild(0), cfg, READ, nodeMap);
      CAstNode element = copyNodes(root.getChild(1), cfg, READ, nodeMap);
      if (element.getKind() == CAstNode.CONSTANT && element.getValue() instanceof String) {
        readLoop = makeConstRead(root, receiver, element, context, nodeMap);
      } else {
        readLoop = makeVarRead(root, receiver, element, context, nodeMap);
      }
      return readLoop;

    } else if (kind == CAstNode.ASSIGN_PRE_OP || kind == CAstNode.ASSIGN_POST_OP) {
      // handle cases like x.f++, represented as ASSIGN_POST_OP(x.f,1,+)
      AssignPreOrPostOpContext ctxt = new AssignPreOrPostOpContext();
      // generate loop for the first child (x.f for example), keeping the loop var and element var
      // in ctxt
      CAstNode lval = copyNodes(root.getChild(0), cfg, ctxt, nodeMap);
      CAstNode rval = copyNodes(root.getChild(1), cfg, READ, nodeMap);
      CAstNode op = copyNodes(root.getChild(2), cfg, READ, nodeMap);
      if (ctxt.receiverTemp != null) { // if we found a nested property access
        String temp1 = TEMP_NAME + (readTempCounter++);
        String temp2 = TEMP_NAME + (readTempCounter++);
        CAstNode copy =
            Ast.makeNode(
                CAstNode.BLOCK_EXPR,
                // assign lval to temp1 (where lval is a block that includes the prototype chain
                // loop)
                Ast.makeNode(
                    CAstNode.DECL_STMT,
                    Ast.makeConstant(new InternalCAstSymbol(temp1, true, false)),
                    lval),
                // ? --MS
                // rval,
                // assign temp2 the new value to be assigned
                Ast.makeNode(
                    CAstNode.DECL_STMT,
                    Ast.makeConstant(new InternalCAstSymbol(temp2, true, false)),
                    Ast.makeNode(
                        CAstNode.BINARY_EXPR,
                        op,
                        Ast.makeNode(CAstNode.VAR, Ast.makeConstant(temp1)),
                        rval)),
                // write temp2 into the property
                Ast.makeNode(
                    CAstNode.ASSIGN,
                    Ast.makeNode(CAstNode.OBJECT_REF, ctxt.receiverTemp, ctxt.elementTemp),
                    Ast.makeNode(CAstNode.VAR, Ast.makeConstant(temp2))),
                // final value depends on whether we had a pre op or post op
                Ast.makeNode(
                    CAstNode.VAR,
                    Ast.makeConstant((kind == CAstNode.ASSIGN_PRE_OP) ? temp2 : temp1)));
        nodeMap.put(Pair.make(root, context.key()), copy);
        return copy;
      } else {
        CAstNode copy = Ast.makeNode(kind, lval, rval, op);
        nodeMap.put(Pair.make(root, context.key()), copy);
        return copy;
      }

    } else if (kind == CAstNode.ASSIGN) {
      // use ASSIGN context for LHS so we don't translate property accesses there
      CAstNode copy =
          Ast.makeNode(
              CAstNode.ASSIGN,
              copyNodes(root.getChild(0), cfg, ASSIGN, nodeMap),
              copyNodes(root.getChild(1), cfg, READ, nodeMap));
      nodeMap.put(Pair.make(root, context.key()), copy);
      return copy;

    } else if (kind == CAstNode.BLOCK_EXPR) {
      CAstNode children[] = new CAstNode[root.getChildCount()];
      int last = (children.length - 1);
      for (int i = 0; i < last; i++) {
        children[i] = copyNodes(root.getChild(i), cfg, READ, nodeMap);
      }
      children[last] = copyNodes(root.getChild(last), cfg, context, nodeMap);

      CAstNode copy = Ast.makeNode(CAstNode.BLOCK_EXPR, children);
      nodeMap.put(Pair.make(root, context.key()), copy);
      return copy;

    } else if (root.getKind() == CAstNode.CONSTANT) {
      CAstNode copy = Ast.makeConstant(root.getValue());
      nodeMap.put(Pair.make(root, context.key()), copy);
      return copy;

    } else if (root.getKind() == CAstNode.OPERATOR) {
      nodeMap.put(Pair.make(root, context.key()), root);
      return root;

    } else {
      CAstNode children[] = new CAstNode[root.getChildCount()];
      for (int i = 0; i < children.length; i++) {
        children[i] = copyNodes(root.getChild(i), cfg, READ, nodeMap);
      }
      for (Object label : cfg.getTargetLabels(root)) {
        if (label instanceof CAstNode) {
          copyNodes((CAstNode) label, cfg, READ, nodeMap);
        }
      }
      CAstNode copy = Ast.makeNode(kind, children);
      nodeMap.put(Pair.make(root, context.key()), copy);
      return copy;
    }
  }