This commit is contained in:
Lana Steuck 2014-09-11 14:30:00 -07:00
commit 87d4175a75
78 changed files with 1783 additions and 365 deletions

View File

@ -6,8 +6,7 @@ dist/*
/nbproject/private/ /nbproject/private/
private.xml private.xml
private.properties private.properties
webrev/* ^webrev
webrev.zip
.classpath .classpath
*.class *.class
*.clazz *.clazz

View File

@ -194,12 +194,12 @@ final class AssignSymbols extends NodeVisitor<LexicalContext> implements Loggabl
*/ */
private void acceptDeclarations(final FunctionNode functionNode, final Block body) { private void acceptDeclarations(final FunctionNode functionNode, final Block body) {
// This visitor will assign symbol to all declared variables, except "var" declarations in for loop initializers. // This visitor will assign symbol to all declared variables, except "var" declarations in for loop initializers.
//
body.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { body.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
@Override @Override
public boolean enterFunctionNode(final FunctionNode nestedFn) { protected boolean enterDefault(final Node node) {
// Don't descend into nested functions // Don't bother visiting expressions; var is a statement, it can't be inside an expression.
return false; // This will also prevent visiting nested functions (as FunctionNode is an expression).
return !(node instanceof Expression);
} }
@Override @Override
@ -443,12 +443,27 @@ final class AssignSymbols extends NodeVisitor<LexicalContext> implements Loggabl
if (lc.isFunctionBody()) { if (lc.isFunctionBody()) {
block.clearSymbols(); block.clearSymbols();
final FunctionNode fn = lc.getCurrentFunction();
if (isUnparsedFunction(fn)) {
// It's a skipped nested function. Just mark the symbols being used by it as being in use.
for(final String name: compiler.getScriptFunctionData(fn.getId()).getExternalSymbolNames()) {
nameIsUsed(name, null);
}
// Don't bother descending into it, it must be empty anyway.
assert block.getStatements().isEmpty();
return false;
}
enterFunctionBody(); enterFunctionBody();
} }
return true; return true;
} }
private boolean isUnparsedFunction(final FunctionNode fn) {
return compiler.isOnDemandCompilation() && fn != lc.getOutermostFunction();
}
@Override @Override
public boolean enterCatchNode(final CatchNode catchNode) { public boolean enterCatchNode(final CatchNode catchNode) {
final IdentNode exception = catchNode.getException(); final IdentNode exception = catchNode.getException();
@ -492,18 +507,13 @@ final class AssignSymbols extends NodeVisitor<LexicalContext> implements Loggabl
@Override @Override
public boolean enterFunctionNode(final FunctionNode functionNode) { 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); start(functionNode, false);
thisProperties.push(new HashSet<String>()); 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()) { if (functionNode.isDeclared()) {
// Can't use lc.getCurrentBlock() as we can have an outermost function in our lexical context that
// is not a program - it is a function being compiled on-demand.
final Iterator<Block> blocks = lc.getBlocks(); final Iterator<Block> blocks = lc.getBlocks();
if (blocks.hasNext()) { if (blocks.hasNext()) {
final IdentNode ident = functionNode.getIdent(); final IdentNode ident = functionNode.getIdent();
@ -511,6 +521,11 @@ final class AssignSymbols extends NodeVisitor<LexicalContext> implements Loggabl
} }
} }
// Every function has a body, even the ones skipped on reparse (they have an empty one). We're
// asserting this as even for those, enterBlock() must be invoked to correctly process symbols that
// are used in them.
assert functionNode.getBody() != null;
return true; return true;
} }
@ -533,7 +548,7 @@ final class AssignSymbols extends NodeVisitor<LexicalContext> implements Loggabl
/** /**
* This has to run before fix assignment types, store any type specializations for * 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 * parameters, then turn them into objects for the generic version of this method.
* *
* @param functionNode functionNode * @param functionNode functionNode
*/ */
@ -733,14 +748,20 @@ final class AssignSymbols extends NodeVisitor<LexicalContext> implements Loggabl
@Override @Override
public Node leaveBlock(final Block block) { public Node leaveBlock(final Block block) {
// It's not necessary to guard the marking of symbols as locals with this "if"condition for correctness, it's // It's not necessary to guard the marking of symbols as locals with this "if" condition for
// just an optimization -- runtime type calculation is not used when the compilation is not an on-demand // correctness, it's just an optimization -- runtime type calculation is not used when the compilation
// optimistic compilation, so we can skip locals marking then. // is not an on-demand optimistic compilation, so we can skip locals marking then.
if (compiler.useOptimisticTypes() && compiler.isOnDemandCompilation()) { if (compiler.useOptimisticTypes() && compiler.isOnDemandCompilation()) {
for (final Symbol symbol: block.getSymbols()) { // OTOH, we must not declare symbols from nested functions to be locals. As we're doing on-demand
if (!symbol.isScope()) { // compilation, and we're skipping parsing the function bodies for nested functions, this
assert symbol.isVar() || symbol.isParam(); // basically only means their parameters. It'd be enough to mistakenly declare to be a local a
compiler.declareLocalSymbol(symbol.getName()); // symbol in the outer function named the same as one of the parameters, though.
if (lc.getFunction(block) == lc.getOutermostFunction()) {
for (final Symbol symbol: block.getSymbols()) {
if (!symbol.isScope()) {
assert symbol.isVar() || symbol.isParam();
compiler.declareLocalSymbol(symbol.getName());
}
} }
} }
} }
@ -811,24 +832,45 @@ final class AssignSymbols extends NodeVisitor<LexicalContext> implements Loggabl
@Override @Override
public Node leaveFunctionNode(final FunctionNode functionNode) { public Node leaveFunctionNode(final FunctionNode functionNode) {
final FunctionNode finalizedFunction;
return markProgramBlock( if (isUnparsedFunction(functionNode)) {
finalizedFunction = functionNode;
} else {
finalizedFunction =
markProgramBlock(
removeUnusedSlots( removeUnusedSlots(
createSyntheticInitializers( createSyntheticInitializers(
finalizeParameters( finalizeParameters(
lc.applyTopFlags(functionNode)))) lc.applyTopFlags(functionNode))))
.setThisProperties(lc, thisProperties.pop().size()) .setThisProperties(lc, thisProperties.pop().size()));
.setState(lc, CompilationState.SYMBOLS_ASSIGNED)); }
return finalizedFunction.setState(lc, CompilationState.SYMBOLS_ASSIGNED);
} }
@Override @Override
public Node leaveIdentNode(final IdentNode identNode) { public Node leaveIdentNode(final IdentNode identNode) {
final String name = identNode.getName();
if (identNode.isPropertyName()) { if (identNode.isPropertyName()) {
return identNode; return identNode;
} }
final Symbol symbol = nameIsUsed(identNode.getName(), identNode);
if (!identNode.isInitializedHere()) {
symbol.increaseUseCount();
}
IdentNode newIdentNode = identNode.setSymbol(symbol);
// If a block-scoped var is used before its declaration mark it as dead.
// We can only statically detect this for local vars, cross-function symbols require runtime checks.
if (symbol.isBlockScoped() && !symbol.hasBeenDeclared() && !identNode.isDeclaredHere() && isLocal(lc.getCurrentFunction(), symbol)) {
newIdentNode = newIdentNode.markDead();
}
return end(newIdentNode);
}
private Symbol nameIsUsed(final String name, final IdentNode origin) {
final Block block = lc.getCurrentBlock(); final Block block = lc.getCurrentBlock();
Symbol symbol = findSymbol(block, name); Symbol symbol = findSymbol(block, name);
@ -847,24 +889,11 @@ final class AssignSymbols extends NodeVisitor<LexicalContext> implements Loggabl
maybeForceScope(symbol); maybeForceScope(symbol);
} else { } else {
log.info("No symbol exists. Declare as global: ", name); log.info("No symbol exists. Declare as global: ", name);
symbol = defineSymbol(block, name, identNode, IS_GLOBAL | IS_SCOPE); symbol = defineSymbol(block, name, origin, IS_GLOBAL | IS_SCOPE);
} }
functionUsesSymbol(symbol); functionUsesSymbol(symbol);
return symbol;
if (!identNode.isInitializedHere()) {
symbol.increaseUseCount();
}
IdentNode newIdentNode = identNode.setSymbol(symbol);
// If a block-scoped var is used before its declaration mark it as dead.
// We can only statically detect this for local vars, cross-function symbols require runtime checks.
if (symbol.isBlockScoped() && !symbol.hasBeenDeclared() && !identNode.isDeclaredHere() && isLocal(lc.getCurrentFunction(), symbol)) {
newIdentNode = newIdentNode.markDead();
}
return end(newIdentNode);
} }
@Override @Override
@ -912,7 +941,6 @@ final class AssignSymbols extends NodeVisitor<LexicalContext> implements Loggabl
return functionNode; return functionNode;
} }
assert functionNode.getId() == 1;
return functionNode.setBody(lc, functionNode.getBody().setFlag(lc, Block.IS_GLOBAL_SCOPE)); return functionNode.setBody(lc, functionNode.getBody().setFlag(lc, Block.IS_GLOBAL_SCOPE));
} }

View File

