com.ibm.wala.cast.js.translator.JSAstTranslator Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of com.ibm.wala.cast.js Show documentation
Show all versions of com.ibm.wala.cast.js Show documentation
T. J. Watson Libraries for Analysis
The newest version!
/*
* 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 com.ibm.wala.cast.ir.translator.AstTranslator;
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.ssa.PrototypeLookup;
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.loader.DynamicCallSiteReference;
import com.ibm.wala.cast.tree.CAstEntity;
import com.ibm.wala.cast.tree.CAstNode;
import com.ibm.wala.cast.tree.CAstSourcePositionMap.Position;
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.cfg.IBasicBlock;
import com.ibm.wala.classLoader.NewSiteReference;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.ssa.SymbolTable;
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 java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
/** Specialization of {@link AstTranslator} for JavaScript. */
public class JSAstTranslator extends AstTranslator {
private static final boolean DEBUG = false;
public JSAstTranslator(JavaScriptLoader loader) {
super(loader);
}
private static boolean isPrologueScript(WalkContext context) {
return JavaScriptLoader.bootstrapFileNames.contains(context.getModule().getName());
}
@Override
protected boolean useDefaultInitValues() {
return false;
}
@Override
protected boolean hasImplicitGlobals() {
return true;
}
@Override
protected boolean treatGlobalsAsLexicallyScoped() {
return false;
}
@Override
protected TypeReference defaultCatchType() {
return JavaScriptTypes.Root;
}
@Override
protected TypeReference makeType(CAstType type) {
assert "Any".equals(type.getName());
return JavaScriptTypes.Root;
}
@Override
protected boolean ignoreName(String name) {
return super.ignoreName(name) || name.endsWith(" temp");
}
@Override
protected String[] makeNameMap(CAstEntity n, Set scopes, SSAInstruction[] insts) {
String[] names = super.makeNameMap(n, scopes, insts);
for (SSAInstruction inst : insts) {
if (inst instanceof PrototypeLookup) {
if (names[inst.getUse(0)] != null) {
names[inst.getDef()] = names[inst.getUse(0)];
}
}
}
return names;
}
/**
* generate an instruction that checks if readVn is undefined and throws an exception if it isn't
*/
private void addDefinedCheck(CAstNode n, WalkContext context, int readVn) {
context.cfg().addPreNode(n);
context
.cfg()
.addInstruction(
((JSInstructionFactory) insts)
.CheckReference(context.cfg().getCurrentInstruction(), readVn));
CAstNode target = context.getControlFlow().getTarget(n, JavaScriptTypes.Root);
context.cfg().addPreNode(n, context.getUnwindState());
context.cfg().newBlock(true);
if (target != null) {
context.cfg().addPreEdge(n, target, true);
} else {
context.cfg().addPreEdgeToExit(n, true);
}
}
@Override
protected int doLexicallyScopedRead(
CAstNode n, WalkContext context, String name, TypeReference type) {
int readVn = super.doLexicallyScopedRead(n, context, name, type);
// should get an exception if name is undefined
addDefinedCheck(n, context, readVn);
return readVn;
}
@Override
protected int doGlobalRead(CAstNode n, WalkContext context, String name, TypeReference type) {
int readVn = super.doGlobalRead(n, context, name, type);
// add a check if name is undefined, unless we're reading the value 'undefined'
if (n != null
&& !("undefined".equals(name)
|| "$$undefined".equals(name)
|| "__WALA__int3rnal__global".equals(name))) {
addDefinedCheck(n, context, readVn);
}
return readVn;
}
@Override
protected boolean defineType(CAstEntity type, WalkContext wc) {
Assertions.UNREACHABLE(
"JavaScript doesn't have types. I suggest you look elsewhere for your amusement.");
return false;
}
@Override
protected void defineField(CAstEntity topEntity, WalkContext wc, CAstEntity n) {
Assertions.UNREACHABLE("JavaScript doesn't have fields");
}
@Override
protected String composeEntityName(WalkContext parent, CAstEntity f) {
if (f.getKind() == CAstEntity.SCRIPT_ENTITY) return f.getName();
else return parent.getName() + '/' + f.getName();
}
@Override
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(), N, context);
} else if (N.getKind() == CAstEntity.FUNCTION_ENTITY) {
((JavaScriptLoader) loader).defineFunctionType('L' + fnName, N.getPosition(), N, context);
} else {
Assertions.UNREACHABLE();
}
}
@Override
protected void defineFunction(
CAstEntity N,
WalkContext definingContext,
AbstractCFG> cfg,
SymbolTable symtab,
boolean hasCatchBlock,
Map, 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);
((JavaScriptLoader) loader)
.defineCodeBodyCode(
'L' + fnName, cfg, symtab, hasCatchBlock, caughtTypes, hasMonitorOp, LI, debugInfo);
}
@Override
protected void doThrow(WalkContext context, int exception) {
context
.cfg()
.addInstruction(insts.ThrowInstruction(context.cfg().getCurrentInstruction(), exception));
}
@Override
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(
context.cfg().getCurrentInstruction(),
receiver,
result,
arguments,
exception,
new DynamicCallSiteReference(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);
}
@Override
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(
context.cfg().getCurrentInstruction(),
result,
NewSiteReference.make(context.cfg().getCurrentInstruction(), typeRef)));
}
@Override
protected void doMaterializeFunction(
CAstNode n, WalkContext context, int result, int exception, CAstEntity fn) {
int nm = context.currentScope().getConstantValue('L' + composeEntityName(context, fn));
// "Function" is the name we use to model the constructor of function values
int tmp = super.doGlobalRead(n, context, "Function", JavaScriptTypes.Function);
Position old = null;
if (n.getKind() == CAstNode.FUNCTION_STMT) {
// For function statements, set the current position (used for the constructor invocation
// instruction added below) to be the location of the statement itself.
old = getCurrentPosition();
CAstEntity entity = (CAstEntity) n.getChild(0).getValue();
currentPosition = entity.getPosition();
}
try {
context
.cfg()
.addInstruction(
((JSInstructionFactory) insts)
.Invoke(
context.cfg().getCurrentInstruction(),
tmp,
result,
new int[] {nm},
exception,
new DynamicCallSiteReference(
JavaScriptMethods.ctorReference, context.cfg().getCurrentInstruction())));
} finally {
// Reset the current position if it was temporarily updated for a function statement
if (old != null) {
currentPosition = old;
}
}
}
@Override
public void doArrayRead(
WalkContext context, int result, int arrayValue, CAstNode arrayRef, int[] dimValues) {
Assertions.UNREACHABLE("JSAstTranslator.doArrayRead() called!");
}
@Override
public void doArrayWrite(
WalkContext context, int arrayValue, CAstNode arrayRef, int[] dimValues, int rval) {
Assertions.UNREACHABLE("JSAstTranslator.doArrayWrite() called!");
}
@Override
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(context.cfg().getCurrentInstruction(), x, receiver));
context
.cfg()
.addInstruction(
((JSInstructionFactory) insts)
.PrototypeLookup(context.cfg().getCurrentInstruction(), 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(context.cfg().getCurrentInstruction(), result, x, field));
} else {
context
.cfg()
.addInstruction(
((JSInstructionFactory) insts)
.PropertyRead(
context.cfg().getCurrentInstruction(), result, x, context.getValue(elt)));
}
// generate code to handle read of property from null or undefined
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);
}
@Override
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(context.cfg().getCurrentInstruction(), receiver, rval));
return;
}
}
/*
} else {
context.currentScope().getConstantValue(field);
SSAPutInstruction put = ((JSInstructionFactory) insts).PutInstruction(context.cfg().getCurrentInstruction(), 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(
context.cfg().getCurrentInstruction(), receiver, context.getValue(elt), rval));
context.cfg().addPreNode(parent, context.getUnwindState());
// generate code to handle read of property from null or undefined
context.cfg().newBlock(true);
if (context.getControlFlow().getTarget(parent, JavaScriptTypes.TypeError) != null)
context
.cfg()
.addPreEdge(
parent, context.getControlFlow().getTarget(parent, JavaScriptTypes.TypeError), true);
else context.cfg().addPreEdgeToExit(parent, true);
// }
}
private void doPrimitiveNew(WalkContext context, int resultVal, String typeName) {
doNewObject(context, null, resultVal, typeName + "Object", null);
// set the class property of the new object
int rval = context.currentScope().getConstantValue(typeName);
context.currentScope().getConstantValue("class");
context
.cfg()
.addInstruction(
((JSInstructionFactory) insts)
.PutInstruction(context.cfg().getCurrentInstruction(), resultVal, rval, "class"));
}
@Override
protected void doPrimitive(int resultVal, WalkContext context, CAstNode primitiveCall) {
try {
String name = (String) primitiveCall.getChild(0).getValue();
switch (name) {
case "GlobalNaN":
context
.cfg()
.addInstruction(
((JSInstructionFactory) insts)
.AssignInstruction(
context.cfg().getCurrentInstruction(),
resultVal,
context.currentScope().getConstantValue(Float.NaN)));
break;
case "GlobalInfinity":
context
.cfg()
.addInstruction(
((JSInstructionFactory) insts)
.AssignInstruction(
context.cfg().getCurrentInstruction(),
resultVal,
context.currentScope().getConstantValue(Float.POSITIVE_INFINITY)));
break;
case "MathE":
context
.cfg()
.addInstruction(
((JSInstructionFactory) insts)
.AssignInstruction(
context.cfg().getCurrentInstruction(),
resultVal,
context.currentScope().getConstantValue(Math.E)));
break;
case "MathPI":
context
.cfg()
.addInstruction(
((JSInstructionFactory) insts)
.AssignInstruction(
context.cfg().getCurrentInstruction(),
resultVal,
context.currentScope().getConstantValue(Math.PI)));
break;
case "MathSQRT1_2":
context
.cfg()
.addInstruction(
((JSInstructionFactory) insts)
.AssignInstruction(
context.cfg().getCurrentInstruction(),
resultVal,
context.currentScope().getConstantValue(Math.sqrt(.5))));
break;
case "MathSQRT2":
context
.cfg()
.addInstruction(
((JSInstructionFactory) insts)
.AssignInstruction(
context.cfg().getCurrentInstruction(),
resultVal,
context.currentScope().getConstantValue(Math.sqrt(2))));
break;
case "MathLN2":
context
.cfg()
.addInstruction(
((JSInstructionFactory) insts)
.AssignInstruction(
context.cfg().getCurrentInstruction(),
resultVal,
context.currentScope().getConstantValue(Math.log(2))));
break;
case "MathLN10":
context
.cfg()
.addInstruction(
((JSInstructionFactory) insts)
.AssignInstruction(
context.cfg().getCurrentInstruction(),
resultVal,
context.currentScope().getConstantValue(Math.log(10))));
break;
case "NewObject":
doNewObject(context, null, resultVal, "Object", null);
break;
case "NewArray":
doNewObject(context, null, resultVal, "Array", null);
break;
case "NewString":
doPrimitiveNew(context, resultVal, "String");
break;
case "NewNumber":
doPrimitiveNew(context, resultVal, "Number");
break;
case "NewRegExp":
doPrimitiveNew(context, resultVal, "RegExp");
break;
case "NewFunction":
doNewObject(context, null, resultVal, "Function", null);
break;
case "NewUndefined":
doNewObject(context, null, resultVal, "Undefined", null);
break;
default:
context
.cfg()
.addInstruction(
((JSInstructionFactory) insts)
.AssignInstruction(
context.cfg().getCurrentInstruction(),
resultVal,
context.currentScope().getConstantValue(null)));
break;
}
} catch (ClassCastException e) {
throw new RuntimeException(
"Cannot translate primitive " + primitiveCall.getChild(0).getValue(), e);
}
}
@Override
protected boolean visitInstanceOf(CAstNode n, WalkContext c, CAstVisitor visitor) {
WalkContext context = c;
int result = context.currentScope().allocateTempValue();
context.setValue(n, result);
return false;
}
@Override
protected void leaveInstanceOf(CAstNode n, WalkContext c, CAstVisitor visitor) {
WalkContext context = 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(context.cfg().getCurrentInstruction(), result, value, type));
}
@Override
protected void doPrologue(WalkContext context) {
super.doPrologue(context);
int tempVal = context.currentScope().allocateTempValue();
doNewObject(context, null, tempVal, "Array", null);
CAstSymbol args = new CAstSymbolImpl("arguments", Any);
context.currentScope().declare(args, tempVal);
// context.cfg().addInstruction(((JSInstructionFactory)insts).PutInstruction(context.cfg().getCurrentInstruction(), 1, tempVal, "arguments"));
}
@Override
protected boolean doVisit(CAstNode n, WalkContext context, CAstVisitor visitor) {
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(context.cfg().getCurrentInstruction(), 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(
context.cfg().getCurrentInstruction(),
ref,
n.getKind() == JavaScriptCAstNode.ENTER_WITH));
return true;
}
default:
{
return false;
}
}
}
public static final CAstType Any =
new CAstType() {
@Override
public String getName() {
return "Any";
}
@Override
public Collection getSupertypes() {
return Collections.emptySet();
}
};
@Override
protected CAstType topType() {
return Any;
}
@Override
protected CAstType exceptionType() {
return Any;
}
@Override
protected Position[] getParameterPositions(CAstEntity e) {
if (e.getKind() == CAstEntity.SCRIPT_ENTITY) {
return new Position[0];
} else {
Position[] ps = new Position[e.getArgumentCount()];
for (int i = 2; i < e.getArgumentCount(); i++) {
ps[i] = e.getPosition(i - 2);
}
return ps;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy