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

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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