protected void doFieldWrite( WalkContext context, int receiver, CAstNode elt, CAstNode parent, int rval) { this.visit(elt, context, this); if (elt.getKind() == CAstNode.CONSTANT && elt.getValue() instanceof String) { String field = (String) elt.getValue(); if (isPrologueScript(context) && "__proto__".equals(field)) { context.cfg().addInstruction(((JSInstructionFactory) insts).SetPrototype(receiver, rval)); return; } } /* } else { context.currentScope().getConstantValue(field); SSAPutInstruction put = ((JSInstructionFactory) insts).PutInstruction(receiver, rval, field); try { assert field.equals(put.getDeclaredField().getName().toUnicodeString()); } catch (UTFDataFormatException e) { Assertions.UNREACHABLE(); } context.cfg().addInstruction(put); } } else { */ context .cfg() .addInstruction( ((JSInstructionFactory) insts).PropertyWrite(receiver, context.getValue(elt), rval)); // } }
protected void leaveInstanceOf(CAstNode n, WalkContext c, CAstVisitor<WalkContext> visitor) { WalkContext context = (WalkContext) c; int result = context.getValue(n); visit(n.getChild(0), context, visitor); int value = context.getValue(n.getChild(0)); visit(n.getChild(1), context, visitor); int type = context.getValue(n.getChild(1)); context.cfg().addInstruction(new JavaScriptInstanceOf(result, value, type)); }
/** * 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; }
protected void doFieldRead( WalkContext context, int result, int receiver, CAstNode elt, CAstNode readNode) { this.visit(elt, context, this); int x = context.currentScope().allocateTempValue(); context.cfg().addInstruction(((JSInstructionFactory) insts).AssignInstruction(x, receiver)); context.cfg().addInstruction(((JSInstructionFactory) insts).PrototypeLookup(x, x)); if (elt.getKind() == CAstNode.CONSTANT && elt.getValue() instanceof String) { String field = (String) elt.getValue(); // symtab needs to have this value context.currentScope().getConstantValue(field); context.cfg().addInstruction(((JSInstructionFactory) insts).GetInstruction(result, x, field)); } else { context .cfg() .addInstruction( ((JSInstructionFactory) insts).PropertyRead(result, x, context.getValue(elt))); } // generate code to handle read of non-existent property if (context.getControlFlow().getMappedNodes().contains(readNode)) { context.cfg().addPreNode(readNode, context.getUnwindState()); context.cfg().newBlock(true); if (context.getControlFlow().getTarget(readNode, JavaScriptTypes.TypeError) != null) context .cfg() .addPreEdge( readNode, context.getControlFlow().getTarget(readNode, JavaScriptTypes.TypeError), true); else context.cfg().addPreEdgeToExit(readNode, true); } }
protected void doIsFieldDefined(WalkContext context, int result, int ref, CAstNode f) { if (f.getKind() == CAstNode.CONSTANT && f.getValue() instanceof String) { String field = (String) f.getValue(); FieldReference fieldRef = FieldReference.findOrCreate( JavaScriptTypes.Root, Atom.findOrCreateUnicodeAtom((String) field), JavaScriptTypes.Root); context .cfg() .addInstruction( ((JSInstructionFactory) insts).IsDefinedInstruction(result, ref, fieldRef)); } else { context .cfg() .addInstruction( ((JSInstructionFactory) insts) .IsDefinedInstruction(result, ref, context.getValue(f))); } }
protected void doCall( WalkContext context, CAstNode call, int result, int exception, CAstNode name, int receiver, int[] arguments) { MethodReference ref = name.getValue().equals("ctor") ? JavaScriptMethods.ctorReference : name.getValue().equals("dispatch") ? JavaScriptMethods.dispatchReference : AstMethodReference.fnReference(JavaScriptTypes.CodeBody); context .cfg() .addInstruction( ((JSInstructionFactory) insts) .Invoke( receiver, result, arguments, exception, new JSCallSiteReference(ref, context.cfg().getCurrentInstruction()))); context.cfg().addPreNode(call, context.getUnwindState()); // this new block is for the normal termination case context.cfg().newBlock(true); // exceptional case: flow to target given in CAst, or if null, the exit node if (context.getControlFlow().getTarget(call, null) != null) context.cfg().addPreEdge(call, context.getControlFlow().getTarget(call, null), true); else context.cfg().addPreEdgeToExit(call, true); }
protected boolean doVisit(CAstNode n, WalkContext cntxt, CAstVisitor<WalkContext> visitor) { WalkContext context = (WalkContext) cntxt; switch (n.getKind()) { case CAstNode.TYPE_OF: { int result = context.currentScope().allocateTempValue(); this.visit(n.getChild(0), context, this); int ref = context.getValue(n.getChild(0)); context .cfg() .addInstruction(((JSInstructionFactory) insts).TypeOfInstruction(result, ref)); context.setValue(n, result); return true; } case JavaScriptCAstNode.ENTER_WITH: case JavaScriptCAstNode.EXIT_WITH: { this.visit(n.getChild(0), context, this); int ref = context.getValue(n.getChild(0)); context .cfg() .addInstruction( ((JSInstructionFactory) insts) .WithRegion(ref, n.getKind() == JavaScriptCAstNode.ENTER_WITH)); return true; } default: { return false; } } }
protected void doPrimitive(int resultVal, WalkContext context, CAstNode primitiveCall) { try { String name = (String) primitiveCall.getChild(0).getValue(); if (name.equals("GlobalNaN")) { context .cfg() .addInstruction( ((JSInstructionFactory) insts) .AssignInstruction( resultVal, context.currentScope().getConstantValue(new Float(Float.NaN)))); } else if (name.equals("GlobalInfinity")) { context .cfg() .addInstruction( ((JSInstructionFactory) insts) .AssignInstruction( resultVal, context .currentScope() .getConstantValue(new Float(Float.POSITIVE_INFINITY)))); } else if (name.equals("MathE")) { context .cfg() .addInstruction( ((JSInstructionFactory) insts) .AssignInstruction( resultVal, context.currentScope().getConstantValue(new Double(Math.E)))); } else if (name.equals("MathPI")) { context .cfg() .addInstruction( ((JSInstructionFactory) insts) .AssignInstruction( resultVal, context.currentScope().getConstantValue(new Double(Math.PI)))); } else if (name.equals("MathSQRT1_2")) { context .cfg() .addInstruction( ((JSInstructionFactory) insts) .AssignInstruction( resultVal, context.currentScope().getConstantValue(new Double(Math.sqrt(.5))))); } else if (name.equals("MathSQRT2")) { context .cfg() .addInstruction( ((JSInstructionFactory) insts) .AssignInstruction( resultVal, context.currentScope().getConstantValue(new Double(Math.sqrt(2))))); } else if (name.equals("MathLN2")) { context .cfg() .addInstruction( ((JSInstructionFactory) insts) .AssignInstruction( resultVal, context.currentScope().getConstantValue(new Double(Math.log(2))))); } else if (name.equals("MathLN10")) { context .cfg() .addInstruction( ((JSInstructionFactory) insts) .AssignInstruction( resultVal, context.currentScope().getConstantValue(new Double(Math.log(10))))); } else if (name.equals("NewObject")) { doNewObject(context, null, resultVal, "Object", null); } else if (name.equals("NewArray")) { doNewObject(context, null, resultVal, "Array", null); } else if (name.equals("NewString")) { doPrimitiveNew(context, resultVal, "String"); } else if (name.equals("NewNumber")) { doPrimitiveNew(context, resultVal, "Number"); } else if (name.equals("NewRegExp")) { doPrimitiveNew(context, resultVal, "RegExp"); } else if (name.equals("NewFunction")) { doNewObject(context, null, resultVal, "Function", null); } else if (name.equals("NewUndefined")) { doNewObject(context, null, resultVal, "Undefined", null); } else { context .cfg() .addInstruction( ((JSInstructionFactory) insts) .AssignInstruction(resultVal, context.currentScope().getConstantValue(null))); } } catch (ClassCastException e) { throw new RuntimeException( "Cannot translate primitive " + primitiveCall.getChild(0).getValue()); } }
@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; } }