aboutsummaryrefslogtreecommitdiff
path: root/src/jdk/nashorn/internal/codegen/Attr.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/jdk/nashorn/internal/codegen/Attr.java')
-rw-r--r--src/jdk/nashorn/internal/codegen/Attr.java667
1 files changed, 329 insertions, 338 deletions
diff --git a/src/jdk/nashorn/internal/codegen/Attr.java b/src/jdk/nashorn/internal/codegen/Attr.java
index 9b17c47f..4e47892f 100644
--- a/src/jdk/nashorn/internal/codegen/Attr.java
+++ b/src/jdk/nashorn/internal/codegen/Attr.java
@@ -29,11 +29,13 @@ import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS;
import static jdk.nashorn.internal.codegen.CompilerConstants.CALLEE;
import static jdk.nashorn.internal.codegen.CompilerConstants.EXCEPTION_PREFIX;
import static jdk.nashorn.internal.codegen.CompilerConstants.ITERATOR_PREFIX;
+import static jdk.nashorn.internal.codegen.CompilerConstants.LITERAL_PREFIX;
import static jdk.nashorn.internal.codegen.CompilerConstants.RETURN;
import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
import static jdk.nashorn.internal.codegen.CompilerConstants.SWITCH_TAG_PREFIX;
import static jdk.nashorn.internal.codegen.CompilerConstants.THIS;
import static jdk.nashorn.internal.codegen.CompilerConstants.VARARGS;
+import static jdk.nashorn.internal.ir.Symbol.IS_CONSTANT;
import static jdk.nashorn.internal.ir.Symbol.IS_FUNCTION_SELF;
import static jdk.nashorn.internal.ir.Symbol.IS_GLOBAL;
import static jdk.nashorn.internal.ir.Symbol.IS_INTERNAL;
@@ -73,8 +75,10 @@ import jdk.nashorn.internal.ir.PropertyNode;
import jdk.nashorn.internal.ir.ReturnNode;
import jdk.nashorn.internal.ir.RuntimeNode;
import jdk.nashorn.internal.ir.RuntimeNode.Request;
+import jdk.nashorn.internal.ir.Statement;
import jdk.nashorn.internal.ir.SwitchNode;
import jdk.nashorn.internal.ir.Symbol;
+import jdk.nashorn.internal.ir.TemporarySymbols;
import jdk.nashorn.internal.ir.TernaryNode;
import jdk.nashorn.internal.ir.TryNode;
import jdk.nashorn.internal.ir.UnaryNode;
@@ -90,7 +94,6 @@ import jdk.nashorn.internal.runtime.ECMAException;
import jdk.nashorn.internal.runtime.JSType;
import jdk.nashorn.internal.runtime.Property;
import jdk.nashorn.internal.runtime.PropertyMap;
-import jdk.nashorn.internal.runtime.ScriptFunction;
import jdk.nashorn.internal.runtime.ScriptObject;
/**
@@ -129,13 +132,16 @@ final class Attr extends NodeOperatorVisitor {
private static final DebugLogger LOG = new DebugLogger("attr");
private static final boolean DEBUG = LOG.isEnabled();
+ private final TemporarySymbols temporarySymbols;
+
/**
* Constructor.
*/
- Attr() {
- localDefs = new ArrayDeque<>();
- localUses = new ArrayDeque<>();
- returnTypes = new ArrayDeque<>();
+ Attr(final TemporarySymbols temporarySymbols) {
+ this.temporarySymbols = temporarySymbols;
+ this.localDefs = new ArrayDeque<>();
+ this.localUses = new ArrayDeque<>();
+ this.returnTypes = new ArrayDeque<>();
}
@Override
@@ -150,67 +156,50 @@ final class Attr extends NodeOperatorVisitor {
@Override
public Node leaveAccessNode(final AccessNode accessNode) {
- ensureSymbol(Type.OBJECT, accessNode); //While Object type is assigned here, Access Specialization in FinalizeTypes may narrow this
- end(accessNode);
- return accessNode;
+ //While Object type is assigned here, Access Specialization in FinalizeTypes may narrow this, that
+ //is why we can't set the access node base to be an object here, that will ruin access specialization
+ //for example for a.x | 17.
+ return end(ensureSymbol(Type.OBJECT, accessNode));
}
- private void enterFunctionBody() {
+ private void initFunctionWideVariables(final FunctionNode functionNode, final Block body) {
+ initCompileConstant(CALLEE, body, IS_PARAM | IS_INTERNAL, FunctionNode.FUNCTION_TYPE);
+ initCompileConstant(THIS, body, IS_PARAM | IS_THIS, Type.OBJECT);
- final FunctionNode functionNode = getLexicalContext().getCurrentFunction();
- final Block body = getLexicalContext().getCurrentBlock();
- initCallee(body);
- initThis(body);
if (functionNode.isVarArg()) {
- initVarArg(body, functionNode.needsArguments());
+ initCompileConstant(VARARGS, body, IS_PARAM | IS_INTERNAL, Type.OBJECT_ARRAY);
+ if (functionNode.needsArguments()) {
+ initCompileConstant(ARGUMENTS, body, IS_VAR | IS_INTERNAL, Type.typeFor(ScriptObject.class));
+ addLocalDef(ARGUMENTS.symbolName());
+ }
}
initParameters(functionNode, body);
- initScope(body);
- initReturn(body);
-
- if (functionNode.isProgram()) {
- initFromPropertyMap(body);
- } else if(!functionNode.isDeclared()) {
- // It's neither declared nor program - it's a function expression then; assign it a self-symbol.
-
- if (functionNode.getSymbol() != null) {
- // a temporary left over from an earlier pass when the function was lazy
- assert functionNode.getSymbol().isTemp();
- // remove it
- functionNode.setSymbol(null);
- }
- final boolean anonymous = functionNode.isAnonymous();
- final String name = anonymous ? null : functionNode.getIdent().getName();
- if (anonymous || body.getExistingSymbol(name) != null) {
- // The function is either anonymous, or another local identifier already trumps its name on entry:
- // either it has the same name as one of its parameters, or is named "arguments" and also references the
- // "arguments" identifier in its body.
- ensureSymbol(functionNode, Type.typeFor(ScriptFunction.class), functionNode);
- } else {
- final Symbol selfSymbol = defineSymbol(body, name, IS_VAR | IS_FUNCTION_SELF, functionNode);
- assert selfSymbol.isFunctionSelf();
- newType(selfSymbol, Type.OBJECT);
- }
- }
+ initCompileConstant(SCOPE, body, IS_VAR | IS_INTERNAL, Type.typeFor(ScriptObject.class));
+ initCompileConstant(RETURN, body, IS_VAR | IS_INTERNAL, Type.OBJECT);
+ }
- /*
- * This pushes all declarations (except for non-statements, i.e. for
- * node temporaries) to the top of the function scope. This way we can
- * get around problems like
- *
- * while (true) {
- * break;
- * if (true) {
- * var s;
- * }
- * }
- *
- * to an arbitrary nesting depth.
- *
- * @see NASHORN-73
- */
+ /**
+ * This pushes all declarations (except for non-statements, i.e. for
+ * node temporaries) to the top of the function scope. This way we can
+ * get around problems like
+ *
+ * while (true) {
+ * break;
+ * if (true) {
+ * var s;
+ * }
+ * }
+ *
+ * to an arbitrary nesting depth.
+ *
+ * see NASHORN-73
+ *
+ * @param functionNode the FunctionNode we are entering
+ * @param body the body of the FunctionNode we are entering
+ */
+ private void acceptDeclarations(final FunctionNode functionNode, final Block body) {
// This visitor will assign symbol to all declared variables, except function declarations (which are taken care
// in a separate step above) and "var" declarations in for loop initializers.
body.accept(new NodeOperatorVisitor() {
@@ -220,27 +209,52 @@ final class Attr extends NodeOperatorVisitor {
}
@Override
- public boolean enterVarNode(final VarNode varNode) {
-
+ public Node leaveVarNode(final VarNode varNode) {
// any declared symbols that aren't visited need to be typed as well, hence the list
-
if (varNode.isStatement()) {
-
- final IdentNode ident = varNode.getName();
- final Symbol symbol = defineSymbol(body, ident.getName(), IS_VAR, new IdentNode(ident));
+ final IdentNode ident = varNode.getName();
+ final Symbol symbol = defineSymbol(body, ident.getName(), IS_VAR);
functionNode.addDeclaredSymbol(symbol);
if (varNode.isFunctionDeclaration()) {
newType(symbol, FunctionNode.FUNCTION_TYPE);
}
+ return varNode.setName((IdentNode)ident.setSymbol(getLexicalContext(), symbol));
}
- return false;
+ return varNode;
}
});
}
+ private void enterFunctionBody() {
+
+ final FunctionNode functionNode = getLexicalContext().getCurrentFunction();
+ final Block body = getLexicalContext().getCurrentBlock();
+
+ initFunctionWideVariables(functionNode, body);
+
+ if (functionNode.isProgram()) {
+ initFromPropertyMap(body);
+ } else if (!functionNode.isDeclared()) {
+ // It's neither declared nor program - it's a function expression then; assign it a self-symbol.
+ assert functionNode.getSymbol() == null;
+
+ final boolean anonymous = functionNode.isAnonymous();
+ final String name = anonymous ? null : functionNode.getIdent().getName();
+ if (!(anonymous || body.getExistingSymbol(name) != null)) {
+ assert !anonymous && name != null;
+ newType(defineSymbol(body, name, IS_VAR | IS_FUNCTION_SELF), Type.OBJECT);
+ }
+ }
+
+ acceptDeclarations(functionNode, body);
+ }
+
@Override
public boolean enterBlock(final Block block) {
start(block);
+ //ensure that we don't use information from a previous compile. This is very ugly TODO
+ //the symbols in the block should really be stateless
+ block.clearSymbols();
if (getLexicalContext().isFunctionBody()) {
enterFunctionBody();
@@ -257,14 +271,13 @@ final class Attr extends NodeOperatorVisitor {
}
@Override
- public Node leaveCallNode(final CallNode callNode) {
- ensureSymbol(callNode.getType(), callNode);
- return end(callNode);
+ public boolean enterCallNode(final CallNode callNode) {
+ return start(callNode);
}
@Override
- public boolean enterCallNode(final CallNode callNode) {
- return start(callNode);
+ public Node leaveCallNode(final CallNode callNode) {
+ return end(ensureSymbol(callNode.getType(), callNode));
}
@Override
@@ -275,23 +288,31 @@ final class Attr extends NodeOperatorVisitor {
start(catchNode);
// define block-local exception variable
- final Symbol def = defineSymbol(block, exception.getName(), IS_VAR | IS_LET, exception);
+ final Symbol def = defineSymbol(block, exception.getName(), IS_VAR | IS_LET);
newType(def, Type.OBJECT);
addLocalDef(exception.getName());
return true;
}
+ @Override
+ public Node leaveCatchNode(final CatchNode catchNode) {
+ final IdentNode exception = catchNode.getException();
+ final Block block = getLexicalContext().getCurrentBlock();
+ final Symbol symbol = findSymbol(block, exception.getName());
+ assert symbol != null;
+ return end(catchNode.setException((IdentNode)exception.setSymbol(getLexicalContext(), symbol)));
+ }
+
/**
* Declare the definition of a new symbol.
*
* @param name Name of symbol.
* @param symbolFlags Symbol flags.
- * @param node Defining Node.
*
* @return Symbol for given name or null for redefinition.
*/
- private Symbol defineSymbol(final Block block, final String name, final int symbolFlags, final Node node) {
+ private Symbol defineSymbol(final Block block, final String name, final int symbolFlags) {
int flags = symbolFlags;
Symbol symbol = findSymbol(block, name); // Locate symbol.
@@ -337,7 +358,7 @@ final class Attr extends NodeOperatorVisitor {
// Create and add to appropriate block.
symbol = new Symbol(name, flags);
- symbolBlock.putSymbol(name, symbol);
+ symbolBlock.putSymbol(getLexicalContext(), symbol);
if ((flags & Symbol.KINDMASK) != IS_GLOBAL) {
symbol.setNeedsSlot(true);
@@ -346,10 +367,6 @@ final class Attr extends NodeOperatorVisitor {
symbol.setFlags(flags);
}
- if (node != null) {
- node.setSymbol(symbol);
- }
-
return symbol;
}
@@ -357,30 +374,22 @@ final class Attr extends NodeOperatorVisitor {
public boolean enterFunctionNode(final FunctionNode functionNode) {
start(functionNode, false);
+ if (functionNode.isLazy()) {
+ return false;
+ }
+
+ //an outermost function in our lexical context that is not a program (runScript)
+ //is possible - it is a function being compiled lazily
if (functionNode.isDeclared()) {
final Iterator<Block> blocks = getLexicalContext().getBlocks();
if (blocks.hasNext()) {
- defineSymbol(
- blocks.next(),
- functionNode.getIdent().getName(),
- IS_VAR,
- functionNode);
- } else {
- // Q: What's an outermost function in a lexical context that is not a program?
- // A: It's a function being compiled lazily!
- assert getLexicalContext().getOutermostFunction() == functionNode && !functionNode.isProgram();
+ defineSymbol(blocks.next(), functionNode.getIdent().getName(), IS_VAR);
}
}
- if (functionNode.isLazy()) {
- LOG.info("LAZY: ", functionNode.getName(), " => Promoting to OBJECT");
- ensureSymbol(getLexicalContext().getCurrentFunction(), Type.OBJECT, functionNode);
- end(functionNode);
- return false;
- }
-
returnTypes.push(functionNode.getReturnType());
pushLocalsFunction();
+
return true;
}
@@ -390,9 +399,30 @@ final class Attr extends NodeOperatorVisitor {
final LexicalContext lc = getLexicalContext();
+ final Block body = newFunctionNode.getBody();
+
+ //look for this function in the parent block
+ if (functionNode.isDeclared()) {
+ final Iterator<Block> blocks = getLexicalContext().getBlocks();
+ if (blocks.hasNext()) {
+ newFunctionNode = (FunctionNode)newFunctionNode.setSymbol(lc, findSymbol(blocks.next(), functionNode.getIdent().getName()));
+ }
+ } else if (!functionNode.isProgram()) {
+ final boolean anonymous = functionNode.isAnonymous();
+ final String name = anonymous ? null : functionNode.getIdent().getName();
+ if (anonymous || body.getExistingSymbol(name) != null) {
+ newFunctionNode = (FunctionNode)ensureSymbol(lc, FunctionNode.FUNCTION_TYPE, newFunctionNode);
+ } else {
+ assert name != null;
+ final Symbol self = body.getExistingSymbol(name);
+ assert self != null && self.isFunctionSelf();
+ newFunctionNode = (FunctionNode)newFunctionNode.setSymbol(lc, body.getExistingSymbol(name));
+ }
+ }
+
//unknown parameters are promoted to object type.
- finalizeParameters(newFunctionNode);
- finalizeTypes(newFunctionNode);
+ newFunctionNode = finalizeParameters(newFunctionNode);
+ newFunctionNode = finalizeTypes(newFunctionNode);
for (final Symbol symbol : newFunctionNode.getDeclaredSymbols()) {
if (symbol.getSymbolType().isUnknown()) {
symbol.setType(Type.OBJECT);
@@ -400,8 +430,6 @@ final class Attr extends NodeOperatorVisitor {
}
}
- final Block body = newFunctionNode.getBody();
-
if (newFunctionNode.hasLazyChildren()) {
//the final body has already been assigned as we have left the function node block body by now
objectifySymbols(body);
@@ -409,9 +437,9 @@ final class Attr extends NodeOperatorVisitor {
if (body.getFlag(Block.NEEDS_SELF_SYMBOL)) {
final IdentNode callee = compilerConstant(CALLEE);
- final VarNode selfInit =
+ VarNode selfInit =
new VarNode(
- newFunctionNode.getSource(),
+ newFunctionNode.getLineNumber(),
newFunctionNode.getToken(),
newFunctionNode.getFinish(),
newFunctionNode.getIdent(),
@@ -419,8 +447,7 @@ final class Attr extends NodeOperatorVisitor {
LOG.info("Accepting self symbol init ", selfInit, " for ", newFunctionNode.getName());
- final List<Node> newStatements = new ArrayList<>();
- newStatements.add(selfInit);
+ final List<Statement> newStatements = new ArrayList<>();
assert callee.getSymbol() != null && callee.getSymbol().hasSlot();
final IdentNode name = selfInit.getName();
@@ -428,9 +455,10 @@ final class Attr extends NodeOperatorVisitor {
assert nameSymbol != null;
- name.setSymbol(nameSymbol);
- selfInit.setSymbol(nameSymbol);
+ selfInit = selfInit.setName((IdentNode)name.setSymbol(lc, nameSymbol));
+ selfInit = (VarNode)selfInit.setSymbol(lc, nameSymbol);
+ newStatements.add(selfInit);
newStatements.addAll(body.getStatements());
newFunctionNode = newFunctionNode.setBody(lc, body.setStatements(lc, newStatements));
}
@@ -447,34 +475,32 @@ final class Attr extends NodeOperatorVisitor {
end(newFunctionNode, false);
- return newFunctionNode; //.setFlag(lc, lc.getFlags(functionNode));
+ return newFunctionNode;
}
@Override
public Node leaveCONVERT(final UnaryNode unaryNode) {
assert false : "There should be no convert operators in IR during Attribution";
- end(unaryNode);
- return unaryNode;
+ return end(unaryNode);
}
@Override
- public boolean enterIdentNode(final IdentNode identNode) {
+ public Node leaveIdentNode(final IdentNode identNode) {
final String name = identNode.getName();
start(identNode);
+ final LexicalContext lc = getLexicalContext();
+
if (identNode.isPropertyName()) {
// assign a pseudo symbol to property name
final Symbol pseudoSymbol = pseudoSymbol(name);
LOG.info("IdentNode is property name -> assigning pseudo symbol ", pseudoSymbol);
LOG.unindent();
- identNode.setSymbol(pseudoSymbol);
- return false;
+ return end(identNode.setSymbol(lc, pseudoSymbol));
}
- final LexicalContext lc = getLexicalContext();
- final Block block = lc.getCurrentBlock();
- final Symbol oldSymbol = identNode.getSymbol();
+ final Block block = lc.getCurrentBlock();
Symbol symbol = findSymbol(block, name);
@@ -495,12 +521,11 @@ final class Attr extends NodeOperatorVisitor {
}
}
- identNode.setSymbol(symbol);
// if symbol is non-local or we're in a with block, we need to put symbol in scope (if it isn't already)
maybeForceScope(symbol);
} else {
LOG.info("No symbol exists. Declare undefined: ", symbol);
- symbol = defineSymbol(block, name, IS_GLOBAL, identNode);
+ symbol = defineSymbol(block, name, IS_GLOBAL);
// we have never seen this before, it can be undefined
newType(symbol, Type.OBJECT); // TODO unknown -we have explicit casts anyway?
symbol.setCanBeUndefined();
@@ -509,14 +534,12 @@ final class Attr extends NodeOperatorVisitor {
setBlockScope(name, symbol);
- if (symbol != oldSymbol && !identNode.isInitializedHere()) {
+ if (!identNode.isInitializedHere()) {
symbol.increaseUseCount();
}
addLocalUse(identNode.getName());
- end(identNode);
-
- return false;
+ return end(identNode.setSymbol(lc, symbol));
}
/**
@@ -525,7 +548,7 @@ final class Attr extends NodeOperatorVisitor {
* @param symbol the symbol that might be scoped
*/
private void maybeForceScope(final Symbol symbol) {
- if(!symbol.isScope() && symbolNeedsToBeScope(symbol)) {
+ if (!symbol.isScope() && symbolNeedsToBeScope(symbol)) {
Symbol.setSymbolIsScope(getLexicalContext(), symbol);
}
}
@@ -612,11 +635,11 @@ final class Attr extends NodeOperatorVisitor {
private Symbol findSymbol(final Block block, final String name) {
// Search up block chain to locate symbol.
- for(final Iterator<Block> blocks = getLexicalContext().getBlocks(block); blocks.hasNext();) {
+ for (final Iterator<Block> blocks = getLexicalContext().getBlocks(block); blocks.hasNext();) {
// Find name.
final Symbol symbol = blocks.next().getExistingSymbol(name);
// If found then we are good.
- if(symbol != null) {
+ if (symbol != null) {
return symbol;
}
}
@@ -625,39 +648,19 @@ final class Attr extends NodeOperatorVisitor {
@Override
public Node leaveIndexNode(final IndexNode indexNode) {
- ensureSymbol(Type.OBJECT, indexNode); //TODO
- return indexNode;
+ return end(ensureSymbol(Type.OBJECT, indexNode));
}
@SuppressWarnings("rawtypes")
@Override
- public boolean enterLiteralNode(final LiteralNode literalNode) {
- try {
- start(literalNode);
- assert !literalNode.isTokenType(TokenType.THIS) : "tokentype for " + literalNode + " is this"; //guard against old dead code case. literal nodes should never inherit tokens
-
- if (literalNode instanceof ArrayLiteralNode) {
- final ArrayLiteralNode arrayLiteralNode = (ArrayLiteralNode)literalNode;
- final Node[] array = arrayLiteralNode.getValue();
-
- for (int i = 0; i < array.length; i++) {
- final Node element = array[i];
- if (element != null) {
- array[i] = element.accept(this);
- }
- }
- arrayLiteralNode.analyze();
- //array literal node now has an element type and all elements are attributed
- } else {
- assert !(literalNode.getValue() instanceof Node) : "literals with Node values not supported";
- }
-
- getLexicalContext().getCurrentFunction().newLiteral(literalNode);
- } finally {
- end(literalNode);
+ public Node leaveLiteralNode(final LiteralNode literalNode) {
+ assert !literalNode.isTokenType(TokenType.THIS) : "tokentype for " + literalNode + " is this"; //guard against old dead code case. literal nodes should never inherit tokens
+ assert literalNode instanceof ArrayLiteralNode || !(literalNode.getValue() instanceof Node) : "literals with Node values not supported";
+ final Symbol symbol = new Symbol(getLexicalContext().getCurrentFunction().uniqueName(LITERAL_PREFIX.symbolName()), IS_CONSTANT, literalNode.getType());
+ if (literalNode instanceof ArrayLiteralNode) {
+ ((ArrayLiteralNode)literalNode).analyze();
}
-
- return false;
+ return end(literalNode.setSymbol(getLexicalContext(), symbol));
}
@Override
@@ -667,18 +670,13 @@ final class Attr extends NodeOperatorVisitor {
@Override
public Node leaveObjectNode(final ObjectNode objectNode) {
- ensureSymbol(Type.OBJECT, objectNode);
- return end(objectNode);
+ return end(ensureSymbol(Type.OBJECT, objectNode));
}
- //TODO is this correct why not leave?
@Override
- public boolean enterPropertyNode(final PropertyNode propertyNode) {
+ public Node leavePropertyNode(final PropertyNode propertyNode) {
// assign a pseudo symbol to property name, see NASHORN-710
- start(propertyNode);
- propertyNode.setSymbol(new Symbol(propertyNode.getKeyName(), 0, Type.OBJECT));
- end(propertyNode);
- return true;
+ return propertyNode.setSymbol(getLexicalContext(), new Symbol(propertyNode.getKeyName(), 0, Type.OBJECT));
}
@Override
@@ -763,12 +761,9 @@ final class Attr extends NodeOperatorVisitor {
final IdentNode ident = varNode.getName();
final String name = ident.getName();
- final Symbol symbol = defineSymbol(getLexicalContext().getCurrentBlock(), name, IS_VAR, ident);
+ final Symbol symbol = defineSymbol(getLexicalContext().getCurrentBlock(), name, IS_VAR);
assert symbol != null;
- LOG.info("VarNode ", varNode, " set symbol ", symbol);
- varNode.setSymbol(symbol);
-
// NASHORN-467 - use before definition of vars - conservative
if (isLocalUse(ident.getName())) {
newType(symbol, Type.OBJECT);
@@ -780,22 +775,32 @@ final class Attr extends NodeOperatorVisitor {
@Override
public Node leaveVarNode(final VarNode varNode) {
- final Node init = varNode.getInit();
- final IdentNode ident = varNode.getName();
+ VarNode newVarNode = varNode;
+
+ final Node init = newVarNode.getInit();
+ final IdentNode ident = newVarNode.getName();
final String name = ident.getName();
+ final LexicalContext lc = getLexicalContext();
+ final Symbol symbol = findSymbol(lc.getCurrentBlock(), ident.getName());
+
if (init == null) {
// var x; with no init will be treated like a use of x by
- // visit(IdentNode) unless we remove the name
- // from the localdef list.
+ // leaveIdentNode unless we remove the name from the localdef list.
removeLocalDef(name);
- return varNode;
+ return end(newVarNode.setSymbol(lc, symbol));
}
addLocalDef(name);
- final Symbol symbol = varNode.getSymbol();
- final boolean isScript = getLexicalContext().getDefiningFunction(symbol).isProgram(); //see NASHORN-56
+ assert symbol != null;
+
+ final IdentNode newIdent = (IdentNode)ident.setSymbol(lc, symbol);
+
+ newVarNode = newVarNode.setName(newIdent);
+ newVarNode = (VarNode)newVarNode.setSymbol(lc, symbol);
+
+ final boolean isScript = lc.getDefiningFunction(symbol).isProgram(); //see NASHORN-56
if ((init.getType().isNumeric() || init.getType().isBoolean()) && !isScript) {
// Forbid integers as local vars for now as we have no way to treat them as undefined
newType(symbol, init.getType());
@@ -803,36 +808,28 @@ final class Attr extends NodeOperatorVisitor {
newType(symbol, Type.OBJECT);
}
- assert varNode.hasType() : varNode;
-
- end(varNode);
+ assert newVarNode.hasType() : newVarNode + " has no type";
- return varNode;
+ return end(newVarNode);
}
@Override
public Node leaveADD(final UnaryNode unaryNode) {
- ensureSymbol(arithType(), unaryNode);
- end(unaryNode);
- return unaryNode;
+ return end(ensureSymbol(arithType(), unaryNode));
}
@Override
public Node leaveBIT_NOT(final UnaryNode unaryNode) {
- ensureSymbol(Type.INT, unaryNode);
- end(unaryNode);
- return unaryNode;
+ return end(ensureSymbol(Type.INT, unaryNode));
}
@Override
public Node leaveDECINC(final UnaryNode unaryNode) {
// @see assignOffset
- ensureAssignmentSlots(getLexicalContext().getCurrentFunction(), unaryNode.rhs());
+ final UnaryNode newUnaryNode = unaryNode.setRHS(ensureAssignmentSlots(unaryNode.rhs()));
final Type type = arithType();
- newType(unaryNode.rhs().getSymbol(), type);
- ensureSymbol(type, unaryNode);
- end(unaryNode);
- return unaryNode;
+ newType(newUnaryNode.rhs().getSymbol(), type);
+ return end(ensureSymbol(type, newUnaryNode));
}
@Override
@@ -908,23 +905,24 @@ final class Attr extends NodeOperatorVisitor {
@Override
public Node leaveNEW(final UnaryNode unaryNode) {
- ensureSymbol(Type.OBJECT, unaryNode);
- end(unaryNode);
- return unaryNode;
+ return end(ensureSymbol(Type.OBJECT, unaryNode));
}
@Override
public Node leaveNOT(final UnaryNode unaryNode) {
- ensureSymbol(Type.BOOLEAN, unaryNode);
- end(unaryNode);
- return unaryNode;
+ return end(ensureSymbol(Type.BOOLEAN, unaryNode));
}
private IdentNode compilerConstant(CompilerConstants cc) {
final FunctionNode functionNode = getLexicalContext().getCurrentFunction();
- final IdentNode node = new IdentNode(functionNode.getSource(), functionNode.getToken(), functionNode.getFinish(), cc.symbolName());
- node.setSymbol(functionNode.compilerConstant(cc));
- return node;
+ return (IdentNode)
+ new IdentNode(
+ functionNode.getToken(),
+ functionNode.getFinish(),
+ cc.symbolName()).
+ setSymbol(
+ getLexicalContext(),
+ functionNode.compilerConstant(cc));
}
@Override
@@ -952,15 +950,12 @@ final class Attr extends NodeOperatorVisitor {
@Override
public Node leaveRuntimeNode(final RuntimeNode runtimeNode) {
- ensureSymbol(runtimeNode.getRequest().getReturnType(), runtimeNode);
- return runtimeNode;
+ return end(ensureSymbol(runtimeNode.getRequest().getReturnType(), runtimeNode));
}
@Override
public Node leaveSUB(final UnaryNode unaryNode) {
- ensureSymbol(arithType(), unaryNode);
- end(unaryNode);
- return unaryNode;
+ return end(ensureSymbol(arithType(), unaryNode));
}
@Override
@@ -982,18 +977,16 @@ final class Attr extends NodeOperatorVisitor {
ensureTypeNotUnknown(lhs);
ensureTypeNotUnknown(rhs);
- ensureSymbol(Type.widest(lhs.getType(), rhs.getType()), binaryNode);
-
- end(binaryNode);
-
- return binaryNode;
+ //even if we are adding two known types, this can overflow. i.e.
+ //int and number -> number.
+ //int and int are also number though.
+ //something and object is object
+ return end(ensureSymbol(Type.widest(arithType(), Type.widest(lhs.getType(), rhs.getType())), binaryNode));
}
@Override
public Node leaveAND(final BinaryNode binaryNode) {
- ensureSymbol(Type.OBJECT, binaryNode);
- end(binaryNode);
- return binaryNode;
+ return end(ensureSymbol(Type.OBJECT, binaryNode));
}
/**
@@ -1013,8 +1006,7 @@ final class Attr extends NodeOperatorVisitor {
Symbol symbol = findSymbol(block, name);
if (symbol == null) {
- symbol = defineSymbol(block, name, IS_GLOBAL, ident);
- binaryNode.setSymbol(symbol);
+ symbol = defineSymbol(block, name, IS_GLOBAL);
} else {
maybeForceScope(symbol);
}
@@ -1025,6 +1017,31 @@ final class Attr extends NodeOperatorVisitor {
return true;
}
+
+ /**
+ * This assign helper is called after an assignment, when all children of
+ * the assign has been processed. It fixes the types and recursively makes
+ * sure that everyhing has slots that should have them in the chain.
+ *
+ * @param binaryNode assignment node
+ */
+ private Node leaveAssignmentNode(final BinaryNode binaryNode) {
+ BinaryNode newBinaryNode = binaryNode;
+
+ final Node lhs = binaryNode.lhs();
+ final Node rhs = binaryNode.rhs();
+ final Type type;
+
+ if (rhs.getType().isNumeric()) {
+ type = Type.widest(binaryNode.lhs().getType(), binaryNode.rhs().getType());
+ } else {
+ type = Type.OBJECT; //force lhs to be an object if not numeric assignment, e.g. strings too.
+ }
+
+ newType(lhs.getSymbol(), type);
+ return end(ensureSymbol(type, newBinaryNode));
+ }
+
private boolean isLocal(FunctionNode function, Symbol symbol) {
final FunctionNode definingFn = getLexicalContext().getDefiningFunction(symbol);
// Temp symbols are not assigned to a block, so their defining fn is null; those can be assumed local
@@ -1173,14 +1190,12 @@ final class Attr extends NodeOperatorVisitor {
@Override
public Node leaveCOMMARIGHT(final BinaryNode binaryNode) {
- ensureSymbol(binaryNode.rhs().getType(), binaryNode);
- return binaryNode;
+ return end(ensureSymbol(binaryNode.rhs().getType(), binaryNode));
}
@Override
public Node leaveCOMMALEFT(final BinaryNode binaryNode) {
- ensureSymbol(binaryNode.lhs().getType(), binaryNode);
- return binaryNode;
+ return end(ensureSymbol(binaryNode.lhs().getType(), binaryNode));
}
@Override
@@ -1189,15 +1204,10 @@ final class Attr extends NodeOperatorVisitor {
}
private Node leaveCmp(final BinaryNode binaryNode) {
- final Node lhs = binaryNode.lhs();
- final Node rhs = binaryNode.rhs();
+ ensureTypeNotUnknown(binaryNode.lhs());
+ ensureTypeNotUnknown(binaryNode.rhs());
- ensureSymbol(Type.BOOLEAN, binaryNode);
- ensureTypeNotUnknown(lhs);
- ensureTypeNotUnknown(rhs);
-
- end(binaryNode);
- return binaryNode;
+ return end(ensureSymbol(Type.BOOLEAN, binaryNode));
}
private Node coerce(final BinaryNode binaryNode, final Type operandType, final Type destType) {
@@ -1207,11 +1217,9 @@ final class Attr extends NodeOperatorVisitor {
// as, say, an int : function(x) { return x & 4711 }, and x is not defined in
// the function. to make this work, uncomment the following two type inferences
// and debug.
-
//newType(binaryNode.lhs().getSymbol(), operandType);
//newType(binaryNode.rhs().getSymbol(), operandType);
- ensureSymbol(destType, binaryNode);
- return binaryNode;
+ return ensureSymbol(destType, binaryNode);
}
private Node coerce(final BinaryNode binaryNode, final Type type) {
@@ -1295,9 +1303,7 @@ final class Attr extends NodeOperatorVisitor {
@Override
public Node leaveOR(final BinaryNode binaryNode) {
- ensureSymbol(Type.OBJECT, binaryNode);
- end(binaryNode);
- return binaryNode;
+ return end(ensureSymbol(Type.OBJECT, binaryNode));
}
@Override
@@ -1346,50 +1352,13 @@ final class Attr extends NodeOperatorVisitor {
ensureTypeNotUnknown(rhs);
final Type type = Type.widest(lhs.getType(), rhs.getType());
- ensureSymbol(type, ternaryNode);
-
- end(ternaryNode);
- assert ternaryNode.getSymbol() != null;
-
- return ternaryNode;
- }
-
- private void initThis(final Block block) {
- final Symbol thisSymbol = defineSymbol(block, THIS.symbolName(), IS_PARAM | IS_THIS, null);
- newType(thisSymbol, Type.OBJECT);
- thisSymbol.setNeedsSlot(true);
- }
-
- private void initScope(final Block block) {
- final Symbol scopeSymbol = defineSymbol(block, SCOPE.symbolName(), IS_VAR | IS_INTERNAL, null);
- newType(scopeSymbol, Type.typeFor(ScriptObject.class));
- scopeSymbol.setNeedsSlot(true);
+ return end(ensureSymbol(type, ternaryNode));
}
- private void initReturn(final Block block) {
- final Symbol returnSymbol = defineSymbol(block, RETURN.symbolName(), IS_VAR | IS_INTERNAL, null);
- newType(returnSymbol, Type.OBJECT);
- returnSymbol.setNeedsSlot(true);
- //return symbol is always object as it's the __return__ thing. What returnType is is another matter though
- }
-
- private void initVarArg(final Block block, final boolean needsArguments) {
- final Symbol varArgsSymbol = defineSymbol(block, VARARGS.symbolName(), IS_PARAM | IS_INTERNAL, null);
- varArgsSymbol.setTypeOverride(Type.OBJECT_ARRAY);
- varArgsSymbol.setNeedsSlot(true);
-
- if (needsArguments) {
- final Symbol argumentsSymbol = defineSymbol(block, ARGUMENTS.symbolName(), IS_VAR | IS_INTERNAL, null);
- newType(argumentsSymbol, Type.typeFor(ScriptObject.class));
- argumentsSymbol.setNeedsSlot(true);
- addLocalDef(ARGUMENTS.symbolName());
- }
- }
-
- private void initCallee(final Block block) {
- final Symbol calleeSymbol = defineSymbol(block, CALLEE.symbolName(), IS_PARAM | IS_INTERNAL, null);
- newType(calleeSymbol, FunctionNode.FUNCTION_TYPE);
- calleeSymbol.setNeedsSlot(true);
+ private void initCompileConstant(final CompilerConstants cc, final Block block, final int flags, final Type type) {
+ final Symbol symbol = defineSymbol(block, cc.symbolName(), flags);
+ newType(symbol, type);
+ symbol.setNeedsSlot(true);
}
/**
@@ -1399,19 +1368,26 @@ final class Attr extends NodeOperatorVisitor {
* @param functionNode the function node
*/
private void initParameters(final FunctionNode functionNode, final Block body) {
+ int pos = 0;
for (final IdentNode param : functionNode.getParameters()) {
addLocalDef(param.getName());
- final Symbol paramSymbol = defineSymbol(body, param.getName(), IS_PARAM, param);
- if (paramSymbol != null) {
- final Type callSiteParamType = functionNode.getSpecializedType(param);
- if (callSiteParamType != null) {
- LOG.info("Param ", paramSymbol, " has a callsite type ", callSiteParamType, ". Using that.");
- }
- newType(paramSymbol, callSiteParamType == null ? Type.UNKNOWN : callSiteParamType);
+
+ final Type callSiteParamType = functionNode.getHints().getParameterType(pos);
+ int flags = IS_PARAM;
+ if (callSiteParamType != null) {
+ LOG.info("Param ", param, " has a callsite type ", callSiteParamType, ". Using that.");
+ flags |= Symbol.IS_SPECIALIZED_PARAM;
}
- LOG.info("Initialized param ", paramSymbol);
+ final Symbol paramSymbol = defineSymbol(body, param.getName(), flags);
+ assert paramSymbol != null;
+
+ newType(paramSymbol, callSiteParamType == null ? Type.UNKNOWN : callSiteParamType);
+
+ LOG.info("Initialized param ", pos, "=", paramSymbol);
+ pos++;
}
+
}
/**
@@ -1420,23 +1396,34 @@ final class Attr extends NodeOperatorVisitor {
*
* @param functionNode functionNode
*/
- private static void finalizeParameters(final FunctionNode functionNode) {
+ private FunctionNode finalizeParameters(final FunctionNode functionNode) {
+ final List<IdentNode> newParams = new ArrayList<>();
final boolean isVarArg = functionNode.isVarArg();
+ final int nparams = functionNode.getParameters().size();
- for (final IdentNode ident : functionNode.getParameters()) {
- final Symbol paramSymbol = ident.getSymbol();
+ int specialize = 0;
+ int pos = 0;
+ for (final IdentNode param : functionNode.getParameters()) {
+ final Symbol paramSymbol = functionNode.getBody().getExistingSymbol(param.getName());
+ assert paramSymbol != null;
+ assert paramSymbol.isParam();
+ newParams.add((IdentNode)param.setSymbol(getLexicalContext(), paramSymbol));
assert paramSymbol != null;
- Type type = functionNode.getSpecializedType(ident);
+ Type type = functionNode.getHints().getParameterType(pos);
if (type == null) {
type = Type.OBJECT;
}
// if we know that a parameter is only used as a certain type throughout
// this function, we can tell the runtime system that no matter what the
- // call site is, use this information. TODO
- if (!paramSymbol.getSymbolType().isObject()) {
- LOG.finest("Parameter ", ident, " could profit from specialization to ", paramSymbol.getSymbolType());
+ // call site is, use this information:
+ // we also need more than half of the parameters to be specializable
+ // for the heuristic to be worth it, and we need more than one use of
+ // the parameter to consider it, i.e. function(x) { call(x); } doens't count
+ if (paramSymbol.getUseCount() > 1 && !paramSymbol.getSymbolType().isObject()) {
+ LOG.finest("Parameter ", param, " could profit from specialization to ", paramSymbol.getSymbolType());
+ specialize++;
}
newType(paramSymbol, Type.widest(type, paramSymbol.getSymbolType()));
@@ -1445,7 +1432,17 @@ final class Attr extends NodeOperatorVisitor {
if (isVarArg) {
paramSymbol.setNeedsSlot(false);
}
+
+ pos++;
}
+
+ FunctionNode newFunctionNode = functionNode;
+
+ if (nparams == 0 || (specialize * 2) < nparams) {
+ newFunctionNode = newFunctionNode.clearSnapshot(getLexicalContext());
+ }
+
+ return newFunctionNode.setParameters(getLexicalContext(), newParams);
}
/**
@@ -1459,7 +1456,7 @@ final class Attr extends NodeOperatorVisitor {
for (final Property property : map.getProperties()) {
final String key = property.getKey();
- final Symbol symbol = defineSymbol(block, key, IS_GLOBAL, null);
+ final Symbol symbol = defineSymbol(block, key, IS_GLOBAL);
newType(symbol, Type.OBJECT);
LOG.info("Added global symbol from property map ", symbol);
}
@@ -1498,7 +1495,7 @@ final class Attr extends NodeOperatorVisitor {
* objects as parameters, for example +, but not *, which is known
* to coerce types into doubles
*/
- if (node.getType().isUnknown() || symbol.isParam()) {
+ if (node.getType().isUnknown() || (symbol.isParam() && !symbol.isSpecializedParam())) {
newType(symbol, Type.OBJECT);
symbol.setCanBeUndefined();
}
@@ -1520,19 +1517,25 @@ final class Attr extends NodeOperatorVisitor {
*
* see NASHORN-258
*
- * @param functionNode the current function node (has to be passed as it changes in the visitor below)
* @param assignmentDest the destination node of the assignment, e.g. lhs for binary nodes
*/
- private static void ensureAssignmentSlots(final FunctionNode functionNode, final Node assignmentDest) {
- assignmentDest.accept(new NodeVisitor() {
+ private Node ensureAssignmentSlots(final Node assignmentDest) {
+ final LexicalContext attrLexicalContext = getLexicalContext();
+ return assignmentDest.accept(new NodeVisitor() {
@Override
public Node leaveIndexNode(final IndexNode indexNode) {
assert indexNode.getSymbol().isTemp();
final Node index = indexNode.getIndex();
//only temps can be set as needing slots. the others will self resolve
//it is illegal to take a scope var and force it to be a slot, that breaks
- if (index.getSymbol().isTemp() && !index.getSymbol().isConstant()) {
- index.getSymbol().setNeedsSlot(true);
+ Symbol indexSymbol = index.getSymbol();
+ if (indexSymbol.isTemp() && !indexSymbol.isConstant() && !indexSymbol.hasSlot()) {
+ if(indexSymbol.isShared()) {
+ indexSymbol = temporarySymbols.createUnshared(indexSymbol);
+ }
+ indexSymbol.setNeedsSlot(true);
+ attrLexicalContext.getCurrentBlock().putSymbol(attrLexicalContext, indexSymbol);
+ return indexNode.setIndex(index.setSymbol(attrLexicalContext, indexSymbol));
}
return indexNode;
}
@@ -1557,22 +1560,30 @@ final class Attr extends NodeOperatorVisitor {
*
* @param functionNode
*/
- private static void finalizeTypes(final FunctionNode functionNode) {
+ private FunctionNode finalizeTypes(final FunctionNode functionNode) {
final Set<Node> changed = new HashSet<>();
+ FunctionNode currentFunctionNode = functionNode;
do {
changed.clear();
- functionNode.accept(new NodeVisitor() {
+ final FunctionNode newFunctionNode = (FunctionNode)currentFunctionNode.accept(new NodeVisitor() {
- private void widen(final Node node, final Type to) {
+ private Node widen(final Node node, final Type to) {
if (node instanceof LiteralNode) {
- return;
+ return node;
}
Type from = node.getType();
if (!Type.areEquivalent(from, to) && Type.widest(from, to) == to) {
- LOG.fine("Had to post pass widen '", node, "' " + Debug.id(node), " from ", node.getType(), " to ", to);
- newType(node.getSymbol(), to);
- changed.add(node);
+ LOG.fine("Had to post pass widen '", node, "' ", Debug.id(node), " from ", node.getType(), " to ", to);
+ Symbol symbol = node.getSymbol();
+ if(symbol.isShared() && symbol.wouldChangeType(to)) {
+ symbol = temporarySymbols.getTypedTemporarySymbol(to);
+ }
+ newType(symbol, to);
+ final Node newNode = node.setSymbol(getLexicalContext(), symbol);
+ changed.add(newNode);
+ return newNode;
}
+ return node;
}
@Override
@@ -1598,43 +1609,23 @@ final class Attr extends NodeOperatorVisitor {
@Override
public Node leaveBinaryNode(final BinaryNode binaryNode) {
final Type widest = Type.widest(binaryNode.lhs().getType(), binaryNode.rhs().getType());
+ BinaryNode newBinaryNode = binaryNode;
switch (binaryNode.tokenType()) {
default:
if (!binaryNode.isAssignment() || binaryNode.isSelfModifying()) {
break;
}
- widen(binaryNode.lhs(), widest);
+ newBinaryNode = newBinaryNode.setLHS(widen(newBinaryNode.lhs(), widest));
case ADD:
- widen(binaryNode, widest);
- break;
+ newBinaryNode = (BinaryNode)widen(newBinaryNode, widest);
}
- return binaryNode;
+ return newBinaryNode;
}
});
+ getLexicalContext().replace(currentFunctionNode, newFunctionNode);
+ currentFunctionNode = newFunctionNode;
} while (!changed.isEmpty());
- }
-
- /**
- * This assign helper is called after an assignment, when all children of
- * the assign has been processed. It fixes the types and recursively makes
- * sure that everyhing has slots that should have them in the chain.
- *
- * @param binaryNode assignment node
- */
- private Node leaveAssignmentNode(final BinaryNode binaryNode) {
- final Node lhs = binaryNode.lhs();
- final Node rhs = binaryNode.rhs();
-
- final Type type;
- if (rhs.getType().isNumeric()) {
- type = Type.widest(binaryNode.lhs().getType(), binaryNode.rhs().getType());
- } else {
- type = Type.OBJECT; //force lhs to be an object if not numeric assignment, e.g. strings too.
- }
- ensureSymbol(type, binaryNode);
- newType(lhs.getSymbol(), type);
- end(binaryNode);
- return binaryNode;
+ return currentFunctionNode;
}
private Node leaveSelfModifyingAssignmentNode(final BinaryNode binaryNode) {
@@ -1646,25 +1637,18 @@ final class Attr extends NodeOperatorVisitor {
final Node lhs = binaryNode.lhs();
newType(lhs.getSymbol(), destType); //may not narrow if dest is already wider than destType
- ensureSymbol(destType, binaryNode); //for OP= nodes, the node can carry a narrower types than its lhs rhs. This is perfectly fine
+// ensureSymbol(destType, binaryNode); //for OP= nodes, the node can carry a narrower types than its lhs rhs. This is perfectly fine
- ensureAssignmentSlots(getLexicalContext().getCurrentFunction(), binaryNode);
-
- end(binaryNode);
- return binaryNode;
- }
-
- private Symbol ensureSymbol(final FunctionNode functionNode, final Type type, final Node node) {
- LOG.info("New TEMPORARY added to ", functionNode.getName(), " type=", type);
- return functionNode.ensureSymbol(getLexicalContext().getCurrentBlock(), type, node);
+ return end(ensureSymbol(destType, ensureAssignmentSlots(binaryNode)));
}
- private Symbol ensureSymbol(final Type type, final Node node) {
- return ensureSymbol(getLexicalContext().getCurrentFunction(), type, node);
+ private Node ensureSymbol(final Type type, final Node node) {
+ LOG.info("New TEMPORARY added to ", getLexicalContext().getCurrentFunction().getName(), " type=", type);
+ return ensureSymbol(getLexicalContext(), type, node);
}
private Symbol newInternal(final String name, final Type type) {
- final Symbol iter = defineSymbol(getLexicalContext().getCurrentBlock(), name, IS_VAR | IS_INTERNAL, null);
+ final Symbol iter = defineSymbol(getLexicalContext().getCurrentBlock(), name, IS_VAR | IS_INTERNAL);
iter.setType(type); // NASHORN-73
return iter;
}
@@ -1721,6 +1705,10 @@ final class Attr extends NodeOperatorVisitor {
localUses.peek().add(name);
}
+ private Node ensureSymbol(final LexicalContext lc, final Type type, final Node node) {
+ return temporarySymbols.ensureSymbol(lc, type, node);
+ }
+
/**
* Pessimistically promote all symbols in current function node to Object types
* This is done when the function contains unevaluated black boxes such as
@@ -1731,8 +1719,7 @@ final class Attr extends NodeOperatorVisitor {
private static void objectifySymbols(final Block body) {
body.accept(new NodeVisitor() {
private void toObject(final Block block) {
- for (final Iterator<Symbol> iter = block.symbolIterator(); iter.hasNext();) {
- final Symbol symbol = iter.next();
+ for (final Symbol symbol : block.getSymbols()) {
if (!symbol.isTemp()) {
newType(symbol, Type.OBJECT);
}
@@ -1788,6 +1775,10 @@ final class Attr extends NodeOperatorVisitor {
}
private Node end(final Node node, final boolean printNode) {
+ if(node instanceof Statement) {
+ // If we're done with a statement, all temporaries can be reused.
+ temporarySymbols.reuse();
+ }
if (DEBUG) {
final StringBuilder sb = new StringBuilder();