Merge
This commit is contained in:
commit
87d4175a75
@ -6,8 +6,7 @@ dist/*
|
||||
/nbproject/private/
|
||||
private.xml
|
||||
private.properties
|
||||
webrev/*
|
||||
webrev.zip
|
||||
^webrev
|
||||
.classpath
|
||||
*.class
|
||||
*.clazz
|
||||
|
@ -194,12 +194,12 @@ final class AssignSymbols extends NodeVisitor<LexicalContext> implements Loggabl
|
||||
*/
|
||||
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.
|
||||
//
|
||||
body.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
|
||||
@Override
|
||||
public boolean enterFunctionNode(final FunctionNode nestedFn) {
|
||||
// Don't descend into nested functions
|
||||
return false;
|
||||
protected boolean enterDefault(final Node node) {
|
||||
// Don't bother visiting expressions; var is a statement, it can't be inside an expression.
|
||||
// This will also prevent visiting nested functions (as FunctionNode is an expression).
|
||||
return !(node instanceof Expression);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -443,12 +443,27 @@ final class AssignSymbols extends NodeVisitor<LexicalContext> implements Loggabl
|
||||
|
||||
if (lc.isFunctionBody()) {
|
||||
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();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean isUnparsedFunction(final FunctionNode fn) {
|
||||
return compiler.isOnDemandCompilation() && fn != lc.getOutermostFunction();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean enterCatchNode(final CatchNode catchNode) {
|
||||
final IdentNode exception = catchNode.getException();
|
||||
@ -492,18 +507,13 @@ final class AssignSymbols extends NodeVisitor<LexicalContext> implements Loggabl
|
||||
|
||||
@Override
|
||||
public boolean enterFunctionNode(final FunctionNode functionNode) {
|
||||
// TODO: once we have information on symbols used by nested functions, we can stop descending into nested
|
||||
// functions with on-demand compilation, e.g. add
|
||||
// if(!thisProperties.isEmpty() && env.isOnDemandCompilation()) {
|
||||
// return false;
|
||||
// }
|
||||
start(functionNode, false);
|
||||
|
||||
thisProperties.push(new HashSet<String>());
|
||||
|
||||
//an outermost function in our lexical context that is not a program
|
||||
//is possible - it is a function being compiled lazily
|
||||
if (functionNode.isDeclared()) {
|
||||
// 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();
|
||||
if (blocks.hasNext()) {
|
||||
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;
|
||||
}
|
||||
|
||||
@ -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
|
||||
* 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
|
||||
*/
|
||||
@ -733,14 +748,20 @@ final class AssignSymbols extends NodeVisitor<LexicalContext> implements Loggabl
|
||||
|
||||
@Override
|
||||
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
|
||||
// just an optimization -- runtime type calculation is not used when the compilation is not an on-demand
|
||||
// optimistic compilation, so we can skip locals marking then.
|
||||
// It's not necessary to guard the marking of symbols as locals with this "if" condition for
|
||||
// correctness, it's just an optimization -- runtime type calculation is not used when the compilation
|
||||
// is not an on-demand optimistic compilation, so we can skip locals marking then.
|
||||
if (compiler.useOptimisticTypes() && compiler.isOnDemandCompilation()) {
|
||||
for (final Symbol symbol: block.getSymbols()) {
|
||||
if (!symbol.isScope()) {
|
||||
assert symbol.isVar() || symbol.isParam();
|
||||
compiler.declareLocalSymbol(symbol.getName());
|
||||
// OTOH, we must not declare symbols from nested functions to be locals. As we're doing on-demand
|
||||
// compilation, and we're skipping parsing the function bodies for nested functions, this
|
||||
// basically only means their parameters. It'd be enough to mistakenly declare to be a local a
|
||||
// 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
|
||||
public Node leaveFunctionNode(final FunctionNode functionNode) {
|
||||
|
||||
return markProgramBlock(
|
||||
final FunctionNode finalizedFunction;
|
||||
if (isUnparsedFunction(functionNode)) {
|
||||
finalizedFunction = functionNode;
|
||||
} else {
|
||||
finalizedFunction =
|
||||
markProgramBlock(
|
||||
removeUnusedSlots(
|
||||
createSyntheticInitializers(
|
||||
finalizeParameters(
|
||||
lc.applyTopFlags(functionNode))))
|
||||
.setThisProperties(lc, thisProperties.pop().size())
|
||||
.setState(lc, CompilationState.SYMBOLS_ASSIGNED));
|
||||
.setThisProperties(lc, thisProperties.pop().size()));
|
||||
}
|
||||
return finalizedFunction.setState(lc, CompilationState.SYMBOLS_ASSIGNED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveIdentNode(final IdentNode identNode) {
|
||||
final String name = identNode.getName();
|
||||
|
||||
if (identNode.isPropertyName()) {
|
||||
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();
|
||||
|
||||
Symbol symbol = findSymbol(block, name);
|
||||
@ -847,24 +889,11 @@ final class AssignSymbols extends NodeVisitor<LexicalContext> implements Loggabl
|
||||
maybeForceScope(symbol);
|
||||
} else {
|
||||
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);
|
||||
|
||||
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);
|
||||
return symbol;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -912,7 +941,6 @@ final class AssignSymbols extends NodeVisitor<LexicalContext> implements Loggabl
|
||||
return functionNode;
|
||||
}
|
||||
|
||||
assert functionNode.getId() == 1;
|
||||
return functionNode.setBody(lc, functionNode.getBody().setFlag(lc, Block.IS_GLOBAL_SCOPE));
|
||||
}
|
||||
|
||||
|
@ -59,6 +59,7 @@ import java.security.PrivilegedAction;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import jdk.internal.org.objectweb.asm.ClassWriter;
|
||||
import jdk.internal.org.objectweb.asm.MethodVisitor;
|
||||
import jdk.internal.org.objectweb.asm.util.TraceClassVisitor;
|
||||
@ -135,6 +136,16 @@ public class ClassEmitter implements Emitter {
|
||||
/** Set of constants access methods required. */
|
||||
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
|
||||
* abstraction towards ASM or other code generator below
|
||||
@ -146,6 +157,11 @@ public class ClassEmitter implements Emitter {
|
||||
this.context = context;
|
||||
this.cw = cw;
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
@ -359,9 +407,16 @@ public class ClassEmitter implements Emitter {
|
||||
*/
|
||||
@Override
|
||||
public void end() {
|
||||
assert classStarted;
|
||||
assert classStarted : "class not started for " + unitClassName;
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
@ -419,6 +474,8 @@ public class ClassEmitter implements Emitter {
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@ -446,6 +503,8 @@ public class ClassEmitter implements Emitter {
|
||||
* @return method emitter to use for weaving this method
|
||||
*/
|
||||
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));
|
||||
}
|
||||
|
||||
@ -471,6 +530,8 @@ public class ClassEmitter implements Emitter {
|
||||
* @return method emitter to use for weaving this method
|
||||
*/
|
||||
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));
|
||||
}
|
||||
|
||||
@ -481,6 +542,8 @@ public class ClassEmitter implements Emitter {
|
||||
* @return method emitter to use for weaving this method
|
||||
*/
|
||||
MethodEmitter method(final FunctionNode functionNode) {
|
||||
methodCount++;
|
||||
methodNames.add(functionNode.getName());
|
||||
final FunctionSignature signature = new FunctionSignature(functionNode);
|
||||
final MethodVisitor mv = cw.visitMethod(
|
||||
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
|
||||
*/
|
||||
MethodEmitter restOfMethod(final FunctionNode functionNode) {
|
||||
methodCount++;
|
||||
methodNames.add(functionNode.getName());
|
||||
final MethodVisitor mv = cw.visitMethod(
|
||||
ACC_PUBLIC | ACC_STATIC,
|
||||
functionNode.getName(),
|
||||
@ -516,6 +581,7 @@ public class ClassEmitter implements Emitter {
|
||||
* @return method emitter to use for weaving <clinit>
|
||||
*/
|
||||
MethodEmitter clinit() {
|
||||
clinitCount++;
|
||||
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
|
||||
*/
|
||||
MethodEmitter init() {
|
||||
initCount++;
|
||||
return method(INIT.symbolName(), void.class);
|
||||
}
|
||||
|
||||
@ -535,6 +602,7 @@ public class ClassEmitter implements Emitter {
|
||||
* @return method emitter to use for weaving <init>()V
|
||||
*/
|
||||
MethodEmitter init(final Class<?>... ptypes) {
|
||||
initCount++;
|
||||
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
|
||||
*/
|
||||
MethodEmitter init(final EnumSet<Flag> flags, final Class<?>... ptypes) {
|
||||
initCount++;
|
||||
return method(flags, INIT.symbolName(), void.class, ptypes);
|
||||
}
|
||||
|
||||
@ -561,6 +630,7 @@ public class ClassEmitter implements Emitter {
|
||||
* @see ClassEmitter.Flag
|
||||
*/
|
||||
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();
|
||||
}
|
||||
|
||||
|
@ -1622,9 +1622,18 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
|
||||
@Override
|
||||
protected void evaluate() {
|
||||
method.load(ITERATOR_TYPE, iterSlot);
|
||||
// TODO: optimistic for-in iteration
|
||||
method.invoke(interfaceCallNoLookup(ITERATOR_CLASS, "next", Object.class));
|
||||
new OptimisticOperation((Optimistic)forNode.getInit(), TypeBounds.UNBOUNDED) {
|
||||
@Override
|
||||
void loadStack() {
|
||||
method.load(ITERATOR_TYPE, iterSlot);
|
||||
}
|
||||
|
||||
@Override
|
||||
void consumeStack() {
|
||||
method.invoke(interfaceCallNoLookup(ITERATOR_CLASS, "next", Object.class));
|
||||
convertOptimisticReturnValue();
|
||||
}
|
||||
}.emit();
|
||||
}
|
||||
}.store();
|
||||
body.accept(this);
|
||||
|
@ -31,6 +31,7 @@ import java.util.Collections;
|
||||
import java.util.Deque;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import jdk.nashorn.internal.IntDeque;
|
||||
import jdk.nashorn.internal.codegen.types.Type;
|
||||
import jdk.nashorn.internal.ir.Block;
|
||||
@ -158,7 +159,9 @@ final class CodeGeneratorLexicalContext extends LexicalContext {
|
||||
|
||||
CompileUnit popCompileUnit(final CompileUnit 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();
|
||||
}
|
||||
|
||||
|
@ -48,6 +48,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
import jdk.nashorn.internal.AssertsEnabled;
|
||||
import jdk.nashorn.internal.codegen.Compiler.CompilationPhases;
|
||||
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
|
||||
* to recompile stuff
|
||||
@ -334,6 +336,8 @@ enum CompilationPhase {
|
||||
if (phases.isRestOfCompilation()) {
|
||||
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());
|
||||
log.fine("Creating new compile unit ", oldUnit, " => ", newUnit);
|
||||
map.put(oldUnit, newUnit);
|
||||
@ -430,8 +434,14 @@ enum CompilationPhase {
|
||||
|
||||
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());
|
||||
|
||||
final CodeGenerator codegen = new CodeGenerator(compiler, phases.isRestOfCompilation() ? compiler.getContinuationEntryPoints() : null);
|
||||
|
||||
try {
|
||||
// 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().
|
||||
@ -455,12 +465,18 @@ enum CompilationPhase {
|
||||
final ClassEmitter classEmitter = compileUnit.getClassEmitter();
|
||||
classEmitter.end();
|
||||
|
||||
if (!compileUnit.isUsed()) {
|
||||
compiler.getLogger().fine("Skipping unused compile unit ", compileUnit);
|
||||
continue;
|
||||
}
|
||||
|
||||
final byte[] bytecode = classEmitter.toByteArray();
|
||||
assert bytecode != null;
|
||||
|
||||
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?
|
||||
if (senv._verify_code) {
|
||||
@ -536,6 +552,9 @@ enum CompilationPhase {
|
||||
|
||||
// initialize function in the compile units
|
||||
for (final CompileUnit unit : compiler.getCompileUnits()) {
|
||||
if (!unit.isUsed()) {
|
||||
continue;
|
||||
}
|
||||
unit.setCode(installedClasses.get(unit.getUnitClassName()));
|
||||
}
|
||||
|
||||
|
@ -42,6 +42,10 @@ public final class CompileUnit implements Comparable<CompileUnit> {
|
||||
|
||||
private Class<?> clazz;
|
||||
|
||||
private boolean isUsed;
|
||||
|
||||
private static int emittedUnitCount;
|
||||
|
||||
CompileUnit(final String className, final ClassEmitter classEmitter, final long initialWeight) {
|
||||
this.className = className;
|
||||
this.weight = initialWeight;
|
||||
@ -52,6 +56,33 @@ public final class CompileUnit implements Comparable<CompileUnit> {
|
||||
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
|
||||
* generated yet
|
||||
@ -121,7 +152,8 @@ public final class CompileUnit implements Comparable<CompileUnit> {
|
||||
|
||||
@Override
|
||||
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
|
||||
|
@ -38,7 +38,6 @@ import java.lang.invoke.MethodType;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
@ -51,8 +50,8 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.logging.Level;
|
||||
import jdk.internal.dynalink.support.NameCodec;
|
||||
import jdk.nashorn.internal.codegen.ClassEmitter.Flag;
|
||||
import jdk.nashorn.internal.codegen.types.Type;
|
||||
import jdk.nashorn.internal.ir.Expression;
|
||||
import jdk.nashorn.internal.ir.FunctionNode;
|
||||
import jdk.nashorn.internal.ir.Optimistic;
|
||||
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.ScriptEnvironment;
|
||||
import jdk.nashorn.internal.runtime.ScriptObject;
|
||||
import jdk.nashorn.internal.runtime.ScriptRuntime;
|
||||
import jdk.nashorn.internal.runtime.Source;
|
||||
import jdk.nashorn.internal.runtime.logging.DebugLogger;
|
||||
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()]));
|
||||
}
|
||||
|
||||
@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) {
|
||||
final LinkedList<CompilationPhase> list = new LinkedList<>();
|
||||
for (final CompilationPhase p : phases) {
|
||||
@ -473,6 +482,19 @@ public final class Compiler implements Loggable {
|
||||
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) {
|
||||
invalidatedProgramPoints.put(programPoint, type);
|
||||
}
|
||||
@ -675,16 +697,8 @@ public final class Compiler implements Loggable {
|
||||
CompileUnit createCompileUnit(final String unitClassName, final long initialWeight) {
|
||||
final ClassEmitter classEmitter = new ClassEmitter(context, sourceName, unitClassName, isStrict());
|
||||
final CompileUnit compileUnit = new CompileUnit(unitClassName, classEmitter, initialWeight);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@ -722,13 +736,6 @@ public final class Compiler implements Loggable {
|
||||
return name.replace('/', '.');
|
||||
}
|
||||
|
||||
RecompilableScriptFunctionData getProgram() {
|
||||
if (compiledFunction == null) {
|
||||
return null;
|
||||
}
|
||||
return compiledFunction.getProgram();
|
||||
}
|
||||
|
||||
RecompilableScriptFunctionData getScriptFunctionData(final int functionId) {
|
||||
return compiledFunction == null ? null : compiledFunction.getScriptFunctionData(functionId);
|
||||
}
|
||||
|
@ -25,8 +25,6 @@
|
||||
|
||||
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 java.util.HashMap;
|
||||
@ -34,6 +32,7 @@ import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import jdk.nashorn.internal.codegen.ObjectClassGenerator.AllocatorDescriptor;
|
||||
import jdk.nashorn.internal.ir.Block;
|
||||
import jdk.nashorn.internal.ir.FunctionNode;
|
||||
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.visitor.NodeVisitor;
|
||||
import jdk.nashorn.internal.runtime.Context;
|
||||
import jdk.nashorn.internal.runtime.PropertyMap;
|
||||
import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData;
|
||||
import jdk.nashorn.internal.runtime.logging.DebugLogger;
|
||||
import jdk.nashorn.internal.runtime.logging.Loggable;
|
||||
@ -208,14 +206,10 @@ final class FindScopeDepths extends NodeVisitor<LexicalContext> implements Logga
|
||||
|
||||
assert nestedFunctions != null;
|
||||
// Generate the object class and property map in case this function is ever used as constructor
|
||||
final int fieldCount = getPaddedFieldCount(newFunctionNode.getThisProperties());
|
||||
final String allocatorClassName = Compiler.binaryName(getClassName(fieldCount));
|
||||
final PropertyMap allocatorMap = PropertyMap.newMap(null, allocatorClassName, 0, fieldCount, 0);
|
||||
final RecompilableScriptFunctionData data = new RecompilableScriptFunctionData(
|
||||
newFunctionNode,
|
||||
compiler.getCodeInstaller(),
|
||||
allocatorClassName,
|
||||
allocatorMap,
|
||||
new AllocatorDescriptor(newFunctionNode.getThisProperties()),
|
||||
nestedFunctions,
|
||||
externalSymbolDepths.get(fnId),
|
||||
internalSymbols.get(fnId)
|
||||
|
@ -551,13 +551,19 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
|
||||
|
||||
final Expression init = forNode.getInit();
|
||||
if(forNode.isForIn()) {
|
||||
forNode.getModify().accept(this);
|
||||
enterTestFirstLoop(forNode, null, init);
|
||||
final JoinPredecessorExpression iterable = forNode.getModify();
|
||||
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 {
|
||||
if(init != null) {
|
||||
init.accept(this);
|
||||
}
|
||||
enterTestFirstLoop(forNode, forNode.getModify(), null);
|
||||
enterTestFirstLoop(forNode, forNode.getModify(), null, false);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -792,7 +798,8 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
|
||||
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();
|
||||
if(isAlwaysFalse(test)) {
|
||||
test.accept(this);
|
||||
@ -814,8 +821,12 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
|
||||
jumpToLabel(test, breakLabel);
|
||||
}
|
||||
if(iteratorValues instanceof IdentNode) {
|
||||
// Receives iterator values; they're currently all objects (JDK-8034954).
|
||||
onAssignment((IdentNode)iteratorValues, LvarType.OBJECT);
|
||||
final IdentNode ident = (IdentNode)iteratorValues;
|
||||
// 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();
|
||||
body.accept(this);
|
||||
@ -955,7 +966,7 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
|
||||
if(whileNode.isDoWhile()) {
|
||||
enterDoWhileLoop(whileNode);
|
||||
} else {
|
||||
enterTestFirstLoop(whileNode, null, null);
|
||||
enterTestFirstLoop(whileNode, null, null, false);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -71,7 +71,6 @@ import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor;
|
||||
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
import jdk.nashorn.internal.parser.Token;
|
||||
import jdk.nashorn.internal.parser.TokenType;
|
||||
import jdk.nashorn.internal.runtime.CodeInstaller;
|
||||
import jdk.nashorn.internal.runtime.Context;
|
||||
import jdk.nashorn.internal.runtime.JSType;
|
||||
import jdk.nashorn.internal.runtime.Source;
|
||||
@ -93,9 +92,6 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> implements Lo
|
||||
|
||||
private final DebugLogger log;
|
||||
|
||||
// needed only to get unique eval id
|
||||
private final CodeInstaller<?> installer;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
@ -143,7 +139,6 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> implements Lo
|
||||
}
|
||||
});
|
||||
|
||||
this.installer = compiler.getCodeInstaller();
|
||||
this.log = initLogger(compiler.getContext());
|
||||
}
|
||||
|
||||
@ -566,16 +561,13 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> implements Lo
|
||||
private String evalLocation(final IdentNode node) {
|
||||
final Source source = lc.getCurrentFunction().getSource();
|
||||
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().
|
||||
append(source.getName()).
|
||||
append('#').
|
||||
append(source.getLine(pos)).
|
||||
append(':').
|
||||
append(source.getColumn(pos)).
|
||||
append("<eval>@").
|
||||
append(id).
|
||||
append("<eval>").
|
||||
toString();
|
||||
}
|
||||
|
||||
|
@ -308,7 +308,7 @@ public final class ObjectClassGenerator implements Loggable {
|
||||
newEmptyInit(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.end();
|
||||
|
||||
return toByteArray(classEmitter);
|
||||
return toByteArray(className, classEmitter);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -484,15 +484,13 @@ public final class ObjectClassGenerator implements Loggable {
|
||||
* @param classEmitter Open class emitter.
|
||||
* @return Byte codes for the class.
|
||||
*/
|
||||
private byte[] toByteArray(final ClassEmitter classEmitter) {
|
||||
private byte[] toByteArray(final String className, final ClassEmitter classEmitter) {
|
||||
classEmitter.end();
|
||||
|
||||
final byte[] code = classEmitter.toByteArray();
|
||||
final ScriptEnvironment env = context.getEnv();
|
||||
|
||||
if (env._print_code && env._print_code_dir == null) {
|
||||
env.getErr().println(ClassEmitter.disassemble(code));
|
||||
}
|
||||
DumpBytecode.dumpBytecode(env, log, code, className);
|
||||
|
||||
if (env._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));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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() + "]";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -55,6 +55,19 @@ final class TypeEvaluator {
|
||||
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) {
|
||||
assert compiler.useOptimisticTypes();
|
||||
|
||||
|
@ -276,6 +276,14 @@ public class Block extends Node implements BreakableNode, Terminal, Flags<Block>
|
||||
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.
|
||||
* @return the line number of the first statement in the block, or -1 if the block has no statements.
|
||||
|
@ -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();
|
||||
}
|
@ -34,10 +34,8 @@ import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALL
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import jdk.nashorn.internal.AssertsEnabled;
|
||||
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.Immutable;
|
||||
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData;
|
||||
import jdk.nashorn.internal.runtime.ScriptFunction;
|
||||
import jdk.nashorn.internal.runtime.Source;
|
||||
import jdk.nashorn.internal.runtime.UserAccessorProperty;
|
||||
@ -57,7 +56,7 @@ import jdk.nashorn.internal.runtime.linker.LinkerCallSite;
|
||||
* IR representation for function (or script.)
|
||||
*/
|
||||
@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 */
|
||||
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. */
|
||||
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. */
|
||||
@Ignore
|
||||
@ -256,6 +258,14 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
||||
/** trace callsite values in this function? */
|
||||
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 */
|
||||
public static final int EXTENSION_CALLSITE_FLAGS = IS_PRINT_PARSE |
|
||||
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. */
|
||||
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.
|
||||
* We also pessimistically need a parent scope if we have lazy children that have not yet been compiled */
|
||||
/** 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. */
|
||||
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? */
|
||||
private Type returnType = Type.UNKNOWN;
|
||||
|
||||
@ -288,11 +291,10 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
||||
* Constructor
|
||||
*
|
||||
* @param source the source
|
||||
* @param id unique id
|
||||
* @param lineNumber line number
|
||||
* @param token token
|
||||
* @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 ident the identifier
|
||||
* @param name the name of the function
|
||||
@ -302,7 +304,6 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
||||
*/
|
||||
public FunctionNode(
|
||||
final Source source,
|
||||
final int id,
|
||||
final int lineNumber,
|
||||
final long token,
|
||||
final int finish,
|
||||
@ -316,7 +317,6 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
||||
super(token, finish);
|
||||
|
||||
this.source = source;
|
||||
this.id = id;
|
||||
this.lineNumber = lineNumber;
|
||||
this.ident = ident;
|
||||
this.name = name;
|
||||
@ -331,11 +331,13 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
||||
this.body = null;
|
||||
this.thisProperties = 0;
|
||||
this.rootClass = null;
|
||||
this.endParserState = null;
|
||||
}
|
||||
|
||||
private FunctionNode(
|
||||
final FunctionNode functionNode,
|
||||
final long lastToken,
|
||||
Object endParserState,
|
||||
final int flags,
|
||||
final String name,
|
||||
final Type returnType,
|
||||
@ -347,6 +349,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
||||
final Class<?> rootClass) {
|
||||
super(functionNode);
|
||||
|
||||
this.endParserState = endParserState;
|
||||
this.lineNumber = functionNode.lineNumber;
|
||||
this.flags = flags;
|
||||
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
|
||||
this.source = functionNode.source;
|
||||
this.id = functionNode.id;
|
||||
this.ident = functionNode.ident;
|
||||
this.namespace = functionNode.namespace;
|
||||
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
|
||||
*/
|
||||
public int getId() {
|
||||
return id;
|
||||
return position();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -535,6 +537,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
||||
new FunctionNode(
|
||||
this,
|
||||
lastToken,
|
||||
endParserState,
|
||||
flags,
|
||||
name,
|
||||
returnType,
|
||||
@ -606,6 +609,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
||||
new FunctionNode(
|
||||
this,
|
||||
lastToken,
|
||||
endParserState,
|
||||
flags,
|
||||
name,
|
||||
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() {
|
||||
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
|
||||
* @return the first token
|
||||
@ -741,6 +754,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
||||
new FunctionNode(
|
||||
this,
|
||||
lastToken,
|
||||
endParserState,
|
||||
flags |
|
||||
(body.needsScope() ?
|
||||
FunctionNode.HAS_SCOPE_BLOCK :
|
||||
@ -839,6 +853,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
||||
new FunctionNode(
|
||||
this,
|
||||
lastToken,
|
||||
endParserState,
|
||||
flags,
|
||||
name,
|
||||
returnType,
|
||||
@ -899,6 +914,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
||||
new FunctionNode(
|
||||
this,
|
||||
lastToken,
|
||||
endParserState,
|
||||
flags,
|
||||
name,
|
||||
returnType,
|
||||
@ -910,6 +926,41 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
||||
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
|
||||
* @return the name
|
||||
@ -934,6 +985,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
||||
new FunctionNode(
|
||||
this,
|
||||
lastToken,
|
||||
endParserState,
|
||||
flags,
|
||||
name,
|
||||
returnType,
|
||||
@ -999,6 +1051,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
||||
new FunctionNode(
|
||||
this,
|
||||
lastToken,
|
||||
endParserState,
|
||||
flags,
|
||||
name,
|
||||
returnType,
|
||||
@ -1077,6 +1130,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
||||
new FunctionNode(
|
||||
this,
|
||||
lastToken,
|
||||
endParserState,
|
||||
flags,
|
||||
name,
|
||||
type,
|
||||
@ -1102,6 +1156,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
||||
* @see Compiler
|
||||
* @return the compile unit
|
||||
*/
|
||||
@Override
|
||||
public CompileUnit getCompileUnit() {
|
||||
return compileUnit;
|
||||
}
|
||||
@ -1123,6 +1178,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
||||
new FunctionNode(
|
||||
this,
|
||||
lastToken,
|
||||
endParserState,
|
||||
flags,
|
||||
name,
|
||||
returnType,
|
||||
@ -1178,6 +1234,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
||||
new FunctionNode(
|
||||
this,
|
||||
lastToken,
|
||||
endParserState,
|
||||
flags,
|
||||
name,
|
||||
returnType,
|
||||
|
@ -351,8 +351,7 @@ public class LexicalContext {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the function for this block. If the block is itself a function
|
||||
* this returns identity
|
||||
* Get the function for this block.
|
||||
* @param block block for which to get function
|
||||
* @return function for block
|
||||
*/
|
||||
|
@ -603,7 +603,7 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey {
|
||||
* An ArrayUnit is a range in an ArrayLiteral. ArrayLiterals can
|
||||
* 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. */
|
||||
private final CompileUnit compileUnit;
|
||||
|
||||
@ -642,6 +642,7 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey {
|
||||
* The array compile unit
|
||||
* @return array compile unit
|
||||
*/
|
||||
@Override
|
||||
public CompileUnit getCompileUnit() {
|
||||
return compileUnit;
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
* Node indicating code is split across classes.
|
||||
*/
|
||||
@Immutable
|
||||
public class SplitNode extends LexicalContextStatement implements Labels {
|
||||
public class SplitNode extends LexicalContextStatement implements Labels, CompileUnitHolder {
|
||||
/** Split node method name. */
|
||||
private final String name;
|
||||
|
||||
@ -116,6 +116,7 @@ public class SplitNode extends LexicalContextStatement implements Labels {
|
||||
* Get the compile unit for this split node
|
||||
* @return compile unit
|
||||
*/
|
||||
@Override
|
||||
public CompileUnit getCompileUnit() {
|
||||
return compileUnit;
|
||||
}
|
||||
|
@ -630,6 +630,24 @@ public final class Global extends ScriptObject implements Scope {
|
||||
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
|
||||
*
|
||||
|
@ -51,9 +51,9 @@ import jdk.nashorn.internal.runtime.linker.PrimitiveLookup;
|
||||
public final class NativeBoolean extends ScriptObject {
|
||||
private final boolean value;
|
||||
|
||||
// Method handle to create an object wrapper for a primitive boolean
|
||||
private static final MethodHandle WRAPFILTER = findOwnMH("wrapFilter", MH.type(NativeBoolean.class, Object.class));
|
||||
// Method handle to retrieve the Boolean prototype object
|
||||
/** Method handle to create an object wrapper for a primitive boolean. */
|
||||
static final MethodHandle WRAPFILTER = findOwnMH("wrapFilter", MH.type(NativeBoolean.class, Object.class));
|
||||
/** Method handle to retrieve the Boolean prototype object. */
|
||||
private static final MethodHandle PROTOFILTER = findOwnMH("protoFilter", MH.type(Object.class, Object.class));
|
||||
|
||||
// initialized by nasgen
|
||||
|
@ -28,6 +28,7 @@ package jdk.nashorn.internal.objects;
|
||||
import static jdk.nashorn.internal.lookup.Lookup.MH;
|
||||
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
|
||||
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.MethodHandles;
|
||||
@ -697,7 +698,7 @@ public final class NativeJSAdapter extends ScriptObject {
|
||||
if (methodHandle != null) {
|
||||
return new GuardedInvocation(
|
||||
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()));
|
||||
}
|
||||
}
|
||||
|
@ -57,9 +57,9 @@ import jdk.nashorn.internal.runtime.linker.PrimitiveLookup;
|
||||
@ScriptClass("Number")
|
||||
public final class NativeNumber extends ScriptObject {
|
||||
|
||||
// Method handle to create an object wrapper for a primitive number
|
||||
private static final MethodHandle WRAPFILTER = findOwnMH("wrapFilter", MH.type(NativeNumber.class, Object.class));
|
||||
// Method handle to retrieve the Number prototype object
|
||||
/** Method handle to create an object wrapper for a primitive number. */
|
||||
static final MethodHandle WRAPFILTER = findOwnMH("wrapFilter", MH.type(NativeNumber.class, Object.class));
|
||||
/** Method handle to retrieve the Number prototype object. */
|
||||
private static final MethodHandle PROTOFILTER = findOwnMH("protoFilter", MH.type(Object.class, Object.class));
|
||||
|
||||
/** ECMA 15.7.3.2 largest positive finite value */
|
||||
|
@ -71,9 +71,9 @@ public final class NativeString extends ScriptObject {
|
||||
|
||||
private final CharSequence value;
|
||||
|
||||
// Method handle to create an object wrapper for a primitive string
|
||||
private static final MethodHandle WRAPFILTER = findOwnMH("wrapFilter", MH.type(NativeString.class, Object.class));
|
||||
// Method handle to retrieve the String prototype object
|
||||
/** Method handle to create an object wrapper for a primitive string */
|
||||
static final MethodHandle WRAPFILTER = findOwnMH("wrapFilter", MH.type(NativeString.class, Object.class));
|
||||
/** Method handle to retrieve the String prototype object */
|
||||
private static final MethodHandle PROTOFILTER = findOwnMH("protoFilter", MH.type(Object.class, Object.class));
|
||||
|
||||
// initialized by nasgen
|
||||
|
@ -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.
|
||||
*
|
||||
* @throws ParserException on unexpected token type
|
||||
*/
|
||||
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) {
|
||||
throw error(expectMessage(expected));
|
||||
}
|
||||
|
||||
next();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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.EXECSTRING;
|
||||
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.LBRACE;
|
||||
import static jdk.nashorn.internal.parser.TokenType.LPAREN;
|
||||
@ -85,6 +86,9 @@ public class Lexer extends Scanner {
|
||||
/** Type of last token added. */
|
||||
private TokenType last;
|
||||
|
||||
private final boolean pauseOnFunctionBody;
|
||||
private boolean pauseOnNextLeftBrace;
|
||||
|
||||
private static final String SPACETAB = " \t"; // ASCII space and tab
|
||||
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
|
||||
*/
|
||||
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 start start position in source from which to start lexing
|
||||
* @param len length of source segment to lex
|
||||
* @param stream token stream to lex
|
||||
* @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);
|
||||
this.source = source;
|
||||
this.stream = stream;
|
||||
@ -203,6 +210,8 @@ public class Lexer extends Scanner {
|
||||
this.nested = false;
|
||||
this.pendingLine = 1;
|
||||
this.last = EOL;
|
||||
|
||||
this.pauseOnFunctionBody = pauseOnFunctionBody;
|
||||
}
|
||||
|
||||
private Lexer(final Lexer lexer, final State state) {
|
||||
@ -216,6 +225,7 @@ public class Lexer extends Scanner {
|
||||
pendingLine = state.pendingLine;
|
||||
linePosition = state.linePosition;
|
||||
last = EOL;
|
||||
pauseOnFunctionBody = false;
|
||||
}
|
||||
|
||||
static class State extends Scanner.State {
|
||||
@ -810,6 +820,9 @@ public class Lexer extends Scanner {
|
||||
final int length = scanIdentifier();
|
||||
// Check to see if it is a keyword.
|
||||
final TokenType type = TokenLookup.lookupKeyword(content, start, length);
|
||||
if (type == FUNCTION && pauseOnFunctionBody) {
|
||||
pauseOnNextLeftBrace = true;
|
||||
}
|
||||
// Add keyword or identifier token.
|
||||
add(type, start);
|
||||
}
|
||||
@ -1597,6 +1610,9 @@ public class Lexer extends Scanner {
|
||||
// We break to let the parser decide what it is.
|
||||
if (canStartLiteral(type)) {
|
||||
break;
|
||||
} else if (type == LBRACE && pauseOnNextLeftBrace) {
|
||||
pauseOnNextLeftBrace = false;
|
||||
break;
|
||||
}
|
||||
} else if (Character.isJavaIdentifierStart(ch0) || ch0 == '\\' && ch1 == 'u') {
|
||||
// Scan and add identifier or keyword.
|
||||
|
@ -148,7 +148,7 @@ public class Parser extends AbstractParser implements Loggable {
|
||||
/** to receive line information from Lexer when scanning multine literals. */
|
||||
protected final Lexer.LineInfoReceiver lineInfoReceiver;
|
||||
|
||||
private int nextFunctionId;
|
||||
private RecompilableScriptFunctionData reparsedFunction;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
@ -171,7 +171,7 @@ public class Parser extends AbstractParser implements Loggable {
|
||||
* @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) {
|
||||
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 errors error manager
|
||||
* @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 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);
|
||||
this.env = env;
|
||||
this.namespace = new Namespace(env.getNamespace());
|
||||
this.nextFunctionId = nextFunctionId;
|
||||
this.scripting = env._scripting;
|
||||
if (this.scripting) {
|
||||
this.lineInfoReceiver = new Lexer.LineInfoReceiver() {
|
||||
@ -227,6 +225,16 @@ public class Parser extends AbstractParser implements Loggable {
|
||||
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.
|
||||
* Errors will be thrown and the error manager will contain information
|
||||
@ -264,7 +272,7 @@ public class Parser extends AbstractParser implements Loggable {
|
||||
|
||||
try {
|
||||
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;
|
||||
line = lineOffset;
|
||||
|
||||
@ -472,7 +480,6 @@ loop:
|
||||
final FunctionNode functionNode =
|
||||
new FunctionNode(
|
||||
source,
|
||||
nextFunctionId++,
|
||||
functionLine,
|
||||
token,
|
||||
Token.descPosition(token),
|
||||
@ -2828,10 +2835,14 @@ loop:
|
||||
FunctionNode functionNode = null;
|
||||
long lastToken = 0L;
|
||||
|
||||
final boolean parseBody;
|
||||
Object endParserState = null;
|
||||
try {
|
||||
// Create a new function block.
|
||||
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
|
||||
if (!env._no_syntax_extensions && type != LBRACE) {
|
||||
/*
|
||||
@ -2847,34 +2858,143 @@ loop:
|
||||
assert lc.getCurrentBlock() == lc.getFunctionBody(functionNode);
|
||||
// EOL uses length field to store the line number
|
||||
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);
|
||||
appendStatement(returnNode);
|
||||
functionNode.setFinish(lastFinish);
|
||||
|
||||
} else {
|
||||
expect(LBRACE);
|
||||
|
||||
// Gather the function elements.
|
||||
final List<Statement> prevFunctionDecls = functionDeclarations;
|
||||
functionDeclarations = new ArrayList<>();
|
||||
try {
|
||||
sourceElements(false);
|
||||
addFunctionDeclarations(functionNode);
|
||||
} finally {
|
||||
functionDeclarations = prevFunctionDecls;
|
||||
// Only create the return node if we aren't skipping nested functions. Note that we aren't
|
||||
// skipping parsing of these extended functions; they're considered to be small anyway. Also,
|
||||
// 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
|
||||
// details).
|
||||
if (parseBody) {
|
||||
final ReturnNode returnNode = new ReturnNode(functionNode.getLineNumber(), expr.getToken(), lastFinish, expr);
|
||||
appendStatement(returnNode);
|
||||
}
|
||||
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);
|
||||
functionNode.setFinish(finish);
|
||||
}
|
||||
} finally {
|
||||
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);
|
||||
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) {
|
||||
if (functionNode.getFlag(FunctionNode.IS_PRINT_AST)) {
|
||||
env.getErr().println(new ASTWriter(functionNode));
|
||||
@ -3247,6 +3367,9 @@ loop:
|
||||
} else {
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
@ -209,4 +209,8 @@ public class TokenStream {
|
||||
in = count;
|
||||
buffer = newBuffer;
|
||||
}
|
||||
|
||||
void reset() {
|
||||
in = out = count = base = 0;
|
||||
}
|
||||
}
|
||||
|
@ -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() + "]";
|
||||
}
|
||||
}
|
@ -79,12 +79,6 @@ public interface CodeInstaller<T> {
|
||||
*/
|
||||
public long getUniqueScriptId();
|
||||
|
||||
/**
|
||||
* Get next unique eval id
|
||||
* @return unique eval id
|
||||
*/
|
||||
public long getUniqueEvalId();
|
||||
|
||||
/**
|
||||
* Store a compiled script for later reuse
|
||||
* @param source the script source
|
||||
|
@ -27,7 +27,6 @@ package jdk.nashorn.internal.runtime;
|
||||
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.isValid;
|
||||
|
||||
import java.lang.invoke.CallSite;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
@ -39,6 +38,7 @@ import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import jdk.internal.dynalink.linker.GuardedInvocation;
|
||||
import jdk.nashorn.internal.codegen.Compiler;
|
||||
import jdk.nashorn.internal.codegen.Compiler.CompilationPhases;
|
||||
|
@ -196,16 +196,11 @@ public final class Context {
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getUniqueEvalId() {
|
||||
return context.getUniqueEvalId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void storeScript(final String classInfoFile, final Source source, final String mainClassName,
|
||||
public void storeScript(final String cacheKey, final Source source, final String mainClassName,
|
||||
final Map<String,byte[]> classBytes, final Map<Integer, FunctionInitializer> initializers,
|
||||
final Object[] constants, final int compilationId) {
|
||||
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 */
|
||||
private final AtomicLong uniqueScriptId;
|
||||
|
||||
/** Unique id for 'eval' */
|
||||
private final AtomicLong uniqueEvalId;
|
||||
|
||||
/** Optional class filter to use for Java classes. Can be null. */
|
||||
private final ClassFilter classFilter;
|
||||
|
||||
@ -450,7 +442,6 @@ public final class Context {
|
||||
this.uniqueScriptId = new AtomicLong();
|
||||
}
|
||||
this.errors = errors;
|
||||
this.uniqueEvalId = new AtomicLong();
|
||||
|
||||
// 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.
|
||||
@ -1190,10 +1181,6 @@ public final class Context {
|
||||
}, CREATE_LOADER_ACC_CTXT);
|
||||
}
|
||||
|
||||
private long getUniqueEvalId() {
|
||||
return uniqueEvalId.getAndIncrement();
|
||||
}
|
||||
|
||||
private long getUniqueScriptId() {
|
||||
return uniqueScriptId.getAndIncrement();
|
||||
}
|
||||
|
@ -29,7 +29,9 @@ import static jdk.nashorn.internal.lookup.Lookup.MH;
|
||||
import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.isValid;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import jdk.internal.dynalink.linker.LinkRequest;
|
||||
import jdk.nashorn.internal.codegen.ObjectClassGenerator;
|
||||
import jdk.nashorn.internal.objects.Global;
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @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;
|
||||
if (isValid(programPoint)) {
|
||||
getter = property.getOptimisticGetter(type, programPoint);
|
||||
} else {
|
||||
getter = property.getGetter(type);
|
||||
}
|
||||
return getGetterInner(getter);
|
||||
}
|
||||
|
||||
private MethodHandle getGetterInner(final MethodHandle getter) {
|
||||
if (property instanceof UserAccessorProperty) {
|
||||
final UserAccessorProperty uc = (UserAccessorProperty)property;
|
||||
final ScriptObject owner = getOwner();
|
||||
final ScriptObject container = (owner != null) ? owner : self;
|
||||
return MH.insertArguments(getter, 0, uc.getAccessors(container));
|
||||
return insertAccessorsGetter((UserAccessorProperty) property, request, getter);
|
||||
}
|
||||
return getter;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -111,18 +105,31 @@ public final class FindProperty {
|
||||
*
|
||||
* @return method handle for the getter
|
||||
*/
|
||||
public MethodHandle getSetter(final Class<?> type, final boolean strict) {
|
||||
final MethodHandle setter = property.getSetter(type, getOwner().getMap());
|
||||
public MethodHandle getSetter(final Class<?> type, final boolean strict, final LinkRequest request) {
|
||||
MethodHandle setter = property.getSetter(type, getOwner().getMap());
|
||||
if (property instanceof UserAccessorProperty) {
|
||||
final UserAccessorProperty uc = (UserAccessorProperty)property;
|
||||
final ScriptObject owner = getOwner();
|
||||
final ScriptObject container = (owner != null) ? owner : self;
|
||||
return MH.insertArguments(setter, 0, uc.getAccessors(container), strict ? property.getKey() : null);
|
||||
setter = MH.insertArguments(setter, 1, strict ? property.getKey() : null);
|
||||
return insertAccessorsGetter((UserAccessorProperty) property, request, 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 owner of property
|
||||
@ -136,7 +143,7 @@ public final class FindProperty {
|
||||
* @return appropriate receiver
|
||||
*/
|
||||
public ScriptObject getGetterReceiver() {
|
||||
return property != null && property.hasGetterFunction(prototype) ? self : prototype;
|
||||
return property != null && property instanceof UserAccessorProperty ? self : prototype;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -28,6 +28,8 @@ package jdk.nashorn.internal.runtime;
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.staticCall;
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCall;
|
||||
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 java.lang.invoke.MethodHandle;
|
||||
@ -370,22 +372,19 @@ public final class GlobalConstants implements Loggable {
|
||||
* @param find property lookup
|
||||
* @param receiver receiver
|
||||
* @param desc callsite descriptor
|
||||
* @param request link request
|
||||
* @param operator operator
|
||||
*
|
||||
* @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) {
|
||||
if (GLOBAL_ONLY && !find.getOwner().isGlobal()) {
|
||||
synchronized GuardedInvocation findGetMethod(final FindProperty find, final ScriptObject receiver, final CallSiteDescriptor desc) {
|
||||
// Also return null if property may have side effects
|
||||
if ((GLOBAL_ONLY && !find.getOwner().isGlobal()) || find.getProperty() instanceof UserAccessorProperty) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final int programPoint = NashornCallSiteDescriptor.isOptimistic(desc) ?
|
||||
NashornCallSiteDescriptor.getProgramPoint(desc) :
|
||||
UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
|
||||
final boolean isOptimistic = programPoint != UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
|
||||
final Class<?> retType = desc.getMethodType().returnType();
|
||||
final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
|
||||
final boolean isOptimistic = NashornCallSiteDescriptor.isOptimistic(desc);
|
||||
final int programPoint = isOptimistic ? getProgramPoint(desc) : INVALID_PROGRAM_POINT;
|
||||
final Class<?> retType = desc.getMethodType().returnType();
|
||||
final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
|
||||
|
||||
final Access acc = getOrCreateSwitchPoint(name);
|
||||
|
||||
|
@ -42,6 +42,7 @@ import jdk.nashorn.internal.codegen.Compiler;
|
||||
import jdk.nashorn.internal.codegen.Compiler.CompilationPhases;
|
||||
import jdk.nashorn.internal.codegen.CompilerConstants;
|
||||
import jdk.nashorn.internal.codegen.FunctionSignature;
|
||||
import jdk.nashorn.internal.codegen.ObjectClassGenerator.AllocatorDescriptor;
|
||||
import jdk.nashorn.internal.codegen.OptimisticTypesPersistence;
|
||||
import jdk.nashorn.internal.codegen.TypeMap;
|
||||
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.Loggable;
|
||||
import jdk.nashorn.internal.runtime.logging.Logger;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
@ -81,26 +81,29 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
|
||||
/** Token of this function within the source. */
|
||||
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 */
|
||||
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;
|
||||
|
||||
/** Id to parent function if one exists */
|
||||
private RecompilableScriptFunctionData parent;
|
||||
|
||||
private final boolean isDeclared;
|
||||
private final boolean isAnonymous;
|
||||
private final boolean needsCallee;
|
||||
/** Copy of the {@link FunctionNode} flags. */
|
||||
private final int functionFlags;
|
||||
|
||||
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 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 allocatorMap allocator map to seed instances with, when constructing
|
||||
* @param allocationDescriptor descriptor for the allocation behavior when this function is used as a constructor
|
||||
* @param nestedFunctions nested function map
|
||||
* @param externalScopeDepths external scope depths
|
||||
* @param internalSymbols internal symbols to method, defined in its scope
|
||||
@ -128,30 +130,27 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
|
||||
public RecompilableScriptFunctionData(
|
||||
final FunctionNode functionNode,
|
||||
final CodeInstaller<ScriptEnvironment> installer,
|
||||
final String allocatorClassName,
|
||||
final PropertyMap allocatorMap,
|
||||
final AllocatorDescriptor allocationDescriptor,
|
||||
final Map<Integer, RecompilableScriptFunctionData> nestedFunctions,
|
||||
final Map<String, Integer> externalScopeDepths,
|
||||
final Set<String> internalSymbols) {
|
||||
|
||||
super(functionName(functionNode),
|
||||
Math.min(functionNode.getParameters().size(), MAX_ARITY),
|
||||
getFlags(functionNode));
|
||||
getDataFlags(functionNode));
|
||||
|
||||
this.functionName = functionNode.getName();
|
||||
this.lineNumber = functionNode.getLineNumber();
|
||||
this.isDeclared = functionNode.isDeclared();
|
||||
this.needsCallee = functionNode.needsCallee();
|
||||
this.isAnonymous = functionNode.isAnonymous();
|
||||
this.functionFlags = functionNode.getFlags() | (functionNode.needsCallee() ? FunctionNode.NEEDS_CALLEE : 0);
|
||||
this.functionNodeId = functionNode.getId();
|
||||
this.source = functionNode.getSource();
|
||||
this.endParserState = functionNode.getEndParserState();
|
||||
this.token = tokenFor(functionNode);
|
||||
this.installer = installer;
|
||||
this.allocatorClassName = allocatorClassName;
|
||||
this.allocatorMap = allocatorMap;
|
||||
this.nestedFunctions = nestedFunctions;
|
||||
this.externalScopeDepths = externalScopeDepths;
|
||||
this.internalSymbols = new HashSet<>(internalSymbols);
|
||||
this.allocationStrategy = AllocationStrategy.get(allocationDescriptor);
|
||||
this.nestedFunctions = smallMap(nestedFunctions);
|
||||
this.externalScopeDepths = smallMap(externalScopeDepths);
|
||||
this.internalSymbols = smallSet(new HashSet<>(internalSymbols));
|
||||
|
||||
for (final RecompilableScriptFunctionData nfn : nestedFunctions.values()) {
|
||||
assert nfn.getParent() == null;
|
||||
@ -161,6 +160,27 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
|
||||
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
|
||||
public DebugLogger getLogger() {
|
||||
return log;
|
||||
@ -190,17 +210,30 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
|
||||
* @return the external symbol table with proto depths
|
||||
*/
|
||||
public int getExternalSymbolDepth(final String symbolName) {
|
||||
final Map<String, Integer> map = externalScopeDepths;
|
||||
if (map == null) {
|
||||
return -1;
|
||||
}
|
||||
final Integer depth = map.get(symbolName);
|
||||
final Integer depth = externalScopeDepths.get(symbolName);
|
||||
if (depth == null) {
|
||||
return -1;
|
||||
}
|
||||
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
|
||||
* a nested function, we have a parent. Note that "null" return value
|
||||
@ -269,7 +302,7 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
|
||||
|
||||
@Override
|
||||
public boolean inDynamicContext() {
|
||||
return (flags & IN_DYNAMIC_CONTEXT) != 0;
|
||||
return getFunctionFlag(FunctionNode.IN_DYNAMIC_CONTEXT);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
private static int getFlags(final FunctionNode functionNode) {
|
||||
private static int getDataFlags(final FunctionNode functionNode) {
|
||||
int flags = IS_CONSTRUCTOR;
|
||||
if (functionNode.isStrict()) {
|
||||
flags |= IS_STRICT;
|
||||
@ -307,37 +340,20 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
|
||||
if (functionNode.isVarArg()) {
|
||||
flags |= IS_VARIABLE_ARITY;
|
||||
}
|
||||
if (functionNode.inDynamicContext()) {
|
||||
flags |= IN_DYNAMIC_CONTEXT;
|
||||
}
|
||||
return flags;
|
||||
}
|
||||
|
||||
@Override
|
||||
PropertyMap getAllocatorMap() {
|
||||
return allocatorMap;
|
||||
return allocationStrategy.getAllocatorMap();
|
||||
}
|
||||
|
||||
@Override
|
||||
ScriptObject allocate(final PropertyMap map) {
|
||||
try {
|
||||
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));
|
||||
}
|
||||
return allocationStrategy.allocate(map);
|
||||
}
|
||||
|
||||
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
|
||||
final int descPosition = Token.descPosition(token);
|
||||
final Context context = Context.getContextTrusted();
|
||||
@ -346,18 +362,27 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
|
||||
source,
|
||||
new Context.ThrowErrorManager(),
|
||||
isStrict(),
|
||||
functionNodeId - (isProgram ? 0 : 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
|
||||
|
||||
if (isAnonymous) {
|
||||
if (getFunctionFlag(FunctionNode.IS_ANONYMOUS)) {
|
||||
parser.setFunctionName(functionName);
|
||||
}
|
||||
parser.setReparsedFunction(this);
|
||||
|
||||
final FunctionNode program = parser.parse(CompilerConstants.PROGRAM.symbolName(), descPosition, Token.descLength(token), true);
|
||||
// Parser generates a program AST even if we're recompiling a single function, so when we are only recompiling a
|
||||
// single function, extract it from the program.
|
||||
return (isProgram ? program : extractFunctionFromScript(program)).setName(null, functionName);
|
||||
final FunctionNode program = parser.parse(CompilerConstants.PROGRAM.symbolName(), descPosition,
|
||||
Token.descLength(token), true);
|
||||
// Parser generates a program AST even if we're recompiling a single function, so when we are only
|
||||
// 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) {
|
||||
@ -546,7 +571,7 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
|
||||
assert fns.size() == 1 : "got back more than one method in recompilation";
|
||||
final FunctionNode f = fns.iterator().next();
|
||||
assert f.getId() == functionNodeId;
|
||||
if (!isDeclared && f.isDeclared()) {
|
||||
if (!getFunctionFlag(FunctionNode.IS_DECLARED) && f.isDeclared()) {
|
||||
return f.clearFlag(null, FunctionNode.IS_DECLARED);
|
||||
}
|
||||
return f;
|
||||
@ -669,7 +694,15 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
|
||||
|
||||
@Override
|
||||
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
|
||||
@ -720,21 +753,6 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
|
||||
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
|
||||
* in outermost scope and not shadowed by being parameter or assignment in inner
|
||||
|
@ -90,8 +90,6 @@ public abstract class ScriptFunctionData implements Serializable {
|
||||
public static final int USES_THIS = 1 << 4;
|
||||
/** Is this a variable arity function? */
|
||||
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 */
|
||||
public static final int IS_STRICT_OR_BUILTIN = IS_STRICT | IS_BUILTIN;
|
||||
|
@ -1050,7 +1050,7 @@ public abstract class ScriptObject implements PropertyAccess {
|
||||
}
|
||||
|
||||
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) {
|
||||
try {
|
||||
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) {
|
||||
final MethodHandle getter = find.getGetter(long.class, programPoint);
|
||||
final MethodHandle getter = find.getGetter(long.class, programPoint, null);
|
||||
if (getter != null) {
|
||||
try {
|
||||
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) {
|
||||
final MethodHandle getter = find.getGetter(double.class, programPoint);
|
||||
final MethodHandle getter = find.getGetter(double.class, programPoint, null);
|
||||
if (getter != null) {
|
||||
try {
|
||||
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) {
|
||||
return cinv;
|
||||
}
|
||||
@ -1983,7 +1983,7 @@ public abstract class ScriptObject implements PropertyAccess {
|
||||
NashornCallSiteDescriptor.getProgramPoint(desc) :
|
||||
UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
|
||||
|
||||
mh = find.getGetter(returnType, programPoint);
|
||||
mh = find.getGetter(returnType, programPoint, request);
|
||||
// Get the appropriate guard for this callsite and property.
|
||||
final MethodHandle guard = NashornGuards.getGuard(this, property, desc, explicitInstanceOfCheck);
|
||||
final ScriptObject owner = find.getOwner();
|
||||
@ -1995,8 +1995,9 @@ public abstract class ScriptObject implements PropertyAccess {
|
||||
mh = Lookup.emptyGetter(returnType);
|
||||
protoSwitchPoint = getProtoSwitchPoint(name, owner);
|
||||
} else if (!find.isSelf()) {
|
||||
assert mh.type().returnType().equals(returnType) : "returntype mismatch for getter " + mh.type().returnType() + " != " + returnType;
|
||||
if (!property.hasGetterFunction(owner)) {
|
||||
assert mh.type().returnType().equals(returnType) :
|
||||
"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.
|
||||
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);
|
||||
if (cinv != null) {
|
||||
@ -2320,13 +2321,13 @@ public abstract class ScriptObject implements PropertyAccess {
|
||||
find.isSelf()?
|
||||
getKnownFunctionPropertyGuardSelf(
|
||||
getMap(),
|
||||
find.getGetter(Object.class, INVALID_PROGRAM_POINT),
|
||||
find.getGetter(Object.class, INVALID_PROGRAM_POINT, request),
|
||||
func)
|
||||
:
|
||||
//TODO this always does a scriptobject check
|
||||
getKnownFunctionPropertyGuardProto(
|
||||
getMap(),
|
||||
find.getGetter(Object.class, INVALID_PROGRAM_POINT),
|
||||
find.getGetter(Object.class, INVALID_PROGRAM_POINT, request),
|
||||
find.getProtoChainLength(),
|
||||
func),
|
||||
getProtoSwitchPoint(NO_SUCH_PROPERTY_NAME, find.getOwner()),
|
||||
|
@ -33,6 +33,7 @@ import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.SwitchPoint;
|
||||
import jdk.internal.dynalink.CallSiteDescriptor;
|
||||
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.NashornGuards;
|
||||
|
||||
@ -48,7 +49,7 @@ final class SetMethodCreator {
|
||||
private final FindProperty find;
|
||||
private final CallSiteDescriptor desc;
|
||||
private final Class<?> type;
|
||||
private final boolean explicitInstanceOfCheck;
|
||||
private final LinkRequest request;
|
||||
|
||||
/**
|
||||
* 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
|
||||
* 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 request the link request
|
||||
*/
|
||||
SetMethodCreator(final ScriptObject sobj, final FindProperty find, final CallSiteDescriptor desc, final boolean explicitInstanceOfCheck) {
|
||||
this.sobj = sobj;
|
||||
this.map = sobj.getMap();
|
||||
this.find = find;
|
||||
this.desc = desc;
|
||||
this.type = desc.getMethodType().parameterType(1);
|
||||
this.explicitInstanceOfCheck = explicitInstanceOfCheck;
|
||||
SetMethodCreator(final ScriptObject sobj, final FindProperty find, final CallSiteDescriptor desc, final LinkRequest request) {
|
||||
this.sobj = sobj;
|
||||
this.map = sobj.getMap();
|
||||
this.find = find;
|
||||
this.desc = desc;
|
||||
this.type = desc.getMethodType().parameterType(1);
|
||||
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 returns a map guard that casts its argument to ScriptObject, and if that fails, we need to
|
||||
// relink on ClassCastException.
|
||||
final boolean explicitInstanceOfCheck = NashornGuards.explicitInstanceOfCheck(desc, request);
|
||||
return new GuardedInvocation(methodHandle, NashornGuards.getGuard(sobj, property, desc, explicitInstanceOfCheck),
|
||||
(SwitchPoint)null, explicitInstanceOfCheck ? null : ClassCastException.class);
|
||||
}
|
||||
@ -140,6 +143,7 @@ final class SetMethodCreator {
|
||||
|
||||
private SetMethod createExistingPropertySetter() {
|
||||
final Property property = find.getProperty();
|
||||
final boolean isStrict = NashornCallSiteDescriptor.isStrict(desc);
|
||||
final MethodHandle methodHandle;
|
||||
|
||||
if (NashornCallSiteDescriptor.isDeclaration(desc)) {
|
||||
@ -152,7 +156,7 @@ final class SetMethodCreator {
|
||||
final PropertyMap oldMap = getMap();
|
||||
final Property newProperty = property.removeFlags(Property.NEEDS_DECLARATION);
|
||||
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());
|
||||
|
||||
// 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));
|
||||
methodHandle = MH.guardWithTest(casMap, fastSetter, slowSetter);
|
||||
} else {
|
||||
methodHandle = find.getSetter(type, NashornCallSiteDescriptor.isStrict(desc));
|
||||
methodHandle = find.getSetter(type, isStrict, request);
|
||||
}
|
||||
|
||||
assert methodHandle != null;
|
||||
assert property != null;
|
||||
|
||||
final MethodHandle boundHandle;
|
||||
if (!property.hasSetterFunction(find.getOwner()) && find.isInherited()) {
|
||||
if (!(property instanceof UserAccessorProperty) && find.isInherited()) {
|
||||
boundHandle = ScriptObject.addProtoFilter(methodHandle, find.getProtoChainLength());
|
||||
} else {
|
||||
boundHandle = methodHandle;
|
||||
|
@ -33,6 +33,8 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import jdk.nashorn.internal.codegen.CompileUnit;
|
||||
import jdk.nashorn.internal.runtime.logging.DebugLogger;
|
||||
import jdk.nashorn.internal.runtime.logging.Loggable;
|
||||
import jdk.nashorn.internal.runtime.logging.Logger;
|
||||
@ -189,7 +191,7 @@ public final class Timing implements Loggable {
|
||||
maxKeyLength++;
|
||||
|
||||
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()) {
|
||||
int len;
|
||||
|
||||
@ -224,6 +226,9 @@ public final class Timing implements Loggable {
|
||||
append((int)(knownTime * 100.0 / total)).
|
||||
append("%])");
|
||||
|
||||
sb.append("\n\nEmitted compile units: ").
|
||||
append(CompileUnit.getEmittedUnitCount());
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
@ -34,8 +34,8 @@ import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.util.concurrent.Callable;
|
||||
import jdk.nashorn.internal.codegen.CompilerConstants;
|
||||
import jdk.nashorn.internal.lookup.Lookup;
|
||||
import jdk.nashorn.internal.runtime.linker.Bootstrap;
|
||||
|
||||
@ -48,7 +48,7 @@ public final class UserAccessorProperty extends SpillProperty {
|
||||
|
||||
private static final long serialVersionUID = -5928687246526840321L;
|
||||
|
||||
static class Accessors {
|
||||
static final class Accessors {
|
||||
Object getter;
|
||||
Object setter;
|
||||
|
||||
@ -67,20 +67,20 @@ public final class UserAccessorProperty extends SpillProperty {
|
||||
}
|
||||
}
|
||||
|
||||
private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
|
||||
|
||||
/** Getter method handle */
|
||||
private final static CompilerConstants.Call USER_ACCESSOR_GETTER = staticCall(MethodHandles.lookup(), UserAccessorProperty.class,
|
||||
"userAccessorGetter", Object.class, Accessors.class, Object.class);
|
||||
private final static MethodHandle INVOKE_GETTER_ACCESSOR = findOwnMH_S("invokeGetterAccessor", Object.class, Accessors.class, Object.class);
|
||||
|
||||
/** Setter method handle */
|
||||
private final static CompilerConstants.Call USER_ACCESSOR_SETTER = staticCall(MethodHandles.lookup(), UserAccessorProperty.class,
|
||||
"userAccessorSetter", void.class, Accessors.class, String.class, Object.class, Object.class);
|
||||
private final static MethodHandle INVOKE_SETTER_ACCESSOR = findOwnMH_S("invokeSetterAccessor", void.class, Accessors.class, String.class, Object.class, Object.class);
|
||||
|
||||
/** 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() {
|
||||
|
||||
return Context.getGlobal().getDynamicInvoker(INVOKE_UA_GETTER,
|
||||
return Context.getGlobal().getDynamicInvoker(GETTER_INVOKER_KEY,
|
||||
new Callable<MethodHandle>() {
|
||||
@Override
|
||||
public MethodHandle call() {
|
||||
@ -91,10 +91,10 @@ public final class UserAccessorProperty extends SpillProperty {
|
||||
}
|
||||
|
||||
/** 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() {
|
||||
return Context.getGlobal().getDynamicInvoker(INVOKE_UA_SETTER,
|
||||
return Context.getGlobal().getDynamicInvoker(SETTER_INVOKER_KEY,
|
||||
new Callable<MethodHandle>() {
|
||||
@Override
|
||||
public MethodHandle call() {
|
||||
@ -190,7 +190,7 @@ public final class UserAccessorProperty extends SpillProperty {
|
||||
|
||||
@Override
|
||||
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
|
||||
@ -210,13 +210,13 @@ public final class UserAccessorProperty extends SpillProperty {
|
||||
|
||||
@Override
|
||||
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
|
||||
public MethodHandle getGetter(final Class<?> type) {
|
||||
//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
|
||||
@ -260,7 +260,7 @@ public final class UserAccessorProperty extends SpillProperty {
|
||||
|
||||
@Override
|
||||
public MethodHandle getSetter(final Class<?> type, final PropertyMap currentMap) {
|
||||
return USER_ACCESSOR_SETTER.methodHandle();
|
||||
return INVOKE_SETTER_ACCESSOR;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -269,11 +269,21 @@ public final class UserAccessorProperty extends SpillProperty {
|
||||
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
|
||||
// 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
|
||||
// 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;
|
||||
if (func instanceof ScriptFunction) {
|
||||
try {
|
||||
@ -288,7 +298,7 @@ public final class UserAccessorProperty extends SpillProperty {
|
||||
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;
|
||||
if (func instanceof ScriptFunction) {
|
||||
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));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -32,11 +32,10 @@ import java.lang.invoke.MethodType;
|
||||
import jdk.internal.dynalink.CallSiteDescriptor;
|
||||
import jdk.internal.dynalink.linker.GuardedInvocation;
|
||||
import jdk.internal.dynalink.linker.LinkRequest;
|
||||
import jdk.internal.dynalink.support.CallSiteDescriptorFactory;
|
||||
import jdk.internal.dynalink.support.Guards;
|
||||
import jdk.nashorn.internal.lookup.Lookup;
|
||||
import jdk.nashorn.internal.runtime.FindProperty;
|
||||
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
|
||||
@ -86,15 +85,6 @@ public final class PrimitiveLookup {
|
||||
final ScriptObject wrappedReceiver, final MethodHandle wrapFilter,
|
||||
final MethodHandle protoFilter) {
|
||||
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) {
|
||||
final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
|
||||
@ -102,7 +92,7 @@ public final class PrimitiveLookup {
|
||||
if(find == null) {
|
||||
// Give up early, give chance to BeanLinker and NashornBottomLinker to deal with it.
|
||||
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
|
||||
// the proto filter instead of going through wrapper instantiation below.
|
||||
final ScriptObject proto = wrappedReceiver.getProto();
|
||||
|
@ -246,7 +246,7 @@ public class Shell {
|
||||
|
||||
// For each file on the command line.
|
||||
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) {
|
||||
return COMPILATION_ERROR;
|
||||
|
@ -41,6 +41,6 @@ str +="g()";
|
||||
try {
|
||||
eval(str);
|
||||
} catch (e) {
|
||||
print(e.stack.replace(/\\/g, '/').replace(/<eval>@[0-9]+/, '<eval>@<id>'));
|
||||
print(e.stack.replace(/\\/g, '/'));
|
||||
}
|
||||
|
||||
|
@ -1,3 +1,3 @@
|
||||
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)
|
||||
|
@ -25,11 +25,10 @@
|
||||
* JDK-8048079: Persistent code store is broken after optimistic types merge
|
||||
*
|
||||
* @test
|
||||
* @run
|
||||
* @runif external.prototype
|
||||
* @option -pcc
|
||||
* @option -Dnashorn.persistent.code.cache=build/nashorn_code_cache
|
||||
* @fork
|
||||
*/
|
||||
|
||||
load(__DIR__ + 'prototype.js');
|
||||
load(__DIR__ + 'yui.js');
|
1
nashorn/test/script/basic/JDK-8048079_1a.js.EXPECTED
Normal file
1
nashorn/test/script/basic/JDK-8048079_1a.js.EXPECTED
Normal file
@ -0,0 +1 @@
|
||||
parsed and compiled ok prototype.js
|
@ -25,11 +25,10 @@
|
||||
* JDK-8048079: Persistent code store is broken after optimistic types merge
|
||||
*
|
||||
* @test
|
||||
* @run
|
||||
* @runif external.yui
|
||||
* @option -pcc
|
||||
* @option -Dnashorn.persistent.code.cache=build/nashorn_code_cache
|
||||
* @fork
|
||||
*/
|
||||
|
||||
load(__DIR__ + 'prototype.js');
|
||||
load(__DIR__ + 'yui.js');
|
@ -1,3 +1,2 @@
|
||||
parsed and compiled ok prototype.js
|
||||
parsed and compiled ok yui-min.js
|
||||
parsed and compiled ok yui.js
|
34
nashorn/test/script/basic/JDK-8048079_2a.js
Normal file
34
nashorn/test/script/basic/JDK-8048079_2a.js
Normal 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');
|
1
nashorn/test/script/basic/JDK-8048079_2a.js.EXPECTED
Normal file
1
nashorn/test/script/basic/JDK-8048079_2a.js.EXPECTED
Normal file
@ -0,0 +1 @@
|
||||
parsed and compiled ok prototype.js
|
34
nashorn/test/script/basic/JDK-8048079_2b.js
Normal file
34
nashorn/test/script/basic/JDK-8048079_2b.js
Normal 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');
|
@ -1,3 +1,2 @@
|
||||
parsed and compiled ok prototype.js
|
||||
parsed and compiled ok yui-min.js
|
||||
parsed and compiled ok yui.js
|
48
nashorn/test/script/basic/JDK-8058179.js
Normal file
48
nashorn/test/script/basic/JDK-8058179.js
Normal 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);
|
4
nashorn/test/script/basic/JDK-8058179.js.EXPECTED
Normal file
4
nashorn/test/script/basic/JDK-8058179.js.EXPECTED
Normal file
@ -0,0 +1,4 @@
|
||||
getting value
|
||||
setting value: value 2
|
||||
value 1
|
||||
value 2
|
@ -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;
|
||||
^
|
||||
|
59
nashorn/test/script/basic/es6/const-redeclare-extra.js
Normal file
59
nashorn/test/script/basic/es6/const-redeclare-extra.js
Normal 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)
|
||||
|
@ -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 () {}
|
||||
^
|
@ -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;
|
||||
^
|
||||
|
70
nashorn/test/script/basic/es6/let-redeclare-extra.js
Normal file
70
nashorn/test/script/basic/es6/let-redeclare-extra.js
Normal 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)
|
@ -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 () {};
|
||||
^
|
@ -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;
|
||||
^
|
||||
|
123
nashorn/test/script/basic/es6/let_const_closure.js
Normal file
123
nashorn/test/script/basic/es6/let_const_closure.js
Normal 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)
|
13
nashorn/test/script/basic/es6/let_const_closure.js.EXPECTED
Normal file
13
nashorn/test/script/basic/es6/let_const_closure.js.EXPECTED
Normal file
@ -0,0 +1,13 @@
|
||||
41
|
||||
42
|
||||
43
|
||||
44
|
||||
test
|
||||
test
|
||||
test
|
||||
3
|
||||
3
|
||||
3
|
||||
0
|
||||
1
|
||||
2
|
71
nashorn/test/script/basic/es6/let_const_reuse.js
Normal file
71
nashorn/test/script/basic/es6/let_const_reuse.js
Normal 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)
|
@ -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
|
73
nashorn/test/script/basic/es6/let_different_types.js
Normal file
73
nashorn/test/script/basic/es6/let_different_types.js
Normal 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)
|
||||
|
@ -0,0 +1,13 @@
|
||||
function
|
||||
number
|
||||
number
|
||||
number
|
||||
function
|
||||
string
|
||||
undefined
|
||||
number
|
||||
54.7
|
||||
function
|
||||
function
|
||||
Hello World!
|
||||
object
|
80
nashorn/test/script/basic/es6/let_loops.js
Normal file
80
nashorn/test/script/basic/es6/let_loops.js
Normal 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)
|
8
nashorn/test/script/basic/es6/let_loops.js.EXPECTED
Normal file
8
nashorn/test/script/basic/es6/let_loops.js.EXPECTED
Normal 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
|
@ -36,13 +36,18 @@ var trees = new Array("redwood", "bay", "cedar", "oak");
|
||||
|
||||
// Testing conditional operator
|
||||
print(inspect("" ? b : x.a, "ternary operator"))
|
||||
print(inspect(x.b ? b : x.a, "ternary operator"))
|
||||
print(inspect(c ? b : a, "ternary operator"))
|
||||
print(inspect(!c ? b : a, "ternary operator"))
|
||||
print(inspect(d ? b : x.c, "ternary operator"))
|
||||
var b1 = b;
|
||||
print(inspect(x.b ? b1 : x.a, "ternary operator"))
|
||||
var b2 = b;
|
||||
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(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
|
||||
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"))
|
||||
trees[1] = 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]
|
||||
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("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
|
||||
function f1() {
|
||||
|
@ -30,7 +30,5 @@
|
||||
* @fork
|
||||
*/
|
||||
|
||||
load(__DIR__ + 'prototype.js');
|
||||
load(__DIR__ + 'yui.js');
|
||||
load(__DIR__ + 'NASHORN-689.js');
|
||||
load(__DIR__ + 'NASHORN-58.js');
|
||||
|
@ -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=9
|
||||
a=8
|
||||
|
33
nashorn/test/script/basic/splitter_prototype.js
Normal file
33
nashorn/test/script/basic/splitter_prototype.js
Normal 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');
|
1
nashorn/test/script/basic/splitter_prototype.js.EXPECTED
Normal file
1
nashorn/test/script/basic/splitter_prototype.js.EXPECTED
Normal file
@ -0,0 +1 @@
|
||||
parsed and compiled ok prototype.js
|
33
nashorn/test/script/basic/splitter_yui.js
Normal file
33
nashorn/test/script/basic/splitter_yui.js
Normal 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');
|
2
nashorn/test/script/basic/splitter_yui.js.EXPECTED
Normal file
2
nashorn/test/script/basic/splitter_yui.js.EXPECTED
Normal file
@ -0,0 +1,2 @@
|
||||
parsed and compiled ok yui-min.js
|
||||
parsed and compiled ok yui.js
|
@ -407,6 +407,75 @@ public class ScopeTest {
|
||||
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.
|
||||
*/
|
||||
|
@ -261,14 +261,17 @@ final class TestFinder {
|
||||
isTest = false;
|
||||
isNotTest = true;
|
||||
break;
|
||||
case "@runif":
|
||||
if (System.getProperty(scanner.next()) != null) {
|
||||
case "@runif": {
|
||||
final String prop = scanner.next();
|
||||
if (System.getProperty(prop) != null) {
|
||||
shouldRun = true;
|
||||
} else {
|
||||
factory.log("WARNING: (" + prop + ") skipping " + testFile);
|
||||
isTest = false;
|
||||
isNotTest = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "@run":
|
||||
shouldRun = true;
|
||||
break;
|
||||
|
Loading…
Reference in New Issue
Block a user