@ -59,6 +59,7 @@ import java.security.PrivilegedAction;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import jdk.internal.org.objectweb.asm.ClassWriter; import jdk.internal.org.objectweb.asm.ClassWriter;
import jdk.internal.org.objectweb.asm.MethodVisitor; import jdk.internal.org.objectweb.asm.MethodVisitor;
import jdk.internal.org.objectweb.asm.util.TraceClassVisitor; import jdk.internal.org.objectweb.asm.util.TraceClassVisitor;
@ -135,6 +136,16 @@ public class ClassEmitter implements Emitter {
/** Set of constants access methods required. */ /** Set of constants access methods required. */
private Set<Class<?>> constantMethodNeeded; private Set<Class<?>> constantMethodNeeded;
private int methodCount;
private int initCount;
private int clinitCount;
private int fieldCount;
private final Set<String> methodNames;
/** /**
* Constructor - only used internally in this class as it breaks * Constructor - only used internally in this class as it breaks
* abstraction towards ASM or other code generator below * abstraction towards ASM or other code generator below
@ -146,6 +157,11 @@ public class ClassEmitter implements Emitter {
this.context = context; this.context = context;
this.cw = cw; this.cw = cw;
this.methodsStarted = new HashSet<>(); this.methodsStarted = new HashSet<>();
this.methodNames = new HashSet<>();
}
public Set<String> getMethodNames() {
return methodNames;
} }
/** /**
@ -208,6 +224,38 @@ public class ClassEmitter implements Emitter {
return unitClassName; return unitClassName;
} }
/**
* Get the method count, including init and clinit methods
* @return method count
*/
public int getMethodCount() {
return methodCount;
}
/**
* Get the clinit count
* @return clinit count
*/
public int getClinitCount() {
return clinitCount;
}
/**
* Get the init count
* @return init count
*/
public int getInitCount() {
return initCount;
}
/**
* Get the field count
* @return field count
*/
public int getFieldCount() {
return fieldCount;
}
/** /**
* Convert a binary name to a package/class name. * Convert a binary name to a package/class name.
* *
@ -359,9 +407,16 @@ public class ClassEmitter implements Emitter {
*/ */
@Override @Override
public void end() { public void end() {
assert classStarted; assert classStarted : "class not started for " + unitClassName;
if (unitClassName != null) { if (unitClassName != null) {
final MethodEmitter initMethod = init(EnumSet.of(Flag.PRIVATE));
initMethod.begin();
initMethod.load(Type.OBJECT, 0);
initMethod.newInstance(jdk.nashorn.internal.scripts.JS.class);
initMethod.returnVoid();
initMethod.end();
defineCommonUtilities(); defineCommonUtilities();
} }
@ -419,6 +474,8 @@ public class ClassEmitter implements Emitter {
} }
SplitMethodEmitter method(final SplitNode splitNode, final String methodName, final Class<?> rtype, final Class<?>... ptypes) { SplitMethodEmitter method(final SplitNode splitNode, final String methodName, final Class<?> rtype, final Class<?>... ptypes) {
methodCount++;
methodNames.add(methodName);
return new SplitMethodEmitter(this, methodVisitor(EnumSet.of(Flag.PUBLIC, Flag.STATIC), methodName, rtype, ptypes), splitNode); return new SplitMethodEmitter(this, methodVisitor(EnumSet.of(Flag.PUBLIC, Flag.STATIC), methodName, rtype, ptypes), splitNode);
} }
@ -446,6 +503,8 @@ public class ClassEmitter implements Emitter {
* @return method emitter to use for weaving this method * @return method emitter to use for weaving this method
*/ */
MethodEmitter method(final EnumSet<Flag> methodFlags, final String methodName, final Class<?> rtype, final Class<?>... ptypes) { MethodEmitter method(final EnumSet<Flag> methodFlags, final String methodName, final Class<?> rtype, final Class<?>... ptypes) {
methodCount++;
methodNames.add(methodName);
return new MethodEmitter(this, methodVisitor(methodFlags, methodName, rtype, ptypes)); return new MethodEmitter(this, methodVisitor(methodFlags, methodName, rtype, ptypes));
} }
@ -471,6 +530,8 @@ public class ClassEmitter implements Emitter {
* @return method emitter to use for weaving this method * @return method emitter to use for weaving this method
*/ */
MethodEmitter method(final EnumSet<Flag> methodFlags, final String methodName, final String descriptor) { MethodEmitter method(final EnumSet<Flag> methodFlags, final String methodName, final String descriptor) {
methodCount++;
methodNames.add(methodName);
return new MethodEmitter(this, cw.visitMethod(Flag.getValue(methodFlags), methodName, descriptor, null, null)); return new MethodEmitter(this, cw.visitMethod(Flag.getValue(methodFlags), methodName, descriptor, null, null));
} }
@ -481,6 +542,8 @@ public class ClassEmitter implements Emitter {
* @return method emitter to use for weaving this method * @return method emitter to use for weaving this method
*/ */
MethodEmitter method(final FunctionNode functionNode) { MethodEmitter method(final FunctionNode functionNode) {
methodCount++;
methodNames.add(functionNode.getName());
final FunctionSignature signature = new FunctionSignature(functionNode); final FunctionSignature signature = new FunctionSignature(functionNode);
final MethodVisitor mv = cw.visitMethod( final MethodVisitor mv = cw.visitMethod(
ACC_PUBLIC | ACC_STATIC | (functionNode.isVarArg() ? ACC_VARARGS : 0), ACC_PUBLIC | ACC_STATIC | (functionNode.isVarArg() ? ACC_VARARGS : 0),
@ -499,6 +562,8 @@ public class ClassEmitter implements Emitter {
* @return method emitter to use for weaving this method * @return method emitter to use for weaving this method
*/ */
MethodEmitter restOfMethod(final FunctionNode functionNode) { MethodEmitter restOfMethod(final FunctionNode functionNode) {
methodCount++;
methodNames.add(functionNode.getName());
final MethodVisitor mv = cw.visitMethod( final MethodVisitor mv = cw.visitMethod(
ACC_PUBLIC | ACC_STATIC, ACC_PUBLIC | ACC_STATIC,
functionNode.getName(), functionNode.getName(),
@ -516,6 +581,7 @@ public class ClassEmitter implements Emitter {
* @return method emitter to use for weaving <clinit> * @return method emitter to use for weaving <clinit>
*/ */
MethodEmitter clinit() { MethodEmitter clinit() {
clinitCount++;
return method(EnumSet.of(Flag.STATIC), CLINIT.symbolName(), void.class); return method(EnumSet.of(Flag.STATIC), CLINIT.symbolName(), void.class);
} }
@ -525,6 +591,7 @@ public class ClassEmitter implements Emitter {
* @return method emitter to use for weaving <init>()V * @return method emitter to use for weaving <init>()V
*/ */
MethodEmitter init() { MethodEmitter init() {
initCount++;
return method(INIT.symbolName(), void.class); return method(INIT.symbolName(), void.class);
} }
@ -535,6 +602,7 @@ public class ClassEmitter implements Emitter {
* @return method emitter to use for weaving <init>()V * @return method emitter to use for weaving <init>()V
*/ */
MethodEmitter init(final Class<?>... ptypes) { MethodEmitter init(final Class<?>... ptypes) {
initCount++;
return method(INIT.symbolName(), void.class, ptypes); return method(INIT.symbolName(), void.class, ptypes);
} }
@ -547,6 +615,7 @@ public class ClassEmitter implements Emitter {
* @return method emitter to use for weaving <init>(...)V * @return method emitter to use for weaving <init>(...)V
*/ */
MethodEmitter init(final EnumSet<Flag> flags, final Class<?>... ptypes) { MethodEmitter init(final EnumSet<Flag> flags, final Class<?>... ptypes) {
initCount++;
return method(flags, INIT.symbolName(), void.class, ptypes); return method(flags, INIT.symbolName(), void.class, ptypes);
} }
@ -561,6 +630,7 @@ public class ClassEmitter implements Emitter {
* @see ClassEmitter.Flag * @see ClassEmitter.Flag
*/ */
final void field(final EnumSet<Flag> fieldFlags, final String fieldName, final Class<?> fieldType, final Object value) { final void field(final EnumSet<Flag> fieldFlags, final String fieldName, final Class<?> fieldType, final Object value) {
fieldCount++;
cw.visitField(Flag.getValue(fieldFlags), fieldName, typeDescriptor(fieldType), null, value).visitEnd(); cw.visitField(Flag.getValue(fieldFlags), fieldName, typeDescriptor(fieldType), null, value).visitEnd();
} }

View File

@ -1622,9 +1622,18 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
@Override @Override
protected void evaluate() { protected void evaluate() {
method.load(ITERATOR_TYPE, iterSlot); new OptimisticOperation((Optimistic)forNode.getInit(), TypeBounds.UNBOUNDED) {
// TODO: optimistic for-in iteration @Override
method.invoke(interfaceCallNoLookup(ITERATOR_CLASS, "next", Object.class)); void loadStack() {
method.load(ITERATOR_TYPE, iterSlot);
}
@Override
void consumeStack() {
method.invoke(interfaceCallNoLookup(ITERATOR_CLASS, "next", Object.class));
convertOptimisticReturnValue();
}
}.emit();
} }
}.store(); }.store();
body.accept(this); body.accept(this);

View File

@ -31,6 +31,7 @@ import java.util.Collections;
import java.util.Deque; import java.util.Deque;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import jdk.nashorn.internal.IntDeque; import jdk.nashorn.internal.IntDeque;
import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.Block; import jdk.nashorn.internal.ir.Block;
@ -158,7 +159,9 @@ final class CodeGeneratorLexicalContext extends LexicalContext {
CompileUnit popCompileUnit(final CompileUnit oldUnit) { CompileUnit popCompileUnit(final CompileUnit oldUnit) {
assert compileUnits.peek() == oldUnit; assert compileUnits.peek() == oldUnit;
compileUnits.pop(); final CompileUnit unit = compileUnits.pop();
assert unit.hasCode() : "compile unit popped without code";
unit.setUsed();
return compileUnits.isEmpty() ? null : compileUnits.peek(); return compileUnits.isEmpty() ? null : compileUnits.peek();
} }

View File

@ -48,6 +48,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.Set; import java.util.Set;
import jdk.nashorn.internal.AssertsEnabled; import jdk.nashorn.internal.AssertsEnabled;
import jdk.nashorn.internal.codegen.Compiler.CompilationPhases; import jdk.nashorn.internal.codegen.Compiler.CompilationPhases;
import jdk.nashorn.internal.ir.FunctionNode; import jdk.nashorn.internal.ir.FunctionNode;
@ -300,6 +301,7 @@ enum CompilationPhase {
} }
}, },
/** /**
* Reuse compile units, if they are already present. We are using the same compiler * Reuse compile units, if they are already present. We are using the same compiler
* to recompile stuff * to recompile stuff
@ -334,6 +336,8 @@ enum CompilationPhase {
if (phases.isRestOfCompilation()) { if (phases.isRestOfCompilation()) {
sb.append("$restOf"); sb.append("$restOf");
} }
//it's ok to not copy the initCount, methodCount and clinitCount here, as codegen is what
//fills those out anyway. Thus no need for a copy constructor
final CompileUnit newUnit = compiler.createCompileUnit(sb.toString(), oldUnit.getWeight()); final CompileUnit newUnit = compiler.createCompileUnit(sb.toString(), oldUnit.getWeight());
log.fine("Creating new compile unit ", oldUnit, " => ", newUnit); log.fine("Creating new compile unit ", oldUnit, " => ", newUnit);
map.put(oldUnit, newUnit); map.put(oldUnit, newUnit);
@ -430,8 +434,14 @@ enum CompilationPhase {
FunctionNode newFunctionNode = fn; FunctionNode newFunctionNode = fn;
//root class is special, as it is bootstrapped from createProgramFunction, thus it's skipped
//in CodeGeneration - the rest can be used as a working "is compile unit used" metric
fn.getCompileUnit().setUsed();
compiler.getLogger().fine("Starting bytecode generation for ", quote(fn.getName()), " - restOf=", phases.isRestOfCompilation()); compiler.getLogger().fine("Starting bytecode generation for ", quote(fn.getName()), " - restOf=", phases.isRestOfCompilation());
final CodeGenerator codegen = new CodeGenerator(compiler, phases.isRestOfCompilation() ? compiler.getContinuationEntryPoints() : null); final CodeGenerator codegen = new CodeGenerator(compiler, phases.isRestOfCompilation() ? compiler.getContinuationEntryPoints() : null);
try { try {
// Explicitly set BYTECODE_GENERATED here; it can not be set in case of skipping codegen for :program // Explicitly set BYTECODE_GENERATED here; it can not be set in case of skipping codegen for :program
// in the lazy + optimistic world. See CodeGenerator.skipFunction(). // in the lazy + optimistic world. See CodeGenerator.skipFunction().
@ -455,12 +465,18 @@ enum CompilationPhase {
final ClassEmitter classEmitter = compileUnit.getClassEmitter(); final ClassEmitter classEmitter = compileUnit.getClassEmitter();
classEmitter.end(); classEmitter.end();
if (!compileUnit.isUsed()) {
compiler.getLogger().fine("Skipping unused compile unit ", compileUnit);
continue;
}
final byte[] bytecode = classEmitter.toByteArray(); final byte[] bytecode = classEmitter.toByteArray();
assert bytecode != null; assert bytecode != null;
final String className = compileUnit.getUnitClassName(); final String className = compileUnit.getUnitClassName();
compiler.addClass(className, bytecode); //classes are only added to the bytecode map if compile unit is used
compiler.addClass(className, bytecode); CompileUnit.increaseEmitCount();
// should we verify the generated code? // should we verify the generated code?
if (senv._verify_code) { if (senv._verify_code) {
@ -536,6 +552,9 @@ enum CompilationPhase {
// initialize function in the compile units // initialize function in the compile units
for (final CompileUnit unit : compiler.getCompileUnits()) { for (final CompileUnit unit : compiler.getCompileUnits()) {
if (!unit.isUsed()) {
continue;
}
unit.setCode(installedClasses.get(unit.getUnitClassName())); unit.setCode(installedClasses.get(unit.getUnitClassName()));
} }

View File

@ -42,6 +42,10 @@ public final class CompileUnit implements Comparable<CompileUnit> {
private Class<?> clazz; private Class<?> clazz;
private boolean isUsed;
private static int emittedUnitCount;
CompileUnit(final String className, final ClassEmitter classEmitter, final long initialWeight) { CompileUnit(final String className, final ClassEmitter classEmitter, final long initialWeight) {
this.className = className; this.className = className;
this.weight = initialWeight; this.weight = initialWeight;
@ -52,6 +56,33 @@ public final class CompileUnit implements Comparable<CompileUnit> {
return new TreeSet<>(); return new TreeSet<>();
} }
static void increaseEmitCount() {
emittedUnitCount++;
}
public static int getEmittedUnitCount() {
return emittedUnitCount;
}
/**
* Check if this compile unit is used
* @return true if tagged as in use - i.e active code that needs to be generated
*/
public boolean isUsed() {
return isUsed;
}
public boolean hasCode() {
return (classEmitter.getMethodCount() - classEmitter.getInitCount() - classEmitter.getClinitCount()) > 0;
}
/**
* Tag this compile unit as used
*/
public void setUsed() {
this.isUsed = true;
}
/** /**
* Return the class that contains the code for this unit, null if not * Return the class that contains the code for this unit, null if not
* generated yet * generated yet
@ -121,7 +152,8 @@ public final class CompileUnit implements Comparable<CompileUnit> {
@Override @Override
public String toString() { public String toString() {
return "[CompileUnit className=" + shortName(className) + " weight=" + weight + '/' + Splitter.SPLIT_THRESHOLD + ']'; final String methods = classEmitter != null ? classEmitter.getMethodNames().toString() : "<anon>";
return "[CompileUnit className=" + shortName(className) + " weight=" + weight + '/' + Splitter.SPLIT_THRESHOLD + " hasCode=" + methods + ']';
} }
@Override @Override

View File

@ -38,7 +38,6 @@ import java.lang.invoke.MethodType;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.EnumSet;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
@ -51,8 +50,8 @@ import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.logging.Level; import java.util.logging.Level;
import jdk.internal.dynalink.support.NameCodec; import jdk.internal.dynalink.support.NameCodec;
import jdk.nashorn.internal.codegen.ClassEmitter.Flag;
import jdk.nashorn.internal.codegen.types.Type; 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;
import jdk.nashorn.internal.ir.Optimistic; import jdk.nashorn.internal.ir.Optimistic;
import jdk.nashorn.internal.ir.debug.ClassHistogramElement; import jdk.nashorn.internal.ir.debug.ClassHistogramElement;
@ -65,6 +64,7 @@ import jdk.nashorn.internal.runtime.ParserException;
import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData; import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData;
import jdk.nashorn.internal.runtime.ScriptEnvironment; import jdk.nashorn.internal.runtime.ScriptEnvironment;
import jdk.nashorn.internal.runtime.ScriptObject; import jdk.nashorn.internal.runtime.ScriptObject;
import jdk.nashorn.internal.runtime.ScriptRuntime;
import jdk.nashorn.internal.runtime.Source; import jdk.nashorn.internal.runtime.Source;
import jdk.nashorn.internal.runtime.logging.DebugLogger; import jdk.nashorn.internal.runtime.logging.DebugLogger;
import jdk.nashorn.internal.runtime.logging.Loggable; import jdk.nashorn.internal.runtime.logging.Loggable;
@ -248,6 +248,15 @@ public final class Compiler implements Loggable {
return new CompilationPhases(desc, list.toArray(new CompilationPhase[list.size()])); return new CompilationPhases(desc, list.toArray(new CompilationPhase[list.size()]));
} }
@SuppressWarnings("unused") //TODO I'll use this soon
private CompilationPhases replace(final CompilationPhase phase, final CompilationPhase newPhase) {
final LinkedList<CompilationPhase> list = new LinkedList<>();
for (final CompilationPhase p : phases) {
list.add(p == phase ? newPhase : p);
}
return new CompilationPhases(desc, list.toArray(new CompilationPhase[list.size()]));
}
private CompilationPhases addAfter(final CompilationPhase phase, final CompilationPhase newPhase) { private CompilationPhases addAfter(final CompilationPhase phase, final CompilationPhase newPhase) {
final LinkedList<CompilationPhase> list = new LinkedList<>(); final LinkedList<CompilationPhase> list = new LinkedList<>();
for (final CompilationPhase p : phases) { for (final CompilationPhase p : phases) {
@ -473,6 +482,19 @@ public final class Compiler implements Loggable {
return typeEvaluator.getOptimisticType(node); return typeEvaluator.getOptimisticType(node);
} }
/**
* Returns true if the expression can be safely evaluated, and its value is an object known to always use
* String as the type of its property names retrieved through
* {@link ScriptRuntime#toPropertyIterator(Object)}. It is used to avoid optimistic assumptions about its
* property name types.
* @param expr the expression to test
* @return true if the expression can be safely evaluated, and its value is an object known to always use
* String as the type of its property iterators.
*/
boolean hasStringPropertyIterator(final Expression expr) {
return typeEvaluator.hasStringPropertyIterator(expr);
}
void addInvalidatedProgramPoint(final int programPoint, final Type type) { void addInvalidatedProgramPoint(final int programPoint, final Type type) {
invalidatedProgramPoints.put(programPoint, type); invalidatedProgramPoints.put(programPoint, type);
} }
@ -675,16 +697,8 @@ public final class Compiler implements Loggable {
CompileUnit createCompileUnit(final String unitClassName, final long initialWeight) { CompileUnit createCompileUnit(final String unitClassName, final long initialWeight) {
final ClassEmitter classEmitter = new ClassEmitter(context, sourceName, unitClassName, isStrict()); final ClassEmitter classEmitter = new ClassEmitter(context, sourceName, unitClassName, isStrict());
final CompileUnit compileUnit = new CompileUnit(unitClassName, classEmitter, initialWeight); final CompileUnit compileUnit = new CompileUnit(unitClassName, classEmitter, initialWeight);
classEmitter.begin(); classEmitter.begin();
final MethodEmitter initMethod = classEmitter.init(EnumSet.of(Flag.PRIVATE));
initMethod.begin();
initMethod.load(Type.OBJECT, 0);
initMethod.newInstance(jdk.nashorn.internal.scripts.JS.class);
initMethod.returnVoid();
initMethod.end();
return compileUnit; return compileUnit;
} }
@ -722,13 +736,6 @@ public final class Compiler implements Loggable {
return name.replace('/', '.'); return name.replace('/', '.');
} }
RecompilableScriptFunctionData getProgram() {
if (compiledFunction == null) {
return null;
}
return compiledFunction.getProgram();
}
RecompilableScriptFunctionData getScriptFunctionData(final int functionId) { RecompilableScriptFunctionData getScriptFunctionData(final int functionId) {
return compiledFunction == null ? null : compiledFunction.getScriptFunctionData(functionId); return compiledFunction == null ? null : compiledFunction.getScriptFunctionData(functionId);
} }

View File

@ -25,8 +25,6 @@
package jdk.nashorn.internal.codegen; package jdk.nashorn.internal.codegen;
import static jdk.nashorn.internal.codegen.ObjectClassGenerator.getClassName;
import static jdk.nashorn.internal.codegen.ObjectClassGenerator.getPaddedFieldCount;
import static jdk.nashorn.internal.runtime.logging.DebugLogger.quote; import static jdk.nashorn.internal.runtime.logging.DebugLogger.quote;
import java.util.HashMap; import java.util.HashMap;
@ -34,6 +32,7 @@ import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import jdk.nashorn.internal.codegen.ObjectClassGenerator.AllocatorDescriptor;
import jdk.nashorn.internal.ir.Block; import jdk.nashorn.internal.ir.Block;
import jdk.nashorn.internal.ir.FunctionNode; import jdk.nashorn.internal.ir.FunctionNode;
import jdk.nashorn.internal.ir.FunctionNode.CompilationState; import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
@ -44,7 +43,6 @@ import jdk.nashorn.internal.ir.Symbol;
import jdk.nashorn.internal.ir.WithNode; import jdk.nashorn.internal.ir.WithNode;
import jdk.nashorn.internal.ir.visitor.NodeVisitor; import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.Context; import jdk.nashorn.internal.runtime.Context;
import jdk.nashorn.internal.runtime.PropertyMap;
import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData; import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData;
import jdk.nashorn.internal.runtime.logging.DebugLogger; import jdk.nashorn.internal.runtime.logging.DebugLogger;
import jdk.nashorn.internal.runtime.logging.Loggable; import jdk.nashorn.internal.runtime.logging.Loggable;
@ -208,14 +206,10 @@ final class FindScopeDepths extends NodeVisitor<LexicalContext> implements Logga
assert nestedFunctions != null; assert nestedFunctions != null;
// Generate the object class and property map in case this function is ever used as constructor // Generate the object class and property map in case this function is ever used as constructor
final int fieldCount = getPaddedFieldCount(newFunctionNode.getThisProperties());
final String allocatorClassName = Compiler.binaryName(getClassName(fieldCount));
final PropertyMap allocatorMap = PropertyMap.newMap(null, allocatorClassName, 0, fieldCount, 0);
final RecompilableScriptFunctionData data = new RecompilableScriptFunctionData( final RecompilableScriptFunctionData data = new RecompilableScriptFunctionData(
newFunctionNode, newFunctionNode,
compiler.getCodeInstaller(), compiler.getCodeInstaller(),
allocatorClassName, new AllocatorDescriptor(newFunctionNode.getThisProperties()),
allocatorMap,
nestedFunctions, nestedFunctions,
externalSymbolDepths.get(fnId), externalSymbolDepths.get(fnId),
internalSymbols.get(fnId) internalSymbols.get(fnId)

View File

@ -551,13 +551,19 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
final Expression init = forNode.getInit(); final Expression init = forNode.getInit();
if(forNode.isForIn()) { if(forNode.isForIn()) {
forNode.getModify().accept(this); final JoinPredecessorExpression iterable = forNode.getModify();
enterTestFirstLoop(forNode, null, init); iterable.accept(this);
enterTestFirstLoop(forNode, null, init,
// If we're iterating over property names, and we can discern from the runtime environment
// of the compilation that the object being iterated over must use strings for property
// names (e.g., it is a native JS object or array), then we'll not bother trying to treat
// the property names optimistically.
!forNode.isForEach() && compiler.hasStringPropertyIterator(iterable.getExpression()));
} else { } else {
if(init != null) { if(init != null) {
init.accept(this); init.accept(this);
} }
enterTestFirstLoop(forNode, forNode.getModify(), null); enterTestFirstLoop(forNode, forNode.getModify(), null, false);
} }
return false; return false;
} }
@ -792,7 +798,8 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
return false; return false;
} }
private void enterTestFirstLoop(final LoopNode loopNode, final JoinPredecessorExpression modify, final Expression iteratorValues) { private void enterTestFirstLoop(final LoopNode loopNode, final JoinPredecessorExpression modify,
final Expression iteratorValues, final boolean iteratorValuesAreObject) {
final JoinPredecessorExpression test = loopNode.getTest(); final JoinPredecessorExpression test = loopNode.getTest();
if(isAlwaysFalse(test)) { if(isAlwaysFalse(test)) {
test.accept(this); test.accept(this);
@ -814,8 +821,12 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
jumpToLabel(test, breakLabel); jumpToLabel(test, breakLabel);
} }
if(iteratorValues instanceof IdentNode) { if(iteratorValues instanceof IdentNode) {
// Receives iterator values; they're currently all objects (JDK-8034954). final IdentNode ident = (IdentNode)iteratorValues;
onAssignment((IdentNode)iteratorValues, LvarType.OBJECT); // Receives iterator values; the optimistic type of the iterator values is tracked on the
// identifier, but we override optimism if it's known that the object being iterated over will
// never have primitive property names.
onAssignment(ident, iteratorValuesAreObject ? LvarType.OBJECT :
toLvarType(compiler.getOptimisticType(ident)));
} }
final Block body = loopNode.getBody(); final Block body = loopNode.getBody();
body.accept(this); body.accept(this);
@ -955,7 +966,7 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
if(whileNode.isDoWhile()) { if(whileNode.isDoWhile()) {
enterDoWhileLoop(whileNode); enterDoWhileLoop(whileNode);
} else { } else {
enterTestFirstLoop(whileNode, null, null); enterTestFirstLoop(whileNode, null, null, false);
} }
return false; return false;
} }

View File

@ -71,7 +71,6 @@ import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor;
import jdk.nashorn.internal.ir.visitor.NodeVisitor; import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.parser.Token; import jdk.nashorn.internal.parser.Token;
import jdk.nashorn.internal.parser.TokenType; import jdk.nashorn.internal.parser.TokenType;
import jdk.nashorn.internal.runtime.CodeInstaller;
import jdk.nashorn.internal.runtime.Context; import jdk.nashorn.internal.runtime.Context;
import jdk.nashorn.internal.runtime.JSType; import jdk.nashorn.internal.runtime.JSType;
import jdk.nashorn.internal.runtime.Source; import jdk.nashorn.internal.runtime.Source;
@ -93,9 +92,6 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> implements Lo
private final DebugLogger log; private final DebugLogger log;
// needed only to get unique eval id
private final CodeInstaller<?> installer;
/** /**
* Constructor. * Constructor.
*/ */
@ -143,7 +139,6 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> implements Lo
} }
}); });
this.installer = compiler.getCodeInstaller();
this.log = initLogger(compiler.getContext()); this.log = initLogger(compiler.getContext());
} }
@ -566,16 +561,13 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> implements Lo
private String evalLocation(final IdentNode node) { private String evalLocation(final IdentNode node) {
final Source source = lc.getCurrentFunction().getSource(); final Source source = lc.getCurrentFunction().getSource();
final int pos = node.position(); final int pos = node.position();
// Code installer is null when running with --compile-only, use 0 as id in that case
final long id = installer == null ? 0 : installer.getUniqueEvalId();
return new StringBuilder(). return new StringBuilder().
append(source.getName()). append(source.getName()).
append('#'). append('#').
append(source.getLine(pos)). append(source.getLine(pos)).
append(':'). append(':').
append(source.getColumn(pos)). append(source.getColumn(pos)).
append("<eval>@"). append("<eval>").
append(id).
toString(); toString();
} }

View File

@ -308,7 +308,7 @@ public final class ObjectClassGenerator implements Loggable {
newEmptyInit(className, classEmitter); newEmptyInit(className, classEmitter);
newAllocate(className, classEmitter); newAllocate(className, classEmitter);
return toByteArray(classEmitter); return toByteArray(className, classEmitter);
} }
/** /**
@ -341,7 +341,7 @@ public final class ObjectClassGenerator implements Loggable {
initWithArguments.returnVoid(); initWithArguments.returnVoid();
initWithArguments.end(); initWithArguments.end();
return toByteArray(classEmitter); return toByteArray(className, classEmitter);
} }
/** /**
@ -484,15 +484,13 @@ public final class ObjectClassGenerator implements Loggable {
* @param classEmitter Open class emitter. * @param classEmitter Open class emitter.
* @return Byte codes for the class. * @return Byte codes for the class.
*/ */
private byte[] toByteArray(final ClassEmitter classEmitter) { private byte[] toByteArray(final String className, final ClassEmitter classEmitter) {
classEmitter.end(); classEmitter.end();
final byte[] code = classEmitter.toByteArray(); final byte[] code = classEmitter.toByteArray();
final ScriptEnvironment env = context.getEnv(); final ScriptEnvironment env = context.getEnv();
if (env._print_code && env._print_code_dir == null) { DumpBytecode.dumpBytecode(env, log, code, className);
env.getErr().println(ClassEmitter.disassemble(code));
}
if (env._verify_code) { if (env._verify_code) {
context.verify(code); context.verify(code);
@ -827,5 +825,45 @@ public final class ObjectClassGenerator implements Loggable {
return MH.findStatic(MethodHandles.lookup(), ObjectClassGenerator.class, name, MH.type(rtype, types)); return MH.findStatic(MethodHandles.lookup(), ObjectClassGenerator.class, name, MH.type(rtype, types));
} }
/**
* Describes the allocator class name and property map for a constructor function with the specified
* number of "this" properties that it initializes.
*
*/
public static class AllocatorDescriptor {
private final String allocatorClassName;
private final PropertyMap allocatorMap;
/**
* Creates a new allocator descriptor
* @param thisProperties the number of "this" properties that the function initializes
*/
public AllocatorDescriptor(final int thisProperties) {
final int paddedFieldCount = getPaddedFieldCount(thisProperties);
this.allocatorClassName = Compiler.binaryName(getClassName(paddedFieldCount));
this.allocatorMap = PropertyMap.newMap(null, allocatorClassName, 0, paddedFieldCount, 0);
}
/**
* Returns the name of the class that the function allocates
* @return the name of the class that the function allocates
*/
public String getAllocatorClassName() {
return allocatorClassName;
}
/**
* Returns the allocator map for the function.
* @return the allocator map for the function.
*/
public PropertyMap getAllocatorMap() {
return allocatorMap;
}
@Override
public String toString() {
return "AllocatorDescriptor[allocatorClassName=" + allocatorClassName + ", allocatorMap.size=" +
allocatorMap.size() + "]";
}
}
} }

View File

@ -55,6 +55,19 @@ final class TypeEvaluator {
this.runtimeScope = runtimeScope; this.runtimeScope = runtimeScope;
} }
/**
* Returns true if the expression can be safely evaluated, and its value is an object known to always use
* String as the type of its property names retrieved through
* {@link ScriptRuntime#toPropertyIterator(Object)}. It is used to avoid optimistic assumptions about its
* property name types.
* @param expr the expression to test
* @return true if the expression can be safely evaluated, and its value is an object known to always use
* String as the type of its property iterators.
*/
boolean hasStringPropertyIterator(final Expression expr) {
return evaluateSafely(expr) instanceof ScriptObject;
}
Type getOptimisticType(final Optimistic node) { Type getOptimisticType(final Optimistic node) {
assert compiler.useOptimisticTypes(); assert compiler.useOptimisticTypes();

View File

@ -276,6 +276,14 @@ public class Block extends Node implements BreakableNode, Terminal, Flags<Block>
return Collections.unmodifiableList(statements); return Collections.unmodifiableList(statements);
} }
/**
* Returns the number of statements in the block.
* @return the number of statements in the block.
*/
public int getStatementCount() {
return statements.size();
}
/** /**
* Returns the line number of the first statement in the block. * 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. * @return the line number of the first statement in the block, or -1 if the block has no statements.

View File

@ -0,0 +1,40 @@
/*
* 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.CompileUnit;
/**
* Marker interface for things in the IR that can hold compile units.
* {@link CompileUnit}
*/
public interface CompileUnitHolder {
/**
* Return the compile unit held by this instance
* @return compile unit
*/
public CompileUnit getCompileUnit();
}

View File

@ -34,10 +34,8 @@ import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALL
import java.util.Collections; import java.util.Collections;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Set;
import java.util.function.Function; import java.util.function.Function;
import jdk.nashorn.internal.AssertsEnabled; import jdk.nashorn.internal.AssertsEnabled;
import jdk.nashorn.internal.codegen.CompileUnit; import jdk.nashorn.internal.codegen.CompileUnit;
@ -48,6 +46,7 @@ import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.annotations.Ignore; import jdk.nashorn.internal.ir.annotations.Ignore;
import jdk.nashorn.internal.ir.annotations.Immutable; import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor; import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData;
import jdk.nashorn.internal.runtime.ScriptFunction; import jdk.nashorn.internal.runtime.ScriptFunction;
import jdk.nashorn.internal.runtime.Source; import jdk.nashorn.internal.runtime.Source;
import jdk.nashorn.internal.runtime.UserAccessorProperty; import jdk.nashorn.internal.runtime.UserAccessorProperty;
@ -57,7 +56,7 @@ import jdk.nashorn.internal.runtime.linker.LinkerCallSite;
* IR representation for function (or script.) * IR representation for function (or script.)
*/ */
@Immutable @Immutable
public final class FunctionNode extends LexicalContextExpression implements Flags<FunctionNode> { public final class FunctionNode extends LexicalContextExpression implements Flags<FunctionNode>, CompileUnitHolder {
/** Type used for all FunctionNodes */ /** Type used for all FunctionNodes */
public static final Type FUNCTION_TYPE = Type.typeFor(ScriptFunction.class); public static final Type FUNCTION_TYPE = Type.typeFor(ScriptFunction.class);
@ -110,8 +109,11 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
/** Source of entity. */ /** Source of entity. */
private final Source source; private final Source source;
/** Unique ID used for recompilation among other things */ /**
private final int id; * Opaque object representing parser state at the end of the function. Used when reparsing outer functions
* to skip parsing inner functions.
*/
private final Object endParserState;
/** External function identifier. */ /** External function identifier. */
@Ignore @Ignore
@ -256,6 +258,14 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
/** trace callsite values in this function? */ /** trace callsite values in this function? */
public static final int IS_TRACE_VALUES = 1 << 26; public static final int IS_TRACE_VALUES = 1 << 26;
/**
* Whether this function needs the callee {@link ScriptFunction} instance passed to its code as a
* parameter on invocation. Note that we aren't, in fact using this flag in function nodes.
* Rather, it is always calculated (see {@link #needsCallee()}). {@link RecompilableScriptFunctionData}
* will, however, cache the value of this flag.
*/
public static final int NEEDS_CALLEE = 1 << 27;
/** extension callsite flags mask */ /** extension callsite flags mask */
public static final int EXTENSION_CALLSITE_FLAGS = IS_PRINT_PARSE | public static final int EXTENSION_CALLSITE_FLAGS = IS_PRINT_PARSE |
IS_PRINT_LOWER_PARSE | IS_PRINT_AST | IS_PRINT_LOWER_AST | IS_PRINT_LOWER_PARSE | IS_PRINT_AST | IS_PRINT_LOWER_AST |
@ -271,16 +281,9 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
/** Does this function potentially need "arguments"? Note that this is not a full test, as further negative check of REDEFINES_ARGS is needed. */ /** Does this function potentially need "arguments"? Note that this is not a full test, as further negative check of REDEFINES_ARGS is needed. */
private static final int MAYBE_NEEDS_ARGUMENTS = USES_ARGUMENTS | HAS_EVAL; private static final int MAYBE_NEEDS_ARGUMENTS = USES_ARGUMENTS | HAS_EVAL;
/** Does this function need the parent scope? It needs it if either it or its descendants use variables from it, or have a deep eval. /** Does this function need the parent scope? It needs it if either it or its descendants use variables from it, or have a deep eval. */
* We also pessimistically need a parent scope if we have lazy children that have not yet been compiled */
private static final int NEEDS_PARENT_SCOPE = USES_ANCESTOR_SCOPE | HAS_DEEP_EVAL; private static final int NEEDS_PARENT_SCOPE = USES_ANCESTOR_SCOPE | HAS_DEEP_EVAL;
/** Used to signify "null", e.g. if someone asks for the parent of the program node */
public static final int NO_FUNCTION_ID = 0;
/** Where to start assigning global and unique function node ids */
public static final int FIRST_FUNCTION_ID = NO_FUNCTION_ID + 1;
/** What is the return type of this function? */ /** What is the return type of this function? */
private Type returnType = Type.UNKNOWN; private Type returnType = Type.UNKNOWN;
@ -288,11 +291,10 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
* Constructor * Constructor
* *
* @param source the source * @param source the source
* @param id unique id
* @param lineNumber line number * @param lineNumber line number
* @param token token * @param token token
* @param finish finish * @param finish finish
* @param firstToken first token of the funtion node (including the function declaration) * @param firstToken first token of the function node (including the function declaration)
* @param namespace the namespace * @param namespace the namespace
* @param ident the identifier * @param ident the identifier
* @param name the name of the function * @param name the name of the function
@ -302,7 +304,6 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
*/ */
public FunctionNode( public FunctionNode(
final Source source, final Source source,
final int id,
final int lineNumber, final int lineNumber,
final long token, final long token,
final int finish, final int finish,
@ -316,7 +317,6 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
super(token, finish); super(token, finish);
this.source = source; this.source = source;
this.id = id;
this.lineNumber = lineNumber; this.lineNumber = lineNumber;
this.ident = ident; this.ident = ident;
this.name = name; this.name = name;
@ -331,11 +331,13 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
this.body = null; this.body = null;
this.thisProperties = 0; this.thisProperties = 0;
this.rootClass = null; this.rootClass = null;
this.endParserState = null;
} }
private FunctionNode( private FunctionNode(
final FunctionNode functionNode, final FunctionNode functionNode,
final long lastToken, final long lastToken,
Object endParserState,
final int flags, final int flags,
final String name, final String name,
final Type returnType, final Type returnType,
@ -347,6 +349,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
final Class<?> rootClass) { final Class<?> rootClass) {
super(functionNode); super(functionNode);
this.endParserState = endParserState;
this.lineNumber = functionNode.lineNumber; this.lineNumber = functionNode.lineNumber;
this.flags = flags; this.flags = flags;
this.name = name; this.name = name;
@ -361,7 +364,6 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
// the fields below never change - they are final and assigned in constructor // the fields below never change - they are final and assigned in constructor
this.source = functionNode.source; this.source = functionNode.source;
this.id = functionNode.id;
this.ident = functionNode.ident; this.ident = functionNode.ident;
this.namespace = functionNode.namespace; this.namespace = functionNode.namespace;
this.kind = functionNode.kind; this.kind = functionNode.kind;
@ -429,11 +431,11 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
} }
/** /**
* Get the unique ID for this function * Get the unique ID for this function within the script file.
* @return the id * @return the id
*/ */
public int getId() { public int getId() {
return id; return position();
} }
/** /**
@ -535,6 +537,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
new FunctionNode( new FunctionNode(
this, this,
lastToken, lastToken,
endParserState,
flags, flags,
name, name,
returnType, returnType,
@ -606,6 +609,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
new FunctionNode( new FunctionNode(
this, this,
lastToken, lastToken,
endParserState,
flags, flags,
name, name,
returnType, returnType,
@ -644,14 +648,23 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
} }
/** /**
* Check if the {@code eval} keyword is used in this function * Check if this function has a call expression for the identifier "eval" (that is, {@code eval(...)}).
* *
* @return true if {@code eval} is used * @return true if {@code eval} is called.
*/ */
public boolean hasEval() { public boolean hasEval() {
return getFlag(HAS_EVAL); return getFlag(HAS_EVAL);
} }
/**
* Returns true if a function nested (directly or transitively) within this function {@link #hasEval()}.
*
* @return true if a nested function calls {@code eval}.
*/
public boolean hasNestedEval() {
return getFlag(HAS_NESTED_EVAL);
}
/** /**
* Get the first token for this function * Get the first token for this function
* @return the first token * @return the first token
@ -741,6 +754,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
new FunctionNode( new FunctionNode(
this, this,
lastToken, lastToken,
endParserState,
flags | flags |
(body.needsScope() ? (body.needsScope() ?
FunctionNode.HAS_SCOPE_BLOCK : FunctionNode.HAS_SCOPE_BLOCK :
@ -839,6 +853,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
new FunctionNode( new FunctionNode(
this, this,
lastToken, lastToken,
endParserState,
flags, flags,
name, name,
returnType, returnType,
@ -899,6 +914,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
new FunctionNode( new FunctionNode(
this, this,
lastToken, lastToken,
endParserState,
flags, flags,
name, name,
returnType, returnType,
@ -910,6 +926,41 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
rootClass)); rootClass));
} }
/**
* Returns the end parser state for this function.
* @return the end parser state for this function.
*/
public Object getEndParserState() {
return endParserState;
}
/**
* Set the end parser state for this function.
* @param lc lexical context
* @param endParserState the parser state to set
* @return function node or a new one if state was changed
*/
public FunctionNode setEndParserState(final LexicalContext lc, final Object endParserState) {
if (this.endParserState == endParserState) {
return this;
}
return Node.replaceInLexicalContext(
lc,
this,
new FunctionNode(
this,
lastToken,
endParserState,
flags,
name,
returnType,
compileUnit,
compilationState,
body,
parameters,
thisProperties, rootClass));
}
/** /**
* Get the name of this function * Get the name of this function
* @return the name * @return the name
@ -934,6 +985,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
new FunctionNode( new FunctionNode(
this, this,
lastToken, lastToken,
endParserState,
flags, flags,
name, name,
returnType, returnType,
@ -999,6 +1051,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
new FunctionNode( new FunctionNode(
this, this,
lastToken, lastToken,
endParserState,
flags, flags,
name, name,
returnType, returnType,
@ -1077,6 +1130,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
new FunctionNode( new FunctionNode(
this, this,
lastToken, lastToken,
endParserState,
flags, flags,
name, name,
type, type,
@ -1102,6 +1156,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
* @see Compiler * @see Compiler
* @return the compile unit * @return the compile unit
*/ */
@Override
public CompileUnit getCompileUnit() { public CompileUnit getCompileUnit() {
return compileUnit; return compileUnit;
} }
@ -1123,6 +1178,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
new FunctionNode( new FunctionNode(
this, this,
lastToken, lastToken,
endParserState,
flags, flags,
name, name,
returnType, returnType,
@ -1178,6 +1234,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
new FunctionNode( new FunctionNode(
this, this,
lastToken, lastToken,
endParserState,
flags, flags,
name, name,
returnType, returnType,

View File

@ -351,8 +351,7 @@ public class LexicalContext {
} }
/** /**
* Get the function for this block. If the block is itself a function * Get the function for this block.
* this returns identity
* @param block block for which to get function * @param block block for which to get function
* @return function for block * @return function for block
*/ */

View File

@ -603,7 +603,7 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey {
* An ArrayUnit is a range in an ArrayLiteral. ArrayLiterals can * An ArrayUnit is a range in an ArrayLiteral. ArrayLiterals can
* be split if they are too large, for bytecode generation reasons * be split if they are too large, for bytecode generation reasons
*/ */
public static final class ArrayUnit { public static final class ArrayUnit implements CompileUnitHolder {
/** Compile unit associated with the postsets range. */ /** Compile unit associated with the postsets range. */
private final CompileUnit compileUnit; private final CompileUnit compileUnit;
@ -642,6 +642,7 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey {
* The array compile unit * The array compile unit
* @return array compile unit * @return array compile unit
*/ */
@Override
public CompileUnit getCompileUnit() { public CompileUnit getCompileUnit() {
return compileUnit; return compileUnit;
} }

View File

@ -39,7 +39,7 @@ import jdk.nashorn.internal.ir.visitor.NodeVisitor;
* Node indicating code is split across classes. * Node indicating code is split across classes.
*/ */
@Immutable @Immutable
public class SplitNode extends LexicalContextStatement implements Labels { public class SplitNode extends LexicalContextStatement implements Labels, CompileUnitHolder {
/** Split node method name. */ /** Split node method name. */
private final String name; private final String name;
@ -116,6 +116,7 @@ public class SplitNode extends LexicalContextStatement implements Labels {
* Get the compile unit for this split node * Get the compile unit for this split node
* @return compile unit * @return compile unit
*/ */
@Override
public CompileUnit getCompileUnit() { public CompileUnit getCompileUnit() {
return compileUnit; return compileUnit;
} }

View File

@ -630,6 +630,24 @@ public final class Global extends ScriptObject implements Scope {
throw new IllegalArgumentException("Unsupported primitive: " + self); throw new IllegalArgumentException("Unsupported primitive: " + self);
} }
/**
* Returns a method handle that creates a wrapper object for a JS primitive value.
*
* @param self receiver object
* @return method handle to create wrapper objects for primitive receiver
*/
public static MethodHandle getPrimitiveWrapFilter(final Object self) {
if (self instanceof String || self instanceof ConsString) {
return NativeString.WRAPFILTER;
} else if (self instanceof Number) {
return NativeNumber.WRAPFILTER;
} else if (self instanceof Boolean) {
return NativeBoolean.WRAPFILTER;
}
throw new IllegalArgumentException("Unsupported primitive: " + self);
}
/** /**
* Create a new empty script object * Create a new empty script object
* *

View File

@ -51,9 +51,9 @@ import jdk.nashorn.internal.runtime.linker.PrimitiveLookup;
public final class NativeBoolean extends ScriptObject { public final class NativeBoolean extends ScriptObject {
private final boolean value; private final boolean value;
// Method handle to create an object wrapper for a primitive boolean /** Method handle to create an object wrapper for a primitive boolean. */
private static final MethodHandle WRAPFILTER = findOwnMH("wrapFilter", MH.type(NativeBoolean.class, Object.class)); static final MethodHandle WRAPFILTER = findOwnMH("wrapFilter", MH.type(NativeBoolean.class, Object.class));
// Method handle to retrieve the Boolean prototype object /** Method handle to retrieve the Boolean prototype object. */
private static final MethodHandle PROTOFILTER = findOwnMH("protoFilter", MH.type(Object.class, Object.class)); private static final MethodHandle PROTOFILTER = findOwnMH("protoFilter", MH.type(Object.class, Object.class));
// initialized by nasgen // initialized by nasgen

View File

@ -28,6 +28,7 @@ package jdk.nashorn.internal.objects;
import static jdk.nashorn.internal.lookup.Lookup.MH; import static jdk.nashorn.internal.lookup.Lookup.MH;
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED; import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles;
@ -697,7 +698,7 @@ public final class NativeJSAdapter extends ScriptObject {
if (methodHandle != null) { if (methodHandle != null) {
return new GuardedInvocation( return new GuardedInvocation(
methodHandle, methodHandle,
testJSAdaptor(adaptee, findData.getGetter(Object.class, UnwarrantedOptimismException.INVALID_PROGRAM_POINT), findData.getOwner(), func), testJSAdaptor(adaptee, findData.getGetter(Object.class, INVALID_PROGRAM_POINT, null), findData.getOwner(), func),
adaptee.getProtoSwitchPoint(hook, findData.getOwner())); adaptee.getProtoSwitchPoint(hook, findData.getOwner()));
} }
} }

View File

@ -57,9 +57,9 @@ import jdk.nashorn.internal.runtime.linker.PrimitiveLookup;
@ScriptClass("Number") @ScriptClass("Number")
public final class NativeNumber extends ScriptObject { public final class NativeNumber extends ScriptObject {
// Method handle to create an object wrapper for a primitive number /** Method handle to create an object wrapper for a primitive number. */
private static final MethodHandle WRAPFILTER = findOwnMH("wrapFilter", MH.type(NativeNumber.class, Object.class)); static final MethodHandle WRAPFILTER = findOwnMH("wrapFilter", MH.type(NativeNumber.class, Object.class));
// Method handle to retrieve the Number prototype object /** Method handle to retrieve the Number prototype object. */
private static final MethodHandle PROTOFILTER = findOwnMH("protoFilter", MH.type(Object.class, Object.class)); private static final MethodHandle PROTOFILTER = findOwnMH("protoFilter", MH.type(Object.class, Object.class));
/** ECMA 15.7.3.2 largest positive finite value */ /** ECMA 15.7.3.2 largest positive finite value */

View File

@ -71,9 +71,9 @@ public final class NativeString extends ScriptObject {
private final CharSequence value; private final CharSequence value;
// Method handle to create an object wrapper for a primitive string /** Method handle to create an object wrapper for a primitive string */
private static final MethodHandle WRAPFILTER = findOwnMH("wrapFilter", MH.type(NativeString.class, Object.class)); static final MethodHandle WRAPFILTER = findOwnMH("wrapFilter", MH.type(NativeString.class, Object.class));
// Method handle to retrieve the String prototype object /** Method handle to retrieve the String prototype object */
private static final MethodHandle PROTOFILTER = findOwnMH("protoFilter", MH.type(Object.class, Object.class)); private static final MethodHandle PROTOFILTER = findOwnMH("protoFilter", MH.type(Object.class, Object.class));
// initialized by nasgen // initialized by nasgen

View File

@ -326,18 +326,28 @@ public abstract class AbstractParser {
} }
/** /**
* Check next token and advance. * Check current token and advance to the next token.
* *
* @param expected Expected tokenType. * @param expected Expected tokenType.
* *
* @throws ParserException on unexpected token type * @throws ParserException on unexpected token type
*/ */
protected final void expect(final TokenType expected) throws ParserException { protected final void expect(final TokenType expected) throws ParserException {
expectDontAdvance(expected);
next();
}
/**
* Check current token, but don't advance to the next token.
*
* @param expected Expected tokenType.
*
* @throws ParserException on unexpected token type
*/
protected final void expectDontAdvance(final TokenType expected) throws ParserException {
if (type != expected) { if (type != expected) {
throw error(expectMessage(expected)); throw error(expectMessage(expected));
} }
next();
} }
/** /**

View File

@ -35,6 +35,7 @@ import static jdk.nashorn.internal.parser.TokenType.ERROR;
import static jdk.nashorn.internal.parser.TokenType.ESCSTRING; import static jdk.nashorn.internal.parser.TokenType.ESCSTRING;
import static jdk.nashorn.internal.parser.TokenType.EXECSTRING; import static jdk.nashorn.internal.parser.TokenType.EXECSTRING;
import static jdk.nashorn.internal.parser.TokenType.FLOATING; import static jdk.nashorn.internal.parser.TokenType.FLOATING;
import static jdk.nashorn.internal.parser.TokenType.FUNCTION;
import static jdk.nashorn.internal.parser.TokenType.HEXADECIMAL; import static jdk.nashorn.internal.parser.TokenType.HEXADECIMAL;
import static jdk.nashorn.internal.parser.TokenType.LBRACE; import static jdk.nashorn.internal.parser.TokenType.LBRACE;
import static jdk.nashorn.internal.parser.TokenType.LPAREN; import static jdk.nashorn.internal.parser.TokenType.LPAREN;
@ -85,6 +86,9 @@ public class Lexer extends Scanner {
/** Type of last token added. */ /** Type of last token added. */
private TokenType last; private TokenType last;
private final boolean pauseOnFunctionBody;
private boolean pauseOnNextLeftBrace;
private static final String SPACETAB = " \t"; // ASCII space and tab private static final String SPACETAB = " \t"; // ASCII space and tab
private static final String LFCR = "\n\r"; // line feed and carriage return (ctrl-m) private static final String LFCR = "\n\r"; // line feed and carriage return (ctrl-m)
@ -182,20 +186,23 @@ public class Lexer extends Scanner {
* @param scripting are we in scripting mode * @param scripting are we in scripting mode
*/ */
public Lexer(final Source source, final TokenStream stream, final boolean scripting) { public Lexer(final Source source, final TokenStream stream, final boolean scripting) {
this(source, 0, source.getLength(), stream, scripting); this(source, 0, source.getLength(), stream, scripting, false);
} }
/** /**
* Contructor * Constructor
* *
* @param source the source * @param source the source
* @param start start position in source from which to start lexing * @param start start position in source from which to start lexing
* @param len length of source segment to lex * @param len length of source segment to lex
* @param stream token stream to lex * @param stream token stream to lex
* @param scripting are we in scripting mode * @param scripting are we in scripting mode
* @param pauseOnFunctionBody if true, lexer will return from {@link #lexify()} when it encounters a
* function body. This is used with the feature where the parser is skipping nested function bodies to
* avoid reading ahead unnecessarily when we skip the function bodies.
*/ */
public Lexer(final Source source, final int start, final int len, final TokenStream stream, final boolean scripting) { public Lexer(final Source source, final int start, final int len, final TokenStream stream, final boolean scripting, final boolean pauseOnFunctionBody) {
super(source.getContent(), 1, start, len); super(source.getContent(), 1, start, len);
this.source = source; this.source = source;
this.stream = stream; this.stream = stream;
@ -203,6 +210,8 @@ public class Lexer extends Scanner {
this.nested = false; this.nested = false;
this.pendingLine = 1; this.pendingLine = 1;
this.last = EOL; this.last = EOL;
this.pauseOnFunctionBody = pauseOnFunctionBody;
} }
private Lexer(final Lexer lexer, final State state) { private Lexer(final Lexer lexer, final State state) {
@ -216,6 +225,7 @@ public class Lexer extends Scanner {
pendingLine = state.pendingLine; pendingLine = state.pendingLine;
linePosition = state.linePosition; linePosition = state.linePosition;
last = EOL; last = EOL;
pauseOnFunctionBody = false;
} }
static class State extends Scanner.State { static class State extends Scanner.State {
@ -810,6 +820,9 @@ public class Lexer extends Scanner {
final int length = scanIdentifier(); final int length = scanIdentifier();
// Check to see if it is a keyword. // Check to see if it is a keyword.
final TokenType type = TokenLookup.lookupKeyword(content, start, length); final TokenType type = TokenLookup.lookupKeyword(content, start, length);
if (type == FUNCTION && pauseOnFunctionBody) {
pauseOnNextLeftBrace = true;
}
// Add keyword or identifier token. // Add keyword or identifier token.
add(type, start); add(type, start);
} }
@ -1597,6 +1610,9 @@ public class Lexer extends Scanner {
// We break to let the parser decide what it is. // We break to let the parser decide what it is.
if (canStartLiteral(type)) { if (canStartLiteral(type)) {
break; break;
} else if (type == LBRACE && pauseOnNextLeftBrace) {
pauseOnNextLeftBrace = false;
break;
} }
} else if (Character.isJavaIdentifierStart(ch0) || ch0 == '\\' && ch1 == 'u') { } else if (Character.isJavaIdentifierStart(ch0) || ch0 == '\\' && ch1 == 'u') {
// Scan and add identifier or keyword. // Scan and add identifier or keyword.

View File

@ -148,7 +148,7 @@ public class Parser extends AbstractParser implements Loggable {
/** to receive line information from Lexer when scanning multine literals. */ /** to receive line information from Lexer when scanning multine literals. */
protected final Lexer.LineInfoReceiver lineInfoReceiver; protected final Lexer.LineInfoReceiver lineInfoReceiver;
private int nextFunctionId; private RecompilableScriptFunctionData reparsedFunction;
/** /**
* Constructor * Constructor
@ -171,7 +171,7 @@ public class Parser extends AbstractParser implements Loggable {
* @param log debug logger if one is needed * @param log debug logger if one is needed
*/ */
public Parser(final ScriptEnvironment env, final Source source, final ErrorManager errors, final boolean strict, final DebugLogger log) { public Parser(final ScriptEnvironment env, final Source source, final ErrorManager errors, final boolean strict, final DebugLogger log) {
this(env, source, errors, strict, FunctionNode.FIRST_FUNCTION_ID, 0, log); this(env, source, errors, strict, 0, log);
} }
/** /**
@ -181,15 +181,13 @@ public class Parser extends AbstractParser implements Loggable {
* @param source source to parse * @param source source to parse
* @param errors error manager * @param errors error manager
* @param strict parser created with strict mode enabled. * @param strict parser created with strict mode enabled.
* @param nextFunctionId starting value for assigning new unique ids to function nodes
* @param lineOffset line offset to start counting lines from * @param lineOffset line offset to start counting lines from
* @param log debug logger if one is needed * @param log debug logger if one is needed
*/ */
public Parser(final ScriptEnvironment env, final Source source, final ErrorManager errors, final boolean strict, final int nextFunctionId, final int lineOffset, final DebugLogger log) { public Parser(final ScriptEnvironment env, final Source source, final ErrorManager errors, final boolean strict, final int lineOffset, final DebugLogger log) {
super(source, errors, strict, lineOffset); super(source, errors, strict, lineOffset);
this.env = env; this.env = env;
this.namespace = new Namespace(env.getNamespace()); this.namespace = new Namespace(env.getNamespace());
this.nextFunctionId = nextFunctionId;
this.scripting = env._scripting; this.scripting = env._scripting;
if (this.scripting) { if (this.scripting) {
this.lineInfoReceiver = new Lexer.LineInfoReceiver() { this.lineInfoReceiver = new Lexer.LineInfoReceiver() {
@ -227,6 +225,16 @@ public class Parser extends AbstractParser implements Loggable {
defaultNames.push(createIdentNode(0, 0, name)); defaultNames.push(createIdentNode(0, 0, name));
} }
/**
* Sets the {@link RecompilableScriptFunctionData} representing the function being reparsed (when this
* parser instance is used to reparse a previously parsed function, as part of its on-demand compilation).
* This will trigger various special behaviors, such as skipping nested function bodies.
* @param reparsedFunction the function being reparsed.
*/
public void setReparsedFunction(final RecompilableScriptFunctionData reparsedFunction) {
this.reparsedFunction = reparsedFunction;
}
/** /**
* Execute parse and return the resulting function node. * Execute parse and return the resulting function node.
* Errors will be thrown and the error manager will contain information * Errors will be thrown and the error manager will contain information
@ -264,7 +272,7 @@ public class Parser extends AbstractParser implements Loggable {
try { try {
stream = new TokenStream(); stream = new TokenStream();
lexer = new Lexer(source, startPos, len, stream, scripting && !env._no_syntax_extensions); lexer = new Lexer(source, startPos, len, stream, scripting && !env._no_syntax_extensions, reparsedFunction != null);
lexer.line = lexer.pendingLine = lineOffset + 1; lexer.line = lexer.pendingLine = lineOffset + 1;
line = lineOffset; line = lineOffset;
@ -472,7 +480,6 @@ loop:
final FunctionNode functionNode = final FunctionNode functionNode =
new FunctionNode( new FunctionNode(
source, source,
nextFunctionId++,
functionLine, functionLine,
token, token,
Token.descPosition(token), Token.descPosition(token),
@ -2828,10 +2835,14 @@ loop:
FunctionNode functionNode = null; FunctionNode functionNode = null;
long lastToken = 0L; long lastToken = 0L;
final boolean parseBody;
Object endParserState = null;
try { try {
// Create a new function block. // Create a new function block.
functionNode = newFunctionNode(firstToken, ident, parameters, kind, functionLine); functionNode = newFunctionNode(firstToken, ident, parameters, kind, functionLine);
assert functionNode != null;
final int functionId = functionNode.getId();
parseBody = reparsedFunction == null || functionId <= reparsedFunction.getFunctionNodeId();
// Nashorn extension: expression closures // Nashorn extension: expression closures
if (!env._no_syntax_extensions && type != LBRACE) { if (!env._no_syntax_extensions && type != LBRACE) {
/* /*
@ -2847,34 +2858,143 @@ loop:
assert lc.getCurrentBlock() == lc.getFunctionBody(functionNode); assert lc.getCurrentBlock() == lc.getFunctionBody(functionNode);
// EOL uses length field to store the line number // EOL uses length field to store the line number
final int lastFinish = Token.descPosition(lastToken) + (Token.descType(lastToken) == EOL ? 0 : Token.descLength(lastToken)); final int lastFinish = Token.descPosition(lastToken) + (Token.descType(lastToken) == EOL ? 0 : Token.descLength(lastToken));
final ReturnNode returnNode = new ReturnNode(functionNode.getLineNumber(), expr.getToken(), lastFinish, expr); // Only create the return node if we aren't skipping nested functions. Note that we aren't
appendStatement(returnNode); // skipping parsing of these extended functions; they're considered to be small anyway. Also,
functionNode.setFinish(lastFinish); // they don't end with a single well known token, so it'd be very hard to get correctly (see
// the note below for reasoning on skipping happening before instead of after RBRACE for
} else { // details).
expect(LBRACE); if (parseBody) {
final ReturnNode returnNode = new ReturnNode(functionNode.getLineNumber(), expr.getToken(), lastFinish, expr);
// Gather the function elements. appendStatement(returnNode);
final List<Statement> prevFunctionDecls = functionDeclarations;
functionDeclarations = new ArrayList<>();
try {
sourceElements(false);
addFunctionDeclarations(functionNode);
} finally {
functionDeclarations = prevFunctionDecls;
} }
functionNode.setFinish(lastFinish);
} else {
expectDontAdvance(LBRACE);
if (parseBody || !skipFunctionBody(functionNode)) {
next();
// Gather the function elements.
final List<Statement> prevFunctionDecls = functionDeclarations;
functionDeclarations = new ArrayList<>();
try {
sourceElements(false);
addFunctionDeclarations(functionNode);
} finally {
functionDeclarations = prevFunctionDecls;
}
lastToken = token; lastToken = token;
if (parseBody) {
// Since the lexer can read ahead and lexify some number of tokens in advance and have
// them buffered in the TokenStream, we need to produce a lexer state as it was just
// before it lexified RBRACE, and not whatever is its current (quite possibly well read
// ahead) state.
endParserState = new ParserState(Token.descPosition(token), line, linePosition);
// NOTE: you might wonder why do we capture/restore parser state before RBRACE instead of
// after RBRACE; after all, we could skip the below "expect(RBRACE);" if we captured the
// state after it. The reason is that RBRACE is a well-known token that we can expect and
// will never involve us getting into a weird lexer state, and as such is a great reparse
// point. Typical example of a weird lexer state after RBRACE would be:
// function this_is_skipped() { ... } "use strict";
// because lexer is doing weird off-by-one maneuvers around string literal quotes. Instead
// of compensating for the possibility of a string literal (or similar) after RBRACE,
// we'll rather just restart parsing from this well-known, friendly token instead.
}
}
expect(RBRACE); expect(RBRACE);
functionNode.setFinish(finish); functionNode.setFinish(finish);
} }
} finally { } finally {
functionNode = restoreFunctionNode(functionNode, lastToken); functionNode = restoreFunctionNode(functionNode, lastToken);
} }
// NOTE: we can only do alterations to the function node after restoreFunctionNode.
if (parseBody) {
functionNode = functionNode.setEndParserState(lc, endParserState);
} else if (functionNode.getBody().getStatementCount() > 0){
// This is to ensure the body is empty when !parseBody but we couldn't skip parsing it (see
// skipFunctionBody() for possible reasons). While it is not strictly necessary for correctness to
// enforce empty bodies in nested functions that were supposed to be skipped, we do assert it as
// an invariant in few places in the compiler pipeline, so for consistency's sake we'll throw away
// nested bodies early if we were supposed to skip 'em.
functionNode = functionNode.setBody(null, functionNode.getBody().setStatements(null,
Collections.<Statement>emptyList()));
}
if (reparsedFunction != null) {
// We restore the flags stored in the function's ScriptFunctionData that we got when we first
// eagerly parsed the code. We're doing it because some flags would be set based on the
// content of the function, or even content of its nested functions, most of which are normally
// skipped during an on-demand compilation.
final RecompilableScriptFunctionData data = reparsedFunction.getScriptFunctionData(functionNode.getId());
if (data != null) {
// Data can be null if when we originally parsed the file, we removed the function declaration
// as it was dead code.
functionNode = functionNode.setFlags(lc, data.getFunctionFlags());
// This compensates for missing markEval() in case the function contains an inner function
// that contains eval(), that now we didn't discover since we skipped the inner function.
if (functionNode.hasNestedEval()) {
assert functionNode.hasScopeBlock();
functionNode = functionNode.setBody(lc, functionNode.getBody().setNeedsScope(null));
}
}
}
printAST(functionNode); printAST(functionNode);
return functionNode; return functionNode;
} }
private boolean skipFunctionBody(final FunctionNode functionNode) {
if (reparsedFunction == null) {
// Not reparsing, so don't skip any function body.
return false;
}
// Skip to the RBRACE of this function, and continue parsing from there.
final RecompilableScriptFunctionData data = reparsedFunction.getScriptFunctionData(functionNode.getId());
if (data == null) {
// Nested function is not known to the reparsed function. This can happen if the FunctionNode was
// in dead code that was removed. Both FoldConstants and Lower prune dead code. In that case, the
// FunctionNode was dropped before a RecompilableScriptFunctionData could've been created for it.
return false;
}
final ParserState parserState = (ParserState)data.getEndParserState();
assert parserState != null;
stream.reset();
lexer = parserState.createLexer(source, lexer, stream, scripting && !env._no_syntax_extensions);
line = parserState.line;
linePosition = parserState.linePosition;
// Doesn't really matter, but it's safe to treat it as if there were a semicolon before
// the RBRACE.
type = SEMICOLON;
k = -1;
next();
return true;
}
/**
* Encapsulates part of the state of the parser, enough to reconstruct the state of both parser and lexer
* for resuming parsing after skipping a function body.
*/
private static class ParserState {
private final int position;
private final int line;
private final int linePosition;
ParserState(final int position, final int line, final int linePosition) {
this.position = position;
this.line = line;
this.linePosition = linePosition;
}
Lexer createLexer(final Source source, final Lexer lexer, final TokenStream stream, final boolean scripting) {
final Lexer newLexer = new Lexer(source, position, lexer.limit - position, stream, scripting, true);
newLexer.restoreState(new Lexer.State(position, Integer.MAX_VALUE, line, -1, linePosition, SEMICOLON));
return newLexer;
}
}
private void printAST(final FunctionNode functionNode) { private void printAST(final FunctionNode functionNode) {
if (functionNode.getFlag(FunctionNode.IS_PRINT_AST)) { if (functionNode.getFlag(FunctionNode.IS_PRINT_AST)) {
env.getErr().println(new ASTWriter(functionNode)); env.getErr().println(new ASTWriter(functionNode));
@ -3247,6 +3367,9 @@ loop:
} else { } else {
lc.setFlag(fn, FunctionNode.HAS_NESTED_EVAL); lc.setFlag(fn, FunctionNode.HAS_NESTED_EVAL);
} }
// NOTE: it is crucial to mark the body of the outer function as needing scope even when we skip
// parsing a nested function. functionBody() contains code to compensate for the lack of invoking
// this method when the parser skips a nested function.
lc.setBlockNeedsScope(lc.getFunctionBody(fn)); lc.setBlockNeedsScope(lc.getFunctionBody(fn));
} }
} }

View File

@ -209,4 +209,8 @@ public class TokenStream {
in = count; in = count;
buffer = newBuffer; buffer = newBuffer;
} }
void reset() {
in = out = count = base = 0;
}
} }

View File

@ -0,0 +1,104 @@
/*
* Copyright (c) 2014, 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.runtime;
import static jdk.nashorn.internal.lookup.Lookup.MH;
import java.io.Serializable;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import jdk.nashorn.internal.codegen.CompilerConstants;
import jdk.nashorn.internal.codegen.ObjectClassGenerator.AllocatorDescriptor;
/**
* Encapsulates the allocation strategy for a function when used as a constructor. Basically the same as
* {@link AllocatorDescriptor}, but with an additionally cached resolved method handle. There is also a
* canonical default allocation strategy for functions that don't assign any "this" properties (vast majority
* of all functions), therefore saving some storage space in {@link RecompilableScriptFunctionData} that would
* otherwise be lost to identical tuples of (map, className, handle) fields.
*/
final class AllocationStrategy implements Serializable {
private static final long serialVersionUID = 1L;
private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
private static final AllocationStrategy DEFAULT_STRATEGY = new AllocationStrategy(new AllocatorDescriptor(0));
/** Allocator map from allocator descriptor */
private final PropertyMap allocatorMap;
/** Name of class where allocator function resides */
private final String allocatorClassName;
/** lazily generated allocator */
private transient MethodHandle allocator;
private AllocationStrategy(final AllocatorDescriptor desc) {
this.allocatorMap = desc.getAllocatorMap();
// These classes get loaded, so an interned variant of their name is most likely around anyway.
this.allocatorClassName = desc.getAllocatorClassName().intern();
}
private boolean matches(final AllocatorDescriptor desc) {
return desc.getAllocatorMap().size() == allocatorMap.size() &&
desc.getAllocatorClassName().equals(allocatorClassName);
}
static AllocationStrategy get(final AllocatorDescriptor desc) {
return DEFAULT_STRATEGY.matches(desc) ? DEFAULT_STRATEGY : new AllocationStrategy(desc);
}
PropertyMap getAllocatorMap() {
return allocatorMap;
}
ScriptObject allocate(final PropertyMap map) {
try {
if (allocator == null) {
allocator = MH.findStatic(LOOKUP, Context.forStructureClass(allocatorClassName),
CompilerConstants.ALLOCATE.symbolName(), MH.type(ScriptObject.class, PropertyMap.class));
}
return (ScriptObject)allocator.invokeExact(map);
} catch (final RuntimeException | Error e) {
throw e;
} catch (final Throwable t) {
throw new RuntimeException(t);
}
}
private Object readResolve() {
if(allocatorMap.size() == DEFAULT_STRATEGY.allocatorMap.size() &&
allocatorClassName.equals(DEFAULT_STRATEGY.allocatorClassName)) {
return DEFAULT_STRATEGY;
}
return this;
}
@Override
public String toString() {
return "AllocationStrategy[allocatorClassName=" + allocatorClassName + ", allocatorMap.size=" +
allocatorMap.size() + "]";
}
}

View File

@ -79,12 +79,6 @@ public interface CodeInstaller<T> {
*/ */
public long getUniqueScriptId(); public long getUniqueScriptId();
/**
* Get next unique eval id
* @return unique eval id
*/
public long getUniqueEvalId();
/** /**
* Store a compiled script for later reuse * Store a compiled script for later reuse
* @param source the script source * @param source the script source

View File

@ -27,7 +27,6 @@ package jdk.nashorn.internal.runtime;
import static jdk.nashorn.internal.lookup.Lookup.MH; import static jdk.nashorn.internal.lookup.Lookup.MH;
import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT; import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.isValid; import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.isValid;
import java.lang.invoke.CallSite; import java.lang.invoke.CallSite;
import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles;
@ -39,6 +38,7 @@ import java.util.Map;
import java.util.TreeMap; import java.util.TreeMap;
import java.util.function.Supplier; import java.util.function.Supplier;
import java.util.logging.Level; import java.util.logging.Level;
import jdk.internal.dynalink.linker.GuardedInvocation; import jdk.internal.dynalink.linker.GuardedInvocation;
import jdk.nashorn.internal.codegen.Compiler; import jdk.nashorn.internal.codegen.Compiler;
import jdk.nashorn.internal.codegen.Compiler.CompilationPhases; import jdk.nashorn.internal.codegen.Compiler.CompilationPhases;

View File

@ -196,16 +196,11 @@ public final class Context {
} }
@Override @Override
public long getUniqueEvalId() { public void storeScript(final String cacheKey, final Source source, final String mainClassName,
return context.getUniqueEvalId();
}
@Override
public void storeScript(final String classInfoFile, final Source source, final String mainClassName,
final Map<String,byte[]> classBytes, final Map<Integer, FunctionInitializer> initializers, final Map<String,byte[]> classBytes, final Map<Integer, FunctionInitializer> initializers,
final Object[] constants, final int compilationId) { final Object[] constants, final int compilationId) {
if (context.codeStore != null) { if (context.codeStore != null) {
context.codeStore.storeScript(classInfoFile, source, mainClassName, classBytes, initializers, constants, compilationId); context.codeStore.storeScript(cacheKey, source, mainClassName, classBytes, initializers, constants, compilationId);
} }
} }
@ -334,9 +329,6 @@ public final class Context {
/** Unique id for script. Used only when --loader-per-compile=false */ /** Unique id for script. Used only when --loader-per-compile=false */
private final AtomicLong uniqueScriptId; private final AtomicLong uniqueScriptId;
/** Unique id for 'eval' */
private final AtomicLong uniqueEvalId;
/** Optional class filter to use for Java classes. Can be null. */ /** Optional class filter to use for Java classes. Can be null. */
private final ClassFilter classFilter; private final ClassFilter classFilter;
@ -450,7 +442,6 @@ public final class Context {
this.uniqueScriptId = new AtomicLong(); this.uniqueScriptId = new AtomicLong();
} }
this.errors = errors; this.errors = errors;
this.uniqueEvalId = new AtomicLong();
// if user passed -classpath option, make a class loader with that and set it as // if user passed -classpath option, make a class loader with that and set it as
// thread context class loader so that script can access classes from that path. // thread context class loader so that script can access classes from that path.
@ -1190,10 +1181,6 @@ public final class Context {
}, CREATE_LOADER_ACC_CTXT); }, CREATE_LOADER_ACC_CTXT);
} }
private long getUniqueEvalId() {
return uniqueEvalId.getAndIncrement();
}
private long getUniqueScriptId() { private long getUniqueScriptId() {
return uniqueScriptId.getAndIncrement(); return uniqueScriptId.getAndIncrement();
} }

View File

@ -29,7 +29,9 @@ import static jdk.nashorn.internal.lookup.Lookup.MH;
import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.isValid; import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.isValid;
import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandle;
import jdk.internal.dynalink.linker.LinkRequest;
import jdk.nashorn.internal.codegen.ObjectClassGenerator; import jdk.nashorn.internal.codegen.ObjectClassGenerator;
import jdk.nashorn.internal.objects.Global;
/** /**
* This class represents the result from a find property search. * This class represents the result from a find property search.
@ -79,25 +81,17 @@ public final class FindProperty {
* @param programPoint program point, or INVALID_PROGRAM_POINT if pessimistic * @param programPoint program point, or INVALID_PROGRAM_POINT if pessimistic
* @return method handle for the getter * @return method handle for the getter
*/ */
public MethodHandle getGetter(final Class<?> type, final int programPoint) { public MethodHandle getGetter(final Class<?> type, final int programPoint, final LinkRequest request) {
final MethodHandle getter; final MethodHandle getter;
if (isValid(programPoint)) { if (isValid(programPoint)) {
getter = property.getOptimisticGetter(type, programPoint); getter = property.getOptimisticGetter(type, programPoint);
} else { } else {
getter = property.getGetter(type); getter = property.getGetter(type);
} }
return getGetterInner(getter);
}
private MethodHandle getGetterInner(final MethodHandle getter) {
if (property instanceof UserAccessorProperty) { if (property instanceof UserAccessorProperty) {
final UserAccessorProperty uc = (UserAccessorProperty)property; return insertAccessorsGetter((UserAccessorProperty) property, request, getter);
final ScriptObject owner = getOwner();
final ScriptObject container = (owner != null) ? owner : self;
return MH.insertArguments(getter, 0, uc.getAccessors(container));
} }
return getter; return getter;
} }
/** /**
@ -111,18 +105,31 @@ public final class FindProperty {
* *
* @return method handle for the getter * @return method handle for the getter
*/ */
public MethodHandle getSetter(final Class<?> type, final boolean strict) { public MethodHandle getSetter(final Class<?> type, final boolean strict, final LinkRequest request) {
final MethodHandle setter = property.getSetter(type, getOwner().getMap()); MethodHandle setter = property.getSetter(type, getOwner().getMap());
if (property instanceof UserAccessorProperty) { if (property instanceof UserAccessorProperty) {
final UserAccessorProperty uc = (UserAccessorProperty)property; setter = MH.insertArguments(setter, 1, strict ? property.getKey() : null);
final ScriptObject owner = getOwner(); return insertAccessorsGetter((UserAccessorProperty) property, request, setter);
final ScriptObject container = (owner != null) ? owner : self;
return MH.insertArguments(setter, 0, uc.getAccessors(container), strict ? property.getKey() : null);
} }
return setter; return setter;
} }
// Fold an accessor getter into the method handle of a user accessor property.
private MethodHandle insertAccessorsGetter(final UserAccessorProperty uap, final LinkRequest request, final MethodHandle mh) {
MethodHandle superGetter = uap.getAccessorsGetter();
if (isInherited()) {
superGetter = ScriptObject.addProtoFilter(superGetter, getProtoChainLength());
}
if (request != null && !(request.getReceiver() instanceof ScriptObject)) {
final MethodHandle wrapFilter = Global.getPrimitiveWrapFilter(request.getReceiver());
superGetter = MH.filterArguments(superGetter, 0, wrapFilter.asType(wrapFilter.type().changeReturnType(superGetter.type().parameterType(0))));
}
superGetter = MH.asType(superGetter, superGetter.type().changeParameterType(0, Object.class));
return MH.foldArguments(mh, superGetter);
}
/** /**
* Return the {@code ScriptObject} owning of the property: this means the prototype. * Return the {@code ScriptObject} owning of the property: this means the prototype.
* @return owner of property * @return owner of property
@ -136,7 +143,7 @@ public final class FindProperty {
* @return appropriate receiver * @return appropriate receiver
*/ */
public ScriptObject getGetterReceiver() { public ScriptObject getGetterReceiver() {
return property != null && property.hasGetterFunction(prototype) ? self : prototype; return property != null && property instanceof UserAccessorProperty ? self : prototype;
} }
/** /**

View File

@ -28,6 +28,8 @@ package jdk.nashorn.internal.runtime;
import static jdk.nashorn.internal.codegen.CompilerConstants.staticCall; import static jdk.nashorn.internal.codegen.CompilerConstants.staticCall;
import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCall; import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCall;
import static jdk.nashorn.internal.lookup.Lookup.MH; import static jdk.nashorn.internal.lookup.Lookup.MH;
import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.getProgramPoint;
import static jdk.nashorn.internal.runtime.logging.DebugLogger.quote; import static jdk.nashorn.internal.runtime.logging.DebugLogger.quote;
import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandle;
@ -370,22 +372,19 @@ public final class GlobalConstants implements Loggable {
* @param find property lookup * @param find property lookup
* @param receiver receiver * @param receiver receiver
* @param desc callsite descriptor * @param desc callsite descriptor
* @param request link request
* @param operator operator
* *
* @return resulting getter, or null if failed to create constant * @return resulting getter, or null if failed to create constant
*/ */
synchronized GuardedInvocation findGetMethod(final FindProperty find, final ScriptObject receiver, final CallSiteDescriptor desc, final LinkRequest request, final String operator) { synchronized GuardedInvocation findGetMethod(final FindProperty find, final ScriptObject receiver, final CallSiteDescriptor desc) {
if (GLOBAL_ONLY && !find.getOwner().isGlobal()) { // Also return null if property may have side effects
if ((GLOBAL_ONLY && !find.getOwner().isGlobal()) || find.getProperty() instanceof UserAccessorProperty) {
return null; return null;
} }
final int programPoint = NashornCallSiteDescriptor.isOptimistic(desc) ? final boolean isOptimistic = NashornCallSiteDescriptor.isOptimistic(desc);
NashornCallSiteDescriptor.getProgramPoint(desc) : final int programPoint = isOptimistic ? getProgramPoint(desc) : INVALID_PROGRAM_POINT;
UnwarrantedOptimismException.INVALID_PROGRAM_POINT; final Class<?> retType = desc.getMethodType().returnType();
final boolean isOptimistic = programPoint != UnwarrantedOptimismException.INVALID_PROGRAM_POINT; final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
final Class<?> retType = desc.getMethodType().returnType();
final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
final Access acc = getOrCreateSwitchPoint(name); final Access acc = getOrCreateSwitchPoint(name);

View File

@ -42,6 +42,7 @@ import jdk.nashorn.internal.codegen.Compiler;
import jdk.nashorn.internal.codegen.Compiler.CompilationPhases; import jdk.nashorn.internal.codegen.Compiler.CompilationPhases;
import jdk.nashorn.internal.codegen.CompilerConstants; import jdk.nashorn.internal.codegen.CompilerConstants;
import jdk.nashorn.internal.codegen.FunctionSignature; import jdk.nashorn.internal.codegen.FunctionSignature;
import jdk.nashorn.internal.codegen.ObjectClassGenerator.AllocatorDescriptor;
import jdk.nashorn.internal.codegen.OptimisticTypesPersistence; import jdk.nashorn.internal.codegen.OptimisticTypesPersistence;
import jdk.nashorn.internal.codegen.TypeMap; import jdk.nashorn.internal.codegen.TypeMap;
import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.codegen.types.Type;
@ -55,7 +56,6 @@ import jdk.nashorn.internal.parser.TokenType;
import jdk.nashorn.internal.runtime.logging.DebugLogger; import jdk.nashorn.internal.runtime.logging.DebugLogger;
import jdk.nashorn.internal.runtime.logging.Loggable; import jdk.nashorn.internal.runtime.logging.Loggable;
import jdk.nashorn.internal.runtime.logging.Logger; import jdk.nashorn.internal.runtime.logging.Logger;
/** /**
* This is a subclass that represents a script function that may be regenerated, * This is a subclass that represents a script function that may be regenerated,
* for example with specialization based on call site types, or lazily generated. * for example with specialization based on call site types, or lazily generated.
@ -81,26 +81,29 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
/** Token of this function within the source. */ /** Token of this function within the source. */
private final long token; private final long token;
/** Allocator map from makeMap() */ /**
private final PropertyMap allocatorMap; * Represents the allocation strategy (property map, script object class, and method handle) for when
* this function is used as a constructor. Note that majority of functions (those not setting any this.*
* properties) will share a single canonical "default strategy" instance.
*/
private final AllocationStrategy allocationStrategy;
/**
* Opaque object representing parser state at the end of the function. Used when reparsing outer function
* to help with skipping parsing inner functions.
*/
private final Object endParserState;
/** Code installer used for all further recompilation/specialization of this ScriptFunction */ /** Code installer used for all further recompilation/specialization of this ScriptFunction */
private transient CodeInstaller<ScriptEnvironment> installer; private transient CodeInstaller<ScriptEnvironment> installer;
/** Name of class where allocator function resides */
private final String allocatorClassName;
/** lazily generated allocator */
private transient MethodHandle allocator;
private final Map<Integer, RecompilableScriptFunctionData> nestedFunctions; private final Map<Integer, RecompilableScriptFunctionData> nestedFunctions;
/** Id to parent function if one exists */ /** Id to parent function if one exists */
private RecompilableScriptFunctionData parent; private RecompilableScriptFunctionData parent;
private final boolean isDeclared; /** Copy of the {@link FunctionNode} flags. */
private final boolean isAnonymous; private final int functionFlags;
private final boolean needsCallee;
private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup(); private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
@ -119,8 +122,7 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
* *
* @param functionNode functionNode that represents this function code * @param functionNode functionNode that represents this function code
* @param installer installer for code regeneration versions of this function * @param installer installer for code regeneration versions of this function
* @param allocatorClassName name of our allocator class, will be looked up dynamically if used as a constructor * @param allocationDescriptor descriptor for the allocation behavior when this function is used as a constructor
* @param allocatorMap allocator map to seed instances with, when constructing
* @param nestedFunctions nested function map * @param nestedFunctions nested function map
* @param externalScopeDepths external scope depths * @param externalScopeDepths external scope depths
* @param internalSymbols internal symbols to method, defined in its scope * @param internalSymbols internal symbols to method, defined in its scope
@ -128,30 +130,27 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
public RecompilableScriptFunctionData( public RecompilableScriptFunctionData(
final FunctionNode functionNode, final FunctionNode functionNode,
final CodeInstaller<ScriptEnvironment> installer, final CodeInstaller<ScriptEnvironment> installer,
final String allocatorClassName, final AllocatorDescriptor allocationDescriptor,
final PropertyMap allocatorMap,
final Map<Integer, RecompilableScriptFunctionData> nestedFunctions, final Map<Integer, RecompilableScriptFunctionData> nestedFunctions,
final Map<String, Integer> externalScopeDepths, final Map<String, Integer> externalScopeDepths,
final Set<String> internalSymbols) { final Set<String> internalSymbols) {
super(functionName(functionNode), super(functionName(functionNode),
Math.min(functionNode.getParameters().size(), MAX_ARITY), Math.min(functionNode.getParameters().size(), MAX_ARITY),
getFlags(functionNode)); getDataFlags(functionNode));
this.functionName = functionNode.getName(); this.functionName = functionNode.getName();
this.lineNumber = functionNode.getLineNumber(); this.lineNumber = functionNode.getLineNumber();
this.isDeclared = functionNode.isDeclared(); this.functionFlags = functionNode.getFlags() | (functionNode.needsCallee() ? FunctionNode.NEEDS_CALLEE : 0);
this.needsCallee = functionNode.needsCallee();
this.isAnonymous = functionNode.isAnonymous();
this.functionNodeId = functionNode.getId(); this.functionNodeId = functionNode.getId();
this.source = functionNode.getSource(); this.source = functionNode.getSource();
this.endParserState = functionNode.getEndParserState();
this.token = tokenFor(functionNode); this.token = tokenFor(functionNode);
this.installer = installer; this.installer = installer;
this.allocatorClassName = allocatorClassName; this.allocationStrategy = AllocationStrategy.get(allocationDescriptor);
this.allocatorMap = allocatorMap; this.nestedFunctions = smallMap(nestedFunctions);
this.nestedFunctions = nestedFunctions; this.externalScopeDepths = smallMap(externalScopeDepths);
this.externalScopeDepths = externalScopeDepths; this.internalSymbols = smallSet(new HashSet<>(internalSymbols));
this.internalSymbols = new HashSet<>(internalSymbols);
for (final RecompilableScriptFunctionData nfn : nestedFunctions.values()) { for (final RecompilableScriptFunctionData nfn : nestedFunctions.values()) {
assert nfn.getParent() == null; assert nfn.getParent() == null;
@ -161,6 +160,27 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
createLogger(); createLogger();
} }
private static <K, V> Map<K, V> smallMap(final Map<K, V> map) {
if (map == null || map.isEmpty()) {
return Collections.emptyMap();
} else if (map.size() == 1) {
final Map.Entry<K, V> entry = map.entrySet().iterator().next();
return Collections.singletonMap(entry.getKey(), entry.getValue());
} else {
return map;
}
}
private static <T> Set<T> smallSet(final Set<T> set) {
if (set == null || set.isEmpty()) {
return Collections.emptySet();
} else if (set.size() == 1) {
return Collections.singleton(set.iterator().next());
} else {
return set;
}
}
@Override @Override
public DebugLogger getLogger() { public DebugLogger getLogger() {
return log; return log;
@ -190,17 +210,30 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
* @return the external symbol table with proto depths * @return the external symbol table with proto depths
*/ */
public int getExternalSymbolDepth(final String symbolName) { public int getExternalSymbolDepth(final String symbolName) {
final Map<String, Integer> map = externalScopeDepths; final Integer depth = externalScopeDepths.get(symbolName);
if (map == null) {
return -1;
}
final Integer depth = map.get(symbolName);
if (depth == null) { if (depth == null) {
return -1; return -1;
} }
return depth; return depth;
} }
/**
* Returns the names of all external symbols this function uses.
* @return the names of all external symbols this function uses.
*/
public Set<String> getExternalSymbolNames() {
return Collections.unmodifiableSet(externalScopeDepths.keySet());
}
/**
* Returns the opaque object representing the parser state at the end of this function's body, used to
* skip parsing this function when reparsing its containing outer function.
* @return the object representing the end parser state
*/
public Object getEndParserState() {
return endParserState;
}
/** /**
* Get the parent of this RecompilableScriptFunctionData. If we are * Get the parent of this RecompilableScriptFunctionData. If we are
* a nested function, we have a parent. Note that "null" return value * a nested function, we have a parent. Note that "null" return value
@ -269,7 +302,7 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
@Override @Override
public boolean inDynamicContext() { public boolean inDynamicContext() {
return (flags & IN_DYNAMIC_CONTEXT) != 0; return getFunctionFlag(FunctionNode.IN_DYNAMIC_CONTEXT);
} }
private static String functionName(final FunctionNode fn) { private static String functionName(final FunctionNode fn) {
@ -293,7 +326,7 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
return Token.toDesc(TokenType.FUNCTION, position, length); return Token.toDesc(TokenType.FUNCTION, position, length);
} }
private static int getFlags(final FunctionNode functionNode) { private static int getDataFlags(final FunctionNode functionNode) {
int flags = IS_CONSTRUCTOR; int flags = IS_CONSTRUCTOR;
if (functionNode.isStrict()) { if (functionNode.isStrict()) {
flags |= IS_STRICT; flags |= IS_STRICT;
@ -307,37 +340,20 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
if (functionNode.isVarArg()) { if (functionNode.isVarArg()) {
flags |= IS_VARIABLE_ARITY; flags |= IS_VARIABLE_ARITY;
} }
if (functionNode.inDynamicContext()) {
flags |= IN_DYNAMIC_CONTEXT;
}
return flags; return flags;
} }
@Override @Override
PropertyMap getAllocatorMap() { PropertyMap getAllocatorMap() {
return allocatorMap; return allocationStrategy.getAllocatorMap();
} }
@Override @Override
ScriptObject allocate(final PropertyMap map) { ScriptObject allocate(final PropertyMap map) {
try { return allocationStrategy.allocate(map);
ensureHasAllocator(); //if allocatorClass name is set to null (e.g. for bound functions) we don't even try
return allocator == null ? null : (ScriptObject)allocator.invokeExact(map);
} catch (final RuntimeException | Error e) {
throw e;
} catch (final Throwable t) {
throw new RuntimeException(t);
}
}
private void ensureHasAllocator() throws ClassNotFoundException {
if (allocator == null && allocatorClassName != null) {
this.allocator = MH.findStatic(LOOKUP, Context.forStructureClass(allocatorClassName), CompilerConstants.ALLOCATE.symbolName(), MH.type(ScriptObject.class, PropertyMap.class));
}
} }
FunctionNode reparse() { FunctionNode reparse() {
final boolean isProgram = functionNodeId == FunctionNode.FIRST_FUNCTION_ID;
// NOTE: If we aren't recompiling the top-level program, we decrease functionNodeId 'cause we'll have a synthetic program node // NOTE: If we aren't recompiling the top-level program, we decrease functionNodeId 'cause we'll have a synthetic program node
final int descPosition = Token.descPosition(token); final int descPosition = Token.descPosition(token);
final Context context = Context.getContextTrusted(); final Context context = Context.getContextTrusted();
@ -346,18 +362,27 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
source, source,
new Context.ThrowErrorManager(), new Context.ThrowErrorManager(),
isStrict(), isStrict(),
functionNodeId - (isProgram ? 0 : 1),
lineNumber - 1, lineNumber - 1,
context.getLogger(Parser.class)); // source starts at line 0, so even though lineNumber is the correct declaration line, back off one to make it exclusive context.getLogger(Parser.class)); // source starts at line 0, so even though lineNumber is the correct declaration line, back off one to make it exclusive
if (isAnonymous) { if (getFunctionFlag(FunctionNode.IS_ANONYMOUS)) {
parser.setFunctionName(functionName); parser.setFunctionName(functionName);
} }
parser.setReparsedFunction(this);
final FunctionNode program = parser.parse(CompilerConstants.PROGRAM.symbolName(), descPosition, Token.descLength(token), true); final FunctionNode program = parser.parse(CompilerConstants.PROGRAM.symbolName(), descPosition,
// Parser generates a program AST even if we're recompiling a single function, so when we are only recompiling a Token.descLength(token), true);
// single function, extract it from the program. // Parser generates a program AST even if we're recompiling a single function, so when we are only
return (isProgram ? program : extractFunctionFromScript(program)).setName(null, functionName); // recompiling a single function, extract it from the program.
return (isProgram() ? program : extractFunctionFromScript(program)).setName(null, functionName);
}
private boolean getFunctionFlag(final int flag) {
return (functionFlags & flag) != 0;
}
private boolean isProgram() {
return getFunctionFlag(FunctionNode.IS_PROGRAM);
} }
TypeMap typeMap(final MethodType fnCallSiteType) { TypeMap typeMap(final MethodType fnCallSiteType) {
@ -546,7 +571,7 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
assert fns.size() == 1 : "got back more than one method in recompilation"; assert fns.size() == 1 : "got back more than one method in recompilation";
final FunctionNode f = fns.iterator().next(); final FunctionNode f = fns.iterator().next();
assert f.getId() == functionNodeId; assert f.getId() == functionNodeId;
if (!isDeclared && f.isDeclared()) { if (!getFunctionFlag(FunctionNode.IS_DECLARED) && f.isDeclared()) {
return f.clearFlag(null, FunctionNode.IS_DECLARED); return f.clearFlag(null, FunctionNode.IS_DECLARED);
} }
return f; return f;
@ -669,7 +694,15 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
@Override @Override
public boolean needsCallee() { public boolean needsCallee() {
return needsCallee; return getFunctionFlag(FunctionNode.NEEDS_CALLEE);
}
/**
* Returns the {@link FunctionNode} flags associated with this function data.
* @return the {@link FunctionNode} flags associated with this function data.
*/
public int getFunctionFlags() {
return functionFlags;
} }
@Override @Override
@ -720,21 +753,6 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
return null; return null;
} }
/**
* Get the uppermost parent, the program, for this data
* @return program
*/
public RecompilableScriptFunctionData getProgram() {
RecompilableScriptFunctionData program = this;
while (true) {
final RecompilableScriptFunctionData p = program.getParent();
if (p == null) {
return program;
}
program = p;
}
}
/** /**
* Check whether a certain name is a global symbol, i.e. only exists as defined * Check whether a certain name is a global symbol, i.e. only exists as defined
* in outermost scope and not shadowed by being parameter or assignment in inner * in outermost scope and not shadowed by being parameter or assignment in inner

View File

@ -90,8 +90,6 @@ public abstract class ScriptFunctionData implements Serializable {
public static final int USES_THIS = 1 << 4; public static final int USES_THIS = 1 << 4;
/** Is this a variable arity function? */ /** Is this a variable arity function? */
public static final int IS_VARIABLE_ARITY = 1 << 5; public static final int IS_VARIABLE_ARITY = 1 << 5;
/** Is this declared in a dynamic context */
public static final int IN_DYNAMIC_CONTEXT = 1 << 6;
/** Flag for strict or built-in functions */ /** Flag for strict or built-in functions */
public static final int IS_STRICT_OR_BUILTIN = IS_STRICT | IS_BUILTIN; public static final int IS_STRICT_OR_BUILTIN = IS_STRICT | IS_BUILTIN;

View File

@ -1050,7 +1050,7 @@ public abstract class ScriptObject implements PropertyAccess {
} }
private static int getIntValue(final FindProperty find, final int programPoint) { private static int getIntValue(final FindProperty find, final int programPoint) {
final MethodHandle getter = find.getGetter(int.class, programPoint); final MethodHandle getter = find.getGetter(int.class, programPoint, null);
if (getter != null) { if (getter != null) {
try { try {
return (int)getter.invokeExact((Object)find.getGetterReceiver()); return (int)getter.invokeExact((Object)find.getGetterReceiver());
@ -1065,7 +1065,7 @@ public abstract class ScriptObject implements PropertyAccess {
} }
private static long getLongValue(final FindProperty find, final int programPoint) { private static long getLongValue(final FindProperty find, final int programPoint) {
final MethodHandle getter = find.getGetter(long.class, programPoint); final MethodHandle getter = find.getGetter(long.class, programPoint, null);
if (getter != null) { if (getter != null) {
try { try {
return (long)getter.invokeExact((Object)find.getGetterReceiver()); return (long)getter.invokeExact((Object)find.getGetterReceiver());
@ -1080,7 +1080,7 @@ public abstract class ScriptObject implements PropertyAccess {
} }
private static double getDoubleValue(final FindProperty find, final int programPoint) { private static double getDoubleValue(final FindProperty find, final int programPoint) {
final MethodHandle getter = find.getGetter(double.class, programPoint); final MethodHandle getter = find.getGetter(double.class, programPoint, null);
if (getter != null) { if (getter != null) {
try { try {
return (double)getter.invokeExact((Object)find.getGetterReceiver()); return (double)getter.invokeExact((Object)find.getGetterReceiver());
@ -1971,7 +1971,7 @@ public abstract class ScriptObject implements PropertyAccess {
} }
} }
final GuardedInvocation cinv = Global.getConstants().findGetMethod(find, this, desc, request, operator); final GuardedInvocation cinv = Global.getConstants().findGetMethod(find, this, desc);
if (cinv != null) { if (cinv != null) {
return cinv; return cinv;
} }
@ -1983,7 +1983,7 @@ public abstract class ScriptObject implements PropertyAccess {
NashornCallSiteDescriptor.getProgramPoint(desc) : NashornCallSiteDescriptor.getProgramPoint(desc) :
UnwarrantedOptimismException.INVALID_PROGRAM_POINT; UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
mh = find.getGetter(returnType, programPoint); mh = find.getGetter(returnType, programPoint, request);
// Get the appropriate guard for this callsite and property. // Get the appropriate guard for this callsite and property.
final MethodHandle guard = NashornGuards.getGuard(this, property, desc, explicitInstanceOfCheck); final MethodHandle guard = NashornGuards.getGuard(this, property, desc, explicitInstanceOfCheck);
final ScriptObject owner = find.getOwner(); final ScriptObject owner = find.getOwner();
@ -1995,8 +1995,9 @@ public abstract class ScriptObject implements PropertyAccess {
mh = Lookup.emptyGetter(returnType); mh = Lookup.emptyGetter(returnType);
protoSwitchPoint = getProtoSwitchPoint(name, owner); protoSwitchPoint = getProtoSwitchPoint(name, owner);
} else if (!find.isSelf()) { } else if (!find.isSelf()) {
assert mh.type().returnType().equals(returnType) : "returntype mismatch for getter " + mh.type().returnType() + " != " + returnType; assert mh.type().returnType().equals(returnType) :
if (!property.hasGetterFunction(owner)) { "return type mismatch for getter " + mh.type().returnType() + " != " + returnType;
if (!(property instanceof UserAccessorProperty)) {
// Add a filter that replaces the self object with the prototype owning the property. // Add a filter that replaces the self object with the prototype owning the property.
mh = addProtoFilter(mh, find.getProtoChainLength()); mh = addProtoFilter(mh, find.getProtoChainLength());
} }
@ -2167,7 +2168,7 @@ public abstract class ScriptObject implements PropertyAccess {
} }
} }
final GuardedInvocation inv = new SetMethodCreator(this, find, desc, explicitInstanceOfCheck).createGuardedInvocation(); final GuardedInvocation inv = new SetMethodCreator(this, find, desc, request).createGuardedInvocation();
final GuardedInvocation cinv = Global.getConstants().findSetMethod(find, this, inv, desc, request); final GuardedInvocation cinv = Global.getConstants().findSetMethod(find, this, inv, desc, request);
if (cinv != null) { if (cinv != null) {
@ -2320,13 +2321,13 @@ public abstract class ScriptObject implements PropertyAccess {
find.isSelf()? find.isSelf()?
getKnownFunctionPropertyGuardSelf( getKnownFunctionPropertyGuardSelf(
getMap(), getMap(),
find.getGetter(Object.class, INVALID_PROGRAM_POINT), find.getGetter(Object.class, INVALID_PROGRAM_POINT, request),
func) func)
: :
//TODO this always does a scriptobject check //TODO this always does a scriptobject check
getKnownFunctionPropertyGuardProto( getKnownFunctionPropertyGuardProto(
getMap(), getMap(),
find.getGetter(Object.class, INVALID_PROGRAM_POINT), find.getGetter(Object.class, INVALID_PROGRAM_POINT, request),
find.getProtoChainLength(), find.getProtoChainLength(),
func), func),
getProtoSwitchPoint(NO_SUCH_PROPERTY_NAME, find.getOwner()), getProtoSwitchPoint(NO_SUCH_PROPERTY_NAME, find.getOwner()),

View File

@ -33,6 +33,7 @@ import java.lang.invoke.MethodHandle;
import java.lang.invoke.SwitchPoint; import java.lang.invoke.SwitchPoint;
import jdk.internal.dynalink.CallSiteDescriptor; import jdk.internal.dynalink.CallSiteDescriptor;
import jdk.internal.dynalink.linker.GuardedInvocation; import jdk.internal.dynalink.linker.GuardedInvocation;
import jdk.internal.dynalink.linker.LinkRequest;
import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor; import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;
import jdk.nashorn.internal.runtime.linker.NashornGuards; import jdk.nashorn.internal.runtime.linker.NashornGuards;
@ -48,7 +49,7 @@ final class SetMethodCreator {
private final FindProperty find; private final FindProperty find;
private final CallSiteDescriptor desc; private final CallSiteDescriptor desc;
private final Class<?> type; private final Class<?> type;
private final boolean explicitInstanceOfCheck; private final LinkRequest request;
/** /**
* Creates a new property setter method creator. * Creates a new property setter method creator.
@ -56,14 +57,15 @@ final class SetMethodCreator {
* @param find a result of a {@link ScriptObject#findProperty(String, boolean)} on the object for the property we * @param find a result of a {@link ScriptObject#findProperty(String, boolean)} on the object for the property we
* want to create a setter for. Can be null if the property does not yet exist on the object. * want to create a setter for. Can be null if the property does not yet exist on the object.
* @param desc the descriptor of the call site that triggered the property setter lookup * @param desc the descriptor of the call site that triggered the property setter lookup
* @param request the link request
*/ */
SetMethodCreator(final ScriptObject sobj, final FindProperty find, final CallSiteDescriptor desc, final boolean explicitInstanceOfCheck) { SetMethodCreator(final ScriptObject sobj, final FindProperty find, final CallSiteDescriptor desc, final LinkRequest request) {
this.sobj = sobj; this.sobj = sobj;
this.map = sobj.getMap(); this.map = sobj.getMap();
this.find = find; this.find = find;
this.desc = desc; this.desc = desc;
this.type = desc.getMethodType().parameterType(1); this.type = desc.getMethodType().parameterType(1);
this.explicitInstanceOfCheck = explicitInstanceOfCheck; this.request = request;
} }
@ -111,6 +113,7 @@ final class SetMethodCreator {
// getGuard() and getException() either both return null, or neither does. The reason for that is that now // getGuard() and getException() either both return null, or neither does. The reason for that is that now
// getGuard returns a map guard that casts its argument to ScriptObject, and if that fails, we need to // getGuard returns a map guard that casts its argument to ScriptObject, and if that fails, we need to
// relink on ClassCastException. // relink on ClassCastException.
final boolean explicitInstanceOfCheck = NashornGuards.explicitInstanceOfCheck(desc, request);
return new GuardedInvocation(methodHandle, NashornGuards.getGuard(sobj, property, desc, explicitInstanceOfCheck), return new GuardedInvocation(methodHandle, NashornGuards.getGuard(sobj, property, desc, explicitInstanceOfCheck),
(SwitchPoint)null, explicitInstanceOfCheck ? null : ClassCastException.class); (SwitchPoint)null, explicitInstanceOfCheck ? null : ClassCastException.class);
} }
@ -140,6 +143,7 @@ final class SetMethodCreator {
private SetMethod createExistingPropertySetter() { private SetMethod createExistingPropertySetter() {
final Property property = find.getProperty(); final Property property = find.getProperty();
final boolean isStrict = NashornCallSiteDescriptor.isStrict(desc);
final MethodHandle methodHandle; final MethodHandle methodHandle;
if (NashornCallSiteDescriptor.isDeclaration(desc)) { if (NashornCallSiteDescriptor.isDeclaration(desc)) {
@ -152,7 +156,7 @@ final class SetMethodCreator {
final PropertyMap oldMap = getMap(); final PropertyMap oldMap = getMap();
final Property newProperty = property.removeFlags(Property.NEEDS_DECLARATION); final Property newProperty = property.removeFlags(Property.NEEDS_DECLARATION);
final PropertyMap newMap = oldMap.replaceProperty(property, newProperty); final PropertyMap newMap = oldMap.replaceProperty(property, newProperty);
final MethodHandle fastSetter = find.replaceProperty(newProperty).getSetter(type, NashornCallSiteDescriptor.isStrict(desc)); final MethodHandle fastSetter = find.replaceProperty(newProperty).getSetter(type, isStrict, request);
final MethodHandle slowSetter = MH.insertArguments(ScriptObject.DECLARE_AND_SET, 1, getName()).asType(fastSetter.type()); final MethodHandle slowSetter = MH.insertArguments(ScriptObject.DECLARE_AND_SET, 1, getName()).asType(fastSetter.type());
// cas map used as guard, if true that means we can do the set fast // cas map used as guard, if true that means we can do the set fast
@ -161,14 +165,14 @@ final class SetMethodCreator {
casMap = MH.asType(casMap, casMap.type().changeParameterType(0, Object.class)); casMap = MH.asType(casMap, casMap.type().changeParameterType(0, Object.class));
methodHandle = MH.guardWithTest(casMap, fastSetter, slowSetter); methodHandle = MH.guardWithTest(casMap, fastSetter, slowSetter);
} else { } else {
methodHandle = find.getSetter(type, NashornCallSiteDescriptor.isStrict(desc)); methodHandle = find.getSetter(type, isStrict, request);
} }
assert methodHandle != null; assert methodHandle != null;
assert property != null; assert property != null;
final MethodHandle boundHandle; final MethodHandle boundHandle;
if (!property.hasSetterFunction(find.getOwner()) && find.isInherited()) { if (!(property instanceof UserAccessorProperty) && find.isInherited()) {
boundHandle = ScriptObject.addProtoFilter(methodHandle, find.getProtoChainLength()); boundHandle = ScriptObject.addProtoFilter(methodHandle, find.getProtoChainLength());
} else { } else {
boundHandle = methodHandle; boundHandle = methodHandle;

View File

@ -33,6 +33,8 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.function.Supplier; import java.util.function.Supplier;
import jdk.nashorn.internal.codegen.CompileUnit;
import jdk.nashorn.internal.runtime.logging.DebugLogger; import jdk.nashorn.internal.runtime.logging.DebugLogger;
import jdk.nashorn.internal.runtime.logging.Loggable; import jdk.nashorn.internal.runtime.logging.Loggable;
import jdk.nashorn.internal.runtime.logging.Logger; import jdk.nashorn.internal.runtime.logging.Logger;
@ -189,7 +191,7 @@ public final class Timing implements Loggable {
maxKeyLength++; maxKeyLength++;
final StringBuilder sb = new StringBuilder(); final StringBuilder sb = new StringBuilder();
sb.append("Accumulated complation phase Timings:\n\n"); sb.append("Accumulated compilation phase timings:\n\n");
for (final Map.Entry<String, Long> entry : timings.entrySet()) { for (final Map.Entry<String, Long> entry : timings.entrySet()) {
int len; int len;
@ -224,6 +226,9 @@ public final class Timing implements Loggable {
append((int)(knownTime * 100.0 / total)). append((int)(knownTime * 100.0 / total)).
append("%])"); append("%])");
sb.append("\n\nEmitted compile units: ").
append(CompileUnit.getEmittedUnitCount());
return sb.toString(); return sb.toString();
} }

View File

@ -34,8 +34,8 @@ import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import jdk.nashorn.internal.codegen.CompilerConstants;
import jdk.nashorn.internal.lookup.Lookup; import jdk.nashorn.internal.lookup.Lookup;
import jdk.nashorn.internal.runtime.linker.Bootstrap; import jdk.nashorn.internal.runtime.linker.Bootstrap;
@ -48,7 +48,7 @@ public final class UserAccessorProperty extends SpillProperty {
private static final long serialVersionUID = -5928687246526840321L; private static final long serialVersionUID = -5928687246526840321L;
static class Accessors { static final class Accessors {
Object getter; Object getter;
Object setter; Object setter;
@ -67,20 +67,20 @@ public final class UserAccessorProperty extends SpillProperty {
} }
} }
private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
/** Getter method handle */ /** Getter method handle */
private final static CompilerConstants.Call USER_ACCESSOR_GETTER = staticCall(MethodHandles.lookup(), UserAccessorProperty.class, private final static MethodHandle INVOKE_GETTER_ACCESSOR = findOwnMH_S("invokeGetterAccessor", Object.class, Accessors.class, Object.class);
"userAccessorGetter", Object.class, Accessors.class, Object.class);
/** Setter method handle */ /** Setter method handle */
private final static CompilerConstants.Call USER_ACCESSOR_SETTER = staticCall(MethodHandles.lookup(), UserAccessorProperty.class, private final static MethodHandle INVOKE_SETTER_ACCESSOR = findOwnMH_S("invokeSetterAccessor", void.class, Accessors.class, String.class, Object.class, Object.class);
"userAccessorSetter", void.class, Accessors.class, String.class, Object.class, Object.class);
/** Dynamic invoker for getter */ /** Dynamic invoker for getter */
private static final Object INVOKE_UA_GETTER = new Object(); private static final Object GETTER_INVOKER_KEY = new Object();
private static MethodHandle getINVOKE_UA_GETTER() { private static MethodHandle getINVOKE_UA_GETTER() {
return Context.getGlobal().getDynamicInvoker(INVOKE_UA_GETTER, return Context.getGlobal().getDynamicInvoker(GETTER_INVOKER_KEY,
new Callable<MethodHandle>() { new Callable<MethodHandle>() {
@Override @Override
public MethodHandle call() { public MethodHandle call() {
@ -91,10 +91,10 @@ public final class UserAccessorProperty extends SpillProperty {
} }
/** Dynamic invoker for setter */ /** Dynamic invoker for setter */
private static Object INVOKE_UA_SETTER = new Object(); private static Object SETTER_INVOKER_KEY = new Object();
private static MethodHandle getINVOKE_UA_SETTER() { private static MethodHandle getINVOKE_UA_SETTER() {
return Context.getGlobal().getDynamicInvoker(INVOKE_UA_SETTER, return Context.getGlobal().getDynamicInvoker(SETTER_INVOKER_KEY,
new Callable<MethodHandle>() { new Callable<MethodHandle>() {
@Override @Override
public MethodHandle call() { public MethodHandle call() {
@ -190,7 +190,7 @@ public final class UserAccessorProperty extends SpillProperty {
@Override @Override
public Object getObjectValue(final ScriptObject self, final ScriptObject owner) { public Object getObjectValue(final ScriptObject self, final ScriptObject owner) {
return userAccessorGetter(getAccessors((owner != null) ? owner : self), self); return invokeGetterAccessor(getAccessors((owner != null) ? owner : self), self);
} }
@Override @Override
@ -210,13 +210,13 @@ public final class UserAccessorProperty extends SpillProperty {
@Override @Override
public void setValue(final ScriptObject self, final ScriptObject owner, final Object value, final boolean strict) { public void setValue(final ScriptObject self, final ScriptObject owner, final Object value, final boolean strict) {
userAccessorSetter(getAccessors((owner != null) ? owner : self), strict ? getKey() : null, self, value); invokeSetterAccessor(getAccessors((owner != null) ? owner : self), strict ? getKey() : null, self, value);
} }
@Override @Override
public MethodHandle getGetter(final Class<?> type) { public MethodHandle getGetter(final Class<?> type) {
//this returns a getter on the format (Accessors, Object receiver) //this returns a getter on the format (Accessors, Object receiver)
return Lookup.filterReturnType(USER_ACCESSOR_GETTER.methodHandle(), type); return Lookup.filterReturnType(INVOKE_GETTER_ACCESSOR, type);
} }
@Override @Override
@ -260,7 +260,7 @@ public final class UserAccessorProperty extends SpillProperty {
@Override @Override
public MethodHandle getSetter(final Class<?> type, final PropertyMap currentMap) { public MethodHandle getSetter(final Class<?> type, final PropertyMap currentMap) {
return USER_ACCESSOR_SETTER.methodHandle(); return INVOKE_SETTER_ACCESSOR;
} }
@Override @Override
@ -269,11 +269,21 @@ public final class UserAccessorProperty extends SpillProperty {
return (value instanceof ScriptFunction) ? (ScriptFunction)value : null; return (value instanceof ScriptFunction) ? (ScriptFunction)value : null;
} }
/**
* Get the getter for the {@code Accessors} object.
* This is the the super {@code Object} type getter with {@code Accessors} return type.
*
* @return The getter handle for the Accessors
*/
MethodHandle getAccessorsGetter() {
return super.getGetter(Object.class).asType(MethodType.methodType(Accessors.class, Object.class));
}
// User defined getter and setter are always called by "dyn:call". Note that the user // User defined getter and setter are always called by "dyn:call". Note that the user
// getter/setter may be inherited. If so, proto is bound during lookup. In either // getter/setter may be inherited. If so, proto is bound during lookup. In either
// inherited or self case, slot is also bound during lookup. Actual ScriptFunction // inherited or self case, slot is also bound during lookup. Actual ScriptFunction
// to be called is retrieved everytime and applied. // to be called is retrieved everytime and applied.
static Object userAccessorGetter(final Accessors gs, final Object self) { private static Object invokeGetterAccessor(final Accessors gs, final Object self) {
final Object func = gs.getter; final Object func = gs.getter;
if (func instanceof ScriptFunction) { if (func instanceof ScriptFunction) {
try { try {
@ -288,7 +298,7 @@ public final class UserAccessorProperty extends SpillProperty {
return UNDEFINED; return UNDEFINED;
} }
static void userAccessorSetter(final Accessors gs, final String name, final Object self, final Object value) { private static void invokeSetterAccessor(final Accessors gs, final String name, final Object self, final Object value) {
final Object func = gs.setter; final Object func = gs.setter;
if (func instanceof ScriptFunction) { if (func instanceof ScriptFunction) {
try { try {
@ -303,4 +313,8 @@ public final class UserAccessorProperty extends SpillProperty {
} }
} }
private static MethodHandle findOwnMH_S(final String name, final Class<?> rtype, final Class<?>... types) {
return MH.findStatic(LOOKUP, UserAccessorProperty.class, name, MH.type(rtype, types));
}
} }

View File

@ -32,11 +32,10 @@ import java.lang.invoke.MethodType;
import jdk.internal.dynalink.CallSiteDescriptor; import jdk.internal.dynalink.CallSiteDescriptor;
import jdk.internal.dynalink.linker.GuardedInvocation; import jdk.internal.dynalink.linker.GuardedInvocation;
import jdk.internal.dynalink.linker.LinkRequest; import jdk.internal.dynalink.linker.LinkRequest;
import jdk.internal.dynalink.support.CallSiteDescriptorFactory;
import jdk.internal.dynalink.support.Guards; import jdk.internal.dynalink.support.Guards;
import jdk.nashorn.internal.lookup.Lookup;
import jdk.nashorn.internal.runtime.FindProperty; import jdk.nashorn.internal.runtime.FindProperty;
import jdk.nashorn.internal.runtime.ScriptObject; import jdk.nashorn.internal.runtime.ScriptObject;
import jdk.nashorn.internal.runtime.UserAccessorProperty;
/** /**
* Implements lookup of methods to link for dynamic operations on JavaScript primitive values (booleans, strings, and * Implements lookup of methods to link for dynamic operations on JavaScript primitive values (booleans, strings, and
@ -86,15 +85,6 @@ public final class PrimitiveLookup {
final ScriptObject wrappedReceiver, final MethodHandle wrapFilter, final ScriptObject wrappedReceiver, final MethodHandle wrapFilter,
final MethodHandle protoFilter) { final MethodHandle protoFilter) {
final CallSiteDescriptor desc = request.getCallSiteDescriptor(); final CallSiteDescriptor desc = request.getCallSiteDescriptor();
final String operator = CallSiteDescriptorFactory.tokenizeOperators(desc).get(0);
if ("setProp".equals(operator) || "setElem".equals(operator)) {
final MethodType type = desc.getMethodType();
MethodHandle method = MH.asType(Lookup.EMPTY_SETTER, MH.type(void.class, Object.class, type.parameterType(1)));
if (type.parameterCount() == 3) {
method = MH.dropArguments(method, 2, type.parameterType(2));
}
return new GuardedInvocation(method, guard);
}
if(desc.getNameTokenCount() > 2) { if(desc.getNameTokenCount() > 2) {
final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND); final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
@ -102,7 +92,7 @@ public final class PrimitiveLookup {
if(find == null) { if(find == null) {
// Give up early, give chance to BeanLinker and NashornBottomLinker to deal with it. // Give up early, give chance to BeanLinker and NashornBottomLinker to deal with it.
return null; return null;
} else if (find.isInherited() && !find.getProperty().hasGetterFunction(find.getOwner())) { } else if (find.isInherited() && !(find.getProperty() instanceof UserAccessorProperty)) {
// If property is found in the prototype object bind the method handle directly to // If property is found in the prototype object bind the method handle directly to
// the proto filter instead of going through wrapper instantiation below. // the proto filter instead of going through wrapper instantiation below.
final ScriptObject proto = wrappedReceiver.getProto(); final ScriptObject proto = wrappedReceiver.getProto();

View File

@ -246,7 +246,7 @@ public class Shell {
// For each file on the command line. // For each file on the command line.
for (final String fileName : files) { for (final String fileName : files) {
final FunctionNode functionNode = new Parser(env, sourceFor(fileName, new File(fileName)), errors, env._strict, FunctionNode.FIRST_FUNCTION_ID, 0, context.getLogger(Parser.class)).parse(); final FunctionNode functionNode = new Parser(env, sourceFor(fileName, new File(fileName)), errors, env._strict, 0, context.getLogger(Parser.class)).parse();
if (errors.getNumberOfErrors() != 0) { if (errors.getNumberOfErrors() != 0) {
return COMPILATION_ERROR; return COMPILATION_ERROR;

View File

@ -41,6 +41,6 @@ str +="g()";
try { try {
eval(str); eval(str);
} catch (e) { } catch (e) {
print(e.stack.replace(/\\/g, '/').replace(/<eval>@[0-9]+/, '<eval>@<id>')); print(e.stack.replace(/\\/g, '/'));
} }

View File

@ -1,3 +1,3 @@
ReferenceError: "g" is not defined ReferenceError: "g" is not defined
at <program> (test/script/basic/JDK-8030182_2.js#42:4<eval>@<id>:-1) at <program> (test/script/basic/JDK-8030182_2.js#42:4<eval>:-1)
at <program> (test/script/basic/JDK-8030182_2.js:42) at <program> (test/script/basic/JDK-8030182_2.js:42)

View File

@ -25,11 +25,10 @@
* JDK-8048079: Persistent code store is broken after optimistic types merge * JDK-8048079: Persistent code store is broken after optimistic types merge
* *
* @test * @test
* @run * @runif external.prototype
* @option -pcc * @option -pcc
* @option -Dnashorn.persistent.code.cache=build/nashorn_code_cache * @option -Dnashorn.persistent.code.cache=build/nashorn_code_cache
* @fork * @fork
*/ */
load(__DIR__ + 'prototype.js'); load(__DIR__ + 'prototype.js');
load(__DIR__ + 'yui.js');

View File

@ -0,0 +1 @@
parsed and compiled ok prototype.js

View File

@ -25,11 +25,10 @@
* JDK-8048079: Persistent code store is broken after optimistic types merge * JDK-8048079: Persistent code store is broken after optimistic types merge
* *
* @test * @test
* @run * @runif external.yui
* @option -pcc * @option -pcc
* @option -Dnashorn.persistent.code.cache=build/nashorn_code_cache * @option -Dnashorn.persistent.code.cache=build/nashorn_code_cache
* @fork * @fork
*/ */
load(__DIR__ + 'prototype.js');
load(__DIR__ + 'yui.js'); load(__DIR__ + 'yui.js');

View File

@ -1,3 +1,2 @@
parsed and compiled ok prototype.js
parsed and compiled ok yui-min.js parsed and compiled ok yui-min.js
parsed and compiled ok yui.js parsed and compiled ok yui.js

View File

@ -0,0 +1,34 @@
/*
* Copyright (c) 2010, 2014, 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.
*/
/**
* JDK-8048079: Persistent code store is broken after optimistic types merge.
* Same script as JDK-8048079_1a.js to exercise code cache.
* @test
* @runif external.prototype
* @option -pcc
* @option -Dnashorn.persistent.code.cache=build/nashorn_code_cache
* @fork
*/
load(__DIR__ + 'prototype.js');

View File

@ -0,0 +1 @@
parsed and compiled ok prototype.js

View File

@ -0,0 +1,34 @@
/*
* Copyright (c) 2010, 2014, 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.
*/
/**
* JDK-8048079: Persistent code store is broken after optimistic types merge
* Same script as JDK-8048079_1b.js to exercise code cache again.
* @test
* @runif external.yui
* @option -pcc
* @option -Dnashorn.persistent.code.cache=build/nashorn_code_cache
* @fork
*/
load(__DIR__ + 'yui.js');

View File

@ -1,3 +1,2 @@
parsed and compiled ok prototype.js
parsed and compiled ok yui-min.js parsed and compiled ok yui-min.js
parsed and compiled ok yui.js parsed and compiled ok yui.js

View File

@ -0,0 +1,48 @@
/*
* Copyright (c) 2014, 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.
*/
/**
* JDK-8058179: Global constants get in the way of self-modifying properties
*
* @test
* @run
*/
var global = this;
Object.defineProperty(global, "value", {
get: function() {
print("getting value");
global["value"] = "value 2";
return "value 1";
},
set: function(value) {
print("setting value: " + value);
delete global["value"];
global["value"] = value;
},
configurable: true
});
print(value);
print(value);

View File

@ -0,0 +1,4 @@
getting value
setting value: value 2
value 1
value 2

View File

@ -1,3 +1,3 @@
SyntaxError: test/script/basic/es6/const-empty.js#33:4<eval>@1:2:7 Missing assignment to constant "x" SyntaxError: test/script/basic/es6/const-empty.js#33:4<eval>:2:7 Missing assignment to constant "x"
const x; const x;
^ ^

View File

@ -0,0 +1,59 @@
/*
* Copyright (c) 2010, 2014, 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.
*/
/**
* JDK-8057678: Tests for let&const keywords in Nashorn
*
* @test
* @run
* @option --language=es6
* @option -scripting
*/
function tryIt (code) {
try {
eval(code)
} catch (e) {
print(e)
}
}
tryIt(<<CODE
"use strict";
const x = 2;
var x = {};
CODE)
tryIt(<<CODE
"use strict";
var x = 2;
const x = {};
CODE)
tryIt(<<CODE
"use strict";
function x () {}
const x = 5;
CODE)

View File

@ -0,0 +1,9 @@
SyntaxError: test/script/basic/es6/const-redeclare-extra.js#36:8<eval>:3:8 Variable "x" has already been declared
var x = {};
^
SyntaxError: test/script/basic/es6/const-redeclare-extra.js#36:8<eval>:2:8 Variable "x" has already been declared
var x = 2;
^
SyntaxError: test/script/basic/es6/const-redeclare-extra.js#36:8<eval>:2:13 Variable "x" has already been declared
function x () {}
^

View File

@ -1,3 +1,3 @@
SyntaxError: test/script/basic/es6/const-redeclare.js#33:4<eval>@1:2:6 Variable "x" has already been declared SyntaxError: test/script/basic/es6/const-redeclare.js#33:4<eval>:2:6 Variable "x" has already been declared
const x = 2; const x = 2;
^ ^

View File

@ -0,0 +1,70 @@
/*
* Copyright (c) 2010, 2014, 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.
*/
/**
* JDK-8057678: Tests for let&const keywords in Nashorn
*
* @test
* @run
* @option --language=es6
* @option -scripting
*/
function tryIt (code) {
try {
eval(code)
} catch (e) {
print(e)
}
}
tryIt(<<CODE
"use strict";
let x = 2;
const x = function (a,b,c) {};
CODE)
tryIt(<<CODE
"use strict";
let x = {};
var x = 2;
CODE)
tryIt(<<CODE
"use strict";
var x = 2;
let x = undefined;
CODE)
tryIt(<<CODE
"use strict";
const x = function (){};
let x = {};
CODE)
tryIt(<<CODE
"use strict";
let a = 2;
function a () {};
CODE)

View File

@ -0,0 +1,15 @@
SyntaxError: test/script/basic/es6/let-redeclare-extra.js#35:8<eval>:2:8 Variable "x" has already been declared
let x = 2;
^
SyntaxError: test/script/basic/es6/let-redeclare-extra.js#35:8<eval>:3:8 Variable "x" has already been declared
var x = 2;
^
SyntaxError: test/script/basic/es6/let-redeclare-extra.js#35:8<eval>:2:8 Variable "x" has already been declared
var x = 2;
^
SyntaxError: test/script/basic/es6/let-redeclare-extra.js#35:8<eval>:2:10 Variable "x" has already been declared
const x = function (){};
^
SyntaxError: test/script/basic/es6/let-redeclare-extra.js#35:8<eval>:3:13 Variable "a" has already been declared
function a () {};
^

View File

@ -1,3 +1,3 @@
SyntaxError: test/script/basic/es6/let-redeclare.js#33:4<eval>@1:2:4 Variable "x" has already been declared SyntaxError: test/script/basic/es6/let-redeclare.js#33:4<eval>:2:4 Variable "x" has already been declared
let x = 2; let x = 2;
^ ^

View File

@ -0,0 +1,123 @@
/*
* Copyright (c) 2014, 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.
*/
/**
* JDK-8057678: Tests for let&const keywords in Nashorn
*
* @test
* @run
* @option --language=es6
* @option -scripting
*/
function tryIt(code) {
try {
eval(code)
} catch (e) {
print(e)
}
}
tryIt(<<CODE
function a () {
this.val = 41
let self = this
this.b = function () {
return self.val;
}
}
c = new a()
print(c.b.call(null))
CODE)
tryIt(<<CODE
function a () {
this.val = 42
let self = this
this.b = function () {
return this.val;
}.bind(self)
}
c = new a()
print(c.b.call(null))
CODE)
tryIt(<<CODE
function a () {
this.val = 43
const self = this
this.b = function () {
return self.val;
}
}
c = new a()
print(c.b.call(null))
CODE)
tryIt(<<CODE
function a () {
this.val = 44
const self = this
this.b = function () {
return this.val;
}.bind(self)
}
c = new a()
print(c.b.call(null))
CODE)
tryIt(<<CODE
let a = {name : 'test'}
let f = function () {
print(this.name)
}
let nf = f.bind(a)
nf()
if (true) {
let a = null
nf()
}
nf()
CODE)
tryIt(<<CODE
let arr = []
for (let i = 0; i < 3; i++) {
arr[i] = function(){return i;}
}
for (let i in arr) {
print(arr[i]())
}
arr = []
for (var i = 0; i < 3; i++) {
(function(i){
arr[i] = function(){return i;}
})(i)
}
for (let i in arr) {
print(arr[i]())
}
CODE)

View File

@ -0,0 +1,13 @@
41
42
43
44
test
test
test
3
3
3
0
1
2

View File

@ -0,0 +1,71 @@
/*
* Copyright (c) 2010, 2014, 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.
*/
/**
* JDK-8057678: Tests for let&const keywords in Nashorn
*
* @test
* @run
* @option --language=es6
* @option -scripting
*/
function tryIt (code) {
try {
eval(code)
} catch (e) {
print(e)
}
}
tryIt(<<CODE
let a = 23
if (true) {
a--
let a = 43;
}
CODE)
tryIt(<<CODE
const a = 23
if (true) {
a--
const a = 43;
}
CODE)
tryIt(<<CODE
let a = 23
if (true) {
a--
const a = 43;
}
CODE)
tryIt(<<CODE
const a = 23
if (true) {
a--
let a = 43;
}
CODE)

View File

@ -0,0 +1,8 @@
ReferenceError: "a" is not defined
SyntaxError: test/script/basic/es6/let_const_reuse.js#35:9<eval>:3:8 Assignment to constant "a"
a--
^
SyntaxError: test/script/basic/es6/let_const_reuse.js#35:9<eval>:3:8 Assignment to constant "a"
a--
^
ReferenceError: "a" is not defined

View File

@ -0,0 +1,73 @@
/*
* Copyright (c) 2010, 2014, 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.
*/
/**
* JDK-8057678: Tests for let&const keywords in Nashorn
*
* @test
* @run
* @option --language=es6
* @option -scripting
*/
function tryIt (code) {
try {
eval(code)
} catch (e) {
print(e)
}
}
tryIt(<<CODE
let a = function () { var a = "Hello World!"; return a; }
print(typeof a)
{
let a = 34;
print(typeof a)
if (true) {
let c = 54.7
var d = c
print(typeof c)
print(typeof d)
}
}
print(typeof a)
print(typeof a())
print(typeof c)
print(typeof d)
print(d)
CODE)
tryIt(<<CODE
let a = {}
if (true) {
function a () {
print (typeof a)
return 'Hello World!'
}
print(typeof a)
print(a())
}
print(typeof a)
CODE)

View File

@ -0,0 +1,13 @@
function
number
number
number
function
string
undefined
number
54.7
function
function
Hello World!
object

View File

@ -0,0 +1,80 @@
/*
* Copyright (c) 2010, 2014, 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.
*/
/**
* JDK-8057678: Tests for let&const keywords in Nashorn
*
* @test
* @run
* @option --language=es6
* @option -scripting
*/
function tryIt (code) {
try {
eval(code)
} catch (e) {
print(e)
}
}
tryIt(<<CODE
let a = 2;
do {
a--;
let b = a;
} while (a > 0);
print(a)
print(b)
CODE)
tryIt(<<CODE
let a = 2
while(a > 0) {
a--
let b = a
}
print(a)
print(b)
CODE)
tryIt(<<CODE
let a = 2
while(a > 0) {
a--
const b = a
}
print(a)
print(b)
CODE)
tryIt(<<CODE
let a = 2;
do {
a--;
const b = a;
} while (a > 0);
print(a)
print(b)
CODE)

View File

@ -0,0 +1,8 @@
0
ReferenceError: "b" is not defined
0
ReferenceError: "b" is not defined
0
ReferenceError: "b" is not defined
0
ReferenceError: "b" is not defined

View File

@ -36,13 +36,18 @@ var trees = new Array("redwood", "bay", "cedar", "oak");
// Testing conditional operator // Testing conditional operator
print(inspect("" ? b : x.a, "ternary operator")) print(inspect("" ? b : x.a, "ternary operator"))
print(inspect(x.b ? b : x.a, "ternary operator")) var b1 = b;
print(inspect(c ? b : a, "ternary operator")) print(inspect(x.b ? b1 : x.a, "ternary operator"))
print(inspect(!c ? b : a, "ternary operator")) var b2 = b;
print(inspect(d ? b : x.c, "ternary operator")) print(inspect(c ? b2 : a, "ternary operator"))
var b3 = b;
print(inspect(!c ? b3 : a, "ternary operator"))
var b4 = b;
print(inspect(d ? b4 : x.c, "ternary operator"))
print(inspect(x.c ? a : c, "ternary operator")) print(inspect(x.c ? a : c, "ternary operator"))
print(inspect(c ? d : a, "ternary operator")) print(inspect(c ? d : a, "ternary operator"))
print(inspect(c ? +a : b, "ternary operator")) var b5 = b;
print(inspect(c ? +a : b5, "ternary operator"))
// Testing format methods // Testing format methods
print(inspect(b.toFixed(2), "global double toFixed()")) print(inspect(b.toFixed(2), "global double toFixed()"))
@ -53,11 +58,14 @@ print(inspect(b.toExponential(2), "global double toExponential()"))
print(inspect(trees[1], "member object")) print(inspect(trees[1], "member object"))
trees[1] = undefined; trees[1] = undefined;
print(inspect(trees[1], "member undefined")) print(inspect(trees[1], "member undefined"))
print(inspect(1 in trees ? b : a, "conditional on array member")) var b6=b;
print(inspect(1 in trees ? b6 : a, "conditional on array member"))
delete trees[2] delete trees[2]
print(inspect(2 in trees ? b : a, "conditional on array member")) var b7=b;
print(inspect(2 in trees ? b7 : a, "conditional on array member"))
print(inspect(3 in trees ? trees[2]="bay" : a, "conditional on array member")) print(inspect(3 in trees ? trees[2]="bay" : a, "conditional on array member"))
print(inspect("oak" in trees ? b : a, "conditional on array member")) var b8=b;
print(inspect("oak" in trees ? b8 : a, "conditional on array member"))
// Testing nested functions and return value // Testing nested functions and return value
function f1() { function f1() {

View File

@ -30,7 +30,5 @@
* @fork * @fork
*/ */
load(__DIR__ + 'prototype.js');
load(__DIR__ + 'yui.js');
load(__DIR__ + 'NASHORN-689.js'); load(__DIR__ + 'NASHORN-689.js');
load(__DIR__ + 'NASHORN-58.js'); load(__DIR__ + 'NASHORN-58.js');

View File

@ -1,6 +1,3 @@
parsed and compiled ok prototype.js
parsed and compiled ok yui-min.js
parsed and compiled ok yui.js
a=10 a=10
a=9 a=9
a=8 a=8

View File

@ -0,0 +1,33 @@
/*
* 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.
*/
/**
* Test various scripts with low splitter threshold
*
* @test
* @option -Dnashorn.compiler.splitter.threshold=200
* @runif external.prototype
* @fork
*/
load(__DIR__ + 'prototype.js');

View File

@ -0,0 +1 @@
parsed and compiled ok prototype.js

View File

@ -0,0 +1,33 @@
/*
* 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.
*/
/**
* Test various scripts with low splitter threshold
*
* @test
* @option -Dnashorn.compiler.splitter.threshold=200
* @runif external.yui
* @fork
*/
load(__DIR__ + 'yui.js');

View File

@ -0,0 +1,2 @@
parsed and compiled ok yui-min.js
parsed and compiled ok yui.js

View File

@ -407,6 +407,75 @@ public class ScopeTest {
Assert.assertEquals(e.eval(sharedScript, newCtxt), "newer context"); Assert.assertEquals(e.eval(sharedScript, newCtxt), "newer context");
} }
/**
* Test multi-threaded access to prototype user accessor properties for shared script classes with multiple globals.
*/
@Test
public static void multiThreadedAccessorTest() throws ScriptException, InterruptedException {
final ScriptEngineManager m = new ScriptEngineManager();
final ScriptEngine e = m.getEngineByName("nashorn");
final Bindings b = e.createBindings();
final ScriptContext origContext = e.getContext();
final ScriptContext newCtxt = new SimpleScriptContext();
newCtxt.setBindings(b, ScriptContext.ENGINE_SCOPE);
e.eval("Object.defineProperty(Object.prototype, 'foo', { get: function() 'original context' })", origContext);
e.eval("Object.defineProperty(Object.prototype, 'foo', { get: function() 'new context', configurable: true })", newCtxt);
final String sharedScript = "({}).foo";
final Thread t1 = new Thread(new ScriptRunner(e, origContext, sharedScript, "original context", 1000));
final Thread t2 = new Thread(new ScriptRunner(e, newCtxt, sharedScript, "new context", 1000));
t1.start();
t2.start();
t1.join();
t2.join();
final Object obj3 = e.eval("delete Object.prototype.foo; Object.prototype.foo = 'newer context';", newCtxt);
assertEquals(obj3, "newer context");
final Thread t3 = new Thread(new ScriptRunner(e, origContext, sharedScript, "original context", 1000));
final Thread t4 = new Thread(new ScriptRunner(e, newCtxt, sharedScript, "newer context", 1000));
t3.start();
t4.start();
t3.join();
t4.join();
}
/**
* Test multi-threaded access to primitive prototype user accessor properties for shared script classes with multiple globals.
*/
@Test
public static void multiThreadedPrimitiveAccessorTest() throws ScriptException, InterruptedException {
final ScriptEngineManager m = new ScriptEngineManager();
final ScriptEngine e = m.getEngineByName("nashorn");
final Bindings b = e.createBindings();
final ScriptContext origContext = e.getContext();
final ScriptContext newCtxt = new SimpleScriptContext();
newCtxt.setBindings(b, ScriptContext.ENGINE_SCOPE);
e.eval("Object.defineProperty(String.prototype, 'foo', { get: function() 'original context' })", origContext);
e.eval("Object.defineProperty(String.prototype, 'foo', { get: function() 'new context' })", newCtxt);
final String sharedScript = "''.foo";
final Thread t1 = new Thread(new ScriptRunner(e, origContext, sharedScript, "original context", 1000));
final Thread t2 = new Thread(new ScriptRunner(e, newCtxt, sharedScript, "new context", 1000));
t1.start();
t2.start();
t1.join();
t2.join();
final Object obj3 = e.eval("delete String.prototype.foo; Object.prototype.foo = 'newer context';", newCtxt);
assertEquals(obj3, "newer context");
final Thread t3 = new Thread(new ScriptRunner(e, origContext, sharedScript, "original context", 1000));
final Thread t4 = new Thread(new ScriptRunner(e, newCtxt, sharedScript, "newer context", 1000));
t3.start();
t4.start();
t3.join();
t4.join();
}
/** /**
* Test multi-threaded scope function invocation for shared script classes with multiple globals. * Test multi-threaded scope function invocation for shared script classes with multiple globals.
*/ */

View File

@ -261,14 +261,17 @@ final class TestFinder {
isTest = false; isTest = false;
isNotTest = true; isNotTest = true;
break; break;
case "@runif": case "@runif": {
if (System.getProperty(scanner.next()) != null) { final String prop = scanner.next();
if (System.getProperty(prop) != null) {
shouldRun = true; shouldRun = true;
} else { } else {
factory.log("WARNING: (" + prop + ") skipping " + testFile);
isTest = false; isTest = false;
isNotTest = true; isNotTest = true;
} }
break; break;
}
case "@run": case "@run":
shouldRun = true; shouldRun = true;
break; break;