/****************************************************************************** * Copyright (c) 2002 - 2006 IBM Corporation. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *****************************************************************************/ package com.ibm.wala.cast.js.translator; import java.io.UTFDataFormatException; import com.ibm.wala.cast.ir.translator.AstTranslator; import com.ibm.wala.cast.js.loader.JSCallSiteReference; import com.ibm.wala.cast.js.loader.JavaScriptLoader; import com.ibm.wala.cast.js.ssa.JSInstructionFactory; import com.ibm.wala.cast.js.ssa.JavaScriptInstanceOf; import com.ibm.wala.cast.js.types.JavaScriptMethods; import com.ibm.wala.cast.js.types.JavaScriptTypes; import com.ibm.wala.cast.loader.AstMethod.DebuggingInformation; import com.ibm.wala.cast.tree.CAstEntity; import com.ibm.wala.cast.tree.CAstNode; import com.ibm.wala.cast.tree.CAstSymbol; import com.ibm.wala.cast.tree.CAstType; import com.ibm.wala.cast.tree.impl.CAstSymbolImpl; import com.ibm.wala.cast.tree.visit.CAstVisitor; import com.ibm.wala.cast.types.AstMethodReference; import com.ibm.wala.cfg.AbstractCFG; import com.ibm.wala.classLoader.NewSiteReference; import com.ibm.wala.ssa.SSAPutInstruction; import com.ibm.wala.ssa.SymbolTable; import com.ibm.wala.types.FieldReference; import com.ibm.wala.types.MethodReference; import com.ibm.wala.types.TypeName; import com.ibm.wala.types.TypeReference; import com.ibm.wala.util.debug.Assertions; import com.ibm.wala.util.strings.Atom; public class JSAstTranslator extends AstTranslator { private final static boolean DEBUG = false; public JSAstTranslator(JavaScriptLoader loader) { super(loader); } protected boolean useDefaultInitValues() { return false; } protected boolean hasImplicitGlobals() { return true; } protected boolean treatGlobalsAsLexicallyScoped() { return true; } protected boolean useLocalValuesForLexicalVars() { return true; } protected TypeReference defaultCatchType() { return JavaScriptTypes.Root; } protected TypeReference makeType(CAstType type) { Assertions.UNREACHABLE("JavaScript does not use CAstType"); return null; } private void addDefinedCheck(CAstNode n, WalkContext context, int readVn) { context.cfg().addPreNode(n); context.cfg().addInstruction(((JSInstructionFactory)insts).CheckReference(readVn)); CAstNode target = context.getControlFlow().getTarget(n, JavaScriptTypes.ReferenceError); if (target != null) { context.cfg().addPreEdge(n, target, true); } else { context.cfg().addPreEdgeToExit(n, true); } context.cfg().newBlock(true); } protected int doLexicallyScopedRead(CAstNode n, WalkContext context, String name) { int readVn = super.doLexicallyScopedRead(n, context, name); addDefinedCheck(n, context, readVn); return readVn; } protected int doGlobalRead(CAstNode n, WalkContext context, String name) { int readVn = super.doGlobalRead(n, context, name); if (! ("undefined".equals(name) || "$$undefined".equals(name))) { addDefinedCheck(n, context, readVn); } return readVn; } protected void defineType(CAstEntity type, WalkContext wc) { Assertions.UNREACHABLE("JavaScript doesn't have types. I suggest you look elsewhere for your amusement."); } protected void defineField(CAstEntity topEntity, WalkContext wc, CAstEntity n) { Assertions.UNREACHABLE("JavaScript doesn't have fields, numb-nuts!"); } protected String composeEntityName(WalkContext parent, CAstEntity f) { if (f.getKind() == CAstEntity.SCRIPT_ENTITY) return f.getName(); else return parent.getName() + "/" + f.getName(); } protected void declareFunction(CAstEntity N, WalkContext context) { String fnName = composeEntityName(context, N); if (N.getKind() == CAstEntity.SCRIPT_ENTITY) { ((JavaScriptLoader)loader).defineScriptType("L"+fnName, N.getPosition()); } else if (N.getKind() == CAstEntity.FUNCTION_ENTITY) { ((JavaScriptLoader)loader).defineFunctionType("L"+fnName, N.getPosition()); } else { Assertions.UNREACHABLE(); } } protected void defineFunction(CAstEntity N, WalkContext definingContext, AbstractCFG cfg, SymbolTable symtab, boolean hasCatchBlock, TypeReference[][] caughtTypes, boolean hasMonitorOp, AstLexicalInformation LI, DebuggingInformation debugInfo) { if (DEBUG) System.err.println(("\n\nAdding code for " + N)); String fnName = composeEntityName(definingContext, N); if (DEBUG) System.err.println(cfg); symtab.getConstant("arguments"); symtab.getConstant("length"); for(int i = 0; i < 20; i++) { symtab.getConstant(i); } ((JavaScriptLoader)loader).defineCodeBodyCode("L"+fnName, cfg, symtab, hasCatchBlock, caughtTypes, hasMonitorOp, LI, debugInfo); } protected void doThrow(WalkContext context, int exception) { context.cfg().addInstruction(insts.ThrowInstruction(exception)); } 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: 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()); context.cfg().newBlock( true ); if (context.getControlFlow().getTarget(call, null) != null) context.cfg().addPreEdge(call, context.getControlFlow().getTarget(call, null), true); else context.cfg().addPreEdgeToExit( call, true ); } protected void doNewObject(WalkContext context, CAstNode newNode, int result, Object type, int[] arguments) { assert arguments == null; TypeReference typeRef = TypeReference.findOrCreate( JavaScriptTypes.jsLoader, TypeName.string2TypeName( "L" + type )); context.cfg().addInstruction( insts.NewInstruction( result, NewSiteReference.make( context.cfg().getCurrentInstruction(), typeRef))); } protected void doMaterializeFunction(CAstNode n, WalkContext context, int result, int exception, CAstEntity fn) { int nm = context.currentScope().getConstantValue("L"+composeEntityName(context, fn)); int tmp = super.doGlobalRead(n, context, "Function"); context.cfg().addInstruction( ((JSInstructionFactory)insts).Invoke(tmp, result, new int[]{ nm }, exception, new JSCallSiteReference( JavaScriptMethods.ctorReference, context.cfg().getCurrentInstruction()))); } public void doArrayRead(WalkContext context, int result, int arrayValue, CAstNode arrayRef, int[] dimValues) { Assertions.UNREACHABLE("JSAstTranslator.doArrayRead() called!"); } public void doArrayWrite(WalkContext context, int arrayValue, CAstNode arrayRef, int[] dimValues, int rval) { Assertions.UNREACHABLE("JSAstTranslator.doArrayWrite() called!"); } protected void doFieldRead(WalkContext context, int result, int receiver, CAstNode elt, CAstNode parent) { this.visit(elt, context, this); int x = context.currentScope().allocateTempValue(); context.cfg().addInstruction(((JSInstructionFactory)insts).AssignInstruction(x, receiver)); 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, getValue(elt) )); } } 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(); 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, getValue(elt), rval)); } } private void doPrimitiveNew(WalkContext context, int resultVal, String typeName) { doNewObject(context, null, resultVal, typeName + "Object", null); int rval = context.currentScope().getConstantValue(typeName); context.currentScope().getConstantValue("class"); context.cfg().addInstruction( ((JSInstructionFactory)insts).PutInstruction(resultVal, rval, "class")); } 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()); } } 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, getValue(f))); } } protected boolean visitInstanceOf(CAstNode n, Context c, CAstVisitor visitor) { WalkContext context = (WalkContext)c; int result = context.currentScope().allocateTempValue(); setValue(n, result); return false; } protected void leaveInstanceOf(CAstNode n, Context c, CAstVisitor visitor) { WalkContext context = (WalkContext)c; int result = getValue(n); visit(n.getChild(0), context, visitor); int value = getValue(n.getChild(0)); visit(n.getChild(1), context, visitor); int type = getValue(n.getChild(1)); context.cfg().addInstruction(new JavaScriptInstanceOf(result, value, type)); } protected void doPrologue(WalkContext context) { super.doPrologue(context); int tempVal = context.currentScope().allocateTempValue(); doNewObject(context, null, tempVal, "Array", null); CAstSymbol args = new CAstSymbolImpl("arguments"); context.currentScope().declare(args, tempVal); context.cfg().addInstruction( ((JSInstructionFactory)insts).PutInstruction(1, tempVal, "arguments")); } protected boolean doVisit(CAstNode n, Context cntxt, CAstVisitor 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 = getValue(n.getChild(0)); context.cfg().addInstruction( ((JSInstructionFactory)insts).TypeOfInstruction(result, ref)); setValue(n, result); return true; } case JavaScriptCAstNode.ENTER_WITH: case JavaScriptCAstNode.EXIT_WITH: { this.visit(n.getChild(0), context, this); int ref = getValue(n.getChild(0)); context.cfg().addInstruction(((JSInstructionFactory)insts).WithRegion(ref, n.getKind()==JavaScriptCAstNode.ENTER_WITH)); return true; } default: { return false; } } } }