8013477: Node.setSymbol needs to be copy on write - enable IR snapshots for recompilation based on callsite type specialization. [not enabled by default, hidden by a flag for now]

Reviewed-by: jlaskey, hannesw
This commit is contained in:
Marcus Lagergren 2013-05-03 15:33:54 +02:00
parent cd9c2c1bb2
commit 6f6ec2d9d1
36 changed files with 830 additions and 600 deletions

View File

@ -26,4 +26,4 @@
[ -z "$JAVA_HOME" ] && echo "Please set JAVA_HOME" && exit 1;
$JAVA_HOME/bin/java -server -XX:-TieredCompilation -Xms2G -Xmx2G -esa -ea -Djava.ext.dirs=`dirname $0`/../dist:$JAVA_HOME/jre/lib/ext -XX:+HeapDumpOnOutOfMemoryError -Djava.lang.invoke.MethodHandle.DEBUG_NAMES=false -Dnashorn.debug=true jdk.nashorn.tools.Shell $*
$JAVA_HOME/bin/java -server -XX:+TieredCompilation -Xms2G -Xmx2G -esa -ea -Djava.ext.dirs=`dirname $0`/../dist:$JAVA_HOME/jre/lib/ext -XX:+HeapDumpOnOutOfMemoryError -Djava.lang.invoke.MethodHandle.DEBUG_NAMES=false -Dnashorn.debug=true jdk.nashorn.tools.Shell $*

View File

