8042118: Separate types from symbols

Reviewed-by: hannesw, lagergren
This commit is contained in:
Attila Szegedi 2014-05-13 11:30:40 +02:00
parent 31c5e7065a
commit 64fd9c191a
119 changed files with 8363 additions and 7513 deletions

View file

@ -281,7 +281,7 @@ public class TypeUtilities {
}
if(sourceType.isPrimitive()) {
if(sourceType == void.class) {
return true; // Void can be losslessly represented by any type
return false; // Void can't be losslessly represented by any type
}
if(targetType.isPrimitive()) {
return isProperPrimitiveLosslessSubtype(sourceType, targetType);

View file

@ -164,7 +164,7 @@ public final class NashornScriptEngineFactory implements ScriptEngineFactory {
* @param args arguments array passed to script engine.
* @return newly created script engine.
*/
public ScriptEngine getScriptEngine(final String[] args) {
public ScriptEngine getScriptEngine(final String... args) {
checkConfigPermission();
return new NashornScriptEngine(this, args, getAppClassLoader());
}

View file

@ -35,7 +35,6 @@ import java.util.Deque;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import jdk.nashorn.internal.ir.AccessNode;
import jdk.nashorn.internal.ir.CallNode;
import jdk.nashorn.internal.ir.Expression;
@ -45,12 +44,12 @@ import jdk.nashorn.internal.ir.LexicalContext;
import jdk.nashorn.internal.ir.Node;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.objects.Global;
import jdk.nashorn.internal.runtime.logging.DebugLogger;
import jdk.nashorn.internal.runtime.logging.Loggable;
import jdk.nashorn.internal.runtime.logging.Logger;
import jdk.nashorn.internal.runtime.Context;
import jdk.nashorn.internal.runtime.Debug;
import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData;
import jdk.nashorn.internal.runtime.logging.DebugLogger;
import jdk.nashorn.internal.runtime.logging.Loggable;
import jdk.nashorn.internal.runtime.logging.Logger;
import jdk.nashorn.internal.runtime.options.Options;
/**
@ -59,7 +58,6 @@ import jdk.nashorn.internal.runtime.options.Options;
* introduces expensive args collection and boxing
*
* <pre>
* {@code
* var Class = {
* create: function() {
* return function() { //vararg
@ -80,7 +78,6 @@ import jdk.nashorn.internal.runtime.options.Options;
* }
*
* new Color(17, 47, 11);
* }
* </pre>
*/
@ -303,16 +300,9 @@ public final class ApplySpecialization implements Loggable {
return finish();
}
private static boolean isApply(final Node node) {
if (node instanceof AccessNode) {
return isApply(((AccessNode)node).getProperty());
}
return node instanceof IdentNode && "apply".equals(((IdentNode)node).getName());
}
private static boolean isApply(final CallNode callNode) {
final Expression f = callNode.getFunction();
return f instanceof AccessNode && isApply(f);
return f instanceof AccessNode && "apply".equals(((AccessNode)f).getProperty());
}
}

View file

@ -0,0 +1,946 @@
/*
* 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.
*/
package jdk.nashorn.internal.codegen;
import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS;
import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS_VAR;
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.RETURN;
import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
import static jdk.nashorn.internal.codegen.CompilerConstants.SWITCH_TAG_PREFIX;
import static jdk.nashorn.internal.codegen.CompilerConstants.THIS;
import static jdk.nashorn.internal.codegen.CompilerConstants.VARARGS;
import static jdk.nashorn.internal.ir.Symbol.HAS_OBJECT_VALUE;
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_PROGRAM_LEVEL;
import static jdk.nashorn.internal.ir.Symbol.IS_SCOPE;
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;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
import jdk.nashorn.internal.ir.AccessNode;
import jdk.nashorn.internal.ir.BinaryNode;
import jdk.nashorn.internal.ir.Block;
import jdk.nashorn.internal.ir.CatchNode;
import jdk.nashorn.internal.ir.Expression;
import jdk.nashorn.internal.ir.ForNode;
import jdk.nashorn.internal.ir.FunctionNode;
import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
import jdk.nashorn.internal.ir.IdentNode;
import jdk.nashorn.internal.ir.IndexNode;
import jdk.nashorn.internal.ir.LexicalContext;
import jdk.nashorn.internal.ir.LexicalContextNode;
import jdk.nashorn.internal.ir.LiteralNode;
import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode;
import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode.ArrayUnit;
import jdk.nashorn.internal.ir.Node;
import jdk.nashorn.internal.ir.RuntimeNode;
import jdk.nashorn.internal.ir.RuntimeNode.Request;
import jdk.nashorn.internal.ir.SplitNode;
import jdk.nashorn.internal.ir.Statement;
import jdk.nashorn.internal.ir.SwitchNode;
import jdk.nashorn.internal.ir.Symbol;
import jdk.nashorn.internal.ir.TryNode;
import jdk.nashorn.internal.ir.UnaryNode;
import jdk.nashorn.internal.ir.VarNode;
import jdk.nashorn.internal.ir.WithNode;
import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.Context;
import jdk.nashorn.internal.runtime.Property;
import jdk.nashorn.internal.runtime.PropertyMap;
import jdk.nashorn.internal.runtime.logging.DebugLogger;
import jdk.nashorn.internal.runtime.logging.Loggable;
import jdk.nashorn.internal.runtime.logging.Logger;
/**
* This visitor assigns symbols to identifiers denoting variables. It does few more minor calculations that are only
* possible after symbols have been assigned; such is the transformation of "delete" and "typeof" operators into runtime
* nodes and counting of number of properties assigned to "this" in constructor functions. This visitor is also notable
* for what it doesn't do, most significantly it does no type calculations as in JavaScript variables can change types
* during runtime and as such symbols don't have types. Calculation of expression types is performed by a separate
* visitor.
*/
@Logger(name="symbols")
final class AssignSymbols extends NodeOperatorVisitor<LexicalContext> implements Loggable {
private final DebugLogger log;
private final boolean debug;
private static boolean isParamOrVar(final IdentNode identNode) {
final Symbol symbol = identNode.getSymbol();
return symbol.isParam() || symbol.isVar();
}
private static String name(final Node node) {
final String cn = node.getClass().getName();
final int lastDot = cn.lastIndexOf('.');
if (lastDot == -1) {
return cn;
}
return cn.substring(lastDot + 1);
}
/**
* Checks if various symbols that were provisionally marked as needing a slot ended up unused, and marks them as not
* needing a slot after all.
* @param functionNode the function node
* @return the passed in node, for easy chaining
*/
private static FunctionNode removeUnusedSlots(final FunctionNode functionNode) {
if (!functionNode.needsCallee()) {
functionNode.compilerConstant(CALLEE).setNeedsSlot(false);
}
if (!(functionNode.hasScopeBlock() || functionNode.needsParentScope())) {
functionNode.compilerConstant(SCOPE).setNeedsSlot(false);
}
if (!functionNode.usesReturnSymbol()) {
functionNode.compilerConstant(RETURN).setNeedsSlot(false);
}
// Named function expressions that end up not referencing themselves won't need a local slot for the self symbol.
if(!functionNode.isDeclared() && !functionNode.usesSelfSymbol() && !functionNode.isAnonymous()) {
final Symbol selfSymbol = functionNode.getBody().getExistingSymbol(functionNode.getIdent().getName());
if(selfSymbol != null) {
if(selfSymbol.isFunctionSelf()) {
selfSymbol.setNeedsSlot(false);
selfSymbol.clearFlag(Symbol.IS_VAR);
}
} else {
assert functionNode.isProgram();
}
}
return functionNode;
}
private final Deque<Set<String>> thisProperties = new ArrayDeque<>();
private final Map<String, Symbol> globalSymbols = new HashMap<>(); //reuse the same global symbol
private final CompilationEnvironment env;
public AssignSymbols(final CompilationEnvironment env) {
super(new LexicalContext());
this.env = env;
this.log = initLogger(env.getContext());
this.debug = log.isEnabled();
}
@Override
public DebugLogger getLogger() {
return log;
}
@Override
public DebugLogger initLogger(final Context context) {
return context.getLogger(this.getClass());
}
/**
* Define symbols for all variable declarations at the top of the function scope. This way we can get around
* problems like
*
* while (true) {
* break;
* if (true) {
* var s;
* }
* }
*
* to an arbitrary nesting depth.
*
* see NASHORN-73
*
* @param functionNode the FunctionNode we are entering
* @param body the body of the FunctionNode we are entering
*/
private void acceptDeclarations(final FunctionNode functionNode, final Block body) {
// This visitor will assign symbol to all declared variables, except function declarations (which are taken care
// in a separate step above) and "var" declarations in for loop initializers.
//
body.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
@Override
public boolean enterFunctionNode(final FunctionNode nestedFn) {
// Don't descend into nested functions
return false;
}
@Override
public Node leaveVarNode(final VarNode varNode) {
if (varNode.isStatement()) {
final IdentNode ident = varNode.getName();
final Symbol symbol = defineSymbol(body, ident.getName(), IS_VAR);
functionNode.addDeclaredSymbol(symbol);
if (varNode.isFunctionDeclaration()) {
symbol.setIsFunctionDeclaration();
}
return varNode.setName((IdentNode)ident.setSymbol(symbol));
}
return varNode;
}
});
}
private IdentNode compilerConstantIdentifier(CompilerConstants cc) {
return (IdentNode)createImplicitIdentifier(cc.symbolName()).setSymbol(lc.getCurrentFunction().compilerConstant(cc));
}
/**
* Creates an ident node for an implicit identifier within the function (one not declared in the script source
* code). These identifiers are defined with function's token and finish.
* @param name the name of the identifier
* @return an ident node representing the implicit identifier.
*/
private IdentNode createImplicitIdentifier(final String name) {
final FunctionNode fn = lc.getCurrentFunction();
return new IdentNode(fn.getToken(), fn.getFinish(), name);
}
private Symbol createSymbol(final String name, final int flags) {
if ((flags & Symbol.KINDMASK) == IS_GLOBAL) {
//reuse global symbols so they can be hashed
Symbol global = globalSymbols.get(name);
if (global == null) {
global = new Symbol(name, flags);
globalSymbols.put(name, global);
}
return global;
}
return new Symbol(name, flags);
}
/**
* Creates a synthetic initializer for a variable (a var statement that doesn't occur in the source code). Typically
* used to create assignmnent of {@code :callee} to the function name symbol in self-referential function
* expressions as well as for assignment of {@code :arguments} to {@code arguments}.
*
* @param name the ident node identifying the variable to initialize
* @param initConstant the compiler constant it is initialized to
* @param fn the function node the assignment is for
* @return a var node with the appropriate assignment
*/
private VarNode createSyntheticInitializer(final IdentNode name, final CompilerConstants initConstant, final FunctionNode fn) {
final IdentNode init = compilerConstantIdentifier(initConstant);
assert init.getSymbol() != null && init.getSymbol().isBytecodeLocal();
final VarNode synthVar = new VarNode(fn.getLineNumber(), fn.getToken(), fn.getFinish(), name, init);
final Symbol nameSymbol = fn.getBody().getExistingSymbol(name.getName());
assert nameSymbol != null;
return (VarNode)synthVar.setName((IdentNode)name.setSymbol(nameSymbol)).accept(this);
}
private FunctionNode createSyntheticInitializers(final FunctionNode functionNode) {
final List<VarNode> syntheticInitializers = new ArrayList<>(2);
// Must visit the new var nodes in the context of the body. We could also just set the new statements into the
// block and then revisit the entire block, but that seems to be too much double work.
final Block body = functionNode.getBody();
lc.push(body);
try {
if (functionNode.usesSelfSymbol()) {
// "var fn = :callee"
syntheticInitializers.add(createSyntheticInitializer(functionNode.getIdent(), CALLEE, functionNode));
}
if (functionNode.needsArguments()) {
// "var arguments = :arguments"
syntheticInitializers.add(createSyntheticInitializer(createImplicitIdentifier(ARGUMENTS_VAR.symbolName()),
ARGUMENTS, functionNode));
}
if (syntheticInitializers.isEmpty()) {
return functionNode;
}
for(final ListIterator<VarNode> it = syntheticInitializers.listIterator(); it.hasNext();) {
it.set((VarNode)it.next().accept(this));
}
} finally {
lc.pop(body);
}
final List<Statement> stmts = body.getStatements();
final List<Statement> newStatements = new ArrayList<>(stmts.size() + syntheticInitializers.size());
newStatements.addAll(syntheticInitializers);
newStatements.addAll(stmts);
return functionNode.setBody(lc, body.setStatements(lc, newStatements));
}
private Symbol defineGlobalSymbol(final Block block, final String name) {
return defineSymbol(block, name, IS_GLOBAL);
}
/**
* Defines a new symbol in the given block.
*
* @param block the block in which to define the symbol
* @param name name of symbol.
* @param symbolFlags Symbol flags.
*
* @return Symbol for given name or null for redefinition.
*/
private Symbol defineSymbol(final Block block, final String name, final int symbolFlags) {
int flags = symbolFlags;
Symbol symbol = findSymbol(block, name); // Locate symbol.
final boolean isGlobal = (flags & KINDMASK) == IS_GLOBAL;
// Global variables are implicitly always scope variables too.
if (isGlobal) {
flags |= IS_SCOPE;
}
if (lc.getCurrentFunction().isProgram()) {
flags |= IS_PROGRAM_LEVEL;
}
final boolean isParam = (flags & KINDMASK) == IS_PARAM;
final boolean isVar = (flags & KINDMASK) == IS_VAR;
final FunctionNode function = lc.getFunction(block);
if (symbol != null) {
// Symbol was already defined. Check if it needs to be redefined.
if (isParam) {
if (!isLocal(function, symbol)) {
// Not defined in this function. Create a new definition.
symbol = null;
} else if (symbol.isParam()) {
// Duplicate parameter. Null return will force an error.
throw new AssertionError("duplicate parameter");
}
} else if (isVar) {
if ((flags & IS_INTERNAL) == IS_INTERNAL || (flags & IS_LET) == IS_LET) {
// Always create a new definition.
symbol = null;
} else {
// Not defined in this function. Create a new definition.
if (!isLocal(function, symbol) || symbol.less(IS_VAR)) {
symbol = null;
}
}
}
}
if (symbol == null) {
// If not found, then create a new one.
Block symbolBlock;
// Determine where to create it.
if (isVar && ((flags & IS_INTERNAL) == IS_INTERNAL || (flags & IS_LET) == IS_LET)) {
symbolBlock = block; //internal vars are always defined in the block closest to them
} else if (isGlobal) {
symbolBlock = lc.getOutermostFunction().getBody();
} else {
symbolBlock = lc.getFunctionBody(function);
}
// Create and add to appropriate block.
symbol = createSymbol(name, flags);
symbolBlock.putSymbol(lc, symbol);
if ((flags & IS_SCOPE) == 0) {
// Initial assumption; symbol can lose its slot later
symbol.setNeedsSlot(true);
}
} else if (symbol.less(flags)) {
symbol.setFlags(flags);
}
if((isVar || isParam) && env.useOptimisticTypes() && env.isOnDemandCompilation()) {
env.declareLocalSymbol(name);
}
return symbol;
}
private <T extends Node> T end(final T node) {
return end(node, true);
}
private <T extends Node> T end(final T node, final boolean printNode) {
if (debug) {
final StringBuilder sb = new StringBuilder();
sb.append("[LEAVE ").
append(name(node)).
append("] ").
append(printNode ? node.toString() : "").
append(" in '").
append(lc.getCurrentFunction().getName()).
append('\'');
if (node instanceof IdentNode) {
final Symbol symbol = ((IdentNode)node).getSymbol();
if (symbol == null) {
sb.append(" <NO SYMBOL>");
} else {
sb.append(" <symbol=").append(symbol).append('>');
}
}
log.unindent();
log.info(sb);
}
return node;
}
@Override
public boolean enterBlock(final Block block) {
start(block);
block.clearSymbols();
if (lc.isFunctionBody()) {
enterFunctionBody();
}
return true;
}
@Override
public boolean enterCatchNode(final CatchNode catchNode) {
final IdentNode exception = catchNode.getException();
final Block block = lc.getCurrentBlock();
start(catchNode);
// define block-local exception variable
final String exname = exception.getName();
// If the name of the exception starts with ":e", this is a synthetic catch block, likely a catch-all. Its
// symbol is naturally internal, and should be treated as such.
final boolean isInternal = exname.startsWith(EXCEPTION_PREFIX.symbolName());
defineSymbol(block, exname, IS_VAR | IS_LET | (isInternal ? IS_INTERNAL : 0) | HAS_OBJECT_VALUE);
return true;
}
private void enterFunctionBody() {
final FunctionNode functionNode = lc.getCurrentFunction();
final Block body = lc.getCurrentBlock();
initFunctionWideVariables(functionNode, body);
if (functionNode.isProgram()) {
initGlobalSymbols(body);
} else if (!functionNode.isDeclared() && !functionNode.isAnonymous()) {
// It's neither declared nor program - it's a function expression then; assign it a self-symbol unless it's
// anonymous.
final String name = functionNode.getIdent().getName();
assert name != null;
assert body.getExistingSymbol(name) == null;
defineSymbol(body, name, IS_VAR | IS_FUNCTION_SELF | HAS_OBJECT_VALUE);
if(functionNode.allVarsInScope()) { // basically, has deep eval
lc.setFlag(functionNode, FunctionNode.USES_SELF_SYMBOL);
}
}
acceptDeclarations(functionNode, body);
}
@Override
public boolean enterFunctionNode(final FunctionNode functionNode) {
// TODO: once we have information on symbols used by nested functions, we can stop descending into nested
// functions with on-demand compilation, e.g. add
// if(!thisProperties.isEmpty() && env.isOnDemandCompilation()) {
// return false;
// }
start(functionNode, false);
thisProperties.push(new HashSet<String>());
//an outermost function in our lexical context that is not a program
//is possible - it is a function being compiled lazily
if (functionNode.isDeclared()) {
final Iterator<Block> blocks = lc.getBlocks();
if (blocks.hasNext()) {
defineSymbol(blocks.next(), functionNode.getIdent().getName(), IS_VAR);
}
}
return true;
}
@Override
public boolean enterVarNode(final VarNode varNode) {
start(varNode);
defineSymbol(lc.getCurrentBlock(), varNode.getName().getName(), IS_VAR | (lc.getCurrentFunction().isProgram() ? IS_SCOPE : 0));
return true;
}
private Symbol exceptionSymbol() {
return newObjectInternal(EXCEPTION_PREFIX);
}
/**
* This has to run before fix assignment types, store any type specializations for
* paramters, then turn then to objects for the generic version of this method
*
* @param functionNode functionNode
*/
private FunctionNode finalizeParameters(final FunctionNode functionNode) {
final List<IdentNode> newParams = new ArrayList<>();
final boolean isVarArg = functionNode.isVarArg();
final Block body = functionNode.getBody();
for (final IdentNode param : functionNode.getParameters()) {
final Symbol paramSymbol = body.getExistingSymbol(param.getName());
assert paramSymbol != null;
assert paramSymbol.isParam() : paramSymbol + " " + paramSymbol.getFlags();
newParams.add((IdentNode)param.setSymbol(paramSymbol));
// parameters should not be slots for a function that uses variable arity signature
if (isVarArg) {
paramSymbol.setNeedsSlot(false);
}
}
return functionNode.setParameters(lc, newParams);
}
/**
* Search for symbol in the lexical context starting from the given block.
* @param name Symbol name.
* @return Found symbol or null if not found.
*/
private Symbol findSymbol(final Block block, final String name) {
for (final Iterator<Block> blocks = lc.getBlocks(block); blocks.hasNext();) {
final Symbol symbol = blocks.next().getExistingSymbol(name);
if (symbol != null) {
return symbol;
}
}
return null;
}
/**
* Marks the current function as one using any global symbol. The function and all its parent functions will all be
* marked as needing parent scope.
* @see FunctionNode#needsParentScope()
*/
private void functionUsesGlobalSymbol() {
for (final Iterator<FunctionNode> fns = lc.getFunctions(); fns.hasNext();) {
lc.setFlag(fns.next(), FunctionNode.USES_ANCESTOR_SCOPE);
}
}
/**
* Marks the current function as one using a scoped symbol. The block defining the symbol will be marked as needing
* its own scope to hold the variable. If the symbol is defined outside of the current function, it and all
* functions up to (but not including) the function containing the defining block will be marked as needing parent
* function scope.
* @see FunctionNode#needsParentScope()
*/
private void functionUsesScopeSymbol(final Symbol symbol) {
final String name = symbol.getName();
for (final Iterator<LexicalContextNode> contextNodeIter = lc.getAllNodes(); contextNodeIter.hasNext(); ) {
final LexicalContextNode node = contextNodeIter.next();
if (node instanceof Block) {
final Block block = (Block)node;
if (block.getExistingSymbol(name) != null) {
assert lc.contains(block);
lc.setBlockNeedsScope(block);
break;
}
} else if (node instanceof FunctionNode) {
lc.setFlag(node, FunctionNode.USES_ANCESTOR_SCOPE);
}
}
}
/**
* Declares that the current function is using the symbol.
* @param symbol the symbol used by the current function.
*/
private void functionUsesSymbol(final Symbol symbol) {
assert symbol != null;
if (symbol.isScope()) {
if (symbol.isGlobal()) {
functionUsesGlobalSymbol();
} else {
functionUsesScopeSymbol(symbol);
}
} else {
assert !symbol.isGlobal(); // Every global is also scope
}
}
private void initCompileConstant(final CompilerConstants cc, final Block block, final int flags) {
defineSymbol(block, cc.symbolName(), flags).setNeedsSlot(true);
}
private void initFunctionWideVariables(final FunctionNode functionNode, final Block body) {
initCompileConstant(CALLEE, body, IS_PARAM | IS_INTERNAL | HAS_OBJECT_VALUE);
initCompileConstant(THIS, body, IS_PARAM | IS_THIS | HAS_OBJECT_VALUE);
if (functionNode.isVarArg()) {
initCompileConstant(VARARGS, body, IS_PARAM | IS_INTERNAL | HAS_OBJECT_VALUE);
if (functionNode.needsArguments()) {
initCompileConstant(ARGUMENTS, body, IS_VAR | IS_INTERNAL | HAS_OBJECT_VALUE);
defineSymbol(body, ARGUMENTS_VAR.symbolName(), IS_VAR | HAS_OBJECT_VALUE);
}
}
initParameters(functionNode, body);
initCompileConstant(SCOPE, body, IS_VAR | IS_INTERNAL | HAS_OBJECT_VALUE);
initCompileConstant(RETURN, body, IS_VAR | IS_INTERNAL);
}
/**
* Move any properties from the global map into the scope of this function (which must be a program function).
* @param block the function node body for which to init scope vars
*/
private void initGlobalSymbols(final Block block) {
final PropertyMap map = Context.getGlobalMap();
for (final Property property : map.getProperties()) {
final Symbol symbol = defineGlobalSymbol(block, property.getKey());
log.info("Added global symbol from property map ", symbol);
}
}
/**
* Initialize parameters for function node.
* @param functionNode the function node
*/
private void initParameters(final FunctionNode functionNode, final Block body) {
final boolean isVarArg = functionNode.isVarArg();
final boolean scopeParams = functionNode.allVarsInScope() || isVarArg;
for (final IdentNode param : functionNode.getParameters()) {
final Symbol symbol = defineSymbol(body, param.getName(), IS_PARAM);
if(scopeParams) {
// NOTE: this "set is scope" is a poor substitute for clear expression of where the symbol is stored.
// It will force creation of scopes where they would otherwise not necessarily be needed (functions
// using arguments object and other variable arity functions). Tracked by JDK-8038942.
symbol.setIsScope();
assert symbol.hasSlot();
if(isVarArg) {
symbol.setNeedsSlot(false);
}
}
}
}
/**
* Is the symbol local to (that is, defined in) the specified function?
* @param function the function
* @param symbol the symbol
* @return true if the symbol is defined in the specified function
*/
private boolean isLocal(final FunctionNode function, final Symbol symbol) {
final FunctionNode definingFn = lc.getDefiningFunction(symbol);
assert definingFn != null;
return definingFn == function;
}
@Override
public Node leaveASSIGN(final BinaryNode binaryNode) {
// If we're assigning a property of the this object ("this.foo = ..."), record it.
final Expression lhs = binaryNode.lhs();
if (lhs instanceof AccessNode) {
final AccessNode accessNode = (AccessNode) lhs;
final Expression base = accessNode.getBase();
if (base instanceof IdentNode) {
final Symbol symbol = ((IdentNode)base).getSymbol();
if(symbol.isThis()) {
thisProperties.peek().add(accessNode.getProperty());
}
}
}
return binaryNode;
}
@Override
public Node leaveDELETE(final UnaryNode unaryNode) {
final FunctionNode currentFunctionNode = lc.getCurrentFunction();
final boolean strictMode = currentFunctionNode.isStrict();
final Expression rhs = unaryNode.getExpression();
final Expression strictFlagNode = (Expression)LiteralNode.newInstance(unaryNode, strictMode).accept(this);
Request request = Request.DELETE;
final List<Expression> args = new ArrayList<>();
if (rhs instanceof IdentNode) {
final IdentNode ident = (IdentNode)rhs;
// If this is a declared variable or a function parameter, delete always fails (except for globals).
final String name = ident.getName();
final Symbol symbol = ident.getSymbol();
final boolean failDelete = strictMode || symbol.isParam() || (symbol.isVar() && !symbol.isProgramLevel());
if (failDelete && symbol.isThis()) {
return LiteralNode.newInstance(unaryNode, true).accept(this);
}
final Expression literalNode = (Expression)LiteralNode.newInstance(unaryNode, name).accept(this);
if (!failDelete) {
args.add(compilerConstantIdentifier(SCOPE));
}
args.add(literalNode);
args.add(strictFlagNode);
if (failDelete) {
request = Request.FAIL_DELETE;
}
} else if (rhs instanceof AccessNode) {
final Expression base = ((AccessNode)rhs).getBase();
final String property = ((AccessNode)rhs).getProperty();
args.add(base);
args.add((Expression)LiteralNode.newInstance(unaryNode, property).accept(this));
args.add(strictFlagNode);
} else if (rhs instanceof IndexNode) {
final IndexNode indexNode = (IndexNode)rhs;
final Expression base = indexNode.getBase();
final Expression index = indexNode.getIndex();
args.add(base);
args.add(index);
args.add(strictFlagNode);
} else {
return LiteralNode.newInstance(unaryNode, true).accept(this);
}
return new RuntimeNode(unaryNode, request, args).accept(this);
}
@Override
public Node leaveForNode(final ForNode forNode) {
if (forNode.isForIn()) {
forNode.setIterator(newObjectInternal(ITERATOR_PREFIX)); //NASHORN-73
}
return end(forNode);
}
@Override
public Node leaveFunctionNode(FunctionNode functionNode) {
return markProgramBlock(
removeUnusedSlots(
createSyntheticInitializers(
finalizeParameters(
lc.applyTopFlags(functionNode))))
.setThisProperties(lc, thisProperties.pop().size())
.setState(lc, CompilationState.SYMBOLS_ASSIGNED));
}
@Override
public Node leaveIdentNode(final IdentNode identNode) {
final String name = identNode.getName();
if (identNode.isPropertyName()) {
return identNode;
}
final Block block = lc.getCurrentBlock();
Symbol symbol = findSymbol(block, name);
//If an existing symbol with the name is found, use that otherwise, declare a new one
if (symbol != null) {
log.info("Existing symbol = ", symbol);
if (symbol.isFunctionSelf()) {
final FunctionNode functionNode = lc.getDefiningFunction(symbol);
assert functionNode != null;
assert lc.getFunctionBody(functionNode).getExistingSymbol(CALLEE.symbolName()) != null;
lc.setFlag(functionNode, FunctionNode.USES_SELF_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 as global: ", symbol);
symbol = defineGlobalSymbol(block, name);
Symbol.setSymbolIsScope(lc, symbol);
}
functionUsesSymbol(symbol);
if (!identNode.isInitializedHere()) {
symbol.increaseUseCount();
}
return end(identNode.setSymbol(symbol));
}
@Override
public Node leaveSwitchNode(final SwitchNode switchNode) {
// We only need a symbol for the tag if it's not an integer switch node
if(!switchNode.isInteger()) {
switchNode.setTag(newObjectInternal(SWITCH_TAG_PREFIX));
}
return switchNode;
}
@Override
public Node leaveTryNode(final TryNode tryNode) {
tryNode.setException(exceptionSymbol());
if (tryNode.getFinallyBody() != null) {
tryNode.setFinallyCatchAll(exceptionSymbol());
}
end(tryNode);
return tryNode;
}
@Override
public Node leaveTYPEOF(final UnaryNode unaryNode) {
final Expression rhs = unaryNode.getExpression();
final List<Expression> args = new ArrayList<>();
if (rhs instanceof IdentNode && !isParamOrVar((IdentNode)rhs)) {
args.add(compilerConstantIdentifier(SCOPE));
args.add((Expression)LiteralNode.newInstance(rhs, ((IdentNode)rhs).getName()).accept(this)); //null
} else {
args.add(rhs);
args.add((Expression)LiteralNode.newInstance(unaryNode).accept(this)); //null, do not reuse token of identifier rhs, it can be e.g. 'this'
}
final Node runtimeNode = new RuntimeNode(unaryNode, Request.TYPEOF, args).accept(this);
end(unaryNode);
return runtimeNode;
}
private FunctionNode markProgramBlock(FunctionNode functionNode) {
if (env.isOnDemandCompilation() || !functionNode.isProgram()) {
return functionNode;
}
assert functionNode.getId() == 1;
return functionNode.setBody(lc, functionNode.getBody().setFlag(lc, Block.IS_GLOBAL_SCOPE));
}
/**
* If the symbol isn't already a scope symbol, but it needs to be (see {@link #symbolNeedsToBeScope(Symbol)}, it is
* promoted to a scope symbol and its block marked as needing a scope.
* @param symbol the symbol that might be scoped
*/
private void maybeForceScope(final Symbol symbol) {
if (!symbol.isScope() && symbolNeedsToBeScope(symbol)) {
Symbol.setSymbolIsScope(lc, symbol);
}
}
private Symbol newInternal(final CompilerConstants cc, final int flags) {
return defineSymbol(lc.getCurrentBlock(), lc.getCurrentFunction().uniqueName(cc.symbolName()), IS_VAR | IS_INTERNAL | flags); //NASHORN-73
}
private Symbol newObjectInternal(final CompilerConstants cc) {
return newInternal(cc, HAS_OBJECT_VALUE);
}
private boolean start(final Node node) {
return start(node, true);
}
private boolean start(final Node node, final boolean printNode) {
if (debug) {
final StringBuilder sb = new StringBuilder();
sb.append("[ENTER ").
append(name(node)).
append("] ").
append(printNode ? node.toString() : "").
append(" in '").
append(lc.getCurrentFunction().getName()).
append("'");
log.info(sb);
log.indent();
}
return true;
}
/**
* Determines if the symbol has to be a scope symbol. In general terms, it has to be a scope symbol if it can only
* be reached from the current block by traversing a function node, a split node, or a with node.
* @param symbol the symbol checked for needing to be a scope symbol
* @return true if the symbol has to be a scope symbol.
*/
private boolean symbolNeedsToBeScope(final Symbol symbol) {
if (symbol.isThis() || symbol.isInternal()) {
return false;
}
if (lc.getCurrentFunction().allVarsInScope()) {
return true;
}
boolean previousWasBlock = false;
for (final Iterator<LexicalContextNode> it = lc.getAllNodes(); it.hasNext();) {
final LexicalContextNode node = it.next();
if (node instanceof FunctionNode || node instanceof SplitNode || isSplitArray(node)) {
// We reached the function boundary or a splitting boundary without seeing a definition for the symbol.
// It needs to be in scope.
return true;
} else if (node instanceof WithNode) {
if (previousWasBlock) {
// We reached a WithNode; the symbol must be scoped. Note that if the WithNode was not immediately
// preceded by a block, this means we're currently processing its expression, not its body,
// therefore it doesn't count.
return true;
}
previousWasBlock = false;
} else if (node instanceof Block) {
if (((Block)node).getExistingSymbol(symbol.getName()) == symbol) {
// We reached the block that defines the symbol without reaching either the function boundary, or a
// WithNode. The symbol need not be scoped.
return false;
}
previousWasBlock = true;
} else {
previousWasBlock = false;
}
}
throw new AssertionError();
}
private static boolean isSplitArray(final LexicalContextNode expr) {
if(!(expr instanceof ArrayLiteralNode)) {
return false;
}
final List<ArrayUnit> units = ((ArrayLiteralNode)expr).getUnits();
return !(units == null || units.isEmpty());
}
}

File diff suppressed because it is too large Load diff

View file

@ -32,10 +32,10 @@ import static jdk.nashorn.internal.codegen.Condition.LE;
import static jdk.nashorn.internal.codegen.Condition.LT;
import static jdk.nashorn.internal.codegen.Condition.NE;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.BinaryNode;
import jdk.nashorn.internal.ir.Expression;
import jdk.nashorn.internal.ir.TernaryNode;
import jdk.nashorn.internal.ir.JoinPredecessorExpression;
import jdk.nashorn.internal.ir.LocalVariableConversion;
import jdk.nashorn.internal.ir.UnaryNode;
/**
@ -71,13 +71,7 @@ final class BranchOptimizer {
break;
}
// convert to boolean
codegen.load(unaryNode, Type.BOOLEAN);
if (state) {
method.ifne(label);
} else {
method.ifeq(label);
}
loadTestAndJump(unaryNode, label, state);
}
private void branchOptimizer(final BinaryNode binaryNode, final Label label, final boolean state) {
@ -88,56 +82,56 @@ final class BranchOptimizer {
case AND:
if (state) {
final Label skip = new Label("skip");
branchOptimizer(lhs, skip, false);
branchOptimizer(rhs, label, true);
optimizeLogicalOperand(lhs, skip, false, false);
optimizeLogicalOperand(rhs, label, true, true);
method.label(skip);
} else {
branchOptimizer(lhs, label, false);
branchOptimizer(rhs, label, false);
optimizeLogicalOperand(lhs, label, false, false);
optimizeLogicalOperand(rhs, label, false, true);
}
return;
case OR:
if (state) {
branchOptimizer(lhs, label, true);
branchOptimizer(rhs, label, true);
optimizeLogicalOperand(lhs, label, true, false);
optimizeLogicalOperand(rhs, label, true, true);
} else {
final Label skip = new Label("skip");
branchOptimizer(lhs, skip, true);
branchOptimizer(rhs, label, false);
optimizeLogicalOperand(lhs, skip, true, false);
optimizeLogicalOperand(rhs, label, false, true);
method.label(skip);
}
return;
case EQ:
case EQ_STRICT:
codegen.loadBinaryOperands(lhs, rhs, Type.widest(lhs.getType(), rhs.getType()));
codegen.loadBinaryOperands(binaryNode);
method.conditionalJump(state ? EQ : NE, true, label);
return;
case NE:
case NE_STRICT:
codegen.loadBinaryOperands(lhs, rhs, Type.widest(lhs.getType(), rhs.getType()));
codegen.loadBinaryOperands(binaryNode);
method.conditionalJump(state ? NE : EQ, true, label);
return;
case GE:
codegen.loadBinaryOperands(lhs, rhs, Type.widest(lhs.getType(), rhs.getType()));
codegen.loadBinaryOperands(binaryNode);
method.conditionalJump(state ? GE : LT, false, label);
return;
case GT:
codegen.loadBinaryOperands(lhs, rhs, Type.widest(lhs.getType(), rhs.getType()));
codegen.loadBinaryOperands(binaryNode);
method.conditionalJump(state ? GT : LE, false, label);
return;
case LE:
codegen.loadBinaryOperands(lhs, rhs, Type.widest(lhs.getType(), rhs.getType()));
codegen.loadBinaryOperands(binaryNode);
method.conditionalJump(state ? LE : GT, true, label);
return;
case LT:
codegen.loadBinaryOperands(lhs, rhs, Type.widest(lhs.getType(), rhs.getType()));
codegen.loadBinaryOperands(binaryNode);
method.conditionalJump(state ? LT : GE, true, label);
return;
@ -145,29 +139,40 @@ final class BranchOptimizer {
break;
}
codegen.load(binaryNode, Type.BOOLEAN);
if (state) {
method.ifne(label);
} else {
method.ifeq(label);
}
loadTestAndJump(binaryNode, label, state);
}
private void optimizeLogicalOperand(final Expression expr, final Label label, final boolean state, final boolean isRhs) {
final JoinPredecessorExpression jpexpr = (JoinPredecessorExpression)expr;
if(LocalVariableConversion.hasLiveConversion(jpexpr)) {
final Label after = new Label("after");
branchOptimizer(jpexpr.getExpression(), after, !state);
method.beforeJoinPoint(jpexpr);
method._goto(label);
method.label(after);
if(isRhs) {
method.beforeJoinPoint(jpexpr);
}
} else {
branchOptimizer(jpexpr.getExpression(), label, state);
}
}
private void branchOptimizer(final Expression node, final Label label, final boolean state) {
if (!(node instanceof TernaryNode)) {
if (node instanceof BinaryNode) {
branchOptimizer((BinaryNode)node, label, state);
return;
}
if (node instanceof UnaryNode) {
branchOptimizer((UnaryNode)node, label, state);
return;
}
if (node instanceof BinaryNode) {
branchOptimizer((BinaryNode)node, label, state);
return;
}
codegen.load(node, Type.BOOLEAN);
if (node instanceof UnaryNode) {
branchOptimizer((UnaryNode)node, label, state);
return;
}
loadTestAndJump(node, label, state);
}
private void loadTestAndJump(final Expression node, final Label label, final boolean state) {
codegen.loadExpressionAsBoolean(node);
if (state) {
method.ifne(label);
} else {

View file

@ -489,9 +489,7 @@ public class ClassEmitter implements Emitter {
null,
null);
final MethodEmitter method = new MethodEmitter(this, mv, functionNode);
method.setParameterTypes(signature.getParamTypes());
return method;
return new MethodEmitter(this, mv, functionNode);
}
/**
@ -508,9 +506,7 @@ public class ClassEmitter implements Emitter {
null,
null);
final MethodEmitter method = new MethodEmitter(this, mv, functionNode);
method.setParameterTypes(new FunctionSignature(functionNode).getParamTypes());
return method;
return new MethodEmitter(this, mv, functionNode);
}

File diff suppressed because it is too large Load diff

View file

@ -31,7 +31,6 @@ import java.util.Collections;
import java.util.Deque;
import java.util.HashMap;
import java.util.Map;
import jdk.nashorn.internal.IntDeque;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.Block;
@ -39,7 +38,6 @@ import jdk.nashorn.internal.ir.FunctionNode;
import jdk.nashorn.internal.ir.LexicalContext;
import jdk.nashorn.internal.ir.LexicalContextNode;
import jdk.nashorn.internal.ir.Node;
import jdk.nashorn.internal.ir.SplitNode;
import jdk.nashorn.internal.ir.Symbol;
import jdk.nashorn.internal.ir.WithNode;
@ -89,18 +87,18 @@ final class CodeGeneratorLexicalContext extends LexicalContext {
dynamicScopeCount++;
}
splitNodes.push(0);
} else if (node instanceof SplitNode) {
enterSplitNode();
}
return super.push(node);
}
void enterSplitNode() {
splitNodes.getAndIncrement();
pushFreeSlots(methodEmitters.peek().getUsedSlotsWithLiveTemporaries());
}
void exitSplitNode() {
splitNodes.decrementAndGet();
final int count = splitNodes.decrementAndGet();
assert count >= 0;
}
@Override
@ -116,8 +114,6 @@ final class CodeGeneratorLexicalContext extends LexicalContext {
}
assert splitNodes.peek() == 0;
splitNodes.pop();
} else if (node instanceof SplitNode) {
exitSplitNode();
}
return popped;
}
@ -208,60 +204,67 @@ final class CodeGeneratorLexicalContext extends LexicalContext {
return getScopeCall(unit, symbol, valueType, valueType, null, flags);
}
void onEnterBlock(final Block block) {
pushFreeSlots(assignSlots(block, isFunctionBody() ? 0 : getUsedSlotCount()));
}
void nextFreeSlot(final Block block) {
final int nextFreeSlot = isFunctionBody() ? 0 : getUsedSlotCount();
private void pushFreeSlots(final int freeSlots) {
if (nextFreeSlotsSize == nextFreeSlots.length) {
final int[] newNextFreeSlots = new int[nextFreeSlotsSize * 2];
System.arraycopy(nextFreeSlots, 0, newNextFreeSlots, 0, nextFreeSlotsSize);
nextFreeSlots = newNextFreeSlots;
}
nextFreeSlots[nextFreeSlotsSize++] = assignSlots(block, nextFreeSlot);
nextFreeSlots[nextFreeSlotsSize++] = freeSlots;
}
int getUsedSlotCount() {
return nextFreeSlots[nextFreeSlotsSize - 1];
}
void releaseBlockSlots(final boolean optimistic) {
void releaseSlots() {
--nextFreeSlotsSize;
if(optimistic) {
slotTypesDescriptors.peek().setLength(nextFreeSlots[nextFreeSlotsSize]);
final int undefinedFromSlot = nextFreeSlotsSize == 0 ? 0 : nextFreeSlots[nextFreeSlotsSize - 1];
if(!slotTypesDescriptors.isEmpty()) {
slotTypesDescriptors.peek().setLength(undefinedFromSlot);
}
methodEmitters.peek().undefineLocalVariables(undefinedFromSlot, false);
}
private int assignSlots(final Block block, final int firstSlot) {
int nextSlot = firstSlot;
int fromSlot = firstSlot;
final MethodEmitter method = methodEmitters.peek();
for (final Symbol symbol : block.getSymbols()) {
if (symbol.hasSlot()) {
symbol.setSlot(nextSlot);
nextSlot += symbol.slotCount();
symbol.setFirstSlot(fromSlot);
final int toSlot = fromSlot + symbol.slotCount();
method.defineBlockLocalVariable(fromSlot, toSlot);
fromSlot = toSlot;
}
}
methodEmitters.peek().ensureLocalVariableCount(nextSlot);
return nextSlot;
return fromSlot;
}
static Type getTypeForSlotDescriptor(final char typeDesc) {
// Recognizing both lowercase and uppercase as we're using both to signify symbol boundaries; see
// MethodEmitter.markSymbolBoundariesInLvarTypesDescriptor().
switch(typeDesc) {
case 'I': {
case 'I':
case 'i':
return Type.INT;
}
case 'J': {
case 'J':
case 'j':
return Type.LONG;
}
case 'D': {
case 'D':
case 'd':
return Type.NUMBER;
}
case 'A': {
case 'A':
case 'a':
return Type.OBJECT;
}
case 'U': {
case 'U':
case 'u':
return Type.UNKNOWN;
}
default: {
default:
throw new AssertionError();
}
}
}
@ -277,12 +280,8 @@ final class CodeGeneratorLexicalContext extends LexicalContext {
return discard.peek();
}
int quickSlot(final Symbol symbol) {
final int quickSlot = nextFreeSlots[nextFreeSlotsSize - 1];
nextFreeSlots[nextFreeSlotsSize - 1] = quickSlot + symbol.slotCount();
methodEmitters.peek().ensureLocalVariableCount(quickSlot);
return quickSlot;
int quickSlot(final Type type) {
return methodEmitters.peek().defineTemporaryLocalVariable(type.getSlots());
}
}

View file

@ -34,7 +34,6 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.AccessNode;
import jdk.nashorn.internal.ir.Expression;
@ -44,11 +43,12 @@ import jdk.nashorn.internal.ir.IndexNode;
import jdk.nashorn.internal.ir.Optimistic;
import jdk.nashorn.internal.objects.NativeArray;
import jdk.nashorn.internal.runtime.Context;
import jdk.nashorn.internal.runtime.JSType;
import jdk.nashorn.internal.runtime.FindProperty;
import jdk.nashorn.internal.runtime.JSType;
import jdk.nashorn.internal.runtime.Property;
import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData;
import jdk.nashorn.internal.runtime.ScriptObject;
import jdk.nashorn.internal.runtime.ScriptRuntime;
/**
* Class for managing metadata during a compilation, e.g. which phases
@ -104,10 +104,10 @@ public final class CompilationEnvironment {
CompilationPhase.CONSTANT_FOLDING_PHASE,
CompilationPhase.LOWERING_PHASE,
CompilationPhase.SPLITTING_PHASE,
CompilationPhase.ATTRIBUTION_PHASE,
CompilationPhase.RANGE_ANALYSIS_PHASE,
CompilationPhase.TYPE_FINALIZATION_PHASE,
CompilationPhase.SYMBOL_ASSIGNMENT_PHASE,
CompilationPhase.SCOPE_DEPTH_COMPUTATION_PHASE,
CompilationPhase.OPTIMISTIC_TYPE_ASSIGNMENT_PHASE,
CompilationPhase.LOCAL_VARIABLE_TYPE_CALCULATION_PHASE,
CompilationPhase.BYTECODE_GENERATION_PHASE
};
@ -402,6 +402,18 @@ public final class CompilationEnvironment {
return mostOptimisticType;
}
/**
* Tells the compilation environment that a symbol of a particular name is a local variables in a function. Used
* with on-demand compilation, this will hide symbols of the same name from a parent scope and prevent them from
* being mistakenly found by the optimistic types heuristics.
* @param symbolName the name of the symbols to declare.
*/
void declareLocalSymbol(final String symbolName) {
assert useOptimisticTypes() && isOnDemandCompilation() && runtimeScope != null;
if(runtimeScope.findProperty(symbolName, false) == null) {
runtimeScope.set(symbolName, ScriptRuntime.UNDEFINED, true);
}
}
private Type getEvaluatedType(final Optimistic expr) {
if(expr instanceof IdentNode) {
@ -412,7 +424,7 @@ public final class CompilationEnvironment {
if(!(base instanceof ScriptObject)) {
return null;
}
return getPropertyType((ScriptObject)base, accessNode.getProperty().getName());
return getPropertyType((ScriptObject)base, accessNode.getProperty());
} else if(expr instanceof IndexNode) {
final IndexNode indexNode = (IndexNode)expr;
final Object base = evaluateSafely(indexNode.getBase());
@ -453,8 +465,12 @@ public final class CompilationEnvironment {
}
// Safely evaluate the property, and return the narrowest type for the actual value (e.g. Type.INT for a boxed
// integer).
return Type.typeFor(JSType.unboxedFieldType(property.getObjectValue(owner, owner)));
// integer). Continue not making guesses for undefined.
final Object value = property.getObjectValue(owner, owner);
if(value == ScriptRuntime.UNDEFINED) {
return null;
}
return Type.typeFor(JSType.unboxedFieldType(value));
}
private Object evaluateSafely(final Expression expr) {
@ -466,7 +482,7 @@ public final class CompilationEnvironment {
if(!(base instanceof ScriptObject)) {
return null;
}
return evaluatePropertySafely((ScriptObject)base, accessNode.getProperty().getName());
return evaluatePropertySafely((ScriptObject)base, accessNode.getProperty());
}
return null;
}

View file

@ -25,34 +25,21 @@
package jdk.nashorn.internal.codegen;
import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.ATTR;
import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.CONSTANT_FOLDED;
import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.FINALIZED;
import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.INITIALIZED;
import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.LOCAL_VARIABLE_TYPES_CALCULATED;
import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.LOWERED;
import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.OPTIMISTIC_TYPES_ASSIGNED;
import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.PARSED;
import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.SCOPE_DEPTHS_COMPUTED;
import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.SPLIT;
import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.SYMBOLS_ASSIGNED;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.EnumSet;
import java.util.List;
import jdk.nashorn.internal.codegen.types.Range;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.Expression;
import jdk.nashorn.internal.ir.FunctionNode;
import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
import jdk.nashorn.internal.ir.LexicalContext;
import jdk.nashorn.internal.ir.Node;
import jdk.nashorn.internal.ir.ReturnNode;
import jdk.nashorn.internal.ir.Symbol;
import jdk.nashorn.internal.ir.TemporarySymbols;
import jdk.nashorn.internal.ir.debug.ASTWriter;
import jdk.nashorn.internal.ir.debug.PrintVisitor;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.ScriptEnvironment;
import jdk.nashorn.internal.runtime.Timing;
@ -140,169 +127,19 @@ enum CompilationPhase {
}
},
/**
* Attribution Assign symbols and types to all nodes.
*/
ATTRIBUTION_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED, SPLIT)) {
SYMBOL_ASSIGNMENT_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED, SPLIT)) {
@Override
FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
final TemporarySymbols ts = compiler.getTemporarySymbols();
final FunctionNode newFunctionNode =
(FunctionNode)enterAttr(fn, ts).
accept(new Attr(compiler.getCompilationEnvironment(), ts));
if (compiler.getEnv()._print_mem_usage) {
compiler.getLogger().info("Attr temporary symbol count:", ts.getTotalSymbolCount());
}
return newFunctionNode;
}
/**
* Pessimistically set all lazy functions' return types to Object
* and the function symbols to object
* @param functionNode node where to start iterating
*/
private FunctionNode enterAttr(final FunctionNode functionNode, final TemporarySymbols ts) {
return (FunctionNode)functionNode.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
@Override
public Node leaveFunctionNode(final FunctionNode node) {
return node.setReturnType(lc, Type.UNKNOWN).setSymbol(lc, null);
}
});
return (FunctionNode)fn.accept(new AssignSymbols(compiler.getCompilationEnvironment()));
}
@Override
public String toString() {
return "[Type Attribution]";
return "[Symbol Assignment]";
}
},
/**
* Range analysis
* Conservatively prove that certain variables can be narrower than
* the most generic number type
*/
RANGE_ANALYSIS_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED, SPLIT, ATTR)) {
@Override
FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
if (!compiler.getEnv()._range_analysis) {
return fn;
}
FunctionNode newFunctionNode = (FunctionNode)fn.accept(new RangeAnalyzer(compiler.getCompilationEnvironment()));
final List<ReturnNode> returns = new ArrayList<>();
newFunctionNode = (FunctionNode)newFunctionNode.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
private final Deque<ArrayList<ReturnNode>> returnStack = new ArrayDeque<>();
@Override
public boolean enterFunctionNode(final FunctionNode functionNode) {
returnStack.push(new ArrayList<ReturnNode>());
return true;
}
@Override
public Node leaveFunctionNode(final FunctionNode functionNode) {
Type returnType = Type.UNKNOWN;
for (final ReturnNode ret : returnStack.pop()) {
if (ret.getExpression() == null) {
returnType = Type.OBJECT;
break;
}
returnType = Type.widest(returnType, ret.getExpression().getType());
}
return functionNode.setReturnType(lc, returnType);
}
@Override
public Node leaveReturnNode(final ReturnNode returnNode) {
final ReturnNode result = (ReturnNode)leaveDefault(returnNode);
returns.add(result);
return result;
}
@Override
public Node leaveDefault(final Node node) {
if (node instanceof Expression) {
final Expression expr = (Expression)node;
final Symbol symbol = expr.getSymbol();
if (symbol != null) {
final Range range = symbol.getRange();
final Type symbolType = symbol.getSymbolType();
if (!symbolType.isUnknown() && !symbolType.isNumeric()) {
return expr;
}
final Type rangeType = range.getType();
if (!rangeType.isUnknown() && !Type.areEquivalent(symbolType, rangeType) && Type.widest(symbolType, rangeType) == symbolType) { //we can narrow range
compiler.getCompilationEnvironment().getContext().getLogger(RangeAnalyzer.class).info("[", lc.getCurrentFunction().getName(), "] ", symbol, " can be ", range.getType(), " ", symbol.getRange());
return expr.setSymbol(lc, symbol.setTypeOverrideShared(range.getType(), compiler.getTemporarySymbols()));
}
}
}
return node;
}
});
Type returnType = Type.UNKNOWN;
for (final ReturnNode node : returns) {
if (node.getExpression() != null) {
returnType = Type.widest(returnType, node.getExpression().getType());
} else {
returnType = Type.OBJECT;
break;
}
}
return newFunctionNode.setReturnType(null, returnType);
}
@Override
public String toString() {
return "[Range Analysis]";
}
},
/**
* FinalizeTypes
*
* This pass finalizes the types for nodes. If Attr created wider types than
* known during the first pass, convert nodes are inserted or access nodes
* are specialized where scope accesses.
*
* Runtime nodes may be removed and primitivized or reintroduced depending
* on information that was established in Attr.
*
* Contract: all variables must have slot assignments and scope assignments
* before type finalization.
*/
TYPE_FINALIZATION_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED, ATTR, SPLIT)) {
@Override
FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
final ScriptEnvironment env = compiler.getEnv();
final FunctionNode newFunctionNode = (FunctionNode)fn.accept(new FinalizeTypes(compiler.getCompilationEnvironment()));
if (env._print_lower_ast) {
env.getErr().println(new ASTWriter(newFunctionNode));
}
if (env._print_lower_parse) {
env.getErr().println(new PrintVisitor(newFunctionNode));
}
return newFunctionNode;
}
@Override
public String toString() {
return "[Type Finalization]";
}
},
SCOPE_DEPTH_COMPUTATION_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED, ATTR, SPLIT, FINALIZED)) {
SCOPE_DEPTH_COMPUTATION_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED, SPLIT, SYMBOLS_ASSIGNED)) {
@Override
FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
return (FunctionNode)fn.accept(new FindScopeDepths(compiler));
@ -314,19 +151,55 @@ enum CompilationPhase {
}
},
OPTIMISTIC_TYPE_ASSIGNMENT_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED, SPLIT, SYMBOLS_ASSIGNED, SCOPE_DEPTHS_COMPUTED)) {
@Override
FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
if(compiler.getCompilationEnvironment().useOptimisticTypes()) {
return (FunctionNode)fn.accept(new OptimisticTypesCalculator(compiler.getCompilationEnvironment()));
}
return fn.setState(null, OPTIMISTIC_TYPES_ASSIGNED);
}
@Override
public String toString() {
return "[Optimistic Type Assignment]";
}
},
LOCAL_VARIABLE_TYPE_CALCULATION_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED, SPLIT, SYMBOLS_ASSIGNED, SCOPE_DEPTHS_COMPUTED, OPTIMISTIC_TYPES_ASSIGNED)) {
@Override
FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
return (FunctionNode)fn.accept(new LocalVariableTypesCalculator(compiler.getCompilationEnvironment()));
}
@Override
public String toString() {
return "[Local Variable Type Calculation]";
}
},
/**
* Bytecode generation:
*
* Generate the byte code class(es) resulting from the compiled FunctionNode
*/
BYTECODE_GENERATION_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED, ATTR, SPLIT, FINALIZED, SCOPE_DEPTHS_COMPUTED)) {
BYTECODE_GENERATION_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED, SPLIT, SYMBOLS_ASSIGNED, SCOPE_DEPTHS_COMPUTED, OPTIMISTIC_TYPES_ASSIGNED, LOCAL_VARIABLE_TYPES_CALCULATED)) {
@Override
FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
final ScriptEnvironment env = compiler.getEnv();
if (env._print_lower_ast) {
env.getErr().println(new ASTWriter(fn));
}
if (env._print_lower_parse) {
env.getErr().println(new PrintVisitor(fn));
}
FunctionNode newFunctionNode = fn;
final CodeGenerator codegen = new CodeGenerator(compiler);
try {
final CodeGenerator codegen = new CodeGenerator(compiler);
newFunctionNode = (FunctionNode)newFunctionNode.accept(codegen);
codegen.generateScopeCalls();
} catch (final VerifyError e) {
@ -338,6 +211,9 @@ enum CompilationPhase {
} else {
throw e;
}
} catch (final Throwable e) {
// Provide source file and line number being compiled when the assertion occurred
throw new AssertionError("Failed generating bytecode for " + fn.getSourceName() + ":" + codegen.getLastLineNumber(), e);
}
for (final CompileUnit compileUnit : compiler.getCompileUnits()) {

View file

@ -58,7 +58,6 @@ import jdk.nashorn.internal.codegen.CompilationEnvironment.CompilationPhases;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.FunctionNode;
import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
import jdk.nashorn.internal.ir.TemporarySymbols;
import jdk.nashorn.internal.ir.debug.ClassHistogramElement;
import jdk.nashorn.internal.ir.debug.ObjectSizeCalculator;
import jdk.nashorn.internal.runtime.CodeInstaller;
@ -105,8 +104,6 @@ public final class Compiler implements Loggable {
private final CodeInstaller<ScriptEnvironment> installer;
private final TemporarySymbols temporarySymbols = new TemporarySymbols();
/** logger for compiler, trampolines, splits and related code generation events
* that affect classes */
private final DebugLogger log;
@ -437,10 +434,6 @@ public final class Compiler implements Loggable {
return installer;
}
TemporarySymbols getTemporarySymbols() {
return temporarySymbols;
}
void addClass(final String name, final byte[] code) {
bytecode.put(name, code);
}

View file

@ -29,8 +29,9 @@ import static jdk.nashorn.internal.lookup.Lookup.MH;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.runtime.ScriptFunction;
import jdk.nashorn.internal.runtime.ScriptObject;
@ -42,7 +43,6 @@ import jdk.nashorn.internal.runtime.Source;
*/
public enum CompilerConstants {
/** the __FILE__ variable */
__FILE__,
@ -87,7 +87,7 @@ public enum CompilerConstants {
* representations of ECMAScript functions. It is not assigned a slot, as its position in the method signature is
* dependent on other factors (most notably, callee can precede it).
*/
THIS("this"),
THIS("this", Object.class),
/** this debugger symbol */
THIS_DEBUGGER(":this"),
@ -120,7 +120,7 @@ public enum CompilerConstants {
/** prefix for tag variable used for switch evaluation */
SWITCH_TAG_PREFIX(":s"),
/** prefix for all exceptions */
/** prefix for JVM exceptions */
EXCEPTION_PREFIX(":e", Throwable.class),
/** prefix for quick slots generated in Store */
@ -184,6 +184,8 @@ public enum CompilerConstants {
}
}
private static Set<String> symbolNames;
/**
* Prefix used for internal methods generated in script clases.
*/
@ -223,12 +225,17 @@ public enum CompilerConstants {
* @return true if compiler constant name
*/
public static boolean isCompilerConstant(final String name) {
for (final CompilerConstants cc : CompilerConstants.values()) {
if (name.equals(cc.symbolName())) {
return true;
ensureSymbolNames();
return symbolNames.contains(name);
}
private static void ensureSymbolNames() {
if(symbolNames == null) {
symbolNames = new HashSet<>();
for(final CompilerConstants cc: CompilerConstants.values()) {
symbolNames.add(cc.symbolName);
}
}
return false;
}
/**

View file

@ -1,252 +0,0 @@
/*
* 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.
*/
package jdk.nashorn.internal.codegen;
import static jdk.nashorn.internal.codegen.CompilerConstants.CALLEE;
import static jdk.nashorn.internal.codegen.CompilerConstants.RETURN;
import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
import jdk.nashorn.internal.ir.BinaryNode;
import jdk.nashorn.internal.ir.Block;
import jdk.nashorn.internal.ir.Expression;
import jdk.nashorn.internal.ir.ExpressionStatement;
import jdk.nashorn.internal.ir.ForNode;
import jdk.nashorn.internal.ir.FunctionNode;
import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
import jdk.nashorn.internal.ir.LexicalContext;
import jdk.nashorn.internal.ir.Node;
import jdk.nashorn.internal.ir.RuntimeNode;
import jdk.nashorn.internal.ir.RuntimeNode.Request;
import jdk.nashorn.internal.ir.Symbol;
import jdk.nashorn.internal.ir.UnaryNode;
import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor;
import jdk.nashorn.internal.parser.Token;
import jdk.nashorn.internal.parser.TokenType;
import jdk.nashorn.internal.runtime.Context;
import jdk.nashorn.internal.runtime.logging.DebugLogger;
import jdk.nashorn.internal.runtime.logging.Loggable;
import jdk.nashorn.internal.runtime.logging.Logger;
/**
* Lower to more primitive operations. After lowering, an AST has symbols and
* types. Lowering may also add specialized versions of methods to the script if
* the optimizer is turned on.
*
* Any expression that requires temporary storage as part of computation will
* also be detected here and give a temporary symbol
*
* For any op that we process in FinalizeTypes it is an absolute guarantee
* that scope and slot information is correct. This enables e.g. AccessSpecialization
* and frame optimizations
*/
@Logger(name="finalize")
final class FinalizeTypes extends NodeOperatorVisitor<LexicalContext> implements Loggable {
private final DebugLogger log;
FinalizeTypes(final CompilationEnvironment env) {
super(new LexicalContext());
this.log = initLogger(env.getContext());
}
@Override
public DebugLogger getLogger() {
return log;
}
@Override
public DebugLogger initLogger(final Context context) {
return context.getLogger(this.getClass());
}
@Override
public Node leaveForNode(final ForNode forNode) {
if (forNode.isForIn()) {
return forNode;
}
final Expression init = forNode.getInit();
final Expression test = forNode.getTest();
final Expression modify = forNode.getModify();
assert test != null || forNode.hasGoto() : "forNode " + forNode + " needs goto and is missing it in " + lc.getCurrentFunction();
return forNode.
setInit(lc, init == null ? null : discard(init)).
setModify(lc, modify == null ? null : discard(modify));
}
private static Node createIsUndefined(final Expression parent, final Expression lhs, final Expression rhs, final Request request) {
if ("undefined".equals(lhs.getSymbol().getName()) || "undefined".equals(rhs.getSymbol().getName())) {
return new RuntimeNode(parent, request, lhs, rhs);
}
return parent;
}
@Override
public Node leaveEQ_STRICT(final BinaryNode binaryNode) {
return createIsUndefined(binaryNode, binaryNode.lhs(), binaryNode.rhs(), Request.IS_UNDEFINED);
}
@Override
public Node leaveNE_STRICT(final BinaryNode binaryNode) {
return createIsUndefined(binaryNode, binaryNode.lhs(), binaryNode.rhs(), Request.IS_NOT_UNDEFINED);
}
@Override
public Node leaveRuntimeNode(final RuntimeNode runtimeNode) {
switch (runtimeNode.getRequest()) {
case EQ_STRICT:
return createIsUndefined(runtimeNode, runtimeNode.getArgs().get(0), runtimeNode.getArgs().get(1), Request.IS_UNDEFINED);
case NE_STRICT:
return createIsUndefined(runtimeNode, runtimeNode.getArgs().get(0), runtimeNode.getArgs().get(1), Request.IS_NOT_UNDEFINED);
default:
return runtimeNode;
}
}
@Override
public Node leaveCOMMALEFT(final BinaryNode binaryNode) {
assert binaryNode.getSymbol() != null;
return binaryNode.setRHS(discard(binaryNode.rhs()));
}
@Override
public Node leaveCOMMARIGHT(final BinaryNode binaryNode) {
assert binaryNode.getSymbol() != null;
return binaryNode.setLHS(discard(binaryNode.lhs()));
}
@Override
public boolean enterBlock(final Block block) {
updateSymbols(block);
return true;
}
@Override
public Node leaveExpressionStatement(final ExpressionStatement expressionStatement) {
return expressionStatement.setExpression(discard(expressionStatement.getExpression()));
}
@Override
public boolean enterFunctionNode(final FunctionNode functionNode) {
// TODO: now that Splitter comes before Attr, these can probably all be moved to Attr.
// If the function doesn't need a callee, we ensure its CALLEE symbol doesn't get a slot. We can't do this
// earlier, as access to scoped variables, self symbol, etc. in previous phases can all trigger the need for the
// callee.
if (!functionNode.needsCallee()) {
functionNode.compilerConstant(CALLEE).setNeedsSlot(false);
}
// Similar reasoning applies to SCOPE symbol: if the function doesn't need either parent scope and none of its
// blocks create a scope, we ensure it doesn't get a slot, but we can't determine whether it needs a scope
// earlier than this phase.
if (!(functionNode.hasScopeBlock() || functionNode.needsParentScope())) {
functionNode.compilerConstant(SCOPE).setNeedsSlot(false);
}
// Also, we must wait until after Splitter to see if the function ended up needing the RETURN symbol.
if (!functionNode.usesReturnSymbol()) {
functionNode.compilerConstant(RETURN).setNeedsSlot(false);
}
// Named function expressions that end up not referencing themselves won't need a local slot for the self symbol.
if(!functionNode.isDeclared() && !functionNode.usesSelfSymbol() && !functionNode.isAnonymous()) {
final Symbol selfSymbol = functionNode.getBody().getExistingSymbol(functionNode.getIdent().getName());
if(selfSymbol != null) {
if(selfSymbol.isFunctionSelf()) {
selfSymbol.setNeedsSlot(false);
selfSymbol.clearFlag(Symbol.IS_VAR);
}
} else {
assert functionNode.isProgram();
}
}
return true;
}
@Override
public Node leaveFunctionNode(final FunctionNode functionNode) {
return functionNode.setState(lc, CompilationState.FINALIZED);
}
private void updateSymbolsLog(final FunctionNode functionNode, final Symbol symbol, final boolean loseSlot) {
if (log.isEnabled()) {
if (!symbol.isScope()) {
log.finest("updateSymbols: ", symbol, " => scope, because all vars in ", functionNode.getName(), " are in scope");
}
if (loseSlot && symbol.hasSlot()) {
log.finest("updateSymbols: ", symbol, " => no slot, because all vars in ", functionNode.getName(), " are in scope");
}
}
}
/**
* Called after a block or function node (subclass of block) is finished. Guarantees
* that scope and slot information is correct for every symbol
* @param block block for which to to finalize type info.
*/
private void updateSymbols(final Block block) {
if (!block.needsScope()) {
return; // nothing to do
}
final FunctionNode functionNode = lc.getFunction(block);
final boolean allVarsInScope = functionNode.allVarsInScope();
final boolean isVarArg = functionNode.isVarArg();
for (final Symbol symbol : block.getSymbols()) {
if (symbol.isInternal() || symbol.isThis() || symbol.isTemp()) {
continue;
}
if (symbol.isVar()) {
if (allVarsInScope || symbol.isScope()) {
updateSymbolsLog(functionNode, symbol, true);
Symbol.setSymbolIsScope(lc, symbol);
symbol.setNeedsSlot(false);
} else {
assert symbol.hasSlot() : symbol + " should have a slot only, no scope";
}
} else if (symbol.isParam() && (allVarsInScope || isVarArg || symbol.isScope())) {
updateSymbolsLog(functionNode, symbol, isVarArg);
Symbol.setSymbolIsScope(lc, symbol);
symbol.setNeedsSlot(!isVarArg);
}
}
}
private static Expression discard(final Expression expr) {
if (expr.getSymbol() != null) {
final UnaryNode discard = new UnaryNode(Token.recast(expr.getToken(), TokenType.DISCARD), expr);
//discard never has a symbol in the discard node - then it would be a nop
assert !expr.isTerminal();
return discard;
}
// node has no result (symbol) so we can keep it the way it is
return expr;
}
}

View file

@ -34,15 +34,14 @@ import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import jdk.nashorn.internal.ir.Block;
import jdk.nashorn.internal.ir.Expression;
import jdk.nashorn.internal.ir.FunctionNode;
import jdk.nashorn.internal.ir.WithNode;
import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
import jdk.nashorn.internal.ir.IdentNode;
import jdk.nashorn.internal.ir.LexicalContext;
import jdk.nashorn.internal.ir.Node;
import jdk.nashorn.internal.ir.Symbol;
import jdk.nashorn.internal.ir.WithNode;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.Context;
import jdk.nashorn.internal.runtime.PropertyMap;
@ -211,7 +210,7 @@ final class FindScopeDepths extends NodeVisitor<LexicalContext> implements Logga
assert nestedFunctions != null;
// Generate the object class and property map in case this function is ever used as constructor
final int fieldCount = getPaddedFieldCount(newFunctionNode.countThisProperties());
final int fieldCount = getPaddedFieldCount(newFunctionNode.getThisProperties());
final String allocatorClassName = Compiler.binaryName(getClassName(fieldCount));
final PropertyMap allocatorMap = PropertyMap.newMap(null, 0, fieldCount, 0);
final RecompilableScriptFunctionData data = new RecompilableScriptFunctionData(
@ -292,8 +291,8 @@ final class FindScopeDepths extends NodeVisitor<LexicalContext> implements Logga
@Override
public final boolean enterDefault(final Node node) {
if (!env.isOnDemandCompilation()) {
if (node instanceof Expression) {
final Symbol symbol = ((Expression)node).getSymbol();
if (node instanceof IdentNode) {
final Symbol symbol = ((IdentNode)node).getSymbol();
if (symbol != null && symbol.isScope()) {
//if this is an internal symbol, skip it.
symbols.add(symbol);

View file

@ -24,6 +24,12 @@
*/
package jdk.nashorn.internal.codegen;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import jdk.nashorn.internal.codegen.types.Type;
/**
@ -37,23 +43,23 @@ public final class Label {
//byte code generation evaluation type stack for consistency check
//and correct opcode selection. one per label as a label may be a
//join point
static final class Stack {
static final class Stack implements Cloneable {
static final int NON_LOAD = -1;
Type[] data = new Type[8];
int[] localLoads = new int[8];
Type[] data;
int[] localLoads;
int sp;
Stack() {
}
List<Type> localVariableTypes;
int firstTemp; // index of the first temporary local variable
// Bitmap marking last slot belonging to a single symbol.
BitSet symbolBoundary;
private Stack(final Stack original) {
this();
this.sp = original.sp;
this.data = new Type[original.data.length];
System.arraycopy(original.data, 0, data, 0, sp);
this.localLoads = new int[original.localLoads.length];
System.arraycopy(original.localLoads, 0, localLoads, 0, sp);
Stack() {
data = new Type[8];
localLoads = new int[8];
localVariableTypes = new ArrayList<>(8);
symbolBoundary = new BitSet();
}
boolean isEmpty() {
@ -64,18 +70,6 @@ public final class Label {
return sp;
}
boolean isEquivalentInTypesTo(final Stack other) {
if (sp != other.sp) {
return false;
}
for (int i = 0; i < sp; i++) {
if (!data[i].isEquivalentTo(other.data[i])) {
return false;
}
}
return true;
}
void clear() {
sp = 0;
}
@ -123,24 +117,201 @@ public final class Label {
}
/**
* When joining branches, local loads that differ on different branches are invalidated.
* @param other the stack from the other branch.
* Returns the number of used local variable slots, including all live stack-store temporaries.
* @return the number of used local variable slots, including all live stack-store temporaries.
*/
void mergeLocalLoads(final Stack other) {
final int[] otherLoads = other.localLoads;
int getUsedSlotsWithLiveTemporaries() {
// There are at least as many as are declared by the current blocks.
int usedSlots = firstTemp;
// Look at every load on the stack, and bump the number of used slots up by the temporaries seen there.
for(int i = sp; i-->0;) {
final int slot = localLoads[i];
if(slot != Label.Stack.NON_LOAD) {
final int afterSlot = slot + localVariableTypes.get(slot).getSlots();
if(afterSlot > usedSlots) {
usedSlots = afterSlot;
}
}
}
return usedSlots;
}
/**
*
* @param joinOrigin the stack from the other branch.
*/
void joinFrom(final Stack joinOrigin, final boolean breakTarget) {
assert isStackCompatible(joinOrigin);
if(breakTarget) {
// As we're joining labels that can jump across block boundaries, the number of local variables can
// differ, and we should always respect the one having less variables.
firstTemp = Math.min(firstTemp, joinOrigin.firstTemp);
} else {
assert firstTemp == joinOrigin.firstTemp;
}
final int[] otherLoads = joinOrigin.localLoads;
int firstDeadTemp = firstTemp;
for(int i = 0; i < sp; ++i) {
if(localLoads[i] != otherLoads[i]) {
final int localLoad = localLoads[i];
if(localLoad != otherLoads[i]) {
localLoads[i] = NON_LOAD;
} else if(localLoad >= firstDeadTemp) {
firstDeadTemp = localLoad + localVariableTypes.get(localLoad).getSlots();
}
}
// Eliminate dead temporaries
undefineLocalVariables(firstDeadTemp, false);
assert isVariablePartitioningEqual(joinOrigin, firstDeadTemp);
mergeVariableTypes(joinOrigin, firstDeadTemp);
}
private void mergeVariableTypes(final Stack joinOrigin, final int toSlot) {
final ListIterator<Type> it1 = localVariableTypes.listIterator();
final Iterator<Type> it2 = joinOrigin.localVariableTypes.iterator();
for(int i = 0; i < toSlot; ++i) {
final Type thisType = it1.next();
final Type otherType = it2.next();
if(otherType == Type.UNKNOWN) {
// Variables that are <unknown> on the other branch will become <unknown> here too.
it1.set(Type.UNKNOWN);
} else if (thisType != otherType) {
if(thisType.isObject() && otherType.isObject()) {
// different object types are merged into Object.
// TODO: maybe find most common superclass?
it1.set(Type.OBJECT);
} else {
assert thisType == Type.UNKNOWN;
}
}
}
}
void joinFromTry(final Stack joinOrigin) {
// As we're joining labels that can jump across block boundaries, the number of local variables can
// differ, and we should always respect the one having less variables.
firstTemp = Math.min(firstTemp, joinOrigin.firstTemp);
assert isVariablePartitioningEqual(joinOrigin, firstTemp);
mergeVariableTypes(joinOrigin, firstTemp);
}
private int getFirstDeadLocal(List<Type> types) {
int i = types.size();
for(final ListIterator<Type> it = types.listIterator(i);
it.hasPrevious() && it.previous() == Type.UNKNOWN;
--i); // no body
// Respect symbol boundaries; we never chop off half a symbol's storage
while(!symbolBoundary.get(i - 1)) {
++i;
}
return i;
}
private boolean isStackCompatible(final Stack other) {
if (sp != other.sp) {
return false;
}
for (int i = 0; i < sp; i++) {
if (!data[i].isEquivalentTo(other.data[i])) {
return false;
}
}
return true;
}
private boolean isVariablePartitioningEqual(final Stack other, final int toSlot) {
// No difference in the symbol boundaries before the toSlot
final BitSet diff = other.getSymbolBoundaryCopy();
diff.xor(symbolBoundary);
return diff.previousSetBit(toSlot - 1) == -1;
}
void markDeadLocalVariables(final int fromSlot, final int slotCount) {
final int localCount = localVariableTypes.size();
if(fromSlot >= localCount) {
return;
}
final int toSlot = Math.min(fromSlot + slotCount, localCount);
invalidateLocalLoadsOnStack(fromSlot, toSlot);
for(int i = fromSlot; i < toSlot; ++i) {
localVariableTypes.set(i, Type.UNKNOWN);
}
}
@SuppressWarnings("unchecked")
List<Type> getLocalVariableTypesCopy() {
return (List<Type>)((ArrayList<Type>)localVariableTypes).clone();
}
BitSet getSymbolBoundaryCopy() {
return (BitSet)symbolBoundary.clone();
}
/**
* Returns a list of local variable slot types, but for those symbols that have multiple values, only the slot
* holding the widest type is marked as live.
* @return a list of widest local variable slot types.
*/
List<Type> getWidestLiveLocals(final List<Type> lvarTypes) {
List<Type> widestLiveLocals = new ArrayList<>(lvarTypes);
boolean keepNextValue = true;
final int size = widestLiveLocals.size();
for(int i = size - 1; i-- > 0;) {
if(symbolBoundary.get(i)) {
keepNextValue = true;
}
final Type t = widestLiveLocals.get(i);
if(t != Type.UNKNOWN) {
if(keepNextValue) {
if(t != Type.SLOT_2) {
keepNextValue = false;
}
} else {
widestLiveLocals.set(i, Type.UNKNOWN);
}
}
}
widestLiveLocals.subList(Math.max(getFirstDeadLocal(widestLiveLocals), firstTemp), widestLiveLocals.size()).clear();
return widestLiveLocals;
}
String markSymbolBoundariesInLvarTypesDescriptor(final String lvarDescriptor) {
final char[] chars = lvarDescriptor.toCharArray();
int j = 0;
for(int i = 0; i < chars.length; ++i) {
final char c = chars[i];
final int nextj = j + CodeGeneratorLexicalContext.getTypeForSlotDescriptor(c).getSlots();
if(!symbolBoundary.get(nextj - 1)) {
chars[i] = Character.toLowerCase(c);
}
j = nextj;
}
return new String(chars);
}
Type pop() {
assert sp > 0;
return data[--sp];
}
Stack copy() {
return new Stack(this);
@Override
public Stack clone() {
try {
final Stack clone = (Stack)super.clone();
clone.data = data.clone();
clone.localLoads = localLoads.clone();
clone.symbolBoundary = getSymbolBoundaryCopy();
clone.localVariableTypes = getLocalVariableTypesCopy();
return clone;
} catch(final CloneNotSupportedException e) {
throw new AssertionError("", e);
}
}
private Stack cloneWithEmptyStack() {
final Stack stack = clone();
stack.sp = 0;
return stack;
}
int getTopLocalLoad() {
@ -152,33 +323,167 @@ public final class Label {
}
/**
* If we store a value in a local slot, it invalidates any on-stack loads from that same slot, as the values
* could have changed.
* Performs various bookeeping when a value is stored in a local variable slot.
* @param slot the slot written to
* @param slotCount the size of the value, either 1 or 2 slots
* @param onlySymbolLiveValue if true, this is the symbol's only live value, and other values of the symbol
* should be marked dead
* @param Type the type written to the slot
*/
void markLocalStore(final int slot, final int slotCount) {
void onLocalStore(final Type type, final int slot, final boolean onlySymbolLiveValue) {
if(onlySymbolLiveValue) {
final int fromSlot = slot == 0 ? 0 : (symbolBoundary.previousSetBit(slot - 1) + 1);
final int toSlot = symbolBoundary.nextSetBit(slot) + 1;
for(int i = fromSlot; i < toSlot; ++i) {
localVariableTypes.set(i, Type.UNKNOWN);
}
invalidateLocalLoadsOnStack(fromSlot, toSlot);
} else {
invalidateLocalLoadsOnStack(slot, slot + type.getSlots());
}
localVariableTypes.set(slot, type);
if(type.isCategory2()) {
localVariableTypes.set(slot + 1, Type.SLOT_2);
}
}
/**
* Given a slot range, invalidate knowledge about local loads on stack from these slots (because they're being
* killed).
* @param fromSlot first slot, inclusive.
* @param toSlot last slot, exclusive.
*/
private void invalidateLocalLoadsOnStack(final int fromSlot, final int toSlot) {
for(int i = 0; i < sp; ++i) {
final int load = localLoads[i];
if(load == slot || load == slot + slotCount - 1) {
final int localLoad = localLoads[i];
if(localLoad >= fromSlot && localLoad < toSlot) {
localLoads[i] = NON_LOAD;
}
}
}
@Override
public String toString() {
final StringBuilder builder = new StringBuilder("[");
for (int i = 0; i < sp; i++) {
builder.append(data[i]);
if (i < sp - 1) {
builder.append(", ");
/**
* Marks a range of slots as belonging to a defined local variable. The slots will start out with no live value
* in them.
* @param fromSlot first slot, inclusive.
* @param toSlot last slot, exclusive.
*/
void defineBlockLocalVariable(final int fromSlot, final int toSlot) {
defineLocalVariable(fromSlot, toSlot);
assert firstTemp < toSlot;
firstTemp = toSlot;
}
/**
* Defines a new temporary local variable and returns its allocated index.
* @param width the required width (in slots) for the new variable.
* @return the bytecode slot index where the newly allocated local begins.
*/
int defineTemporaryLocalVariable(final int width) {
final int fromSlot = getUsedSlotsWithLiveTemporaries();
defineLocalVariable(fromSlot, fromSlot + width);
return fromSlot;
}
/**
* Marks a range of slots as belonging to a defined temporary local variable. The slots will start out with no
* live value in them.
* @param fromSlot first slot, inclusive.
* @param toSlot last slot, exclusive.
*/
void defineTemporaryLocalVariable(final int fromSlot, final int toSlot) {
defineLocalVariable(fromSlot, toSlot);
}
private void defineLocalVariable(final int fromSlot, final int toSlot) {
assert !hasLoadsOnStack(fromSlot, toSlot);
assert fromSlot < toSlot;
symbolBoundary.clear(fromSlot, toSlot - 1);
symbolBoundary.set(toSlot - 1);
final int lastExisting = Math.min(toSlot, localVariableTypes.size());
for(int i = fromSlot; i < lastExisting; ++i) {
localVariableTypes.set(i, Type.UNKNOWN);
}
for(int i = lastExisting; i < toSlot; ++i) {
localVariableTypes.add(i, Type.UNKNOWN);
}
}
/**
* Undefines all local variables past the specified slot.
* @param fromSlot the first slot to be undefined
* @param canTruncateSymbol if false, the fromSlot must be either the first slot of a symbol, or the first slot
* after the last symbol. If true, the fromSlot can be in the middle of the storage area for a symbol. This
* should be used with care - it is only meant for use in optimism exception handlers.
*/
void undefineLocalVariables(final int fromSlot, final boolean canTruncateSymbol) {
final int lvarCount = localVariableTypes.size();
assert lvarCount == symbolBoundary.length();
assert !hasLoadsOnStack(fromSlot, lvarCount);
if(canTruncateSymbol) {
if(fromSlot > 0) {
symbolBoundary.set(fromSlot - 1);
}
} else {
assert fromSlot == 0 || symbolBoundary.get(fromSlot - 1);
}
if(fromSlot < lvarCount) {
symbolBoundary.clear(fromSlot, lvarCount);
localVariableTypes.subList(fromSlot, lvarCount).clear();
}
firstTemp = Math.min(fromSlot, firstTemp);
assert symbolBoundary.length() == localVariableTypes.size();
assert symbolBoundary.length() == fromSlot;
}
private void markAsOptimisticCatchHandler(final int liveLocalCount) {
// Live temporaries that are no longer on stack are undefined
undefineLocalVariables(liveLocalCount, true);
// Temporaries are promoted
firstTemp = liveLocalCount;
// No trailing undefineds
localVariableTypes.subList(firstTemp, localVariableTypes.size()).clear();
assert symbolBoundary.length() == firstTemp;
// Generalize all reference types to Object, and promote boolean to int
for(final ListIterator<Type> it = localVariableTypes.listIterator(); it.hasNext();) {
final Type type = it.next();
if(type == Type.BOOLEAN) {
it.set(Type.INT);
} else if(type.isObject() && type != Type.OBJECT) {
it.set(Type.OBJECT);
}
}
return builder.append("]").toString();
}
/**
* Returns true if any loads on the stack come from the specified slot range.
* @param fromSlot start of the range (inclusive)
* @param toSlot end of the range (exclusive)
* @return true if any loads on the stack come from the specified slot range.
*/
boolean hasLoadsOnStack(final int fromSlot, final int toSlot) {
for(int i = 0; i < sp; ++i) {
final int load = localLoads[i];
if(load >= fromSlot && load < toSlot) {
return true;
}
}
return false;
}
@Override
public String toString() {
return "stack=" + Arrays.toString(Arrays.copyOf(data, sp))
+ ", symbolBoundaries=" + String.valueOf(symbolBoundary)
+ ", firstTemp=" + firstTemp
+ ", localTypes=" + String.valueOf(localVariableTypes)
;
}
}
/** Next id for debugging purposes, remove if footprint becomes unmanageable */
private static int nextId = 0;
/** Name of this label */
private final String name;
@ -191,8 +496,10 @@ public final class Label {
/** Id for debugging purposes, remove if footprint becomes unmanageable */
private final int id;
/** Next id for debugging purposes, remove if footprint becomes unmanageable */
private static int nextId = 0;
/** Is this label reachable (anything ever jumped to it)? */
private boolean reachable;
private boolean breakTarget;
/**
* Constructor
@ -228,8 +535,57 @@ public final class Label {
return stack;
}
void setStack(final Label.Stack stack) {
this.stack = stack;
void joinFrom(final Label.Stack joinOrigin) {
this.reachable = true;
if(stack == null) {
stack = joinOrigin.clone();
} else {
stack.joinFrom(joinOrigin, breakTarget);
}
}
void joinFromTry(final Label.Stack joinOrigin, final boolean isOptimismHandler) {
this.reachable = true;
if (stack == null) {
if(!isOptimismHandler) {
stack = joinOrigin.cloneWithEmptyStack();
// Optimism handler needs temporaries to remain live, others don't.
stack.undefineLocalVariables(stack.firstTemp, false);
}
} else {
assert !isOptimismHandler;
stack.joinFromTry(joinOrigin);
}
}
void markAsBreakTarget() {
breakTarget = true;
}
boolean isBreakTarget() {
return breakTarget;
}
void onCatch() {
if(stack != null) {
stack = stack.cloneWithEmptyStack();
}
}
void markAsOptimisticCatchHandler(final Label.Stack currentStack, final int liveLocalCount) {
stack = currentStack.cloneWithEmptyStack();
stack.markAsOptimisticCatchHandler(liveLocalCount);
}
void markAsOptimisticContinuationHandlerFor(final Label afterConsumeStackLabel) {
stack = afterConsumeStackLabel.stack.cloneWithEmptyStack();
}
boolean isReachable() {
return reachable;
}
boolean isAfter(final Label other) {
return label.getOffset() > other.label.getOffset();
}
@Override

File diff suppressed because it is too large Load diff

View file

@ -28,13 +28,13 @@ package jdk.nashorn.internal.codegen;
import static jdk.nashorn.internal.codegen.CompilerConstants.EVAL;
import static jdk.nashorn.internal.codegen.CompilerConstants.RETURN;
import static jdk.nashorn.internal.codegen.CompilerConstants.THIS;
import static jdk.nashorn.internal.ir.Expression.isAlwaysTrue;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.ListIterator;
import jdk.nashorn.internal.ir.BaseNode;
import jdk.nashorn.internal.ir.BinaryNode;
import jdk.nashorn.internal.ir.Block;
@ -42,6 +42,7 @@ import jdk.nashorn.internal.ir.BlockLexicalContext;
import jdk.nashorn.internal.ir.BlockStatement;
import jdk.nashorn.internal.ir.BreakNode;
import jdk.nashorn.internal.ir.CallNode;
import jdk.nashorn.internal.ir.CaseNode;
import jdk.nashorn.internal.ir.CatchNode;
import jdk.nashorn.internal.ir.ContinueNode;
import jdk.nashorn.internal.ir.EmptyNode;
@ -58,6 +59,7 @@ import jdk.nashorn.internal.ir.LiteralNode;
import jdk.nashorn.internal.ir.LoopNode;
import jdk.nashorn.internal.ir.Node;
import jdk.nashorn.internal.ir.ReturnNode;
import jdk.nashorn.internal.ir.RuntimeNode;
import jdk.nashorn.internal.ir.Statement;
import jdk.nashorn.internal.ir.SwitchNode;
import jdk.nashorn.internal.ir.Symbol;
@ -72,6 +74,7 @@ import jdk.nashorn.internal.parser.Token;
import jdk.nashorn.internal.parser.TokenType;
import jdk.nashorn.internal.runtime.CodeInstaller;
import jdk.nashorn.internal.runtime.Context;
import jdk.nashorn.internal.runtime.JSType;
import jdk.nashorn.internal.runtime.ScriptRuntime;
import jdk.nashorn.internal.runtime.Source;
import jdk.nashorn.internal.runtime.logging.DebugLogger;
@ -156,15 +159,6 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> implements Lo
return context.getLogger(this.getClass());
}
@Override
public boolean enterBlock(final Block block) {
final FunctionNode function = lc.getCurrentFunction();
if (lc.isFunctionBody() && function.isProgram() && !function.hasDeclaredFunctions()) {
new ExpressionStatement(function.getLineNumber(), block.getToken(), block.getFinish(), LiteralNode.newInstance(block, ScriptRuntime.UNDEFINED)).accept(this);
}
return true;
}
@Override
public Node leaveBlock(final Block block) {
//now we have committed the entire statement list to the block, but we need to truncate
@ -247,19 +241,15 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> implements Lo
public Node leaveForNode(final ForNode forNode) {
ForNode newForNode = forNode;
final Node test = forNode.getTest();
if (!forNode.isForIn() && conservativeAlwaysTrue(test)) {
final Expression test = forNode.getTest();
if (!forNode.isForIn() && isAlwaysTrue(test)) {
newForNode = forNode.setTest(lc, null);
}
newForNode = checkEscape(newForNode);
if(newForNode.isForIn()) {
// Wrap it in a block so its internally created iterator is restricted in scope
BlockStatement b = BlockStatement.createReplacement(newForNode, Collections.singletonList((Statement)newForNode));
if(newForNode.isTerminal()) {
b = b.setBlock(b.getBlock().setIsTerminal(null, true));
}
addStatement(b);
addStatementEnclosedInBlock(newForNode);
} else {
addStatement(newForNode);
}
@ -277,6 +267,16 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> implements Lo
return addStatement(ifNode);
}
@Override
public Node leaveIN(BinaryNode binaryNode) {
return new RuntimeNode(binaryNode);
}
@Override
public Node leaveINSTANCEOF(BinaryNode binaryNode) {
return new RuntimeNode(binaryNode);
}
@Override
public Node leaveLabelNode(final LabelNode labelNode) {
return addStatement(labelNode);
@ -288,16 +288,35 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> implements Lo
return returnNode;
}
@Override
public Node leaveCaseNode(CaseNode caseNode) {
// Try to represent the case test as an integer
final Node test = caseNode.getTest();
if (test instanceof LiteralNode) {
final LiteralNode<?> lit = (LiteralNode<?>)test;
if (lit.isNumeric() && !(lit.getValue() instanceof Integer)) {
if (JSType.isRepresentableAsInt(lit.getNumber())) {
return caseNode.setTest((Expression)LiteralNode.newInstance(lit, lit.getInt32()).accept(this));
}
}
}
return caseNode;
}
@Override
public Node leaveSwitchNode(final SwitchNode switchNode) {
return addStatement(switchNode);
if(!switchNode.isInteger()) {
// Wrap it in a block so its internally created tag is restricted in scope
addStatementEnclosedInBlock(switchNode);
} else {
addStatement(switchNode);
}
return switchNode;
}
@Override
public Node leaveThrowNode(final ThrowNode throwNode) {
addStatement(throwNode); //ThrowNodes are always terminal, marked as such in constructor
return throwNode;
return addStatement(throwNode); //ThrowNodes are always terminal, marked as such in constructor
}
private static Node ensureUniqueNamesIn(final Node node) {
@ -333,10 +352,10 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> implements Lo
final IdentNode exception = new IdentNode(token, finish, lc.getCurrentFunction().uniqueName(CompilerConstants.EXCEPTION_PREFIX.symbolName()));
final Block catchBody = new Block(token, finish, new ThrowNode(lineNumber, token, finish, new IdentNode(exception), ThrowNode.IS_SYNTHETIC_RETHROW));
final Block catchBody = new Block(token, finish, new ThrowNode(lineNumber, token, finish, new IdentNode(exception), true));
assert catchBody.isTerminal(); //ends with throw, so terminal
final CatchNode catchAllNode = new CatchNode(lineNumber, token, finish, new IdentNode(exception), null, catchBody, CatchNode.IS_SYNTHETIC_RETHROW);
final CatchNode catchAllNode = new CatchNode(lineNumber, token, finish, new IdentNode(exception), null, catchBody, true);
final Block catchAllBlock = new Block(token, finish, catchAllNode);
//catchallblock -> catchallnode (catchnode) -> exception -> throw
@ -394,12 +413,12 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> implements Lo
@Override
public Node leaveBreakNode(final BreakNode breakNode) {
return copy(breakNode, (Node)Lower.this.lc.getBreakable(breakNode.getLabel()));
return copy(breakNode, (Node)Lower.this.lc.getBreakable(breakNode.getLabelName()));
}
@Override
public Node leaveContinueNode(final ContinueNode continueNode) {
return copy(continueNode, Lower.this.lc.getContinueTo(continueNode.getLabel()));
return copy(continueNode, Lower.this.lc.getContinueTo(continueNode.getLabelName()));
}
@Override
@ -451,7 +470,7 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> implements Lo
final Block finallyBody = tryNode.getFinallyBody();
if (finallyBody == null) {
return addStatement(tryNode);
return addStatement(ensureUnconditionalCatch(tryNode));
}
/*
@ -494,7 +513,7 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> implements Lo
if (tryNode.getCatchBlocks().isEmpty()) {
newTryNode = tryNode.setFinallyBody(null);
} else {
final Block outerBody = new Block(tryNode.getToken(), tryNode.getFinish(), tryNode.setFinallyBody(null));
final Block outerBody = new Block(tryNode.getToken(), tryNode.getFinish(), ensureUnconditionalCatch(tryNode.setFinallyBody(null)));
newTryNode = tryNode.setBody(outerBody).setCatchBlocks(null);
}
@ -507,6 +526,18 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> implements Lo
return spliceFinally(newTryNode, rethrows, finallyBody);
}
private TryNode ensureUnconditionalCatch(TryNode tryNode) {
final List<CatchNode> catches = tryNode.getCatches();
if(catches == null || catches.isEmpty() || catches.get(catches.size() - 1).getExceptionCondition() == null) {
return tryNode;
}
// If the last catch block is conditional, add an unconditional rethrow block
final List<Block> newCatchBlocks = new ArrayList<>(tryNode.getCatchBlocks());
newCatchBlocks.add(catchAllBlock(tryNode));
return tryNode.setCatchBlocks(newCatchBlocks);
}
@Override
public Node leaveVarNode(final VarNode varNode) {
addStatement(varNode);
@ -518,12 +549,12 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> implements Lo
@Override
public Node leaveWhileNode(final WhileNode whileNode) {
final Node test = whileNode.getTest();
final Expression test = whileNode.getTest();
final Block body = whileNode.getBody();
if (conservativeAlwaysTrue(test)) {
if (isAlwaysTrue(test)) {
//turn it into a for node without a test.
final ForNode forNode = (ForNode)new ForNode(whileNode.getLineNumber(), whileNode.getToken(), whileNode.getFinish(), null, null, body, null, ForNode.IS_FOR).accept(this);
final ForNode forNode = (ForNode)new ForNode(whileNode.getLineNumber(), whileNode.getToken(), whileNode.getFinish(), body, ForNode.IS_FOR).accept(this);
lc.replace(whileNode, forNode);
return forNode;
}
@ -610,10 +641,6 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> implements Lo
return callNode;
}
private static boolean conservativeAlwaysTrue(final Node node) {
return node == null || ((node instanceof LiteralNode) && Boolean.TRUE.equals(((LiteralNode<?>)node).getValue()));
}
/**
* Helper that given a loop body makes sure that it is not terminal if it
* has a continue that leads to the loop header or to outer loops' loop
@ -636,7 +663,7 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> implements Lo
@Override
public Node leaveContinueNode(final ContinueNode node) {
// all inner loops have been popped.
if (lex.contains(lex.getContinueTo(node.getLabel()))) {
if (lex.contains(lex.getContinueTo(node.getLabelName()))) {
escapes.add(node);
}
return node;
@ -663,6 +690,14 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> implements Lo
return statement;
}
private void addStatementEnclosedInBlock(final Statement stmt) {
BlockStatement b = BlockStatement.createReplacement(stmt, Collections.<Statement>singletonList(stmt));
if(stmt.isTerminal()) {
b = b.setBlock(b.getBlock().setIsTerminal(null, true));
}
addStatement(b);
}
/**
* An internal expression has a symbol that is tagged internal. Check if
* this is such a node
@ -671,7 +706,10 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> implements Lo
* @return true if internal, false otherwise
*/
private static boolean isInternalExpression(final Expression expression) {
final Symbol symbol = expression.getSymbol();
if (!(expression instanceof IdentNode)) {
return false;
}
final Symbol symbol = ((IdentNode)expression).getSymbol();
return symbol != null && symbol.isInternal();
}
@ -684,7 +722,6 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> implements Lo
*/
private static boolean isEvalResultAssignment(final Node expression) {
final Node e = expression;
assert e.tokenType() != TokenType.DISCARD; //there are no discards this early anymore
if (e instanceof BinaryNode) {
final Node lhs = ((BinaryNode)e).lhs();
if (lhs instanceof IdentNode) {
@ -693,5 +730,4 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> implements Lo
}
return false;
}
}

View file

@ -139,10 +139,6 @@ public class MapCreator<T> {
flags |= Property.NOT_CONFIGURABLE;
}
if (symbol.canBeUndefined()) {
flags |= Property.CAN_BE_UNDEFINED;
}
if (symbol.isFunctionDeclaration()) {
flags |= Property.IS_FUNCTION_DECLARATION;
}

View file

@ -26,6 +26,8 @@
package jdk.nashorn.internal.codegen;
import static jdk.nashorn.internal.codegen.ObjectClassGenerator.OBJECT_FIELDS_ONLY;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.Symbol;
/**
@ -35,15 +37,17 @@ import jdk.nashorn.internal.ir.Symbol;
class MapTuple<T> {
final String key;
final Symbol symbol;
final Type type;
final T value;
MapTuple(final String key, final Symbol symbol) {
this(key, symbol, null);
MapTuple(final String key, final Symbol symbol, final Type type) {
this(key, symbol, type, null);
}
MapTuple(final String key, final Symbol symbol, final T value) {
MapTuple(final String key, final Symbol symbol, final Type type, final T value) {
this.key = key;
this.symbol = symbol;
this.type = type;
this.value = value;
}

View file

@ -43,11 +43,11 @@ import static jdk.internal.org.objectweb.asm.Opcodes.IFNULL;
import static jdk.internal.org.objectweb.asm.Opcodes.IF_ACMPEQ;
import static jdk.internal.org.objectweb.asm.Opcodes.IF_ACMPNE;
import static jdk.internal.org.objectweb.asm.Opcodes.IF_ICMPEQ;
import static jdk.internal.org.objectweb.asm.Opcodes.IF_ICMPNE;
import static jdk.internal.org.objectweb.asm.Opcodes.IF_ICMPGE;
import static jdk.internal.org.objectweb.asm.Opcodes.IF_ICMPGT;
import static jdk.internal.org.objectweb.asm.Opcodes.IF_ICMPLE;
import static jdk.internal.org.objectweb.asm.Opcodes.IF_ICMPLT;
import static jdk.internal.org.objectweb.asm.Opcodes.IF_ICMPNE;
import static jdk.internal.org.objectweb.asm.Opcodes.INSTANCEOF;
import static jdk.internal.org.objectweb.asm.Opcodes.INVOKEINTERFACE;
import static jdk.internal.org.objectweb.asm.Opcodes.INVOKESPECIAL;
@ -74,11 +74,11 @@ import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALL
import java.io.PrintStream;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collection;
import java.util.EnumSet;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import jdk.internal.dynalink.support.NameCodec;
import jdk.internal.org.objectweb.asm.Handle;
import jdk.internal.org.objectweb.asm.MethodVisitor;
@ -89,12 +89,16 @@ import jdk.nashorn.internal.codegen.types.ArrayType;
import jdk.nashorn.internal.codegen.types.BitwiseType;
import jdk.nashorn.internal.codegen.types.NumericType;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.BreakableNode;
import jdk.nashorn.internal.ir.FunctionNode;
import jdk.nashorn.internal.ir.IdentNode;
import jdk.nashorn.internal.ir.JoinPredecessor;
import jdk.nashorn.internal.ir.LexicalContext;
import jdk.nashorn.internal.ir.LiteralNode;
import jdk.nashorn.internal.ir.LocalVariableConversion;
import jdk.nashorn.internal.ir.RuntimeNode;
import jdk.nashorn.internal.ir.Symbol;
import jdk.nashorn.internal.ir.TryNode;
import jdk.nashorn.internal.objects.Global;
import jdk.nashorn.internal.runtime.ArgumentSetter;
import jdk.nashorn.internal.runtime.Context;
@ -102,6 +106,7 @@ import jdk.nashorn.internal.runtime.Debug;
import jdk.nashorn.internal.runtime.JSType;
import jdk.nashorn.internal.runtime.RewriteException;
import jdk.nashorn.internal.runtime.ScriptObject;
import jdk.nashorn.internal.runtime.UnwarrantedOptimismException;
import jdk.nashorn.internal.runtime.linker.Bootstrap;
import jdk.nashorn.internal.runtime.logging.DebugLogger;
import jdk.nashorn.internal.runtime.options.Options;
@ -123,23 +128,28 @@ public class MethodEmitter implements Emitter {
/** The ASM MethodVisitor we are plugged into */
private final MethodVisitor method;
/** Current type stack for current evaluation */
private Label.Stack stack;
/** Parent classEmitter representing the class of this method */
private final ClassEmitter classEmitter;
/** FunctionNode representing this method, or null if none exists */
protected FunctionNode functionNode;
/** Current type stack for current evaluation */
private Label.Stack stack;
/** Check whether this emitter ever has a function return point */
private boolean hasReturn;
private boolean preventUndefinedLoad;
/**
* Map of live local variable definitions.
*/
private final Map<Symbol, LocalVariableDef> localVariableDefs = new IdentityHashMap<>();
/** The context */
private final Context context;
private final List<Type> localVariableTypes = new ArrayList<>();
/** Threshold in chars for when string constants should be split */
static final int LARGE_STRING_THRESHOLD = 32 * 1024;
@ -225,9 +235,12 @@ public class MethodEmitter implements Emitter {
classEmitter.endMethod(this);
}
void createNewStack() {
assert stack == null;
newStack();
boolean isReachable() {
return stack != null;
}
private void doesNotContinueSequentially() {
stack = null;
}
private void newStack() {
@ -273,26 +286,40 @@ public class MethodEmitter implements Emitter {
}
/**
* Pop a type from the existing stack, ensuring that it is numeric,
* assert if not
* Pop a type from the existing stack, ensuring that it is numeric. Boolean type is popped as int type.
*
* @return the type
*/
private NumericType popNumeric() {
final Type type = popType();
assert type.isNumeric() : type + " is not numeric";
if(type.isBoolean()) {
// Booleans are treated as int for purposes of arithmetic operations
return Type.INT;
}
assert type.isNumeric();
return (NumericType)type;
}
/**
* Pop a type from the existing stack, ensuring that it is an integer type
* (integer or long), assert if not
* (integer or long). Boolean type is popped as int type.
*
* @return the type
*/
private BitwiseType popBitwise() {
final Type type = popType();
if(type == Type.BOOLEAN) {
return Type.INT;
}
return (BitwiseType)type;
}
private BitwiseType popInteger() {
final Type type = popType();
assert type.isInteger() || type.isLong() : type + " is not an integer or long";
if(type == Type.BOOLEAN) {
return Type.INT;
}
assert type == Type.INT;
return (BitwiseType)type;
}
@ -534,24 +561,14 @@ public class MethodEmitter implements Emitter {
}
/**
* Add a local variable. This is a nop if the symbol has no slot
*
* @param symbol symbol for the local variable
* @param start start of scope
* @param end end of scope
* Initializes a bytecode method parameter
* @param symbol the symbol for the parameter
* @param type the type of the parameter
* @param start the label for the start of the method
*/
void localVariable(final Symbol symbol, final Label start, final Label end) {
if (!symbol.hasSlot()) {
return;
}
String name = symbol.getName();
if (name.equals(THIS.symbolName())) {
name = THIS_DEBUGGER.symbolName();
}
method.visitLocalVariable(name, symbol.getSymbolType().getDescriptor(), null, start.getLabel(), end.getLabel(), symbol.getSlot());
void initializeMethodParameter(final Symbol symbol, final Type type, final Label start) {
assert symbol.isBytecodeLocal();
localVariableDefs.put(symbol, new LocalVariableDef(start.getLabel(), type));
}
/**
@ -619,8 +636,8 @@ public class MethodEmitter implements Emitter {
*/
MethodEmitter shr() {
debug("shr");
popType(Type.INT);
pushType(popInteger().shr(method));
popInteger();
pushType(popBitwise().shr(method));
return this;
}
@ -632,21 +649,21 @@ public class MethodEmitter implements Emitter {
*/
MethodEmitter shl() {
debug("shl");
popType(Type.INT);
pushType(popInteger().shl(method));
popInteger();
pushType(popBitwise().shl(method));
return this;
}
/**
* Pops two integer types from the stack, performs a bitwise arithetic shift right and pushes
* Pops two integer types from the stack, performs a bitwise arithmetic shift right and pushes
* the result. The shift count, the first element, must be INT.
*
* @return the method emitter
*/
MethodEmitter sar() {
debug("sar");
popType(Type.INT);
pushType(popInteger().sar(method));
popInteger();
pushType(popBitwise().sar(method));
return this;
}
@ -668,9 +685,12 @@ public class MethodEmitter implements Emitter {
* @param recovery label pointing to start of catch block
*/
void _catch(final Label recovery) {
// While in JVM a catch block can be reached through normal control flow, our code generator never does this,
// so we might as well presume there's no stack on entry.
assert stack == null;
recovery.onCatch();
label(recovery);
stack.clear();
pushType(Type.typeFor(Throwable.class));
beginCatchBlock();
}
/**
@ -680,13 +700,21 @@ public class MethodEmitter implements Emitter {
* @param recoveries labels pointing to start of catch block
*/
void _catch(final Collection<Label> recoveries) {
assert stack == null;
for(final Label l: recoveries) {
label(l);
}
stack.clear();
pushType(Type.OBJECT);
beginCatchBlock();
}
private void beginCatchBlock() {
// It can happen that the catch label wasn't marked as reachable. They are marked as reachable if there's an
// assignment in the try block, but it's possible that there was none.
if(!isReachable()) {
newStack();
}
pushType(Type.typeFor(Throwable.class));
}
/**
* Start a try/catch block.
*
@ -694,8 +722,12 @@ public class MethodEmitter implements Emitter {
* @param exit end label for try
* @param recovery start label for catch
* @param typeDescriptor type descriptor for exception
* @param isOptimismHandler true if this is a hander for {@code UnwarrantedOptimismException}. Normally joining on a
* catch handler kills temporary variables, but optimism handlers are an exception, as they need to capture
* temporaries as well, so they must remain live.
*/
void _try(final Label entry, final Label exit, final Label recovery, final String typeDescriptor) {
private void _try(final Label entry, final Label exit, final Label recovery, final String typeDescriptor, final boolean isOptimismHandler) {
recovery.joinFromTry(entry.getStack(), isOptimismHandler);
method.visitTryCatchBlock(entry.getLabel(), exit.getLabel(), recovery.getLabel(), typeDescriptor);
}
@ -708,7 +740,7 @@ public class MethodEmitter implements Emitter {
* @param clazz exception class
*/
void _try(final Label entry, final Label exit, final Label recovery, final Class<?> clazz) {
method.visitTryCatchBlock(entry.getLabel(), exit.getLabel(), recovery.getLabel(), CompilerConstants.className(clazz));
_try(entry, exit, recovery, CompilerConstants.className(clazz), clazz == UnwarrantedOptimismException.class);
}
/**
@ -719,9 +751,12 @@ public class MethodEmitter implements Emitter {
* @param recovery start label for catch
*/
void _try(final Label entry, final Label exit, final Label recovery) {
_try(entry, exit, recovery, (String)null);
_try(entry, exit, recovery, (String)null, false);
}
void markLabelAsOptimisticCatchHandler(final Label label, final int liveLocalCount) {
label.markAsOptimisticCatchHandler(stack, liveLocalCount);
}
/**
* Load the constants array
@ -894,23 +929,36 @@ public class MethodEmitter implements Emitter {
}
/**
* Push a local variable to the stack. If the symbol representing
* the local variable doesn't have a slot, this is a NOP
* Pushes the value of an identifier to the stack. If the identifier does not represent a local variable or a
* parameter, this will be a no-op.
*
* @param symbol the symbol representing the local variable.
* @param ident the identifier for the variable being loaded.
*
* @return the method emitter
*/
MethodEmitter load(final Symbol symbol) {
MethodEmitter load(final IdentNode ident) {
return load(ident.getSymbol(), ident.getType());
}
/**
* Pushes the value of the symbol to the stack with the specified type. No type conversion is being performed, and
* the type is only being used if the symbol addresses a local variable slot. The value of the symbol is loaded if
* it addresses a local variable slot, or it is a parameter (in which case it can also be loaded from a vararg array
* or the arguments object). If it is neither, the operation is a no-op.
*
* @param symbol the symbol addressing the value being loaded
* @param type the presumed type of the value when it is loaded from a local variable slot
* @return the method emitter
*/
MethodEmitter load(final Symbol symbol, final Type type) {
assert symbol != null;
if (symbol.hasSlot()) {
final int slot = symbol.getSlot();
debug("load symbol", symbol.getName(), " slot=", slot, "type=", symbol.getSymbolType());
load(symbol.getSymbolType(), slot);
final int slot = symbol.getSlot(type);
debug("load symbol", symbol.getName(), " slot=", slot, "type=", type);
load(type, slot);
// _try(new Label("dummy"), new Label("dummy2"), recovery);
// method.visitTryCatchBlock(new Label(), arg1, arg2, arg3);
} else if (symbol.isParam()) {
assert !symbol.isScope();
assert functionNode.isVarArg() : "Non-vararg functions have slotted parameters";
final int index = symbol.getFieldIndex();
if (functionNode.needsArguments()) {
@ -931,7 +979,7 @@ public class MethodEmitter implements Emitter {
}
/**
* Push a local variable to the stack, given an explicit bytecode slot
* Push a local variable to the stack, given an explicit bytecode slot.
* This is used e.g. for stub generation where we know where items like
* "this" and "scope" reside.
*
@ -945,6 +993,8 @@ public class MethodEmitter implements Emitter {
final Type loadType = type.load(method, slot);
assert loadType != null;
pushType(loadType == Type.OBJECT && isThisSlot(slot) ? Type.THIS : loadType);
assert !preventUndefinedLoad || (slot < stack.localVariableTypes.size() && stack.localVariableTypes.get(slot) != Type.UNKNOWN)
: "Attempted load of uninitialized slot " + slot + " (as type " + type + ")";
stack.markLocalLoad(slot);
return this;
}
@ -953,7 +1003,7 @@ public class MethodEmitter implements Emitter {
if (functionNode == null) {
return slot == CompilerConstants.JAVA_THIS.slot();
}
final int thisSlot = compilerConstant(THIS).getSlot();
final int thisSlot = getCompilerConstantSymbol(THIS).getSlot(Type.OBJECT);
assert !functionNode.needsCallee() || thisSlot == 1; // needsCallee -> thisSlot == 1
assert functionNode.needsCallee() || thisSlot == 0; // !needsCallee -> thisSlot == 0
return slot == thisSlot;
@ -975,7 +1025,7 @@ public class MethodEmitter implements Emitter {
return this;
}
private Symbol compilerConstant(final CompilerConstants cc) {
private Symbol getCompilerConstantSymbol(final CompilerConstants cc) {
return functionNode.getBody().getExistingSymbol(cc.symbolName());
}
@ -985,22 +1035,38 @@ public class MethodEmitter implements Emitter {
* @return if this method has a slot allocated for the scope variable.
*/
boolean hasScope() {
return compilerConstant(SCOPE).hasSlot();
return getCompilerConstantSymbol(SCOPE).hasSlot();
}
MethodEmitter loadCompilerConstant(final CompilerConstants cc) {
final Symbol symbol = compilerConstant(cc);
return loadCompilerConstant(cc, null);
}
MethodEmitter loadCompilerConstant(final CompilerConstants cc, final Type type) {
if (cc == SCOPE && peekType() == Type.SCOPE) {
dup();
return this;
}
return load(symbol);
return load(getCompilerConstantSymbol(cc), type != null ? type : getCompilerConstantType(cc));
}
void storeCompilerConstant(final CompilerConstants cc) {
final Symbol symbol = compilerConstant(cc);
storeCompilerConstant(cc, null);
}
void storeCompilerConstant(final CompilerConstants cc, final Type type) {
final Symbol symbol = getCompilerConstantSymbol(cc);
if(!symbol.hasSlot()) {
return;
}
debug("store compiler constant ", symbol);
store(symbol);
store(symbol, type != null ? type : getCompilerConstantType(cc));
}
private static Type getCompilerConstantType(final CompilerConstants cc) {
final Class<?> constantType = cc.type();
assert constantType != null;
return Type.typeFor(constantType);
}
/**
@ -1032,74 +1098,212 @@ public class MethodEmitter implements Emitter {
/**
* Pop a value from the stack and store it in a local variable represented
* by the given symbol. If the symbol has no slot, this is a NOP
* by the given identifier. If the symbol has no slot, this is a NOP
*
* @param symbol symbol to store stack to
* @param ident identifier to store stack to
*/
void store(final Symbol symbol) {
void store(final IdentNode ident) {
final Type type = ident.getType();
final Symbol symbol = ident.getSymbol();
if(type == Type.UNDEFINED) {
assert peekType() == Type.UNDEFINED;
store(symbol, Type.OBJECT);
} else {
store(symbol, type);
}
}
/**
* Represents a definition of a local variable with a type. Used for local variable table building.
*/
private static class LocalVariableDef {
// The start label from where this definition lives.
private final jdk.internal.org.objectweb.asm.Label label;
// The currently live type of the local variable.
private final Type type;
LocalVariableDef(final jdk.internal.org.objectweb.asm.Label label, final Type type) {
this.label = label;
this.type = type;
}
}
void closeLocalVariable(final Symbol symbol, final Label label) {
final LocalVariableDef def = localVariableDefs.get(symbol);
if(def != null) {
endLocalValueDef(symbol, def, label.getLabel());
}
if(isReachable()) {
markDeadLocalVariable(symbol);
}
}
void markDeadLocalVariable(final Symbol symbol) {
if(!symbol.isDead()) {
markDeadSlots(symbol.getFirstSlot(), symbol.slotCount());
}
}
void markDeadSlots(final int firstSlot, final int slotCount) {
stack.markDeadLocalVariables(firstSlot, slotCount);
}
private void endLocalValueDef(final Symbol symbol, final LocalVariableDef def, final jdk.internal.org.objectweb.asm.Label label) {
String name = symbol.getName();
if (name.equals(THIS.symbolName())) {
name = THIS_DEBUGGER.symbolName();
}
method.visitLocalVariable(name, def.type.getDescriptor(), null, def.label, label, symbol.getSlot(def.type));
}
void store(final Symbol symbol, final Type type) {
store(symbol, type, true);
}
/**
* Pop a value from the stack and store it in a variable denoted by the given symbol. The variable should be either
* a local variable, or a function parameter (and not a scoped variable). For local variables, this method will also
* do the bookeeping of the local variable table as well as mark values in all alternative slots for the symbol as
* dead. In this regard it differs from {@link #storeHidden(Type, int)}.
*
* @param symbol the symbol to store into.
* @param type the type to store
* @param onlySymbolLiveValue if true, this is the sole live value for the symbol. If false, currently live values should
* be kept live.
*/
void store(final Symbol symbol, final Type type, final boolean onlySymbolLiveValue) {
assert symbol != null : "No symbol to store";
if (symbol.hasSlot()) {
final int slot = symbol.getSlot();
debug("store symbol", symbol.getName(), " slot=", slot);
store(symbol.getSymbolType(), slot);
final boolean isLiveType = symbol.hasSlotFor(type);
final LocalVariableDef existingDef = localVariableDefs.get(symbol);
if(existingDef == null || existingDef.type != type) {
final jdk.internal.org.objectweb.asm.Label here = new jdk.internal.org.objectweb.asm.Label();
if(isLiveType) {
final LocalVariableDef newDef = new LocalVariableDef(here, type);
localVariableDefs.put(symbol, newDef);
}
method.visitLabel(here);
if(existingDef != null) {
endLocalValueDef(symbol, existingDef, here);
}
}
if(isLiveType) {
final int slot = symbol.getSlot(type);
debug("store symbol", symbol.getName(), " type=", type, " slot=", slot);
storeHidden(type, slot, onlySymbolLiveValue);
} else {
if(onlySymbolLiveValue) {
markDeadLocalVariable(symbol);
}
debug("dead store symbol ", symbol.getName(), " type=", type);
pop();
}
} else if (symbol.isParam()) {
assert !symbol.isScope();
assert functionNode.isVarArg() : "Non-vararg functions have slotted parameters";
final int index = symbol.getFieldIndex();
if (functionNode.needsArguments()) {
convert(Type.OBJECT);
debug("store symbol", symbol.getName(), " arguments index=", index);
loadCompilerConstant(ARGUMENTS);
load(index);
ArgumentSetter.SET_ARGUMENT.invoke(this);
} else {
convert(Type.OBJECT);
// varargs without arguments object - just do array store to __varargs__
debug("store symbol", symbol.getName(), " array index=", index);
loadCompilerConstant(VARARGS);
load(index);
ArgumentSetter.SET_ARRAY_ELEMENT.invoke(this);
}
} else {
debug("dead store symbol ", symbol.getName(), " type=", type);
pop();
}
}
/**
* Pop a value from the stack and store it in a given local variable
* slot.
* Pop a value from the stack and store it in a local variable slot. Note that in contrast with
* {@link #store(Symbol, Type)}, this method does not adjust the local variable table, nor marks slots for
* alternative value types for the symbol as being dead. For that reason, this method is usually not called
* directly. Notable exceptions are temporary internal locals (e.g. quick store, last-catch-condition, etc.) that
* are not desired to show up in the local variable table.
*
* @param type the type to pop
* @param slot the slot
*/
void store(final Type type, final int slot) {
void storeHidden(final Type type, final int slot) {
storeHidden(type, slot, true);
}
void storeHidden(final Type type, final int slot, final boolean onlyLiveSymbolValue) {
explicitStore(type, slot);
stack.onLocalStore(type, slot, onlyLiveSymbolValue);
}
void storeTemp(final Type type, final int slot) {
explicitStore(type, slot);
defineTemporaryLocalVariable(slot, slot + type.getSlots());
onLocalStore(type, slot);
}
void onLocalStore(final Type type, final int slot) {
stack.onLocalStore(type, slot, true);
}
private void explicitStore(final Type type, final int slot) {
assert slot != -1;
debug("explicit store", type, slot);
popType(type);
type.store(method, slot);
// TODO: disable this when not running with optimistic types?
final int slotCount = type.getSlots();
ensureLocalVariableCount(slot + slotCount);
localVariableTypes.set(slot, type);
if(slotCount == 2) {
localVariableTypes.set(slot + 1, Type.SLOT_2);
}
stack.markLocalStore(slot, slotCount);
}
void ensureLocalVariableCount(final int slotCount) {
while(localVariableTypes.size() < slotCount) {
localVariableTypes.add(Type.UNKNOWN);
/**
* Marks a range of slots as belonging to a defined local variable. The slots will start out with no live value
* in them.
* @param fromSlot first slot, inclusive.
* @param toSlot last slot, exclusive.
*/
void defineBlockLocalVariable(final int fromSlot, final int toSlot) {
stack.defineBlockLocalVariable(fromSlot, toSlot);
}
/**
* Marks a range of slots as belonging to a defined temporary local variable. The slots will start out with no
* live value in them.
* @param fromSlot first slot, inclusive.
* @param toSlot last slot, exclusive.
*/
void defineTemporaryLocalVariable(final int fromSlot, final int toSlot) {
stack.defineTemporaryLocalVariable(fromSlot, toSlot);
}
/**
* Defines a new temporary local variable and returns its allocated index.
* @param width the required width (in slots) for the new variable.
* @return the bytecode slot index where the newly allocated local begins.
*/
int defineTemporaryLocalVariable(final int width) {
return stack.defineTemporaryLocalVariable(width);
}
void undefineLocalVariables(final int fromSlot, final boolean canTruncateSymbol) {
if(isReachable()) {
stack.undefineLocalVariables(fromSlot, canTruncateSymbol);
}
}
List<Type> getLocalVariableTypes() {
return localVariableTypes;
return stack.localVariableTypes;
}
void setParameterTypes(final Type... paramTypes) {
assert localVariableTypes.isEmpty();
for(final Type type: paramTypes) {
localVariableTypes.add(type);
if(type.isCategory2()) {
localVariableTypes.add(Type.SLOT_2);
}
}
List<Type> getWidestLiveLocals(final List<Type> localTypes) {
return stack.getWidestLiveLocals(localTypes);
}
String markSymbolBoundariesInLvarTypesDescriptor(final String lvarDescriptor) {
return stack.markSymbolBoundariesInLvarTypesDescriptor(lvarDescriptor);
}
/**
@ -1122,7 +1326,7 @@ public class MethodEmitter implements Emitter {
final Type receiver = popType(Type.OBJECT);
assert Throwable.class.isAssignableFrom(receiver.getTypeClass()) : receiver.getTypeClass();
method.visitInsn(ATHROW);
stack = null;
doesNotContinueSequentially();
}
/**
@ -1354,7 +1558,7 @@ public class MethodEmitter implements Emitter {
debug("lookupswitch", peekType());
adjustStackForSwitch(defaultLabel, table);
method.visitLookupSwitchInsn(defaultLabel.getLabel(), values, getLabels(table));
stack = null; //whoever reaches the point after us provides the stack, because we don't
doesNotContinueSequentially();
}
/**
@ -1368,14 +1572,14 @@ public class MethodEmitter implements Emitter {
debug("tableswitch", peekType());
adjustStackForSwitch(defaultLabel, table);
method.visitTableSwitchInsn(lo, hi, defaultLabel.getLabel(), getLabels(table));
stack = null; //whoever reaches the point after us provides the stack, because we don't
doesNotContinueSequentially();
}
private void adjustStackForSwitch(final Label defaultLabel, final Label... table) {
popType(Type.INT);
mergeStackTo(defaultLabel);
joinTo(defaultLabel);
for(final Label label: table) {
mergeStackTo(label);
joinTo(label);
}
}
@ -1432,7 +1636,7 @@ public class MethodEmitter implements Emitter {
convert(type);
}
popType(type)._return(method);
stack = null;
doesNotContinueSequentially();
}
/**
@ -1449,7 +1653,7 @@ public class MethodEmitter implements Emitter {
debug("return [void]");
assert stack.isEmpty() : stack;
method.visitInsn(RETURN);
stack = null;
doesNotContinueSequentially();
}
/**
@ -1458,8 +1662,10 @@ public class MethodEmitter implements Emitter {
* jump target is another method
*
* @param label destination label
* @param targetNode the node to which the destination label belongs (the label is normally a break or continue
* label)
*/
void splitAwareGoto(final LexicalContext lc, final Label label) {
void splitAwareGoto(final LexicalContext lc, final Label label, final BreakableNode targetNode) {
_goto(label);
}
@ -1486,7 +1692,7 @@ public class MethodEmitter implements Emitter {
assert peekType().isInteger() || peekType().isBoolean() || peekType().isObject() : "expecting integer type or object for jump, but found " + peekType();
popType();
}
mergeStackTo(label);
joinTo(label);
method.visitJumpInsn(opcode, label.getLabel());
}
@ -1658,17 +1864,41 @@ public class MethodEmitter implements Emitter {
void _goto(final Label label) {
debug("goto", label);
jump(GOTO, label, 0);
stack = null; //whoever reaches the point after us provides the stack, because we don't
doesNotContinueSequentially(); //whoever reaches the point after us provides the stack, because we don't
}
/**
* Examine two stacks and make sure they are of the same size and their
* contents are equivalent to each other
* @param s0 first stack
* @param s1 second stack
* Unconditional jump to the start label of a loop. It differs from ordinary {@link #_goto(Label)} in that it will
* preserve the current label stack, as the next instruction after the goto is loop body that the loop will come
* back to. Also used to jump at the start label of the continuation handler, as it behaves much like a loop test in
* the sense that after it is evaluated, it also jumps backwards.
*
* @return true if stacks are equivalent, false otherwise
* @param loopStart start label of a loop
*/
void gotoLoopStart(final Label loopStart) {
debug("goto (loop)", loopStart);
jump(GOTO, loopStart, 0);
}
/**
* Unconditional jump without any control flow and data flow testing. You should not normally use this method when
* generating code, except if you're very sure that you know what you're doing. Normally only used for the
* admittedly torturous control flow of continuation handler plumbing.
* @param target the target of the jump
*/
void uncheckedGoto(final Label target) {
method.visitJumpInsn(GOTO, target.getLabel());
}
/**
* Potential transfer of control to a catch block.
*
* @param catchLabel destination catch label
*/
void canThrow(final Label catchLabel) {
catchLabel.joinFromTry(stack, false);
}
/**
* A join in control flow - helper function that makes sure all entry stacks
* discovered for the join point so far are equivalent
@ -1678,46 +1908,46 @@ public class MethodEmitter implements Emitter {
*
* @param label label
*/
private void mergeStackTo(final Label label) {
//sometimes we can do a merge stack without having a stack - i.e. when jumping ahead to dead code
//see NASHORN-73. So far we had been saved by the line number nodes. This should have been fixed
//by Lower removing everything after an unconditionally executed terminating statement OR a break
//or continue in a block. Previously code left over after breaks and continues was still there
//and caused bytecode to be generated - which crashed on stack not being there, as the merge
//was not in fact preceeded by a visit. Furthermore, this led to ASM putting out its NOP NOP NOP
//ATHROW sequences instead of no code being generated at all. This should now be fixed.
assert stack != null : label + " entered with no stack. deadcode that remains?";
final Label.Stack labelStack = label.getStack();
if (labelStack == null) {
label.setStack(stack.copy());
return;
}
assert stack.isEquivalentInTypesTo(labelStack) : "stacks " + stack + " is not equivalent with " + labelStack + " at join point " + label;
stack.mergeLocalLoads(labelStack);
private void joinTo(final Label label) {
assert isReachable();
label.joinFrom(stack);
}
/**
* Register a new label, enter it here.
*
* @param label the label
* @param label
*/
void label(final Label label) {
/*
* If stack == null, this means that we came here not through a fallthrough.
* E.g. a label after an athrow. Then we create a new stack if one doesn't exist
* for this location already.
*/
if (stack == null) {
stack = label.getStack();
if (stack == null) {
newStack();
}
breakLabel(label, -1);
}
/**
* Register a new break target label, enter it here.
*
* @param label the label
* @param liveLocals the number of live locals at this label
*/
void breakLabel(final Label label, final int liveLocals) {
if (!isReachable()) {
// If we emit a label, and the label's stack is null, it must not be reachable.
assert (label.getStack() == null) != label.isReachable();
} else {
joinTo(label);
}
// Use label's stack as we might have no stack.
final Label.Stack labelStack = label.getStack();
stack = labelStack == null ? null : labelStack.clone();
if(stack != null && label.isBreakTarget() && liveLocals != -1) {
// This has to be done because we might not have another frame to provide us with its firstTemp if the label
// is only reachable through a break or continue statement; also in this case, the frame can actually
// give us a higher number of live locals, e.g. if it comes from a catch. Typical example:
// for(;;) { try{ throw 0; } catch(e) { break; } }.
// Since the for loop can only be exited through the break in the catch block, it'll bring with it the
// "e" as a live local, and we need to trim it off here.
assert stack.firstTemp >= liveLocals;
stack.firstTemp = liveLocals;
}
debug_label(label);
mergeStackTo(label); //we have to merge our stack to whatever is in the label
method.visitLabel(label.getLabel());
}
@ -1777,8 +2007,8 @@ public class MethodEmitter implements Emitter {
* @return common type
*/
private BitwiseType get2i() {
final BitwiseType p0 = popInteger();
final BitwiseType p1 = popInteger();
final BitwiseType p0 = popBitwise();
final BitwiseType p1 = popBitwise();
assert p0.isEquivalentTo(p1) : "expecting equivalent types on stack but got " + p0 + " and " + p1;
return p0;
}
@ -1844,9 +2074,9 @@ public class MethodEmitter implements Emitter {
*
* @return the method emitter
*/
MethodEmitter rem() {
MethodEmitter rem(final int programPoint) {
debug("rem");
pushType(get2n().rem(method));
pushType(get2n().rem(method, programPoint));
return this;
}
@ -1868,6 +2098,14 @@ public class MethodEmitter implements Emitter {
return stack.size();
}
int getFirstTemp() {
return stack.firstTemp;
}
int getUsedSlotsWithLiveTemporaries() {
return stack.getUsedSlotsWithLiveTemporaries();
}
/**
* Helper function to generate a function signature based on stack contents
* and argument count and return type
@ -2054,7 +2292,6 @@ public class MethodEmitter implements Emitter {
return this;
}
private static String getProgramPoint(final int flags) {
if((flags & CALLSITE_OPTIMISTIC) == 0) {
return "";
@ -2236,6 +2473,44 @@ public class MethodEmitter implements Emitter {
}
}
void beforeJoinPoint(final JoinPredecessor joinPredecessor) {
LocalVariableConversion next = joinPredecessor.getLocalVariableConversion();
while(next != null) {
final Symbol symbol = next.getSymbol();
if(next.isLive()) {
emitLocalVariableConversion(next, true);
} else {
markDeadLocalVariable(symbol);
}
next = next.getNext();
}
}
void beforeTry(final TryNode tryNode, final Label recovery) {
LocalVariableConversion next = tryNode.getLocalVariableConversion();
while(next != null) {
if(next.isLive()) {
final Type to = emitLocalVariableConversion(next, false);
recovery.getStack().onLocalStore(to, next.getSymbol().getSlot(to), true);
}
next = next.getNext();
}
}
private Type emitLocalVariableConversion(final LocalVariableConversion conversion, final boolean onlySymbolLiveValue) {
final Type from = conversion.getFrom();
final Type to = conversion.getTo();
final Symbol symbol = conversion.getSymbol();
assert symbol.isBytecodeLocal();
if(from == Type.UNDEFINED) {
loadUndefined(to);
} else {
load(symbol, from).convert(to);
}
store(symbol, to, onlySymbolLiveValue);
return to;
}
/*
* Debugging below
*/
@ -2334,7 +2609,7 @@ public class MethodEmitter implements Emitter {
pad--;
}
if (stack != null && !stack.isEmpty()) {
if (isReachable() && !stack.isEmpty()) {
sb.append("{");
sb.append(stack.size());
sb.append(":");
@ -2408,8 +2683,14 @@ public class MethodEmitter implements Emitter {
return hasReturn;
}
List<Label> getExternalTargets() {
return null;
/**
* Invoke to enforce assertions preventing load from a local variable slot that's known to not have been written to.
* Used by CodeGenerator, as it strictly enforces tracking of stores. Simpler uses of MethodEmitter, e.g. those
* for creating initializers for structure classes, array getters, etc. don't have strict tracking of stores,
* therefore they would fail if they had this assertion turned on.
*/
void setPreventUndefinedLoad() {
this.preventUndefinedLoad = true;
}
private static boolean isOptimistic(final int flags) {

View file

@ -38,12 +38,12 @@ import static jdk.nashorn.internal.lookup.Lookup.MH;
import static jdk.nashorn.internal.runtime.JSType.CONVERT_OBJECT;
import static jdk.nashorn.internal.runtime.JSType.CONVERT_OBJECT_OPTIMISTIC;
import static jdk.nashorn.internal.runtime.JSType.GET_UNDEFINED;
import static jdk.nashorn.internal.runtime.JSType.getAccessorTypeIndex;
import static jdk.nashorn.internal.runtime.JSType.TYPE_UNDEFINED_INDEX;
import static jdk.nashorn.internal.runtime.JSType.TYPE_DOUBLE_INDEX;
import static jdk.nashorn.internal.runtime.JSType.TYPE_INT_INDEX;
import static jdk.nashorn.internal.runtime.JSType.TYPE_LONG_INDEX;
import static jdk.nashorn.internal.runtime.JSType.TYPE_DOUBLE_INDEX;
import static jdk.nashorn.internal.runtime.JSType.TYPE_OBJECT_INDEX;
import static jdk.nashorn.internal.runtime.JSType.TYPE_UNDEFINED_INDEX;
import static jdk.nashorn.internal.runtime.JSType.getAccessorTypeIndex;
import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.isValid;
import java.lang.invoke.MethodHandle;
@ -58,6 +58,7 @@ import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.runtime.AccessorProperty;
import jdk.nashorn.internal.runtime.Context;
import jdk.nashorn.internal.runtime.FunctionScope;
import jdk.nashorn.internal.runtime.JSType;
import jdk.nashorn.internal.runtime.PropertyMap;
import jdk.nashorn.internal.runtime.ScriptEnvironment;
import jdk.nashorn.internal.runtime.ScriptObject;
@ -303,11 +304,11 @@ public final class ObjectClassGenerator implements Loggable {
addFields(classEmitter, fieldCount);
final MethodEmitter init = newInitMethod(className, classEmitter);
final MethodEmitter init = newInitMethod(classEmitter);
init.returnVoid();
init.end();
final MethodEmitter initWithSpillArrays = newInitWithSpillArraysMethod(className, classEmitter, ScriptObject.class);
final MethodEmitter initWithSpillArrays = newInitWithSpillArraysMethod(classEmitter, ScriptObject.class);
initWithSpillArrays.returnVoid();
initWithSpillArrays.end();
@ -332,17 +333,17 @@ public final class ObjectClassGenerator implements Loggable {
final ClassEmitter classEmitter = newClassEmitter(className, superName);
final List<String> initFields = addFields(classEmitter, fieldCount);
final MethodEmitter init = newInitScopeMethod(className, classEmitter);
final MethodEmitter init = newInitScopeMethod(classEmitter);
initializeToUndefined(init, className, initFields);
init.returnVoid();
init.end();
final MethodEmitter initWithSpillArrays = newInitWithSpillArraysMethod(className, classEmitter, FunctionScope.class);
final MethodEmitter initWithSpillArrays = newInitWithSpillArraysMethod(classEmitter, FunctionScope.class);
initializeToUndefined(initWithSpillArrays, className, initFields);
initWithSpillArrays.returnVoid();
initWithSpillArrays.end();
final MethodEmitter initWithArguments = newInitScopeWithArgumentsMethod(className, classEmitter);
final MethodEmitter initWithArguments = newInitScopeWithArgumentsMethod(classEmitter);
initializeToUndefined(initWithArguments, className, initFields);
initWithArguments.returnVoid();
initWithArguments.end();
@ -396,7 +397,7 @@ public final class ObjectClassGenerator implements Loggable {
*
* @return Open method emitter.
*/
private static MethodEmitter newInitMethod(final String className, final ClassEmitter classEmitter) {
private static MethodEmitter newInitMethod(final ClassEmitter classEmitter) {
final MethodEmitter init = classEmitter.init(PropertyMap.class);
init.begin();
init.load(Type.OBJECT, JAVA_THIS.slot());
@ -406,7 +407,7 @@ public final class ObjectClassGenerator implements Loggable {
return init;
}
private static MethodEmitter newInitWithSpillArraysMethod(final String className, final ClassEmitter classEmitter, final Class<?> superClass) {
private static MethodEmitter newInitWithSpillArraysMethod(final ClassEmitter classEmitter, final Class<?> superClass) {
final MethodEmitter init = classEmitter.init(PropertyMap.class, long[].class, Object[].class);
init.begin();
init.load(Type.OBJECT, JAVA_THIS.slot());
@ -423,7 +424,7 @@ public final class ObjectClassGenerator implements Loggable {
* @param classEmitter Open class emitter.
* @return Open method emitter.
*/
private static MethodEmitter newInitScopeMethod(final String className, final ClassEmitter classEmitter) {
private static MethodEmitter newInitScopeMethod(final ClassEmitter classEmitter) {
final MethodEmitter init = classEmitter.init(PropertyMap.class, ScriptObject.class);
init.begin();
init.load(Type.OBJECT, JAVA_THIS.slot());
@ -439,7 +440,7 @@ public final class ObjectClassGenerator implements Loggable {
* @param classEmitter Open class emitter.
* @return Open method emitter.
*/
private static MethodEmitter newInitScopeWithArgumentsMethod(final String className, final ClassEmitter classEmitter) {
private static MethodEmitter newInitScopeWithArgumentsMethod(final ClassEmitter classEmitter) {
final MethodEmitter init = classEmitter.init(PropertyMap.class, ScriptObject.class, ScriptObject.class);
init.begin();
init.load(Type.OBJECT, JAVA_THIS.slot());
@ -523,8 +524,7 @@ public final class ObjectClassGenerator implements Loggable {
final MethodHandle sameTypeGetter = getterForType(forType, primitiveGetter, objectGetter);
final MethodHandle mh = MH.asType(sameTypeGetter, sameTypeGetter.type().changeReturnType(Object.class));
try {
@SuppressWarnings("cast")
final Object value = (Object)mh.invokeExact(receiver);
final Object value = mh.invokeExact(receiver);
throw new UnwarrantedOptimismException(value, programPoint);
} catch (final Error | RuntimeException e) {
throw e;
@ -648,12 +648,14 @@ public final class ObjectClassGenerator implements Loggable {
assert primitiveGetter != null;
final MethodType tgetterType = tgetter.type();
switch (fti) {
case TYPE_INT_INDEX:
case TYPE_INT_INDEX: {
return MH.asType(tgetter, tgetterType.changeReturnType(type));
}
case TYPE_LONG_INDEX:
switch (ti) {
case TYPE_INT_INDEX:
//get int while an int, truncating cast of long value
return MH.explicitCastArguments(tgetter, tgetterType.changeReturnType(int.class));
return MH.filterReturnValue(tgetter, JSType.TO_INT32_L.methodHandle);
case TYPE_LONG_INDEX:
return primitiveGetter;
default:
@ -662,6 +664,7 @@ public final class ObjectClassGenerator implements Loggable {
case TYPE_DOUBLE_INDEX:
switch (ti) {
case TYPE_INT_INDEX:
return MH.filterReturnValue(tgetter, JSType.TO_INT32_D.methodHandle);
case TYPE_LONG_INDEX:
return MH.explicitCastArguments(tgetter, tgetterType.changeReturnType(type));
case TYPE_DOUBLE_INDEX:

View file

@ -128,11 +128,12 @@ public abstract class ObjectCreator<T> {
* Technique for loading an initial value. Defined by anonymous subclasses in code gen.
*
* @param value Value to load.
* @param type the type of the value to load
*/
protected abstract void loadValue(T value);
protected abstract void loadValue(T value, Type type);
MethodEmitter loadTuple(final MethodEmitter method, final MapTuple<T> tuple, final boolean pack) {
loadValue(tuple.value);
loadValue(tuple.value, tuple.type);
if (pack && tuple.isPrimitive()) {
method.pack();
} else {

View file

@ -0,0 +1,284 @@
/*
* 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.
*/
package jdk.nashorn.internal.codegen;
import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.isValid;
import java.util.ArrayDeque;
import java.util.BitSet;
import java.util.Deque;
import jdk.nashorn.internal.IntDeque;
import jdk.nashorn.internal.ir.AccessNode;
import jdk.nashorn.internal.ir.BinaryNode;
import jdk.nashorn.internal.ir.CallNode;
import jdk.nashorn.internal.ir.CatchNode;
import jdk.nashorn.internal.ir.Expression;
import jdk.nashorn.internal.ir.ExpressionStatement;
import jdk.nashorn.internal.ir.ForNode;
import jdk.nashorn.internal.ir.FunctionNode;
import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
import jdk.nashorn.internal.ir.IdentNode;
import jdk.nashorn.internal.ir.IfNode;
import jdk.nashorn.internal.ir.IndexNode;
import jdk.nashorn.internal.ir.JoinPredecessorExpression;
import jdk.nashorn.internal.ir.LexicalContext;
import jdk.nashorn.internal.ir.LoopNode;
import jdk.nashorn.internal.ir.Node;
import jdk.nashorn.internal.ir.Optimistic;
import jdk.nashorn.internal.ir.PropertyNode;
import jdk.nashorn.internal.ir.SplitNode;
import jdk.nashorn.internal.ir.Symbol;
import jdk.nashorn.internal.ir.TernaryNode;
import jdk.nashorn.internal.ir.UnaryNode;
import jdk.nashorn.internal.ir.VarNode;
import jdk.nashorn.internal.ir.WhileNode;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.parser.TokenType;
import jdk.nashorn.internal.runtime.ScriptObject;
/**
* Assigns optimistic types to expressions that can have them. This class mainly contains logic for which expressions
* must not ever be marked as optimistic, assigning narrowest non-invalidated types to program points from the
* compilation environment, as well as initializing optimistic types of global properties for scripts.
*/
final class OptimisticTypesCalculator extends NodeVisitor<LexicalContext> {
final CompilationEnvironment env;
// Per-function bit set of program points that must never be optimistic.
final Deque<BitSet> neverOptimistic = new ArrayDeque<>();
// Per-function depth of split nodes
final IntDeque splitDepth = new IntDeque();
OptimisticTypesCalculator(final CompilationEnvironment env) {
super(new LexicalContext());
this.env = env;
}
@Override
public boolean enterAccessNode(final AccessNode accessNode) {
tagNeverOptimistic(accessNode.getBase());
return true;
}
@Override
public boolean enterPropertyNode(PropertyNode propertyNode) {
if(propertyNode.getKeyName().equals(ScriptObject.PROTO_PROPERTY_NAME)) {
tagNeverOptimistic(propertyNode.getValue());
}
return super.enterPropertyNode(propertyNode);
}
@Override
public boolean enterBinaryNode(final BinaryNode binaryNode) {
if(binaryNode.isAssignment()) {
final Expression lhs = binaryNode.lhs();
if(!binaryNode.isSelfModifying()) {
tagNeverOptimistic(lhs);
}
if(lhs instanceof IdentNode) {
final Symbol symbol = ((IdentNode)lhs).getSymbol();
// Assignment to internal symbols is never optimistic, except for self-assignment expressions
if(symbol.isInternal() && !binaryNode.rhs().isSelfModifying()) {
tagNeverOptimistic(binaryNode.rhs());
}
}
} else if(binaryNode.isTokenType(TokenType.INSTANCEOF)) {
tagNeverOptimistic(binaryNode.lhs());
tagNeverOptimistic(binaryNode.rhs());
}
return true;
}
@Override
public boolean enterCallNode(final CallNode callNode) {
tagNeverOptimistic(callNode.getFunction());
return true;
}
@Override
public boolean enterCatchNode(final CatchNode catchNode) {
// Condition is never optimistic (always coerced to boolean).
tagNeverOptimistic(catchNode.getExceptionCondition());
return true;
}
@Override
public boolean enterExpressionStatement(final ExpressionStatement expressionStatement) {
final Expression expr = expressionStatement.getExpression();
if(!expr.isSelfModifying()) {
tagNeverOptimistic(expr);
}
return true;
}
@Override
public boolean enterForNode(final ForNode forNode) {
if(forNode.isForIn()) {
// for..in has the iterable in its "modify"
tagNeverOptimistic(forNode.getModify());
} else {
// Test is never optimistic (always coerced to boolean).
tagNeverOptimisticLoopTest(forNode);
}
return true;
}
@Override
public boolean enterFunctionNode(final FunctionNode functionNode) {
if (!neverOptimistic.isEmpty() && env.isOnDemandCompilation()) {
// This is a nested function, and we're doing on-demand compilation. In these compilations, we never descend
// into nested functions.
return false;
}
neverOptimistic.push(new BitSet());
splitDepth.push(0);
return true;
}
@Override
public boolean enterIfNode(final IfNode ifNode) {
// Test is never optimistic (always coerced to boolean).
tagNeverOptimistic(ifNode.getTest());
return true;
}
@Override
public boolean enterIndexNode(final IndexNode indexNode) {
tagNeverOptimistic(indexNode.getBase());
return true;
}
@Override
public boolean enterTernaryNode(final TernaryNode ternaryNode) {
// Test is never optimistic (always coerced to boolean).
tagNeverOptimistic(ternaryNode.getTest());
return true;
}
@Override
public boolean enterUnaryNode(final UnaryNode unaryNode) {
if(unaryNode.isTokenType(TokenType.NOT) || unaryNode.isTokenType(TokenType.NEW)) {
// Operand of boolean negation is never optimistic (always coerced to boolean).
// Operand of "new" is never optimistic (always coerced to Object).
tagNeverOptimistic(unaryNode.getExpression());
}
return true;
}
@Override
public boolean enterSplitNode(SplitNode splitNode) {
splitDepth.getAndIncrement();
return true;
}
@Override
public Node leaveSplitNode(SplitNode splitNode) {
final int depth = splitDepth.decrementAndGet();
assert depth >= 0;
return splitNode;
}
@Override
public boolean enterVarNode(final VarNode varNode) {
tagNeverOptimistic(varNode.getName());
return true;
}
@Override
public boolean enterWhileNode(final WhileNode whileNode) {
// Test is never optimistic (always coerced to boolean).
tagNeverOptimisticLoopTest(whileNode);
return true;
}
@Override
protected Node leaveDefault(final Node node) {
if(node instanceof Optimistic) {
return leaveOptimistic((Optimistic)node);
}
return node;
}
@Override
public Node leaveFunctionNode(final FunctionNode functionNode) {
neverOptimistic.pop();
final int lastSplitDepth = splitDepth.pop();
assert lastSplitDepth == 0;
return functionNode.setState(lc, CompilationState.OPTIMISTIC_TYPES_ASSIGNED);
}
@Override
public Node leaveIdentNode(final IdentNode identNode) {
if(inSplitNode()) {
return identNode;
}
final Symbol symbol = identNode.getSymbol();
if(symbol == null) {
assert identNode.isPropertyName();
return identNode;
} else if(symbol.isBytecodeLocal()) {
// Identifiers accessing bytecode local variables will never be optimistic, as type calculation phase over
// them will always assign them statically provable types. Note that access to function parameters can still
// be optimistic if the parameter needs to be in scope as it's used by a nested function.
return identNode;
} else if(symbol.isParam() && lc.getCurrentFunction().isVarArg()) {
// Parameters in vararg methods are not optimistic; we always access them using Object getters.
return identNode.setType(identNode.getMostPessimisticType());
} else {
assert symbol.isScope();
return leaveOptimistic(identNode);
}
}
private Expression leaveOptimistic(final Optimistic opt) {
final int pp = opt.getProgramPoint();
if(isValid(pp) && !inSplitNode() && !neverOptimistic.peek().get(pp)) {
return (Expression)opt.setType(env.getOptimisticType(opt));
}
return (Expression)opt;
}
private void tagNeverOptimistic(final Expression expr) {
if(expr instanceof Optimistic) {
final int pp = ((Optimistic)expr).getProgramPoint();
if(isValid(pp)) {
neverOptimistic.peek().set(pp);
}
}
}
private void tagNeverOptimisticLoopTest(final LoopNode loopNode) {
final JoinPredecessorExpression test = loopNode.getTest();
if(test != null) {
tagNeverOptimistic(test.getExpression());
}
}
private boolean inSplitNode() {
return splitDepth.peek() > 0;
}
}

View file

@ -27,11 +27,9 @@ package jdk.nashorn.internal.codegen;
import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.FIRST_PROGRAM_POINT;
import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.MAX_PROGRAM_POINT_VALUE;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.HashSet;
import java.util.Set;
import jdk.nashorn.internal.IntDeque;
import jdk.nashorn.internal.ir.AccessNode;
import jdk.nashorn.internal.ir.BinaryNode;
import jdk.nashorn.internal.ir.CallNode;
@ -51,7 +49,7 @@ import jdk.nashorn.internal.ir.visitor.NodeVisitor;
*/
class ProgramPoints extends NodeVisitor<LexicalContext> {
private final Deque<int[]> nextProgramPoint = new ArrayDeque<>();
private final IntDeque nextProgramPoint = new IntDeque();
private final Set<Node> noProgramPoint = new HashSet<>();
ProgramPoints() {
@ -59,7 +57,7 @@ class ProgramPoints extends NodeVisitor<LexicalContext> {
}
private int next() {
final int next = nextProgramPoint.peek()[0]++;
final int next = nextProgramPoint.getAndIncrement();
if(next > MAX_PROGRAM_POINT_VALUE) {
throw new AssertionError("Function has more than " + MAX_PROGRAM_POINT_VALUE + " program points");
}
@ -68,7 +66,7 @@ class ProgramPoints extends NodeVisitor<LexicalContext> {
@Override
public boolean enterFunctionNode(final FunctionNode functionNode) {
nextProgramPoint.push(new int[] { FIRST_PROGRAM_POINT });
nextProgramPoint.push(FIRST_PROGRAM_POINT);
return true;
}
@ -78,11 +76,11 @@ class ProgramPoints extends NodeVisitor<LexicalContext> {
return functionNode;
}
private Optimistic setProgramPoint(final Optimistic optimistic, final int programPoint) {
private Expression setProgramPoint(final Optimistic optimistic) {
if (noProgramPoint.contains(optimistic)) {
return optimistic;
return (Expression)optimistic;
}
return (Optimistic)(Expression)optimistic.setProgramPoint(programPoint);
return (Expression)(optimistic.canBeOptimistic() ? optimistic.setProgramPoint(next()) : optimistic);
}
@Override
@ -101,31 +99,34 @@ class ProgramPoints extends NodeVisitor<LexicalContext> {
@Override
public Node leaveIdentNode(final IdentNode identNode) {
return (Node)setProgramPoint(identNode, next());
if(identNode.isPropertyName()) {
return identNode;
}
return setProgramPoint(identNode);
}
@Override
public Node leaveCallNode(final CallNode callNode) {
return (Node)setProgramPoint(callNode, next());
return setProgramPoint(callNode);
}
@Override
public Node leaveAccessNode(final AccessNode accessNode) {
return (Node)setProgramPoint(accessNode, next());
return setProgramPoint(accessNode);
}
@Override
public Node leaveIndexNode(final IndexNode indexNode) {
return (Node)setProgramPoint(indexNode, next());
return setProgramPoint(indexNode);
}
@Override
public Node leaveBinaryNode(final BinaryNode binaryNode) {
return (Node)setProgramPoint(binaryNode, next());
return setProgramPoint(binaryNode);
}
@Override
public Node leaveUnaryNode(final UnaryNode unaryNode) {
return (Node)setProgramPoint(unaryNode, next());
return setProgramPoint(unaryNode);
}
}

View file

@ -1,491 +0,0 @@
/*
* 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.
*/
package jdk.nashorn.internal.codegen;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import jdk.nashorn.internal.codegen.types.Range;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.Assignment;
import jdk.nashorn.internal.ir.BinaryNode;
import jdk.nashorn.internal.ir.Expression;
import jdk.nashorn.internal.ir.ForNode;
import jdk.nashorn.internal.ir.IdentNode;
import jdk.nashorn.internal.ir.LexicalContext;
import jdk.nashorn.internal.ir.LiteralNode;
import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode;
import jdk.nashorn.internal.ir.LoopNode;
import jdk.nashorn.internal.ir.Node;
import jdk.nashorn.internal.ir.RuntimeNode;
import jdk.nashorn.internal.ir.Symbol;
import jdk.nashorn.internal.ir.UnaryNode;
import jdk.nashorn.internal.ir.VarNode;
import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.parser.TokenType;
import jdk.nashorn.internal.runtime.Context;
import jdk.nashorn.internal.runtime.logging.DebugLogger;
import jdk.nashorn.internal.runtime.logging.Loggable;
import jdk.nashorn.internal.runtime.logging.Logger;
/**
* Range analysis and narrowing of type where it can be proven
* that there is no spillover, e.g.
*
* function func(c) {
* var v = c & 0xfff;
* var w = c & 0xeee;
* var x = v * w;
* return x;
* }
*
* Proves that the multiplication never exceeds 24 bits and can thus be an int
*/
@Logger(name="ranges")
final class RangeAnalyzer extends NodeOperatorVisitor<LexicalContext> implements Loggable {
private final DebugLogger log;
private final Range.Functionality func;
private final Map<LoopNode, Symbol> loopCounters = new HashMap<>();
RangeAnalyzer(final CompilationEnvironment env) {
super(new LexicalContext());
this.log = initLogger(env.getContext());
this.func = new Range.Functionality(log);
}
@Override
public DebugLogger getLogger() {
return log;
}
@Override
public DebugLogger initLogger(final Context context) {
return context.getLogger(this.getClass());
}
@Override
public boolean enterForNode(final ForNode forNode) {
//conservatively attempt to identify the loop counter. Null means that it wasn't
//properly identified and that no optimizations can be made with it - its range is
//simply unknown in that case, if it is assigned in the loop
final Symbol counter = findLoopCounter(forNode);
log.fine("Entering forNode " + forNode + " counter = " + counter);
if (counter != null && !assignedInLoop(forNode, counter)) {
loopCounters.put(forNode, counter);
}
return true;
}
//destination visited
private Symbol setRange(final Expression dest, final Range range) {
if (range.isUnknown()) {
return null;
}
final Symbol symbol = dest.getSymbol();
assert symbol != null : dest + " " + dest.getClass() + " has no symbol";
assert symbol.getRange() != null : symbol + " has no range";
final Range symRange = func.join(symbol.getRange(), range);
//anything assigned in the loop, not being the safe loop counter(s) invalidates its entire range
if (lc.inLoop() && !isLoopCounter(lc.getCurrentLoop(), symbol)) {
symbol.setRange(Range.createGenericRange());
return symbol;
}
if (!symRange.equals(symbol.getRange())) {
log.fine("Modify range for " + dest + " " + symbol + " from " + symbol.getRange() + " to " + symRange + " (in node = " + dest + ")" );
symbol.setRange(symRange);
}
return null;
}
@Override
public Node leaveADD(final BinaryNode node) {
setRange(node, func.add(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange()));
return node;
}
@Override
public Node leaveSUB(final BinaryNode node) {
setRange(node, func.sub(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange()));
return node;
}
@Override
public Node leaveMUL(final BinaryNode node) {
setRange(node, func.mul(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange()));
return node;
}
@Override
public Node leaveDIV(final BinaryNode node) {
setRange(node, func.div(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange()));
return node;
}
@Override
public Node leaveMOD(final BinaryNode node) {
setRange(node, func.mod(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange()));
return node;
}
@Override
public Node leaveBIT_AND(final BinaryNode node) {
setRange(node, func.and(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange()));
return node;
}
@Override
public Node leaveBIT_OR(final BinaryNode node) {
setRange(node, func.or(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange()));
return node;
}
@Override
public Node leaveBIT_XOR(final BinaryNode node) {
setRange(node, func.xor(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange()));
return node;
}
@Override
public Node leaveSAR(final BinaryNode node) {
setRange(node, func.sar(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange()));
return node;
}
@Override
public Node leaveSHL(final BinaryNode node) {
setRange(node, func.shl(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange()));
return node;
}
@Override
public Node leaveSHR(final BinaryNode node) {
setRange(node, func.shr(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange()));
return node;
}
private Node leaveCmp(final BinaryNode node) {
setRange(node, Range.createTypeRange(Type.BOOLEAN));
return node;
}
@Override
public Node leaveEQ(final BinaryNode node) {
return leaveCmp(node);
}
@Override
public Node leaveEQ_STRICT(final BinaryNode node) {
return leaveCmp(node);
}
@Override
public Node leaveNE(final BinaryNode node) {
return leaveCmp(node);
}
@Override
public Node leaveNE_STRICT(final BinaryNode node) {
return leaveCmp(node);
}
@Override
public Node leaveLT(final BinaryNode node) {
return leaveCmp(node);
}
@Override
public Node leaveLE(final BinaryNode node) {
return leaveCmp(node);
}
@Override
public Node leaveGT(final BinaryNode node) {
return leaveCmp(node);
}
@Override
public Node leaveGE(final BinaryNode node) {
return leaveCmp(node);
}
@Override
public Node leaveASSIGN(final BinaryNode node) {
Range range = node.rhs().getSymbol().getRange();
if (range.isUnknown()) {
range = Range.createGenericRange();
}
setRange(node.lhs(), range);
setRange(node, range);
return node;
}
private Node leaveSelfModifyingAssign(final BinaryNode node, final Range range) {
setRange(node.lhs(), range);
setRange(node, range);
return node;
}
private Node leaveSelfModifyingAssign(final UnaryNode node, final Range range) {
setRange(node.getExpression(), range);
setRange(node, range);
return node;
}
@Override
public Node leaveASSIGN_ADD(final BinaryNode node) {
return leaveSelfModifyingAssign(node, func.add(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange()));
}
@Override
public Node leaveASSIGN_SUB(final BinaryNode node) {
return leaveSelfModifyingAssign(node, func.sub(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange()));
}
@Override
public Node leaveASSIGN_MUL(final BinaryNode node) {
return leaveSelfModifyingAssign(node, func.mul(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange()));
}
@Override
public Node leaveASSIGN_DIV(final BinaryNode node) {
return leaveSelfModifyingAssign(node, Range.createTypeRange(Type.NUMBER));
}
@Override
public Node leaveASSIGN_MOD(final BinaryNode node) {
return leaveSelfModifyingAssign(node, Range.createTypeRange(Type.NUMBER));
}
@Override
public Node leaveASSIGN_BIT_AND(final BinaryNode node) {
return leaveSelfModifyingAssign(node, func.and(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange()));
}
@Override
public Node leaveASSIGN_BIT_OR(final BinaryNode node) {
return leaveSelfModifyingAssign(node, func.or(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange()));
}
@Override
public Node leaveASSIGN_BIT_XOR(final BinaryNode node) {
return leaveSelfModifyingAssign(node, func.xor(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange()));
}
@Override
public Node leaveASSIGN_SAR(final BinaryNode node) {
return leaveSelfModifyingAssign(node, func.sar(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange()));
}
@Override
public Node leaveASSIGN_SHR(final BinaryNode node) {
return leaveSelfModifyingAssign(node, func.shr(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange()));
}
@Override
public Node leaveASSIGN_SHL(final BinaryNode node) {
return leaveSelfModifyingAssign(node, func.shl(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange()));
}
@Override
public Node leaveDECINC(final UnaryNode node) {
switch (node.tokenType()) {
case DECPREFIX:
case DECPOSTFIX:
return leaveSelfModifyingAssign(node, func.sub(node.getExpression().getSymbol().getRange(), Range.createRange(1)));
case INCPREFIX:
case INCPOSTFIX:
return leaveSelfModifyingAssign(node, func.add(node.getExpression().getSymbol().getRange(), Range.createRange(1)));
default:
throw new UnsupportedOperationException("" + node.tokenType());
}
}
@Override
public Node leaveADD(final UnaryNode node) {
Range range = node.getExpression().getSymbol().getRange();
if (!range.getType().isNumeric()) {
range = Range.createTypeRange(Type.NUMBER);
}
setRange(node, range);
return node;
}
@Override
public Node leaveBIT_NOT(final UnaryNode node) {
setRange(node, Range.createTypeRange(Type.INT));
return node;
}
@Override
public Node leaveNOT(final UnaryNode node) {
setRange(node, Range.createTypeRange(Type.BOOLEAN));
return node;
}
@Override
public Node leaveSUB(final UnaryNode node) {
setRange(node, func.neg(node.getExpression().getSymbol().getRange()));
return node;
}
@Override
public Node leaveVarNode(final VarNode node) {
if (node.isAssignment()) {
Range range = node.getInit().getSymbol().getRange();
range = range.isUnknown() ? Range.createGenericRange() : range;
setRange(node.getName(), range);
}
return node;
}
@SuppressWarnings("rawtypes")
@Override
public boolean enterLiteralNode(final LiteralNode node) {
// ignore array literals
return !(node instanceof ArrayLiteralNode);
}
@Override
public Node leaveLiteralNode(@SuppressWarnings("rawtypes") final LiteralNode node) {
if (node.getType().isInteger()) {
setRange(node, Range.createRange(node.getInt32()));
} else if (node.getType().isNumber()) {
setRange(node, Range.createRange(node.getNumber()));
} else if (node.getType().isLong()) {
setRange(node, Range.createRange(node.getLong()));
} else if (node.getType().isBoolean()) {
setRange(node, Range.createTypeRange(Type.BOOLEAN));
} else {
setRange(node, Range.createGenericRange());
}
return node;
}
@Override
public boolean enterRuntimeNode(final RuntimeNode node) {
// a runtime node that cannot be specialized is no point entering
return node.getRequest().canSpecialize();
}
/**
* Check whether a symbol is unsafely assigned in a loop - i.e. repeteadly assigned and
* not being identified as the loop counter. That means we don't really know anything
* about its range.
* @param loopNode loop node
* @param symbol symbol
* @return true if assigned in loop
*/
// TODO - this currently checks for nodes only - needs to be augmented for while nodes
// assignment analysis is also very conservative
private static boolean assignedInLoop(final LoopNode loopNode, final Symbol symbol) {
final HashSet<Node> skip = new HashSet<>();
final HashSet<Node> assignmentsInLoop = new HashSet<>();
loopNode.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
private boolean assigns(final Node node, final Symbol s) {
return node.isAssignment() && ((Assignment<?>)node).getAssignmentDest().getSymbol() == s;
}
@Override
public boolean enterForNode(final ForNode forNode) {
if (forNode.getInit() != null) {
skip.add(forNode.getInit());
}
if (forNode.getModify() != null) {
skip.add(forNode.getModify());
}
return true;
}
@Override
public Node leaveDefault(final Node node) {
//if this is an assignment to symbol
if (!skip.contains(node) && assigns(node, symbol)) {
assignmentsInLoop.add(node);
}
return node;
}
});
return !assignmentsInLoop.isEmpty();
}
/**
* Check for a loop counter. This is currently quite conservative, in that it only handles
* x <= counter and x < counter.
*
* @param node loop node to check
* @return
*/
private Symbol findLoopCounter(final LoopNode node) {
final Expression test = node.getTest();
if (test != null && test.isComparison()) {
final BinaryNode binaryNode = (BinaryNode)test;
final Expression lhs = binaryNode.lhs();
final Expression rhs = binaryNode.rhs();
//detect ident cmp int_literal
if (lhs instanceof IdentNode && rhs instanceof LiteralNode && ((LiteralNode<?>)rhs).getType().isInteger()) {
final Symbol symbol = lhs.getSymbol();
final int margin = ((LiteralNode<?>)rhs).getInt32();
final TokenType op = test.tokenType();
switch (op) {
case LT:
case LE:
symbol.setRange(func.join(symbol.getRange(), Range.createRange(op == TokenType.LT ? margin - 1 : margin)));
return symbol;
case GT:
case GE:
//setRange(lhs, Range.createRange(op == TokenType.GT ? margin + 1 : margin));
//return symbol;
default:
break;
}
}
}
return null;
}
private boolean isLoopCounter(final LoopNode loopNode, final Symbol symbol) {
//this only works if loop nodes aren't replaced by other ones during this transform, but they are not
return loopCounters.get(loopNode) == symbol;
}
}

View file

@ -32,7 +32,7 @@ import static jdk.nashorn.internal.codegen.ObjectClassGenerator.OBJECT_FIELDS_ON
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.Expression;
import jdk.nashorn.internal.ir.LiteralNode;
import jdk.nashorn.internal.runtime.JSType;
@ -193,7 +193,7 @@ public final class SpillObjectCreator extends ObjectCreator<Expression> {
}
@Override
protected void loadValue(final Expression node) {
codegen.load(node);
protected void loadValue(final Expression expr, final Type type) {
codegen.loadExpressionAsType(expr, type);
}
}

View file

@ -25,13 +25,13 @@
package jdk.nashorn.internal.codegen;
import java.util.ArrayList;
import java.util.List;
import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
import java.util.ArrayList;
import java.util.List;
import jdk.internal.org.objectweb.asm.MethodVisitor;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.BreakableNode;
import jdk.nashorn.internal.ir.LexicalContext;
import jdk.nashorn.internal.ir.SplitNode;
import jdk.nashorn.internal.runtime.Scope;
@ -47,6 +47,13 @@ public class SplitMethodEmitter extends MethodEmitter {
private final SplitNode splitNode;
private final List<Label> externalTargets = new ArrayList<>();
/**
* In addition to external target labels, we need to track the target breakables too as the code generator needs to
* be able to correctly pop the scopes to the target, see {@link CodeGenerator#leaveSplitNode(SplitNode)}. Note that
* this is only used within CodeGenerator, which doesn't mutate the AST, so keeping pointers to other nodes is not
* incorrect.
*/
private final List<BreakableNode> externalTargetNodes = new ArrayList<>();
SplitMethodEmitter(final ClassEmitter classEmitter, final MethodVisitor mv, SplitNode splitNode) {
super(classEmitter, mv);
@ -54,9 +61,9 @@ public class SplitMethodEmitter extends MethodEmitter {
}
@Override
void splitAwareGoto(final LexicalContext lc, final Label label) {
void splitAwareGoto(final LexicalContext lc, final Label label, final BreakableNode targetNode) {
assert splitNode != null;
final int index = findExternalTarget(lc, label);
final int index = findExternalTarget(lc, label, targetNode);
if (index >= 0) {
loadCompilerConstant(SCOPE);
checkcast(Scope.class);
@ -66,18 +73,19 @@ public class SplitMethodEmitter extends MethodEmitter {
_return(functionNode.getReturnType());
return;
}
super.splitAwareGoto(lc, label);
super.splitAwareGoto(lc, label, targetNode);
}
private int findExternalTarget(final LexicalContext lc, final Label label) {
private int findExternalTarget(final LexicalContext lc, final Label label, final BreakableNode targetNode) {
final int index = externalTargets.indexOf(label);
if (index >= 0) {
return index;
}
if (lc.isExternalTarget(splitNode, label)) {
if (lc.isExternalTarget(splitNode, targetNode)) {
externalTargets.add(label);
externalTargetNodes.add(targetNode);
return externalTargets.size() - 1;
}
return -1;
@ -93,8 +101,11 @@ public class SplitMethodEmitter extends MethodEmitter {
return this;
}
@Override
final List<Label> getExternalTargets() {
return externalTargets;
}
final List<BreakableNode> getExternalTargetNodes() {
return externalTargetNodes;
}
}

View file

@ -306,11 +306,6 @@ final class WeighNodes extends NodeOperatorVisitor<LexicalContext> {
return runtimeNodeWeight(unaryNode);
}
@Override
public Node leaveDISCARD(final UnaryNode unaryNode) {
return unaryNodeWeight(unaryNode);
}
@Override
public Node leaveNEW(final UnaryNode unaryNode) {
weight += NEW_WEIGHT;

View file

@ -50,6 +50,9 @@
package jdk.nashorn.internal.codegen.types;
import static jdk.internal.org.objectweb.asm.Opcodes.I2D;
import static jdk.internal.org.objectweb.asm.Opcodes.I2L;
import static jdk.internal.org.objectweb.asm.Opcodes.IADD;
import static jdk.internal.org.objectweb.asm.Opcodes.ICONST_0;
import static jdk.internal.org.objectweb.asm.Opcodes.ICONST_1;
import static jdk.internal.org.objectweb.asm.Opcodes.ILOAD;
@ -57,9 +60,10 @@ import static jdk.internal.org.objectweb.asm.Opcodes.IRETURN;
import static jdk.internal.org.objectweb.asm.Opcodes.ISTORE;
import static jdk.nashorn.internal.codegen.CompilerConstants.staticCallNoLookup;
import static jdk.nashorn.internal.runtime.JSType.UNDEFINED_INT;
import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
import jdk.internal.org.objectweb.asm.MethodVisitor;
import jdk.nashorn.internal.codegen.CompilerConstants;
import jdk.nashorn.internal.runtime.JSType;
/**
* The boolean type class
@ -67,6 +71,7 @@ import jdk.nashorn.internal.runtime.JSType;
public final class BooleanType extends Type {
private static final CompilerConstants.Call VALUE_OF = staticCallNoLookup(Boolean.class, "valueOf", Boolean.class, boolean.class);
private static final CompilerConstants.Call TO_STRING = staticCallNoLookup(Boolean.class, "toString", String.class, boolean.class);
/**
* Constructor
@ -134,19 +139,13 @@ public final class BooleanType extends Type {
}
if (to.isNumber()) {
convert(method, OBJECT);
invokestatic(method, JSType.TO_NUMBER);
method.visitInsn(I2D);
} else if (to.isLong()) {
method.visitInsn(I2L);
} else if (to.isInteger()) {
return to; // do nothing.
} else if (to.isLong()) {
convert(method, OBJECT);
invokestatic(method, JSType.TO_UINT32);
} else if (to.isLong()) {
convert(method, OBJECT);
invokestatic(method, JSType.TO_LONG);
//nop
} else if (to.isString()) {
invokestatic(method, VALUE_OF);
invokestatic(method, JSType.TO_PRIMITIVE_TO_STRING);
invokestatic(method, TO_STRING);
} else if (to.isObject()) {
invokestatic(method, VALUE_OF);
} else {
@ -158,6 +157,12 @@ public final class BooleanType extends Type {
@Override
public Type add(final MethodVisitor method, final int programPoint) {
throw new UnsupportedOperationException("add");
// Adding booleans in JavaScript is perfectly valid, they add as if false=0 and true=1
if(programPoint == INVALID_PROGRAM_POINT) {
method.visitInsn(IADD);
} else {
method.visitInvokeDynamicInsn("iadd", "(II)I", MATHBOOTSTRAP, programPoint);
}
return INT;
}
}

View file

@ -84,7 +84,7 @@ interface BytecodeNumericOps {
* @param programPoint program point id
* @return result type
*/
Type rem(MethodVisitor method);
Type rem(MethodVisitor method, int programPoint);
/**
* Comparison with int return value, e.g. LCMP, DCMP.

View file

@ -28,6 +28,7 @@ package jdk.nashorn.internal.codegen.types;
import static jdk.internal.org.objectweb.asm.Opcodes.BIPUSH;
import static jdk.internal.org.objectweb.asm.Opcodes.I2D;
import static jdk.internal.org.objectweb.asm.Opcodes.I2L;
import static jdk.internal.org.objectweb.asm.Opcodes.IADD;
import static jdk.internal.org.objectweb.asm.Opcodes.IAND;
import static jdk.internal.org.objectweb.asm.Opcodes.ICONST_0;
import static jdk.internal.org.objectweb.asm.Opcodes.ICONST_1;
@ -37,17 +38,21 @@ import static jdk.internal.org.objectweb.asm.Opcodes.ICONST_4;
import static jdk.internal.org.objectweb.asm.Opcodes.ICONST_5;
import static jdk.internal.org.objectweb.asm.Opcodes.ICONST_M1;
import static jdk.internal.org.objectweb.asm.Opcodes.ILOAD;
import static jdk.internal.org.objectweb.asm.Opcodes.IMUL;
import static jdk.internal.org.objectweb.asm.Opcodes.INEG;
import static jdk.internal.org.objectweb.asm.Opcodes.IOR;
import static jdk.internal.org.objectweb.asm.Opcodes.IREM;
import static jdk.internal.org.objectweb.asm.Opcodes.IRETURN;
import static jdk.internal.org.objectweb.asm.Opcodes.ISHL;
import static jdk.internal.org.objectweb.asm.Opcodes.ISHR;
import static jdk.internal.org.objectweb.asm.Opcodes.ISTORE;
import static jdk.internal.org.objectweb.asm.Opcodes.ISUB;
import static jdk.internal.org.objectweb.asm.Opcodes.IUSHR;
import static jdk.internal.org.objectweb.asm.Opcodes.IXOR;
import static jdk.internal.org.objectweb.asm.Opcodes.SIPUSH;
import static jdk.nashorn.internal.codegen.CompilerConstants.staticCallNoLookup;
import static jdk.nashorn.internal.runtime.JSType.UNDEFINED_INT;
import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
import jdk.internal.org.objectweb.asm.MethodVisitor;
import jdk.nashorn.internal.codegen.CompilerConstants;
@ -145,7 +150,11 @@ class IntType extends BitwiseType {
@Override
public Type add(final MethodVisitor method, final int programPoint) {
method.visitInvokeDynamicInsn("iadd", "(II)I", MATHBOOTSTRAP, programPoint);
if(programPoint == INVALID_PROGRAM_POINT) {
method.visitInsn(IADD);
} else {
method.visitInvokeDynamicInsn("iadd", "(II)I", MATHBOOTSTRAP, programPoint);
}
return INT;
}
@ -200,31 +209,49 @@ class IntType extends BitwiseType {
@Override
public Type sub(final MethodVisitor method, final int programPoint) {
method.visitInvokeDynamicInsn("isub", "(II)I", MATHBOOTSTRAP, programPoint);
if(programPoint == INVALID_PROGRAM_POINT) {
method.visitInsn(ISUB);
} else {
method.visitInvokeDynamicInsn("isub", "(II)I", MATHBOOTSTRAP, programPoint);
}
return INT;
}
@Override
public Type mul(final MethodVisitor method, final int programPoint) {
method.visitInvokeDynamicInsn("imul", "(II)I", MATHBOOTSTRAP, programPoint);
if(programPoint == INVALID_PROGRAM_POINT) {
method.visitInsn(IMUL);
} else {
method.visitInvokeDynamicInsn("imul", "(II)I", MATHBOOTSTRAP, programPoint);
}
return INT;
}
@Override
public Type div(final MethodVisitor method, final int programPoint) {
// Never perform non-optimistic integer division in JavaScript.
assert programPoint != INVALID_PROGRAM_POINT;
method.visitInvokeDynamicInsn("idiv", "(II)I", MATHBOOTSTRAP, programPoint);
return INT;
}
@Override
public Type rem(final MethodVisitor method) {
method.visitInsn(IREM);
public Type rem(final MethodVisitor method, final int programPoint) {
// Never perform non-optimistic integer remainder in JavaScript.
assert programPoint != INVALID_PROGRAM_POINT;
method.visitInvokeDynamicInsn("irem", "(II)I", MATHBOOTSTRAP, programPoint);
return INT;
}
@Override
public Type neg(final MethodVisitor method, final int programPoint) {
method.visitInvokeDynamicInsn("ineg", "(I)I", MATHBOOTSTRAP, programPoint);
if(programPoint == INVALID_PROGRAM_POINT) {
method.visitInsn(INEG);
} else {
method.visitInvokeDynamicInsn("ineg", "(I)I", MATHBOOTSTRAP, programPoint);
}
return INT;
}

View file

@ -27,21 +27,24 @@ package jdk.nashorn.internal.codegen.types;
import static jdk.internal.org.objectweb.asm.Opcodes.L2D;
import static jdk.internal.org.objectweb.asm.Opcodes.L2I;
import static jdk.internal.org.objectweb.asm.Opcodes.LADD;
import static jdk.internal.org.objectweb.asm.Opcodes.LAND;
import static jdk.internal.org.objectweb.asm.Opcodes.LCMP;
import static jdk.internal.org.objectweb.asm.Opcodes.LCONST_0;
import static jdk.internal.org.objectweb.asm.Opcodes.LCONST_1;
import static jdk.internal.org.objectweb.asm.Opcodes.LLOAD;
import static jdk.internal.org.objectweb.asm.Opcodes.LMUL;
import static jdk.internal.org.objectweb.asm.Opcodes.LOR;
import static jdk.internal.org.objectweb.asm.Opcodes.LREM;
import static jdk.internal.org.objectweb.asm.Opcodes.LRETURN;
import static jdk.internal.org.objectweb.asm.Opcodes.LSHL;
import static jdk.internal.org.objectweb.asm.Opcodes.LSHR;
import static jdk.internal.org.objectweb.asm.Opcodes.LSTORE;
import static jdk.internal.org.objectweb.asm.Opcodes.LSUB;
import static jdk.internal.org.objectweb.asm.Opcodes.LUSHR;
import static jdk.internal.org.objectweb.asm.Opcodes.LXOR;
import static jdk.nashorn.internal.codegen.CompilerConstants.staticCallNoLookup;
import static jdk.nashorn.internal.runtime.JSType.UNDEFINED_LONG;
import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
import jdk.internal.org.objectweb.asm.MethodVisitor;
import jdk.nashorn.internal.codegen.CompilerConstants;
@ -136,31 +139,49 @@ class LongType extends BitwiseType {
@Override
public Type add(final MethodVisitor method, final int programPoint) {
method.visitInvokeDynamicInsn("ladd", "(JJ)J", MATHBOOTSTRAP, programPoint);
if(programPoint == INVALID_PROGRAM_POINT) {
method.visitInsn(LADD);
} else {
method.visitInvokeDynamicInsn("ladd", "(JJ)J", MATHBOOTSTRAP, programPoint);
}
return LONG;
}
@Override
public Type sub(final MethodVisitor method, final int programPoint) {
method.visitInvokeDynamicInsn("lsub", "(JJ)J", MATHBOOTSTRAP, programPoint);
if(programPoint == INVALID_PROGRAM_POINT) {
method.visitInsn(LSUB);
} else {
method.visitInvokeDynamicInsn("lsub", "(JJ)J", MATHBOOTSTRAP, programPoint);
}
return LONG;
}
@Override
public Type mul(final MethodVisitor method, final int programPoint) {
method.visitInvokeDynamicInsn("lmul", "(JJ)J", MATHBOOTSTRAP, programPoint);
if(programPoint == INVALID_PROGRAM_POINT) {
method.visitInsn(LMUL);
} else {
method.visitInvokeDynamicInsn("lmul", "(JJ)J", MATHBOOTSTRAP, programPoint);
}
return LONG;
}
@Override
public Type div(final MethodVisitor method, final int programPoint) {
// Never perform non-optimistic integer division in JavaScript.
assert programPoint != INVALID_PROGRAM_POINT;
method.visitInvokeDynamicInsn("ldiv", "(JJ)J", MATHBOOTSTRAP, programPoint);
return LONG;
}
@Override
public Type rem(final MethodVisitor method) {
method.visitInsn(LREM);
public Type rem(final MethodVisitor method, final int programPoint) {
// Never perform non-optimistic integer remainder in JavaScript.
assert programPoint != INVALID_PROGRAM_POINT;
method.visitInvokeDynamicInsn("lrem", "(JJ)J", MATHBOOTSTRAP, programPoint);
return LONG;
}

View file

@ -164,7 +164,7 @@ class NumberType extends NumericType {
}
@Override
public Type rem(final MethodVisitor method) {
public Type rem(final MethodVisitor method, final int programPoint) {
method.visitInsn(DREM);
return NUMBER;
}

View file

@ -1,705 +0,0 @@
/*
* 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.
*/
package jdk.nashorn.internal.codegen.types;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import jdk.nashorn.internal.runtime.logging.DebugLogger;
import jdk.nashorn.internal.runtime.JSType;
/**
* Represents the value range of a symbol.
*/
public abstract class Range {
private static final Range GENERIC_RANGE = new Range() {
@Override
public Type getType() {
return Type.OBJECT;
}
};
private static final Range NUMBER_RANGE = new Range() {
@Override
public Type getType() {
return Type.NUMBER;
}
};
private static final Range UNKNOWN_RANGE = new Range() {
@Override
public Type getType() {
return Type.UNKNOWN;
}
@Override
public boolean isUnknown() {
return true;
}
};
private static class IntegerRange extends Range {
private final long min;
private final long max;
private final Type type;
private IntegerRange(final long min, final long max) {
assert min <= max;
this.min = min;
this.max = max;
this.type = typeFromRange(min, max);
}
private static Type typeFromRange(final long from, final long to) {
if (from >= Integer.MIN_VALUE && to <= Integer.MAX_VALUE) {
return Type.INT;
}
return Type.LONG;
}
@Override
public Type getType() {
return type;
}
public long getMin() {
return min;
}
public long getMax() {
return max;
}
@Override
public boolean isIntegerConst() {
return getMin() == getMax();
}
private long getBitMask() {
if (min == max) {
return min;
}
if (min < 0) {
return ~0L;
}
long mask = 1;
while (mask < max) {
mask = (mask << 1) | 1;
}
return mask;
}
@Override
public boolean equals(final Object obj) {
if (obj instanceof IntegerRange) {
final IntegerRange other = (IntegerRange)obj;
return this.type == other.type && this.min == other.min && this.max == other.max;
}
return false;
}
@Override
public int hashCode() {
return Long.hashCode(min) ^ Long.hashCode(max);
}
@Override
public String toString() {
return super.toString() + "[" + min +", " + max + "]";
}
}
/**
* Get narrowest type for this range
* @return type
*/
public abstract Type getType();
/**
* Is this range unknown
* @return true if unknown
*/
public boolean isUnknown() {
return false;
}
/**
* Check if an integer is enough to span this range
* @return true if integer is enough
*/
public boolean isIntegerType() {
return this instanceof IntegerRange;
}
/**
* Check if an integer is enough to span this range
* @return true if integer is enough
*/
public boolean isIntegerConst() {
return false;
}
/**
* Create an unknown range - this is most likely a singleton object
* and it represents "we have no known range information"
* @return the range
*/
public static Range createUnknownRange() {
return UNKNOWN_RANGE;
}
/**
* Create a constant range: [value, value]
* @param value value
* @return the range
*/
public static Range createRange(final int value) {
return createIntegerRange(value, value);
}
/**
* Create a constant range: [value, value]
* @param value value
* @return the range
*/
public static Range createRange(final long value) {
return createIntegerRange(value, value);
}
/**
* Create a constant range: [value, value]
* @param value value
* @return the range
*/
public static Range createRange(final double value) {
if (isRepresentableAsLong(value)) {
return createIntegerRange((long) value, (long) value);
}
return createNumberRange();
}
/**
* Create a constant range: [value, value]
* @param value value
* @return the range
*/
public static Range createRange(final Object value) {
if (value instanceof Integer) {
return createRange((int)value);
} else if (value instanceof Long) {
return createRange((long)value);
} else if (value instanceof Double) {
return createRange((double)value);
}
return createGenericRange();
}
/**
* Create a generic range - object symbol that carries no range
* information
* @return the range
*/
public static Range createGenericRange() {
return GENERIC_RANGE;
}
/**
* Create a number range - number symbol that carries no range
* information
* @return the range
*/
public static Range createNumberRange() {
return NUMBER_RANGE;
}
/**
* Create an integer range [min, max]
* @param min minimum value, inclusive
* @param max maximum value, inclusive
* @return the range
*/
public static IntegerRange createIntegerRange(final long min, final long max) {
return new IntegerRange(min, max);
}
/**
* Create an integer range of maximum type width for the given type
* @param type the type
* @return the range
*/
public static IntegerRange createIntegerRange(final Type type) {
assert type.isNumeric() && !type.isNumber();
final long min;
final long max;
if (type.isInteger()) {
min = Integer.MIN_VALUE;
max = Integer.MAX_VALUE;
} else if (type.isLong()) {
min = Long.MIN_VALUE;
max = Long.MAX_VALUE;
} else {
throw new AssertionError(); //type incompatible with integer range
}
return new IntegerRange(min, max);
}
/**
* Create an range of maximum type width for the given type
* @param type the type
* @return the range
*/
public static Range createTypeRange(final Type type) {
if (type.isNumber()) {
return createNumberRange();
} else if (type.isNumeric()) {
return createIntegerRange(type);
} else {
return createGenericRange();
}
}
// check that add doesn't overflow
private static boolean checkAdd(final long a, final long b) {
final long result = a + b;
return ((a ^ result) & (b ^ result)) >= 0;
}
// check that sub doesn't overflow
private static boolean checkSub(final long a, final long b) {
final long result = a - b;
return ((a ^ result) & (b ^ result)) >= 0;
}
private static boolean checkMul(final long a, final long b) {
// TODO correct overflow check
return a >= Integer.MIN_VALUE && a <= Integer.MAX_VALUE && b >= Integer.MIN_VALUE && b <= Integer.MAX_VALUE;
}
/**
* The range functionality class responsible for merging ranges and drawing
* range conclusions from operations executed
*/
public static class Functionality {
/** logger */
protected final DebugLogger log;
/**
* Constructor
* @param log logger
*/
public Functionality(final DebugLogger log) {
this.log = log;
}
/**
* Join two ranges
* @param a first range
* @param b second range
* @return the joined range
*/
public Range join(final Range a, final Range b) {
if (a.equals(b)) {
return a;
}
Type joinedType = a.getType();
if (a.getType() != b.getType()) {
if (a.isUnknown()) {
return b;
}
if (b.isUnknown()) {
return a;
}
joinedType = Type.widest(a.getType(), b.getType());
}
if (joinedType.isInteger() || joinedType.isLong()) {
return createIntegerRange(
Math.min(((IntegerRange) a).getMin(), ((IntegerRange) b).getMin()),
Math.max(((IntegerRange) a).getMax(), ((IntegerRange) b).getMax()));
}
return createTypeRange(joinedType);
}
/**
* Add operation
* @param a range of first symbol to be added
* @param b range of second symbol to be added
* @return resulting range representing the value range after add
*/
public Range add(final Range a, final Range b) {
if (a.isIntegerType() && b.isIntegerType()) {
final IntegerRange lhs = (IntegerRange)a;
final IntegerRange rhs = (IntegerRange)b;
if (checkAdd(lhs.getMin(), rhs.getMin()) && checkAdd(lhs.getMax(), rhs.getMax())) {
return createIntegerRange(lhs.getMin() + rhs.getMin(), lhs.getMax() + rhs.getMax());
}
}
if (a.getType().isNumeric() && b.getType().isNumeric()) {
return createNumberRange();
}
return createGenericRange();
}
/**
* Sub operation
* @param a range of first symbol to be subtracted
* @param b range of second symbol to be subtracted
* @return resulting range representing the value range after subtraction
*/
public Range sub(final Range a, final Range b) {
if (a.isIntegerType() && b.isIntegerType()) {
final IntegerRange lhs = (IntegerRange)a;
final IntegerRange rhs = (IntegerRange)b;
if (checkSub(lhs.getMin(), rhs.getMax()) && checkSub(lhs.getMax(), rhs.getMin())) {
return createIntegerRange(lhs.getMin() - rhs.getMax(), lhs.getMax() - rhs.getMin());
}
}
if (a.getType().isNumeric() && b.getType().isNumeric()) {
return createNumberRange();
}
return createGenericRange();
}
/**
* Mul operation
* @param a range of first symbol to be multiplied
* @param b range of second symbol to be multiplied
* @return resulting range representing the value range after multiplication
*/
public Range mul(final Range a, final Range b) {
if (a.isIntegerType() && b.isIntegerType()) {
final IntegerRange lhs = (IntegerRange)a;
final IntegerRange rhs = (IntegerRange)b;
//ensure that nothing ever overflows or underflows
if (checkMul(lhs.getMin(), rhs.getMin()) &&
checkMul(lhs.getMax(), rhs.getMax()) &&
checkMul(lhs.getMin(), rhs.getMax()) &&
checkMul(lhs.getMax(), rhs.getMin())) {
final List<Long> results =
Arrays.asList(
lhs.getMin() * rhs.getMin(),
lhs.getMin() * rhs.getMax(),
lhs.getMax() * rhs.getMin(),
lhs.getMax() * rhs.getMax());
return createIntegerRange(Collections.min(results), Collections.max(results));
}
}
if (a.getType().isNumeric() && b.getType().isNumeric()) {
return createNumberRange();
}
return createGenericRange();
}
/**
* Neg operation
* @param a range of value symbol to be negated
* @return resulting range representing the value range after neg
*/
public Range neg(final Range a) {
if (a.isIntegerType()) {
final IntegerRange rhs = (IntegerRange)a;
if (rhs.getMin() != Long.MIN_VALUE && rhs.getMax() != Long.MIN_VALUE) {
return createIntegerRange(-rhs.getMax(), -rhs.getMin());
}
}
if (a.getType().isNumeric()) {
return createNumberRange();
}
return createGenericRange();
}
/**
* Bitwise and operation
* @param a range of first symbol to be and:ed
* @param b range of second symbol to be and:ed
* @return resulting range representing the value range after and
*/
public Range and(final Range a, final Range b) {
if (a.isIntegerType() && b.isIntegerType()) {
final int resultMask = (int) (((IntegerRange)a).getBitMask() & ((IntegerRange)b).getBitMask());
if (resultMask >= 0) {
return createIntegerRange(0, resultMask);
}
} else if (a.isUnknown() && b.isIntegerType()) {
final long operandMask = ((IntegerRange)b).getBitMask();
if (operandMask >= 0) {
return createIntegerRange(0, operandMask);
}
} else if (a.isIntegerType() && b.isUnknown()) {
final long operandMask = ((IntegerRange)a).getBitMask();
if (operandMask >= 0) {
return createIntegerRange(0, operandMask);
}
}
return createTypeRange(Type.INT);
}
/**
* Bitwise or operation
* @param a range of first symbol to be or:ed
* @param b range of second symbol to be or:ed
* @return resulting range representing the value range after or
*/
public Range or(final Range a, final Range b) {
if (a.isIntegerType() && b.isIntegerType()) {
final int resultMask = (int)(((IntegerRange)a).getBitMask() | ((IntegerRange)b).getBitMask());
if (resultMask >= 0) {
return createIntegerRange(0, resultMask);
}
}
return createTypeRange(Type.INT);
}
/**
* Bitwise xor operation
* @param a range of first symbol to be xor:ed
* @param b range of second symbol to be xor:ed
* @return resulting range representing the value range after and
*/
public Range xor(final Range a, final Range b) {
if (a.isIntegerConst() && b.isIntegerConst()) {
return createRange(((IntegerRange)a).getMin() ^ ((IntegerRange)b).getMin());
}
if (a.isIntegerType() && b.isIntegerType()) {
final int resultMask = (int)(((IntegerRange)a).getBitMask() | ((IntegerRange)b).getBitMask());
if (resultMask >= 0) {
return createIntegerRange(0, createIntegerRange(0, resultMask).getBitMask());
}
}
return createTypeRange(Type.INT);
}
/**
* Bitwise shl operation
* @param a range of first symbol to be shl:ed
* @param b range of second symbol to be shl:ed
* @return resulting range representing the value range after shl
*/
public Range shl(final Range a, final Range b) {
if (b.isIntegerType() && b.isIntegerConst()) {
final IntegerRange left = (IntegerRange)(a.isIntegerType() ? a : createTypeRange(Type.INT));
final int shift = (int)((IntegerRange) b).getMin() & 0x1f;
final int min = (int)left.getMin() << shift;
final int max = (int)left.getMax() << shift;
if (min >> shift == left.getMin() && max >> shift == left.getMax()) {
return createIntegerRange(min, max);
}
}
return createTypeRange(Type.INT);
}
/**
* Bitwise shr operation
* @param a range of first symbol to be shr:ed
* @param b range of second symbol to be shr:ed
* @return resulting range representing the value range after shr
*/
public Range shr(final Range a, final Range b) {
if (b.isIntegerType() && b.isIntegerConst()) {
final long shift = ((IntegerRange) b).getMin() & 0x1f;
final IntegerRange left = (IntegerRange)(a.isIntegerType() ? a : createTypeRange(Type.INT));
if (left.getMin() >= 0) {
final long min = left.getMin() >>> shift;
final long max = left.getMax() >>> shift;
return createIntegerRange(min, max);
} else if (shift >= 1) {
return createIntegerRange(0, JSType.MAX_UINT >>> shift);
}
}
return createTypeRange(Type.INT);
}
/**
* Bitwise sar operation
* @param a range of first symbol to be sar:ed
* @param b range of second symbol to be sar:ed
* @return resulting range representing the value range after sar
*/
public Range sar(final Range a, final Range b) {
if (b.isIntegerType() && b.isIntegerConst()) {
final IntegerRange left = (IntegerRange)(a.isIntegerType() ? a : createTypeRange(Type.INT));
final long shift = ((IntegerRange) b).getMin() & 0x1f;
final long min = left.getMin() >> shift;
final long max = left.getMax() >> shift;
return createIntegerRange(min, max);
}
return createTypeRange(Type.INT);
}
/**
* Modulo operation
* @param a range of first symbol to the mod operation
* @param b range of second symbol to be mod operation
* @return resulting range representing the value range after mod
*/
public Range mod(final Range a, final Range b) {
if (a.isIntegerType() && b.isIntegerType()) {
final IntegerRange rhs = (IntegerRange) b;
if (rhs.getMin() > 0 || rhs.getMax() < 0) { // divisor range must not include 0
final long absmax = Math.max(Math.abs(rhs.getMin()), Math.abs(rhs.getMax())) - 1;
return createIntegerRange(rhs.getMin() > 0 ? 0 : -absmax, rhs.getMax() < 0 ? 0 : +absmax);
}
}
return createTypeRange(Type.NUMBER);
}
/**
* Division operation
* @param a range of first symbol to the division
* @param b range of second symbol to be division
* @return resulting range representing the value range after division
*/
public Range div(final Range a, final Range b) {
// TODO
return createTypeRange(Type.NUMBER);
}
}
/**
* Simple trace functionality that will log range creation
*/
public static class TraceFunctionality extends Functionality {
TraceFunctionality(final DebugLogger log) {
super(log);
}
private Range trace(final Range result, final String operation, final Range... operands) {
log.fine("range::" + operation + Arrays.toString(operands) + " => " + result);
return result;
}
@Override
public Range join(final Range a, final Range b) {
final Range result = super.join(a, b);
if (!a.equals(b)) {
trace(result, "join", a, b);
}
return result;
}
@Override
public Range add(final Range a, final Range b) {
return trace(super.add(a, b), "add", a, b);
}
@Override
public Range sub(final Range a, final Range b) {
return trace(super.sub(a, b), "sub", a, b);
}
@Override
public Range mul(final Range a, final Range b) {
return trace(super.mul(a, b), "mul", a, b);
}
@Override
public Range neg(final Range a) {
return trace(super.neg(a), "neg", a);
}
@Override
public Range and(final Range a, final Range b) {
return trace(super.and(a, b), "and", a, b);
}
@Override
public Range or(final Range a, final Range b) {
return trace(super.or(a, b), "or", a, b);
}
@Override
public Range xor(final Range a, final Range b) {
return trace(super.xor(a, b), "xor", a, b);
}
@Override
public Range shl(final Range a, final Range b) {
return trace(super.shl(a, b), "shl", a, b);
}
@Override
public Range shr(final Range a, final Range b) {
return trace(super.shr(a, b), "shr", a, b);
}
@Override
public Range sar(final Range a, final Range b) {
return trace(super.sar(a, b), "sar", a, b);
}
@Override
public Range mod(final Range a, final Range b) {
return trace(super.mod(a, b), "mod", a, b);
}
@Override
public Range div(final Range a, final Range b) {
return trace(super.div(a, b), "div", a, b);
}
}
@Override
public String toString() {
return String.valueOf(getType());
}
@SuppressWarnings("unused")
private static boolean isRepresentableAsInt(final double number) {
return (int)number == number && !isNegativeZero(number);
}
private static boolean isRepresentableAsLong(final double number) {
return (long)number == number && !isNegativeZero(number);
}
private static boolean isNegativeZero(final double number) {
return Double.doubleToLongBits(number) == Double.doubleToLongBits(-0.0);
}
}

View file

@ -54,7 +54,6 @@ import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import jdk.internal.org.objectweb.asm.Handle;
import jdk.internal.org.objectweb.asm.MethodVisitor;
import jdk.nashorn.internal.codegen.CompilerConstants.Call;
@ -497,6 +496,37 @@ public abstract class Type implements Comparable<Type>, BytecodeOps {
return type0.weight() > type1.weight() ? type0 : type1;
}
/**
* When doing widening for return types of a function or a ternary operator, it is not valid to widen a boolean to
* anything other than object. Note that this wouldn't be necessary if {@code Type.widest} did not allow
* boolean-to-number widening. Eventually, we should address it there, but it affects too many other parts of the
* system and is sometimes legitimate (e.g. whenever a boolean value would undergo ToNumber conversion anyway).
* @param t1 type 1
* @param t2 type 2
* @return wider of t1 and t2, except if one is boolean and the other is neither boolean nor unknown, in which case
* {@code Type.OBJECT} is returned.
*/
public static Type widestReturnType(final Type t1, final Type t2) {
if (t1.isUnknown()) {
return t2;
} else if (t2.isUnknown()) {
return t1;
} else if(t1.isBoolean() != t2.isBoolean() || t1.isNumeric() != t2.isNumeric()) {
return Type.OBJECT;
}
return Type.widest(t1, t2);
}
/**
* Returns a generic version of the type. Basically, if the type {@link #isObject()}, returns {@link #OBJECT},
* otherwise returns the type unchanged.
* @param type the type to generify
* @return the generified type
*/
public static Type generic(final Type type) {
return type.isObject() ? Type.OBJECT : type;
}
/**
* Returns the narrowest or least common of two types
*
@ -752,17 +782,17 @@ public abstract class Type implements Comparable<Type>, BytecodeOps {
/**
* This is an integer type, i.e INT, INT32.
*/
public static final Type INT = putInCache(new IntType());
public static final BitwiseType INT = putInCache(new IntType());
/**
* This is the number singleton, used for all number types
*/
public static final Type NUMBER = putInCache(new NumberType());
public static final NumericType NUMBER = putInCache(new NumberType());
/**
* This is the long singleton, used for all long types
*/
public static final Type LONG = putInCache(new LongType());
public static final BitwiseType LONG = putInCache(new LongType());
/**
* A string singleton

View file

@ -34,8 +34,8 @@ import jdk.nashorn.internal.ir.visitor.NodeVisitor;
*/
@Immutable
public final class AccessNode extends BaseNode {
/** Property ident. */
private final IdentNode property;
/** Property name. */
private final String property;
/**
* Constructor
@ -45,13 +45,13 @@ public final class AccessNode extends BaseNode {
* @param base base node
* @param property property
*/
public AccessNode(final long token, final int finish, final Expression base, final IdentNode property) {
public AccessNode(final long token, final int finish, final Expression base, final String property) {
super(token, finish, base, false);
this.property = property.setIsPropertyName();
this.property = property;
}
private AccessNode(final AccessNode accessNode, final Expression base, final IdentNode property, final boolean isFunction, final Type optimisticType, final boolean isOptimistic, final int id) {
super(accessNode, base, isFunction, optimisticType, isOptimistic, id);
private AccessNode(final AccessNode accessNode, final Expression base, final String property, final boolean isFunction, final Type type, final int id) {
super(accessNode, base, isFunction, type, id);
this.property = property;
}
@ -63,8 +63,7 @@ public final class AccessNode extends BaseNode {
public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
if (visitor.enterAccessNode(this)) {
return visitor.leaveAccessNode(
setBase((Expression)base.accept(visitor)).
setProperty((IdentNode)property.accept(visitor)));
setBase((Expression)base.accept(visitor)));
}
return this;
}
@ -73,7 +72,7 @@ public final class AccessNode extends BaseNode {
public void toString(final StringBuilder sb) {
final boolean needsParen = tokenType().needsParens(getBase().tokenType(), true);
Node.optimisticType(this, sb);
optimisticTypeToString(sb);
if (needsParen) {
sb.append('(');
@ -86,15 +85,15 @@ public final class AccessNode extends BaseNode {
}
sb.append('.');
sb.append(property.getName());
sb.append(property);
}
/**
* Get the property
* Get the property name
*
* @return the property IdentNode
* @return the property name
*/
public IdentNode getProperty() {
public String getProperty() {
return property;
}
@ -102,22 +101,15 @@ public final class AccessNode extends BaseNode {
if (this.base == base) {
return this;
}
return new AccessNode(this, base, property, isFunction(), optimisticType, isOptimistic, programPoint);
}
private AccessNode setProperty(final IdentNode property) {
if (this.property == property) {
return this;
}
return new AccessNode(this, base, property, isFunction(), optimisticType, isOptimistic, programPoint);
return new AccessNode(this, base, property, isFunction(), type, programPoint);
}
@Override
public AccessNode setType(final TemporarySymbols ts, final Type optimisticType) {
if (this.optimisticType == optimisticType) {
public AccessNode setType(final Type type) {
if (this.type == type) {
return this;
}
return new AccessNode(this, base, property, isFunction(), optimisticType, isOptimistic, programPoint);
return new AccessNode(this, base, property, isFunction(), type, programPoint);
}
@Override
@ -125,7 +117,7 @@ public final class AccessNode extends BaseNode {
if (this.programPoint == programPoint) {
return this;
}
return new AccessNode(this, base, property, isFunction(), optimisticType, isOptimistic, programPoint);
return new AccessNode(this, base, property, isFunction(), type, programPoint);
}
@Override
@ -133,15 +125,6 @@ public final class AccessNode extends BaseNode {
if (isFunction()) {
return this;
}
return new AccessNode(this, base, property, true, optimisticType, isOptimistic, programPoint);
return new AccessNode(this, base, property, true, type, programPoint);
}
@Override
public AccessNode setIsOptimistic(final boolean isOptimistic) {
if (this.isOptimistic == isOptimistic) {
return this;
}
return new AccessNode(this, base, property, isFunction(), optimisticType, isOptimistic, programPoint);
}
}

View file

@ -27,6 +27,7 @@ package jdk.nashorn.internal.ir;
import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
import java.util.function.Function;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.annotations.Immutable;
@ -44,11 +45,8 @@ public abstract class BaseNode extends Expression implements FunctionCall, Optim
private final boolean isFunction;
/** Callsite type for this node, if overriden optimistically or conservatively depending on coercion */
protected final Type optimisticType;
/** Does this node have a callsite type, and it is optimistic rather than inferred from coercion semantics */
protected final boolean isOptimistic;
/** Callsite type for this node, if overridden optimistically or conservatively depending on coercion */
protected final Type type;
/** Program point id */
protected final int programPoint;
@ -65,9 +63,8 @@ public abstract class BaseNode extends Expression implements FunctionCall, Optim
super(token, base.getStart(), finish);
this.base = base;
this.isFunction = isFunction;
this.optimisticType = null;
this.type = null;
this.programPoint = INVALID_PROGRAM_POINT;
this.isOptimistic = false;
}
/**
@ -76,16 +73,14 @@ public abstract class BaseNode extends Expression implements FunctionCall, Optim
* @param base base
* @param isFunction is this a function
* @param callSiteType the callsite type for this base node, either optimistic or conservative
* @param isOptimistic is the callsite type optimistic rather than based on statically known coercion semantics
* @param programPoint program point id
*/
protected BaseNode(final BaseNode baseNode, final Expression base, final boolean isFunction, final Type callSiteType, final boolean isOptimistic, final int programPoint) {
protected BaseNode(final BaseNode baseNode, final Expression base, final boolean isFunction, final Type callSiteType, final int programPoint) {
super(baseNode);
this.base = base;
this.isFunction = isFunction;
this.optimisticType = callSiteType;
this.type = callSiteType;
this.programPoint = programPoint;
this.isOptimistic = isOptimistic;
}
/**
@ -102,8 +97,8 @@ public abstract class BaseNode extends Expression implements FunctionCall, Optim
}
@Override
public final Type getType() {
return optimisticType == null ? super.getType() : optimisticType;
public Type getType(Function<Symbol, Type> localVariableTypes) {
return type == null ? getMostPessimisticType() : type;
}
@Override
@ -126,12 +121,6 @@ public abstract class BaseNode extends Expression implements FunctionCall, Optim
return true;
}
@Override
public boolean isOptimistic() {
return isOptimistic;
}
/**
* Mark this node as being the callee operand of a {@link CallNode}.
* @return a base node identical to this one in all aspects except with its function flag set.

View file

@ -29,7 +29,9 @@ import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.HashSet;
import java.util.Set;
import java.util.function.Function;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.annotations.Ignore;
import jdk.nashorn.internal.ir.annotations.Immutable;
@ -41,6 +43,10 @@ import jdk.nashorn.internal.parser.TokenType;
*/
@Immutable
public final class BinaryNode extends Expression implements Assignment<Expression>, Optimistic {
// Placeholder for "undecided optimistic ADD type". Unfortunately, we can't decide the type of ADD during optimistic
// type calculation as it can have local variables as its operands that will decide its ultimate type.
private static final Type OPTIMISTIC_UNDECIDED_TYPE = Type.typeFor(new Object(){}.getClass());
/** Left hand side argument. */
private final Expression lhs;
@ -48,14 +54,14 @@ public final class BinaryNode extends Expression implements Assignment<Expressio
private final int programPoint;
private final boolean isOptimistic;
private final Type type;
private Type cachedType;
private Object cachedTypeFunction;
@Ignore
private static final List<TokenType> CAN_OVERFLOW =
Collections.unmodifiableList(
Arrays.asList(new TokenType[] {
private static final Set<TokenType> CAN_OVERFLOW =
Collections.unmodifiableSet(new HashSet<>(Arrays.asList(new TokenType[] {
TokenType.ADD,
TokenType.DIV,
TokenType.MOD,
@ -66,7 +72,7 @@ public final class BinaryNode extends Expression implements Assignment<Expressio
TokenType.ASSIGN_MOD,
TokenType.ASSIGN_MUL,
TokenType.ASSIGN_SUB
}));
})));
/**
* Constructor
@ -77,23 +83,25 @@ public final class BinaryNode extends Expression implements Assignment<Expressio
*/
public BinaryNode(final long token, final Expression lhs, final Expression rhs) {
super(token, lhs.getStart(), rhs.getFinish());
assert !(isTokenType(TokenType.AND) || isTokenType(TokenType.OR)) || lhs instanceof JoinPredecessorExpression;
this.lhs = lhs;
this.rhs = rhs;
this.programPoint = INVALID_PROGRAM_POINT;
this.isOptimistic = false;
this.type = null;
}
private BinaryNode(final BinaryNode binaryNode, final Expression lhs, final Expression rhs, final Type type, final int programPoint, final boolean isOptimistic) {
private BinaryNode(final BinaryNode binaryNode, final Expression lhs, final Expression rhs, final Type type, final int programPoint) {
super(binaryNode);
this.lhs = lhs;
this.rhs = rhs;
this.programPoint = programPoint;
this.isOptimistic = isOptimistic;
this.type = type;
}
@Override
/**
* Returns true if the node is a comparison operation.
* @return true if the node is a comparison operation.
*/
public boolean isComparison() {
switch (tokenType()) {
case EQ:
@ -110,6 +118,36 @@ public final class BinaryNode extends Expression implements Assignment<Expressio
}
}
/**
* Returns true if the node is a logical operation.
* @return true if the node is a logical operation.
*/
public boolean isLogical() {
return isLogical(tokenType());
}
/**
* Returns true if the token type represents a logical operation.
* @param tokenType the token type
* @return true if the token type represents a logical operation.
*/
public static boolean isLogical(final TokenType tokenType) {
switch (tokenType) {
case AND:
case OR:
return true;
default:
return false;
}
}
private static final Function<Symbol, Type> UNKNOWN_LOCALS = new Function<Symbol, Type>() {
@Override
public Type apply(Symbol t) {
return null;
}
};
/**
* Return the widest possible type for this operation. This is used for compile time
* static type inference
@ -118,8 +156,47 @@ public final class BinaryNode extends Expression implements Assignment<Expressio
*/
@Override
public Type getWidestOperationType() {
return getWidestOperationType(UNKNOWN_LOCALS);
}
/**
* Return the widest possible operand type for this operation.
*
* @return Type
*/
public Type getWidestOperandType() {
switch (tokenType()) {
case SHR:
case ASSIGN_SHR:
return Type.INT;
case INSTANCEOF:
return Type.OBJECT;
default:
if (isComparison()) {
return Type.OBJECT;
}
return getWidestOperationType();
}
}
private Type getWidestOperationType(final Function<Symbol, Type> localVariableTypes) {
switch (tokenType()) {
case ADD:
case ASSIGN_ADD: {
final Type lhsType = lhs.getType(localVariableTypes);
final Type rhsType = rhs.getType(localVariableTypes);
if(lhsType == Type.BOOLEAN && rhsType == Type.BOOLEAN) {
return Type.INT;
}
final Type widestOperandType = Type.widest(lhs.getType(localVariableTypes), rhs.getType(localVariableTypes));
if(widestOperandType == Type.INT) {
return Type.LONG;
} else if (widestOperandType.isNumeric()) {
return Type.NUMBER;
}
return Type.OBJECT;
}
case SHR:
case ASSIGN_SHR:
return Type.LONG;
case ASSIGN_SAR:
@ -135,13 +212,36 @@ public final class BinaryNode extends Expression implements Assignment<Expressio
return Type.INT;
case DIV:
case MOD:
case ASSIGN_DIV:
case ASSIGN_MOD: {
// Naively, one might think MOD has the same type as the widest of its operands, this is unfortunately not
// true when denominator is zero, so even type(int % int) == double.
return Type.NUMBER;
}
case MUL:
case SUB:
case ASSIGN_DIV:
case ASSIGN_MOD:
case ASSIGN_MUL:
case ASSIGN_SUB:
case ASSIGN_SUB: {
final Type lhsType = lhs.getType(localVariableTypes);
final Type rhsType = rhs.getType(localVariableTypes);
if(lhsType == Type.BOOLEAN && rhsType == Type.BOOLEAN) {
return Type.INT;
}
final Type widestOperandType = Type.widest(booleanToInt(lhsType), booleanToInt(rhsType));
if(widestOperandType == Type.INT) {
return Type.LONG;
}
return Type.NUMBER;
}
case VOID: {
return Type.UNDEFINED;
}
case ASSIGN: {
return rhs.getType(localVariableTypes);
}
case INSTANCEOF: {
return Type.BOOLEAN;
}
default:
if (isComparison()) {
return Type.BOOLEAN;
@ -150,8 +250,12 @@ public final class BinaryNode extends Expression implements Assignment<Expressio
}
}
private static Type booleanToInt(Type type) {
return type == Type.BOOLEAN ? Type.INT : type;
}
/**
* Check if this node is an assigment
* Check if this node is an assignment
*
* @return true if this node assigns a value
*/
@ -203,7 +307,10 @@ public final class BinaryNode extends Expression implements Assignment<Expressio
@Override
public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
if (visitor.enterBinaryNode(this)) {
return visitor.leaveBinaryNode(setLHS((Expression)lhs.accept(visitor)).setRHS((Expression)rhs.accept(visitor)));
if(tokenType().isLeftAssociative()) {
return visitor.leaveBinaryNode(setLHS((Expression)lhs.accept(visitor)).setRHS((Expression)rhs.accept(visitor)));
}
return visitor.leaveBinaryNode(setRHS((Expression)rhs.accept(visitor)).setLHS((Expression)lhs.accept(visitor)));
}
return this;
@ -245,6 +352,30 @@ public final class BinaryNode extends Expression implements Assignment<Expressio
}
}
@Override
public boolean isAlwaysFalse() {
switch (tokenType()) {
case COMMALEFT:
return lhs.isAlwaysFalse();
case COMMARIGHT:
return rhs.isAlwaysFalse();
default:
return false;
}
}
@Override
public boolean isAlwaysTrue() {
switch (tokenType()) {
case COMMALEFT:
return lhs.isAlwaysTrue();
case COMMARIGHT:
return rhs.isAlwaysTrue();
default:
return false;
}
}
@Override
public void toString(final StringBuilder sb) {
final TokenType tokenType = tokenType();
@ -281,7 +412,7 @@ public final class BinaryNode extends Expression implements Assignment<Expressio
}
if (isOptimistic()) {
sb.append(Node.OPT_IDENTIFIER);
sb.append(Expression.OPT_IDENTIFIER);
}
sb.append(' ');
@ -320,7 +451,7 @@ public final class BinaryNode extends Expression implements Assignment<Expressio
if (this.lhs == lhs) {
return this;
}
return new BinaryNode(this, lhs, rhs, type, programPoint, isOptimistic);
return new BinaryNode(this, lhs, rhs, type, programPoint);
}
/**
@ -332,7 +463,7 @@ public final class BinaryNode extends Expression implements Assignment<Expressio
if (this.rhs == rhs) {
return this;
}
return new BinaryNode(this, lhs, rhs, type, programPoint, isOptimistic);
return new BinaryNode(this, lhs, rhs, type, programPoint);
}
@Override
@ -342,7 +473,7 @@ public final class BinaryNode extends Expression implements Assignment<Expressio
@Override
public boolean canBeOptimistic() {
return getMostOptimisticType() != getMostPessimisticType();
return isTokenType(TokenType.ADD) || (getMostOptimisticType() != getMostPessimisticType());
}
@Override
@ -350,22 +481,16 @@ public final class BinaryNode extends Expression implements Assignment<Expressio
if (this.programPoint == programPoint) {
return this;
}
return new BinaryNode(this, lhs, rhs, type, programPoint, isOptimistic);
}
@Override
public BinaryNode setIsOptimistic(final boolean isOptimistic) {
if (this.isOptimistic == isOptimistic) {
return this;
}
assert isOptimistic;
return new BinaryNode(this, lhs, rhs, type, programPoint, isOptimistic);
return new BinaryNode(this, lhs, rhs, type, programPoint);
}
@Override
public Type getMostOptimisticType() {
if (CAN_OVERFLOW.contains(tokenType())) {
return Type.widest(Type.INT, Type.widest(lhs.getType(), rhs.getType()));
final TokenType tokenType = tokenType();
if(tokenType == TokenType.ADD || tokenType == TokenType.ASSIGN_ADD) {
return OPTIMISTIC_UNDECIDED_TYPE;
} else if (CAN_OVERFLOW.contains(tokenType())) {
return Type.INT;
}
return getMostPessimisticType();
}
@ -375,21 +500,41 @@ public final class BinaryNode extends Expression implements Assignment<Expressio
return getWidestOperationType();
}
@Override
public boolean isOptimistic() {
return isOptimistic;
/**
* Returns true if the node has the optimistic type of the node is not yet decided. Optimistic ADD nodes start out
* as undecided until we can figure out if they're numeric or not.
* @return true if the node has the optimistic type of the node is not yet decided.
*/
public boolean isOptimisticUndecidedType() {
return type == OPTIMISTIC_UNDECIDED_TYPE;
}
@Override
public Type getType() {
return type == null ? super.getType() : type;
public Type getType(final Function<Symbol, Type> localVariableTypes) {
if(localVariableTypes == cachedTypeFunction) {
return cachedType;
}
cachedType = getTypeUncached(localVariableTypes);
cachedTypeFunction = localVariableTypes;
return cachedType;
}
private Type getTypeUncached(final Function<Symbol, Type> localVariableTypes) {
if(type == OPTIMISTIC_UNDECIDED_TYPE) {
return Type.widest(lhs.getType(localVariableTypes), rhs.getType(localVariableTypes));
}
final Type widest = getWidestOperationType(localVariableTypes);
if(type == null) {
return widest;
}
return Type.narrowest(widest, Type.widest(type, Type.widest(lhs.getType(localVariableTypes), rhs.getType(localVariableTypes))));
}
@Override
public BinaryNode setType(TemporarySymbols ts, Type type) {
public BinaryNode setType(Type type) {
if (this.type == type) {
return this;
}
return new BinaryNode(this, lhs, rhs, type, programPoint, isOptimistic);
return new BinaryNode(this, lhs, rhs, type, programPoint);
}
}

View file

@ -25,8 +25,6 @@
package jdk.nashorn.internal.ir;
import static jdk.nashorn.internal.codegen.CompilerConstants.RETURN;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
@ -36,7 +34,6 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import jdk.nashorn.internal.codegen.Label;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
@ -60,16 +57,14 @@ public class Block extends Node implements BreakableNode, Flags<Block> {
/** Does the block/function need a new scope? */
protected final int flags;
/**
* @see JoinPredecessor
*/
private final LocalVariableConversion conversion;
/** Flag indicating that this block needs scope */
public static final int NEEDS_SCOPE = 1 << 0;
/**
* Flag indicating whether this block uses the self symbol for the function. This is used only for blocks that are
* bodies of function nodes who refer to themselves by name. It causes Attr to insert a var [fn_name] = __callee__
* at the start of the body
*/
public static final int USES_SELF_SYMBOL = 1 << 1;
/**
* Is this block tagged as terminal based on its contents
* (usually the last statement)
@ -98,6 +93,7 @@ public class Block extends Node implements BreakableNode, Flags<Block> {
this.breakLabel = new Label("block_break");
final int len = statements.length;
this.flags = len > 0 && statements[len - 1].hasTerminalFlags() ? IS_TERMINAL : 0;
this.conversion = null;
}
/**
@ -111,7 +107,7 @@ public class Block extends Node implements BreakableNode, Flags<Block> {
this(token, finish, statements.toArray(new Statement[statements.size()]));
}
private Block(final Block block, final int finish, final List<Statement> statements, final int flags, final Map<String, Symbol> symbols) {
private Block(final Block block, final int finish, final List<Statement> statements, final int flags, final Map<String, Symbol> symbols, LocalVariableConversion conversion) {
super(block);
this.statements = statements;
this.flags = flags;
@ -119,6 +115,7 @@ public class Block extends Node implements BreakableNode, Flags<Block> {
this.entryLabel = new Label(block.entryLabel);
this.breakLabel = new Label(block.breakLabel);
this.finish = finish;
this.conversion = conversion;
}
/**
@ -131,8 +128,8 @@ public class Block extends Node implements BreakableNode, Flags<Block> {
}
/**
* Clear the symbols in a block
* TODO: make this immutable
* Clear the symbols in the block.
* TODO: make this immutable.
*/
public void clearSymbols() {
symbols.clear();
@ -140,7 +137,7 @@ public class Block extends Node implements BreakableNode, Flags<Block> {
@Override
public Node ensureUniqueLabels(final LexicalContext lc) {
return Node.replaceInLexicalContext(lc, this, new Block(this, finish, statements, flags, symbols));
return Node.replaceInLexicalContext(lc, this, new Block(this, finish, statements, flags, symbols, conversion));
}
/**
@ -159,7 +156,7 @@ public class Block extends Node implements BreakableNode, Flags<Block> {
}
/**
* Get an iterator for all the symbols defined in this block
* Get a copy of the list for all the symbols defined in this block
* @return symbol iterator
*/
public List<Symbol> getSymbols() {
@ -229,19 +226,6 @@ public class Block extends Node implements BreakableNode, Flags<Block> {
return isTerminal ? setFlag(lc, IS_TERMINAL) : clearFlag(lc, IS_TERMINAL);
}
/**
* Set the type of the return symbol in this block if present.
* @param returnType the new type
* @return this block
*/
public Block setReturnType(final Type returnType) {
final Symbol symbol = getExistingSymbol(RETURN.symbolName());
if (symbol != null) {
symbol.setTypeOverride(returnType);
}
return this;
}
@Override
public int getFlags() {
return flags;
@ -265,6 +249,19 @@ public class Block extends Node implements BreakableNode, Flags<Block> {
return breakLabel;
}
@Override
public Block setLocalVariableConversion(final LexicalContext lc, final LocalVariableConversion conversion) {
if(this.conversion == conversion) {
return this;
}
return Node.replaceInLexicalContext(lc, this, new Block(this, finish, statements, flags, symbols, conversion));
}
@Override
public LocalVariableConversion getLocalVariableConversion() {
return conversion;
}
/**
* Get the list of statements in this block
*
@ -274,6 +271,17 @@ public class Block extends Node implements BreakableNode, Flags<Block> {
return Collections.unmodifiableList(statements);
}
/**
* Returns the line number of the first statement in the block.
* @return the line number of the first statement in the block, or -1 if the block has no statements.
*/
public int getFirstStatementLineNumber() {
if(statements == null || statements.isEmpty()) {
return -1;
}
return statements.get(0).getLineNumber();
}
/**
* Reset the statement list for this block
*
@ -289,7 +297,7 @@ public class Block extends Node implements BreakableNode, 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, symbols));
return Node.replaceInLexicalContext(lc, this, new Block(this, Math.max(finish, lastFinish), statements, flags, symbols, conversion));
}
/**
@ -316,7 +324,7 @@ public class Block extends Node implements BreakableNode, Flags<Block> {
if (this.flags == flags) {
return this;
}
return Node.replaceInLexicalContext(lc, this, new Block(this, finish, statements, flags, symbols));
return Node.replaceInLexicalContext(lc, this, new Block(this, finish, statements, flags, symbols, conversion));
}
@Override
@ -344,7 +352,7 @@ public class Block extends Node implements BreakableNode, Flags<Block> {
return this;
}
return Node.replaceInLexicalContext(lc, this, new Block(this, finish, statements, flags | NEEDS_SCOPE, symbols));
return Node.replaceInLexicalContext(lc, this, new Block(this, finish, statements, flags | NEEDS_SCOPE, symbols, conversion));
}
/**

View file

@ -32,9 +32,7 @@ import jdk.nashorn.internal.ir.visitor.NodeVisitor;
* IR representation for {@code break} statements.
*/
@Immutable
public final class BreakNode extends Statement {
private final IdentNode label;
public final class BreakNode extends JumpStatement {
/**
* Constructor
@ -42,22 +40,16 @@ public final class BreakNode extends Statement {
* @param lineNumber line number
* @param token token
* @param finish finish
* @param label label for break or null if none
* @param labelName label name for break or null if none
*/
public BreakNode(final int lineNumber, final long token, final int finish, final IdentNode label) {
super(lineNumber, token, finish);
this.label = label;
public BreakNode(final int lineNumber, final long token, final int finish, final String labelName) {
super(lineNumber, token, finish, labelName);
}
@Override
public boolean hasGoto() {
return true;
private BreakNode(final BreakNode breakNode, final LocalVariableConversion conversion) {
super(breakNode, conversion);
}
/**
* Assist in IR navigation.
* @param visitor IR navigating visitor.
*/
@Override
public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
if (visitor.enterBreakNode(this)) {
@ -67,21 +59,13 @@ public final class BreakNode extends Statement {
return this;
}
/**
* Get the label for this break node
* @return label, or null if none
*/
public IdentNode getLabel() {
return label;
@Override
JumpStatement createNewJumpStatement(final LocalVariableConversion conversion) {
return new BreakNode(this, conversion);
}
@Override
public void toString(final StringBuilder sb) {
sb.append("break");
if (label != null) {
sb.append(' ');
label.toString(sb);
}
String getStatementName() {
return "break";
}
}

View file

@ -32,7 +32,7 @@ import jdk.nashorn.internal.codegen.Label;
* This class represents a node from which control flow can execute
* a {@code break} statement
*/
public interface BreakableNode extends LexicalContextNode {
public interface BreakableNode extends LexicalContextNode, JoinPredecessor {
/**
* Ensure that any labels in this breakable node are unique so
* that new jumps won't go to old parts of the tree. Used for

View file

@ -36,6 +36,8 @@ abstract class BreakableStatement extends LexicalContextStatement implements Bre
/** break label. */
protected final Label breakLabel;
final LocalVariableConversion conversion;
/**
* Constructor
*
@ -47,16 +49,19 @@ abstract class BreakableStatement extends LexicalContextStatement implements Bre
protected BreakableStatement(final int lineNumber, final long token, final int finish, final Label breakLabel) {
super(lineNumber, token, finish);
this.breakLabel = breakLabel;
this.conversion = null;
}
/**
* Copy constructor
*
* @param breakableNode source node
* @param conversion the potentially new local variable conversion
*/
protected BreakableStatement(final BreakableStatement breakableNode) {
protected BreakableStatement(final BreakableStatement breakableNode, final LocalVariableConversion conversion) {
super(breakableNode);
this.breakLabel = new Label(breakableNode.getBreakLabel());
this.conversion = conversion;
}
/**
@ -88,4 +93,19 @@ abstract class BreakableStatement extends LexicalContextStatement implements Bre
public List<Label> getLabels() {
return Collections.singletonList(breakLabel);
}
@Override
public JoinPredecessor setLocalVariableConversion(final LexicalContext lc, LocalVariableConversion conversion) {
if(this.conversion == conversion) {
return this;
}
return setLocalVariableConversionChanged(lc, conversion);
}
@Override
public LocalVariableConversion getLocalVariableConversion() {
return conversion;
}
abstract JoinPredecessor setLocalVariableConversionChanged(LexicalContext lc, LocalVariableConversion conversion);
}

View file

@ -25,15 +25,16 @@
package jdk.nashorn.internal.ir;
import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
import java.util.Collections;
import java.util.List;
import java.util.function.Function;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.annotations.Ignore;
import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
/**
* IR representation for a function call.
*/
@ -47,13 +48,10 @@ public final class CallNode extends LexicalContextExpression implements Optimist
private final List<Expression> args;
/** Is this a "new" operation */
private static final int IS_NEW = 1 << 0;
/** Is the callsite type for this call optimistic rather than based on statically known coercion semantics */
private static final int IS_OPTIMISTIC = 1 << 1;
private static final int IS_NEW = 1 << 0;
/** Can this be a Function.call? */
private static final int IS_APPLY_TO_CALL = 1 << 2;
private static final int IS_APPLY_TO_CALL = 1 << 1;
private final int flags;
@ -153,13 +151,14 @@ public final class CallNode extends LexicalContextExpression implements Optimist
* @param finish finish
* @param function the function to call
* @param args args to the call
* @param isNew true if this is a constructor call with the "new" keyword
*/
public CallNode(final int lineNumber, final long token, final int finish, final Expression function, final List<Expression> args) {
public CallNode(final int lineNumber, final long token, final int finish, final Expression function, final List<Expression> args, final boolean isNew) {
super(token, finish);
this.function = function;
this.args = args;
this.flags = 0;
this.flags = isNew ? IS_NEW : 0;
this.evalArgs = null;
this.lineNumber = lineNumber;
this.programPoint = INVALID_PROGRAM_POINT;
@ -186,12 +185,12 @@ public final class CallNode extends LexicalContextExpression implements Optimist
}
@Override
public Type getType() {
return optimisticType == null ? super.getType() : optimisticType;
public Type getType(Function<Symbol, Type> localVariableTypes) {
return optimisticType == null ? Type.OBJECT : optimisticType;
}
@Override
public Optimistic setType(final TemporarySymbols ts, final Type optimisticType) {
public Optimistic setType(final Type optimisticType) {
if (this.optimisticType == optimisticType) {
return this;
}
@ -227,8 +226,7 @@ public final class CallNode extends LexicalContextExpression implements Optimist
@Override
public void toString(final StringBuilder sb) {
Node.optimisticType(this, sb);
optimisticTypeToString(sb);
final StringBuilder fsb = new StringBuilder();
function.toString(fsb);
if (isApplyToCall()) {
@ -349,14 +347,6 @@ public final class CallNode extends LexicalContextExpression implements Optimist
return (flags & IS_NEW) != 0;
}
/**
* Flag this call as a new operation
* @return same node or new one on state change
*/
public CallNode setIsNew() {
return setFlags(flags | IS_NEW);
}
private CallNode setFlags(final int flags) {
if (this.flags == flags) {
return this;
@ -391,17 +381,4 @@ public final class CallNode extends LexicalContextExpression implements Optimist
public boolean canBeOptimistic() {
return true;
}
@Override
public boolean isOptimistic() {
return (flags & IS_OPTIMISTIC) != 0;
}
@Override
public Optimistic setIsOptimistic(final boolean isOptimistic) {
if (isOptimistic() == isOptimistic) {
return this;
}
return new CallNode(this, function, args, isOptimistic ? (flags | IS_OPTIMISTIC) : (flags & ~IS_OPTIMISTIC), optimisticType, evalArgs, programPoint);
}
}

View file

@ -34,7 +34,7 @@ import jdk.nashorn.internal.ir.visitor.NodeVisitor;
* Case nodes are not BreakableNodes, but the SwitchNode is
*/
@Immutable
public final class CaseNode extends Node {
public final class CaseNode extends Node implements JoinPredecessor {
/** Test expression. */
private final Expression test;
@ -44,6 +44,11 @@ public final class CaseNode extends Node {
/** Case entry label. */
private final Label entry;
/**
* @see JoinPredecessor
*/
private final LocalVariableConversion conversion;
/**
* Constructors
*
@ -58,14 +63,16 @@ public final class CaseNode extends Node {
this.test = test;
this.body = body;
this.entry = new Label("entry");
this.conversion = null;
}
CaseNode(final CaseNode caseNode, final Expression test, final Block body) {
CaseNode(final CaseNode caseNode, final Expression test, final Block body, final LocalVariableConversion conversion) {
super(caseNode);
this.test = test;
this.body = body;
this.entry = new Label(caseNode.entry);
this.conversion = conversion;
}
@Override
@ -133,13 +140,26 @@ public final class CaseNode extends Node {
if (this.test == test) {
return this;
}
return new CaseNode(this, test, body);
return new CaseNode(this, test, body, conversion);
}
@Override
public JoinPredecessor setLocalVariableConversion(final LexicalContext lc, final LocalVariableConversion conversion) {
if(this.conversion == conversion) {
return this;
}
return new CaseNode(this, test, body, conversion);
}
@Override
public LocalVariableConversion getLocalVariableConversion() {
return conversion;
}
private CaseNode setBody(final Block body) {
if (this.body == body) {
return this;
}
return new CaseNode(this, test, body);
return new CaseNode(this, test, body, conversion);
}
}

View file

@ -42,10 +42,7 @@ public final class CatchNode extends Statement {
/** Catch body. */
private final Block body;
private final int flags;
/** Is this block a synthethic rethrow created by finally inlining? */
public static final int IS_SYNTHETIC_RETHROW = 1;
private final boolean isSyntheticRethrow;
/**
* Constructors
@ -56,22 +53,24 @@ public final class CatchNode extends Statement {
* @param exception variable name of exception
* @param exceptionCondition exception condition
* @param body catch body
* @param flags flags
* @param isSyntheticRethrow true if this node is a synthetically generated rethrow node.
*/
public CatchNode(final int lineNumber, final long token, final int finish, final IdentNode exception, final Expression exceptionCondition, final Block body, final int flags) {
public CatchNode(final int lineNumber, final long token, final int finish, final IdentNode exception,
final Expression exceptionCondition, final Block body, final boolean isSyntheticRethrow) {
super(lineNumber, token, finish);
this.exception = exception == null ? null : exception.setIsInitializedHere();
this.exceptionCondition = exceptionCondition;
this.body = body;
this.flags = flags;
this.isSyntheticRethrow = isSyntheticRethrow;
}
private CatchNode(final CatchNode catchNode, final IdentNode exception, final Expression exceptionCondition, final Block body, final int flags) {
private CatchNode(final CatchNode catchNode, final IdentNode exception, final Expression exceptionCondition,
final Block body, final boolean isSyntheticRethrow) {
super(catchNode);
this.exception = exception;
this.exceptionCondition = exceptionCondition;
this.body = body;
this.flags = flags;
this.isSyntheticRethrow = isSyntheticRethrow;
}
/**
@ -132,7 +131,7 @@ public final class CatchNode extends Statement {
if (this.exceptionCondition == exceptionCondition) {
return this;
}
return new CatchNode(this, exception, exceptionCondition, body, flags);
return new CatchNode(this, exception, exceptionCondition, body, isSyntheticRethrow);
}
/**
@ -152,14 +151,14 @@ public final class CatchNode extends Statement {
if (this.exception == exception) {
return this;
}
return new CatchNode(this, exception, exceptionCondition, body, flags);
return new CatchNode(this, exception, exceptionCondition, body, isSyntheticRethrow);
}
private CatchNode setBody(final Block body) {
if (this.body == body) {
return this;
}
return new CatchNode(this, exception, exceptionCondition, body, flags);
return new CatchNode(this, exception, exceptionCondition, body, isSyntheticRethrow);
}
/**
@ -170,7 +169,6 @@ public final class CatchNode extends Statement {
* @return true if a finally synthetic rethrow
*/
public boolean isSyntheticRethrow() {
return (flags & IS_SYNTHETIC_RETHROW) == IS_SYNTHETIC_RETHROW;
return isSyntheticRethrow;
}
}

View file

@ -32,26 +32,21 @@ import jdk.nashorn.internal.ir.visitor.NodeVisitor;
* IR representation for CONTINUE statements.
*/
@Immutable
public class ContinueNode extends Statement {
private IdentNode label;
public class ContinueNode extends JumpStatement {
/**
* Constructor
*
* @param lineNumber line number
* @param token token
* @param finish finish
* @param label label for break or null if none
* @param labelName label name for continue or null if none
*/
public ContinueNode(final int lineNumber, final long token, final int finish, final IdentNode label) {
super(lineNumber, token, finish);
this.label = label;
public ContinueNode(final int lineNumber, final long token, final int finish, final String labelName) {
super(lineNumber, token, finish, labelName);
}
@Override
public boolean hasGoto() {
return true;
private ContinueNode(final ContinueNode continueNode, final LocalVariableConversion conversion) {
super(continueNode, conversion);
}
@Override
@ -63,22 +58,14 @@ public class ContinueNode extends Statement {
return this;
}
/**
* Get the label for this break node
* @return label, or null if none
*/
public IdentNode getLabel() {
return label;
@Override
JumpStatement createNewJumpStatement(final LocalVariableConversion conversion) {
return new ContinueNode(this, conversion);
}
@Override
public void toString(final StringBuilder sb) {
sb.append("continue");
if (label != null) {
sb.append(' ');
label.toString(sb);
}
String getStatementName() {
return "continue";
}
}

View file

@ -25,6 +25,7 @@
package jdk.nashorn.internal.ir;
import java.util.function.Function;
import jdk.nashorn.internal.codegen.types.Type;
/**
@ -33,7 +34,12 @@ import jdk.nashorn.internal.codegen.types.Type;
*
*/
public abstract class Expression extends Node {
private Symbol symbol;
private static final Function<Symbol, Type> UNKNOWN_LOCALS = new Function<Symbol, Type>() {
@Override
public Type apply(Symbol t) {
return null;
}
};
Expression(final long token, final int start, final int finish) {
super(token, start, finish);
@ -45,57 +51,25 @@ public abstract class Expression extends Node {
Expression(final Expression expr) {
super(expr);
this.symbol = expr.symbol;
}
/**
* Return the Symbol the compiler has assigned to this Node. The symbol
* is the place where it's expression value is stored after evaluation
* Returns the type of the expression.
*
* @return the symbol
* @return the type of the expression.
*/
public Symbol getSymbol() {
return symbol;
public final Type getType() {
return getType(UNKNOWN_LOCALS);
}
/**
* Assign a symbol to this node. See {@link Expression#getSymbol()} for explanation
* of what a symbol is
*
* @param lc lexical context
* @param symbol the symbol
* @return new node
* Returns the type of the expression under the specified symbol-to-type mapping. By default delegates to
* {@link #getType()} but expressions whose type depends on their subexpressions' types and expressions whose type
* depends on symbol type ({@link IdentNode}) will have a special implementation.
* @param localVariableTypes a mapping from symbols to their types, used for type calculation.
* @return the type of the expression under the specified symbol-to-type mapping.
*/
public Expression setSymbol(final LexicalContext lc, final Symbol symbol) {
if (this.symbol == symbol) {
return this;
}
final Expression newExpr = (Expression)clone();
newExpr.symbol = symbol;
return newExpr;
}
/**
* Check if the expression has a type. The default behavior is to go into the symbol
* and check the symbol type, but there may be overrides, for example in
* getters that require a different type than the internal representation
*
* @return true if a type exists
*/
public boolean hasType() {
return getSymbol() != null;
}
/**
* Returns the type of the expression. Typically this is the symbol type. No types
* are stored in the expression itself, unless it implements TypeOverride.
*
* @return the type of the node.
*/
public Type getType() {
assert hasType() : this + " has no type";
return symbol.getSymbolType();
}
public abstract Type getType(final Function<Symbol, Type> localVariableTypes);
/**
* Returns {@code true} if this expression depends exclusively on state that is constant
@ -108,4 +82,88 @@ public abstract class Expression extends Node {
public boolean isLocal() {
return false;
}
/**
* Is this a self modifying assignment?
* @return true if self modifying, e.g. a++, or a*= 17
*/
public boolean isSelfModifying() {
return false;
}
/**
* Returns widest operation type of this operation.
*
* @return the widest type for this operation
*/
public Type getWidestOperationType() {
return Type.OBJECT;
}
/**
* Returns true if the type of this expression is narrower than its widest operation type (thus, it is
* optimistically typed).
* @return true if this expression is optimistically typed.
*/
public final boolean isOptimistic() {
return getType().narrowerThan(getWidestOperationType());
}
static final String OPT_IDENTIFIER = "%";
void optimisticTypeToString(final StringBuilder sb) {
optimisticTypeToString(sb, isOptimistic());
}
void optimisticTypeToString(final StringBuilder sb, boolean optimistic) {
sb.append('{');
final Type type = getType();
final String desc = type == Type.UNDEFINED ? "U" : type.getDescriptor();
sb.append(desc.charAt(desc.length() - 1) == ';' ? "O" : desc);
if(isOptimistic() && optimistic) {
sb.append(OPT_IDENTIFIER);
sb.append('_');
sb.append(((Optimistic)this).getProgramPoint());
}
sb.append('}');
}
/**
* Returns true if the runtime value of this expression is always false when converted to boolean as per ECMAScript
* ToBoolean conversion. Used in control flow calculations.
* @return true if this expression's runtime value converted to boolean is always false.
*/
public boolean isAlwaysFalse() {
return false;
}
/**
* Returns true if the runtime value of this expression is always true when converted to boolean as per ECMAScript
* ToBoolean conversion. Used in control flow calculations.
* @return true if this expression's runtime value converted to boolean is always true.
*/
public boolean isAlwaysTrue() {
return false;
}
/**
* Returns true if the expression is not null and {@link #isAlwaysFalse()}.
* @param test a test expression used as a predicate of a branch or a loop.
* @return true if the expression is not null and {@link #isAlwaysFalse()}.
*/
public static boolean isAlwaysFalse(Expression test) {
return test != null && test.isAlwaysFalse();
}
/**
* Returns true if the expression is null or {@link #isAlwaysTrue()}. Null is considered to be always true as a
* for loop with no test is equivalent to a for loop with always-true test.
* @param test a test expression used as a predicate of a branch or a loop.
* @return true if the expression is null or {@link #isAlwaysFalse()}.
*/
public static boolean isAlwaysTrue(Expression test) {
return test == null || test.isAlwaysTrue();
}
}

View file

@ -33,11 +33,12 @@ import jdk.nashorn.internal.ir.visitor.NodeVisitor;
*/
@Immutable
public final class ForNode extends LoopNode {
/** Initialize expression. */
/** Initialize expression for an ordinary for statement, or the LHS expression receiving iterated-over values in a
* for-in statement. */
private final Expression init;
/** Test expression. */
private final Expression modify;
/** Modify expression for an ordinary statement, or the source of the iterator in the for-in statement. */
private final JoinPredecessorExpression modify;
/** Iterator symbol. */
private Symbol iterator;
@ -59,30 +60,30 @@ public final class ForNode extends LoopNode {
* @param lineNumber line number
* @param token token
* @param finish finish
* @param init initialization expression
* @param test test
* @param body body
* @param modify modify
* @param flags flags
*/
public ForNode(final int lineNumber, final long token, final int finish, final Expression init, final Expression test, final Block body, final Expression modify, final int flags) {
super(lineNumber, token, finish, test, body, false);
this.init = init;
this.modify = modify;
public ForNode(final int lineNumber, final long token, final int finish, final Block body, final int flags) {
super(lineNumber, token, finish, body, false);
this.flags = flags;
this.init = null;
this.modify = null;
}
private ForNode(final ForNode forNode, final Expression init, final Expression test, final Block body, final Expression modify, final int flags, final boolean controlFlowEscapes) {
super(forNode, test, body, controlFlowEscapes);
private ForNode(final ForNode forNode, final Expression init, final JoinPredecessorExpression test,
final Block body, final JoinPredecessorExpression modify, final int flags, final boolean controlFlowEscapes, LocalVariableConversion conversion) {
super(forNode, test, body, controlFlowEscapes, conversion);
this.init = init;
this.modify = modify;
this.flags = flags;
this.iterator = forNode.iterator; //TODO is this acceptable? symbols are never cloned, just copied as references
// Even if the for node gets cloned in try/finally, the symbol can be shared as only one branch of the finally
// is executed.
this.iterator = forNode.iterator;
}
@Override
public Node ensureUniqueLabels(LexicalContext lc) {
return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes));
return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion));
}
@Override
@ -90,8 +91,8 @@ public final class ForNode extends LoopNode {
if (visitor.enterForNode(this)) {
return visitor.leaveForNode(
setInit(lc, init == null ? null : (Expression)init.accept(visitor)).
setTest(lc, test == null ? null : (Expression)test.accept(visitor)).
setModify(lc, modify == null ? null : (Expression)modify.accept(visitor)).
setTest(lc, test == null ? null : (JoinPredecessorExpression)test.accept(visitor)).
setModify(lc, modify == null ? null : (JoinPredecessorExpression)modify.accept(visitor)).
setBody(lc, (Block)body.accept(visitor)));
}
@ -100,7 +101,8 @@ public final class ForNode extends LoopNode {
@Override
public void toString(final StringBuilder sb) {
sb.append("for (");
sb.append("for");
LocalVariableConversion.toString(conversion, sb).append(' ');
if (isForIn()) {
init.toString(sb);
@ -154,7 +156,7 @@ public final class ForNode extends LoopNode {
if (this.init == init) {
return this;
}
return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes));
return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion));
}
/**
@ -212,7 +214,7 @@ public final class ForNode extends LoopNode {
* Get the modification expression for this ForNode
* @return the modification expression
*/
public Expression getModify() {
public JoinPredecessorExpression getModify() {
return modify;
}
@ -222,24 +224,19 @@ public final class ForNode extends LoopNode {
* @param modify new modification expression
* @return new for node if changed or existing if not
*/
public ForNode setModify(final LexicalContext lc, final Expression modify) {
public ForNode setModify(final LexicalContext lc, final JoinPredecessorExpression modify) {
if (this.modify == modify) {
return this;
}
return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes));
return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion));
}
@Override
public Expression getTest() {
return test;
}
@Override
public ForNode setTest(final LexicalContext lc, final Expression test) {
public ForNode setTest(final LexicalContext lc, final JoinPredecessorExpression test) {
if (this.test == test) {
return this;
}
return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes));
return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion));
}
@Override
@ -252,7 +249,7 @@ public final class ForNode extends LoopNode {
if (this.body == body) {
return this;
}
return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes));
return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion));
}
@Override
@ -260,14 +257,18 @@ public final class ForNode extends LoopNode {
if (this.controlFlowEscapes == controlFlowEscapes) {
return this;
}
return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes));
return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion));
}
private ForNode setFlags(final LexicalContext lc, final int flags) {
if (this.flags == flags) {
return this;
}
return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes));
return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion));
}
@Override
JoinPredecessor setLocalVariableConversionChanged(final LexicalContext lc, final LocalVariableConversion conversion) {
return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion));
}
}

View file

@ -32,7 +32,7 @@ import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.function.Function;
import jdk.nashorn.internal.codegen.CompileUnit;
import jdk.nashorn.internal.codegen.Compiler;
import jdk.nashorn.internal.codegen.CompilerConstants;
@ -79,14 +79,16 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
CONSTANT_FOLDED,
/** method has been lowered */
LOWERED,
/** method hass been attributed */
ATTR,
/** method has been split */
SPLIT,
/** method has had its types finalized */
FINALIZED,
/** method has had symbols assigned */
SYMBOLS_ASSIGNED,
/** computed scope depths for symbols */
SCOPE_DEPTHS_COMPUTED,
/** method has had types calculated*/
OPTIMISTIC_TYPES_ASSIGNED,
/** method has had types calculated*/
LOCAL_VARIABLE_TYPES_CALCULATED,
/** method has been emitted to bytecode */
EMITTED
}
@ -132,9 +134,9 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
@Ignore
private final EnumSet<CompilationState> compilationState;
/** Properties of this object assigned in this function */
/** Number of properties of "this" object assigned in this function */
@Ignore
private HashSet<String> thisProperties;
private final int thisProperties;
/** Function flags. */
private final int flags;
@ -186,14 +188,11 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
/** Does this function have nested declarations? */
public static final int HAS_FUNCTION_DECLARATIONS = 1 << 10;
/** Can this function be specialized? */
public static final int CAN_SPECIALIZE = 1 << 11;
/** Does this function have optimistic expressions? */
public static final int IS_OPTIMISTIC = 1 << 12;
/** Does this function have optimistic expressions? (If it does, it can undergo deoptimizing recompilation.) */
public static final int IS_DEOPTIMIZABLE = 1 << 11;
/** Are we vararg, but do we just pass the arguments along to apply or call */
public static final int HAS_APPLY_TO_CALL_SPECIALIZATION = 1 << 13;
public static final int HAS_APPLY_TO_CALL_SPECIALIZATION = 1 << 12;
/** Does this function explicitly use the {@link CompilerConstants#RETURN} symbol? Some functions are known to
* always use the return symbol, namely a function that is a program (as it must track its last executed expression
@ -201,12 +200,19 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
* partitions). Other functions normally don't use the return symbol (so we optimize away its slot), except in some
* very special cases, e.g. when containing a return statement in a finally block. These special cases set this
* flag. */
public static final int USES_RETURN_SYMBOL = 1 << 14;
public static final int USES_RETURN_SYMBOL = 1 << 13;
/**
* Is this function the top-level program?
*/
public static final int IS_PROGRAM = 1 << 15;
public static final int IS_PROGRAM = 1 << 14;
/**
* Flag indicating whether this function uses the local variable symbol for itself. Only named function expressions
* can have this flag set if they reference themselves (e.g. "(function f() { return f })". Declared functions will
* use the symbol in their parent scope instead when they reference themselves by name.
*/
public static final int USES_SELF_SYMBOL = 1 << 15;
/** Does this function use the "this" keyword? */
public static final int USES_THIS = 1 << 16;
@ -285,6 +291,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
this.sourceURL = sourceURL;
this.compileUnit = null;
this.body = null;
this.thisProperties = 0;
}
private FunctionNode(
@ -297,7 +304,8 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
final CompileUnit compileUnit,
final EnumSet<CompilationState> compilationState,
final Block body,
final List<IdentNode> parameters) {
final List<IdentNode> parameters,
final int thisProperties) {
super(functionNode);
this.lineNumber = functionNode.lineNumber;
this.flags = flags;
@ -309,6 +317,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
this.compilationState = compilationState;
this.body = body;
this.parameters = parameters;
this.thisProperties = thisProperties;
// the fields below never change - they are final and assigned in constructor
this.source = functionNode.source;
@ -318,7 +327,6 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
this.declaredSymbols = functionNode.declaredSymbols;
this.kind = functionNode.kind;
this.firstToken = functionNode.firstToken;
this.thisProperties = functionNode.thisProperties;
}
@Override
@ -329,6 +337,15 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
return this;
}
/**
* Visits the parameter nodes of this function. Parameters are normally not visited automatically.
* @param visitor the visitor to apply to the nodes.
* @return a list of parameter nodes, potentially modified from original ones by the visitor.
*/
public List<IdentNode> visitParameters(final NodeVisitor<? extends LexicalContext> visitor) {
return Node.accept(visitor, IdentNode.class, parameters);
}
/**
* Get the source for this function
* @return the source
@ -374,7 +391,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
return this;
}
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, newSourceURL, name, returnType, compileUnit, compilationState, body, parameters));
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, newSourceURL, name, returnType, compileUnit, compilationState, body, parameters, thisProperties));
}
/**
@ -385,14 +402,6 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
return lineNumber;
}
/**
* Can this function node be regenerated with more specific type args?
* @return true if specialization is possible
*/
public boolean canSpecialize() {
return (flags & (IS_OPTIMISTIC | CAN_SPECIALIZE)) != 0;
}
/**
* Get the compilation state of this function
* @return the compilation state
@ -440,7 +449,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
}
final EnumSet<CompilationState> newState = EnumSet.copyOf(this.compilationState);
newState.add(state);
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, newState, body, parameters));
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, newState, body, parameters, thisProperties));
}
/**
@ -497,7 +506,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
if (this.flags == flags) {
return this;
}
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, compilationState, body, parameters));
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, compilationState, body, parameters, thisProperties));
}
@Override
@ -519,11 +528,11 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
}
/**
* Returns true if the function is optimistic
* @return true if this function is optimistic
* Returns true if the function contains at least one optimistic operation (and thus can be deoptimized).
* @return true if the function contains at least one optimistic operation (and thus can be deoptimized).
*/
public boolean isOptimistic() {
return getFlag(IS_OPTIMISTIC);
public boolean canBeDeoptimized() {
return getFlag(IS_DEOPTIMIZABLE);
}
/**
@ -636,7 +645,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
if(this.body == body) {
return this;
}
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags | (body.needsScope() ? FunctionNode.HAS_SCOPE_BLOCK : 0), sourceURL, name, returnType, compileUnit, compilationState, body, parameters));
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags | (body.needsScope() ? FunctionNode.HAS_SCOPE_BLOCK : 0), sourceURL, name, returnType, compileUnit, compilationState, body, parameters, thisProperties));
}
/**
@ -708,22 +717,24 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
}
/**
* Register a property assigned to the this object in this function.
* @param key the property name
* Set the number of properties assigned to the this object in this function.
* @param lc the current lexical context.
* @param thisProperties number of properties
* @return a potentially modified function node
*/
public void addThisProperty(final String key) {
if (thisProperties == null) {
thisProperties = new HashSet<>();
public FunctionNode setThisProperties(final LexicalContext lc, final int thisProperties) {
if (this.thisProperties == thisProperties) {
return this;
}
thisProperties.add(key);
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, compilationState, body, parameters, thisProperties));
}
/**
* Get the number of properties assigned to the this object in this function.
* @return number of properties
*/
public int countThisProperties() {
return thisProperties == null ? 0 : thisProperties.size();
public int getThisProperties() {
return thisProperties;
}
/**
@ -761,7 +772,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
if (this.lastToken == lastToken) {
return this;
}
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, compilationState, body, parameters));
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, compilationState, body, parameters, thisProperties));
}
/**
@ -782,7 +793,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
if (this.name.equals(name)) {
return this;
}
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, compilationState, body, parameters));
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, compilationState, body, parameters, thisProperties));
}
/**
@ -812,6 +823,16 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
return Collections.unmodifiableList(parameters);
}
/**
* Returns the identifier for a named parameter at the specified position in this function's parameter list.
* @param index the parameter's position.
* @return the identifier for the requested named parameter.
* @throws IndexOutOfBoundsException if the index is invalid.
*/
public IdentNode getParameter(final int index) {
return parameters.get(index);
}
/**
* Reset the compile unit used to compile this function
* @see Compiler
@ -823,7 +844,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
if (this.parameters == parameters) {
return this;
}
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, compilationState, body, parameters));
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, compilationState, body, parameters, thisProperties));
}
/**
@ -849,11 +870,16 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
* @return true if this function node is a named function expression that uses the symbol for itself.
*/
public boolean usesSelfSymbol() {
return body.getFlag(Block.USES_SELF_SYMBOL);
return getFlag(USES_SELF_SYMBOL);
}
@Override
public Type getType() {
public Type getType(Function<Symbol, Type> localVariableTypes) {
return FUNCTION_TYPE;
}
@Override
public Type getWidestOperationType() {
return FUNCTION_TYPE;
}
@ -878,10 +904,10 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
//we never bother with object types narrower than objects, that will lead to byte code verification errors
//as for instance even if we know we are returning a string from a method, the code generator will always
//treat it as an object, at least for now
if (this.returnType == returnType) {
final Type type = returnType.isObject() ? Type.OBJECT : returnType;
if (this.returnType == type) {
return this;
}
final Type type = Type.widest(this.returnType, returnType.isObject() ? Type.OBJECT : returnType);
return Node.replaceInLexicalContext(
lc,
this,
@ -894,8 +920,9 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
type,
compileUnit,
compilationState,
body.setReturnType(type),
parameters
body,
parameters,
thisProperties
));
}
@ -927,7 +954,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
if (this.compileUnit == compileUnit) {
return this;
}
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, compilationState, body, parameters));
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, compilationState, body, parameters, thisProperties));
}
/**

View file

@ -28,33 +28,40 @@ package jdk.nashorn.internal.ir;
import static jdk.nashorn.internal.codegen.CompilerConstants.__DIR__;
import static jdk.nashorn.internal.codegen.CompilerConstants.__FILE__;
import static jdk.nashorn.internal.codegen.CompilerConstants.__LINE__;
import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
import java.util.function.Function;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
import jdk.nashorn.internal.parser.Token;
import jdk.nashorn.internal.parser.TokenType;
/**
* IR representation for an identifier.
*/
@Immutable
public final class IdentNode extends Expression implements PropertyKey, FunctionCall, Optimistic {
public final class IdentNode extends Expression implements PropertyKey, FunctionCall, Optimistic, JoinPredecessor {
private static final int PROPERTY_NAME = 1 << 0;
private static final int INITIALIZED_HERE = 1 << 1;
private static final int FUNCTION = 1 << 2;
private static final int FUTURESTRICT_NAME = 1 << 3;
private static final int OPTIMISTIC = 1 << 4;
/** Identifier. */
private final String name;
/** Optimistic type */
private final Type optimisticType;
private final Type type;
private final int flags;
private final int programPoint;
private final LocalVariableConversion conversion;
private Symbol symbol;
/**
* Constructor
*
@ -64,18 +71,21 @@ public final class IdentNode extends Expression implements PropertyKey, Function
*/
public IdentNode(final long token, final int finish, final String name) {
super(token, finish);
this.name = name.intern();
this.optimisticType = null;
this.flags = 0;
this.programPoint = INVALID_PROGRAM_POINT;
this.name = name.intern();
this.type = null;
this.flags = 0;
this.programPoint = INVALID_PROGRAM_POINT;
this.conversion = null;
}
private IdentNode(final IdentNode identNode, final String name, final Type callSiteType, final int flags, final int programPoint) {
private IdentNode(final IdentNode identNode, final String name, final Type type, final int flags, final int programPoint, final LocalVariableConversion conversion) {
super(identNode);
this.name = name;
this.optimisticType = callSiteType;
this.flags = flags;
this.programPoint = programPoint;
this.name = name;
this.type = type;
this.flags = flags;
this.programPoint = programPoint;
this.conversion = conversion;
this.symbol = identNode.symbol;
}
/**
@ -85,20 +95,33 @@ public final class IdentNode extends Expression implements PropertyKey, Function
*/
public IdentNode(final IdentNode identNode) {
super(identNode);
this.name = identNode.getName();
this.optimisticType = null;
this.flags = identNode.flags;
this.programPoint = INVALID_PROGRAM_POINT;
this.name = identNode.getName();
this.type = identNode.type;
this.flags = identNode.flags;
this.conversion = identNode.conversion;
this.programPoint = INVALID_PROGRAM_POINT;
this.symbol = identNode.symbol;
}
/**
* Creates an identifier for the symbol. Normally used by code generator for creating temporary storage identifiers
* that must contain both a symbol and a type.
* @param symbol the symbol to create a temporary identifier for.
* @return a temporary identifier for the symbol.
*/
public static IdentNode createInternalIdentifier(final Symbol symbol) {
return (IdentNode)new IdentNode(Token.toDesc(TokenType.IDENT, 0, 0), 0, symbol.getName()).setSymbol(symbol);
}
@Override
public Type getType() {
return optimisticType == null ? super.getType() : optimisticType;
}
@Override
public boolean isAtom() {
return true;
public Type getType(Function<Symbol, Type> localVariableTypes) {
if(type != null) {
return type;
} else if(symbol != null && symbol.isScope()) {
return Type.OBJECT;
}
final Type symbolType = localVariableTypes.apply(symbol);
return symbolType == null ? Type.UNDEFINED : symbolType;
}
/**
@ -117,7 +140,7 @@ public final class IdentNode extends Expression implements PropertyKey, Function
@Override
public void toString(final StringBuilder sb) {
Node.optimisticType(this, sb);
optimisticTypeToString(sb, symbol == null || !symbol.hasSlot());
sb.append(name);
}
@ -139,6 +162,31 @@ public final class IdentNode extends Expression implements PropertyKey, Function
return !getSymbol().isScope();
}
/**
* Return the Symbol the compiler has assigned to this identifier. The symbol is a description of the storage
* location for the identifier.
*
* @return the symbol
*/
public Symbol getSymbol() {
return symbol;
}
/**
* Assign a symbol to this identifier. See {@link IdentNode#getSymbol()} for explanation of what a symbol is.
*
* @param symbol the symbol
* @return new node
*/
public Expression setSymbol(final Symbol symbol) {
if (this.symbol == symbol) {
return this;
}
final IdentNode newIdent = (IdentNode)clone();
newIdent.symbol = symbol;
return newIdent;
}
/**
* Check if this IdentNode is a property name
* @return true if this is a property name
@ -155,7 +203,7 @@ public final class IdentNode extends Expression implements PropertyKey, Function
if (isPropertyName()) {
return this;
}
return new IdentNode(this, name, optimisticType, flags | PROPERTY_NAME, programPoint);
return new IdentNode(this, name, type, flags | PROPERTY_NAME, programPoint, conversion);
}
/**
@ -174,7 +222,7 @@ public final class IdentNode extends Expression implements PropertyKey, Function
if (isFutureStrictName()) {
return this;
}
return new IdentNode(this, name, optimisticType, flags | FUTURESTRICT_NAME, programPoint);
return new IdentNode(this, name, type, flags | FUTURESTRICT_NAME, programPoint, conversion);
}
/**
@ -193,7 +241,7 @@ public final class IdentNode extends Expression implements PropertyKey, Function
if (isInitializedHere()) {
return this;
}
return new IdentNode(this, name, optimisticType, flags | INITIALIZED_HERE, programPoint);
return new IdentNode(this, name, type, flags | INITIALIZED_HERE, programPoint, conversion);
}
/**
@ -212,11 +260,11 @@ public final class IdentNode extends Expression implements PropertyKey, Function
}
@Override
public IdentNode setType(final TemporarySymbols ts, final Type callSiteType) {
if (this.optimisticType == callSiteType) {
public IdentNode setType(final Type type) {
if (this.type == type) {
return this;
}
return new IdentNode(this, name, callSiteType, flags, programPoint);
return new IdentNode(this, name, type, flags, programPoint, conversion);
}
/**
@ -227,7 +275,7 @@ public final class IdentNode extends Expression implements PropertyKey, Function
if (isFunction()) {
return this;
}
return new IdentNode(this, name, optimisticType, flags | FUNCTION, programPoint);
return new IdentNode(this, name, type, flags | FUNCTION, programPoint, conversion);
}
@Override
@ -240,7 +288,7 @@ public final class IdentNode extends Expression implements PropertyKey, Function
if (this.programPoint == programPoint) {
return this;
}
return new IdentNode(this, name, optimisticType, flags, programPoint);
return new IdentNode(this, name, type, flags, programPoint, conversion);
}
@Override
@ -259,8 +307,11 @@ public final class IdentNode extends Expression implements PropertyKey, Function
}
@Override
public boolean isOptimistic() {
return (flags & OPTIMISTIC) == OPTIMISTIC;
public JoinPredecessor setLocalVariableConversion(final LexicalContext lc, final LocalVariableConversion conversion) {
if(this.conversion == conversion) {
return this;
}
return new IdentNode(this, name, type, flags, programPoint, conversion);
}
/**
@ -274,10 +325,7 @@ public final class IdentNode extends Expression implements PropertyKey, Function
}
@Override
public Optimistic setIsOptimistic(final boolean isOptimistic) {
if (isOptimistic() == isOptimistic) {
return this;
}
return new IdentNode(this, name, optimisticType, isOptimistic ? (flags | OPTIMISTIC) : (flags & ~OPTIMISTIC), programPoint);
public LocalVariableConversion getLocalVariableConversion() {
return conversion;
}
}

View file

@ -32,7 +32,7 @@ import jdk.nashorn.internal.ir.visitor.NodeVisitor;
* IR representation for an IF statement.
*/
@Immutable
public final class IfNode extends Statement {
public final class IfNode extends Statement implements JoinPredecessor {
/** Test expression. */
private final Expression test;
@ -42,6 +42,12 @@ public final class IfNode extends Statement {
/** Fail statements. */
private final Block fail;
/**
* Local variable conversions that need to be performed after test if it evaluates to false, and there's no else
* branch.
*/
private final LocalVariableConversion conversion;
/**
* Constructor
*
@ -57,13 +63,15 @@ public final class IfNode extends Statement {
this.test = test;
this.pass = pass;
this.fail = fail;
this.conversion = null;
}
private IfNode(final IfNode ifNode, final Expression test, final Block pass, final Block fail) {
private IfNode(final IfNode ifNode, final Expression test, final Block pass, final Block fail, final LocalVariableConversion conversion) {
super(ifNode);
this.test = test;
this.pass = pass;
this.fail = fail;
this.conversion = conversion;
}
@Override
@ -102,7 +110,7 @@ public final class IfNode extends Statement {
if (this.fail == fail) {
return this;
}
return new IfNode(this, test, pass, fail);
return new IfNode(this, test, pass, fail, conversion);
}
/**
@ -117,7 +125,7 @@ public final class IfNode extends Statement {
if (this.pass == pass) {
return this;
}
return new IfNode(this, test, pass, fail);
return new IfNode(this, test, pass, fail, conversion);
}
/**
@ -137,6 +145,19 @@ public final class IfNode extends Statement {
if (this.test == test) {
return this;
}
return new IfNode(this, test, pass, fail);
return new IfNode(this, test, pass, fail, conversion);
}
@Override
public IfNode setLocalVariableConversion(final LexicalContext lc, final LocalVariableConversion conversion) {
if(this.conversion == conversion) {
return this;
}
return new IfNode(this, test, pass, fail, conversion);
}
@Override
public LocalVariableConversion getLocalVariableConversion() {
return conversion;
}
}

View file

@ -49,8 +49,8 @@ public final class IndexNode extends BaseNode {
this.index = index;
}
private IndexNode(final IndexNode indexNode, final Expression base, final Expression index, final boolean isFunction, final Type optimisticType, final boolean isOptimistic, final int programPoint) {
super(indexNode, base, isFunction, optimisticType, isOptimistic, programPoint);
private IndexNode(final IndexNode indexNode, final Expression base, final Expression index, final boolean isFunction, final Type type, final int programPoint) {
super(indexNode, base, isFunction, type, programPoint);
this.index = index;
}
@ -72,7 +72,7 @@ public final class IndexNode extends BaseNode {
sb.append('(');
}
Node.optimisticType(this, sb);
optimisticTypeToString(sb);
base.toString(sb);
@ -97,7 +97,7 @@ public final class IndexNode extends BaseNode {
if (this.base == base) {
return this;
}
return new IndexNode(this, base, index, isFunction(), optimisticType, isOptimistic, programPoint);
return new IndexNode(this, base, index, isFunction(), type, programPoint);
}
/**
@ -109,15 +109,15 @@ public final class IndexNode extends BaseNode {
if(this.index == index) {
return this;
}
return new IndexNode(this, base, index, isFunction(), optimisticType, isOptimistic, programPoint);
return new IndexNode(this, base, index, isFunction(), type, programPoint);
}
@Override
public IndexNode setType(final TemporarySymbols ts, final Type optimisticType) {
if (this.optimisticType == optimisticType) {
public IndexNode setType(final Type type) {
if (this.type == type) {
return this;
}
return new IndexNode(this, base, index, isFunction(), optimisticType, isOptimistic, programPoint);
return new IndexNode(this, base, index, isFunction(), type, programPoint);
}
@Override
@ -125,7 +125,7 @@ public final class IndexNode extends BaseNode {
if (isFunction()) {
return this;
}
return new IndexNode(this, base, index, true, optimisticType, isOptimistic, programPoint);
return new IndexNode(this, base, index, true, type, programPoint);
}
@Override
@ -133,14 +133,6 @@ public final class IndexNode extends BaseNode {
if (this.programPoint == programPoint) {
return this;
}
return new IndexNode(this, base, index, isFunction(), optimisticType, isOptimistic, programPoint);
}
@Override
public IndexNode setIsOptimistic(boolean isOptimistic) {
if (this.isOptimistic == isOptimistic) {
return this;
}
return new IndexNode(this, base, index, isFunction(), optimisticType, isOptimistic, programPoint);
return new IndexNode(this, base, index, isFunction(), type, programPoint);
}
}

View file

@ -0,0 +1,49 @@
/*
* 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.
*/
package jdk.nashorn.internal.ir;
/**
* Interface implemented by AST nodes that either can occur as predecessors of a control flow join, or contain a control
* flow join themselves. JoinPredecessor only provides a getter and setter for a {@link LocalVariableConversion}; the
* semantics of control flow for a particular node implementing the interface are shared between
* {@code LocalVariableTypesCalculator} that creates the conversions, and {@code CodeGenerator} that uses them.
*/
public interface JoinPredecessor {
/**
* Set the local variable conversions needed to unify their types at a control flow join point.
* @param lc the current lexical context
* @param conversion the conversions.
* @return this node or a different node representing the change.
*/
public JoinPredecessor setLocalVariableConversion(LexicalContext lc, LocalVariableConversion conversion);
/**
* Returns the local variable conversions needed to unify their types at a control flow join point.
* @return the local variable conversions needed to unify their types at a control flow join point. Can be null.
* Can contain {@link LocalVariableConversion#isLive() dead conversions}.
*/
public LocalVariableConversion getLocalVariableConversion();
}

View file

@ -0,0 +1,131 @@
/*
* 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.
*/
package jdk.nashorn.internal.ir;
import java.util.function.Function;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
/**
* A wrapper for an expression that is in a position to be a join predecessor.
*/
public class JoinPredecessorExpression extends Expression implements JoinPredecessor {
private final Expression expression;
private final LocalVariableConversion conversion;
/**
* A no-arg constructor does not wrap any expression on its own, but can be used as a place to contain a local
* variable conversion in a place where an expression can otherwise stand.
*/
public JoinPredecessorExpression() {
this(null);
}
/**
* A constructor for wrapping an expression and making it a join predecessor. Typically used on true and false
* subexpressions of the ternary node as well as on the operands of short-circuiting logical expressions {@code &&}
* and {@code ||}.
* @param expression the expression to wrap
*/
public JoinPredecessorExpression(final Expression expression) {
this(expression, null);
}
private JoinPredecessorExpression(final Expression expression, final LocalVariableConversion conversion) {
super(expression == null ? 0L : expression.getToken(), expression == null ? 0 : expression.getStart(), expression == null ? 0 : expression.getFinish());
this.expression = expression;
this.conversion = conversion;
}
@Override
public JoinPredecessor setLocalVariableConversion(final LexicalContext lc, final LocalVariableConversion conversion) {
if(conversion == this.conversion) {
return this;
}
return new JoinPredecessorExpression(expression, conversion);
}
@Override
public Type getType(Function<Symbol, Type> localVariableTypes) {
return expression.getType(localVariableTypes);
}
@Override
public boolean isAlwaysFalse() {
return expression != null && expression.isAlwaysFalse();
}
@Override
public boolean isAlwaysTrue() {
return expression != null && expression.isAlwaysTrue();
}
/**
* Returns the underlying expression.
* @return the underlying expression.
*/
public Expression getExpression() {
return expression;
}
/**
* Sets the underlying expression.
* @param expression the new underlying expression
* @return this or modified join predecessor expression object.
*/
public JoinPredecessorExpression setExpression(final Expression expression) {
if(expression == this.expression) {
return this;
}
return new JoinPredecessorExpression(expression, conversion);
}
@Override
public LocalVariableConversion getLocalVariableConversion() {
return conversion;
}
@Override
public Node accept(NodeVisitor<? extends LexicalContext> visitor) {
if(visitor.enterJoinPredecessorExpression(this)) {
final Expression expr = getExpression();
return visitor.leaveJoinPredecessorExpression(expr == null ? this : setExpression((Expression)expr.accept(visitor)));
}
return this;
}
@Override
public void toString(StringBuilder sb) {
if(expression != null) {
expression.toString(sb);
}
if(conversion != null) {
conversion.toString(sb);
}
}
}

View file

@ -0,0 +1,99 @@
/*
* 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.
*/
package jdk.nashorn.internal.ir;
/**
* Common base class for jump statements (e.g. {@code break} and {@code continue}).
*/
public abstract class JumpStatement extends Statement implements JoinPredecessor {
private final String labelName;
private final LocalVariableConversion conversion;
/**
* Constructor
*
* @param lineNumber line number
* @param token token
* @param finish finish
* @param labelName label name for break or null if none
*/
protected JumpStatement(final int lineNumber, final long token, final int finish, final String labelName) {
super(lineNumber, token, finish);
this.labelName = labelName;
this.conversion = null;
}
/**
* Copy constructor.
* @param jumpStatement the original jump statement.
* @param conversion a new local variable conversion.
*/
protected JumpStatement(final JumpStatement jumpStatement, final LocalVariableConversion conversion) {
super(jumpStatement);
this.labelName = jumpStatement.labelName;
this.conversion = conversion;
}
@Override
public boolean hasGoto() {
return true;
}
/**
* Get the label name for this break node
* @return label name, or null if none
*/
public String getLabelName() {
return labelName;
}
@Override
public void toString(final StringBuilder sb) {
sb.append(getStatementName());
if (labelName != null) {
sb.append(' ').append(labelName);
}
}
abstract String getStatementName();
@Override
public JumpStatement setLocalVariableConversion(final LexicalContext lc, final LocalVariableConversion conversion) {
if(this.conversion == conversion) {
return this;
}
return createNewJumpStatement(conversion);
}
abstract JumpStatement createNewJumpStatement(LocalVariableConversion newConversion);
@Override
public LocalVariableConversion getLocalVariableConversion() {
return conversion;
}
}

View file

@ -29,36 +29,42 @@ import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
/**
* IR representation for a labeled statement.
* IR representation for a labeled statement. It implements JoinPredecessor to hold conversions that need to be effected
* when the block exits normally, but is also targeted by a break statement that might bring different local variable
* types to the join at the break point.
*/
@Immutable
public final class LabelNode extends LexicalContextStatement {
public final class LabelNode extends LexicalContextStatement implements JoinPredecessor {
/** Label ident. */
private final IdentNode label;
private final String labelName;
/** Statements. */
private final Block body;
private final LocalVariableConversion localVariableConversion;
/**
* Constructor
*
* @param lineNumber line number
* @param token token
* @param finish finish
* @param label label identifier
* @param labelName label name
* @param body body of label node
*/
public LabelNode(final int lineNumber, final long token, final int finish, final IdentNode label, final Block body) {
public LabelNode(final int lineNumber, final long token, final int finish, final String labelName, final Block body) {
super(lineNumber, token, finish);
this.label = label;
this.labelName = labelName;
this.body = body;
this.localVariableConversion = null;
}
private LabelNode(final LabelNode labelNode, final IdentNode label, final Block body) {
private LabelNode(final LabelNode labelNode, final String labelName, final Block body, final LocalVariableConversion localVariableConversion) {
super(labelNode);
this.label = label;
this.body = body;
this.labelName = labelName;
this.body = body;
this.localVariableConversion = localVariableConversion;
}
@Override
@ -69,9 +75,7 @@ public final class LabelNode extends LexicalContextStatement {
@Override
public Node accept(final LexicalContext lc, final NodeVisitor<? extends LexicalContext> visitor) {
if (visitor.enterLabelNode(this)) {
return visitor.leaveLabelNode(
setLabel(lc, (IdentNode)label.accept(visitor)).
setBody(lc, (Block)body.accept(visitor)));
return visitor.leaveLabelNode(setBody(lc, (Block)body.accept(visitor)));
}
return this;
@ -79,8 +83,7 @@ public final class LabelNode extends LexicalContextStatement {
@Override
public void toString(final StringBuilder sb) {
label.toString(sb);
sb.append(':');
sb.append(labelName).append(':');
}
/**
@ -101,22 +104,27 @@ public final class LabelNode extends LexicalContextStatement {
if (this.body == body) {
return this;
}
return Node.replaceInLexicalContext(lc, this, new LabelNode(this, label, body));
return Node.replaceInLexicalContext(lc, this, new LabelNode(this, labelName, body, localVariableConversion));
}
/**
* Get the identifier representing the label name
* Get the label name
* @return the label
*/
public IdentNode getLabel() {
return label;
public String getLabelName() {
return labelName;
}
private LabelNode setLabel(final LexicalContext lc, final IdentNode label) {
if (this.label == label) {
@Override
public LocalVariableConversion getLocalVariableConversion() {
return localVariableConversion;
}
@Override
public LabelNode setLocalVariableConversion(final LexicalContext lc, final LocalVariableConversion localVariableConversion) {
if(this.localVariableConversion == localVariableConversion) {
return this;
}
return Node.replaceInLexicalContext(lc, this, new LabelNode(this, label, body));
return Node.replaceInLexicalContext(lc, this, new LabelNode(this, labelName, body, localVariableConversion));
}
}

View file

@ -27,7 +27,6 @@ package jdk.nashorn.internal.ir;
import java.io.File;
import java.util.Iterator;
import java.util.NoSuchElementException;
import jdk.nashorn.internal.codegen.Label;
import jdk.nashorn.internal.runtime.Debug;
import jdk.nashorn.internal.runtime.Source;
@ -202,6 +201,19 @@ public class LexicalContext {
return (T)popped;
}
/**
* Explicitly apply flags to the topmost element on the stack. This is only valid to use from a
* {@code NodeVisitor.leaveXxx()} method and only on the node being exited at the time. It is not mandatory to use,
* as {@link #pop(LexicalContextNode)} will apply the flags automatically, but this method can be used to apply them
* during the {@code leaveXxx()} method in case its logic depends on the value of the flags.
* @param node the node to apply the flags to. Must be the topmost node on the stack.
* @return the passed in node, or a modified node (if any flags were modified)
*/
public <T extends LexicalContextNode & Flags<T>> T applyTopFlags(final T node) {
assert node == peek();
return node.setFlag(this, flags[sp - 1]);
}
/**
* Return the top element in the context
* @return the node that was pushed last
@ -269,6 +281,20 @@ public class LexicalContext {
return iter.hasNext() ? iter.next() : null;
}
/**
* Gets the label node of the current block.
* @return the label node of the current block, if it is labeled. Otherwise returns null.
*/
public LabelNode getCurrentBlockLabelNode() {
assert stack[sp - 1] instanceof Block;
if(sp < 2) {
return null;
}
final LexicalContextNode parent = stack[sp - 2];
return parent instanceof LabelNode ? (LabelNode)parent : null;
}
/*
public FunctionNode getProgram() {
final Iterator<FunctionNode> iter = getFunctions();
@ -374,9 +400,6 @@ public class LexicalContext {
* @return block in which the symbol is defined, assert if no such block in context
*/
public Block getDefiningBlock(final Symbol symbol) {
if (symbol.isTemp()) {
return null;
}
final String name = symbol.getName();
for (final Iterator<Block> it = getBlocks(); it.hasNext();) {
final Block next = it.next();
@ -393,9 +416,6 @@ public class LexicalContext {
* @return function node in which this symbol is defined, assert if no such symbol exists in context
*/
public FunctionNode getDefiningFunction(final Symbol symbol) {
if (symbol.isTemp()) {
return null;
}
final String name = symbol.getName();
for (final Iterator<LexicalContextNode> iter = new NodeIterator<>(LexicalContextNode.class); iter.hasNext();) {
final LexicalContextNode next = iter.next();
@ -438,7 +458,10 @@ public class LexicalContext {
}
/**
* Count the number of scopes until a given node.
* Count the number of scopes until a given node. Note that this method is solely used to figure out the number of
* scopes that need to be explicitly popped in order to perform a break or continue jump within the current bytecode
* method. For this reason, the method returns 0 if it encounters a {@code SplitNode} between the current location
* and the break/continue target.
* @param until node to stop counting at. Must be within the current function
* @return number of with scopes encountered in the context
*/
@ -450,6 +473,9 @@ public class LexicalContext {
final LexicalContextNode node = iter.next();
if (node == until) {
break;
} else if (node instanceof SplitNode) {
// Don't bother popping scopes if we're going to do a return from a split method anyway.
return 0;
}
assert !(node instanceof FunctionNode); // Can't go outside current function
if (node instanceof WithNode || node instanceof Block && ((Block)node).needsScope()) {
@ -488,12 +514,13 @@ public class LexicalContext {
/**
* Find the breakable node corresponding to this label.
* @param label label to search for, if null the closest breakable node will be returned unconditionally, e.g. a while loop with no label
* @param labelName name of the label to search for. If null, the closest breakable node will be returned
* unconditionally, e.g. a while loop with no label
* @return closest breakable node
*/
public BreakableNode getBreakable(final IdentNode label) {
if (label != null) {
final LabelNode foundLabel = findLabel(label.getName());
public BreakableNode getBreakable(final String labelName) {
if (labelName != null) {
final LabelNode foundLabel = findLabel(labelName);
if (foundLabel != null) {
// iterate to the nearest breakable to the foundLabel
BreakableNode breakable = null;
@ -513,12 +540,13 @@ public class LexicalContext {
/**
* Find the continue target node corresponding to this label.
* @param label label to search for, if null the closest loop node will be returned unconditionally, e.g. a while loop with no label
* @param labelName label name to search for. If null the closest loop node will be returned unconditionally, e.g. a
* while loop with no label
* @return closest continue target node
*/
public LoopNode getContinueTo(final IdentNode label) {
if (label != null) {
final LabelNode foundLabel = findLabel(label.getName());
public LoopNode getContinueTo(final String labelName) {
if (labelName != null) {
final LabelNode foundLabel = findLabel(labelName);
if (foundLabel != null) {
// iterate to the nearest loop to the foundLabel
LoopNode loop = null;
@ -540,7 +568,7 @@ public class LexicalContext {
public LabelNode findLabel(final String name) {
for (final Iterator<LabelNode> iter = new NodeIterator<>(LabelNode.class, getCurrentFunction()); iter.hasNext(); ) {
final LabelNode next = iter.next();
if (next.getLabel().getName().equals(name)) {
if (next.getLabelName().equals(name)) {
return next;
}
}
@ -548,31 +576,21 @@ public class LexicalContext {
}
/**
* Checks whether a given label is a jump destination that lies outside a given
* split node
* Checks whether a given target is a jump destination that lies outside a given split node
* @param splitNode the split node
* @param label the label
* @return true if label resides outside the split node
* @param target the target node
* @return true if target resides outside the split node
*/
public boolean isExternalTarget(final SplitNode splitNode, final Label label) {
boolean targetFound = false;
for (int i = sp - 1; i >= 0; i--) {
public boolean isExternalTarget(final SplitNode splitNode, final BreakableNode target) {
for (int i = sp; i-- > 0;) {
final LexicalContextNode next = stack[i];
if (next == splitNode) {
return !targetFound;
}
if (next instanceof BreakableNode) {
for (final Label l : ((BreakableNode)next).getLabels()) {
if (l == label) {
targetFound = true;
break;
}
}
return true;
} else if (next == target) {
return false;
}
}
assert false : label + " was expected in lexical context " + LexicalContext.this + " but wasn't";
return false;
throw new AssertionError(target + " was expected in lexical context " + LexicalContext.this + " but wasn't");
}
@Override

View file

@ -45,15 +45,4 @@ abstract class LexicalContextExpression extends Expression implements LexicalCon
public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
return Acceptor.accept(this, visitor);
}
/**
* 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 Expression setSymbol(final LexicalContext lc, final Symbol symbol) {
return Node.replaceInLexicalContext(lc, this, (LexicalContextExpression)super.setSymbol(null, symbol));
}
}

View file

@ -28,6 +28,7 @@ package jdk.nashorn.internal.ir;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.function.Function;
import jdk.nashorn.internal.codegen.CompileUnit;
import jdk.nashorn.internal.codegen.types.ArrayType;
import jdk.nashorn.internal.codegen.types.Type;
@ -85,11 +86,6 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey {
this.value = newValue;
}
@Override
public boolean isAtom() {
return true;
}
/**
* Check if the literal value is null
* @return true if literal value is null
@ -99,7 +95,7 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey {
}
@Override
public Type getType() {
public Type getType(final Function<Symbol, Type> localVariableTypes) {
return Type.typeFor(value.getClass());
}
@ -226,7 +222,7 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey {
* Get the literal node value
* @return the value
*/
public T getValue() {
public final T getValue() {
return value;
}
@ -279,6 +275,16 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey {
public boolean isLocal() {
return true;
}
@Override
public boolean isAlwaysFalse() {
return !isTrue();
}
@Override
public boolean isAlwaysTrue() {
return isTrue();
}
}
@Immutable
@ -298,7 +304,7 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey {
}
@Override
public Type getType() {
public Type getType(final Function<Symbol, Type> localVariableTypes) {
return Type.BOOLEAN;
}
@ -361,7 +367,7 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey {
}
@Override
public Type getType() {
public Type getType(final Function<Symbol, Type> localVariableTypes) {
return type;
}
@ -485,7 +491,7 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey {
}
@Override
public Type getType() {
public Type getType(final Function<Symbol, Type> localVariableTypes) {
return Type.OBJECT;
}
@ -554,7 +560,7 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey {
}
@Override
public Type getType() {
public Type getType(final Function<Symbol, Type> localVariableTypes) {
return Type.OBJECT;
}
@ -567,7 +573,7 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey {
/**
* Array literal node class.
*/
public static final class ArrayLiteralNode extends LiteralNode<Expression[]> {
public static final class ArrayLiteralNode extends LiteralNode<Expression[]> implements LexicalContextNode {
/** Array element type. */
private Type elementType;
@ -738,32 +744,36 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey {
return array;
}
private static Type getNarrowestElementType(final Expression[] value) {
Type elementType = Type.INT;
for (final Expression node : value) {
if (node == null) {
elementType = elementType.widest(Type.OBJECT); //no way to represent undefined as number
/**
* Returns the narrowest element type that is wide enough to represent all the expressions in the array.
* @param elementExpressions the array of expressions
* @return the narrowest element type that is wide enough to represent all the expressions in the array.
*/
private static Type getNarrowestElementType(final Expression[] elementExpressions) {
Type widestElementType = Type.INT;
for (final Expression element : elementExpressions) {
if (element == null) {
widestElementType = widestElementType.widest(Type.OBJECT); //no way to represent undefined as number
break;
}
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;
Type elementType = element.getType();
if (elementType.isUnknown()) {
elementType = Type.OBJECT;
}
if (symbolType.isBoolean()) {
elementType = elementType.widest(Type.OBJECT);
if (elementType.isBoolean()) {
widestElementType = widestElementType.widest(Type.OBJECT);
break;
}
elementType = elementType.widest(symbolType);
widestElementType = widestElementType.widest(elementType);
if (elementType.isObject()) {
if (widestElementType.isObject()) {
break;
}
}
return elementType;
return widestElementType;
}
@Override
@ -792,7 +802,7 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey {
}
@Override
public Type getType() {
public Type getType(final Function<Symbol, Type> localVariableTypes) {
return Type.typeFor(NativeArray.class);
}
@ -866,16 +876,21 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey {
@Override
public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
return Acceptor.accept(this, visitor);
}
@Override
public Node accept(LexicalContext lc, NodeVisitor<? extends LexicalContext> visitor) {
if (visitor.enterLiteralNode(this)) {
final List<Expression> oldValue = Arrays.asList(value);
final List<Expression> newValue = Node.accept(visitor, Expression.class, oldValue);
return visitor.leaveLiteralNode(oldValue != newValue ? setValue(newValue) : this);
return visitor.leaveLiteralNode(oldValue != newValue ? setValue(lc, newValue) : this);
}
return this;
}
private ArrayLiteralNode setValue(final List<Expression> value) {
return new ArrayLiteralNode(this, value.toArray(new Expression[value.size()]));
private ArrayLiteralNode setValue(final LexicalContext lc, final List<Expression> value) {
return (ArrayLiteralNode)lc.replace(this, new ArrayLiteralNode(this, value.toArray(new Expression[value.size()])));
}
@Override

View file

@ -0,0 +1,174 @@
/*
* 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.
*/
package jdk.nashorn.internal.ir;
import jdk.nashorn.internal.codegen.types.Type;
/**
* Class describing one or more local variable conversions that needs to be performed on entry to a control flow join
* point. Note that the class is named as a singular "Conversion" and not a plural "Conversions", but instances of the
* class have a reference to the next conversion, so multiple conversions are always represented with a single instance
* that is a head of a linked list of instances.
* @see JoinPredecessor
*/
public final class LocalVariableConversion {
private final Symbol symbol;
// TODO: maybe introduce a type pair class? These will often be repeated.
private final Type from;
private final Type to;
private final LocalVariableConversion next;
/**
* Creates a new object representing a local variable conversion.
* @param symbol the symbol representing the local variable whose value is being converted.
* @param from the type value is being converted from.
* @param to the type value is being converted to.
* @param next next conversion at the same join point, if any (the conversion object implements a singly-linked
* list of conversions).
*/
public LocalVariableConversion(final Symbol symbol, final Type from, final Type to, final LocalVariableConversion next) {
this.symbol = symbol;
this.from = from;
this.to = to;
this.next = next;
}
/**
* Returns the type being converted from.
* @return the type being converted from.
*/
public Type getFrom() {
return from;
}
/**
* Returns the type being converted to.
* @return the type being converted to.
*/
public Type getTo() {
return to;
}
/**
* Returns the next conversion at the same join point, or null if this is the last one.
* @return the next conversion at the same join point.
*/
public LocalVariableConversion getNext() {
return next;
}
/**
* Returns the symbol representing the local variable whose value is being converted.
* @return the symbol representing the local variable whose value is being converted.
*/
public Symbol getSymbol() {
return symbol;
}
/**
* Returns true if this conversion is live. A conversion is live if the symbol has a slot for the conversion's
* {@link #getTo() to} type. If a conversion is dead, it can be omitted in code generator.
* @return true if this conversion is live.
*/
public boolean isLive() {
return symbol.hasSlotFor(to);
}
/**
* Returns true if this conversion {@link #isLive()}, or if any of its {@link #getNext()} conversions are live.
* @return true if this conversion, or any conversion following it, are live.
*/
public boolean isAnyLive() {
return isLive() || isAnyLive(next);
}
/**
* Returns true if the passed join predecessor has {@link #isAnyLive()} conversion.
* @param jp the join predecessor being examined.
* @return true if the join predecessor conversion is not null and {@link #isAnyLive()}.
*/
public static boolean hasLiveConversion(final JoinPredecessor jp) {
return isAnyLive(jp.getLocalVariableConversion());
}
/**
* Returns true if the passed conversion is not null, and it {@link #isAnyLive()}.
* @parameter conv the conversion being tested for liveness.
* @return true if the conversion is not null and {@link #isAnyLive()}.
*/
private static boolean isAnyLive(final LocalVariableConversion conv) {
return conv != null && conv.isAnyLive();
}
@Override
public String toString() {
return toString(new StringBuilder()).toString();
}
/**
* Generates a string representation of this conversion in the passed string builder.
* @param sb the string builder in which to generate a string representation of this conversion.
* @return the passed in string builder.
*/
public StringBuilder toString(final StringBuilder sb) {
if(isLive()) {
return toStringNext(sb.append('\u27e6'), true).append("\u27e7 ");
}
return next == null ? sb : next.toString(sb);
}
/**
* Generates a string representation of the passed conversion in the passed string builder.
* @param conv the conversion to render in the string builder.
* @param sb the string builder in which to generate a string representation of this conversion.
* @return the passed in string builder.
*/
public static StringBuilder toString(final LocalVariableConversion conv, final StringBuilder sb) {
return conv == null ? sb : conv.toString(sb);
}
private StringBuilder toStringNext(final StringBuilder sb, final boolean first) {
if(isLive()) {
if(!first) {
sb.append(", ");
}
sb.append(symbol.getName()).append(':').append(getTypeChar(from)).append('\u2192').append(getTypeChar(to));
return next == null ? sb : next.toStringNext(sb, false);
}
return next == null ? sb : next.toStringNext(sb, first);
}
private static char getTypeChar(final Type type) {
if(type == Type.UNDEFINED) {
return 'U';
} else if(type.isObject()) {
return 'O';
} else if(type == Type.BOOLEAN) {
return 'Z';
}
return type.getBytecodeStackType();
}
}

View file

@ -37,7 +37,7 @@ public abstract class LoopNode extends BreakableStatement {
protected final Label continueLabel;
/** Loop test node, null if infinite */
protected final Expression test;
protected final JoinPredecessorExpression test;
/** Loop body */
protected final Block body;
@ -51,14 +51,13 @@ public abstract class LoopNode extends BreakableStatement {
* @param lineNumber lineNumber
* @param token token
* @param finish finish
* @param test test, or null if infinite loop
* @param body loop body
* @param controlFlowEscapes controlFlowEscapes
*/
protected LoopNode(final int lineNumber, final long token, final int finish, final Expression test, final Block body, final boolean controlFlowEscapes) {
protected LoopNode(final int lineNumber, final long token, final int finish, final Block body, final boolean controlFlowEscapes) {
super(lineNumber, token, finish, new Label("while_break"));
this.continueLabel = new Label("while_continue");
this.test = test;
this.test = null;
this.body = body;
this.controlFlowEscapes = controlFlowEscapes;
}
@ -70,9 +69,11 @@ public abstract class LoopNode extends BreakableStatement {
* @param test new test
* @param body new body
* @param controlFlowEscapes controlFlowEscapes
* @param conversion the local variable conversion carried by this loop node.
*/
protected LoopNode(final LoopNode loopNode, final Expression test, final Block body, final boolean controlFlowEscapes) {
super(loopNode);
protected LoopNode(final LoopNode loopNode, final JoinPredecessorExpression test, final Block body,
final boolean controlFlowEscapes, final LocalVariableConversion conversion) {
super(loopNode, conversion);
this.continueLabel = new Label(loopNode.continueLabel);
this.test = test;
this.body = body;
@ -150,7 +151,9 @@ public abstract class LoopNode extends BreakableStatement {
* Get the test for this for node
* @return the test
*/
public abstract Expression getTest();
public final JoinPredecessorExpression getTest() {
return test;
}
/**
* Set the test for this for node
@ -159,7 +162,7 @@ public abstract class LoopNode extends BreakableStatement {
* @param test new test
* @return same or new node depending on if test was changed
*/
public abstract LoopNode setTest(final LexicalContext lc, final Expression test);
public abstract LoopNode setTest(final LexicalContext lc, final JoinPredecessorExpression test);
/**
* Set the control flow escapes flag for this node.
@ -170,5 +173,4 @@ public abstract class LoopNode extends BreakableStatement {
* @return new loop node if changed otherwise the same
*/
public abstract LoopNode setControlFlowEscapes(final LexicalContext lc, final boolean controlFlowEscapes);
}

View file

@ -27,7 +27,6 @@ package jdk.nashorn.internal.ir;
import java.util.ArrayList;
import java.util.List;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.parser.Token;
import jdk.nashorn.internal.parser.TokenType;
@ -81,15 +80,6 @@ public abstract class Node implements Cloneable {
this.finish = node.finish;
}
/**
* Is this an atom node - for example a literal or an identity
*
* @return true if atom
*/
public boolean isAtom() {
return false;
}
/**
* Is this a loop node?
*
@ -109,31 +99,6 @@ public abstract class Node implements Cloneable {
return false;
}
/**
* Is this a self modifying assignment?
* @return true if self modifying, e.g. a++, or a*= 17
*/
public boolean isSelfModifying() {
return false;
}
/**
* Returns widest operation type of this operation.
*
* @return the widest type for this operation
*/
public Type getWidestOperationType() {
return Type.OBJECT;
}
/**
* Returns true if this node represents a comparison operator
* @return true if comparison
*/
public boolean isComparison() {
return false;
}
/**
* For reference copies - ensure that labels in the copy node are unique
* using an appropriate copy constructor
@ -282,21 +247,6 @@ public abstract class Node implements Cloneable {
return false;
}
/**
* Tag an expression as optimistic or not. This is a convenience wrapper
* that is a no op of the expression cannot be optimistic
* @param expr expression
* @param isOptimistic is optimistic flag
* @return the new expression, or same if unmodified state
*/
//SAM method in Java 8
public static Expression setIsOptimistic(final Expression expr, final boolean isOptimistic) {
if (expr instanceof Optimistic) {
return (Expression)((Optimistic)expr).setIsOptimistic(isOptimistic);
}
return expr;
}
//on change, we have to replace the entire list, that's we can't simple do ListIterator.set
static <T extends Node> List<T> accept(final NodeVisitor<? extends LexicalContext> visitor, final Class<T> clazz, final List<T> list) {
boolean changed = false;
@ -319,21 +269,4 @@ public abstract class Node implements Cloneable {
}
return newNode;
}
static final String OPT_IDENTIFIER = "%";
static void optimisticType(final Node node, final StringBuilder sb) {
if (node instanceof Optimistic && ((Optimistic)node).isOptimistic()) {
sb.append('{');
final String desc = (((Expression)node).getType()).getDescriptor();
sb.append(desc.charAt(desc.length() - 1) == ';' ? "O" : desc);
if (node instanceof Optimistic && ((Optimistic)node).isOptimistic()) {
sb.append(OPT_IDENTIFIER);
sb.append('_');
sb.append(((Optimistic)node).getProgramPoint());
}
sb.append('}');
}
}
}

View file

@ -27,6 +27,8 @@ package jdk.nashorn.internal.ir;
import java.util.Collections;
import java.util.List;
import java.util.function.Function;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
@ -65,6 +67,11 @@ public final class ObjectNode extends Expression {
return this;
}
@Override
public Type getType(final Function<Symbol, Type> localVariableTypes) {
return Type.OBJECT;
}
@Override
public void toString(final StringBuilder sb) {
sb.append('{');

View file

@ -69,13 +69,6 @@ public interface Optimistic {
*/
public boolean canBeOptimistic();
/**
* Is this op optimistic, i.e. narrower than its narrowest statically provable
* type
* @return true if optimistic
*/
public boolean isOptimistic();
/**
* Get the most optimistic type for this node. Typically we start out as
* an int, and then at runtime we bump this up to number and then Object
@ -92,21 +85,11 @@ public interface Optimistic {
*/
public Type getMostPessimisticType();
/**
* Tag this type override as optimistic rather than based on statically known
* type coercion semantics
* @param isOptimistic is this function optimistic
* @return new optimistic function
*/
public Optimistic setIsOptimistic(final boolean isOptimistic);
/**
* Set the override type
*
* @param ts temporary symbols
* @param type the type
* @return a node equivalent to this one except for the requested change.
*/
public Optimistic setType(final TemporarySymbols ts, final Type type);
public Optimistic setType(final Type type);
}

View file

@ -24,8 +24,6 @@
*/
package jdk.nashorn.internal.ir;
import jdk.nashorn.internal.IntDeque;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collections;
@ -57,7 +55,6 @@ public class OptimisticLexicalContext extends LexicalContext {
/** Optimistic assumptions that could be made per function */
private final Deque<List<Assumption>> optimisticAssumptions = new ArrayDeque<>();
private final IntDeque splitNodes = new IntDeque();
/**
* Constructor
@ -111,9 +108,6 @@ public class OptimisticLexicalContext extends LexicalContext {
if (isEnabled) {
if(node instanceof FunctionNode) {
optimisticAssumptions.push(new ArrayList<Assumption>());
splitNodes.push(0);
} else if(node instanceof SplitNode) {
splitNodes.getAndIncrement();
}
}
@ -126,20 +120,9 @@ public class OptimisticLexicalContext extends LexicalContext {
if (isEnabled) {
if(node instanceof FunctionNode) {
optimisticAssumptions.pop();
assert splitNodes.peek() == 0;
splitNodes.pop();
} else if(node instanceof SplitNode) {
splitNodes.decrementAndGet();
}
}
return popped;
}
/**
* Check whether the lexical context is inside a split node
* @return true if we are traversing a SplitNode
*/
public boolean isInSplitNode() {
return !splitNodes.isEmpty() && splitNodes.peek() > 0;
}
}

View file

@ -25,16 +25,17 @@
package jdk.nashorn.internal.ir;
import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.function.Function;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.parser.TokenType;
import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
/**
* IR representation for a runtime call.
@ -169,9 +170,14 @@ public class RuntimeNode extends Expression implements Optimistic {
* @param node the node
* @return request type
*/
public static Request requestFor(final Node node) {
assert node.isComparison();
public static Request requestFor(final Expression node) {
switch (node.tokenType()) {
case TYPEOF:
return Request.TYPEOF;
case IN:
return Request.IN;
case INSTANCEOF:
return Request.INSTANCEOF;
case EQ_STRICT:
return Request.EQ_STRICT;
case NE_STRICT:
@ -409,13 +415,12 @@ public class RuntimeNode extends Expression implements Optimistic {
}
/**
* Constructor
* Constructor used to replace a binary node with a runtime request.
*
* @param parent parent node from which to inherit source, token, finish and arguments
* @param request the request
*/
public RuntimeNode(final BinaryNode parent, final Request request) {
this(parent, request, parent.lhs(), parent.rhs());
public RuntimeNode(final BinaryNode parent) {
this(parent, Request.requestFor(parent), parent.lhs(), parent.rhs());
}
/**
@ -455,7 +460,7 @@ public class RuntimeNode extends Expression implements Optimistic {
* Return type for the ReferenceNode
*/
@Override
public Type getType() {
public Type getType(Function<Symbol, Type> localVariableTypes) {
return request.getReturnType();
}
@ -568,17 +573,7 @@ public class RuntimeNode extends Expression implements Optimistic {
}
@Override
public boolean isOptimistic() {
return false;
}
@Override
public RuntimeNode setIsOptimistic(final boolean isOptimistic) {
return this;
}
@Override
public RuntimeNode setType(final TemporarySymbols ts, final Type type) {
public RuntimeNode setType(final Type type) {
return this;
}
}

View file

@ -25,7 +25,10 @@
package jdk.nashorn.internal.ir;
import java.util.HashMap;
import java.util.Map;
import jdk.nashorn.internal.codegen.CompileUnit;
import jdk.nashorn.internal.codegen.Label;
import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
@ -41,7 +44,9 @@ public class SplitNode extends LexicalContextStatement {
private final CompileUnit compileUnit;
/** Body of split code. */
private final Node body;
private final Block body;
private Map<Label, JoinPredecessor> jumps;
/**
* Constructor
@ -50,18 +55,19 @@ public class SplitNode extends LexicalContextStatement {
* @param body body of split code
* @param compileUnit compile unit to use for the body
*/
public SplitNode(final String name, final Node body, final CompileUnit compileUnit) {
super(-1, body.getToken(), body.getFinish());
public SplitNode(final String name, final Block body, final CompileUnit compileUnit) {
super(body.getFirstStatementLineNumber(), body.getToken(), body.getFinish());
this.name = name;
this.body = body;
this.compileUnit = compileUnit;
}
private SplitNode(final SplitNode splitNode, final Node body) {
private SplitNode(final SplitNode splitNode, final Block body) {
super(splitNode);
this.name = splitNode.name;
this.body = body;
this.compileUnit = splitNode.compileUnit;
this.jumps = splitNode.jumps;
}
/**
@ -72,7 +78,7 @@ public class SplitNode extends LexicalContextStatement {
return body;
}
private SplitNode setBody(final LexicalContext lc, final Node body) {
private SplitNode setBody(final LexicalContext lc, final Block body) {
if (this.body == body) {
return this;
}
@ -82,7 +88,7 @@ public class SplitNode extends LexicalContextStatement {
@Override
public Node accept(final LexicalContext lc, final NodeVisitor<? extends LexicalContext> visitor) {
if (visitor.enterSplitNode(this)) {
return visitor.leaveSplitNode(setBody(lc, body.accept(visitor)));
return visitor.leaveSplitNode(setBody(lc, (Block)body.accept(visitor)));
}
return this;
}
@ -111,4 +117,25 @@ public class SplitNode extends LexicalContextStatement {
return compileUnit;
}
/**
* Adds a jump that crosses this split node's boundary (it originates within the split node, and goes to a target
* outside of it).
* @param jumpOrigin the join predecessor that's the origin of the jump
* @param targetLabel the label that's the target of the jump.
*/
public void addJump(final JoinPredecessor jumpOrigin, final Label targetLabel) {
if(jumps == null) {
jumps = new HashMap<>();
}
jumps.put(targetLabel, jumpOrigin);
}
/**
* Returns the jump origin within this split node for a target.
* @param targetLabel the target for which a jump origin is sought.
* @return the jump origin, or null.
*/
public JoinPredecessor getJumpOrigin(final Label targetLabel) {
return jumps == null ? null : jumps.get(targetLabel);
}
}

View file

@ -66,11 +66,12 @@ public final class SwitchNode extends BreakableStatement {
this.defaultCaseIndex = defaultCase == null ? -1 : cases.indexOf(defaultCase);
}
private SwitchNode(final SwitchNode switchNode, final Expression expression, final List<CaseNode> cases, final int defaultCase) {
super(switchNode);
private SwitchNode(final SwitchNode switchNode, final Expression expression, final List<CaseNode> cases,
final int defaultCaseIndex, final LocalVariableConversion conversion) {
super(switchNode, conversion);
this.expression = expression;
this.cases = cases;
this.defaultCaseIndex = defaultCase;
this.defaultCaseIndex = defaultCaseIndex;
this.tag = switchNode.getTag(); //TODO are symbols inhereted as references?
}
@ -78,9 +79,9 @@ public final class SwitchNode extends BreakableStatement {
public Node ensureUniqueLabels(final LexicalContext lc) {
final List<CaseNode> newCases = new ArrayList<>();
for (final CaseNode caseNode : cases) {
newCases.add(new CaseNode(caseNode, caseNode.getTest(), caseNode.getBody()));
newCases.add(new CaseNode(caseNode, caseNode.getTest(), caseNode.getBody(), caseNode.getLocalVariableConversion()));
}
return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, newCases, defaultCaseIndex));
return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, newCases, defaultCaseIndex, conversion));
}
@Override
@ -138,7 +139,7 @@ public final class SwitchNode extends BreakableStatement {
* by NodeVisitors who perform operations on every case node
* @param lc lexical context
* @param cases list of cases
* @return new switcy node or same if no state was changed
* @return new switch node or same if no state was changed
*/
public SwitchNode setCases(final LexicalContext lc, final List<CaseNode> cases) {
return setCases(lc, cases, defaultCaseIndex);
@ -148,7 +149,7 @@ public final class SwitchNode extends BreakableStatement {
if (this.cases == cases) {
return this;
}
return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, cases, defaultCaseIndex));
return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, cases, defaultCaseIndex, conversion));
}
/**
@ -180,7 +181,7 @@ public final class SwitchNode extends BreakableStatement {
if (this.expression == expression) {
return this;
}
return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, cases, defaultCaseIndex));
return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, cases, defaultCaseIndex, conversion));
}
/**
@ -200,5 +201,27 @@ public final class SwitchNode extends BreakableStatement {
public void setTag(final Symbol tag) {
this.tag = tag;
}
}
/**
* Returns true if all cases of this switch statement are 32-bit signed integer constants.
* @return true if all cases of this switch statement are 32-bit signed integer constants.
*/
public boolean isInteger() {
for (final CaseNode caseNode : cases) {
final Expression test = caseNode.getTest();
if (test != null && !isIntegerLiteral(test)) {
return false;
}
}
return true;
}
@Override
JoinPredecessor setLocalVariableConversionChanged(final LexicalContext lc, final LocalVariableConversion conversion) {
return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, cases, defaultCaseIndex, conversion));
}
private static boolean isIntegerLiteral(final Expression expr) {
return expr instanceof LiteralNode && ((LiteralNode<?>)expr).getValue() instanceof Integer;
}
}

View file

@ -25,58 +25,64 @@
package jdk.nashorn.internal.ir;
import static jdk.nashorn.internal.codegen.CompilerConstants.RETURN;
import java.io.PrintWriter;
import java.util.HashSet;
import java.util.Set;
import java.util.StringTokenizer;
import jdk.nashorn.internal.codegen.types.Range;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.runtime.Context;
import jdk.nashorn.internal.runtime.Debug;
import jdk.nashorn.internal.runtime.options.Options;
/**
* Maps a name to specific data.
* Symbol is a symbolic address for a value ("variable" if you wish). Identifiers in JavaScript source, as well as
* certain synthetic variables created by the compiler are represented by Symbol objects. Symbols can address either
* local variable slots in bytecode ("slotted symbol"), or properties in scope objects ("scoped symbol"). A symbol can
* also end up being defined but then not used during symbol assignment calculations; such symbol will be neither
* scoped, nor slotted; it represents a dead variable (it might be written to, but is never read). Finally, a symbol can
* be both slotted and in scope. This special case can only occur with bytecode method parameters. They all come in as
* slotted, but if they are used by a nested function (or eval) then they will be copied into the scope object, and used
* from there onwards. Two further special cases are parameters stored in {@code NativeArguments} objects and parameters
* stored in {@code Object[]} parameter to variable-arity functions. Those use the {@code #getFieldIndex()} property to
* refer to their location.
*/
public final class Symbol implements Comparable<Symbol> {
/** Symbol kinds. Kind ordered by precedence. */
public static final int IS_TEMP = 1;
/** Is this Global */
public static final int IS_GLOBAL = 2;
public static final int IS_GLOBAL = 1;
/** Is this a variable */
public static final int IS_VAR = 3;
public static final int IS_VAR = 2;
/** Is this a parameter */
public static final int IS_PARAM = 4;
public static final int IS_PARAM = 3;
/** Is this a constant */
public static final int IS_CONSTANT = 5;
public static final int IS_CONSTANT = 4;
/** Mask for kind flags */
public static final int KINDMASK = (1 << 3) - 1; // Kinds are represented by lower three bits
/** Is this scope */
public static final int IS_SCOPE = 1 << 4;
/** Is this symbol in scope */
public static final int IS_SCOPE = 1 << 3;
/** Is this a this symbol */
public static final int IS_THIS = 1 << 5;
/** Can this symbol ever be undefined */
public static final int CAN_BE_UNDEFINED = 1 << 6;
/** Is this symbol always defined? */
public static final int IS_ALWAYS_DEFINED = 1 << 7;
public static final int IS_THIS = 1 << 4;
/** Is this a let */
public static final int IS_LET = 1 << 8;
public static final int IS_LET = 1 << 5;
/** Is this an internal symbol, never represented explicitly in source code */
public static final int IS_INTERNAL = 1 << 9;
public static final int IS_INTERNAL = 1 << 6;
/** Is this a function self-reference symbol */
public static final int IS_FUNCTION_SELF = 1 << 10;
/** Is this a specialized param, i.e. known type base on runtime callsite? */
public static final int IS_SPECIALIZED_PARAM = 1 << 11;
/** Is this symbol a shared temporary? */
public static final int IS_SHARED = 1 << 12;
public static final int IS_FUNCTION_SELF = 1 << 7;
/** Is this a function declaration? */
public static final int IS_FUNCTION_DECLARATION = 1 << 13;
public static final int IS_FUNCTION_DECLARATION = 1 << 8;
/** Is this a program level symbol? */
public static final int IS_PROGRAM_LEVEL = 1 << 14;
public static final int IS_PROGRAM_LEVEL = 1 << 9;
/** Are this symbols' values stored in local variable slots? */
public static final int HAS_SLOT = 1 << 10;
/** Is this symbol known to store an int value ? */
public static final int HAS_INT_VALUE = 1 << 11;
/** Is this symbol known to store a long value ? */
public static final int HAS_LONG_VALUE = 1 << 12;
/** Is this symbol known to store a double value ? */
public static final int HAS_DOUBLE_VALUE = 1 << 13;
/** Is this symbol known to store an object value ? */
public static final int HAS_OBJECT_VALUE = 1 << 14;
/** Null or name identifying symbol. */
private final String name;
@ -84,11 +90,9 @@ public final class Symbol implements Comparable<Symbol> {
/** Symbol flags. */
private int flags;
/** Type of symbol. */
private Type type;
/** Local variable slot. -1 indicates external property. */
private int slot;
/** First bytecode method local variable slot for storing the value(s) of this variable. -1 indicates the variable
* is not stored in local variable slots or it is not yet known. */
private int firstSlot = -1;
/** Field number in scope or property; array index in varargs when not using arguments object. */
private int fieldIndex;
@ -96,9 +100,6 @@ public final class Symbol implements Comparable<Symbol> {
/** Number of times this symbol is used in code */
private int useCount;
/** Range for symbol */
private Range range;
/** Debugging option - dump info and stack trace when symbols with given names are manipulated */
private static final Set<String> TRACE_SYMBOLS;
private static final Set<String> TRACE_SYMBOLS_STACKTRACE;
@ -132,18 +133,15 @@ public final class Symbol implements Comparable<Symbol> {
*
* @param name name of symbol
* @param flags symbol flags
* @param type type of this symbol
* @param slot bytecode slot for this symbol
*/
protected Symbol(final String name, final int flags, final Type type, final int slot) {
protected Symbol(final String name, final int flags, final int slot) {
this.name = name;
this.flags = flags;
this.type = type;
this.slot = slot;
this.firstSlot = slot;
this.fieldIndex = -1;
this.range = Range.createUnknownRange();
if(shouldTrace()) {
trace("CREATE SYMBOL " + type);
trace("CREATE SYMBOL " + name);
}
}
@ -154,29 +152,7 @@ public final class Symbol implements Comparable<Symbol> {
* @param flags symbol flags
*/
public Symbol(final String name, final int flags) {
this(name, flags, Type.UNKNOWN, -1);
}
/**
* Constructor
*
* @param name name of symbol
* @param flags symbol flags
* @param type type of this symbol
*/
public Symbol(final String name, final int flags, final Type type) {
this(name, flags, type, -1);
}
private Symbol(final Symbol base, final String name, final int flags) {
this.flags = flags;
this.name = name;
this.fieldIndex = base.fieldIndex;
this.slot = base.slot;
this.type = base.type;
this.useCount = base.useCount;
this.range = base.range;
this(name, flags, -1);
}
private static String align(final String string, final int max) {
@ -189,17 +165,6 @@ public final class Symbol implements Comparable<Symbol> {
return sb.toString();
}
/**
* Return the type for this symbol. Normally, if there is no type override,
* this is where any type for any node is stored. If the node has a TypeOverride,
* it may override this, e.g. when asking for a scoped field as a double
*
* @return symbol type
*/
public final Type getSymbolType() {
return type;
}
/**
* Debugging .
*
@ -211,14 +176,10 @@ public final class Symbol implements Comparable<Symbol> {
sb.append(align(name, 20)).
append(": ").
append(align(type.toString(), 10)).
append(", ").
append(align(slot == -1 ? "none" : "" + slot, 10));
append(align(firstSlot == -1 ? "none" : "" + firstSlot, 10));
switch (flags & KINDMASK) {
case IS_TEMP:
sb.append(" temp");
break;
case IS_GLOBAL:
sb.append(" global");
break;
@ -251,10 +212,6 @@ public final class Symbol implements Comparable<Symbol> {
sb.append(" this");
}
if (!canBeUndefined()) {
sb.append(" def'd");
}
if (isProgramLevel()) {
sb.append(" program");
}
@ -281,7 +238,12 @@ public final class Symbol implements Comparable<Symbol> {
* @return the symbol
*/
public Symbol setNeedsSlot(final boolean needsSlot) {
setSlot(needsSlot ? 0 : -1);
if(needsSlot) {
assert !isScope();
flags |= HAS_SLOT;
} else {
flags &= ~HAS_SLOT;
}
return this;
}
@ -291,7 +253,14 @@ public final class Symbol implements Comparable<Symbol> {
* @return Number of slots.
*/
public int slotCount() {
return type.isCategory2() ? 2 : 1;
return ((flags & HAS_INT_VALUE) == 0 ? 0 : 1) +
((flags & HAS_LONG_VALUE) == 0 ? 0 : 2) +
((flags & HAS_DOUBLE_VALUE) == 0 ? 0 : 2) +
((flags & HAS_OBJECT_VALUE) == 0 ? 0 : 1);
}
private boolean isSlotted() {
return firstSlot != -1 && ((flags & HAS_SLOT) != 0);
}
@Override
@ -299,17 +268,18 @@ public final class Symbol implements Comparable<Symbol> {
final StringBuilder sb = new StringBuilder();
sb.append(name).
append(' ').
append('(').
append(getSymbolType().getTypeClass().getSimpleName()).
append(')');
append(' ');
if (hasSlot()) {
sb.append(' ').
append('(').
append("slot=").
append(slot).
append(')');
append(firstSlot).append(' ');
if((flags & HAS_INT_VALUE) != 0) { sb.append('I'); }
if((flags & HAS_LONG_VALUE) != 0) { sb.append('J'); }
if((flags & HAS_DOUBLE_VALUE) != 0) { sb.append('D'); }
if((flags & HAS_OBJECT_VALUE) != 0) { sb.append('O'); }
sb.append(')');
}
if (isScope()) {
@ -329,21 +299,31 @@ public final class Symbol implements Comparable<Symbol> {
}
/**
* Does this symbol have an allocated bytecode slot. If not, it is scope
* and must be loaded from memory upon access
* Does this symbol have an allocated bytecode slot? Note that having an allocated bytecode slot doesn't necessarily
* mean the symbol's value will be stored in it. Namely, a function parameter can have a bytecode slot, but if it is
* in scope, then the bytecode slot will not be used. See {@link #isBytecodeLocal()}.
*
* @return true if this symbol has a local bytecode slot
*/
public boolean hasSlot() {
return slot >= 0;
return (flags & HAS_SLOT) != 0;
}
/**
* Check if this is a temporary symbol
* @return true if temporary
* Is this symbol a local variable stored in bytecode local variable slots? This is true for a slotted variable that
* is not in scope. (E.g. a parameter that is in scope is slotted, but it will not be a local variable).
* @return true if this symbol is using bytecode local slots for its storage.
*/
public boolean isTemp() {
return (flags & KINDMASK) == IS_TEMP;
public boolean isBytecodeLocal() {
return hasSlot() && !isScope();
}
/**
* Returns true if this symbol is dead (it is a local variable that is statically proven to never be read in any type).
* @return true if this symbol is dead
*/
public boolean isDead() {
return (flags & (HAS_SLOT | IS_SCOPE)) == 0;
}
/**
@ -354,15 +334,7 @@ public final class Symbol implements Comparable<Symbol> {
*/
public boolean isScope() {
assert (flags & KINDMASK) != IS_GLOBAL || (flags & IS_SCOPE) == IS_SCOPE : "global without scope flag";
return (flags & IS_SCOPE) == IS_SCOPE;
}
/**
* Returns true if this symbol is a temporary that is being shared across expressions.
* @return true if this symbol is a temporary that is being shared across expressions.
*/
public boolean isShared() {
return (flags & IS_SHARED) == IS_SHARED;
return (flags & IS_SCOPE) != 0;
}
/**
@ -370,17 +342,7 @@ public final class Symbol implements Comparable<Symbol> {
* @return true if a function declaration
*/
public boolean isFunctionDeclaration() {
return (flags & IS_FUNCTION_DECLARATION) == IS_FUNCTION_DECLARATION;
}
/**
* Creates an unshared copy of a symbol. The symbol must be currently shared.
* @param newName the name for the new symbol.
* @return a new, unshared symbol.
*/
public Symbol createUnshared(final String newName) {
assert isShared();
return new Symbol(this, newName, flags & ~IS_SHARED);
return (flags & IS_FUNCTION_DECLARATION) != 0;
}
/**
@ -392,28 +354,14 @@ public final class Symbol implements Comparable<Symbol> {
if(shouldTrace()) {
trace("SET IS SCOPE");
}
assert !isShared();
flags |= IS_SCOPE;
if(!isParam()) {
flags &= ~HAS_SLOT;
}
}
return this;
}
/**
* Mark this symbol as one being shared by multiple expressions. The symbol must be a temporary.
* @return the symbol
*/
public Symbol setIsShared() {
if (!isShared()) {
assert isTemp();
if(shouldTrace()) {
trace("SET IS SHARED");
}
flags |= IS_SHARED;
}
return this;
}
/**
* Mark this symbol as a function declaration.
*/
@ -450,77 +398,12 @@ public final class Symbol implements Comparable<Symbol> {
return (flags & KINDMASK) == IS_PARAM;
}
/**
* Check if this symbol is always defined, which overrides all canBeUndefined tags
* @return true if always defined
*/
public boolean isAlwaysDefined() {
return isParam() || (flags & IS_ALWAYS_DEFINED) == IS_ALWAYS_DEFINED;
}
/**
* Check if this is a program (script) level definition
* @return true if program level
*/
public boolean isProgramLevel() {
return (flags & IS_PROGRAM_LEVEL) == IS_PROGRAM_LEVEL;
}
/**
* Get the range for this symbol
* @return range for symbol
*/
public Range getRange() {
return range;
}
/**
* Set the range for this symbol
* @param range range
* @return the symbol
*/
public Symbol setRange(final Range range) {
this.range = range;
return this;
}
/**
* Check if this symbol represents a return value with a known non-generic type.
* @return true if specialized return value
*/
public boolean isNonGenericReturn() {
return getName().equals(RETURN.symbolName()) && type != Type.OBJECT;
}
/**
* 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 if this symbol can ever be undefined
* @return true if can be undefined
*/
public boolean canBeUndefined() {
return (flags & CAN_BE_UNDEFINED) == CAN_BE_UNDEFINED;
}
/**
* Flag this symbol as potentially undefined in parts of the program
* @return the symbol
*/
public Symbol setCanBeUndefined() {
if (isAlwaysDefined()) {
return this;
} else if (!canBeUndefined()) {
assert !isShared();
flags |= CAN_BE_UNDEFINED;
}
return this;
return (flags & IS_PROGRAM_LEVEL) != 0;
}
/**
@ -553,7 +436,7 @@ public final class Symbol implements Comparable<Symbol> {
* @return true if let
*/
public boolean isLet() {
return (flags & IS_LET) == IS_LET;
return (flags & IS_LET) != 0;
}
/**
@ -561,7 +444,6 @@ public final class Symbol implements Comparable<Symbol> {
*/
public void setIsLet() {
if (!isLet()) {
assert !isShared();
flags |= IS_LET;
}
}
@ -571,7 +453,7 @@ public final class Symbol implements Comparable<Symbol> {
* @return true if this symbol as a function's self-referencing symbol.
*/
public boolean isFunctionSelf() {
return (flags & IS_FUNCTION_SELF) == IS_FUNCTION_SELF;
return (flags & IS_FUNCTION_SELF) != 0;
}
/**
@ -594,7 +476,6 @@ public final class Symbol implements Comparable<Symbol> {
*/
public Symbol setFieldIndex(final int fieldIndex) {
if (this.fieldIndex != fieldIndex) {
assert !isShared();
this.fieldIndex = fieldIndex;
}
return this;
@ -615,7 +496,6 @@ public final class Symbol implements Comparable<Symbol> {
*/
public Symbol setFlags(final int flags) {
if (this.flags != flags) {
assert !isShared() : this;
this.flags = flags;
}
return this;
@ -628,7 +508,6 @@ public final class Symbol implements Comparable<Symbol> {
*/
public Symbol setFlag(final int flag) {
if ((this.flags & flag) == 0) {
assert !isShared() : this;
this.flags |= flag;
}
return this;
@ -641,7 +520,6 @@ public final class Symbol implements Comparable<Symbol> {
*/
public Symbol clearFlag(final int flag) {
if ((this.flags & flag) != 0) {
assert !isShared() : this;
this.flags &= ~flag;
}
return this;
@ -656,11 +534,73 @@ public final class Symbol implements Comparable<Symbol> {
}
/**
* Get the byte code slot for this symbol
* @return byte code slot, or -1 if no slot allocated/possible
* Get the index of the first bytecode slot for this symbol
* @return byte code slot
*/
public int getSlot() {
return slot;
public int getFirstSlot() {
assert isSlotted();
return firstSlot;
}
/**
* Get the index of the bytecode slot for this symbol for storing a value of the specified type.
* @param type the requested type
* @return byte code slot
*/
public int getSlot(final Type type) {
assert isSlotted();
int typeSlot = firstSlot;
if(type.isBoolean() || type.isInteger()) {
assert (flags & HAS_INT_VALUE) != 0;
return typeSlot;
}
typeSlot += ((flags & HAS_INT_VALUE) == 0 ? 0 : 1);
if(type.isLong()) {
assert (flags & HAS_LONG_VALUE) != 0;
return typeSlot;
}
typeSlot += ((flags & HAS_LONG_VALUE) == 0 ? 0 : 2);
if(type.isNumber()) {
assert (flags & HAS_DOUBLE_VALUE) != 0;
return typeSlot;
}
assert type.isObject();
assert (flags & HAS_OBJECT_VALUE) != 0 : name;
return typeSlot + ((flags & HAS_DOUBLE_VALUE) == 0 ? 0 : 2);
}
/**
* Returns true if this symbol has a local variable slot for storing a value of specific type.
* @param type the type
* @return true if this symbol has a local variable slot for storing a value of specific type.
*/
public boolean hasSlotFor(final Type type) {
if(type.isBoolean() || type.isInteger()) {
return (flags & HAS_INT_VALUE) != 0;
} else if(type.isLong()) {
return (flags & HAS_LONG_VALUE) != 0;
} else if(type.isNumber()) {
return (flags & HAS_DOUBLE_VALUE) != 0;
}
assert type.isObject();
return (flags & HAS_OBJECT_VALUE) != 0;
}
/**
* Marks this symbol as having a local variable slot for storing a value of specific type.
* @param type the type
*/
public void setHasSlotFor(final Type type) {
if(type.isBoolean() || type.isInteger()) {
setFlag(HAS_INT_VALUE);
} else if(type.isLong()) {
setFlag(HAS_LONG_VALUE);
} else if(type.isNumber()) {
setFlag(HAS_DOUBLE_VALUE);
} else {
assert type.isObject();
setFlag(HAS_OBJECT_VALUE);
}
}
/**
@ -682,88 +622,16 @@ public final class Symbol implements Comparable<Symbol> {
/**
* Set the bytecode slot for this symbol
* @param slot valid bytecode slot, or -1 if not available
* @param firstSlot valid bytecode slot
* @return the symbol
*/
public Symbol setSlot(final int slot) {
if (slot != this.slot) {
assert !isShared();
public Symbol setFirstSlot(final int firstSlot) {
assert firstSlot >= 0 && firstSlot <= 65535;
if (firstSlot != this.firstSlot) {
if(shouldTrace()) {
trace("SET SLOT " + slot);
trace("SET SLOT " + firstSlot);
}
this.slot = slot;
}
return this;
}
/**
* Assign a specific subclass of Object to the symbol
*
* @param type the type
* @return the symbol
*/
public Symbol setType(final Class<?> type) {
assert !type.isPrimitive() && !Number.class.isAssignableFrom(type) : "Class<?> types can only be subclasses of object";
setType(Type.typeFor(type));
return this;
}
/**
* Assign a type to the symbol
*
* @param type the type
* @return the symbol
*/
public Symbol setType(final Type type) {
setTypeOverride(Type.widest(this.type, type));
return this;
}
/**
* Returns true if calling {@link #setType(Type)} on this symbol would effectively change its type.
* @param newType the new type to test for
* @return true if setting this symbols type to a new value would effectively change its type.
*/
public boolean wouldChangeType(final Type newType) {
return Type.widest(this.type, newType) != this.type;
}
/**
* Only use this if you know about an existing type
* constraint - otherwise a type can only be
* widened
*
* @param type the type
* @return the symbol
*/
public Symbol setTypeOverride(final Type type) {
final Type old = this.type;
if (old != type) {
assert !isShared() : this + " is a shared symbol and cannot have its type overridden to " + type;
if(shouldTrace()) {
trace("TYPE CHANGE: " + old + "=>" + type + " == " + type);
}
this.type = type;
}
return this;
}
/**
* Sets the type of the symbol to the specified type. If the type would be changed, but this symbol is a shared
* temporary, it will instead return a different temporary symbol of the requested type from the passed temporary
* symbols. That way, it never mutates the type of a shared temporary.
* @param type the new type for the symbol
* @param ts a holder of temporary symbols
* @return either this symbol, or a different symbol if this symbol is a shared temporary and it type would have to
* be changed.
*/
public Symbol setTypeOverrideShared(final Type type, final TemporarySymbols ts) {
if (getSymbolType() != type) {
if (isShared()) {
assert !hasSlot();
return ts.getTypedTemporarySymbol(type);
}
setTypeOverride(type);
this.firstSlot = firstSlot;
}
return this;
}

View file

@ -1,169 +0,0 @@
/*
* 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.
*/
package jdk.nashorn.internal.ir;
import static jdk.nashorn.internal.codegen.CompilerConstants.TEMP_PREFIX;
import static jdk.nashorn.internal.ir.Symbol.IS_TEMP;
import java.util.HashMap;
import java.util.Map;
import jdk.nashorn.internal.codegen.types.Type;
/**
* Class that holds reusable temporary symbols by type.
*
*/
public class TemporarySymbols {
private static final String prefix = TEMP_PREFIX.symbolName() + "$";
private int totalSymbolCount;
private final Map<Type, TypedTemporarySymbols> temporarySymbolsByType = new HashMap<>();
/**
* Associates a temporary symbol of a given type with a node, if the node doesn't already have any symbol.
* @param lc the current lexical context
* @param type the type of the temporary symbol
* @param node the node
* @return the node that is guaranteed to have a symbol.
*/
public Expression ensureSymbol(final LexicalContext lc, final Type type, final Expression node) {
final Symbol symbol = node.getSymbol();
if (symbol != null) {
return node;
}
return node.setSymbol(lc, getTypedTemporarySymbol(type));
}
/**
* Given a type, returns a temporary symbol of that type.
* @param type the required type of the symbol.
* @return a temporary symbol of the required type.
*/
public Symbol getTypedTemporarySymbol(final Type type) {
return getTypedTemporarySymbols(type).getTemporarySymbol(type);
}
private TypedTemporarySymbols getTypedTemporarySymbols(final Type type) {
TypedTemporarySymbols temporarySymbols = temporarySymbolsByType.get(type);
if(temporarySymbols == null) {
temporarySymbols = new TypedTemporarySymbols();
temporarySymbolsByType.put(type, temporarySymbols);
}
return temporarySymbols;
}
/**
* This method is called to signal to this object that all the symbols it holds can be reused now.
*/
public void reuse() {
for(TypedTemporarySymbols ts: temporarySymbolsByType.values()) {
ts.reuse();
}
}
/**
* Given a shared symbol, creates an unshared copy of it with a unique name.
* @param symbol the shared symbol
* @return the unshared, uniquely named copy of the symbol
*/
public Symbol createUnshared(Symbol symbol) {
return symbol.createUnshared(getUniqueName());
}
private String getUniqueName() {
return prefix + (++totalSymbolCount);
}
/**
* Returns the total number of symbols this object created during its lifetime.
* @return the total number of symbols this object created during its lifetime.
*/
public int getTotalSymbolCount() {
return totalSymbolCount;
}
private class TypedTemporarySymbols {
private Symbol[] symbols = new Symbol[16];
private int nextFreeSymbol = 0;
private int symbolCount = 0;
Symbol getTemporarySymbol(final Type type) {
while(nextFreeSymbol < symbolCount) {
final Symbol nextSymbol = symbols[nextFreeSymbol];
assert nextSymbol != null;
// If it has a slot, we can't reuse it.
if(!nextSymbol.hasSlot()) {
final Type symbolType = nextSymbol.getSymbolType();
if(symbolType == type) {
assert nextSymbol.isTemp();
assert !nextSymbol.isScope();
// If types match, we can reuse it.
nextSymbol.setIsShared();
nextFreeSymbol++;
return nextSymbol;
}
// If its type changed, but it doesn't have a slot then move it to its new home according to its
// new type.
getTypedTemporarySymbols(symbolType).addSymbol(nextSymbol);
}
// If we can move another symbol into its place, do that and repeat the analysis for this symbol.
--symbolCount;
if(symbolCount != nextFreeSymbol) {
final Symbol lastFreeSymbol = symbols[symbolCount];
symbols[nextFreeSymbol] = lastFreeSymbol;
}
symbols[symbolCount] = null;
}
return createNewSymbol(type);
}
private Symbol createNewSymbol(final Type type) {
ensureCapacity();
final Symbol symbol = symbols[nextFreeSymbol] = new Symbol(getUniqueName(), IS_TEMP, type);
nextFreeSymbol++;
symbolCount++;
return symbol;
}
private void addSymbol(Symbol symbol) {
ensureCapacity();
symbols[symbolCount++] = symbol;
}
void reuse() {
nextFreeSymbol = 0;
}
private void ensureCapacity() {
if(symbolCount == symbols.length) {
final Symbol[] newSymbols = new Symbol[symbolCount * 2];
System.arraycopy(symbols, 0, newSymbols, 0, symbolCount);
symbols = newSymbols;
}
}
}
}

View file

@ -25,20 +25,20 @@
package jdk.nashorn.internal.ir;
import java.util.function.Function;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
/**
* TernaryNode nodes represent three operand operations (?:).
* TernaryNode represent the ternary operator {@code ?:}. Note that for control-flow calculation reasons its branch
* expressions (but not its test expression) are always wrapped in instances of {@link JoinPredecessorExpression}.
*/
@Immutable
public final class TernaryNode extends Expression {
private final Expression test;
private final Expression trueExpr;
/** Third argument. */
private final Expression falseExpr;
private final JoinPredecessorExpression trueExpr;
private final JoinPredecessorExpression falseExpr;
/**
* Constructor
@ -48,14 +48,15 @@ public final class TernaryNode extends Expression {
* @param trueExpr expression evaluated when test evaluates to true
* @param falseExpr expression evaluated when test evaluates to true
*/
public TernaryNode(final long token, final Expression test, final Expression trueExpr, final Expression falseExpr) {
public TernaryNode(final long token, final Expression test, final JoinPredecessorExpression trueExpr, final JoinPredecessorExpression falseExpr) {
super(token, falseExpr.getFinish());
this.test = test;
this.trueExpr = trueExpr;
this.falseExpr = falseExpr;
}
private TernaryNode(final TernaryNode ternaryNode, final Expression test, final Expression trueExpr, final Expression falseExpr) {
private TernaryNode(final TernaryNode ternaryNode, final Expression test, final JoinPredecessorExpression trueExpr,
final JoinPredecessorExpression falseExpr) {
super(ternaryNode);
this.test = test;
this.trueExpr = trueExpr;
@ -66,9 +67,9 @@ public final class TernaryNode extends Expression {
public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
if (visitor.enterTernaryNode(this)) {
final Expression newTest = (Expression)getTest().accept(visitor);
final Expression newTrueExpr = (Expression)getTrueExpression().accept(visitor);
final Expression newFalseExpr = (Expression)falseExpr.accept(visitor);
return visitor.leaveTernaryNode(setTest(newTest).setTrueExpression(newTrueExpr).setFalseExpression1(newFalseExpr));
final JoinPredecessorExpression newTrueExpr = (JoinPredecessorExpression)trueExpr.accept(visitor);
final JoinPredecessorExpression newFalseExpr = (JoinPredecessorExpression)falseExpr.accept(visitor);
return visitor.leaveTernaryNode(setTest(newTest).setTrueExpression(newTrueExpr).setFalseExpression(newFalseExpr));
}
return this;
@ -116,6 +117,12 @@ public final class TernaryNode extends Expression {
&& getFalseExpression().isLocal();
}
@Override
public Type getType(Function<Symbol, Type> localVariableTypes) {
return Type.widestReturnType(getTrueExpression().getType(localVariableTypes), getFalseExpression().getType(localVariableTypes));
}
/**
* Get the test expression for this ternary expression, i.e. "x" in x ? y : z
* @return the test expression
@ -128,7 +135,7 @@ public final class TernaryNode extends Expression {
* Get the true expression for this ternary expression, i.e. "y" in x ? y : z
* @return the true expression
*/
public Expression getTrueExpression() {
public JoinPredecessorExpression getTrueExpression() {
return trueExpr;
}
@ -136,7 +143,7 @@ public final class TernaryNode extends Expression {
* Get the false expression for this ternary expression, i.e. "z" in x ? y : z
* @return the false expression
*/
public Expression getFalseExpression() {
public JoinPredecessorExpression getFalseExpression() {
return falseExpr;
}
@ -157,7 +164,7 @@ public final class TernaryNode extends Expression {
* @param trueExpr new true expression
* @return a node equivalent to this one except for the requested change.
*/
public TernaryNode setTrueExpression(final Expression trueExpr) {
public TernaryNode setTrueExpression(final JoinPredecessorExpression trueExpr) {
if (this.trueExpr == trueExpr) {
return this;
}
@ -169,7 +176,7 @@ public final class TernaryNode extends Expression {
* @param falseExpr new false expression
* @return a node equivalent to this one except for the requested change.
*/
public TernaryNode setFalseExpression1(final Expression falseExpr) {
public TernaryNode setFalseExpression(final JoinPredecessorExpression falseExpr) {
if (this.falseExpr == falseExpr) {
return this;
}

View file

@ -32,14 +32,13 @@ import jdk.nashorn.internal.ir.visitor.NodeVisitor;
* IR representation for THROW statements.
*/
@Immutable
public final class ThrowNode extends Statement {
public final class ThrowNode extends Statement implements JoinPredecessor {
/** Exception expression. */
private final Expression expression;
private final int flags;
private final LocalVariableConversion conversion;
/** Is this block a synthethic rethrow created by finally inlining? */
public static final int IS_SYNTHETIC_RETHROW = 1;
private final boolean isSyntheticRethrow;
/**
* Constructor
@ -48,18 +47,21 @@ public final class ThrowNode extends Statement {
* @param token token
* @param finish finish
* @param expression expression to throw
* @param flags flags
* @param isSyntheticRethrow true if this throw node is part of a synthetic rethrow.
*/
public ThrowNode(final int lineNumber, final long token, final int finish, final Expression expression, final int flags) {
public ThrowNode(final int lineNumber, final long token, final int finish, final Expression expression, final boolean isSyntheticRethrow) {
super(lineNumber, token, finish);
this.expression = expression;
this.flags = flags;
this.isSyntheticRethrow = isSyntheticRethrow;
this.conversion = null;
}
private ThrowNode(final ThrowNode node, final Expression expression, final int flags) {
private ThrowNode(final ThrowNode node, final Expression expression, final boolean isSyntheticRethrow,
final LocalVariableConversion conversion) {
super(node);
this.expression = expression;
this.flags = flags;
this.isSyntheticRethrow = isSyntheticRethrow;
this.conversion = conversion;
}
@Override
@ -87,6 +89,9 @@ public final class ThrowNode extends Statement {
if (expression != null) {
expression.toString(sb);
}
if(conversion != null) {
conversion.toString(sb);
}
}
/**
@ -106,7 +111,7 @@ public final class ThrowNode extends Statement {
if (this.expression == expression) {
return this;
}
return new ThrowNode(this, expression, flags);
return new ThrowNode(this, expression, isSyntheticRethrow, conversion);
}
/**
@ -116,7 +121,20 @@ public final class ThrowNode extends Statement {
* @return true if synthetic throw node
*/
public boolean isSyntheticRethrow() {
return (flags & IS_SYNTHETIC_RETHROW) == IS_SYNTHETIC_RETHROW;
return isSyntheticRethrow;
}
@Override
public JoinPredecessor setLocalVariableConversion(final LexicalContext lc, final LocalVariableConversion conversion) {
if(this.conversion == conversion) {
return this;
}
return new ThrowNode(this, expression, isSyntheticRethrow, conversion);
}
@Override
public LocalVariableConversion getLocalVariableConversion() {
return conversion;
}
}

View file

@ -28,8 +28,6 @@ package jdk.nashorn.internal.ir;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import jdk.nashorn.internal.codegen.Label;
import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
@ -37,7 +35,7 @@ import jdk.nashorn.internal.ir.visitor.NodeVisitor;
* IR representation of a TRY statement.
*/
@Immutable
public final class TryNode extends Statement {
public final class TryNode extends Statement implements JoinPredecessor {
/** Try statements. */
private final Block body;
@ -47,15 +45,14 @@ public final class TryNode extends Statement {
/** Finally clause. */
private final Block finallyBody;
/** Exit label. */
private final Label exit;
/** Exception symbol. */
private Symbol exception;
/** Catchall exception for finally expansion, where applicable */
private Symbol finallyCatchAll;
private final LocalVariableConversion conversion;
/**
* Constructor
*
@ -71,21 +68,22 @@ public final class TryNode extends Statement {
this.body = body;
this.catchBlocks = catchBlocks;
this.finallyBody = finallyBody;
this.exit = new Label("exit");
this.conversion = null;
}
private TryNode(final TryNode tryNode, final Block body, final List<Block> catchBlocks, final Block finallyBody) {
private TryNode(final TryNode tryNode, final Block body, final List<Block> catchBlocks, final Block finallyBody, LocalVariableConversion conversion) {
super(tryNode);
this.body = body;
this.catchBlocks = catchBlocks;
this.finallyBody = finallyBody;
this.exit = new Label(tryNode.exit);
this.conversion = conversion;
this.exception = tryNode.exception;
}
@Override
public Node ensureUniqueLabels(final LexicalContext lc) {
//try nodes are never in lex context
return new TryNode(this, body, catchBlocks, finallyBody);
return new TryNode(this, body, catchBlocks, finallyBody, conversion);
}
@Override
@ -115,7 +113,6 @@ public final class TryNode extends Statement {
setBody(newBody).
setFinallyBody(newFinallyBody).
setCatchBlocks(Node.accept(visitor, Block.class, catchBlocks)).
setException(exception).
setFinallyCatchAll(finallyCatchAll));
}
@ -144,7 +141,7 @@ public final class TryNode extends Statement {
if (this.body == body) {
return this;
}
return new TryNode(this, body, catchBlocks, finallyBody);
return new TryNode(this, body, catchBlocks, finallyBody, conversion);
}
/**
@ -154,11 +151,15 @@ public final class TryNode extends Statement {
public List<CatchNode> getCatches() {
final List<CatchNode> catches = new ArrayList<>(catchBlocks.size());
for (final Block catchBlock : catchBlocks) {
catches.add((CatchNode)catchBlock.getStatements().get(0));
catches.add(getCatchNodeFromBlock(catchBlock));
}
return Collections.unmodifiableList(catches);
}
private static CatchNode getCatchNodeFromBlock(final Block catchBlock) {
return (CatchNode)catchBlock.getStatements().get(0);
}
/**
* Get the catch blocks for this try block
* @return a list of blocks
@ -176,7 +177,7 @@ public final class TryNode extends Statement {
if (this.catchBlocks == catchBlocks) {
return this;
}
return new TryNode(this, body, catchBlocks, finallyBody);
return new TryNode(this, body, catchBlocks, finallyBody, conversion);
}
/**
@ -186,7 +187,6 @@ public final class TryNode extends Statement {
public Symbol getException() {
return exception;
}
/**
* Set the exception symbol for this try block
* @param exception a symbol for the compiler to store the exception in
@ -218,14 +218,6 @@ public final class TryNode extends Statement {
return this;
}
/**
* Get the exit label for this try block
* @return exit label
*/
public Label getExit() {
return exit;
}
/**
* Get the body of the finally clause for this try
* @return finally body, or null if no finally
@ -243,6 +235,19 @@ public final class TryNode extends Statement {
if (this.finallyBody == finallyBody) {
return this;
}
return new TryNode(this, body, catchBlocks, finallyBody);
return new TryNode(this, body, catchBlocks, finallyBody, conversion);
}
@Override
public JoinPredecessor setLocalVariableConversion(final LexicalContext lc, final LocalVariableConversion conversion) {
if(this.conversion == conversion) {
return this;
}
return new TryNode(this, body, catchBlocks, finallyBody, conversion);
}
@Override
public LocalVariableConversion getLocalVariableConversion() {
return conversion;
}
}

View file

@ -33,6 +33,7 @@ import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.function.Function;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.annotations.Ignore;
import jdk.nashorn.internal.ir.annotations.Immutable;
@ -50,8 +51,6 @@ public final class UnaryNode extends Expression implements Assignment<Expression
private final int programPoint;
private final boolean isOptimistic;
private final Type type;
@Ignore
@ -88,16 +87,14 @@ public final class UnaryNode extends Expression implements Assignment<Expression
super(token, start, finish);
this.expression = expression;
this.programPoint = INVALID_PROGRAM_POINT;
this.isOptimistic = false;
this.type = null;
}
private UnaryNode(final UnaryNode unaryNode, final Expression expression, final Type type, final int programPoint, final boolean isOptimistic) {
private UnaryNode(final UnaryNode unaryNode, final Expression expression, final Type type, final int programPoint) {
super(unaryNode);
this.expression = expression;
this.programPoint = programPoint;
this.isOptimistic = isOptimistic;
this.type = type;
}
@ -124,12 +121,40 @@ public final class UnaryNode extends Expression implements Assignment<Expression
return isAssignment();
}
private static final Function<Symbol, Type> UNKNOWN_LOCALS = new Function<Symbol, Type>() {
@Override
public Type apply(Symbol t) {
return null;
}
};
@Override
public Type getWidestOperationType() {
return getWidestOperationType(UNKNOWN_LOCALS);
}
private Type getWidestOperationType(final Function<Symbol, Type> localVariableTypes) {
switch (tokenType()) {
case ADD:
final Type operandType = getExpression().getType(localVariableTypes);
if(operandType == Type.BOOLEAN) {
return Type.INT;
} else if(operandType.isObject()) {
return Type.NUMBER;
}
assert operandType.isNumeric();
return operandType;
case SUB:
// This might seems overly conservative until you consider that -0 can only be represented as a double.
return Type.NUMBER;
case NOT:
case DELETE:
return Type.BOOLEAN;
case BIT_NOT:
return Type.INT;
case VOID:
return Type.UNDEFINED;
default:
return isAssignment() ? Type.NUMBER : Type.OBJECT;
}
@ -206,7 +231,7 @@ public final class UnaryNode extends Expression implements Assignment<Expression
final boolean isPostfix = tokenType == DECPOSTFIX || tokenType == INCPOSTFIX;
if (isOptimistic()) {
sb.append(Node.OPT_IDENTIFIER);
sb.append(Expression.OPT_IDENTIFIER);
}
boolean rhsParen = tokenType.needsParens(getExpression().tokenType(), false);
@ -261,7 +286,7 @@ public final class UnaryNode extends Expression implements Assignment<Expression
if (this.expression == expression) {
return this;
}
return new UnaryNode(this, expression, type, programPoint, isOptimistic);
return new UnaryNode(this, expression, type, programPoint);
}
@Override
@ -274,15 +299,7 @@ public final class UnaryNode extends Expression implements Assignment<Expression
if (this.programPoint == programPoint) {
return this;
}
return new UnaryNode(this, expression, type, programPoint, isOptimistic);
}
@Override
public UnaryNode setIsOptimistic(final boolean isOptimistic) {
if (this.isOptimistic == isOptimistic) {
return this;
}
return new UnaryNode(this, expression, type, programPoint, isOptimistic);
return new UnaryNode(this, expression, type, programPoint);
}
@Override
@ -304,22 +321,20 @@ public final class UnaryNode extends Expression implements Assignment<Expression
}
@Override
public boolean isOptimistic() {
//return hasType() && canBeOptimistic() && getType().narrowerThan(getMostPessimisticType());
return isOptimistic;
public Type getType(Function<Symbol, Type> localVariableTypes) {
final Type widest = getWidestOperationType(localVariableTypes);
if(type == null) {
return widest;
}
return Type.narrowest(widest, Type.widest(type, expression.getType(localVariableTypes)));
}
@Override
public Type getType() {
return type == null ? super.getType() : type;
}
@Override
public UnaryNode setType(TemporarySymbols ts, Type type) {
public UnaryNode setType(Type type) {
if (this.type == type) {
return this;
}
return new UnaryNode(this, expression, type, programPoint, isOptimistic);
return new UnaryNode(this, expression, type, programPoint);
}
}

View file

@ -123,8 +123,9 @@ public final class VarNode extends Statement implements Assignment<IdentNode> {
@Override
public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
if (visitor.enterVarNode(this)) {
final IdentNode newName = (IdentNode)name.accept(visitor);
// var is right associative, so visit init before name
final Expression newInit = init == null ? null : (Expression)init.accept(visitor);
final IdentNode newName = (IdentNode)name.accept(visitor);
final VarNode newThis;
if (name != newName || init != newInit) {
newThis = new VarNode(this, newName, newInit, flags);

View file

@ -47,7 +47,7 @@ public final class WhileNode extends LoopNode {
* @param isDoWhile is this a do while loop?
*/
public WhileNode(final int lineNumber, final long token, final int finish, final boolean isDoWhile) {
super(lineNumber, token, finish, null, null, false);
super(lineNumber, token, finish, null, false);
this.isDoWhile = isDoWhile;
}
@ -58,15 +58,16 @@ public final class WhileNode extends LoopNode {
* @param test test
* @param body body
* @param controlFlowEscapes control flow escapes?
* @param conversion TODO
*/
protected WhileNode(final WhileNode whileNode, final Expression test, final Block body, final boolean controlFlowEscapes) {
super(whileNode, test, body, controlFlowEscapes);
private WhileNode(final WhileNode whileNode, final JoinPredecessorExpression test, final Block body, final boolean controlFlowEscapes, LocalVariableConversion conversion) {
super(whileNode, test, body, controlFlowEscapes, conversion);
this.isDoWhile = whileNode.isDoWhile;
}
@Override
public Node ensureUniqueLabels(final LexicalContext lc) {
return Node.replaceInLexicalContext(lc, this, new WhileNode(this, test, body, controlFlowEscapes));
return Node.replaceInLexicalContext(lc, this, new WhileNode(this, test, body, controlFlowEscapes, conversion));
}
@Override
@ -80,26 +81,21 @@ public final class WhileNode extends LoopNode {
if (isDoWhile()) {
return visitor.leaveWhileNode(
setBody(lc, (Block)body.accept(visitor)).
setTest(lc, (Expression)test.accept(visitor)));
setTest(lc, (JoinPredecessorExpression)test.accept(visitor)));
}
return visitor.leaveWhileNode(
setTest(lc, (Expression)test.accept(visitor)).
setTest(lc, (JoinPredecessorExpression)test.accept(visitor)).
setBody(lc, (Block)body.accept(visitor)));
}
return this;
}
@Override
public Expression getTest() {
return test;
}
@Override
public WhileNode setTest(final LexicalContext lc, final Expression test) {
public WhileNode setTest(final LexicalContext lc, final JoinPredecessorExpression test) {
if (this.test == test) {
return this;
}
return Node.replaceInLexicalContext(lc, this, new WhileNode(this, test, body, controlFlowEscapes));
return Node.replaceInLexicalContext(lc, this, new WhileNode(this, test, body, controlFlowEscapes, conversion));
}
@Override
@ -112,7 +108,7 @@ public final class WhileNode extends LoopNode {
if (this.body == body) {
return this;
}
return Node.replaceInLexicalContext(lc, this, new WhileNode(this, test, body, controlFlowEscapes));
return Node.replaceInLexicalContext(lc, this, new WhileNode(this, test, body, controlFlowEscapes, conversion));
}
@Override
@ -120,7 +116,12 @@ public final class WhileNode extends LoopNode {
if (this.controlFlowEscapes == controlFlowEscapes) {
return this;
}
return Node.replaceInLexicalContext(lc, this, new WhileNode(this, test, body, controlFlowEscapes));
return Node.replaceInLexicalContext(lc, this, new WhileNode(this, test, body, controlFlowEscapes, conversion));
}
@Override
JoinPredecessor setLocalVariableConversionChanged(final LexicalContext lc, final LocalVariableConversion conversion) {
return Node.replaceInLexicalContext(lc, this, new WhileNode(this, test, body, controlFlowEscapes, conversion));
}
/**

View file

@ -36,6 +36,7 @@ import java.util.List;
import jdk.nashorn.internal.ir.BinaryNode;
import jdk.nashorn.internal.ir.Block;
import jdk.nashorn.internal.ir.Expression;
import jdk.nashorn.internal.ir.IdentNode;
import jdk.nashorn.internal.ir.Node;
import jdk.nashorn.internal.ir.Symbol;
import jdk.nashorn.internal.ir.TernaryNode;
@ -121,8 +122,8 @@ public final class ASTWriter {
type = "ref: " + type;
}
final Symbol symbol;
if (node instanceof Expression) {
symbol = ((Expression)node).getSymbol();
if (node instanceof IdentNode) {
symbol = ((IdentNode)node).getSymbol();
} else {
symbol = null;
}

View file

@ -25,9 +25,9 @@
package jdk.nashorn.internal.ir.debug;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.ArrayList;
import jdk.nashorn.internal.codegen.CompilerConstants;
import jdk.nashorn.internal.ir.AccessNode;
import jdk.nashorn.internal.ir.BinaryNode;
@ -39,12 +39,14 @@ import jdk.nashorn.internal.ir.CaseNode;
import jdk.nashorn.internal.ir.CatchNode;
import jdk.nashorn.internal.ir.ContinueNode;
import jdk.nashorn.internal.ir.EmptyNode;
import jdk.nashorn.internal.ir.Expression;
import jdk.nashorn.internal.ir.ExpressionStatement;
import jdk.nashorn.internal.ir.ForNode;
import jdk.nashorn.internal.ir.FunctionNode;
import jdk.nashorn.internal.ir.IdentNode;
import jdk.nashorn.internal.ir.IfNode;
import jdk.nashorn.internal.ir.IndexNode;
import jdk.nashorn.internal.ir.JoinPredecessorExpression;
import jdk.nashorn.internal.ir.LabelNode;
import jdk.nashorn.internal.ir.LexicalContext;
import jdk.nashorn.internal.ir.LiteralNode;
@ -99,6 +101,17 @@ public final class JSONWriter extends NodeVisitor<LexicalContext> {
}
}
@Override
public boolean enterJoinPredecessorExpression(JoinPredecessorExpression joinPredecessorExpression) {
final Expression expr = joinPredecessorExpression.getExpression();
if(expr != null) {
expr.accept(this);
} else {
nullValue();
}
return false;
}
@Override
protected boolean enterDefault(final Node node) {
objectStart();
@ -129,8 +142,7 @@ public final class JSONWriter extends NodeVisitor<LexicalContext> {
accessNode.getBase().accept(this);
comma();
property("property");
accessNode.getProperty().accept(this);
property("property", accessNode.getProperty());
comma();
property("computed", false);
@ -150,16 +162,6 @@ public final class JSONWriter extends NodeVisitor<LexicalContext> {
return leave();
}
private static boolean isLogical(final TokenType tt) {
switch (tt) {
case AND:
case OR:
return true;
default:
return false;
}
}
@Override
public boolean enterBinaryNode(final BinaryNode binaryNode) {
enterDefault(binaryNode);
@ -167,7 +169,7 @@ public final class JSONWriter extends NodeVisitor<LexicalContext> {
final String name;
if (binaryNode.isAssignment()) {
name = "AssignmentExpression";
} else if (isLogical(binaryNode.tokenType())) {
} else if (binaryNode.isLogical()) {
name = "LogicalExpression";
} else {
name = "BinaryExpression";
@ -196,11 +198,11 @@ public final class JSONWriter extends NodeVisitor<LexicalContext> {
type("BreakStatement");
comma();
final IdentNode label = breakNode.getLabel();
property("label");
if (label != null) {
label.accept(this);
final String label = breakNode.getLabelName();
if(label != null) {
property("label", label);
} else {
property("label");
nullValue();
}
@ -275,11 +277,11 @@ public final class JSONWriter extends NodeVisitor<LexicalContext> {
type("ContinueStatement");
comma();
final IdentNode label = continueNode.getLabel();
property("label");
if (label != null) {
label.accept(this);
final String label = continueNode.getLabelName();
if(label != null) {
property("label", label);
} else {
property("label");
nullValue();
}
@ -532,8 +534,7 @@ public final class JSONWriter extends NodeVisitor<LexicalContext> {
type("LabeledStatement");
comma();
property("label");
labelNode.getLabel().accept(this);
property("label", labelNode.getLabelName());
comma();
property("body");

View file

@ -39,7 +39,6 @@ import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import jdk.internal.org.objectweb.asm.Attribute;
import jdk.internal.org.objectweb.asm.Handle;
import jdk.internal.org.objectweb.asm.Label;
@ -928,6 +927,10 @@ public final class NashornTextifier extends Printer {
}
} else {
final int lastSlash = desc.lastIndexOf('/');
final int lastBracket = desc.lastIndexOf('[');
if(lastBracket != -1) {
sb.append(desc, 0, lastBracket + 1);
}
sb.append(lastSlash == -1 ? desc : desc.substring(lastSlash + 1));
}
}

View file

@ -29,18 +29,25 @@ import java.util.List;
import jdk.nashorn.internal.ir.BinaryNode;
import jdk.nashorn.internal.ir.Block;
import jdk.nashorn.internal.ir.BlockStatement;
import jdk.nashorn.internal.ir.BreakNode;
import jdk.nashorn.internal.ir.CaseNode;
import jdk.nashorn.internal.ir.CatchNode;
import jdk.nashorn.internal.ir.ContinueNode;
import jdk.nashorn.internal.ir.ExpressionStatement;
import jdk.nashorn.internal.ir.ForNode;
import jdk.nashorn.internal.ir.FunctionNode;
import jdk.nashorn.internal.ir.IdentNode;
import jdk.nashorn.internal.ir.IfNode;
import jdk.nashorn.internal.ir.JoinPredecessor;
import jdk.nashorn.internal.ir.JoinPredecessorExpression;
import jdk.nashorn.internal.ir.LabelNode;
import jdk.nashorn.internal.ir.LexicalContext;
import jdk.nashorn.internal.ir.LocalVariableConversion;
import jdk.nashorn.internal.ir.Node;
import jdk.nashorn.internal.ir.SplitNode;
import jdk.nashorn.internal.ir.Statement;
import jdk.nashorn.internal.ir.SwitchNode;
import jdk.nashorn.internal.ir.ThrowNode;
import jdk.nashorn.internal.ir.TryNode;
import jdk.nashorn.internal.ir.UnaryNode;
import jdk.nashorn.internal.ir.VarNode;
@ -139,6 +146,27 @@ public final class PrintVisitor extends NodeVisitor<LexicalContext> {
return false;
}
@Override
public boolean enterContinueNode(final ContinueNode node) {
node.toString(sb);
printLocalVariableConversion(node);
return false;
}
@Override
public boolean enterBreakNode(final BreakNode node) {
node.toString(sb);
printLocalVariableConversion(node);
return false;
}
@Override
public boolean enterThrowNode(final ThrowNode node) {
node.toString(sb);
printLocalVariableConversion(node);
return false;
}
@Override
public boolean enterBlock(final Block block) {
sb.append(' ');
@ -190,6 +218,7 @@ public final class PrintVisitor extends NodeVisitor<LexicalContext> {
sb.append(EOLN);
indent();
sb.append('}');
printLocalVariableConversion(block);
return false;
}
@ -210,6 +239,24 @@ public final class PrintVisitor extends NodeVisitor<LexicalContext> {
return false;
}
@Override
public boolean enterJoinPredecessorExpression(JoinPredecessorExpression expr) {
expr.getExpression().accept(this);
printLocalVariableConversion(expr);
return false;
}
@Override
public boolean enterIdentNode(IdentNode identNode) {
identNode.toString(sb);
printLocalVariableConversion(identNode);
return true;
}
private void printLocalVariableConversion(final JoinPredecessor joinPredecessor) {
LocalVariableConversion.toString(joinPredecessor.getLocalVariableConversion(), sb);
}
@Override
public boolean enterUnaryNode(final UnaryNode unaryNode) {
unaryNode.toString(sb, new Runnable() {
@ -252,7 +299,12 @@ public final class PrintVisitor extends NodeVisitor<LexicalContext> {
sb.append(" else ");
fail.accept(this);
}
if(ifNode.getLocalVariableConversion() != null) {
assert fail == null;
sb.append(" else ");
printLocalVariableConversion(ifNode);
sb.append(";");
}
return false;
}
@ -263,7 +315,7 @@ public final class PrintVisitor extends NodeVisitor<LexicalContext> {
indent += TABWIDTH;
labeledNode.toString(sb);
labeledNode.getBody().accept(this);
printLocalVariableConversion(labeledNode);
return false;
}
@ -296,12 +348,19 @@ public final class PrintVisitor extends NodeVisitor<LexicalContext> {
sb.append(EOLN);
indent();
caseNode.toString(sb);
printLocalVariableConversion(caseNode);
indent += TABWIDTH;
caseNode.getBody().accept(this);
indent -= TABWIDTH;
sb.append(EOLN);
}
if(switchNode.getLocalVariableConversion() != null) {
sb.append(EOLN);
indent();
sb.append("default: ");
printLocalVariableConversion(switchNode);
sb.append("{}");
}
sb.append(EOLN);
indent();
sb.append("}");
@ -312,6 +371,7 @@ public final class PrintVisitor extends NodeVisitor<LexicalContext> {
@Override
public boolean enterTryNode(final TryNode tryNode) {
tryNode.toString(sb);
printLocalVariableConversion(tryNode);
tryNode.getBody().accept(this);
final List<Block> catchBlocks = tryNode.getCatchBlocks();
@ -336,6 +396,7 @@ public final class PrintVisitor extends NodeVisitor<LexicalContext> {
public boolean enterVarNode(final VarNode varNode) {
sb.append("var ");
varNode.getName().toString(sb);
printLocalVariableConversion(varNode.getName());
final Node init = varNode.getInit();
if (init != null) {
sb.append(" = ");
@ -347,6 +408,7 @@ public final class PrintVisitor extends NodeVisitor<LexicalContext> {
@Override
public boolean enterWhileNode(final WhileNode whileNode) {
printLocalVariableConversion(whileNode);
if (whileNode.isDoWhile()) {
sb.append("do");
whileNode.getBody().accept(this);

View file

@ -53,8 +53,6 @@ public class NodeOperatorVisitor<T extends LexicalContext> extends NodeVisitor<T
return enterBIT_NOT(unaryNode);
case DELETE:
return enterDELETE(unaryNode);
case DISCARD:
return enterDISCARD(unaryNode);
case NEW:
return enterNEW(unaryNode);
case NOT:
@ -84,8 +82,6 @@ public class NodeOperatorVisitor<T extends LexicalContext> extends NodeVisitor<T
return leaveBIT_NOT(unaryNode);
case DELETE:
return leaveDELETE(unaryNode);
case DISCARD:
return leaveDISCARD(unaryNode);
case NEW:
return leaveNEW(unaryNode);
case NOT:
@ -358,26 +354,6 @@ public class NodeOperatorVisitor<T extends LexicalContext> extends NodeVisitor<T
return leaveDefault(unaryNode);
}
/**
* Unary enter - callback for entering a discard operator
*
* @param unaryNode the node
* @return true if traversal should continue and node children be traversed, false otherwise
*/
public boolean enterDISCARD(final UnaryNode unaryNode) {
return enterDefault(unaryNode);
}
/**
* Unary leave - callback for leaving a discard operator
*
* @param unaryNode the node
* @return processed node, which will replace the original one, or the original node
*/
public Node leaveDISCARD(final UnaryNode unaryNode) {
return leaveDefault(unaryNode);
}
/**
* Unary enter - callback for entering a new operator
*

View file

@ -41,6 +41,7 @@ import jdk.nashorn.internal.ir.FunctionNode;
import jdk.nashorn.internal.ir.IdentNode;
import jdk.nashorn.internal.ir.IfNode;
import jdk.nashorn.internal.ir.IndexNode;
import jdk.nashorn.internal.ir.JoinPredecessorExpression;
import jdk.nashorn.internal.ir.LabelNode;
import jdk.nashorn.internal.ir.LexicalContext;
import jdk.nashorn.internal.ir.LiteralNode;
@ -688,6 +689,27 @@ public abstract class NodeVisitor<T extends LexicalContext> {
return leaveDefault(unaryNode);
}
/**
* Callback for entering a {@link JoinPredecessorExpression}.
*
* @param expr the join predecessor expression
* @return true if traversal should continue and node children be traversed, false otherwise
*/
public boolean enterJoinPredecessorExpression(final JoinPredecessorExpression expr) {
return enterDefault(expr);
}
/**
* Callback for leaving a {@link JoinPredecessorExpression}.
*
* @param expr the join predecessor expression
* @return processed node, which will replace the original one, or the original node
*/
public Node leaveJoinPredecessorExpression(final JoinPredecessorExpression expr) {
return leaveDefault(expr);
}
/**
* Callback for entering a VarNode
*

View file

@ -45,7 +45,6 @@ import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicReference;
import jdk.internal.dynalink.linker.GuardedInvocation;
import jdk.internal.dynalink.linker.LinkRequest;
import jdk.nashorn.internal.codegen.ApplySpecialization;
@ -91,18 +90,15 @@ public final class Global extends ScriptObject implements Scope {
* like
*
* <pre>
* {@code
* public boolean setterGuard(final Object receiver) {
* final Global global = Global.instance();
* final ScriptObject sobj = global.getFunctionPrototype();
* final Object apply = sobj.get("apply");
* return apply == receiver;
* }
*
* }
* </pre>
*
* Naturally, checking for builting classes like NativeFunction is cheaper,
* Naturally, checking for builtin classes like NativeFunction is cheaper,
* it's when you start adding property checks for said builtins you have
* problems with guard speed.
*/
@ -1953,7 +1949,7 @@ public final class Global extends ScriptObject implements Scope {
this.builtinFunction = (ScriptFunction)initConstructor("Function");
// create global anonymous function
final ScriptFunction anon = ScriptFunctionImpl.newAnonymousFunction(this);
final ScriptFunction anon = ScriptFunctionImpl.newAnonymousFunction();
// need to copy over members of Function.prototype to anon function
anon.addBoundProperties(getFunctionPrototype());

View file

@ -1147,7 +1147,7 @@ public final class NativeString extends ScriptObject {
return str.substring(start, end + 1);
}
private static Object newObj(final Object self, final CharSequence str) {
private static Object newObj(final CharSequence str) {
return new NativeString(str);
}
@ -1165,7 +1165,7 @@ public final class NativeString extends ScriptObject {
@Constructor(arity = 1)
public static Object constructor(final boolean newObj, final Object self, final Object... args) {
final CharSequence str = args.length > 0 ? JSType.toCharSequence(args[0]) : "";
return newObj ? newObj(self, str) : str.toString();
return newObj ? newObj(str) : str.toString();
}
/**
@ -1180,7 +1180,7 @@ public final class NativeString extends ScriptObject {
*/
@SpecializedConstructor
public static Object constructor(final boolean newObj, final Object self) {
return newObj ? newObj(self, "") : "";
return newObj ? newObj("") : "";
}
/**
@ -1197,7 +1197,7 @@ public final class NativeString extends ScriptObject {
@SpecializedConstructor
public static Object constructor(final boolean newObj, final Object self, final Object arg) {
final CharSequence str = JSType.toCharSequence(arg);
return newObj ? newObj(self, str) : str.toString();
return newObj ? newObj(str) : str.toString();
}
/**
@ -1214,7 +1214,7 @@ public final class NativeString extends ScriptObject {
@SpecializedConstructor
public static Object constructor(final boolean newObj, final Object self, final int arg) {
final String str = JSType.toString(arg);
return newObj ? newObj(self, str) : str;
return newObj ? newObj(str) : str;
}
/**
@ -1231,7 +1231,7 @@ public final class NativeString extends ScriptObject {
@SpecializedConstructor
public static Object constructor(final boolean newObj, final Object self, final boolean arg) {
final String str = JSType.toString(arg);
return newObj ? newObj(self, str) : str;
return newObj ? newObj(str) : str;
}
/**

View file

@ -30,7 +30,6 @@ import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
import java.lang.invoke.MethodHandle;
import java.util.ArrayList;
import jdk.nashorn.internal.runtime.AccessorProperty;
import jdk.nashorn.internal.runtime.GlobalFunctions;
import jdk.nashorn.internal.runtime.Property;
@ -176,13 +175,13 @@ public class ScriptFunctionImpl extends ScriptFunction {
private static class AnonymousFunction extends ScriptFunctionImpl {
private static final PropertyMap anonmap$ = PropertyMap.newMap();
AnonymousFunction(final Global global) {
AnonymousFunction() {
super("", GlobalFunctions.ANONYMOUS, anonmap$, null);
}
}
static ScriptFunctionImpl newAnonymousFunction(final Global global) {
return new AnonymousFunction(global);
static ScriptFunctionImpl newAnonymousFunction() {
return new AnonymousFunction();
}
/**

View file

@ -61,7 +61,6 @@ import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import jdk.internal.dynalink.support.NameCodec;
import jdk.nashorn.internal.codegen.CompilerConstants;
import jdk.nashorn.internal.codegen.Namespace;
@ -86,6 +85,7 @@ import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
import jdk.nashorn.internal.ir.IdentNode;
import jdk.nashorn.internal.ir.IfNode;
import jdk.nashorn.internal.ir.IndexNode;
import jdk.nashorn.internal.ir.JoinPredecessorExpression;
import jdk.nashorn.internal.ir.LabelNode;
import jdk.nashorn.internal.ir.LexicalContext;
import jdk.nashorn.internal.ir.LiteralNode;
@ -471,11 +471,6 @@ loop:
if (isStrictMode) {
flags |= FunctionNode.IS_STRICT;
}
if (env._specialize_calls != null) {
if (env._specialize_calls.contains(name)) {
flags |= FunctionNode.CAN_SPECIALIZE;
}
}
if (parentFunction == null) {
flags |= FunctionNode.IS_PROGRAM;
}
@ -653,9 +648,13 @@ loop:
}
// Build up node.
if(BinaryNode.isLogical(opType)) {
return new BinaryNode(op, new JoinPredecessorExpression(lhs), new JoinPredecessorExpression(rhs));
}
return new BinaryNode(op, lhs, rhs);
}
/**
* Reduce increment/decrement to simpler operations.
* @param firstToken First token.
@ -1198,7 +1197,7 @@ loop:
*/
private void forStatement() {
// Create FOR node, capturing FOR token.
ForNode forNode = new ForNode(line, token, Token.descPosition(token), null, null, null, null, ForNode.IS_FOR);
ForNode forNode = new ForNode(line, token, Token.descPosition(token), null, ForNode.IS_FOR);
lc.push(forNode);
@ -1241,16 +1240,16 @@ loop:
expect(SEMICOLON);
if (type != SEMICOLON) {
forNode = forNode.setTest(lc, expression());
forNode = forNode.setTest(lc, joinPredecessorExpression());
}
expect(SEMICOLON);
if (type != RPAREN) {
forNode = forNode.setModify(lc, expression());
forNode = forNode.setModify(lc, joinPredecessorExpression());
}
break;
case IN:
forNode = forNode.setIsForIn(lc);
forNode = forNode.setIsForIn(lc).setTest(lc, new JoinPredecessorExpression());
if (vars != null) {
// for (var i in obj)
if (vars.size() == 1) {
@ -1283,7 +1282,7 @@ loop:
next();
// Get the collection expression.
forNode = forNode.setModify(lc, expression());
forNode = forNode.setModify(lc, joinPredecessorExpression());
break;
default:
@ -1343,7 +1342,7 @@ loop:
try {
expect(LPAREN);
final int whileLine = line;
final Expression test = expression();
final JoinPredecessorExpression test = joinPredecessorExpression();
expect(RPAREN);
final Block body = getStatement();
appendStatement(whileNode =
@ -1381,7 +1380,7 @@ loop:
expect(WHILE);
expect(LPAREN);
final int doLine = line;
final Expression test = expression();
final JoinPredecessorExpression test = joinPredecessorExpression();
expect(RPAREN);
if (type == SEMICOLON) {
@ -1435,8 +1434,8 @@ loop:
break;
}
final IdentNode label = labelNode == null ? null : labelNode.getLabel();
final LoopNode targetNode = lc.getContinueTo(label);
final String labelName = labelNode == null ? null : labelNode.getLabelName();
final LoopNode targetNode = lc.getContinueTo(labelName);
if (targetNode == null) {
throw error(AbstractParser.message("illegal.continue.stmt"), continueToken);
@ -1445,7 +1444,7 @@ loop:
endOfLine();
// Construct and add CONTINUE node.
appendStatement(new ContinueNode(continueLine, continueToken, finish, label == null ? null : new IdentNode(label)));
appendStatement(new ContinueNode(continueLine, continueToken, finish, labelName));
}
/**
@ -1485,8 +1484,8 @@ loop:
//either an explicit label - then get its node or just a "break" - get first breakable
//targetNode is what we are breaking out from.
final IdentNode label = labelNode == null ? null : labelNode.getLabel();
final BreakableNode targetNode = lc.getBreakable(label);
final String labelName = labelNode == null ? null : labelNode.getLabelName();
final BreakableNode targetNode = lc.getBreakable(labelName);
if (targetNode == null) {
throw error(AbstractParser.message("illegal.break.stmt"), breakToken);
}
@ -1494,7 +1493,7 @@ loop:
endOfLine();
// Construct and add BREAK node.
appendStatement(new BreakNode(breakLine, breakToken, finish, label == null ? null : new IdentNode(label)));
appendStatement(new BreakNode(breakLine, breakToken, finish, labelName));
}
/**
@ -1721,7 +1720,7 @@ loop:
throw error(AbstractParser.message("duplicate.label", ident.getName()), labelToken);
}
LabelNode labelNode = new LabelNode(line, labelToken, finish, ident, null);
LabelNode labelNode = new LabelNode(line, labelToken, finish, ident.getName(), null);
try {
lc.push(labelNode);
labelNode = labelNode.setBody(lc, getStatement());
@ -1768,7 +1767,7 @@ loop:
endOfLine();
appendStatement(new ThrowNode(throwLine, throwToken, finish, expression, 0));
appendStatement(new ThrowNode(throwLine, throwToken, finish, expression, false));
}
/**
@ -1831,7 +1830,7 @@ loop:
try {
// Get CATCH body.
final Block catchBody = getBlock(true);
final CatchNode catchNode = new CatchNode(catchLine, catchToken, finish, exception, ifExpression, catchBody, 0);
final CatchNode catchNode = new CatchNode(catchLine, catchToken, finish, exception, ifExpression, catchBody, false);
appendStatement(catchNode);
} finally {
catchBlock = restoreBlock(catchBlock);
@ -1993,7 +1992,7 @@ loop:
// Skip ending of edit string expression.
expect(RBRACE);
return new CallNode(primaryLine, primaryToken, finish, execIdent, arguments);
return new CallNode(primaryLine, primaryToken, finish, execIdent, arguments, false);
}
/**
@ -2343,7 +2342,7 @@ loop:
detectSpecialFunction((IdentNode)lhs);
}
lhs = new CallNode(callLine, callToken, finish, lhs, arguments);
lhs = new CallNode(callLine, callToken, finish, lhs, arguments, false);
}
loop:
@ -2358,7 +2357,7 @@ loop:
final List<Expression> arguments = optimizeList(argumentList());
// Create call node.
lhs = new CallNode(callLine, callToken, finish, lhs, arguments);
lhs = new CallNode(callLine, callToken, finish, lhs, arguments, false);
break;
@ -2381,7 +2380,7 @@ loop:
final IdentNode property = getIdentifierName();
// Create property access node.
lhs = new AccessNode(callToken, finish, lhs, property);
lhs = new AccessNode(callToken, finish, lhs, property.getName());
break;
@ -2437,7 +2436,7 @@ loop:
arguments.add(objectLiteral());
}
final CallNode callNode = new CallNode(callLine, constructor.getToken(), finish, constructor, optimizeList(arguments));
final CallNode callNode = new CallNode(callLine, constructor.getToken(), finish, constructor, optimizeList(arguments), true);
return new UnaryNode(newToken, callNode);
}
@ -2461,7 +2460,7 @@ loop:
switch (type) {
case NEW:
// Get new exppression.
// Get new expression.
lhs = newExpression();
break;
@ -2505,7 +2504,7 @@ loop:
final IdentNode property = getIdentifierName();
// Create property access node.
lhs = new AccessNode(callToken, finish, lhs, property);
lhs = new AccessNode(callToken, finish, lhs, property.getName());
break;
@ -2709,7 +2708,7 @@ loop:
return ((PropertyKey)nameExpr).getPropertyName();
} else if(nameExpr instanceof AccessNode) {
markDefaultNameUsed();
return ((AccessNode)nameExpr).getProperty().getName();
return ((AccessNode)nameExpr).getProperty();
}
}
return null;
@ -2751,7 +2750,7 @@ loop:
*/
private List<IdentNode> formalParameterList(final TokenType endType) {
// Prepare to gather parameters.
final List<IdentNode> parameters = new ArrayList<>();
final ArrayList<IdentNode> parameters = new ArrayList<>();
// Track commas.
boolean first = true;
@ -2772,6 +2771,7 @@ loop:
parameters.add(ident);
}
parameters.trimToSize();
return parameters;
}
@ -3089,6 +3089,10 @@ loop:
return expression(unaryExpression(), COMMARIGHT.getPrecedence(), false);
}
private JoinPredecessorExpression joinPredecessorExpression() {
return new JoinPredecessorExpression(expression());
}
private Expression expression(final Expression exprLhs, final int minPrecedence, final boolean noIn) {
// Get the precedence of the next operator.
int precedence = type.getPrecedence();
@ -3105,15 +3109,15 @@ loop:
// Pass expression. Middle expression of a conditional expression can be a "in"
// expression - even in the contexts where "in" is not permitted.
final Expression rhs = expression(unaryExpression(), ASSIGN.getPrecedence(), false);
final Expression trueExpr = expression(unaryExpression(), ASSIGN.getPrecedence(), false);
expect(COLON);
// Fail expression.
final Expression third = expression(unaryExpression(), ASSIGN.getPrecedence(), noIn);
final Expression falseExpr = expression(unaryExpression(), ASSIGN.getPrecedence(), noIn);
// Build up node.
lhs = new TernaryNode(op, lhs, rhs, third);
lhs = new TernaryNode(op, lhs, new JoinPredecessorExpression(trueExpr), new JoinPredecessorExpression(falseExpr));
} else {
// Skip operator.
next();

View file

@ -25,7 +25,6 @@
package jdk.nashorn.internal.parser;
import java.util.Locale;
import static jdk.nashorn.internal.parser.TokenKind.BINARY;
import static jdk.nashorn.internal.parser.TokenKind.BRACKET;
import static jdk.nashorn.internal.parser.TokenKind.FUTURE;
@ -36,6 +35,8 @@ import static jdk.nashorn.internal.parser.TokenKind.LITERAL;
import static jdk.nashorn.internal.parser.TokenKind.SPECIAL;
import static jdk.nashorn.internal.parser.TokenKind.UNARY;
import java.util.Locale;
/**
* Description of all the JavaScript tokens.
*/
@ -182,7 +183,6 @@ public enum TokenType {
ARRAY (LITERAL, null),
COMMALEFT (IR, null),
DISCARD (IR, null),
DECPOSTFIX (IR, null),
INCPOSTFIX (IR, null);

View file

@ -42,7 +42,6 @@ import java.lang.invoke.MethodHandles;
import java.lang.invoke.SwitchPoint;
import java.util.function.Supplier;
import java.util.logging.Level;
import jdk.nashorn.internal.codegen.ObjectClassGenerator;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.lookup.Lookup;
@ -367,15 +366,7 @@ public class AccessorProperty extends Property {
* Initialize the type of a property
*/
protected final void initializeType() {
Class<?> initialType = null;
if (OBJECT_FIELDS_ONLY) {
initialType = Object.class;
} else {
if (!canBeUndefined()) { //todo if !canBeUndefined it means that we have an exact initialType
initialType = int.class;
}
}
setCurrentType(initialType);
setCurrentType(OBJECT_FIELDS_ONLY ? Object.class : null);
}
private static MethodHandle bindTo(final MethodHandle mh, final Object receiver) {

View file

@ -37,7 +37,6 @@ import java.lang.invoke.SwitchPoint;
import java.util.Map;
import java.util.TreeMap;
import java.util.logging.Level;
import jdk.nashorn.internal.codegen.types.ArrayType;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.FunctionNode;
@ -68,7 +67,6 @@ final class CompiledFunction {
private MethodHandle constructor;
private OptimismInfo optimismInfo;
private int flags; // from FunctionNode
private boolean applyToCall;
CompiledFunction(final MethodHandle invoker) {
this(invoker, null);
@ -91,7 +89,7 @@ final class CompiledFunction {
CompiledFunction(final MethodHandle invoker, final RecompilableScriptFunctionData functionData, final int flags) {
this(invoker, null, functionData.getLogger());
this.flags = flags;
if ((flags & FunctionNode.IS_OPTIMISTIC) != 0) {
if ((flags & FunctionNode.IS_DEOPTIMIZABLE) != 0) {
optimismInfo = new OptimismInfo(functionData);
} else {
optimismInfo = null;
@ -102,12 +100,8 @@ final class CompiledFunction {
return flags;
}
void setIsApplyToCall() {
applyToCall = true;
}
boolean isApplyToCall() {
return applyToCall;
return (flags & FunctionNode.HAS_APPLY_TO_CALL_SPECIALIZATION) != 0;
}
boolean isVarArg() {
@ -152,10 +146,6 @@ final class CompiledFunction {
return constructor;
}
MethodHandle getInvoker() {
return invoker;
}
/**
* Creates a version of the invoker intended for a pessimistic caller (return type is Object, no caller optimistic
* program point available).
@ -480,10 +470,10 @@ final class CompiledFunction {
* function has no optimistic assumptions.
*/
SwitchPoint getOptimisticAssumptionsSwitchPoint() {
return isOptimistic() ? optimismInfo.optimisticAssumptions : null;
return canBeDeoptimized() ? optimismInfo.optimisticAssumptions : null;
}
boolean isOptimistic() {
boolean canBeDeoptimized() {
return optimismInfo != null;
}
@ -492,7 +482,7 @@ final class CompiledFunction {
// If compiled function is not optimistic, it can't ever change its invoker/constructor, so just return them
// directly.
if(!isOptimistic()) {
if(!canBeDeoptimized()) {
return handle;
}
@ -518,7 +508,7 @@ final class CompiledFunction {
final MethodHandle relink = MethodHandles.insertArguments(RELINK_COMPOSABLE_INVOKER, 0, cs, inv, constructor);
target = assumptions.guardWithTest(handle, MethodHandles.foldArguments(cs.dynamicInvoker(), relink));
}
cs.setTarget(target);
cs.setTarget(target.asType(cs.type()));
}
private MethodHandle getInvokerOrConstructor(final boolean selectCtor) {
@ -526,12 +516,13 @@ final class CompiledFunction {
}
MethodHandle createInvoker(final Class<?> callSiteReturnType, final int callerProgramPoint) {
final boolean isOptimistic = isOptimistic();
final boolean isOptimistic = canBeDeoptimized();
MethodHandle handleRewriteException = isOptimistic ? createRewriteExceptionHandler() : null;
MethodHandle inv = invoker;
if(isValid(callerProgramPoint)) {
inv = OptimisticReturnFilters.filterOptimisticReturnValue(inv, callSiteReturnType, callerProgramPoint);
inv = changeReturnType(inv, callSiteReturnType);
if(callSiteReturnType.isPrimitive() && handleRewriteException != null) {
// because handleRewriteException always returns Object
handleRewriteException = OptimisticReturnFilters.filterOptimisticReturnValue(handleRewriteException,
@ -575,32 +566,30 @@ final class CompiledFunction {
* @return the method handle for the rest-of method, for folding composition.
*/
private MethodHandle handleRewriteException(final OptimismInfo oldOptimismInfo, final RewriteException re) {
if (log.isEnabled()) {
log.info(new RecompilationEvent(Level.INFO, re, re.getReturnValueNonDestructive()), "\tRewriteException ", re.getMessageShort());
}
final MethodType type = type();
// Compiler needs a call site type as its input, which always has a callee parameter, so we must add it if
// this function doesn't have a callee parameter.
final MethodType callSiteType = type.parameterType(0) == ScriptFunction.class ? type : type.insertParameterTypes(0, ScriptFunction.class);
final FunctionNode fn = oldOptimismInfo.recompile(callSiteType, re);
if (log.isEnabled()) {
log.info(new RecompilationEvent(Level.INFO, re, re.getReturnValueNonDestructive()), "\tRewriteException ", re.getMessageShort());
}
// It didn't necessarily recompile, e.g. for an outer invocation of a recursive function if we already
// recompiled a deoptimized version for an inner invocation.
final boolean isOptimistic;
final boolean canBeDeoptimized;
if (fn != null) {
//is recompiled
assert optimismInfo == oldOptimismInfo;
isOptimistic = fn.isOptimistic();
canBeDeoptimized = fn.canBeDeoptimized();
if (log.isEnabled()) {
log.info("Recompiled '", fn.getName(), "' (", Debug.id(this), ")", isOptimistic ? " remains optimistic." : " is no longer optimistic.");
log.info("Recompiled '", fn.getName(), "' (", Debug.id(this), ")", canBeDeoptimized ? " can still be deoptimized." : " is completely deoptimized.");
}
final MethodHandle newInvoker = oldOptimismInfo.data.lookup(fn);
invoker = newInvoker.asType(type.changeReturnType(newInvoker.type().returnType()));
constructor = null; // Will be regenerated when needed
// Note that we only adjust the switch point after we set the invoker/constructor. This is important.
if (isOptimistic) {
if (canBeDeoptimized) {
// Otherwise, set a new switch point.
oldOptimismInfo.newOptimisticAssumptions();
} else {
@ -608,13 +597,15 @@ final class CompiledFunction {
optimismInfo = null;
}
} else {
isOptimistic = isOptimistic();
assert !isOptimistic || optimismInfo == oldOptimismInfo;
// It didn't necessarily recompile, e.g. for an outer invocation of a recursive function if we already
// recompiled a deoptimized version for an inner invocation.
canBeDeoptimized = canBeDeoptimized();
assert !canBeDeoptimized || optimismInfo == oldOptimismInfo;
}
final MethodHandle restOf = changeReturnType(oldOptimismInfo.compileRestOfMethod(callSiteType, re), Object.class);
// If rest-of is itself optimistic, we must make sure that we can repeat a deoptimization if it, too hits an exception.
return isOptimistic ? MH.catchException(restOf, RewriteException.class, createRewriteExceptionHandler()) : restOf;
return canBeDeoptimized ? MH.catchException(restOf, RewriteException.class, createRewriteExceptionHandler()) : restOf;
}
private static class OptimismInfo {

View file

@ -145,13 +145,16 @@ public enum JSType {
/** Div exact wrapper for potentially integer division that turns into float point */
public static final Call DIV_EXACT = staticCall(JSTYPE_LOOKUP, JSType.class, "divExact", int.class, int.class, int.class, int.class);
/** Mod exact wrapper for potentially integer remainders that turns into float point */
public static final Call REM_EXACT = staticCall(JSTYPE_LOOKUP, JSType.class, "remExact", int.class, int.class, int.class, int.class);
/** Decrement exact wrapper for potentially overflowing integer operations */
public static final Call DECREMENT_EXACT = staticCall(JSTYPE_LOOKUP, JSType.class, "decrementExact", int.class, int.class, int.class);
/** Increment exact wrapper for potentially overflowing integer operations */
public static final Call INCREMENT_EXACT = staticCall(JSTYPE_LOOKUP, JSType.class, "incrementExact", int.class, int.class, int.class);
/** Negate exact exact wrapper for potentially overflowing long operations */
/** Negate exact exact wrapper for potentially overflowing integer operations */
public static final Call NEGATE_EXACT = staticCall(JSTYPE_LOOKUP, JSType.class, "negateExact", int.class, int.class, int.class);
/** Add exact wrapper for potentially overflowing long operations */
@ -166,6 +169,9 @@ public enum JSType {
/** Div exact wrapper for potentially integer division that turns into float point */
public static final Call DIV_EXACT_LONG = staticCall(JSTYPE_LOOKUP, JSType.class, "divExact", long.class, long.class, long.class, int.class);
/** Mod exact wrapper for potentially integer remainders that turns into float point */
public static final Call REM_EXACT_LONG = staticCall(JSTYPE_LOOKUP, JSType.class, "remExact", long.class, long.class, long.class, int.class);
/** Decrement exact wrapper for potentially overflowing long operations */
public static final Call DECREMENT_EXACT_LONG = staticCall(JSTYPE_LOOKUP, JSType.class, "decrementExact", long.class, long.class, int.class);
@ -1417,6 +1423,24 @@ public enum JSType {
throw new UnwarrantedOptimismException((double)x / (double)y, programPoint);
}
/**
* Wrapper for modExact. Throws UnwarrantedOptimismException if the modulo can't be represented as int.
*
* @param x first term
* @param y second term
* @param programPoint program point id
* @return the result
* @throws UnwarrantedOptimismException if the modulo can't be represented as int.
*/
public static int remExact(final int x, final int y, final int programPoint) throws UnwarrantedOptimismException {
try {
return x % y;
} catch (final ArithmeticException e) {
assert y == 0; // Only mod by zero anticipated
throw new UnwarrantedOptimismException(Double.NaN, programPoint);
}
}
/**
* Wrapper for divExact. Throws UnwarrantedOptimismException if the result of the division can't be represented as
* long.
@ -1442,6 +1466,24 @@ public enum JSType {
throw new UnwarrantedOptimismException((double)x / (double)y, programPoint);
}
/**
* Wrapper for modExact. Throws UnwarrantedOptimismException if the modulo can't be represented as int.
*
* @param x first term
* @param y second term
* @param programPoint program point id
* @return the result
* @throws UnwarrantedOptimismException if the modulo can't be represented as int.
*/
public static long remExact(final long x, final long y, final int programPoint) throws UnwarrantedOptimismException {
try {
return x % y;
} catch (final ArithmeticException e) {
assert y == 0L; // Only mod by zero anticipated
throw new UnwarrantedOptimismException(Double.NaN, programPoint);
}
}
/**
* Wrapper for decrementExact
*

View file

@ -49,6 +49,7 @@ public final class OptimisticReturnFilters {
private static final int BOOLEAN_TYPE_INDEX;
private static final int CHAR_TYPE_INDEX;
private static final int FLOAT_TYPE_INDEX;
private static final int VOID_TYPE_INDEX;
static {
final MethodHandle INT_DOUBLE = findOwnMH("ensureInt", int.class, double.class, int.class);
@ -57,11 +58,13 @@ public final class OptimisticReturnFilters {
findOwnMH("ensureInt", int.class, long.class, int.class),
INT_DOUBLE,
findOwnMH("ensureInt", int.class, Object.class, int.class),
findOwnMH("ensureInt", int.class, int.class),
findOwnMH("ensureInt", int.class, boolean.class, int.class),
findOwnMH("ensureInt", int.class, char.class, int.class),
INT_DOUBLE.asType(INT_DOUBLE.type().changeParameterType(0, float.class)),
};
VOID_TYPE_INDEX = ENSURE_INT.length - 4;
BOOLEAN_TYPE_INDEX = ENSURE_INT.length - 3;
CHAR_TYPE_INDEX = ENSURE_INT.length - 2;
FLOAT_TYPE_INDEX = ENSURE_INT.length - 1;
@ -72,6 +75,7 @@ public final class OptimisticReturnFilters {
null,
LONG_DOUBLE,
findOwnMH("ensureLong", long.class, Object.class, int.class),
ENSURE_INT[VOID_TYPE_INDEX].asType(ENSURE_INT[VOID_TYPE_INDEX].type().changeReturnType(long.class)),
ENSURE_INT[BOOLEAN_TYPE_INDEX].asType(ENSURE_INT[BOOLEAN_TYPE_INDEX].type().changeReturnType(long.class)),
ENSURE_INT[CHAR_TYPE_INDEX].asType(ENSURE_INT[CHAR_TYPE_INDEX].type().changeReturnType(long.class)),
LONG_DOUBLE.asType(LONG_DOUBLE.type().changeParameterType(0, float.class)),
@ -82,6 +86,7 @@ public final class OptimisticReturnFilters {
null,
null,
findOwnMH("ensureNumber", double.class, Object.class, int.class),
ENSURE_INT[VOID_TYPE_INDEX].asType(ENSURE_INT[VOID_TYPE_INDEX].type().changeReturnType(double.class)),
ENSURE_INT[BOOLEAN_TYPE_INDEX].asType(ENSURE_INT[BOOLEAN_TYPE_INDEX].type().changeReturnType(double.class)),
ENSURE_INT[CHAR_TYPE_INDEX].asType(ENSURE_INT[CHAR_TYPE_INDEX].type().changeReturnType(double.class)),
null
@ -108,12 +113,12 @@ public final class OptimisticReturnFilters {
}
final MethodHandle guard = getOptimisticTypeGuard(expectedReturnType, actualReturnType);
return guard == null ? mh : MH.filterReturnValue(mh, MH.insertArguments(guard, 1, programPoint));
return guard == null ? mh : MH.filterReturnValue(mh, MH.insertArguments(guard, guard.type().parameterCount() - 1, programPoint));
}
/**
* Given a guarded invocation and a callsite descriptor, perform return value filtering
* according to the optimistic type coercion rules, using the return value from the deccriptor
* according to the optimistic type coercion rules, using the return value from the descriptor
* @param inv the invocation
* @param desc the descriptor
* @return filtered invocation
@ -156,6 +161,8 @@ public final class OptimisticReturnFilters {
return accTypeIndex;
} else if(provable == boolean.class) {
return BOOLEAN_TYPE_INDEX;
} else if(provable == void.class) {
return VOID_TYPE_INDEX;
} else if(provable == byte.class || provable == short.class) {
return 0; // never needs a guard, as it's assignable to int
} else if(provable == char.class) {
@ -215,6 +222,12 @@ public final class OptimisticReturnFilters {
throw new UnwarrantedOptimismException(arg, programPoint, Type.OBJECT);
}
@SuppressWarnings("unused")
private static int ensureInt(final int programPoint) {
// Turns a void into UNDEFINED
throw new UnwarrantedOptimismException(ScriptRuntime.UNDEFINED, programPoint, Type.OBJECT);
}
private static long ensureLong(final double arg, final int programPoint) {
if (JSType.isRepresentableAsLong(arg) && !JSType.isNegativeZero(arg)) {
return (long)arg;

View file

@ -73,11 +73,8 @@ public abstract class Property {
/** Is parameter accessed thru arguments? */
public static final int HAS_ARGUMENTS = 1 << 4;
/** Can this property be undefined? */
public static final int CAN_BE_UNDEFINED = 1 << 5;
/** Is this a function declaration property ? */
public static final int IS_FUNCTION_DECLARATION = 1 << 6;
public static final int IS_FUNCTION_DECLARATION = 1 << 5;
/**
* Is this is a primitive field given to us by Nasgen, i.e.
@ -85,7 +82,7 @@ public abstract class Property {
* is narrower than object, e.g. Math.PI which is declared
* as a double
*/
public static final int IS_NASGEN_PRIMITIVE = 1 << 7;
public static final int IS_NASGEN_PRIMITIVE = 1 << 6;
/** Is this property bound to a receiver? This means get/set operations will be delegated to
* a statically defined object instead of the object passed as callsite parameter. */
@ -653,18 +650,6 @@ public abstract class Property {
return false;
}
/**
* Check whether this property can be primitive. This is a conservative
* analysis result, so {@code true} might mean that it can still be
* defined, but it will never say that a property can not be undefined
* if it can
*
* @return can be undefined status
*/
public boolean canBeUndefined() {
return (flags & CAN_BE_UNDEFINED) == CAN_BE_UNDEFINED;
}
/**
* Check whether this property represents a function declaration.
* @return whether this property is a function declaration or not.

View file

@ -36,7 +36,6 @@ import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import jdk.internal.dynalink.support.NameCodec;
import jdk.nashorn.internal.codegen.ApplySpecialization;
import jdk.nashorn.internal.codegen.CompilationEnvironment;
@ -50,6 +49,7 @@ import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.FunctionNode;
import jdk.nashorn.internal.ir.LexicalContext;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.objects.Global;
import jdk.nashorn.internal.parser.Parser;
import jdk.nashorn.internal.parser.Token;
import jdk.nashorn.internal.parser.TokenType;
@ -405,10 +405,6 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
return new ApplyToCallTransform(this, callSiteType);
}
private FunctionNode compileTypeSpecialization(final MethodType actualCallSiteType, final ScriptObject runtimeScope, final FunctionNodeTransform tr) {
return compile(actualCallSiteType, null, runtimeScope, "Type specialized compilation", tr);
}
private ParamTypeMap typeMap(final MethodType fnCallSiteType, final FunctionNodeTransform tr) {
if (isVariableArity() && !tr.wasTransformed()) {
return null;
@ -444,8 +440,17 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
compiler.install(fn);
// look up the rest of method
final MethodHandle mh = lookupWithExplicitType(fn, MethodType.methodType(fn.getReturnType().getTypeClass(), RewriteException.class));
return mh;
return lookupWithExplicitType(fn, MethodType.methodType(fn.getReturnType().getTypeClass(), RewriteException.class));
}
private FunctionNode compileTypeSpecialization(final MethodType actualCallSiteType, final ScriptObject runtimeScope, final FunctionNodeTransform tr) {
// We're creating an empty script object for holding local variables. AssignSymbols will populate it with
// explicit Undefined values for undefined local variables (see AssignSymbols#defineSymbol() and
// CompilationEnvironment#declareLocalSymbol()).
final ScriptObject locals = Global.newEmptyInstance();
locals.setProto(runtimeScope);
return compile(actualCallSiteType, null, locals, "Type specialized compilation", tr);
}
FunctionNode compile(final MethodType actualCallSiteType, final Map<Integer, Type> invalidatedProgramPoints, final ScriptObject runtimeScope, final String reason, final FunctionNodeTransform tr) {
@ -630,7 +635,6 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
final FunctionNode fn = compileTypeSpecialization(callSiteType, runtimeScope, new ApplyToCallTransform(this, callSiteType));
if (fn.hasOptimisticApplyToCall()) { //did the specialization work
existingBest = addCode(fn, callSiteType);
existingBest.setIsApplyToCall();
}
}
@ -711,14 +715,13 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
public boolean isGlobalSymbol(final FunctionNode functionNode, final String symbolName) {
RecompilableScriptFunctionData data = getScriptFunctionData(functionNode.getId());
assert data != null;
final RecompilableScriptFunctionData program = getProgram();
while (data != program) {
do {
if (data.hasInternalSymbol(symbolName)) {
return false;
}
data = data.getParent();
}
} while(data != null);
return true;
}

View file

@ -34,8 +34,9 @@ import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.invoke.MethodType;
import java.lang.reflect.Array;
import java.util.Arrays;
import jdk.nashorn.internal.codegen.CompilerConstants;
import jdk.nashorn.internal.codegen.CompilerConstants.Call;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.lookup.MethodHandleFactory;
@ -52,45 +53,61 @@ public class RewriteException extends Exception {
// Runtime scope in effect at the time of the compilation. Used to evaluate types of expressions and prevent overly
// optimistic assumptions (which will lead to unnecessary deoptimizing recompilations).
private ScriptObject runtimeScope;
//contents of bytecode slots
// Contents of bytecode slots
private Object[] byteCodeSlots;
private final int[] previousContinuationEntryPoints;
/** Methodhandle for getting the contents of the bytecode slots in the exception */
/** Call for getting the contents of the bytecode slots in the exception */
public static final Call GET_BYTECODE_SLOTS = virtualCallNoLookup(RewriteException.class, "getByteCodeSlots", Object[].class);
/** Methodhandle for getting the program point in the exception */
/** Call for getting the program point in the exception */
public static final Call GET_PROGRAM_POINT = virtualCallNoLookup(RewriteException.class, "getProgramPoint", int.class);
/** Methodhandle for getting the return value for the exception */
/** Call for getting the return value for the exception */
public static final Call GET_RETURN_VALUE = virtualCallNoLookup(RewriteException.class, "getReturnValueDestructive", Object.class);
/** Methodhandle for the populate array bootstrap */
/** Call for the populate array bootstrap */
public static final Call BOOTSTRAP = staticCallNoLookup(RewriteException.class, "populateArrayBootstrap", CallSite.class, Lookup.class, String.class, MethodType.class, int.class);
/** Methodhandle for populating an array with local variable state */
/** Call for populating an array with local variable state */
private static final Call POPULATE_ARRAY = staticCall(MethodHandles.lookup(), RewriteException.class, "populateArray", Object[].class, Object[].class, int.class, Object[].class);
/** Call for converting an array to a long array. */
public static final Call TO_LONG_ARRAY = staticCallNoLookup(RewriteException.class, "toLongArray", long[].class, Object.class, RewriteException.class);
/** Call for converting an array to a double array. */
public static final Call TO_DOUBLE_ARRAY = staticCallNoLookup(RewriteException.class, "toDoubleArray", double[].class, Object.class, RewriteException.class);
/** Call for converting an array to an object array. */
public static final Call TO_OBJECT_ARRAY = staticCallNoLookup(RewriteException.class, "toObjectArray", Object[].class, Object.class, RewriteException.class);
/** Call for converting an object to null if it can't be represented as an instance of a class. */
public static final Call INSTANCE_OR_NULL = staticCallNoLookup(RewriteException.class, "instanceOrNull", Object.class, Object.class, Class.class);
/** Call for asserting the length of an array. */
public static final Call ASSERT_ARRAY_LENGTH = staticCallNoLookup(RewriteException.class, "assertArrayLength", void.class, Object[].class, int.class);
/**
* Constructor for a rewrite exception thrown from an optimistic function.
* @param e the {@link UnwarrantedOptimismException} that triggered this exception.
* @param byteCodeSlots contents of local variable slots at the time of rewrite at the program point
* @param byteCodeSymbolNames byte code symbol names
* @param runtimeScope the runtime scope used for known type information when recompiling
* @param byteCodeSymbolNames the names of the variables in the {@code byteCodeSlots} parameter. The array might
* have less elements, and some elements might be unnamed (the name can be null). The information is provided in an
* effort to assist evaluation of expressions for their types by the compiler doing the deoptimizing recompilation,
* and can thus be incomplete - the more complete it is, the more expressions can be evaluated by the compiler, and
* the more unnecessary deoptimizing compilations can be avoided.
*/
public RewriteException(
final UnwarrantedOptimismException e,
final Object[] byteCodeSlots,
final String[] byteCodeSymbolNames,
final ScriptObject runtimeScope) {
this(e, byteCodeSlots, byteCodeSymbolNames, runtimeScope, null);
final String[] byteCodeSymbolNames) {
this(e, byteCodeSlots, byteCodeSymbolNames, null);
}
/**
* Constructor for a rewrite exception thrown from a rest-of method.
* @param e the {@link UnwarrantedOptimismException} that triggered this exception.
* @param byteCodeSymbolNames byte code symbol names
* @param byteCodeSlots contents of local variable slots at the time of rewrite at the program point
* @param runtimeScope the runtime scope used for known type information when recompiling
* @param byteCodeSymbolNames the names of the variables in the {@code byteCodeSlots} parameter. The array might
* have less elements, and some elements might be unnamed (the name can be null). The information is provided in an
* effort to assist evaluation of expressions for their types by the compiler doing the deoptimizing recompilation,
* and can thus be incomplete - the more complete it is, the more expressions can be evaluated by the compiler, and
* the more unnecessary deoptimizing compilations can be avoided.
* @param previousContinuationEntryPoints an array of continuation entry points that were already executed during
* one logical invocation of the function (a rest-of triggering a rest-of triggering a...)
*/
@ -98,11 +115,10 @@ public class RewriteException extends Exception {
final UnwarrantedOptimismException e,
final Object[] byteCodeSlots,
final String[] byteCodeSymbolNames,
final ScriptObject runtimeScope,
final int[] previousContinuationEntryPoints) {
super("", e, false, Context.DEBUG);
this.byteCodeSlots = byteCodeSlots;
this.runtimeScope = mergeSlotsWithScope(byteCodeSlots, byteCodeSymbolNames, runtimeScope);
this.runtimeScope = mergeSlotsWithScope(byteCodeSlots, byteCodeSymbolNames);
this.previousContinuationEntryPoints = previousContinuationEntryPoints;
}
@ -122,14 +138,18 @@ public class RewriteException extends Exception {
return new ConstantCallSite(mh);
}
private static ScriptObject mergeSlotsWithScope(final Object[] byteCodeSlots, final String[] byteCodeSymbolNames,
final ScriptObject runtimeScope) {
private static ScriptObject mergeSlotsWithScope(final Object[] byteCodeSlots, final String[] byteCodeSymbolNames) {
final ScriptObject locals = Global.newEmptyInstance();
final int l = Math.min(byteCodeSlots.length, byteCodeSymbolNames.length);
ScriptObject runtimeScope = null;
final String scopeName = CompilerConstants.SCOPE.symbolName();
for(int i = 0; i < l; ++i) {
final String name = byteCodeSymbolNames[i];
final Object value = byteCodeSlots[i];
if(name != null) {
if(scopeName.equals(name)) {
assert runtimeScope == null;
runtimeScope = (ScriptObject)value;
} else if(name != null) {
locals.set(name, value, true);
}
}
@ -150,6 +170,114 @@ public class RewriteException extends Exception {
return arrayToBePopluated;
}
/**
* Continuation handler calls this method when a local variable carried over into the continuation is expected to be
* a long array in the continued method. Normally, it will also be a long array in the original (interrupted by
* deoptimization) method, but it can actually be an int array that underwent widening in the new code version.
* @param obj the object that has to be converted into a long array
* @param e the exception being processed
* @return a long array
*/
public static long[] toLongArray(final Object obj, final RewriteException e) {
if(obj instanceof long[]) {
return (long[])obj;
}
assert obj instanceof int[];
final int[] in = (int[])obj;
final long[] out = new long[in.length];
for(int i = 0; i < in.length; ++i) {
out[i] = in[i];
}
return e.replaceByteCodeValue(in, out);
}
/**
* Continuation handler calls this method when a local variable carried over into the continuation is expected to be
* a double array in the continued method. Normally, it will also be a double array in the original (interrupted by
* deoptimization) method, but it can actually be an int or long array that underwent widening in the new code version.
* @param obj the object that has to be converted into a double array
* @param e the exception being processed
* @return a double array
*/
public static double[] toDoubleArray(final Object obj, final RewriteException e) {
if(obj instanceof double[]) {
return (double[])obj;
}
assert obj instanceof int[] || obj instanceof long[];
final int l = Array.getLength(obj);
final double[] out = new double[l];
for(int i = 0; i < l; ++i) {
out[i] = Array.getDouble(obj, i);
}
return e.replaceByteCodeValue(obj, out);
}
/**
* Continuation handler calls this method when a local variable carried over into the continuation is expected to be
* an Object array in the continued method. Normally, it will also be an Object array in the original (interrupted by
* deoptimization) method, but it can actually be an int, long, or double array that underwent widening in the new
* code version.
* @param obj the object that has to be converted into an Object array
* @param e the exception being processed
* @return an Object array
*/
public static Object[] toObjectArray(final Object obj, final RewriteException e) {
if(obj instanceof Object[]) {
return (Object[])obj;
}
assert obj instanceof int[] || obj instanceof long[] || obj instanceof double[] : obj.getClass().getName();
final int l = Array.getLength(obj);
final Object[] out = new Object[l];
for(int i = 0; i < l; ++i) {
out[i] = Array.get(obj, i);
}
return e.replaceByteCodeValue(obj, out);
}
/**
* Continuation handler calls this method when a local variable carried over into the continuation is expected to
* have a certain type, but the value can have a different type coming from the deoptimized method as it was a dead
* store. If we had precise liveness analysis, we wouldn't need this.
* @param obj the object inspected for being of a particular type
* @param clazz the type the object must belong to
* @return the object if it belongs to the type, or null otherwise
*/
public static Object instanceOrNull(final Object obj, final Class<?> clazz) {
return clazz.isInstance(obj) ? obj : null;
}
/**
* Asserts the length of an array. Invoked from continuation handler only when running with assertions enabled.
* The array can, in fact, have more elements than asserted, but they must all have Undefined as their value. The
* method does not test for the array having less elements than asserted, as those would already have caused an
* {@code ArrayIndexOutOfBoundsException} to be thrown as the continuation handler attempts to access the missing
* elements.
* @param arr the array
* @param length the asserted length
*/
public static void assertArrayLength(final Object[] arr, final int length) {
for(int i = arr.length; i-- > length;) {
if(arr[i] != ScriptRuntime.UNDEFINED) {
throw new AssertionError(String.format("Expected array length %d, but it is %d", length, i + 1));
}
}
}
private <T> T replaceByteCodeValue(final Object in, final T out) {
for(int i = 0; i < byteCodeSlots.length; ++i) {
if(byteCodeSlots[i] == in) {
byteCodeSlots[i] = out;
}
}
return out;
}
private UnwarrantedOptimismException getUOE() {
return (UnwarrantedOptimismException)getCause();
}
@ -218,6 +346,8 @@ public class RewriteException extends Exception {
String str = returnValue.toString();
if (returnValue instanceof String) {
str = '\'' + str + '\'';
} else if (returnValue instanceof Double) {
str = str + 'd';
} else if (returnValue instanceof Long) {
str = str + 'l';
}

View file

@ -27,14 +27,11 @@ package jdk.nashorn.internal.runtime;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
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;
@ -184,9 +181,6 @@ 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;
@ -254,17 +248,6 @@ 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());
}
}
String dir = null;
String func = null;
final String pc = options.getString("print.code");
@ -323,18 +306,6 @@ public final class ScriptEnvironment {
this._loggers = lo == null ? new HashMap<String, LoggerInfo>() : lo.getLoggers();
}
/**
* 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

@ -25,6 +25,7 @@
package jdk.nashorn.internal.runtime;
import static jdk.nashorn.internal.codegen.CompilerConstants.staticCallNoLookup;
import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCall;
import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup;
import static jdk.nashorn.internal.codegen.ObjectClassGenerator.OBJECT_FIELDS_ONLY;
@ -62,7 +63,6 @@ import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import jdk.internal.dynalink.CallSiteDescriptor;
import jdk.internal.dynalink.linker.GuardedInvocation;
import jdk.internal.dynalink.linker.LinkRequest;
@ -185,7 +185,7 @@ public abstract class ScriptObject implements PropertyAccess {
public static final Call GET_PROTO_DEPTH = virtualCallNoLookup(ScriptObject.class, "getProto", ScriptObject.class, int.class);
/** Method handle for setting the proto of a ScriptObject */
public static final Call SET_PROTO = virtualCallNoLookup(ScriptObject.class, "setInitialProto", void.class, ScriptObject.class);
public static final Call SET_GLOBAL_OBJECT_PROTO = staticCallNoLookup(ScriptObject.class, "setGlobalObjectProto", void.class, ScriptObject.class);
/** Method handle for setting the proto of a ScriptObject after checking argument */
public static final Call SET_PROTO_CHECK = virtualCallNoLookup(ScriptObject.class, "setProtoCheck", void.class, Object.class);
@ -1261,6 +1261,14 @@ public abstract class ScriptObject implements PropertyAccess {
this.proto = initialProto;
}
/**
* Invoked from generated bytecode to initialize the prototype of object literals to the global Object prototype.
* @param obj the object literal that needs to have its prototype initialized to the global Object prototype.
*/
public static void setGlobalObjectProto(final ScriptObject obj) {
obj.setInitialProto(Global.objectPrototype());
}
/**
* Set the __proto__ of an object with checks.
* @param newProto Prototype to set.

View file

@ -39,7 +39,7 @@ public final class UnwarrantedOptimismException extends RuntimeException {
public static final int INVALID_PROGRAM_POINT = -1;
/** The value for the first ordinary program point */
public static final int FIRST_PROGRAM_POINT = 0;
public static final int FIRST_PROGRAM_POINT = 1;
private Object returnValue;
private final int programPoint;
@ -86,6 +86,8 @@ public final class UnwarrantedOptimismException extends RuntimeException {
*/
public UnwarrantedOptimismException(final Object returnValue, final int programPoint, final Type returnType) {
super("", null, false, Context.DEBUG);
assert returnType != Type.OBJECT || returnValue == null || !Type.typeFor(returnValue.getClass()).isNumeric();
assert returnType != Type.INT;
this.returnValue = returnValue;
this.programPoint = programPoint;
this.returnType = returnType;

View file

@ -33,7 +33,6 @@ import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.invoke.MethodType;
import jdk.internal.dynalink.CallSiteDescriptor;
import jdk.internal.dynalink.DynamicLinker;
import jdk.internal.dynalink.DynamicLinkerFactory;
@ -197,6 +196,9 @@ public final class Bootstrap {
case "idiv":
mh = JSType.DIV_EXACT.methodHandle();
break;
case "irem":
mh = JSType.REM_EXACT.methodHandle();
break;
case "ineg":
mh = JSType.NEGATE_EXACT.methodHandle();
break;
@ -212,6 +214,9 @@ public final class Bootstrap {
case "ldiv":
mh = JSType.DIV_EXACT_LONG.methodHandle();
break;
case "lrem":
mh = JSType.REM_EXACT_LONG.methodHandle();
break;
case "lneg":
mh = JSType.NEGATE_EXACT_LONG.methodHandle();
break;

View file

@ -30,7 +30,6 @@ import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.HashMap;
import java.util.Map;
import jdk.internal.dynalink.CallSiteDescriptor;
import jdk.internal.dynalink.linker.GuardedInvocation;
import jdk.internal.dynalink.linker.GuardedTypeConversion;
@ -72,7 +71,7 @@ final class JSObjectLinker implements TypeBasedGuardingDynamicLinker, GuardingTy
final GuardedInvocation inv;
if (self instanceof JSObject) {
inv = lookup(desc, request);
inv = lookup(desc);
} else {
throw new AssertionError(); // Should never reach here.
}
@ -96,7 +95,7 @@ final class JSObjectLinker implements TypeBasedGuardingDynamicLinker, GuardingTy
}
private static GuardedInvocation lookup(final CallSiteDescriptor desc, final LinkRequest request) {
private static GuardedInvocation lookup(final CallSiteDescriptor desc) {
final String operator = CallSiteDescriptorFactory.tokenizeOperators(desc).get(0);
final int c = desc.getNameTokenCount();

Some files were not shown because too many files have changed in this diff Show more