@ -31,7 +31,6 @@ import java.util.List;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineFactory;
import jdk.nashorn.internal.runtime.Version;
import sun.reflect.Reflection;
/**
* JSR-223 compliant script engine factory for Nashorn. The engine answers for:

View File

@ -29,17 +29,21 @@ 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.TEMP_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;
import static jdk.nashorn.internal.ir.Symbol.IS_LET;
import static jdk.nashorn.internal.ir.Symbol.IS_PARAM;
import static jdk.nashorn.internal.ir.Symbol.IS_SCOPE;
import static jdk.nashorn.internal.ir.Symbol.IS_TEMP;
import static jdk.nashorn.internal.ir.Symbol.IS_THIS;
import static jdk.nashorn.internal.ir.Symbol.IS_VAR;
import static jdk.nashorn.internal.ir.Symbol.KINDMASK;
@ -51,6 +55,7 @@ import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.AccessNode;
import jdk.nashorn.internal.ir.BinaryNode;
@ -90,7 +95,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;
/**
@ -133,9 +137,9 @@ final class Attr extends NodeOperatorVisitor {
* Constructor.
*/
Attr() {
localDefs = new ArrayDeque<>();
localUses = new ArrayDeque<>();
returnTypes = new ArrayDeque<>();
this.localDefs = new ArrayDeque<>();
this.localUses = new ArrayDeque<>();
this.returnTypes = new ArrayDeque<>();
}
@Override
@ -150,51 +154,29 @@ 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
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
@ -208,9 +190,12 @@ final class Attr extends NodeOperatorVisitor {
*
* to an arbitrary nesting depth.
*
* @see NASHORN-73
* 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 +205,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 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 +267,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 +284,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 +354,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 +363,6 @@ final class Attr extends NodeOperatorVisitor {
symbol.setFlags(flags);
}
if (node != null) {
node.setSymbol(symbol);
}
return symbol;
}
@ -357,30 +370,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,8 +395,29 @@ 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)Attr.ensureSymbol(lc, body, 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);
newFunctionNode = finalizeParameters(newFunctionNode);
finalizeTypes(newFunctionNode);
for (final Symbol symbol : newFunctionNode.getDeclaredSymbols()) {
if (symbol.getSymbolType().isUnknown()) {
@ -400,8 +426,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,7 +433,7 @@ 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.getToken(),
@ -420,7 +444,6 @@ final class Attr extends NodeOperatorVisitor {
LOG.info("Accepting self symbol init ", selfInit, " for ", newFunctionNode.getName());
final List<Node> newStatements = new ArrayList<>();
newStatements.add(selfInit);
assert callee.getSymbol() != null && callee.getSymbol().hasSlot();
final IdentNode name = selfInit.getName();
@ -428,9 +451,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 +471,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 identNode.setSymbol(lc, pseudoSymbol);
}
final LexicalContext lc = getLexicalContext();
final Block block = lc.getCurrentBlock();
final Symbol oldSymbol = identNode.getSymbol();
Symbol symbol = findSymbol(block, name);
@ -495,12 +517,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 +530,14 @@ final class Attr extends NodeOperatorVisitor {
setBlockScope(name, symbol);
if (symbol != oldSymbol && !identNode.isInitializedHere()) {
if (symbol != null && !identNode.isInitializedHere()) {
symbol.increaseUseCount();
}
addLocalUse(identNode.getName());
end(identNode);
return false;
return identNode.setSymbol(lc, symbol);
}
/**
@ -625,39 +646,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);
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) {
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)literalNode).analyze();
}
}
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);
}
return false;
return literalNode.setSymbol(getLexicalContext(), symbol);
}
@Override
@ -667,18 +668,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 +759,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 +773,31 @@ 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();
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 newVarNode;
}
addLocalDef(name);
final Symbol symbol = varNode.getSymbol();
final boolean isScript = getLexicalContext().getDefiningFunction(symbol).isProgram(); //see NASHORN-56
final LexicalContext lc = getLexicalContext();
final Symbol symbol = findSymbol(lc.getCurrentBlock(), ident.getName());
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,25 +805,19 @@ final class Attr extends NodeOperatorVisitor {
newType(symbol, Type.OBJECT);
}
assert varNode.hasType() : varNode;
assert newVarNode.hasType() : newVarNode + " has no type";
end(varNode);
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
@ -830,9 +826,7 @@ final class Attr extends NodeOperatorVisitor {
ensureAssignmentSlots(getLexicalContext().getCurrentFunction(), unaryNode.rhs());
final Type type = arithType();
newType(unaryNode.rhs().getSymbol(), type);
ensureSymbol(type, unaryNode);
end(unaryNode);
return unaryNode;
return end(ensureSymbol(type, unaryNode));
}
@Override
@ -908,23 +902,25 @@ 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.getSource(),
functionNode.getToken(),
functionNode.getFinish(),
cc.symbolName()).
setSymbol(
getLexicalContext(),
functionNode.compilerConstant(cc));
}
@Override
@ -952,15 +948,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 +975,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 +1004,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 +1015,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 +1188,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 +1202,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 +1215,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 +1301,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 +1350,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;
return end(ensureSymbol(type, 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);
}
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 +1366,28 @@ 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);
final Type callSiteParamType = functionNode.getHints().getParameterType(pos);
int flags = IS_PARAM;
if (callSiteParamType != null) {
LOG.info("Param ", paramSymbol, " has a callsite type ", callSiteParamType, ". Using that.");
LOG.info("Param ", param, " has a callsite type ", callSiteParamType, ". Using that.");
flags |= Symbol.IS_SPECIALIZED_PARAM;
}
final Symbol paramSymbol = defineSymbol(body, param.getName(), flags);
assert paramSymbol != null;
if (paramSymbol != null) {
newType(paramSymbol, callSiteParamType == null ? Type.UNKNOWN : callSiteParamType);
}
LOG.info("Initialized param ", paramSymbol);
LOG.info("Initialized param ", pos, "=", paramSymbol);
pos++;
}
}
/**
@ -1420,14 +1396,19 @@ 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();
for (final IdentNode ident : functionNode.getParameters()) {
final Symbol paramSymbol = ident.getSymbol();
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;
}
@ -1436,7 +1417,7 @@ final class Attr extends NodeOperatorVisitor {
// 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());
LOG.finest("Parameter ", param, " could profit from specialization to ", paramSymbol.getSymbolType());
}
newType(paramSymbol, Type.widest(type, paramSymbol.getSymbolType()));
@ -1445,7 +1426,11 @@ final class Attr extends NodeOperatorVisitor {
if (isVarArg) {
paramSymbol.setNeedsSlot(false);
}
pos++;
}
return functionNode.setParameters(getLexicalContext(), newParams);
}
/**
@ -1459,7 +1444,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 +1483,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();
}
@ -1614,29 +1599,6 @@ final class Attr extends NodeOperatorVisitor {
} 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;
}
private Node leaveSelfModifyingAssignmentNode(final BinaryNode binaryNode) {
return leaveSelfModifyingAssignmentNode(binaryNode, binaryNode.getWidestOperationType());
}
@ -1646,25 +1608,20 @@ 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;
return end(ensureSymbol(destType, 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);
}
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 Attr.ensureSymbol(getLexicalContext(), getLexicalContext().getCurrentBlock(), 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 +1678,17 @@ final class Attr extends NodeOperatorVisitor {
localUses.peek().add(name);
}
static Node ensureSymbol(final LexicalContext lc, final Block block, final Type type, final Node node) {
Symbol symbol = node.getSymbol();
if (symbol != null) {
return node;
}
final String uname = lc.getCurrentFunction().uniqueName(TEMP_PREFIX.symbolName());
symbol = new Symbol(uname, IS_TEMP, type);
block.putSymbol(lc, symbol);
return node.setSymbol(lc, symbol);
}
/**
* 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 +1699,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);
}

View File

@ -568,8 +568,7 @@ final class CodeGenerator extends NodeOperatorVisitor {
* @param block block containing symbols.
*/
private void symbolInfo(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.hasSlot()) {
method.localVariable(symbol, block.getEntryLabel(), block.getBreakLabel());
}
@ -937,11 +936,10 @@ final class CodeGenerator extends NodeOperatorVisitor {
private static int assignSlots(final Block block, final int firstSlot) {
int nextSlot = firstSlot;
for (final Iterator<Symbol> iter = block.symbolIterator(); iter.hasNext(); ) {
final Symbol next = iter.next();
if (next.hasSlot()) {
next.setSlot(nextSlot);
nextSlot += next.slotCount();
for (final Symbol symbol : block.getSymbols()) {
if (symbol.hasSlot()) {
symbol.setSlot(nextSlot);
nextSlot += symbol.slotCount();
}
}
return nextSlot;
@ -1002,10 +1000,7 @@ final class CodeGenerator extends NodeOperatorVisitor {
final boolean hasArguments = function.needsArguments();
final Iterator<Symbol> symbols = block.symbolIterator();
while (symbols.hasNext()) {
final Symbol symbol = symbols.next();
for (final Symbol symbol : block.getSymbols()) {
if (symbol.isInternal() || symbol.isThis() || symbol.isTemp()) {
continue;
@ -1076,12 +1071,7 @@ final class CodeGenerator extends NodeOperatorVisitor {
}
}
final Iterator<Symbol> iter = block.symbolIterator();
final List<Symbol> symbols = new ArrayList<>();
while (iter.hasNext()) {
symbols.add(iter.next());
}
initSymbols(symbols);
initSymbols(block.getSymbols());
}
// Debugging: print symbols? @see --print-symbols flag
@ -2364,7 +2354,6 @@ final class CodeGenerator extends NodeOperatorVisitor {
public boolean enterDISCARD(final UnaryNode unaryNode) {
final Node rhs = unaryNode.rhs();
// System.err.println("**** Enter discard " + unaryNode);
discard.push(rhs);
load(rhs);
@ -2373,7 +2362,7 @@ final class CodeGenerator extends NodeOperatorVisitor {
method.pop();
discard.pop();
}
// System.err.println("**** Leave discard " + unaryNode);
return false;
}

View File

@ -42,7 +42,7 @@ enum CompilationPhase {
*/
LAZY_INITIALIZATION_PHASE(EnumSet.of(INITIALIZED, PARSED)) {
@Override
FunctionNode transform(final Compiler compiler, final FunctionNode fn0) {
FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
/*
* For lazy compilation, we might be given a node previously marked
@ -58,8 +58,7 @@ enum CompilationPhase {
* function from a trampoline
*/
final FunctionNode outermostFunctionNode = compiler.getFunctionNode();
assert outermostFunctionNode == fn0;
final FunctionNode outermostFunctionNode = fn;
final Set<FunctionNode> neverLazy = new HashSet<>();
final Set<FunctionNode> lazy = new HashSet<>();
@ -172,20 +171,26 @@ enum CompilationPhase {
ATTRIBUTION_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED)) {
@Override
FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
return (FunctionNode)initReturnTypes(fn).accept(new Attr());
return (FunctionNode)enterAttr(fn).accept(new Attr());
}
/**
* Pessimistically set all lazy functions' return types to Object
* and the function symbols to object
* @param functionNode node where to start iterating
*/
private FunctionNode initReturnTypes(final FunctionNode functionNode) {
private FunctionNode enterAttr(final FunctionNode functionNode) {
return (FunctionNode)functionNode.accept(new NodeVisitor() {
@Override
public Node leaveFunctionNode(final FunctionNode node) {
return node.isLazy() ?
node.setReturnType(getLexicalContext(), Type.OBJECT) :
node.setReturnType(getLexicalContext(), Type.UNKNOWN);
final LexicalContext lc = getLexicalContext();
if (node.isLazy()) {
FunctionNode newNode = node.setReturnType(getLexicalContext(), Type.OBJECT);
return Attr.ensureSymbol(lc, lc.getCurrentBlock(), Type.OBJECT, newNode);
}
//node may have a reference here that needs to be nulled if it was referred to by
//its outer context, if it is lazy and not attributed
return node.setReturnType(lc, Type.UNKNOWN).setSymbol(lc, null);
}
});
}
@ -207,6 +212,7 @@ enum CompilationPhase {
FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
final CompileUnit outermostCompileUnit = compiler.addCompileUnit(compiler.firstCompileUnitName());
// assert fn.isProgram() ;
final FunctionNode newFunctionNode = new Splitter(compiler, fn, outermostCompileUnit).split(fn);
assert newFunctionNode.getCompileUnit() == outermostCompileUnit : "fn.compileUnit (" + newFunctionNode.getCompileUnit() + ") != " + outermostCompileUnit;
@ -216,15 +222,6 @@ enum CompilationPhase {
compiler.setStrictMode(true);
}
/*
newFunctionNode.accept(new NodeVisitor() {
@Override
public boolean enterFunctionNode(final FunctionNode functionNode) {
assert functionNode.getCompileUnit() != null : functionNode.getName() + " " + Debug.id(functionNode) + " has no compile unit";
return true;
}
});*/
return newFunctionNode;
}

View File

@ -77,6 +77,8 @@ public final class Compiler {
/** Name of the objects package */
public static final String OBJECTS_PACKAGE = "jdk/nashorn/internal/objects";
private Source source;
private final Map<String, byte[]> bytecode;
private final Set<CompileUnit> compileUnits;
@ -87,12 +89,10 @@ public final class Compiler {
private final ScriptEnvironment env;
private final String scriptName;
private String scriptName;
private boolean strict;
private FunctionNode functionNode;
private CodeInstaller<ScriptEnvironment> installer;
/** logger for compiler, trampolines, splits and related code generation events
@ -167,6 +167,41 @@ public final class Compiler {
}
}
/**
* Environment information known to the compile, e.g. params
*/
public static class Hints {
private final Type[] paramTypes;
/** singleton empty hints */
public static final Hints EMPTY = new Hints();
private Hints() {
this.paramTypes = null;
}
/**
* Constructor
* @param paramTypes known parameter types for this callsite
*/
public Hints(final Type[] paramTypes) {
this.paramTypes = paramTypes;
}
/**
* Get the parameter type for this parameter position, or
* null if now known
* @param pos position
* @return parameter type for this callsite if known
*/
public Type getParameterType(final int pos) {
if (paramTypes != null && pos < paramTypes.length) {
return paramTypes[pos];
}
return null;
}
}
/**
* Standard (non-lazy) compilation, that basically will take an entire script
* and JIT it at once. This can lead to long startup time and fewer type
@ -207,21 +242,22 @@ public final class Compiler {
* @param strict should this compilation use strict mode semantics
*/
//TODO support an array of FunctionNodes for batch lazy compilation
Compiler(final ScriptEnvironment env, final CodeInstaller<ScriptEnvironment> installer, final FunctionNode functionNode, final CompilationSequence sequence, final boolean strict) {
Compiler(final ScriptEnvironment env, final CodeInstaller<ScriptEnvironment> installer, final CompilationSequence sequence, final boolean strict) {
this.env = env;
this.functionNode = functionNode;
this.sequence = sequence;
this.installer = installer;
this.strict = strict || functionNode.isStrict();
this.constantData = new ConstantData();
this.compileUnits = new HashSet<>();
this.bytecode = new HashMap<>();
}
private void initCompiler(final FunctionNode functionNode) {
this.strict = strict || functionNode.isStrict();
final StringBuilder sb = new StringBuilder();
sb.append(functionNode.uniqueName(DEFAULT_SCRIPT_NAME.symbolName() + lazyTag(functionNode))).
append('$').
append(safeSourceName(functionNode.getSource()));
this.source = functionNode.getSource();
this.scriptName = sb.toString();
}
@ -229,52 +265,43 @@ public final class Compiler {
* Constructor
*
* @param installer code installer
* @param functionNode function node (in any available {@link CompilationState}) to compile
* @param strict should this compilation use strict mode semantics
*/
public Compiler(final CodeInstaller<ScriptEnvironment> installer, final FunctionNode functionNode, final boolean strict) {
this(installer.getOwner(), installer, functionNode, sequence(installer.getOwner()._lazy_compilation), strict);
public Compiler(final CodeInstaller<ScriptEnvironment> installer, final boolean strict) {
this(installer.getOwner(), installer, sequence(installer.getOwner()._lazy_compilation), strict);
}
/**
* Constructor - compilation will use the same strict semantics as in script environment
*
* @param installer code installer
* @param functionNode function node (in any available {@link CompilationState}) to compile
*/
public Compiler(final CodeInstaller<ScriptEnvironment> installer, final FunctionNode functionNode) {
this(installer.getOwner(), installer, functionNode, sequence(installer.getOwner()._lazy_compilation), installer.getOwner()._strict);
public Compiler(final CodeInstaller<ScriptEnvironment> installer) {
this(installer.getOwner(), installer, sequence(installer.getOwner()._lazy_compilation), installer.getOwner()._strict);
}
/**
* Constructor - compilation needs no installer, but uses a script environment
* Used in "compile only" scenarios
* @param env a script environment
* @param functionNode functionNode to compile
*/
public Compiler(final ScriptEnvironment env, final FunctionNode functionNode) {
this(env, null, functionNode, sequence(env._lazy_compilation), env._strict);
public Compiler(final ScriptEnvironment env) {
this(env, null, sequence(env._lazy_compilation), env._strict);
}
/**
* Execute the compilation this Compiler was created with
* @params param types if known, for specialization
* @param functionNode function node to compile from its current state
* @throws CompilationException if something goes wrong
* @return function node that results from code transforms
*/
public FunctionNode compile() throws CompilationException {
return compile(null);
}
public FunctionNode compile(final FunctionNode functionNode) throws CompilationException {
FunctionNode newFunctionNode = functionNode;
initCompiler(newFunctionNode); //TODO move this state into functionnode?
/**
* Execute the compilation this Compiler was created with
* @param paramTypes param types if known, for specialization
* @throws CompilationException if something goes wrong
* @return function node that results from code transforms
*/
public FunctionNode compile(final Class<?> paramTypes) throws CompilationException {
for (final String reservedName : RESERVED_NAMES) {
functionNode.uniqueName(reservedName);
newFunctionNode.uniqueName(reservedName);
}
final boolean fine = !LOG.levelAbove(Level.FINE);
@ -283,7 +310,7 @@ public final class Compiler {
long time = 0L;
for (final CompilationPhase phase : sequence) {
this.functionNode = phase.apply(this, functionNode);
newFunctionNode = phase.apply(this, newFunctionNode);
final long duration = Timing.isEnabled() ? (phase.getEndTime() - phase.getStartTime()) : 0L;
time += duration;
@ -293,7 +320,7 @@ public final class Compiler {
sb.append(phase.toString()).
append(" done for function '").
append(functionNode.getName()).
append(newFunctionNode.getName()).
append('\'');
if (duration > 0L) {
@ -309,7 +336,7 @@ public final class Compiler {
if (info) {
final StringBuilder sb = new StringBuilder();
sb.append("Compile job for '").
append(functionNode.getName()).
append(newFunctionNode.getName()).
append("' finished");
if (time > 0L) {
@ -321,16 +348,15 @@ public final class Compiler {
LOG.info(sb);
}
return functionNode;
return newFunctionNode;
}
private Class<?> install(final String className, final byte[] code) {
private Class<?> install(final FunctionNode functionNode, final String className, final byte[] code) {
LOG.fine("Installing class ", className);
final Class<?> clazz = installer.install(Compiler.binaryName(className), code);
try {
final Source source = getSource();
final Object[] constants = getConstantData().toArray();
// Need doPrivileged because these fields are private
AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
@ -355,9 +381,10 @@ public final class Compiler {
/**
* Install compiled classes into a given loader
* @param functionNode function node to install - must be in {@link CompilationState#EMITTED} state
* @return root script class - if there are several compile units they will also be installed
*/
public Class<?> install() {
public Class<?> install(final FunctionNode functionNode) {
final long t0 = Timing.isEnabled() ? System.currentTimeMillis() : 0L;
assert functionNode.hasState(CompilationState.EMITTED) : functionNode.getName() + " has no bytecode and cannot be installed";
@ -366,7 +393,7 @@ public final class Compiler {
final String rootClassName = firstCompileUnitName();
final byte[] rootByteCode = bytecode.get(rootClassName);
final Class<?> rootClass = install(rootClassName, rootByteCode);
final Class<?> rootClass = install(functionNode, rootClassName, rootByteCode);
int length = rootByteCode.length;
@ -380,7 +407,7 @@ public final class Compiler {
final byte[] code = entry.getValue();
length += code.length;
installedClasses.put(className, install(className, code));
installedClasses.put(className, install(functionNode, className, code));
}
for (final CompileUnit unit : compileUnits) {
@ -430,10 +457,6 @@ public final class Compiler {
this.strict = strict;
}
FunctionNode getFunctionNode() {
return functionNode;
}
ConstantData getConstantData() {
return constantData;
}
@ -442,10 +465,6 @@ public final class Compiler {
return installer;
}
Source getSource() {
return functionNode.getSource();
}
void addClass(final String name, final byte[] code) {
bytecode.put(name, code);
}
@ -496,7 +515,7 @@ public final class Compiler {
}
private CompileUnit initCompileUnit(final String unitClassName, final long initialWeight) {
final ClassEmitter classEmitter = new ClassEmitter(env, functionNode.getSource().getName(), unitClassName, strict);
final ClassEmitter classEmitter = new ClassEmitter(env, source.getName(), unitClassName, strict);
final CompileUnit compileUnit = new CompileUnit(unitClassName, classEmitter, initialWeight);
classEmitter.begin();

View File

@ -30,7 +30,6 @@ import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.AccessNode;
@ -354,13 +353,6 @@ final class FinalizeTypes extends NodeOperatorVisitor {
return true;
}
/*
@Override
public Node leaveBlock(final Block block) {
final LexicalContext lc = getLexicalContext();
return block;//.setFlag(lc, lc.getFlags(block));
}*/
@Override
public Node leaveCatchNode(final CatchNode catchNode) {
final Node exceptionCondition = catchNode.getExceptionCondition();
@ -551,8 +543,7 @@ final class FinalizeTypes extends NodeOperatorVisitor {
final boolean allVarsInScope = functionNode.allVarsInScope();
final boolean isVarArg = functionNode.isVarArg();
for (final Iterator<Symbol> iter = block.symbolIterator(); iter.hasNext(); ) {
final Symbol symbol = iter.next();
for (final Symbol symbol : block.getSymbols()) {
if (symbol.isInternal() || symbol.isThis() || symbol.isTemp()) {
continue;
}
@ -812,14 +803,12 @@ final class FinalizeTypes extends NodeOperatorVisitor {
LOG.info("CONVERT('", node, "', ", to, ") => '", resultNode, "'");
assert !node.isTerminal();
final LexicalContext lc = getLexicalContext();
//This is the only place in this file that can create new temporaries
//FinalizeTypes may not introduce ANY node that is not a conversion.
lc.getCurrentFunction().ensureSymbol(lc.getCurrentBlock(), to, resultNode);
assert !node.isTerminal();
return resultNode;
return Attr.ensureSymbol(lc, lc.getCurrentBlock(), to, resultNode);
}
private static Node discard(final Node node) {
@ -905,7 +894,7 @@ final class FinalizeTypes extends NodeOperatorVisitor {
if (literalNode != null) {
//inherit literal symbol for attr.
literalNode.setSymbol(parent.getSymbol());
literalNode = (LiteralNode<?>)literalNode.setSymbol(null, parent.getSymbol());
}
return literalNode;

View File

@ -27,7 +27,6 @@ package jdk.nashorn.internal.codegen;
import java.util.List;
import static jdk.nashorn.internal.codegen.ObjectClassGenerator.FIELD_PADDING;
import static jdk.nashorn.internal.codegen.ObjectClassGenerator.FIELD_ROUNDING;
import jdk.nashorn.internal.ir.Symbol;
import jdk.nashorn.internal.runtime.Context;
import jdk.nashorn.internal.runtime.PropertyMap;

View File

@ -95,7 +95,7 @@ final class Splitter extends NodeVisitor {
final LexicalContext lc = getLexicalContext();
long weight = WeighNodes.weigh(functionNode);
final boolean top = compiler.getFunctionNode() == outermost;
final boolean top = fn.isProgram(); //compiler.getFunctionNode() == outermost;
if (weight >= SPLIT_THRESHOLD) {
LOG.finest("Splitting '", functionNode.getName(), "' as its weight ", weight, " exceeds split threshold ", SPLIT_THRESHOLD);
@ -273,7 +273,9 @@ final class Splitter extends NodeVisitor {
return literal;
}
getLexicalContext().setFlag(getLexicalContext().getCurrentFunction(), FunctionNode.IS_SPLIT);
final FunctionNode functionNode = getLexicalContext().getCurrentFunction();
getLexicalContext().setFlag(functionNode, FunctionNode.IS_SPLIT);
if (literal instanceof ArrayLiteralNode) {
final ArrayLiteralNode arrayLiteralNode = (ArrayLiteralNode) literal;

View File

@ -30,7 +30,6 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@ -104,18 +103,26 @@ public class Block extends BreakableNode implements Flags<Block> {
this(source, token, finish, statements.toArray(new Node[statements.size()]));
}
private Block(final Block block, final int finish, final List<Node> statements, final int flags) {
private Block(final Block block, final int finish, final List<Node> statements, final int flags, final Map<String, Symbol> symbols) {
super(block);
this.statements = statements;
this.flags = flags;
this.symbols = block.symbols; //todo - symbols have no dependencies on any IR node and can as far as we understand it be shallow copied now
this.symbols = new LinkedHashMap<>(symbols); //todo - symbols have no dependencies on any IR node and can as far as we understand it be shallow copied now
this.entryLabel = new Label(block.entryLabel);
this.finish = finish;
}
/**
* Clear the symbols in a block
* TODO: make this immutable
*/
public void clearSymbols() {
symbols.clear();
}
@Override
public Node ensureUniqueLabels(final LexicalContext lc) {
return Node.replaceInLexicalContext(lc, this, new Block(this, finish, statements, flags));
return Node.replaceInLexicalContext(lc, this, new Block(this, finish, statements, flags, symbols));
}
/**
@ -137,15 +144,15 @@ public class Block extends BreakableNode implements Flags<Block> {
* Get an iterator for all the symbols defined in this block
* @return symbol iterator
*/
public Iterator<Symbol> symbolIterator() {
return symbols.values().iterator();
public List<Symbol> getSymbols() {
return Collections.unmodifiableList(new ArrayList<>(symbols.values()));
}
/**
* Retrieves an existing symbol defined in the current block.
* @param name the name of the symbol
* @return an existing symbol with the specified name defined in the current block, or null if this block doesn't
* define a symbol with this name.
* define a symbol with this name.T
*/
public Symbol getExistingSymbol(final String name) {
return symbols.get(name);
@ -241,17 +248,17 @@ public class Block extends BreakableNode implements Flags<Block> {
if (!statements.isEmpty()) {
lastFinish = statements.get(statements.size() - 1).getFinish();
}
return Node.replaceInLexicalContext(lc, this, new Block(this, Math.max(finish, lastFinish), statements, flags));
return Node.replaceInLexicalContext(lc, this, new Block(this, Math.max(finish, lastFinish), statements, flags, symbols));
}
/**
* Add or overwrite an existing symbol in the block
*
* @param name name of symbol
* @param lc get lexical context
* @param symbol symbol
*/
public void putSymbol(final String name, final Symbol symbol) {
symbols.put(name, symbol);
public void putSymbol(final LexicalContext lc, final Symbol symbol) {
symbols.put(symbol.getName(), symbol);
}
/**
@ -268,7 +275,7 @@ public class Block extends BreakableNode implements Flags<Block> {
if (this.flags == flags) {
return this;
}
return Node.replaceInLexicalContext(lc, this, new Block(this, finish, statements, flags));
return Node.replaceInLexicalContext(lc, this, new Block(this, finish, statements, flags, symbols));
}
@Override
@ -296,7 +303,7 @@ public class Block extends BreakableNode implements Flags<Block> {
return this;
}
return Node.replaceInLexicalContext(lc, this, new Block(this, finish, statements, flags | NEEDS_SCOPE));
return Node.replaceInLexicalContext(lc, this, new Block(this, finish, statements, flags | NEEDS_SCOPE, symbols));
}
/**
@ -306,10 +313,8 @@ public class Block extends BreakableNode implements Flags<Block> {
* @return next slot
*/
public int nextSlot() {
final Iterator<Symbol> iter = symbolIterator();
int next = 0;
while (iter.hasNext()) {
final Symbol symbol = iter.next();
for (final Symbol symbol : getSymbols()) {
if (symbol.hasSlot()) {
next += symbol.slotCount();
}

View File

@ -138,7 +138,12 @@ public final class CatchNode extends Node {
return body;
}
private CatchNode setException(final IdentNode exception) {
/**
* Resets the exception of a catch block
* @param exception new exception
* @return new catch node if changed, same otherwise
*/
public CatchNode setException(final IdentNode exception) {
if (this.exception == exception) {
return this;
}

View File

@ -25,16 +25,12 @@
package jdk.nashorn.internal.ir;
import static jdk.nashorn.internal.codegen.CompilerConstants.LITERAL_PREFIX;
import static jdk.nashorn.internal.codegen.CompilerConstants.TEMP_PREFIX;
import static jdk.nashorn.internal.ir.Symbol.IS_CONSTANT;
import static jdk.nashorn.internal.ir.Symbol.IS_TEMP;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import jdk.nashorn.internal.codegen.CompileUnit;
import jdk.nashorn.internal.codegen.Compiler;
import jdk.nashorn.internal.codegen.CompilerConstants;
@ -95,6 +91,10 @@ public final class FunctionNode extends LexicalContextNode implements Flags<Func
@Ignore
private final IdentNode ident;
/** Parsed version of functionNode */
@Ignore
private final FunctionNode snapshot;
/** The body of the function node */
private final Block body;
@ -127,6 +127,9 @@ public final class FunctionNode extends LexicalContextNode implements Flags<Func
@Ignore
private final EnumSet<CompilationState> compilationState;
@Ignore
private final Compiler.Hints hints;
/** Function flags. */
private final int flags;
@ -176,6 +179,9 @@ public final class FunctionNode extends LexicalContextNode implements Flags<Func
/** Does this function have nested declarations? */
public static final int HAS_FUNCTION_DECLARATIONS = 1 << 13;
/** Can this function be specialized? */
public static final int CAN_SPECIALIZE = 1 << 14;
/** Does this function or any nested functions contain an eval? */
private static final int HAS_DEEP_EVAL = HAS_EVAL | HAS_NESTED_EVAL;
@ -231,16 +237,32 @@ public final class FunctionNode extends LexicalContextNode implements Flags<Func
this.flags = flags;
this.compileUnit = null;
this.body = null;
this.snapshot = null;
this.hints = null;
}
private FunctionNode(final FunctionNode functionNode, final long lastToken, final int flags, final Type returnType, final CompileUnit compileUnit, final EnumSet<CompilationState> compilationState, final Block body) {
private FunctionNode(
final FunctionNode functionNode,
final long lastToken,
final int flags,
final Type returnType,
final CompileUnit compileUnit,
final EnumSet<CompilationState> compilationState,
final Block body,
final List<IdentNode> parameters,
final FunctionNode snapshot,
final Compiler.Hints hints) {
super(functionNode);
this.flags = flags;
this.returnType = returnType;
this.compileUnit = compileUnit;
this.lastToken = lastToken;
this.compilationState = compilationState;
this.body = body;
this.parameters = parameters;
this.snapshot = snapshot;
this.hints = hints;
// the fields below never change - they are final and assigned in constructor
this.name = functionNode.name;
@ -248,7 +270,6 @@ public final class FunctionNode extends LexicalContextNode implements Flags<Func
this.namespace = functionNode.namespace;
this.declaredSymbols = functionNode.declaredSymbols;
this.kind = functionNode.kind;
this.parameters = functionNode.parameters;
this.firstToken = functionNode.firstToken;
}
@ -260,6 +281,36 @@ public final class FunctionNode extends LexicalContextNode implements Flags<Func
return this;
}
/**
* Get the version of this function node's code as it looked upon construction
* i.e typically parsed and nothing else
* @return initial version of function node
*/
public FunctionNode getSnapshot() {
return snapshot;
}
/**
* Take a snapshot of this function node at a given point in time
* and store it in the function node
* @param lc lexical context
* @return function node
*/
public FunctionNode snapshot(final LexicalContext lc) {
if (this.snapshot == this) {
return this;
}
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, returnType, compileUnit, compilationState, body, parameters, this, hints));
}
/**
* Can this function node be regenerated with more specific type args?
* @return true if specialization is possible
*/
public boolean canSpecialize() {
return getFlag(CAN_SPECIALIZE);
}
/**
* Get the compilation state of this function
* @return the compilation state
@ -307,7 +358,28 @@ public final class FunctionNode extends LexicalContextNode implements Flags<Func
}
final EnumSet<CompilationState> newState = EnumSet.copyOf(this.compilationState);
newState.add(state);
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, returnType, compileUnit, newState, body));
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, returnType, compileUnit, newState, body, parameters, snapshot, hints));
}
/**
* Get any compiler hints that may associated with the function
* @return compiler hints
*/
public Compiler.Hints getHints() {
return this.hints == null ? Compiler.Hints.EMPTY : hints;
}
/**
* Set compiler hints for this function
* @param lc lexical context
* @param hints compiler hints
* @return new function if hints changed
*/
public FunctionNode setHints(final LexicalContext lc, final Compiler.Hints hints) {
if (this.hints == hints) {
return this;
}
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, returnType, compileUnit, compilationState, body, parameters, snapshot, hints));
}
/**
@ -319,20 +391,6 @@ public final class FunctionNode extends LexicalContextNode implements Flags<Func
return namespace.uniqueName(base);
}
/**
* Create a virtual symbol for a literal.
*
* @param literalNode Primary node to use symbol.
*
* @return Symbol used.
*/
public Symbol newLiteral(final LiteralNode<?> literalNode) {
final String uname = uniqueName(LITERAL_PREFIX.symbolName());
final Symbol symbol = new Symbol(uname, IS_CONSTANT, literalNode.getType());
literalNode.setSymbol(symbol);
return symbol;
}
@Override
public void toString(final StringBuilder sb) {
@ -374,7 +432,7 @@ public final class FunctionNode extends LexicalContextNode implements Flags<Func
if (this.flags == flags) {
return this;
}
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, returnType, compileUnit, compilationState, body));
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, returnType, compileUnit, compilationState, body, parameters, snapshot, hints));
}
@Override
@ -483,7 +541,7 @@ public final class FunctionNode extends LexicalContextNode implements Flags<Func
if(this.body == body) {
return this;
}
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, returnType, compileUnit, compilationState, body));
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, returnType, compileUnit, compilationState, body, parameters, snapshot, hints));
}
/**
@ -551,7 +609,7 @@ public final class FunctionNode extends LexicalContextNode implements Flags<Func
if (this.lastToken == lastToken) {
return this;
}
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, returnType, compileUnit, compilationState, body));
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, returnType, compileUnit, compilationState, body, parameters, snapshot, hints));
}
/**
@ -599,13 +657,17 @@ public final class FunctionNode extends LexicalContextNode implements Flags<Func
}
/**
* Get a specialized type for an identity, if one exists
* @param node node to check specialized type for
* @return null if no specialization exists, otherwise type
* Reset the compile unit used to compile this function
* @see Compiler
* @param lc lexical context
* @param parameters the compile unit
* @return function node or a new one if state was changed
*/
@SuppressWarnings("static-method")
public Type getSpecializedType(final IdentNode node) {
return null; //TODO implement specialized types later
public FunctionNode setParameters(final LexicalContext lc, final List<IdentNode> parameters) {
if (this.parameters == parameters) {
return this;
}
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, returnType, compileUnit, compilationState, body, parameters, snapshot, hints));
}
/**
@ -674,7 +736,10 @@ public final class FunctionNode extends LexicalContextNode implements Flags<Func
returnType),
compileUnit,
compilationState,
body));
body,
parameters,
snapshot,
hints));
}
/**
@ -705,7 +770,7 @@ public final class FunctionNode extends LexicalContextNode implements Flags<Func
if (this.compileUnit == compileUnit) {
return this;
}
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, returnType, compileUnit, compilationState, body));
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, returnType, compileUnit, compilationState, body, parameters, snapshot, hints));
}
/**
@ -717,19 +782,6 @@ public final class FunctionNode extends LexicalContextNode implements Flags<Func
*
* @return Symbol used.
*/
public Symbol ensureSymbol(final Block block, final Type type, final Node node) {
Symbol symbol = node.getSymbol();
// If no symbol already present.
if (symbol == null) {
final String uname = uniqueName(TEMP_PREFIX.symbolName());
symbol = new Symbol(uname, IS_TEMP, type);
block.putSymbol(uname, symbol);
node.setSymbol(symbol);
}
return symbol;
}
/**
* Get the symbol for a compiler constant, or null if not available (yet)

View File

@ -64,7 +64,6 @@ public class LexicalContext {
for (int i = sp - 1; i >= 0; i--) {
if (stack[i] == node) {
flags[i] |= flag;
//System.err.println("Setting flag " + node + " " + flag);
return;
}
}
@ -117,8 +116,6 @@ public class LexicalContext {
return (FunctionNode)stack[0];
}
/**
* Pushes a new block on top of the context, making it the innermost open block.
* @param node the new node

View File

@ -70,4 +70,16 @@ public abstract class LexicalContextNode extends Node {
final LexicalContextNode newNode = (LexicalContextNode)accept(lc, visitor);
return lc.pop(newNode);
}
/**
* Set the symbol and replace in lexical context if applicable
* @param lc lexical context
* @param symbol symbol
* @return new node if symbol changed
*/
@Override
public Node setSymbol(final LexicalContext lc, final Symbol symbol) {
return Node.replaceInLexicalContext(lc, this, (LexicalContextNode)super.setSymbol(null, symbol));
}
}

View File

@ -659,9 +659,12 @@ public abstract class LiteralNode<T> extends Node implements PropertyKey {
* Copy constructor
* @param node source array literal node
*/
protected ArrayLiteralNode(final ArrayLiteralNode node) {
super(node);
private ArrayLiteralNode(final ArrayLiteralNode node, final Node[] value) {
super(node, value);
this.elementType = node.elementType;
this.presets = node.presets;
this.postsets = node.postsets;
this.units = node.units;
}
/**
@ -750,9 +753,8 @@ public abstract class LiteralNode<T> extends Node implements PropertyKey {
break;
}
final Symbol symbol = node.getSymbol();
assert symbol != null; //don't run this on unresolved nodes or you are in trouble
Type symbolType = symbol.getSymbolType();
assert node.getSymbol() != null; //don't run this on unresolved nodes or you are in trouble
Type symbolType = node.getSymbol().getSymbolType();
if (symbolType.isUnknown()) {
symbolType = Type.OBJECT;
}
@ -813,7 +815,8 @@ public abstract class LiteralNode<T> extends Node implements PropertyKey {
}
/**
* Get indices of arrays containing computed post sets
* Get indices of arrays containing computed post sets. post sets
* are things like non literals e.g. "x+y" instead of i or 17
* @return post set indices
*/
public int[] getPostsets() {
@ -849,17 +852,17 @@ public abstract class LiteralNode<T> extends Node implements PropertyKey {
@Override
public Node accept(final NodeVisitor visitor) {
if (visitor.enterLiteralNode(this)) {
for (int i = 0; i < value.length; i++) {
final Node element = value[i];
if (element != null) {
value[i] = element.accept(visitor);
}
}
return visitor.leaveLiteralNode(this);
final List<Node> oldValue = Arrays.asList(value);
final List<Node> newValue = Node.accept(visitor, Node.class, oldValue);
return visitor.leaveLiteralNode(oldValue != newValue ? setValue(newValue) : this);
}
return this;
}
private ArrayLiteralNode setValue(final List<Node> value) {
return new ArrayLiteralNode(this, value.toArray(new Node[value.size()]));
}
@Override
public void toString(final StringBuilder sb) {
sb.append('[');

View File

@ -252,10 +252,17 @@ public abstract class Node extends Location {
* Assign a symbol to this node. See {@link Node#getSymbol()} for explanation
* of what a symbol is
*
* @param lc lexical context
* @param symbol the symbol
* @return new node
*/
public void setSymbol(final Symbol symbol) {
this.symbol = symbol;
public Node setSymbol(final LexicalContext lc, final Symbol symbol) {
if (this.symbol == symbol) {
return this;
}
final Node newNode = (Node)clone();
newNode.symbol = symbol;
return newNode;
}
/**
@ -274,7 +281,7 @@ public abstract class Node extends Location {
final List<T> newList = new ArrayList<>();
for (final Node node : list) {
final T newNode = clazz.cast(node.accept(visitor));
final T newNode = node == null ? null : clazz.cast(node.accept(visitor));
if (newNode != node) {
changed = true;
}

View File

@ -67,6 +67,8 @@ public final class Symbol implements Comparable<Symbol> {
public static final int IS_INTERNAL = 1 << 9;
/** Is this a function self-reference symbol */
public static final int IS_FUNCTION_SELF = 1 << 10;
/** Is this a specialized param? */
public static final int IS_SPECIALIZED_PARAM = 1 << 11;
/** Null or name identifying symbol. */
private final String name;
@ -383,6 +385,15 @@ public final class Symbol implements Comparable<Symbol> {
return (flags & KINDMASK) == IS_PARAM;
}
/**
* Check if this symbol is a function parameter of known
* narrowest type
* @return true if parameter
*/
public boolean isSpecializedParam() {
return (flags & IS_SPECIALIZED_PARAM) == IS_SPECIALIZED_PARAM;
}
/**
* Check whether this symbol ever has primitive assignments. Conservative
* @return true if primitive assignments exist

View File

@ -794,15 +794,15 @@ public final class NativeRegExp extends ScriptObject {
RegExpResult match;
final int inputLength = string.length();
int lastLength = -1;
int lastIndex = 0;
int lastLastIndex = 0;
int splitLastLength = -1;
int splitLastIndex = 0;
int splitLastLastIndex = 0;
while ((match = execSplit(string, lastIndex)) != null) {
lastIndex = match.getIndex() + match.length();
while ((match = execSplit(string, splitLastIndex)) != null) {
splitLastIndex = match.getIndex() + match.length();
if (lastIndex > lastLastIndex) {
matches.add(string.substring(lastLastIndex, match.getIndex()));
if (splitLastIndex > splitLastLastIndex) {
matches.add(string.substring(splitLastLastIndex, match.getIndex()));
final Object[] groups = match.getGroups();
if (groups.length > 1 && match.getIndex() < inputLength) {
for (int index = 1; index < groups.length && matches.size() < limit; index++) {
@ -810,7 +810,7 @@ public final class NativeRegExp extends ScriptObject {
}
}
lastLength = match.length();
splitLastLength = match.length();
if (matches.size() >= limit) {
break;
@ -818,10 +818,10 @@ public final class NativeRegExp extends ScriptObject {
}
// bump the index to avoid infinite loop
if (lastIndex == lastLastIndex) {
lastIndex++;
if (splitLastIndex == splitLastLastIndex) {
splitLastIndex++;
} else {
lastLastIndex = lastIndex;
splitLastLastIndex = splitLastIndex;
}
}
@ -829,12 +829,12 @@ public final class NativeRegExp extends ScriptObject {
// check special case if we need to append an empty string at the
// end of the match
// if the lastIndex was the entire string
if (lastLastIndex == string.length()) {
if (lastLength > 0 || execSplit("", 0) == null) {
if (splitLastLastIndex == string.length()) {
if (splitLastLength > 0 || execSplit("", 0) == null) {
matches.add("");
}
} else {
matches.add(string.substring(lastLastIndex, inputLength));
matches.add(string.substring(splitLastLastIndex, inputLength));
}
}
@ -899,10 +899,6 @@ public final class NativeRegExp extends ScriptObject {
}
}
private void setGlobal(final boolean global) {
regexp.setGlobal(global);
}
boolean getGlobal() {
return regexp.isGlobal();
}

View File

@ -249,6 +249,7 @@ public abstract class AbstractParser {
*
* @param errorType The error type of the warning
* @param message Warning message.
* @param errorToken error token
*/
protected final void warning(final JSErrorType errorType, final String message, final long errorToken) {
errors.warning(error(errorType, message, errorToken));

View File

@ -305,6 +305,11 @@ loop:
if (isStrictMode) {
flags |= FunctionNode.IS_STRICT;
}
if (env._specialize_calls != null) {
if (env._specialize_calls.contains(name)) {
flags |= FunctionNode.CAN_SPECIALIZE;
}
}
// Start new block.
FunctionNode functionNode =
@ -320,11 +325,11 @@ loop:
kind,
flags);
functionNode = functionNode.setState(lc, errors.hasErrors() ? CompilationState.PARSE_ERROR : CompilationState.PARSED);
lc.push(functionNode);
// Create new block, and just put it on the context stack, restoreFunctionNode() will associate it with the
// FunctionNode.
newBlock();
return functionNode;
}
@ -332,13 +337,18 @@ loop:
* Restore the current block.
*/
private Block restoreBlock(final Block block) {
return lc.pop(block);//.setFlag(lc, flags);
return lc.pop(block);
}
private FunctionNode restoreFunctionNode(final FunctionNode functionNode, final long lastToken) {
final Block newBody = restoreBlock(lc.getFunctionBody(functionNode));
return lc.pop(functionNode).setBody(lc, newBody).setLastToken(lc, lastToken);
return lc.pop(functionNode).
setBody(lc, newBody).
setLastToken(lc, lastToken).
setState(lc, errors.hasErrors() ? CompilationState.PARSE_ERROR : CompilationState.PARSED).
snapshot(lc);
}
/**
@ -529,6 +539,7 @@ loop:
script = restoreFunctionNode(script, token); //commit code
script = script.setBody(lc, script.getBody().setNeedsScope(lc));
return script;
}
@ -800,7 +811,6 @@ loop:
* @param ident Identifier that is verified
* @param contextString String used in error message to give context to the user
*/
@SuppressWarnings("fallthrough")
private void verifyStrictIdent(final IdentNode ident, final String contextString) {
if (isStrictMode) {
switch (ident.getName()) {

View File

@ -88,7 +88,7 @@ final class CompiledFunction implements Comparable<CompiledFunction> {
int weight = Type.typeFor(type.returnType()).getWeight();
for (final Class<?> paramType : type.parameterArray()) {
final int pweight = Type.typeFor(paramType).getWeight();
final int pweight = Type.typeFor(paramType).getWeight() * 2; //params are more important than call types as return values are always specialized
weight += pweight;
}
return weight;

View File

@ -69,5 +69,4 @@ final class CompiledFunctions extends TreeSet<CompiledFunction> {
return best(type).moreGenericThan(type);
}
}

View File

@ -411,7 +411,7 @@ public final class Context {
return ScriptRuntime.apply(func, evalThis);
}
private Source loadInternal(final String srcStr, final String prefix, final String resourcePath) {
private static Source loadInternal(final String srcStr, final String prefix, final String resourcePath) {
if (srcStr.startsWith(prefix)) {
final String resource = resourcePath + srcStr.substring(prefix.length());
// NOTE: even sandbox scripts should be able to load scripts in nashorn: scheme
@ -759,10 +759,10 @@ public final class Context {
final CodeSource cs = url == null ? null : new CodeSource(url, (CodeSigner[])null);
final CodeInstaller<ScriptEnvironment> installer = new ContextCodeInstaller(this, loader, cs);
final Compiler compiler = new Compiler(installer, functionNode, strict);
final Compiler compiler = new Compiler(installer, strict);
compiler.compile();
script = compiler.install();
final FunctionNode newFunctionNode = compiler.compile(functionNode);
script = compiler.install(newFunctionNode);
if (global != null) {
global.cacheClass(source, script);

View File

@ -25,8 +25,6 @@
package jdk.nashorn.internal.runtime;
import static jdk.nashorn.internal.runtime.ScriptObject.isArray;
import java.lang.invoke.MethodHandle;
import java.util.Iterator;
import java.util.List;

View File

@ -30,9 +30,12 @@ import static jdk.nashorn.internal.lookup.Lookup.MH;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.LinkedList;
import jdk.nashorn.internal.codegen.Compiler;
import jdk.nashorn.internal.codegen.CompilerConstants;
import jdk.nashorn.internal.codegen.FunctionSignature;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.FunctionNode;
import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
import jdk.nashorn.internal.parser.Token;
@ -148,10 +151,10 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData {
if (functionNode.isLazy()) {
Compiler.LOG.info("Trampoline hit: need to do lazy compilation of '", functionNode.getName(), "'");
final Compiler compiler = new Compiler(installer, functionNode);
functionNode = compiler.compile();
final Compiler compiler = new Compiler(installer);
functionNode = compiler.compile(functionNode);
assert !functionNode.isLazy();
compiler.install();
compiler.install(functionNode);
// we don't need to update any flags - varArgs and needsCallee are instrincic
// in the function world we need to get a destination node from the compile instead
@ -164,24 +167,115 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData {
assert functionNode.hasState(CompilationState.EMITTED) : functionNode.getName() + " " + functionNode.getState() + " " + Debug.id(functionNode);
// code exists - look it up and add it into the automatically sorted invoker list
code.add(
new CompiledFunction(
addCode(functionNode, null, null);
}
private MethodHandle addCode(final FunctionNode fn, final MethodHandle guard, final MethodHandle fallback) {
final MethodHandle target =
MH.findStatic(
LOOKUP,
functionNode.getCompileUnit().getCode(),
functionNode.getName(),
new FunctionSignature(functionNode).
getMethodType())));
fn.getCompileUnit().getCode(),
fn.getName(),
new FunctionSignature(fn).
getMethodType());
MethodHandle mh = target;
if (guard != null) {
try {
mh = MH.guardWithTest(MH.asCollector(guard, Object[].class, target.type().parameterCount()), MH.asType(target, fallback.type()), fallback);
} catch (Throwable e) {
e.printStackTrace();
}
}
final CompiledFunction cf = new CompiledFunction(mh);
code.add(cf);
return cf.getInvoker();
}
private static Type runtimeType(final Object arg) {
if (arg == null) {
return Type.OBJECT;
}
final Class<?> clazz = arg.getClass();
assert !clazz.isPrimitive() : "always boxed";
if (clazz == Double.class) {
return JSType.isRepresentableAsInt((double)arg) ? Type.INT : Type.NUMBER;
} else if (clazz == Integer.class) {
return Type.INT;
} else if (clazz == Long.class) {
return Type.LONG;
} else if (clazz == String.class) {
return Type.STRING;
}
return Type.OBJECT;
}
@SuppressWarnings("unused")
private static boolean paramTypeGuard(final Type[] compileTimeTypes, final Type[] runtimeTypes, Object... args) {
//System.err.println("Param type guard " + Arrays.asList(args));
return false;
}
private static final MethodHandle PARAM_TYPE_GUARD = findOwnMH("paramTypeGuard", boolean.class, Type[].class, Type[].class, Object[].class);
@Override
MethodHandle getBestInvoker(final MethodType callSiteType, final Object[] args) {
final MethodHandle mh = super.getBestInvoker(callSiteType, args);
if (code.isLessSpecificThan(callSiteType)) {
// opportunity for code specialization - we can regenerate a better version of this method
}
if (!functionNode.canSpecialize() || !code.isLessSpecificThan(callSiteType)) {
return mh;
}
final FunctionNode snapshot = functionNode.getSnapshot();
int i;
//classes known at runtime
final LinkedList<Type> runtimeArgs = new LinkedList<>();
for (i = args.length - 1; i >= args.length - snapshot.getParameters().size(); i--) {
runtimeArgs.addLast(runtimeType(args[i]));
}
//classes known at compile time
final LinkedList<Type> compileTimeArgs = new LinkedList<>();
for (i = callSiteType.parameterCount() - 1; i >= 0 && compileTimeArgs.size() < snapshot.getParameters().size(); i--) {
compileTimeArgs.addLast(Type.typeFor(callSiteType.parameterType(i)));
}
//the classes known at compile time are a safe to generate as primitives without parameter guards
//the classes known at runtime are safe to generate as primitives IFF there are parameter guards
MethodHandle guard = null;
for (i = 0; i < compileTimeArgs.size(); i++) {
final Type runtimeType = runtimeArgs.get(i);
final Type compileType = compileTimeArgs.get(i);
if (compileType.isObject() && !runtimeType.isObject()) {
if (guard == null) {
guard = PARAM_TYPE_GUARD;
guard = MH.insertArguments(guard, 0, compileTimeArgs.toArray(new Type[compileTimeArgs.size()]), runtimeArgs.toArray(new Type[runtimeArgs.size()]));
}
}
}
//System.err.println("Specialized " + name + " " + runtimeArgs + " known=" + compileTimeArgs);
assert snapshot != null;
assert snapshot != functionNode;
final Compiler compiler = new Compiler(installer);
final FunctionNode compiledSnapshot = compiler.compile(snapshot.setHints(null, new Compiler.Hints(compileTimeArgs.toArray(new Type[compileTimeArgs.size()]))));
compiler.install(compiledSnapshot);
final MethodHandle nmh = addCode(compiledSnapshot, guard, mh);
return nmh;
}
private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
return MH.findStatic(MethodHandles.lookup(), RecompilableScriptFunctionData.class, name, MH.type(rtype, types));
}
}

View File

@ -26,9 +26,13 @@
package jdk.nashorn.internal.runtime;
import java.io.PrintWriter;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.TimeZone;
import jdk.nashorn.internal.codegen.Namespace;
import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;
import jdk.nashorn.internal.runtime.options.KeyValueOption;
@ -151,6 +155,9 @@ public final class ScriptEnvironment {
/** is this environment in scripting mode? */
public final boolean _scripting;
/** is the JIT allowed to specializ calls based on callsite types? */
public final Set<String> _specialize_calls;
/** is this environment in strict mode? */
public final boolean _strict;
@ -213,6 +220,17 @@ public final class ScriptEnvironment {
_version = options.getBoolean("version");
_verify_code = options.getBoolean("verify.code");
final String specialize = options.getString("specialize.calls");
if (specialize == null) {
_specialize_calls = null;
} else {
_specialize_calls = new HashSet<>();
final StringTokenizer st = new StringTokenizer(specialize, ",");
while (st.hasMoreElements()) {
_specialize_calls.add(st.nextToken());
}
}
int callSiteFlags = 0;
if (options.getBoolean("profile.callsites")) {
callSiteFlags |= NashornCallSiteDescriptor.CALLSITE_PROFILE;
@ -246,6 +264,18 @@ public final class ScriptEnvironment {
this._locale = Locale.getDefault();
}
/**
* Can we specialize a particular method name?
* @param functionName method name
* @return true if we are allowed to generate versions of this method
*/
public boolean canSpecialize(final String functionName) {
if (_specialize_calls == null) {
return false;
}
return _specialize_calls.isEmpty() || _specialize_calls.contains(functionName);
}
/**
* Get the output stream for this environment
* @return output print writer

View File

@ -662,9 +662,9 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
}
if (deep) {
final ScriptObject proto = getProto();
if(proto != null) {
return proto.findProperty(key, deep, stopOnNonScope, start);
final ScriptObject myProto = getProto();
if (myProto != null) {
return myProto.findProperty(key, deep, stopOnNonScope, start);
}
}

View File

@ -107,16 +107,16 @@ public class DefaultRegExp extends RegExp {
class DefaultMatcher implements RegExpMatcher {
final String input;
final Matcher matcher;
final Matcher defaultMatcher;
DefaultMatcher(final String input) {
this.input = input;
this.matcher = pattern.matcher(input);
this.defaultMatcher = pattern.matcher(input);
}
@Override
public boolean search(final int start) {
return matcher.find(start);
return defaultMatcher.find(start);
}
@Override
@ -126,37 +126,37 @@ public class DefaultRegExp extends RegExp {
@Override
public int start() {
return matcher.start();
return defaultMatcher.start();
}
@Override
public int start(final int group) {
return matcher.start(group);
return defaultMatcher.start(group);
}
@Override
public int end() {
return matcher.end();
return defaultMatcher.end();
}
@Override
public int end(final int group) {
return matcher.end(group);
return defaultMatcher.end(group);
}
@Override
public String group() {
return matcher.group();
return defaultMatcher.group();
}
@Override
public String group(final int group) {
return matcher.group(group);
return defaultMatcher.group(group);
}
@Override
public int groupCount() {
return matcher.groupCount();
return defaultMatcher.groupCount();
}
}

View File

@ -121,16 +121,16 @@ public class JoniRegExp extends RegExp {
class JoniMatcher implements RegExpMatcher {
final String input;
final Matcher matcher;
final Matcher joniMatcher;
JoniMatcher(final String input) {
this.input = input;
this.matcher = regex.matcher(input.toCharArray());
this.joniMatcher = regex.matcher(input.toCharArray());
}
@Override
public boolean search(final int start) {
return matcher.search(start, input.length(), Option.NONE) > -1;
return joniMatcher.search(start, input.length(), Option.NONE) > -1;
}
@Override
@ -140,27 +140,27 @@ public class JoniRegExp extends RegExp {
@Override
public int start() {
return matcher.getBegin();
return joniMatcher.getBegin();
}
@Override
public int start(final int group) {
return group == 0 ? start() : matcher.getRegion().beg[group];
return group == 0 ? start() : joniMatcher.getRegion().beg[group];
}
@Override
public int end() {
return matcher.getEnd();
return joniMatcher.getEnd();
}
@Override
public int end(final int group) {
return group == 0 ? end() : matcher.getRegion().end[group];
return group == 0 ? end() : joniMatcher.getRegion().end[group];
}
@Override
public String group() {
return input.substring(matcher.getBegin(), matcher.getEnd());
return input.substring(joniMatcher.getBegin(), joniMatcher.getEnd());
}
@Override
@ -168,13 +168,13 @@ public class JoniRegExp extends RegExp {
if (group == 0) {
return group();
}
final Region region = matcher.getRegion();
final Region region = joniMatcher.getRegion();
return input.substring(region.beg[group], region.end[group]);
}
@Override
public int groupCount() {
final Region region = matcher.getRegion();
final Region region = joniMatcher.getRegion();
return region == null ? 0 : region.numRegs - 1;
}
}

View File

@ -934,7 +934,7 @@ final class RegExpScanner extends Scanner {
return true;
}
private void unicode(final int value, final StringBuilder buffer) {
private static void unicode(final int value, final StringBuilder buffer) {
final String hex = Integer.toHexString(value);
buffer.append('u');
for (int i = 0; i < 4 - hex.length(); i++) {
@ -944,7 +944,7 @@ final class RegExpScanner extends Scanner {
}
// Convert what would have been a backreference into a unicode escape, or a number literal, or both.
private void octalOrLiteral(final String numberLiteral, final StringBuilder buffer) {
private static void octalOrLiteral(final String numberLiteral, final StringBuilder buffer) {
final int length = numberLiteral.length();
int octalValue = 0;
int pos = 0;

View File

@ -288,12 +288,12 @@ nashorn.option.scripting = { \
dependency="--anon-functions=true" \
}
nashorn.option.timezone = { \
name="-timezone", \
short_name="-t", \
params="<timezone>", \
desc="Set timezone for script execution.", \
type=TimeZone \
nashorn.option.specialize.calls = { \
name="--specialize-calls", \
is_undocumented=true, \
type=String, \
params="[=function_1,...,function_n]", \
desc="Specialize all or a set of method according to callsite parameter types" \
}
nashorn.option.stdout = { \
@ -312,6 +312,14 @@ nashorn.option.stderr = { \
desc="Redirect stderr to a filename or to another tty, e.g. stdout" \
}
nashorn.option.timezone = { \
name="-timezone", \
short_name="-t", \
params="<timezone>", \
desc="Set timezone for script execution.", \
type=TimeZone \
}
nashorn.option.trace.callsites = { \
name="--trace-callsites", \
short_name="-tcs", \

View File

@ -270,7 +270,7 @@ public class Shell {
}
//null - pass no code installer - this is compile only
new Compiler(env, functionNode).compile();
new Compiler(env).compile(functionNode);
}
} finally {
env.getOut().flush();

View File

@ -0,0 +1,38 @@
/*
* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* paramspec - test that Attr doesn't break parameters when specializing
*
* @run
* @test
*/
function f(a) {
var b = ~a;
return b == ~17;
}
print(f("17"));
print(f(17));

View File

@ -0,0 +1,2 @@
true
true

View File

@ -86,7 +86,7 @@ function runsuite(tests) {
changed = true;
}
} catch (e) {
print("error: " + e);
print("error: " + e.printStackTrace());
if (e.toString().indexOf(tests) == 1) {
throw e;
}

View File

@ -90,16 +90,17 @@ var methodsCalled = [
function check(str, strs) {
for each (s in strs) {
if (s.indexOf(str) !== -1) {
if (str.indexOf(s) !== -1) {
continue;
}
print(method + "not found");
print(s + " not found");
return;
}
print("check ok!");
}
str = runScriptEngine([ "--log=codegen,compiler=finest,methodhandles=finest,fields=finest" ], __DIR__ + "../basic/NASHORN-19.js");
str += runScriptEngine([ "--log=codegen,compiler=finest,methodhandles=finest,fields=finest" ], __DIR__ + "../basic/varargs.js");
check(str, methodsCalled);
check(str, ['return', 'get', 'set', '[fields]']);