This commit is contained in:
Lana Steuck 2015-09-03 16:14:47 -07:00
commit 21ad3ccd0d
47 changed files with 1170 additions and 391 deletions

View File

@ -302,7 +302,7 @@ run.test.jvmargs.common=\
-XX:+HeapDumpOnOutOfMemoryError -XX:+HeapDumpOnOutOfMemoryError
# turn on assertions for tests # turn on assertions for tests
run.test.jvmargs.main=${run.test.jvmargs.common} -esa -ea run.test.jvmargs.main=${run.test.jvmargs.common} -esa -ea -da:java.lang.invoke.LambdaFormEditor
# Extra jvmargs that might be useful for debugging # Extra jvmargs that might be useful for debugging
# and performance improvements/monitoring # and performance improvements/monitoring

View File

@ -107,7 +107,7 @@ class CallerSensitiveDynamicMethod extends SingleDynamicMethod {
private final AccessibleObject target; private final AccessibleObject target;
private final MethodType type; private final MethodType type;
public CallerSensitiveDynamicMethod(final AccessibleObject target) { CallerSensitiveDynamicMethod(final AccessibleObject target) {
super(getName(target)); super(getName(target));
this.target = target; this.target = target;
this.type = getMethodType(target); this.type = getMethodType(target);
@ -115,8 +115,9 @@ class CallerSensitiveDynamicMethod extends SingleDynamicMethod {
private static String getName(final AccessibleObject target) { private static String getName(final AccessibleObject target) {
final Member m = (Member)target; final Member m = (Member)target;
return getMethodNameWithSignature(getMethodType(target), getClassAndMethodName(m.getDeclaringClass(), final boolean constructor = m instanceof Constructor;
m.getName())); return getMethodNameWithSignature(getMethodType(target), constructor ? m.getName() :
getClassAndMethodName(m.getDeclaringClass(), m.getName()), !constructor);
} }
@Override @Override

View File

@ -86,7 +86,9 @@ package jdk.internal.dynalink.beans;
import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType; import java.lang.invoke.MethodType;
import java.text.Collator;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator; import java.util.Iterator;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
@ -242,6 +244,35 @@ class OverloadedDynamicMethod extends DynamicMethod {
return methods.getFirst().isConstructor(); return methods.getFirst().isConstructor();
} }
@Override
public String toString() {
// First gather the names and sort them. This makes it consistent and easier to read.
final List<String> names = new ArrayList<>(methods.size());
int len = 0;
for (final SingleDynamicMethod m: methods) {
final String name = m.getName();
len += name.length();
names.add(name);
}
// Case insensitive sorting, so e.g. "Object" doesn't come before "boolean".
final Collator collator = Collator.getInstance();
collator.setStrength(Collator.SECONDARY);
Collections.sort(names, collator);
final String className = getClass().getName();
// Class name length + length of signatures + 2 chars/per signature for indentation and newline +
// 3 for brackets and initial newline
final int totalLength = className.length() + len + 2 * names.size() + 3;
final StringBuilder b = new StringBuilder(totalLength);
b.append('[').append(className).append('\n');
for(final String name: names) {
b.append(' ').append(name).append('\n');
}
b.append(']');
assert b.length() == totalLength;
return b.toString();
};
ClassLoader getClassLoader() { ClassLoader getClassLoader() {
return classLoader; return classLoader;
} }

View File

@ -122,13 +122,13 @@ class SimpleDynamicMethod extends SingleDynamicMethod {
* @param constructor does this represent a constructor? * @param constructor does this represent a constructor?
*/ */
SimpleDynamicMethod(final MethodHandle target, final Class<?> clazz, final String name, final boolean constructor) { SimpleDynamicMethod(final MethodHandle target, final Class<?> clazz, final String name, final boolean constructor) {
super(getName(target, clazz, name)); super(getName(target, clazz, name, constructor));
this.target = target; this.target = target;
this.constructor = constructor; this.constructor = constructor;
} }
private static String getName(final MethodHandle target, final Class<?> clazz, final String name) { private static String getName(final MethodHandle target, final Class<?> clazz, final String name, final boolean constructor) {
return getMethodNameWithSignature(target.type(), getClassAndMethodName(clazz, name)); return getMethodNameWithSignature(target.type(), constructor ? name : getClassAndMethodName(clazz, name), !constructor);
} }
@Override @Override

View File

@ -143,14 +143,18 @@ abstract class SingleDynamicMethod extends DynamicMethod {
return getMethodType().parameterList().equals(method.getMethodType().parameterList()); return getMethodType().parameterList().equals(method.getMethodType().parameterList());
} }
static String getMethodNameWithSignature(final MethodType type, final String methodName) { static String getMethodNameWithSignature(final MethodType type, final String methodName, final boolean withReturnType) {
final String typeStr = type.toString(); final String typeStr = type.toString();
final int retTypeIndex = typeStr.lastIndexOf(')') + 1; final int retTypeIndex = typeStr.lastIndexOf(')') + 1;
int secondParamIndex = typeStr.indexOf(',') + 1; int secondParamIndex = typeStr.indexOf(',') + 1;
if(secondParamIndex == 0) { if(secondParamIndex == 0) {
secondParamIndex = retTypeIndex - 1; secondParamIndex = retTypeIndex - 1;
} }
return typeStr.substring(retTypeIndex) + " " + methodName + "(" + typeStr.substring(secondParamIndex, retTypeIndex); final StringBuilder b = new StringBuilder();
if (withReturnType) {
b.append(typeStr, retTypeIndex, typeStr.length()).append(' ');
}
return b.append(methodName).append('(').append(typeStr, secondParamIndex, retTypeIndex).toString();
} }
/** /**

View File

@ -51,6 +51,8 @@ public abstract class NashornException extends RuntimeException {
private String fileName; private String fileName;
// script line number // script line number
private int line; private int line;
// are the line and fileName unknown?
private boolean lineAndFileNameUnknown;
// script column number // script column number
private int column; private int column;
// underlying ECMA error object - lazily initialized // underlying ECMA error object - lazily initialized
@ -92,27 +94,10 @@ public abstract class NashornException extends RuntimeException {
*/ */
protected NashornException(final String msg, final Throwable cause) { protected NashornException(final String msg, final Throwable cause) {
super(msg, cause == null ? null : cause); super(msg, cause == null ? null : cause);
// This is not so pretty - but it gets the job done. Note that the stack
// trace has been already filled by "fillInStackTrace" call from
// Throwable
// constructor and so we don't pay additional cost for it.
// Hard luck - no column number info // Hard luck - no column number info
this.column = -1; this.column = -1;
// We can retrieve the line number and file name from the stack trace if needed
// Find the first JavaScript frame by walking and set file, line from it this.lineAndFileNameUnknown = true;
// Usually, we should be able to find it in just few frames depth.
for (final StackTraceElement ste : getStackTrace()) {
if (ECMAErrors.isScriptFrame(ste)) {
// Whatever here is compiled from JavaScript code
this.fileName = ste.getFileName();
this.line = ste.getLineNumber();
return;
}
}
this.fileName = null;
this.line = 0;
} }
/** /**
@ -121,6 +106,7 @@ public abstract class NashornException extends RuntimeException {
* @return the file name * @return the file name
*/ */
public final String getFileName() { public final String getFileName() {
ensureLineAndFileName();
return fileName; return fileName;
} }
@ -131,6 +117,7 @@ public abstract class NashornException extends RuntimeException {
*/ */
public final void setFileName(final String fileName) { public final void setFileName(final String fileName) {
this.fileName = fileName; this.fileName = fileName;
lineAndFileNameUnknown = false;
} }
/** /**
@ -139,6 +126,7 @@ public abstract class NashornException extends RuntimeException {
* @return the line number * @return the line number
*/ */
public final int getLineNumber() { public final int getLineNumber() {
ensureLineAndFileName();
return line; return line;
} }
@ -148,6 +136,7 @@ public abstract class NashornException extends RuntimeException {
* @param line the line number * @param line the line number
*/ */
public final void setLineNumber(final int line) { public final void setLineNumber(final int line) {
lineAndFileNameUnknown = false;
this.line = line; this.line = line;
} }
@ -274,4 +263,19 @@ public abstract class NashornException extends RuntimeException {
public void setEcmaError(final Object ecmaError) { public void setEcmaError(final Object ecmaError) {
this.ecmaError = ecmaError; this.ecmaError = ecmaError;
} }
private void ensureLineAndFileName() {
if (lineAndFileNameUnknown) {
for (final StackTraceElement ste : getStackTrace()) {
if (ECMAErrors.isScriptFrame(ste)) {
// Whatever here is compiled from JavaScript code
fileName = ste.getFileName();
line = ste.getLineNumber();
return;
}
}
lineAndFileNameUnknown = false;
}
}
} }

View File

@ -283,17 +283,13 @@ public final class ApplySpecialization extends NodeVisitor<LexicalContext> imple
start++; start++;
} }
start++; //we always uses this start++; // we always use this
final List<IdentNode> params = functionNode.getParameters(); assert functionNode.getNumOfParams() == 0 : "apply2call on function with named paramaters!";
final List<IdentNode> newParams = new ArrayList<>(); final List<IdentNode> newParams = new ArrayList<>();
final long to = Math.max(params.size(), actualCallSiteType.parameterCount() - start); final long to = actualCallSiteType.parameterCount() - start;
for (int i = 0; i < to; i++) { for (int i = 0; i < to; i++) {
if (i >= params.size()) { newParams.add(new IdentNode(functionNode.getToken(), functionNode.getFinish(), EXPLODED_ARGUMENT_PREFIX.symbolName() + (i)));
newParams.add(new IdentNode(functionNode.getToken(), functionNode.getFinish(), EXPLODED_ARGUMENT_PREFIX.symbolName() + (i)));
} else {
newParams.add(params.get(i));
}
} }
callSiteTypes.push(actualCallSiteType); callSiteTypes.push(actualCallSiteType);
@ -316,6 +312,10 @@ public final class ApplySpecialization extends NodeVisitor<LexicalContext> imple
return false; return false;
} }
if (functionNode.getNumOfParams() != 0) {
return false;
}
if (functionNode.hasEval()) { if (functionNode.hasEval()) {
return false; return false;
} }

View File

@ -149,12 +149,14 @@ final class AssignSymbols extends NodeVisitor<LexicalContext> implements Loggabl
private final Deque<Set<String>> thisProperties = new ArrayDeque<>(); private final Deque<Set<String>> thisProperties = new ArrayDeque<>();
private final Map<String, Symbol> globalSymbols = new HashMap<>(); //reuse the same global symbol private final Map<String, Symbol> globalSymbols = new HashMap<>(); //reuse the same global symbol
private final Compiler compiler; private final Compiler compiler;
private final boolean isOnDemand;
public AssignSymbols(final Compiler compiler) { public AssignSymbols(final Compiler compiler) {
super(new LexicalContext()); super(new LexicalContext());
this.compiler = compiler; this.compiler = compiler;
this.log = initLogger(compiler.getContext()); this.log = initLogger(compiler.getContext());
this.debug = log.isEnabled(); this.debug = log.isEnabled();
this.isOnDemand = compiler.isOnDemandCompilation();
} }
@Override @Override
@ -390,7 +392,7 @@ final class AssignSymbols extends NodeVisitor<LexicalContext> implements Loggabl
// Create and add to appropriate block. // Create and add to appropriate block.
symbol = createSymbol(name, flags); symbol = createSymbol(name, flags);
symbolBlock.putSymbol(lc, symbol); symbolBlock.putSymbol(symbol);
if ((flags & IS_SCOPE) == 0) { if ((flags & IS_SCOPE) == 0) {
// Initial assumption; symbol can lose its slot later // Initial assumption; symbol can lose its slot later
@ -440,7 +442,7 @@ final class AssignSymbols extends NodeVisitor<LexicalContext> implements Loggabl
start(block); start(block);
if (lc.isFunctionBody()) { if (lc.isFunctionBody()) {
block.clearSymbols(); assert !block.hasSymbols();
final FunctionNode fn = lc.getCurrentFunction(); final FunctionNode fn = lc.getCurrentFunction();
if (isUnparsedFunction(fn)) { if (isUnparsedFunction(fn)) {
// It's a skipped nested function. Just mark the symbols being used by it as being in use. // It's a skipped nested function. Just mark the symbols being used by it as being in use.
@ -459,7 +461,7 @@ final class AssignSymbols extends NodeVisitor<LexicalContext> implements Loggabl
} }
private boolean isUnparsedFunction(final FunctionNode fn) { private boolean isUnparsedFunction(final FunctionNode fn) {
return compiler.isOnDemandCompilation() && fn != lc.getOutermostFunction(); return isOnDemand && fn != lc.getOutermostFunction();
} }
@Override @Override
@ -747,28 +749,6 @@ 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.
if (compiler.useOptimisticTypes() && compiler.isOnDemandCompilation()) {
// 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());
}
}
}
}
return block;
}
private Node leaveDELETE(final UnaryNode unaryNode) { private Node leaveDELETE(final UnaryNode unaryNode) {
final FunctionNode currentFunctionNode = lc.getCurrentFunction(); final FunctionNode currentFunctionNode = lc.getCurrentFunction();
final boolean strictMode = currentFunctionNode.isStrict(); final boolean strictMode = currentFunctionNode.isStrict();
@ -786,9 +766,9 @@ final class AssignSymbols extends NodeVisitor<LexicalContext> implements Loggabl
if (symbol.isThis()) { if (symbol.isThis()) {
// Can't delete "this", ignore and return true // Can't delete "this", ignore and return true
return LiteralNode.newInstance(unaryNode, true).accept(this); return LiteralNode.newInstance(unaryNode, true);
} }
final Expression literalNode = (Expression)LiteralNode.newInstance(unaryNode, name).accept(this); final Expression literalNode = LiteralNode.newInstance(unaryNode, name);
final boolean failDelete = strictMode || (!symbol.isScope() && (symbol.isParam() || (symbol.isVar() && !symbol.isProgramLevel()))); final boolean failDelete = strictMode || (!symbol.isScope() && (symbol.isParam() || (symbol.isVar() && !symbol.isProgramLevel())));
if (!failDelete) { if (!failDelete) {
@ -799,7 +779,7 @@ final class AssignSymbols extends NodeVisitor<LexicalContext> implements Loggabl
if (failDelete) { if (failDelete) {
request = Request.FAIL_DELETE; request = Request.FAIL_DELETE;
} else if (symbol.isGlobal() && !symbol.isFunctionDeclaration()) { } else if ((symbol.isGlobal() && !symbol.isFunctionDeclaration()) || symbol.isProgramLevel()) {
request = Request.SLOW_DELETE; request = Request.SLOW_DELETE;
} }
} else if (rhs instanceof AccessNode) { } else if (rhs instanceof AccessNode) {
@ -807,7 +787,7 @@ final class AssignSymbols extends NodeVisitor<LexicalContext> implements Loggabl
final String property = ((AccessNode)rhs).getProperty(); final String property = ((AccessNode)rhs).getProperty();
args.add(base); args.add(base);
args.add((Expression)LiteralNode.newInstance(unaryNode, property).accept(this)); args.add(LiteralNode.newInstance(unaryNode, property));
args.add(strictFlagNode); args.add(strictFlagNode);
} else if (rhs instanceof IndexNode) { } else if (rhs instanceof IndexNode) {
@ -820,15 +800,15 @@ final class AssignSymbols extends NodeVisitor<LexicalContext> implements Loggabl
args.add(strictFlagNode); args.add(strictFlagNode);
} else { } else {
return LiteralNode.newInstance(unaryNode, true).accept(this); return LiteralNode.newInstance(unaryNode, true);
} }
return new RuntimeNode(unaryNode, request, args).accept(this); return new RuntimeNode(unaryNode, request, args);
} }
@Override @Override
public Node leaveForNode(final ForNode forNode) { public Node leaveForNode(final ForNode forNode) {
if (forNode.isForIn()) { if (forNode.isForIn()) {
forNode.setIterator(newObjectInternal(ITERATOR_PREFIX)); //NASHORN-73 return forNode.setIterator(lc, newObjectInternal(ITERATOR_PREFIX)); //NASHORN-73
} }
return end(forNode); return end(forNode);
@ -904,19 +884,18 @@ final class AssignSymbols extends NodeVisitor<LexicalContext> implements Loggabl
public Node leaveSwitchNode(final SwitchNode switchNode) { public Node leaveSwitchNode(final SwitchNode switchNode) {
// We only need a symbol for the tag if it's not an integer switch node // We only need a symbol for the tag if it's not an integer switch node
if(!switchNode.isUniqueInteger()) { if(!switchNode.isUniqueInteger()) {
switchNode.setTag(newObjectInternal(SWITCH_TAG_PREFIX)); return switchNode.setTag(lc, newObjectInternal(SWITCH_TAG_PREFIX));
} }
return switchNode; return switchNode;
} }
@Override @Override
public Node leaveTryNode(final TryNode tryNode) { public Node leaveTryNode(final TryNode tryNode) {
tryNode.setException(exceptionSymbol());
assert tryNode.getFinallyBody() == null; assert tryNode.getFinallyBody() == null;
end(tryNode); end(tryNode);
return tryNode; return tryNode.setException(lc, exceptionSymbol());
} }
private Node leaveTYPEOF(final UnaryNode unaryNode) { private Node leaveTYPEOF(final UnaryNode unaryNode) {
@ -925,13 +904,13 @@ final class AssignSymbols extends NodeVisitor<LexicalContext> implements Loggabl
final List<Expression> args = new ArrayList<>(); final List<Expression> args = new ArrayList<>();
if (rhs instanceof IdentNode && !isParamOrVar((IdentNode)rhs)) { if (rhs instanceof IdentNode && !isParamOrVar((IdentNode)rhs)) {
args.add(compilerConstantIdentifier(SCOPE)); args.add(compilerConstantIdentifier(SCOPE));
args.add((Expression)LiteralNode.newInstance(rhs, ((IdentNode)rhs).getName()).accept(this)); //null args.add(LiteralNode.newInstance(rhs, ((IdentNode)rhs).getName())); //null
} else { } else {
args.add(rhs); args.add(rhs);
args.add((Expression)LiteralNode.newInstance(unaryNode).accept(this)); //null, do not reuse token of identifier rhs, it can be e.g. 'this' args.add(LiteralNode.newInstance(unaryNode)); //null, do not reuse token of identifier rhs, it can be e.g. 'this'
} }
final Node runtimeNode = new RuntimeNode(unaryNode, Request.TYPEOF, args).accept(this); final Node runtimeNode = new RuntimeNode(unaryNode, Request.TYPEOF, args);
end(unaryNode); end(unaryNode);
@ -939,7 +918,7 @@ final class AssignSymbols extends NodeVisitor<LexicalContext> implements Loggabl
} }
private FunctionNode markProgramBlock(final FunctionNode functionNode) { private FunctionNode markProgramBlock(final FunctionNode functionNode) {
if (compiler.isOnDemandCompilation() || !functionNode.isProgram()) { if (isOnDemand || !functionNode.isProgram()) {
return functionNode; return functionNode;
} }

View File

@ -0,0 +1,87 @@
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.nashorn.internal.codegen;
import java.util.ArrayDeque;
import java.util.Collections;
import java.util.Deque;
import jdk.nashorn.internal.ir.FunctionNode;
import jdk.nashorn.internal.ir.LexicalContext;
import jdk.nashorn.internal.ir.Node;
import jdk.nashorn.internal.ir.Statement;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData;
class CacheAst extends NodeVisitor<LexicalContext> {
private final Deque<RecompilableScriptFunctionData> dataStack = new ArrayDeque<>();
private final Compiler compiler;
CacheAst(final Compiler compiler) {
super(new LexicalContext());
this.compiler = compiler;
assert !compiler.isOnDemandCompilation();
}
@Override
public boolean enterFunctionNode(final FunctionNode functionNode) {
final int id = functionNode.getId();
// It isn't necessary to keep a stack of RecompilableScriptFunctionData, but then we'd need to do a
// potentially transitive lookup with compiler.getScriptFunctionData(id) for deeper functions; this way
// we keep it constant time.
dataStack.push(dataStack.isEmpty() ? compiler.getScriptFunctionData(id) : dataStack.peek().getScriptFunctionData(id));
return true;
}
@Override
public Node leaveFunctionNode(final FunctionNode functionNode) {
final RecompilableScriptFunctionData data = dataStack.pop();
if (functionNode.isSplit()) {
// NOTE: cache only split function ASTs from eager pass. Caching non-split functions would require
// some additional work, namely creating the concept of "uncacheable" function and reworking
// ApplySpecialization to ensure that functions undergoing apply-to-call transformations are not
// cacheable as well as recomputing Symbol.useCount when caching the eagerly parsed AST.
// Recomputing Symbol.useCount would be needed so it will only reflect uses from within the
// function being cached (and not reflect uses from its own nested functions or functions it is
// nested in). This is consistent with the count an on-demand recompilation of the function would
// produce. This is important as the decision to emit shared scope calls is based on this count,
// and if it is not matched between a previous version of the code and its deoptimizing rest-of
// compilation, it can result in rest-of not emitting a shared scope call where a previous version
// of the code (compiled from a cached eager pre-pass seeing higher (global) useCount) would emit
// it, causing a mismatch in stack shapes between previous code and its rest-of.
data.setCachedAst(functionNode);
}
if (!dataStack.isEmpty() && ((dataStack.peek().getFunctionFlags() & FunctionNode.IS_SPLIT) != 0)) {
// Return a function node with no body so that caching outer functions doesn't hold on to nested
// functions' bodies. Note we're doing this only for functions directly nested inside split
// functions, since we're only caching the split ones. It is not necessary to limit body removal
// to just these functions, but it's a cheap way to prevent unnecessary AST mutations.
return functionNode.setBody(lc, functionNode.getBody().setStatements(null, Collections.<Statement>emptyList()));
}
return functionNode;
}
}

View File

@ -48,11 +48,13 @@ import java.util.Map.Entry;
import java.util.Set; import java.util.Set;
import jdk.nashorn.internal.AssertsEnabled; import jdk.nashorn.internal.AssertsEnabled;
import jdk.nashorn.internal.codegen.Compiler.CompilationPhases; import jdk.nashorn.internal.codegen.Compiler.CompilationPhases;
import jdk.nashorn.internal.ir.Block;
import jdk.nashorn.internal.ir.FunctionNode; import jdk.nashorn.internal.ir.FunctionNode;
import jdk.nashorn.internal.ir.FunctionNode.CompilationState; import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
import jdk.nashorn.internal.ir.LexicalContext; import jdk.nashorn.internal.ir.LexicalContext;
import jdk.nashorn.internal.ir.LiteralNode; import jdk.nashorn.internal.ir.LiteralNode;
import jdk.nashorn.internal.ir.Node; import jdk.nashorn.internal.ir.Node;
import jdk.nashorn.internal.ir.Symbol;
import jdk.nashorn.internal.ir.debug.ASTWriter; import jdk.nashorn.internal.ir.debug.ASTWriter;
import jdk.nashorn.internal.ir.debug.PrintVisitor; import jdk.nashorn.internal.ir.debug.PrintVisitor;
import jdk.nashorn.internal.ir.visitor.NodeVisitor; import jdk.nashorn.internal.ir.visitor.NodeVisitor;
@ -189,7 +191,7 @@ enum CompilationPhase {
} }
}, },
SERIALIZE_SPLIT_PHASE( CACHE_AST(
EnumSet.of( EnumSet.of(
INITIALIZED, INITIALIZED,
PARSED, PARSED,
@ -199,20 +201,21 @@ enum CompilationPhase {
SPLIT)) { SPLIT)) {
@Override @Override
FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
return transformFunction(fn, new NodeVisitor<LexicalContext>(new LexicalContext()) { if (!compiler.isOnDemandCompilation()) {
@Override // Only do this on initial preprocessing of the source code. For on-demand compilations from
public boolean enterFunctionNode(final FunctionNode functionNode) { // source, FindScopeDepths#leaveFunctionNode() calls data.setCachedAst() for the sole function
if (functionNode.isSplit()) { // being compiled.
compiler.serializeAst(functionNode); transformFunction(fn, new CacheAst(compiler));
} }
return true; // NOTE: we're returning the original fn as we have destructively modified the cached functions by
} // removing their bodies. This step is associating FunctionNode objects with
}); // RecompilableScriptFunctionData; it's not really modifying the AST.
return fn;
} }
@Override @Override
public String toString() { public String toString() {
return "'Serialize Split Functions'"; return "'Cache ASTs'";
} }
}, },
@ -255,6 +258,51 @@ enum CompilationPhase {
} }
}, },
DECLARE_LOCAL_SYMBOLS_TO_COMPILER(
EnumSet.of(
INITIALIZED,
PARSED,
CONSTANT_FOLDED,
LOWERED,
BUILTINS_TRANSFORMED,
SPLIT,
SYMBOLS_ASSIGNED,
SCOPE_DEPTHS_COMPUTED)) {
@Override
FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
// 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()) {
fn.getBody().accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
@Override
public boolean enterFunctionNode(final FunctionNode functionNode) {
// 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.
return false;
};
@Override
public boolean enterBlock(final Block block) {
for (final Symbol symbol: block.getSymbols()) {
if (!symbol.isScope()) {
compiler.declareLocalSymbol(symbol.getName());
}
}
return true;
};
});
}
return fn;
}
@Override
public String toString() {
return "'Local Symbols Declaration'";
}
},
OPTIMISTIC_TYPE_ASSIGNMENT_PHASE( OPTIMISTIC_TYPE_ASSIGNMENT_PHASE(
EnumSet.of( EnumSet.of(
INITIALIZED, INITIALIZED,
@ -382,7 +430,7 @@ enum CompilationPhase {
} }
}, },
REINITIALIZE_SERIALIZED( REINITIALIZE_CACHED(
EnumSet.of( EnumSet.of(
INITIALIZED, INITIALIZED,
PARSED, PARSED,
@ -430,7 +478,7 @@ enum CompilationPhase {
@Override @Override
public String toString() { public String toString() {
return "'Deserialize'"; return "'Reinitialize cached'";
} }
}, },

View File

@ -160,42 +160,41 @@ public final class Compiler implements Loggable {
*/ */
private static final int COMPILE_UNIT_NAME_BUFFER_SIZE = 32; private static final int COMPILE_UNIT_NAME_BUFFER_SIZE = 32;
private final Map<Integer, byte[]> serializedAsts = new HashMap<>();
/** /**
* Compilation phases that a compilation goes through * Compilation phases that a compilation goes through
*/ */
public static class CompilationPhases implements Iterable<CompilationPhase> { public static class CompilationPhases implements Iterable<CompilationPhase> {
/** /**
* Singleton that describes compilation up to the phase where a function can be serialized. * Singleton that describes compilation up to the phase where a function can be cached.
*/ */
private final static CompilationPhases COMPILE_UPTO_SERIALIZABLE = new CompilationPhases( private final static CompilationPhases COMPILE_UPTO_CACHED = new CompilationPhases(
"Common initial phases", "Common initial phases",
CompilationPhase.CONSTANT_FOLDING_PHASE, CompilationPhase.CONSTANT_FOLDING_PHASE,
CompilationPhase.LOWERING_PHASE, CompilationPhase.LOWERING_PHASE,
CompilationPhase.TRANSFORM_BUILTINS_PHASE, CompilationPhase.TRANSFORM_BUILTINS_PHASE,
CompilationPhase.SPLITTING_PHASE, CompilationPhase.SPLITTING_PHASE,
CompilationPhase.PROGRAM_POINT_PHASE, CompilationPhase.PROGRAM_POINT_PHASE,
CompilationPhase.SERIALIZE_SPLIT_PHASE
);
private final static CompilationPhases COMPILE_SERIALIZABLE_UPTO_BYTECODE = new CompilationPhases(
"After common phases, before bytecode generator",
CompilationPhase.SYMBOL_ASSIGNMENT_PHASE, CompilationPhase.SYMBOL_ASSIGNMENT_PHASE,
CompilationPhase.SCOPE_DEPTH_COMPUTATION_PHASE, CompilationPhase.SCOPE_DEPTH_COMPUTATION_PHASE,
CompilationPhase.CACHE_AST
);
private final static CompilationPhases COMPILE_CACHED_UPTO_BYTECODE = new CompilationPhases(
"After common phases, before bytecode generator",
CompilationPhase.DECLARE_LOCAL_SYMBOLS_TO_COMPILER,
CompilationPhase.OPTIMISTIC_TYPE_ASSIGNMENT_PHASE, CompilationPhase.OPTIMISTIC_TYPE_ASSIGNMENT_PHASE,
CompilationPhase.LOCAL_VARIABLE_TYPE_CALCULATION_PHASE CompilationPhase.LOCAL_VARIABLE_TYPE_CALCULATION_PHASE
); );
/** /**
* Singleton that describes additional steps to be taken after deserializing, all the way up to (but not * Singleton that describes additional steps to be taken after retrieving a cached function, all the
* including) generating and installing code. * way up to (but not including) generating and installing code.
*/ */
public final static CompilationPhases RECOMPILE_SERIALIZED_UPTO_BYTECODE = new CompilationPhases( public final static CompilationPhases RECOMPILE_CACHED_UPTO_BYTECODE = new CompilationPhases(
"Recompile serialized function up to bytecode", "Recompile cached function up to bytecode",
CompilationPhase.REINITIALIZE_SERIALIZED, CompilationPhase.REINITIALIZE_CACHED,
COMPILE_SERIALIZABLE_UPTO_BYTECODE COMPILE_CACHED_UPTO_BYTECODE
); );
/** /**
@ -211,8 +210,8 @@ public final class Compiler implements Loggable {
/** Singleton that describes compilation up to the CodeGenerator, but not actually generating code */ /** Singleton that describes compilation up to the CodeGenerator, but not actually generating code */
public final static CompilationPhases COMPILE_UPTO_BYTECODE = new CompilationPhases( public final static CompilationPhases COMPILE_UPTO_BYTECODE = new CompilationPhases(
"Compile upto bytecode", "Compile upto bytecode",
COMPILE_UPTO_SERIALIZABLE, COMPILE_UPTO_CACHED,
COMPILE_SERIALIZABLE_UPTO_BYTECODE); COMPILE_CACHED_UPTO_BYTECODE);
/** Singleton that describes a standard eager compilation, but no installation, for example used by --compile-only */ /** Singleton that describes a standard eager compilation, but no installation, for example used by --compile-only */
public final static CompilationPhases COMPILE_ALL_NO_INSTALL = new CompilationPhases( public final static CompilationPhases COMPILE_ALL_NO_INSTALL = new CompilationPhases(
@ -227,9 +226,9 @@ public final class Compiler implements Loggable {
GENERATE_BYTECODE_AND_INSTALL); GENERATE_BYTECODE_AND_INSTALL);
/** Singleton that describes a full compilation - this includes code installation - from serialized state*/ /** Singleton that describes a full compilation - this includes code installation - from serialized state*/
public final static CompilationPhases COMPILE_ALL_SERIALIZED = new CompilationPhases( public final static CompilationPhases COMPILE_ALL_CACHED = new CompilationPhases(
"Eager compilation from serializaed state", "Eager compilation from serializaed state",
RECOMPILE_SERIALIZED_UPTO_BYTECODE, RECOMPILE_CACHED_UPTO_BYTECODE,
GENERATE_BYTECODE_AND_INSTALL); GENERATE_BYTECODE_AND_INSTALL);
/** /**
@ -248,9 +247,9 @@ public final class Compiler implements Loggable {
GENERATE_BYTECODE_AND_INSTALL_RESTOF); GENERATE_BYTECODE_AND_INSTALL_RESTOF);
/** Compile from serialized for a rest of method */ /** Compile from serialized for a rest of method */
public final static CompilationPhases COMPILE_SERIALIZED_RESTOF = new CompilationPhases( public final static CompilationPhases COMPILE_CACHED_RESTOF = new CompilationPhases(
"Compile serialized, rest of", "Compile serialized, rest of",
RECOMPILE_SERIALIZED_UPTO_BYTECODE, RECOMPILE_CACHED_UPTO_BYTECODE,
GENERATE_BYTECODE_AND_INSTALL_RESTOF); GENERATE_BYTECODE_AND_INSTALL_RESTOF);
private final List<CompilationPhase> phases; private final List<CompilationPhase> phases;
@ -313,7 +312,7 @@ public final class Compiler implements Loggable {
} }
boolean isRestOfCompilation() { boolean isRestOfCompilation() {
return this == COMPILE_ALL_RESTOF || this == GENERATE_BYTECODE_AND_INSTALL_RESTOF || this == COMPILE_SERIALIZED_RESTOF; return this == COMPILE_ALL_RESTOF || this == GENERATE_BYTECODE_AND_INSTALL_RESTOF || this == COMPILE_CACHED_RESTOF;
} }
String getDesc() { String getDesc() {
@ -766,14 +765,6 @@ public final class Compiler implements Loggable {
compileUnits.addAll(newUnits); compileUnits.addAll(newUnits);
} }
void serializeAst(final FunctionNode fn) {
serializedAsts.put(fn.getId(), AstSerializer.serialize(fn));
}
byte[] removeSerializedAst(final int fnId) {
return serializedAsts.remove(fnId);
}
CompileUnit findUnit(final long weight) { CompileUnit findUnit(final long weight) {
for (final CompileUnit unit : compileUnits) { for (final CompileUnit unit : compileUnits) {
if (unit.canHold(weight)) { if (unit.canHold(weight)) {

View File

@ -188,6 +188,9 @@ final class FindScopeDepths extends NodeVisitor<LexicalContext> implements Logga
log.fine("Reviving scriptfunction ", quote(name), " as defined in previous (now lost) dynamic scope."); log.fine("Reviving scriptfunction ", quote(name), " as defined in previous (now lost) dynamic scope.");
newFunctionNode = newFunctionNode.setInDynamicContext(lc); newFunctionNode = newFunctionNode.setInDynamicContext(lc);
} }
if (newFunctionNode == lc.getOutermostFunction() && !newFunctionNode.hasApplyToCallSpecialization()) {
data.setCachedAst(newFunctionNode);
}
return newFunctionNode; return newFunctionNode;
} }
@ -208,8 +211,7 @@ final class FindScopeDepths extends NodeVisitor<LexicalContext> implements Logga
ObjectClassGenerator.createAllocationStrategy(newFunctionNode.getThisProperties(), compiler.getContext().useDualFields()), ObjectClassGenerator.createAllocationStrategy(newFunctionNode.getThisProperties(), compiler.getContext().useDualFields()),
nestedFunctions, nestedFunctions,
externalSymbolDepths.get(fnId), externalSymbolDepths.get(fnId),
internalSymbols.get(fnId), internalSymbols.get(fnId));
compiler.removeSerializedAst(fnId));
if (lc.getOutermostFunction() != newFunctionNode) { if (lc.getOutermostFunction() != newFunctionNode) {
final FunctionNode parentFn = lc.getParentFunction(newFunctionNode); final FunctionNode parentFn = lc.getParentFunction(newFunctionNode);

View File

@ -497,7 +497,7 @@ public final class Label implements Serializable {
private transient Label.Stack stack; private transient Label.Stack stack;
/** ASM representation of this label */ /** ASM representation of this label */
private jdk.internal.org.objectweb.asm.Label label; private transient jdk.internal.org.objectweb.asm.Label label;
/** Id for debugging purposes, remove if footprint becomes unmanageable */ /** Id for debugging purposes, remove if footprint becomes unmanageable */
private final int id; private final int id;

View File

@ -27,21 +27,18 @@ package jdk.nashorn.internal.codegen;
import java.lang.invoke.MethodType; import java.lang.invoke.MethodType;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.NoSuchElementException; import java.util.NoSuchElementException;
import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.FunctionNode; import jdk.nashorn.internal.ir.FunctionNode;
import jdk.nashorn.internal.runtime.ScriptFunction; import jdk.nashorn.internal.runtime.ScriptFunction;
/** /**
* A data structure that maps one or several function nodes (by their unique id:s, not by * A tuple containing function id, parameter types, return type and needsCallee flag.
* the FunctionNode object itself, due to copy on write changing it several times through
* code generation.
*/ */
public class TypeMap { public final class TypeMap {
private final Map<Integer, Type[]> paramTypeMap = new HashMap<>(); private final int functionNodeId;
private final Map<Integer, Type> returnTypeMap = new HashMap<>(); private final Type[] paramTypes;
private final Type returnType;
private final boolean needsCallee; private final boolean needsCallee;
/** /**
@ -56,9 +53,10 @@ public class TypeMap {
for (final Class<?> p : type.parameterArray()) { for (final Class<?> p : type.parameterArray()) {
types[pos++] = Type.typeFor(p); types[pos++] = Type.typeFor(p);
} }
paramTypeMap.put(functionNodeId, types);
returnTypeMap.put(functionNodeId, Type.typeFor(type.returnType()));
this.functionNodeId = functionNodeId;
this.paramTypes = types;
this.returnType = Type.typeFor(type.returnType());
this.needsCallee = needsCallee; this.needsCallee = needsCallee;
} }
@ -69,20 +67,14 @@ public class TypeMap {
* @throws NoSuchElementException if the type map has no mapping for the requested function * @throws NoSuchElementException if the type map has no mapping for the requested function
*/ */
public Type[] getParameterTypes(final int functionNodeId) { public Type[] getParameterTypes(final int functionNodeId) {
final Type[] paramTypes = paramTypeMap.get(functionNodeId); assert this.functionNodeId == functionNodeId;
if (paramTypes == null) {
throw new NoSuchElementException(Integer.toString(functionNodeId));
}
return paramTypes.clone(); return paramTypes.clone();
} }
MethodType getCallSiteType(final FunctionNode functionNode) { MethodType getCallSiteType(final FunctionNode functionNode) {
final Type[] types = paramTypeMap.get(functionNode.getId()); assert this.functionNodeId == functionNode.getId();
if (types == null) { final Type[] types = paramTypes;
return null; MethodType mt = MethodType.methodType(returnType.getTypeClass());
}
MethodType mt = MethodType.methodType(returnTypeMap.get(functionNode.getId()).getTypeClass());
if (needsCallee) { if (needsCallee) {
mt = mt.appendParameterTypes(ScriptFunction.class); mt = mt.appendParameterTypes(ScriptFunction.class);
} }
@ -116,7 +108,8 @@ public class TypeMap {
* @return parameter type for this callsite if known * @return parameter type for this callsite if known
*/ */
Type get(final FunctionNode functionNode, final int pos) { Type get(final FunctionNode functionNode, final int pos) {
final Type[] types = paramTypeMap.get(functionNode.getId()); assert this.functionNodeId == functionNode.getId();
final Type[] types = paramTypes;
assert types == null || pos < types.length : "fn = " + functionNode.getId() + " " + "types=" + Arrays.toString(types) + " || pos=" + pos + " >= length=" + types.length + " in " + this; assert types == null || pos < types.length : "fn = " + functionNode.getId() + " " + "types=" + Arrays.toString(types) + " || pos=" + pos + " >= length=" + types.length + " in " + this;
if (types != null && pos < types.length) { if (types != null && pos < types.length) {
return types[pos]; return types[pos];
@ -124,13 +117,6 @@ public class TypeMap {
return null; return null;
} }
boolean has(final FunctionNode functionNode) {
final int id = functionNode.getId();
final Type[] paramTypes = paramTypeMap.get(id);
assert (paramTypes == null) == (returnTypeMap.get(id) == null) : "inconsistent param and return types in param map";
return paramTypes != null;
}
@Override @Override
public String toString() { public String toString() {
return toString(""); return toString("");
@ -139,27 +125,16 @@ public class TypeMap {
String toString(final String prefix) { String toString(final String prefix) {
final StringBuilder sb = new StringBuilder(); final StringBuilder sb = new StringBuilder();
if (paramTypeMap.isEmpty()) { final int id = functionNodeId;
sb.append(prefix).append("\t<empty>"); sb.append(prefix).append('\t');
return sb.toString(); sb.append("function ").append(id).append('\n');
} sb.append(prefix).append("\t\tparamTypes=");
sb.append(Arrays.toString(paramTypes));
for (final Map.Entry<Integer, Type[]> entry : paramTypeMap.entrySet()) { sb.append('\n');
final int id = entry.getKey(); sb.append(prefix).append("\t\treturnType=");
sb.append(prefix).append('\t'); final Type ret = returnType;
sb.append("function ").append(id).append('\n'); sb.append(ret == null ? "N/A" : ret);
sb.append(prefix).append("\t\tparamTypes="); sb.append('\n');
if (entry.getValue() == null) {
sb.append("[]");
} else {
sb.append(Arrays.toString(entry.getValue()));
}
sb.append('\n');
sb.append(prefix).append("\t\treturnType=");
final Type ret = returnTypeMap.get(id);
sb.append(ret == null ? "N/A" : ret);
sb.append('\n');
}
return sb.toString(); return sb.toString();
} }

View File

@ -159,11 +159,42 @@ public class Block extends Node implements BreakableNode, Terminal, Flags<Block>
} }
/** /**
* Clear the symbols in the block. * Returns true if this block defines any symbols.
* TODO: make this immutable. * @return true if this block defines any symbols.
*/ */
public void clearSymbols() { public boolean hasSymbols() {
symbols.clear(); return !symbols.isEmpty();
}
/**
* Replaces symbols defined in this block with different symbols. Used to ensure symbol tables are
* immutable upon construction and have copy-on-write semantics. Note that this method only replaces the
* symbols in the symbol table, it does not act on any contained AST nodes that might reference the symbols.
* Those should be updated separately as this method is meant to be used as part of such an update pass.
* @param lc the current lexical context
* @param replacements the map of symbol replacements
* @return a new block with replaced symbols, or this block if none of the replacements modified the symbol
* table.
*/
public Block replaceSymbols(final LexicalContext lc, final Map<Symbol, Symbol> replacements) {
if (symbols.isEmpty()) {
return this;
}
final LinkedHashMap<String, Symbol> newSymbols = new LinkedHashMap<>(symbols);
for (final Map.Entry<String, Symbol> entry: newSymbols.entrySet()) {
final Symbol newSymbol = replacements.get(entry.getValue());
assert newSymbol != null : "Missing replacement for " + entry.getKey();
entry.setValue(newSymbol);
}
return Node.replaceInLexicalContext(lc, this, new Block(this, finish, statements, flags, newSymbols, conversion));
}
/**
* Returns a copy of this block with a shallow copy of the symbol table.
* @return a copy of this block with a shallow copy of the symbol table.
*/
public Block copyWithNewSymbols() {
return new Block(this, finish, statements, flags, new LinkedHashMap<>(symbols), conversion);
} }
@Override @Override
@ -191,7 +222,7 @@ public class Block extends Node implements BreakableNode, Terminal, Flags<Block>
* @return symbol iterator * @return symbol iterator
*/ */
public List<Symbol> getSymbols() { public List<Symbol> getSymbols() {
return Collections.unmodifiableList(new ArrayList<>(symbols.values())); return symbols.isEmpty() ? Collections.emptyList() : Collections.unmodifiableList(new ArrayList<>(symbols.values()));
} }
/** /**
@ -355,10 +386,9 @@ public class Block extends Node implements BreakableNode, Terminal, Flags<Block>
/** /**
* Add or overwrite an existing symbol in the block * Add or overwrite an existing symbol in the block
* *
* @param lc get lexical context
* @param symbol symbol * @param symbol symbol
*/ */
public void putSymbol(final LexicalContext lc, final Symbol symbol) { public void putSymbol(final Symbol symbol) {
symbols.put(symbol.getName(), symbol); symbols.put(symbol.getName(), symbol);
} }

View File

@ -43,7 +43,7 @@ public final class ForNode extends LoopNode {
private final JoinPredecessorExpression modify; private final JoinPredecessorExpression modify;
/** Iterator symbol. */ /** Iterator symbol. */
private Symbol iterator; private final Symbol iterator;
/** Is this a normal for in loop? */ /** Is this a normal for in loop? */
public static final int IS_FOR_IN = 1 << 0; public static final int IS_FOR_IN = 1 << 0;
@ -86,23 +86,23 @@ public final class ForNode extends LoopNode {
this.flags = flags; this.flags = flags;
this.init = init; this.init = init;
this.modify = modify; this.modify = modify;
this.iterator = null;
} }
private ForNode(final ForNode forNode, final Expression init, final JoinPredecessorExpression test, private ForNode(final ForNode forNode, final Expression init, final JoinPredecessorExpression test,
final Block body, final JoinPredecessorExpression modify, final int flags, final boolean controlFlowEscapes, final LocalVariableConversion conversion) { final Block body, final JoinPredecessorExpression modify, final int flags,
final boolean controlFlowEscapes, final LocalVariableConversion conversion, final Symbol iterator) {
super(forNode, test, body, controlFlowEscapes, conversion); super(forNode, test, body, controlFlowEscapes, conversion);
this.init = init; this.init = init;
this.modify = modify; this.modify = modify;
this.flags = flags; this.flags = flags;
// Even if the for node gets cloned in try/finally, the symbol can be shared as only one branch of the finally this.iterator = iterator;
// is executed.
this.iterator = forNode.iterator;
} }
@Override @Override
public Node ensureUniqueLabels(final LexicalContext lc) { public Node ensureUniqueLabels(final LexicalContext lc) {
return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion)); return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion, iterator));
} }
@Override @Override
@ -175,7 +175,7 @@ public final class ForNode extends LoopNode {
if (this.init == init) { if (this.init == init) {
return this; return this;
} }
return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion)); return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion, iterator));
} }
/** /**
@ -204,10 +204,15 @@ public final class ForNode extends LoopNode {
/** /**
* Assign an iterator symbol to this ForNode. Used for for in and for each constructs * Assign an iterator symbol to this ForNode. Used for for in and for each constructs
* @param lc the current lexical context
* @param iterator the iterator symbol * @param iterator the iterator symbol
* @return a ForNode with the iterator set
*/ */
public void setIterator(final Symbol iterator) { public ForNode setIterator(final LexicalContext lc, final Symbol iterator) {
this.iterator = iterator; if (this.iterator == iterator) {
return this;
}
return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion, iterator));
} }
/** /**
@ -228,7 +233,7 @@ public final class ForNode extends LoopNode {
if (this.modify == modify) { if (this.modify == modify) {
return this; return this;
} }
return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion)); return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion, iterator));
} }
@Override @Override
@ -236,7 +241,7 @@ public final class ForNode extends LoopNode {
if (this.test == test) { if (this.test == test) {
return this; return this;
} }
return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion)); return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion, iterator));
} }
@Override @Override
@ -249,7 +254,7 @@ public final class ForNode extends LoopNode {
if (this.body == body) { if (this.body == body) {
return this; return this;
} }
return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion)); return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion, iterator));
} }
@Override @Override
@ -257,12 +262,12 @@ public final class ForNode extends LoopNode {
if (this.controlFlowEscapes == controlFlowEscapes) { if (this.controlFlowEscapes == controlFlowEscapes) {
return this; return this;
} }
return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion)); return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion, iterator));
} }
@Override @Override
JoinPredecessor setLocalVariableConversionChanged(final LexicalContext lc, final LocalVariableConversion conversion) { JoinPredecessor setLocalVariableConversionChanged(final LexicalContext lc, final LocalVariableConversion conversion) {
return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion)); return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion, iterator));
} }
@Override @Override

View File

@ -264,6 +264,11 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
*/ */
public static final int NEEDS_CALLEE = 1 << 26; public static final int NEEDS_CALLEE = 1 << 26;
/**
* Is the function node cached?
*/
public static final int IS_CACHED = 1 << 27;
/** extension callsite flags mask */ /** extension callsite flags mask */
public static final int EXTENSION_CALLSITE_FLAGS = IS_PRINT_PARSE | public static final int EXTENSION_CALLSITE_FLAGS = IS_PRINT_PARSE |
IS_PRINT_LOWER_PARSE | IS_PRINT_AST | IS_PRINT_LOWER_AST | IS_PRINT_LOWER_PARSE | IS_PRINT_AST | IS_PRINT_LOWER_AST |
@ -353,7 +358,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
final List<IdentNode> parameters, final List<IdentNode> parameters,
final int thisProperties, final int thisProperties,
final Class<?> rootClass, final Class<?> rootClass,
final Source source, Namespace namespace) { final Source source, final Namespace namespace) {
super(functionNode); super(functionNode);
this.endParserState = endParserState; this.endParserState = endParserState;
@ -757,7 +762,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
*/ */
public boolean needsCallee() { public boolean needsCallee() {
// NOTE: we only need isSplit() here to ensure that :scope can never drop below slot 2 for splitting array units. // NOTE: we only need isSplit() here to ensure that :scope can never drop below slot 2 for splitting array units.
return needsParentScope() || usesSelfSymbol() || isSplit() || (needsArguments() && !isStrict()) || hasOptimisticApplyToCall(); return needsParentScope() || usesSelfSymbol() || isSplit() || (needsArguments() && !isStrict()) || hasApplyToCallSpecialization();
} }
/** /**
@ -774,7 +779,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
* Return true if function contains an apply to call transform * Return true if function contains an apply to call transform
* @return true if this function has transformed apply to call * @return true if this function has transformed apply to call
*/ */
public boolean hasOptimisticApplyToCall() { public boolean hasApplyToCallSpecialization() {
return getFlag(HAS_APPLY_TO_CALL_SPECIALIZATION); return getFlag(HAS_APPLY_TO_CALL_SPECIALIZATION);
} }
@ -1025,6 +1030,14 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
return Collections.unmodifiableList(parameters); return Collections.unmodifiableList(parameters);
} }
/**
* Return the number of parameters to this function
* @return the number of parameters
*/
public int getNumOfParams() {
return parameters.size();
}
/** /**
* Returns the identifier for a named parameter at the specified position in this function's parameter list. * Returns the identifier for a named parameter at the specified position in this function's parameter list.
* @param index the parameter's position. * @param index the parameter's position.
@ -1161,6 +1174,24 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
return getFlag(IS_STRICT); return getFlag(IS_STRICT);
} }
/**
* Returns true if this function node has been cached.
* @return true if this function node has been cached.
*/
public boolean isCached() {
return getFlag(IS_CACHED);
}
/**
* Mark this function node as having been cached.
* @param lc the current lexical context
* @return a function node equivalent to this one, with the flag set.
*/
public FunctionNode setCached(final LexicalContext lc) {
return setFlag(lc, IS_CACHED);
}
/** /**
* Get the compile unit used to compile this function * Get the compile unit used to compile this function
* @see Compiler * @see Compiler

View File

@ -53,7 +53,7 @@ public final class SwitchNode extends BreakableStatement {
private final boolean uniqueInteger; private final boolean uniqueInteger;
/** Tag symbol. */ /** Tag symbol. */
private Symbol tag; private final Symbol tag;
/** /**
* Constructor * Constructor
@ -71,15 +71,16 @@ public final class SwitchNode extends BreakableStatement {
this.cases = cases; this.cases = cases;
this.defaultCaseIndex = defaultCase == null ? -1 : cases.indexOf(defaultCase); this.defaultCaseIndex = defaultCase == null ? -1 : cases.indexOf(defaultCase);
this.uniqueInteger = false; this.uniqueInteger = false;
this.tag = null;
} }
private SwitchNode(final SwitchNode switchNode, final Expression expression, final List<CaseNode> cases, private SwitchNode(final SwitchNode switchNode, final Expression expression, final List<CaseNode> cases,
final int defaultCaseIndex, final LocalVariableConversion conversion, final boolean uniqueInteger) { final int defaultCaseIndex, final LocalVariableConversion conversion, final boolean uniqueInteger, final Symbol tag) {
super(switchNode, conversion); super(switchNode, conversion);
this.expression = expression; this.expression = expression;
this.cases = cases; this.cases = cases;
this.defaultCaseIndex = defaultCaseIndex; this.defaultCaseIndex = defaultCaseIndex;
this.tag = switchNode.getTag(); //TODO are symbols inherited as references? this.tag = tag;
this.uniqueInteger = uniqueInteger; this.uniqueInteger = uniqueInteger;
} }
@ -89,7 +90,7 @@ public final class SwitchNode extends BreakableStatement {
for (final CaseNode caseNode : cases) { for (final CaseNode caseNode : cases) {
newCases.add(new CaseNode(caseNode, caseNode.getTest(), caseNode.getBody(), caseNode.getLocalVariableConversion())); newCases.add(new CaseNode(caseNode, caseNode.getTest(), caseNode.getBody(), caseNode.getLocalVariableConversion()));
} }
return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, newCases, defaultCaseIndex, conversion, uniqueInteger)); return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, newCases, defaultCaseIndex, conversion, uniqueInteger, tag));
} }
@Override @Override
@ -157,7 +158,7 @@ public final class SwitchNode extends BreakableStatement {
if (this.cases == cases) { if (this.cases == cases) {
return this; return this;
} }
return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, cases, defaultCaseIndex, conversion, uniqueInteger)); return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, cases, defaultCaseIndex, conversion, uniqueInteger, tag));
} }
/** /**
@ -189,7 +190,7 @@ public final class SwitchNode extends BreakableStatement {
if (this.expression == expression) { if (this.expression == expression) {
return this; return this;
} }
return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, cases, defaultCaseIndex, conversion, uniqueInteger)); return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, cases, defaultCaseIndex, conversion, uniqueInteger, tag));
} }
/** /**
@ -204,10 +205,15 @@ public final class SwitchNode extends BreakableStatement {
/** /**
* Set the tag symbol for this switch. The tag symbol is where * Set the tag symbol for this switch. The tag symbol is where
* the switch expression result is stored * the switch expression result is stored
* @param lc lexical context
* @param tag a symbol * @param tag a symbol
* @return a switch node with the symbol set
*/ */
public void setTag(final Symbol tag) { public SwitchNode setTag(final LexicalContext lc, final Symbol tag) {
this.tag = tag; if (this.tag == tag) {
return this;
}
return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, cases, defaultCaseIndex, conversion, uniqueInteger, tag));
} }
/** /**
@ -229,12 +235,12 @@ public final class SwitchNode extends BreakableStatement {
if(this.uniqueInteger == uniqueInteger) { if(this.uniqueInteger == uniqueInteger) {
return this; return this;
} }
return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, cases, defaultCaseIndex, conversion, uniqueInteger)); return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, cases, defaultCaseIndex, conversion, uniqueInteger, tag));
} }
@Override @Override
JoinPredecessor setLocalVariableConversionChanged(final LexicalContext lc, final LocalVariableConversion conversion) { JoinPredecessor setLocalVariableConversionChanged(final LexicalContext lc, final LocalVariableConversion conversion) {
return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, cases, defaultCaseIndex, conversion, uniqueInteger)); return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, cases, defaultCaseIndex, conversion, uniqueInteger, tag));
} }
} }

View File

@ -25,7 +25,10 @@
package jdk.nashorn.internal.ir; package jdk.nashorn.internal.ir;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.io.Serializable;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import java.util.StringTokenizer; import java.util.StringTokenizer;
@ -47,7 +50,9 @@ import jdk.nashorn.internal.runtime.options.Options;
* refer to their location. * refer to their location.
*/ */
public final class Symbol implements Comparable<Symbol> { public final class Symbol implements Comparable<Symbol>, Cloneable, Serializable {
private static final long serialVersionUID = 1L;
/** Is this Global */ /** Is this Global */
public static final int IS_GLOBAL = 1; public static final int IS_GLOBAL = 1;
/** Is this a variable */ /** Is this a variable */
@ -94,10 +99,10 @@ public final class Symbol implements Comparable<Symbol> {
/** First bytecode method local variable slot for storing the value(s) of this variable. -1 indicates the variable /** First bytecode method local variable slot for storing the value(s) of this variable. -1 indicates the variable
* is not stored in local variable slots or it is not yet known. */ * is not stored in local variable slots or it is not yet known. */
private int firstSlot = -1; private transient int firstSlot = -1;
/** Field number in scope or property; array index in varargs when not using arguments object. */ /** Field number in scope or property; array index in varargs when not using arguments object. */
private int fieldIndex = -1; private transient int fieldIndex = -1;
/** Number of times this symbol is used in code */ /** Number of times this symbol is used in code */
private int useCount; private int useCount;
@ -144,6 +149,15 @@ public final class Symbol implements Comparable<Symbol> {
} }
} }
@Override
public Symbol clone() {
try {
return (Symbol)super.clone();
} catch (final CloneNotSupportedException e) {
throw new AssertionError(e);
}
}
private static String align(final String string, final int max) { private static String align(final String string, final int max) {
final StringBuilder sb = new StringBuilder(); final StringBuilder sb = new StringBuilder();
sb.append(string.substring(0, Math.min(string.length(), max))); sb.append(string.substring(0, Math.min(string.length(), max)));
@ -337,7 +351,7 @@ public final class Symbol implements Comparable<Symbol> {
* Flag this symbol as scope as described in {@link Symbol#isScope()} * Flag this symbol as scope as described in {@link Symbol#isScope()}
* @return the symbol * @return the symbol
*/ */
public Symbol setIsScope() { public Symbol setIsScope() {
if (!isScope()) { if (!isScope()) {
if(shouldTrace()) { if(shouldTrace()) {
trace("SET IS SCOPE"); trace("SET IS SCOPE");
@ -609,11 +623,11 @@ public final class Symbol implements Comparable<Symbol> {
/** /**
* Increase the symbol's use count by one. * Increase the symbol's use count by one.
* @return the symbol
*/ */
public Symbol increaseUseCount() { public void increaseUseCount() {
useCount++; if (isScope()) { // Avoid dirtying a cache line; we only need the use count for scoped symbols
return this; useCount++;
}
} }
/** /**
@ -669,4 +683,10 @@ public final class Symbol implements Comparable<Symbol> {
new Throwable().printStackTrace(Context.getCurrentErr()); new Throwable().printStackTrace(Context.getCurrentErr());
} }
} }
private void readObject(final ObjectInputStream in) throws ClassNotFoundException, IOException {
in.defaultReadObject();
firstSlot = -1;
fieldIndex = -1;
}
} }

View File

@ -65,7 +65,7 @@ public final class TryNode extends LexicalContextStatement implements JoinPredec
private final List<Block> inlinedFinallies; private final List<Block> inlinedFinallies;
/** Exception symbol. */ /** Exception symbol. */
private Symbol exception; private final Symbol exception;
private final LocalVariableConversion conversion; private final LocalVariableConversion conversion;
@ -86,22 +86,23 @@ public final class TryNode extends LexicalContextStatement implements JoinPredec
this.finallyBody = finallyBody; this.finallyBody = finallyBody;
this.conversion = null; this.conversion = null;
this.inlinedFinallies = Collections.emptyList(); this.inlinedFinallies = Collections.emptyList();
this.exception = null;
} }
private TryNode(final TryNode tryNode, final Block body, final List<Block> catchBlocks, final Block finallyBody, final LocalVariableConversion conversion, final List<Block> inlinedFinallies) { private TryNode(final TryNode tryNode, final Block body, final List<Block> catchBlocks, final Block finallyBody, final LocalVariableConversion conversion, final List<Block> inlinedFinallies, final Symbol exception) {
super(tryNode); super(tryNode);
this.body = body; this.body = body;
this.catchBlocks = catchBlocks; this.catchBlocks = catchBlocks;
this.finallyBody = finallyBody; this.finallyBody = finallyBody;
this.conversion = conversion; this.conversion = conversion;
this.inlinedFinallies = inlinedFinallies; this.inlinedFinallies = inlinedFinallies;
this.exception = tryNode.exception; this.exception = exception;
} }
@Override @Override
public Node ensureUniqueLabels(final LexicalContext lc) { public Node ensureUniqueLabels(final LexicalContext lc) {
//try nodes are never in lex context //try nodes are never in lex context
return new TryNode(this, body, catchBlocks, finallyBody, conversion, inlinedFinallies); return new TryNode(this, body, catchBlocks, finallyBody, conversion, inlinedFinallies, exception);
} }
@Override @Override
@ -160,7 +161,7 @@ public final class TryNode extends LexicalContextStatement implements JoinPredec
if (this.body == body) { if (this.body == body) {
return this; return this;
} }
return Node.replaceInLexicalContext(lc, this, new TryNode(this, body, catchBlocks, finallyBody, conversion, inlinedFinallies)); return Node.replaceInLexicalContext(lc, this, new TryNode(this, body, catchBlocks, finallyBody, conversion, inlinedFinallies, exception));
} }
/** /**
@ -197,7 +198,7 @@ public final class TryNode extends LexicalContextStatement implements JoinPredec
if (this.catchBlocks == catchBlocks) { if (this.catchBlocks == catchBlocks) {
return this; return this;
} }
return Node.replaceInLexicalContext(lc, this, new TryNode(this, body, catchBlocks, finallyBody, conversion, inlinedFinallies)); return Node.replaceInLexicalContext(lc, this, new TryNode(this, body, catchBlocks, finallyBody, conversion, inlinedFinallies, exception));
} }
/** /**
@ -209,12 +210,15 @@ public final class TryNode extends LexicalContextStatement implements JoinPredec
} }
/** /**
* Set the exception symbol for this try block * Set the exception symbol for this try block
* @param lc lexical context
* @param exception a symbol for the compiler to store the exception in * @param exception a symbol for the compiler to store the exception in
* @return new TryNode or same if unchanged * @return new TryNode or same if unchanged
*/ */
public TryNode setException(final Symbol exception) { public TryNode setException(final LexicalContext lc, final Symbol exception) {
this.exception = exception; if (this.exception == exception) {
return this; return this;
}
return Node.replaceInLexicalContext(lc, this, new TryNode(this, body, catchBlocks, finallyBody, conversion, inlinedFinallies, exception));
} }
/** /**
@ -277,7 +281,7 @@ public final class TryNode extends LexicalContextStatement implements JoinPredec
if (this.finallyBody == finallyBody) { if (this.finallyBody == finallyBody) {
return this; return this;
} }
return Node.replaceInLexicalContext(lc, this, new TryNode(this, body, catchBlocks, finallyBody, conversion, inlinedFinallies)); return Node.replaceInLexicalContext(lc, this, new TryNode(this, body, catchBlocks, finallyBody, conversion, inlinedFinallies, exception));
} }
/** /**
@ -293,7 +297,7 @@ public final class TryNode extends LexicalContextStatement implements JoinPredec
return this; return this;
} }
assert checkInlinedFinallies(inlinedFinallies); assert checkInlinedFinallies(inlinedFinallies);
return Node.replaceInLexicalContext(lc, this, new TryNode(this, body, catchBlocks, finallyBody, conversion, inlinedFinallies)); return Node.replaceInLexicalContext(lc, this, new TryNode(this, body, catchBlocks, finallyBody, conversion, inlinedFinallies, exception));
} }
private static boolean checkInlinedFinallies(final List<Block> inlinedFinallies) { private static boolean checkInlinedFinallies(final List<Block> inlinedFinallies) {
@ -314,7 +318,7 @@ public final class TryNode extends LexicalContextStatement implements JoinPredec
if(this.conversion == conversion) { if(this.conversion == conversion) {
return this; return this;
} }
return new TryNode(this, body, catchBlocks, finallyBody, conversion, inlinedFinallies); return new TryNode(this, body, catchBlocks, finallyBody, conversion, inlinedFinallies, exception);
} }
@Override @Override

View File

@ -2419,7 +2419,7 @@ public final class Global extends Scope {
} }
private void initScripting(final ScriptEnvironment scriptEnv) { private void initScripting(final ScriptEnvironment scriptEnv) {
Object value; ScriptObject value;
value = ScriptFunctionImpl.makeFunction("readLine", ScriptingFunctions.READLINE); value = ScriptFunctionImpl.makeFunction("readLine", ScriptingFunctions.READLINE);
addOwnProperty("readLine", Attribute.NOT_ENUMERABLE, value); addOwnProperty("readLine", Attribute.NOT_ENUMERABLE, value);
@ -2428,11 +2428,13 @@ public final class Global extends Scope {
final String execName = ScriptingFunctions.EXEC_NAME; final String execName = ScriptingFunctions.EXEC_NAME;
value = ScriptFunctionImpl.makeFunction(execName, ScriptingFunctions.EXEC); value = ScriptFunctionImpl.makeFunction(execName, ScriptingFunctions.EXEC);
value.addOwnProperty(ScriptingFunctions.THROW_ON_ERROR_NAME, Attribute.NOT_ENUMERABLE, false);
addOwnProperty(execName, Attribute.NOT_ENUMERABLE, value); addOwnProperty(execName, Attribute.NOT_ENUMERABLE, value);
// Nashorn extension: global.echo (scripting-mode-only) // Nashorn extension: global.echo (scripting-mode-only)
// alias for "print" // alias for "print"
value = get("print"); value = (ScriptObject)get("print");
addOwnProperty("echo", Attribute.NOT_ENUMERABLE, value); addOwnProperty("echo", Attribute.NOT_ENUMERABLE, value);
// Nashorn extension: global.$OPTIONS (scripting-mode-only) // Nashorn extension: global.$OPTIONS (scripting-mode-only)

View File

@ -26,6 +26,7 @@
package jdk.nashorn.internal.parser; package jdk.nashorn.internal.parser;
import static jdk.nashorn.internal.parser.TokenType.ADD; import static jdk.nashorn.internal.parser.TokenType.ADD;
import static jdk.nashorn.internal.parser.TokenType.BINARY_NUMBER;
import static jdk.nashorn.internal.parser.TokenType.COMMENT; import static jdk.nashorn.internal.parser.TokenType.COMMENT;
import static jdk.nashorn.internal.parser.TokenType.DECIMAL; import static jdk.nashorn.internal.parser.TokenType.DECIMAL;
import static jdk.nashorn.internal.parser.TokenType.DIRECTIVE_COMMENT; import static jdk.nashorn.internal.parser.TokenType.DIRECTIVE_COMMENT;
@ -40,6 +41,7 @@ import static jdk.nashorn.internal.parser.TokenType.HEXADECIMAL;
import static jdk.nashorn.internal.parser.TokenType.LBRACE; import static jdk.nashorn.internal.parser.TokenType.LBRACE;
import static jdk.nashorn.internal.parser.TokenType.LPAREN; import static jdk.nashorn.internal.parser.TokenType.LPAREN;
import static jdk.nashorn.internal.parser.TokenType.OCTAL; import static jdk.nashorn.internal.parser.TokenType.OCTAL;
import static jdk.nashorn.internal.parser.TokenType.OCTAL_LEGACY;
import static jdk.nashorn.internal.parser.TokenType.RBRACE; import static jdk.nashorn.internal.parser.TokenType.RBRACE;
import static jdk.nashorn.internal.parser.TokenType.REGEX; import static jdk.nashorn.internal.parser.TokenType.REGEX;
import static jdk.nashorn.internal.parser.TokenType.RPAREN; import static jdk.nashorn.internal.parser.TokenType.RPAREN;
@ -47,6 +49,7 @@ import static jdk.nashorn.internal.parser.TokenType.STRING;
import static jdk.nashorn.internal.parser.TokenType.XML; import static jdk.nashorn.internal.parser.TokenType.XML;
import java.io.Serializable; import java.io.Serializable;
import jdk.nashorn.internal.runtime.ECMAErrors; import jdk.nashorn.internal.runtime.ECMAErrors;
import jdk.nashorn.internal.runtime.ErrorManager; import jdk.nashorn.internal.runtime.ErrorManager;
import jdk.nashorn.internal.runtime.JSErrorType; import jdk.nashorn.internal.runtime.JSErrorType;
@ -75,6 +78,9 @@ public class Lexer extends Scanner {
/** True if here and edit strings are supported. */ /** True if here and edit strings are supported. */
private final boolean scripting; private final boolean scripting;
/** True if parsing in ECMAScript 6 mode. */
private final boolean es6;
/** True if a nested scan. (scan to completion, no EOF.) */ /** True if a nested scan. (scan to completion, no EOF.) */
private final boolean nested; private final boolean nested;
@ -173,7 +179,7 @@ public class Lexer extends Scanner {
* @param stream the token stream to lex * @param stream the token stream to lex
*/ */
public Lexer(final Source source, final TokenStream stream) { public Lexer(final Source source, final TokenStream stream) {
this(source, stream, false); this(source, stream, false, false);
} }
/** /**
@ -182,9 +188,10 @@ public class Lexer extends Scanner {
* @param source the source * @param source the source
* @param stream the token stream to lex * @param stream the token stream to lex
* @param scripting are we in scripting mode * @param scripting are we in scripting mode
* @param es6 are we in ECMAScript 6 mode
*/ */
public Lexer(final Source source, final TokenStream stream, final boolean scripting) { public Lexer(final Source source, final TokenStream stream, final boolean scripting, final boolean es6) {
this(source, 0, source.getLength(), stream, scripting, false); this(source, 0, source.getLength(), stream, scripting, es6, false);
} }
/** /**
@ -195,16 +202,18 @@ public class Lexer extends Scanner {
* @param len length of source segment to lex * @param len length of source segment to lex
* @param stream token stream to lex * @param stream token stream to lex
* @param scripting are we in scripting mode * @param scripting are we in scripting mode
* @param es6 are we in ECMAScript 6 mode
* @param pauseOnFunctionBody if true, lexer will return from {@link #lexify()} when it encounters a * @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 * 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. * 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, final boolean pauseOnFunctionBody) { public Lexer(final Source source, final int start, final int len, final TokenStream stream, final boolean scripting, final boolean es6, final boolean pauseOnFunctionBody) {
super(source.getContent(), 1, start, len); super(source.getContent(), 1, start, len);
this.source = source; this.source = source;
this.stream = stream; this.stream = stream;
this.scripting = scripting; this.scripting = scripting;
this.es6 = es6;
this.nested = false; this.nested = false;
this.pendingLine = 1; this.pendingLine = 1;
this.last = EOL; this.last = EOL;
@ -218,6 +227,7 @@ public class Lexer extends Scanner {
source = lexer.source; source = lexer.source;
stream = lexer.stream; stream = lexer.stream;
scripting = lexer.scripting; scripting = lexer.scripting;
es6 = lexer.es6;
nested = true; nested = true;
pendingLine = state.pendingLine; pendingLine = state.pendingLine;
@ -1088,6 +1098,24 @@ public class Lexer extends Scanner {
} }
type = HEXADECIMAL; type = HEXADECIMAL;
} else if (digit == 0 && es6 && (ch1 == 'o' || ch1 == 'O') && convertDigit(ch2, 8) != -1) {
// Skip over 0oN.
skip(3);
// Skip over remaining digits.
while (convertDigit(ch0, 8) != -1) {
skip(1);
}
type = OCTAL;
} else if (digit == 0 && es6 && (ch1 == 'b' || ch1 == 'B') && convertDigit(ch2, 2) != -1) {
// Skip over 0bN.
skip(3);
// Skip over remaining digits.
while (convertDigit(ch0, 2) != -1) {
skip(1);
}
type = BINARY_NUMBER;
} else { } else {
// Check for possible octal constant. // Check for possible octal constant.
boolean octal = digit == 0; boolean octal = digit == 0;
@ -1105,7 +1133,7 @@ public class Lexer extends Scanner {
} }
if (octal && position - start > 1) { if (octal && position - start > 1) {
type = OCTAL; type = OCTAL_LEGACY;
} else if (ch0 == '.' || ch0 == 'E' || ch0 == 'e') { } else if (ch0 == '.' || ch0 == 'E' || ch0 == 'e') {
// Must be a double. // Must be a double.
if (ch0 == '.') { if (ch0 == '.') {
@ -1637,10 +1665,14 @@ public class Lexer extends Scanner {
switch (Token.descType(token)) { switch (Token.descType(token)) {
case DECIMAL: case DECIMAL:
return Lexer.valueOf(source.getString(start, len), 10); // number return Lexer.valueOf(source.getString(start, len), 10); // number
case OCTAL:
return Lexer.valueOf(source.getString(start, len), 8); // number
case HEXADECIMAL: case HEXADECIMAL:
return Lexer.valueOf(source.getString(start + 2, len - 2), 16); // number return Lexer.valueOf(source.getString(start + 2, len - 2), 16); // number
case OCTAL_LEGACY:
return Lexer.valueOf(source.getString(start, len), 8); // number
case OCTAL:
return Lexer.valueOf(source.getString(start + 2, len - 2), 8); // number
case BINARY_NUMBER:
return Lexer.valueOf(source.getString(start + 2, len - 2), 2); // number
case FLOATING: case FLOATING:
final String str = source.getString(start, len); final String str = source.getString(start, len);
final double value = Double.valueOf(str); final double value = Double.valueOf(str);

View File

@ -273,7 +273,7 @@ public class Parser extends AbstractParser implements Loggable {
try { try {
stream = new TokenStream(); stream = new TokenStream();
lexer = new Lexer(source, startPos, len, stream, scripting && !env._no_syntax_extensions, reparsedFunction != null); lexer = new Lexer(source, startPos, len, stream, scripting && !env._no_syntax_extensions, env._es6, reparsedFunction != null);
lexer.line = lexer.pendingLine = lineOffset + 1; lexer.line = lexer.pendingLine = lineOffset + 1;
line = lineOffset; line = lineOffset;
@ -309,7 +309,7 @@ public class Parser extends AbstractParser implements Loggable {
public List<IdentNode> parseFormalParameterList() { public List<IdentNode> parseFormalParameterList() {
try { try {
stream = new TokenStream(); stream = new TokenStream();
lexer = new Lexer(source, stream, scripting && !env._no_syntax_extensions); lexer = new Lexer(source, stream, scripting && !env._no_syntax_extensions, env._es6);
// Set up first token (skips opening EOL.) // Set up first token (skips opening EOL.)
k = -1; k = -1;
@ -333,7 +333,7 @@ public class Parser extends AbstractParser implements Loggable {
public FunctionNode parseFunctionBody() { public FunctionNode parseFunctionBody() {
try { try {
stream = new TokenStream(); stream = new TokenStream();
lexer = new Lexer(source, stream, scripting && !env._no_syntax_extensions); lexer = new Lexer(source, stream, scripting && !env._no_syntax_extensions, env._es6);
final int functionLine = line; final int functionLine = line;
// Set up first token (skips opening EOL.) // Set up first token (skips opening EOL.)
@ -716,7 +716,7 @@ loop:
restoreBlock(body); restoreBlock(body);
body.setFlag(Block.NEEDS_SCOPE); body.setFlag(Block.NEEDS_SCOPE);
final Block programBody = new Block(functionToken, functionLine, body.getFlags() | Block.IS_SYNTHETIC, body.getStatements()); final Block programBody = new Block(functionToken, finish, body.getFlags() | Block.IS_SYNTHETIC, body.getStatements());
lc.pop(script); lc.pop(script);
script.setLastToken(token); script.setLastToken(token);
@ -1216,11 +1216,10 @@ loop:
final long forToken = token; final long forToken = token;
final int forLine = line; final int forLine = line;
// start position of this for statement. This is used // start position of this for statement. This is used
// for sort order for variables declared in the initialzer // for sort order for variables declared in the initializer
// part of this 'for' statement (if any). // part of this 'for' statement (if any).
final int forStart = Token.descPosition(forToken); final int forStart = Token.descPosition(forToken);
// When ES6 for-let is enabled we create a container block to capture the LET. // When ES6 for-let is enabled we create a container block to capture the LET.
final int startLine = start;
final ParserContextBlockNode outer = useBlockScope() ? newBlock() : null; final ParserContextBlockNode outer = useBlockScope() ? newBlock() : null;
// Create FOR node, capturing FOR token. // Create FOR node, capturing FOR token.
@ -1341,22 +1340,24 @@ loop:
body = getStatement(); body = getStatement();
} finally { } finally {
lc.pop(forNode); lc.pop(forNode);
}
if (vars != null) { if (vars != null) {
for (final VarNode var : vars) { for (final VarNode var : vars) {
appendStatement(var); appendStatement(var);
}
}
if (body != null) {
appendStatement(new ForNode(forLine, forToken, body.getFinish(), body, (forNode.getFlags() | flags), init, test, modify));
}
if (outer != null) {
restoreBlock(outer);
if (body != null) {
appendStatement(new BlockStatement(forLine, new Block(
outer.getToken(),
body.getFinish(),
outer.getStatements())));
}
} }
}
if (body != null) {
appendStatement(new ForNode(forLine, forToken, body.getFinish(), body, (forNode.getFlags() | flags), init, test, modify));
}
if (outer != null) {
restoreBlock(outer);
appendStatement(new BlockStatement(startLine, new Block(
outer.getToken(),
body.getFinish(),
outer.getStatements())));
} }
} }
@ -1954,7 +1955,7 @@ loop:
} }
detectSpecialProperty(ident); detectSpecialProperty(ident);
return ident; return ident;
case OCTAL: case OCTAL_LEGACY:
if (isStrictMode) { if (isStrictMode) {
throw error(AbstractParser.message("strict.no.octal"), token); throw error(AbstractParser.message("strict.no.octal"), token);
} }
@ -1962,6 +1963,8 @@ loop:
case ESCSTRING: case ESCSTRING:
case DECIMAL: case DECIMAL:
case HEXADECIMAL: case HEXADECIMAL:
case OCTAL:
case BINARY_NUMBER:
case FLOATING: case FLOATING:
case REGEX: case REGEX:
case XML: case XML:
@ -2053,13 +2056,13 @@ loop:
// LBRACKET tested in caller. // LBRACKET tested in caller.
next(); next();
// Prepare to accummulating elements. // Prepare to accumulate elements.
final List<Expression> elements = new ArrayList<>(); final List<Expression> elements = new ArrayList<>();
// Track elisions. // Track elisions.
boolean elision = true; boolean elision = true;
loop: loop:
while (true) { while (true) {
switch (type) { switch (type) {
case RBRACKET: case RBRACKET:
next(); next();
@ -2223,7 +2226,7 @@ loop:
switch (type) { switch (type) {
case IDENT: case IDENT:
return getIdent().setIsPropertyName(); return getIdent().setIsPropertyName();
case OCTAL: case OCTAL_LEGACY:
if (isStrictMode) { if (isStrictMode) {
throw error(AbstractParser.message("strict.no.octal"), token); throw error(AbstractParser.message("strict.no.octal"), token);
} }
@ -2231,6 +2234,8 @@ loop:
case ESCSTRING: case ESCSTRING:
case DECIMAL: case DECIMAL:
case HEXADECIMAL: case HEXADECIMAL:
case OCTAL:
case BINARY_NUMBER:
case FLOATING: case FLOATING:
return getLiteral(); return getLiteral();
default: default:
@ -2284,7 +2289,7 @@ loop:
} }
} }
propertyName = createIdentNode(propertyToken, finish, ident).setIsPropertyName(); propertyName = createIdentNode(propertyToken, finish, ident).setIsPropertyName();
} else { } else {
propertyName = propertyName(); propertyName = propertyName();
} }
@ -2553,7 +2558,7 @@ loop:
final long callToken = token; final long callToken = token;
switch (type) { switch (type) {
case LBRACKET: case LBRACKET: {
next(); next();
// Get array index. // Get array index.
@ -2565,8 +2570,8 @@ loop:
lhs = new IndexNode(callToken, finish, lhs, index); lhs = new IndexNode(callToken, finish, lhs, index);
break; break;
}
case PERIOD: case PERIOD: {
if (lhs == null) { if (lhs == null) {
throw error(AbstractParser.message("expected.operand", type.getNameOrType())); throw error(AbstractParser.message("expected.operand", type.getNameOrType()));
} }
@ -2579,7 +2584,7 @@ loop:
lhs = new AccessNode(callToken, finish, lhs, property.getName()); lhs = new AccessNode(callToken, finish, lhs, property.getName());
break; break;
}
default: default:
break loop; break loop;
} }
@ -3034,7 +3039,7 @@ loop:
assert parserState != null; assert parserState != null;
stream.reset(); stream.reset();
lexer = parserState.createLexer(source, lexer, stream, scripting && !env._no_syntax_extensions); lexer = parserState.createLexer(source, lexer, stream, scripting && !env._no_syntax_extensions, env._es6);
line = parserState.line; line = parserState.line;
linePosition = parserState.linePosition; linePosition = parserState.linePosition;
// Doesn't really matter, but it's safe to treat it as if there were a semicolon before // Doesn't really matter, but it's safe to treat it as if there were a semicolon before
@ -3063,8 +3068,8 @@ loop:
this.linePosition = linePosition; this.linePosition = linePosition;
} }
Lexer createLexer(final Source source, final Lexer lexer, final TokenStream stream, final boolean scripting) { Lexer createLexer(final Source source, final Lexer lexer, final TokenStream stream, final boolean scripting, final boolean es6) {
final Lexer newLexer = new Lexer(source, position, lexer.limit - position, stream, scripting, true); final Lexer newLexer = new Lexer(source, position, lexer.limit - position, stream, scripting, es6, true);
newLexer.restoreState(new Lexer.State(position, Integer.MAX_VALUE, line, -1, linePosition, SEMICOLON)); newLexer.restoreState(new Lexer.State(position, Integer.MAX_VALUE, line, -1, linePosition, SEMICOLON));
return newLexer; return newLexer;
} }
@ -3107,15 +3112,6 @@ loop:
return new RuntimeNode(lhs.getToken(), lhs.getFinish(), RuntimeNode.Request.REFERENCE_ERROR, args); return new RuntimeNode(lhs.getToken(), lhs.getFinish(), RuntimeNode.Request.REFERENCE_ERROR, args);
} }
/*
* parse LHS [a, b, ..., c].
*
* JavaScript 1.8.
*/
//private Node destructureExpression() {
// return null;
//}
/** /**
* PostfixExpression : * PostfixExpression :
* LeftHandSideExpression * LeftHandSideExpression
@ -3127,7 +3123,7 @@ loop:
* UnaryExpression : * UnaryExpression :
* PostfixExpression * PostfixExpression
* delete UnaryExpression * delete UnaryExpression
* Node UnaryExpression * void UnaryExpression
* typeof UnaryExpression * typeof UnaryExpression
* ++ UnaryExpression * ++ UnaryExpression
* -- UnaryExpression * -- UnaryExpression
@ -3333,7 +3329,6 @@ loop:
// This method is protected so that subclass can get details // This method is protected so that subclass can get details
// at expression start point! // at expression start point!
// TODO - Destructuring array.
// Include commas in expression parsing. // Include commas in expression parsing.
return expression(unaryExpression(), COMMARIGHT.getPrecedence(), false); return expression(unaryExpression(), COMMARIGHT.getPrecedence(), false);
} }
@ -3407,7 +3402,6 @@ loop:
// This method is protected so that subclass can get details // This method is protected so that subclass can get details
// at assignment expression start point! // at assignment expression start point!
// TODO - Handle decompose.
// Exclude commas in expression parsing. // Exclude commas in expression parsing.
return expression(unaryExpression(), ASSIGN.getPrecedence(), noIn); return expression(unaryExpression(), ASSIGN.getPrecedence(), noIn);
} }

View File

@ -170,8 +170,10 @@ public enum TokenType {
YIELD (FUTURESTRICT, "yield"), YIELD (FUTURESTRICT, "yield"),
DECIMAL (LITERAL, null), DECIMAL (LITERAL, null),
OCTAL (LITERAL, null),
HEXADECIMAL (LITERAL, null), HEXADECIMAL (LITERAL, null),
OCTAL_LEGACY (LITERAL, null),
OCTAL (LITERAL, null),
BINARY_NUMBER (LITERAL, null),
FLOATING (LITERAL, null), FLOATING (LITERAL, null),
STRING (LITERAL, null), STRING (LITERAL, null),
ESCSTRING (LITERAL, null), ESCSTRING (LITERAL, null),

View File

@ -22,20 +22,14 @@
* or visit www.oracle.com if you need additional information or have any * or visit www.oracle.com if you need additional information or have any
* questions. * questions.
*/ */
package jdk.nashorn.internal.codegen; package jdk.nashorn.internal.runtime;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.ObjectOutputStream; import java.io.ObjectOutputStream;
import java.util.Collections;
import java.util.zip.Deflater; import java.util.zip.Deflater;
import java.util.zip.DeflaterOutputStream; import java.util.zip.DeflaterOutputStream;
import jdk.nashorn.internal.ir.Block;
import jdk.nashorn.internal.ir.FunctionNode; import jdk.nashorn.internal.ir.FunctionNode;
import jdk.nashorn.internal.ir.LexicalContext;
import jdk.nashorn.internal.ir.Node;
import jdk.nashorn.internal.ir.Statement;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.options.Options; import jdk.nashorn.internal.runtime.options.Options;
/** /**
@ -50,7 +44,7 @@ final class AstSerializer {
final ByteArrayOutputStream out = new ByteArrayOutputStream(); final ByteArrayOutputStream out = new ByteArrayOutputStream();
final Deflater deflater = new Deflater(COMPRESSION_LEVEL); final Deflater deflater = new Deflater(COMPRESSION_LEVEL);
try (final ObjectOutputStream oout = new ObjectOutputStream(new DeflaterOutputStream(out, deflater))) { try (final ObjectOutputStream oout = new ObjectOutputStream(new DeflaterOutputStream(out, deflater))) {
oout.writeObject(removeInnerFunctionBodies(fn)); oout.writeObject(fn);
} catch (final IOException e) { } catch (final IOException e) {
throw new AssertionError("Unexpected exception serializing function", e); throw new AssertionError("Unexpected exception serializing function", e);
} finally { } finally {
@ -58,16 +52,4 @@ final class AstSerializer {
} }
return out.toByteArray(); return out.toByteArray();
} }
private static FunctionNode removeInnerFunctionBodies(final FunctionNode fn) {
return (FunctionNode)fn.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
@Override
public Node leaveBlock(final Block block) {
if (lc.isFunctionBody() && lc.getFunction(block) != lc.getOutermostFunction()) {
return block.setStatements(lc, Collections.<Statement>emptyList());
}
return super.leaveBlock(block);
}
});
}
} }

View File

@ -27,6 +27,7 @@ package jdk.nashorn.internal.runtime;
import static jdk.nashorn.internal.lookup.Lookup.MH; import static jdk.nashorn.internal.lookup.Lookup.MH;
import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT; import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.isValid; import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.isValid;
import java.lang.invoke.CallSite; import java.lang.invoke.CallSite;
import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles;
@ -820,7 +821,7 @@ final class CompiledFunction {
// isn't available, we'll use the old one bound into the call site. // isn't available, we'll use the old one bound into the call site.
final OptimismInfo effectiveOptInfo = currentOptInfo != null ? currentOptInfo : oldOptInfo; final OptimismInfo effectiveOptInfo = currentOptInfo != null ? currentOptInfo : oldOptInfo;
FunctionNode fn = effectiveOptInfo.reparse(); FunctionNode fn = effectiveOptInfo.reparse();
final boolean serialized = effectiveOptInfo.isSerialized(); final boolean cached = fn.isCached();
final Compiler compiler = effectiveOptInfo.getCompiler(fn, ct, re); //set to non rest-of final Compiler compiler = effectiveOptInfo.getCompiler(fn, ct, re); //set to non rest-of
if (!shouldRecompile) { if (!shouldRecompile) {
@ -828,11 +829,11 @@ final class CompiledFunction {
// recompiled a deoptimized version for an inner invocation. // recompiled a deoptimized version for an inner invocation.
// We still need to do the rest of from the beginning // We still need to do the rest of from the beginning
logRecompile("Rest-of compilation [STANDALONE] ", fn, ct, effectiveOptInfo.invalidatedProgramPoints); logRecompile("Rest-of compilation [STANDALONE] ", fn, ct, effectiveOptInfo.invalidatedProgramPoints);
return restOfHandle(effectiveOptInfo, compiler.compile(fn, serialized ? CompilationPhases.COMPILE_SERIALIZED_RESTOF : CompilationPhases.COMPILE_ALL_RESTOF), currentOptInfo != null); return restOfHandle(effectiveOptInfo, compiler.compile(fn, cached ? CompilationPhases.COMPILE_CACHED_RESTOF : CompilationPhases.COMPILE_ALL_RESTOF), currentOptInfo != null);
} }
logRecompile("Deoptimizing recompilation (up to bytecode) ", fn, ct, effectiveOptInfo.invalidatedProgramPoints); logRecompile("Deoptimizing recompilation (up to bytecode) ", fn, ct, effectiveOptInfo.invalidatedProgramPoints);
fn = compiler.compile(fn, serialized ? CompilationPhases.RECOMPILE_SERIALIZED_UPTO_BYTECODE : CompilationPhases.COMPILE_UPTO_BYTECODE); fn = compiler.compile(fn, cached ? CompilationPhases.RECOMPILE_CACHED_UPTO_BYTECODE : CompilationPhases.COMPILE_UPTO_BYTECODE);
log.fine("Reusable IR generated"); log.fine("Reusable IR generated");
// compile the rest of the function, and install it // compile the rest of the function, and install it
@ -956,10 +957,6 @@ final class CompiledFunction {
FunctionNode reparse() { FunctionNode reparse() {
return data.reparse(); return data.reparse();
} }
boolean isSerialized() {
return data.isSerialized();
}
} }
@SuppressWarnings("unused") @SuppressWarnings("unused")

View File

@ -26,16 +26,24 @@
package jdk.nashorn.internal.runtime; package jdk.nashorn.internal.runtime;
import static jdk.nashorn.internal.lookup.Lookup.MH; import static jdk.nashorn.internal.lookup.Lookup.MH;
import java.io.IOException; import java.io.IOException;
import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType; import java.lang.invoke.MethodType;
import java.lang.ref.Reference;
import java.lang.ref.SoftReference;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.TreeMap; import java.util.TreeMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import jdk.internal.dynalink.support.NameCodec; import jdk.internal.dynalink.support.NameCodec;
import jdk.nashorn.internal.codegen.Compiler; import jdk.nashorn.internal.codegen.Compiler;
import jdk.nashorn.internal.codegen.Compiler.CompilationPhases; import jdk.nashorn.internal.codegen.Compiler.CompilationPhases;
@ -45,8 +53,15 @@ import jdk.nashorn.internal.codegen.Namespace;
import jdk.nashorn.internal.codegen.OptimisticTypesPersistence; import jdk.nashorn.internal.codegen.OptimisticTypesPersistence;
import jdk.nashorn.internal.codegen.TypeMap; import jdk.nashorn.internal.codegen.TypeMap;
import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.Block;
import jdk.nashorn.internal.ir.ForNode;
import jdk.nashorn.internal.ir.FunctionNode; import jdk.nashorn.internal.ir.FunctionNode;
import jdk.nashorn.internal.ir.IdentNode;
import jdk.nashorn.internal.ir.LexicalContext; import jdk.nashorn.internal.ir.LexicalContext;
import jdk.nashorn.internal.ir.Node;
import jdk.nashorn.internal.ir.SwitchNode;
import jdk.nashorn.internal.ir.Symbol;
import jdk.nashorn.internal.ir.TryNode;
import jdk.nashorn.internal.ir.visitor.NodeVisitor; import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.objects.Global; import jdk.nashorn.internal.objects.Global;
import jdk.nashorn.internal.parser.Parser; import jdk.nashorn.internal.parser.Parser;
@ -55,6 +70,7 @@ import jdk.nashorn.internal.parser.TokenType;
import jdk.nashorn.internal.runtime.logging.DebugLogger; import jdk.nashorn.internal.runtime.logging.DebugLogger;
import jdk.nashorn.internal.runtime.logging.Loggable; import jdk.nashorn.internal.runtime.logging.Loggable;
import jdk.nashorn.internal.runtime.logging.Logger; import jdk.nashorn.internal.runtime.logging.Logger;
import jdk.nashorn.internal.runtime.options.Options;
/** /**
* This is a subclass that represents a script function that may be regenerated, * This is a subclass that represents a script function that may be regenerated,
* for example with specialization based on call site types, or lazily generated. * for example with specialization based on call site types, or lazily generated.
@ -66,6 +82,8 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
/** Prefix used for all recompiled script classes */ /** Prefix used for all recompiled script classes */
public static final String RECOMPILATION_PREFIX = "Recompilation$"; public static final String RECOMPILATION_PREFIX = "Recompilation$";
private static final ExecutorService astSerializerExecutorService = createAstSerializerExecutorService();
/** Unique function node id for this function node */ /** Unique function node id for this function node */
private final int functionNodeId; private final int functionNodeId;
@ -77,8 +95,12 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
/** Source from which FunctionNode was parsed. */ /** Source from which FunctionNode was parsed. */
private transient Source source; private transient Source source;
/** Serialized, compressed form of the AST. Used by split functions as they can't be reparsed from source. */ /**
private final byte[] serializedAst; * Cached form of the AST. Either a {@code SerializedAst} object used by split functions as they can't be
* reparsed from source, or a soft reference to a {@code FunctionNode} for other functions (it is safe
* to be cleared as they can be reparsed).
*/
private volatile Object cachedAst;
/** Token of this function within the source. */ /** Token of this function within the source. */
private final long token; private final long token;
@ -128,7 +150,6 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
* @param nestedFunctions nested function map * @param nestedFunctions nested function map
* @param externalScopeDepths external scope depths * @param externalScopeDepths external scope depths
* @param internalSymbols internal symbols to method, defined in its scope * @param internalSymbols internal symbols to method, defined in its scope
* @param serializedAst a serialized AST representation. Normally only used for split functions.
*/ */
public RecompilableScriptFunctionData( public RecompilableScriptFunctionData(
final FunctionNode functionNode, final FunctionNode functionNode,
@ -136,8 +157,7 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
final AllocationStrategy allocationStrategy, final AllocationStrategy allocationStrategy,
final Map<Integer, RecompilableScriptFunctionData> nestedFunctions, final Map<Integer, RecompilableScriptFunctionData> nestedFunctions,
final Map<String, Integer> externalScopeDepths, final Map<String, Integer> externalScopeDepths,
final Set<String> internalSymbols, final Set<String> internalSymbols) {
final byte[] serializedAst) {
super(functionName(functionNode), super(functionName(functionNode),
Math.min(functionNode.getParameters().size(), MAX_ARITY), Math.min(functionNode.getParameters().size(), MAX_ARITY),
@ -161,7 +181,6 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
nfn.setParent(this); nfn.setParent(this);
} }
this.serializedAst = serializedAst;
createLogger(); createLogger();
} }
@ -244,7 +263,7 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
* @return parent data, or null if non exists and also null IF UNKNOWN. * @return parent data, or null if non exists and also null IF UNKNOWN.
*/ */
public RecompilableScriptFunctionData getParent() { public RecompilableScriptFunctionData getParent() {
return parent; return parent;
} }
void setParent(final RecompilableScriptFunctionData parent) { void setParent(final RecompilableScriptFunctionData parent) {
@ -358,13 +377,11 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
return allocationStrategy.allocate(map); return allocationStrategy.allocate(map);
} }
boolean isSerialized() {
return serializedAst != null;
}
FunctionNode reparse() { FunctionNode reparse() {
if (isSerialized()) { final FunctionNode cachedFunction = getCachedAst();
return deserialize(); if (cachedFunction != null) {
assert cachedFunction.isCached();
return cachedFunction;
} }
final int descPosition = Token.descPosition(token); final int descPosition = Token.descPosition(token);
@ -391,7 +408,98 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
return (isProgram() ? program : extractFunctionFromScript(program)).setName(null, functionName); return (isProgram() ? program : extractFunctionFromScript(program)).setName(null, functionName);
} }
private FunctionNode deserialize() { private FunctionNode getCachedAst() {
final Object lCachedAst = cachedAst;
// Are we softly caching the AST?
if (lCachedAst instanceof Reference<?>) {
final FunctionNode fn = (FunctionNode)((Reference<?>)lCachedAst).get();
if (fn != null) {
// Yes we are - this is fast
return cloneSymbols(fn);
}
// Are we strongly caching a serialized AST (for split functions only)?
} else if (lCachedAst instanceof SerializedAst) {
final SerializedAst serializedAst = (SerializedAst)lCachedAst;
// Even so, are we also softly caching the AST?
final FunctionNode cachedFn = serializedAst.cachedAst.get();
if (cachedFn != null) {
// Yes we are - this is fast
return cloneSymbols(cachedFn);
}
final FunctionNode deserializedFn = deserialize(serializedAst.serializedAst);
// Softly cache after deserialization, maybe next time we won't need to deserialize
serializedAst.cachedAst = new SoftReference<>(deserializedFn);
return deserializedFn;
}
// No cached representation; return null for reparsing
return null;
}
/**
* Sets the AST to cache in this function
* @param astToCache the new AST to cache
*/
public void setCachedAst(final FunctionNode astToCache) {
assert astToCache.getId() == functionNodeId; // same function
assert !(cachedAst instanceof SerializedAst); // Can't overwrite serialized AST
final boolean isSplit = astToCache.isSplit();
// If we're caching a split function, we're doing it in the eager pass, hence there can be no other
// cached representation already. In other words, isSplit implies cachedAst == null.
assert !isSplit || cachedAst == null; //
final FunctionNode symbolClonedAst = cloneSymbols(astToCache);
final Reference<FunctionNode> ref = new SoftReference<>(symbolClonedAst);
cachedAst = ref;
// Asynchronously serialize split functions.
if (isSplit) {
astSerializerExecutorService.execute(() -> {
cachedAst = new SerializedAst(symbolClonedAst, ref);
});
}
}
/**
* Creates the AST serializer executor service used for in-memory serialization of split functions' ASTs.
* It is created with an unbounded queue (so it can queue any number of pending tasks). Its core and max
* threads is the same, but they are all allowed to time out so when there's no work, they can all go
* away. The threads will be daemons, and they will time out if idle for a minute. Their priority is also
* slightly lower than normal priority as we'd prefer the CPU to keep running the program; serializing
* split function is a memory conservation measure (it allows us to release the AST), it can wait a bit.
* @return an executor service with above described characteristics.
*/
private static ExecutorService createAstSerializerExecutorService() {
final int threads = Math.max(1, Options.getIntProperty("nashorn.serialize.threads", Runtime.getRuntime().availableProcessors() / 2));
final ThreadPoolExecutor service = new ThreadPoolExecutor(threads, threads, 1, TimeUnit.MINUTES, new LinkedBlockingDeque<>(),
(r) -> {
final Thread t = new Thread(r, "Nashorn AST Serializer");
t.setDaemon(true);
t.setPriority(Thread.NORM_PRIORITY - 1);
return t;
});
service.allowCoreThreadTimeOut(true);
return service;
}
/**
* A tuple of a serialized AST and a soft reference to a deserialized AST. This is used to cache split
* functions. Since split functions are altered from their source form, they can't be reparsed from
* source. While we could just use the {@code byte[]} representation in {@link RecompilableScriptFunctionData#cachedAst}
* we're using this tuple instead to also keep a deserialized AST around in memory to cut down on
* deserialization costs.
*/
private static class SerializedAst {
private final byte[] serializedAst;
private volatile Reference<FunctionNode> cachedAst;
SerializedAst(final FunctionNode fn, final Reference<FunctionNode> cachedAst) {
this.serializedAst = AstSerializer.serialize(fn);
this.cachedAst = cachedAst;
}
}
private FunctionNode deserialize(final byte[] serializedAst) {
final ScriptEnvironment env = installer.getOwner(); final ScriptEnvironment env = installer.getOwner();
final Timing timing = env._timing; final Timing timing = env._timing;
final long t1 = System.nanoTime(); final long t1 = System.nanoTime();
@ -402,6 +510,107 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
} }
} }
private FunctionNode cloneSymbols(final FunctionNode fn) {
final IdentityHashMap<Symbol, Symbol> symbolReplacements = new IdentityHashMap<>();
final boolean cached = fn.isCached();
// blockDefinedSymbols is used to re-mark symbols defined outside the function as global. We only
// need to do this when we cache an eagerly parsed function (which currently means a split one, as we
// don't cache non-split functions from the eager pass); those already cached, or those not split
// don't need this step.
final Set<Symbol> blockDefinedSymbols = fn.isSplit() && !cached ? Collections.newSetFromMap(new IdentityHashMap<>()) : null;
FunctionNode newFn = (FunctionNode)fn.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
private Symbol getReplacement(final Symbol original) {
if (original == null) {
return null;
}
final Symbol existingReplacement = symbolReplacements.get(original);
if (existingReplacement != null) {
return existingReplacement;
}
final Symbol newReplacement = original.clone();
symbolReplacements.put(original, newReplacement);
return newReplacement;
}
@Override
public Node leaveIdentNode(final IdentNode identNode) {
final Symbol oldSymbol = identNode.getSymbol();
if (oldSymbol != null) {
final Symbol replacement = getReplacement(oldSymbol);
return identNode.setSymbol(replacement);
}
return identNode;
}
@Override
public Node leaveForNode(final ForNode forNode) {
return ensureUniqueLabels(forNode.setIterator(lc, getReplacement(forNode.getIterator())));
}
@Override
public Node leaveSwitchNode(final SwitchNode switchNode) {
return ensureUniqueLabels(switchNode.setTag(lc, getReplacement(switchNode.getTag())));
}
@Override
public Node leaveTryNode(final TryNode tryNode) {
return ensureUniqueLabels(tryNode.setException(lc, getReplacement(tryNode.getException())));
}
@Override
public boolean enterBlock(final Block block) {
for(final Symbol symbol: block.getSymbols()) {
final Symbol replacement = getReplacement(symbol);
if (blockDefinedSymbols != null) {
blockDefinedSymbols.add(replacement);
}
}
return true;
}
@Override
public Node leaveBlock(final Block block) {
return ensureUniqueLabels(block.replaceSymbols(lc, symbolReplacements));
}
@Override
public Node leaveFunctionNode(final FunctionNode functionNode) {
return functionNode.setParameters(lc, functionNode.visitParameters(this));
}
@Override
protected Node leaveDefault(final Node node) {
return ensureUniqueLabels(node);
};
private Node ensureUniqueLabels(final Node node) {
// If we're returning a cached AST, we must also ensure unique labels
return cached ? node.ensureUniqueLabels(lc) : node;
}
});
if (blockDefinedSymbols != null) {
// Mark all symbols not defined in blocks as globals
Block newBody = null;
for(final Symbol symbol: symbolReplacements.values()) {
if(!blockDefinedSymbols.contains(symbol)) {
assert symbol.isScope(); // must be scope
assert externalScopeDepths.containsKey(symbol.getName()); // must be known to us as an external
// Register it in the function body symbol table as a new global symbol
symbol.setFlags((symbol.getFlags() & ~Symbol.KINDMASK) | Symbol.IS_GLOBAL);
if (newBody == null) {
newBody = newFn.getBody().copyWithNewSymbols();
newFn = newFn.setBody(null, newBody);
}
assert newBody.getExistingSymbol(symbol.getName()) == null; // must not be defined in the body already
newBody.putSymbol(symbol);
}
}
}
return newFn.setCached(null);
}
private boolean getFunctionFlag(final int flag) { private boolean getFunctionFlag(final int flag) {
return (functionFlags & flag) != 0; return (functionFlags & flag) != 0;
} }
@ -512,9 +721,9 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
final FunctionNode fn = reparse(); final FunctionNode fn = reparse();
final Compiler compiler = getCompiler(fn, actualCallSiteType, runtimeScope); final Compiler compiler = getCompiler(fn, actualCallSiteType, runtimeScope);
final FunctionNode compiledFn = compiler.compile(fn, final FunctionNode compiledFn = compiler.compile(fn,
isSerialized() ? CompilationPhases.COMPILE_ALL_SERIALIZED : CompilationPhases.COMPILE_ALL); fn.isCached() ? CompilationPhases.COMPILE_ALL_CACHED : CompilationPhases.COMPILE_ALL);
if (persist && !compiledFn.getFlag(FunctionNode.HAS_APPLY_TO_CALL_SPECIALIZATION)) { if (persist && !compiledFn.hasApplyToCallSpecialization()) {
compiler.persistClassInfo(cacheKey, compiledFn); compiler.persistClassInfo(cacheKey, compiledFn);
} }
return new FunctionInitializer(compiledFn, compiler.getInvalidatedProgramPoints()); return new FunctionInitializer(compiledFn, compiler.getInvalidatedProgramPoints());

View File

@ -26,6 +26,7 @@
package jdk.nashorn.internal.runtime; package jdk.nashorn.internal.runtime;
import static jdk.nashorn.internal.lookup.Lookup.MH; import static jdk.nashorn.internal.lookup.Lookup.MH;
import static jdk.nashorn.internal.runtime.ECMAErrors.rangeError;
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED; import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
@ -70,6 +71,9 @@ public final class ScriptingFunctions {
/** EXIT name - special property used by $EXEC API. */ /** EXIT name - special property used by $EXEC API. */
public static final String EXIT_NAME = "$EXIT"; public static final String EXIT_NAME = "$EXIT";
/** THROW_ON_ERROR name - special property of the $EXEC function used by $EXEC API. */
public static final String THROW_ON_ERROR_NAME = "throwOnError";
/** Names of special properties used by $ENV API. */ /** Names of special properties used by $ENV API. */
public static final String ENV_NAME = "$ENV"; public static final String ENV_NAME = "$ENV";
@ -247,6 +251,19 @@ public final class ScriptingFunctions {
} }
} }
// if we got a non-zero exit code ("failure"), then we have to decide to throw error or not
if (exit != 0) {
// get the $EXEC function object from the global object
final Object exec = global.get(EXEC_NAME);
assert exec instanceof ScriptObject : EXEC_NAME + " is not a script object!";
// Check if the user has set $EXEC.throwOnError property to true. If so, throw RangeError
// If that property is not set or set to false, then silently proceed with the rest.
if (JSType.toBoolean(((ScriptObject)exec).get(THROW_ON_ERROR_NAME))) {
throw rangeError("exec.returned.non.zero", ScriptRuntime.safeToString(exit));
}
}
// Return the result from stdout. // Return the result from stdout.
return out; return out;
} }

View File

@ -30,6 +30,7 @@ final class AdaptationException extends Exception {
private final AdaptationResult adaptationResult; private final AdaptationResult adaptationResult;
AdaptationException(final AdaptationResult.Outcome outcome, final String classList) { AdaptationException(final AdaptationResult.Outcome outcome, final String classList) {
super(null, null, false, false);
this.adaptationResult = new AdaptationResult(outcome, classList); this.adaptationResult = new AdaptationResult(outcome, classList);
} }

View File

@ -189,7 +189,7 @@ public final class Bootstrap {
* @return true if the obj is an instance of @FunctionalInterface interface * @return true if the obj is an instance of @FunctionalInterface interface
*/ */
public static boolean isFunctionalInterfaceObject(final Object obj) { public static boolean isFunctionalInterfaceObject(final Object obj) {
return !JSType.isPrimitive(obj) && (NashornBeansLinker.getFunctionalInterfaceMethod(obj.getClass()) != null); return !JSType.isPrimitive(obj) && (NashornBeansLinker.getFunctionalInterfaceMethodName(obj.getClass()) != null);
} }
/** /**

View File

@ -79,10 +79,10 @@ public class NashornBeansLinker implements GuardingDynamicLinker {
} }
// cache of @FunctionalInterface method of implementor classes // cache of @FunctionalInterface method of implementor classes
private static final ClassValue<Method> FUNCTIONAL_IFACE_METHOD = new ClassValue<Method>() { private static final ClassValue<String> FUNCTIONAL_IFACE_METHOD_NAME = new ClassValue<String>() {
@Override @Override
protected Method computeValue(final Class<?> type) { protected String computeValue(final Class<?> type) {
return findFunctionalInterfaceMethod(type); return findFunctionalInterfaceMethodName(type);
} }
}; };
@ -107,19 +107,21 @@ public class NashornBeansLinker implements GuardingDynamicLinker {
// annotated interface. This way Java method, constructor references or // annotated interface. This way Java method, constructor references or
// implementations of java.util.function.* interfaces can be called as though // implementations of java.util.function.* interfaces can be called as though
// those are script functions. // those are script functions.
final Method m = getFunctionalInterfaceMethod(self.getClass()); final String name = getFunctionalInterfaceMethodName(self.getClass());
if (m != null) { if (name != null) {
final MethodType callType = desc.getMethodType(); final MethodType callType = desc.getMethodType();
// 'callee' and 'thiz' passed from script + actual arguments // drop callee (Undefined ScriptFunction) and change the request to be dyn:callMethod:<name>
if (callType.parameterCount() != m.getParameterCount() + 2) { final NashornCallSiteDescriptor newDesc = NashornCallSiteDescriptor.get(desc.getLookup(),
throw typeError("no.method.matches.args", ScriptRuntime.safeToString(self)); "dyn:callMethod:" + name, desc.getMethodType().dropParameterTypes(1, 2),
} NashornCallSiteDescriptor.getFlags(desc));
return new GuardedInvocation( final GuardedInvocation gi = getGuardedInvocation(beansLinker,
// drop 'thiz' passed from the script. linkRequest.replaceArguments(newDesc, linkRequest.getArguments()),
MH.dropArguments(linkerServices.filterInternalObjects(desc.getLookup().unreflect(m)), 1, new NashornBeansLinkerServices(linkerServices));
callType.parameterType(1)), Guards.getInstanceOfGuard(
m.getDeclaringClass())).asTypeSafeReturn( // drop 'thiz' passed from the script.
new NashornBeansLinkerServices(linkerServices), callType); return gi.replaceMethods(
MH.dropArguments(linkerServices.filterInternalObjects(gi.getInvocation()), 1, callType.parameterType(1)),
gi.getGuard());
} }
} }
return getGuardedInvocation(beansLinker, linkRequest, linkerServices); return getGuardedInvocation(beansLinker, linkRequest, linkerServices);
@ -163,7 +165,7 @@ public class NashornBeansLinker implements GuardingDynamicLinker {
return arg instanceof ConsString ? arg.toString() : arg; return arg instanceof ConsString ? arg.toString() : arg;
} }
private static Method findFunctionalInterfaceMethod(final Class<?> clazz) { private static String findFunctionalInterfaceMethodName(final Class<?> clazz) {
if (clazz == null) { if (clazz == null) {
return null; return null;
} }
@ -179,20 +181,20 @@ public class NashornBeansLinker implements GuardingDynamicLinker {
// return the first abstract method // return the first abstract method
for (final Method m : iface.getMethods()) { for (final Method m : iface.getMethods()) {
if (Modifier.isAbstract(m.getModifiers())) { if (Modifier.isAbstract(m.getModifiers())) {
return m; return m.getName();
} }
} }
} }
} }
// did not find here, try super class // did not find here, try super class
return findFunctionalInterfaceMethod(clazz.getSuperclass()); return findFunctionalInterfaceMethodName(clazz.getSuperclass());
} }
// Returns @FunctionalInterface annotated interface's single abstract // Returns @FunctionalInterface annotated interface's single abstract
// method. If not found, returns null. // method name. If not found, returns null.
static Method getFunctionalInterfaceMethod(final Class<?> clazz) { static String getFunctionalInterfaceMethodName(final Class<?> clazz) {
return FUNCTIONAL_IFACE_METHOD.get(clazz); return FUNCTIONAL_IFACE_METHOD_NAME.get(clazz);
} }
static MethodHandleTransformer createHiddenObjectFilter() { static MethodHandleTransformer createHiddenObjectFilter() {

View File

@ -24,6 +24,6 @@ public class JOniException extends RuntimeException{
private static final long serialVersionUID = -6027192180014164667L; private static final long serialVersionUID = -6027192180014164667L;
public JOniException(final String message) { public JOniException(final String message) {
super(message); super(message, null, false, false);
} }
} }

View File

@ -163,6 +163,7 @@ range.error.invalid.radix=radix argument must be in [2, 36]
range.error.invalid.date=Invalid Date range.error.invalid.date=Invalid Date
range.error.too.many.errors=Script contains too many errors: {0} errors range.error.too.many.errors=Script contains too many errors: {0} errors
range.error.concat.string.too.big=Concatenated String is too big range.error.concat.string.too.big=Concatenated String is too big
range.error.exec.returned.non.zero=$EXEC returned non-zero exit code: {0}
reference.error.not.defined="{0}" is not defined reference.error.not.defined="{0}" is not defined
reference.error.cant.be.used.as.lhs="{0}" can not be used as the left-hand side of assignment reference.error.cant.be.used.as.lhs="{0}" can not be used as the left-hand side of assignment

View File

@ -1,14 +1,28 @@
bcd bcd
[jdk.internal.dynalink.beans.SimpleDynamicMethod String java.lang.String.java.lang.String(char[],int,int)] [jdk.internal.dynalink.beans.SimpleDynamicMethod java.lang.String(char[],int,int)]
red red
TypeError: No such Java class: java.lang.NonExistent TypeError: No such Java class: java.lang.NonExistent
TypeError: No such Java constructor: Object(String) TypeError: No such Java constructor: Object(String)
TypeError: Java constructor signature invalid: Object()xxxxx TypeError: Java constructor signature invalid: Object()xxxxx
TypeError: Java constructor signature invalid: Object( TypeError: Java constructor signature invalid: Object(
TypeError: Java constructor signature invalid: Object) TypeError: Java constructor signature invalid: Object)
TypeError: Java method [jdk.internal.dynalink.beans.OverloadedDynamicMethod java.lang.System.getProperty] cannot be used as a constructor. TypeError: Java method [jdk.internal.dynalink.beans.OverloadedDynamicMethod
TypeError: Java method [jdk.internal.dynalink.beans.OverloadedDynamicMethod java.io.PrintStream.println] cannot be used as a constructor. String java.lang.System.getProperty(String,String)
TypeError: Constructor [jdk.internal.dynalink.beans.SimpleDynamicMethod String java.lang.String.java.lang.String(char[],int,int)] requires "new". String java.lang.System.getProperty(String)
] cannot be used as a constructor.
TypeError: Java method [jdk.internal.dynalink.beans.OverloadedDynamicMethod
void java.io.PrintStream.println()
void java.io.PrintStream.println(boolean)
void java.io.PrintStream.println(char)
void java.io.PrintStream.println(char[])
void java.io.PrintStream.println(double)
void java.io.PrintStream.println(float)
void java.io.PrintStream.println(int)
void java.io.PrintStream.println(long)
void java.io.PrintStream.println(Object)
void java.io.PrintStream.println(String)
] cannot be used as a constructor.
TypeError: Constructor [jdk.internal.dynalink.beans.SimpleDynamicMethod java.lang.String(char[],int,int)] requires "new".
TypeError: No such Java constructor: Runnable() TypeError: No such Java constructor: Runnable()
TypeError: No such Java constructor: Runnable(int) TypeError: No such Java constructor: Runnable(int)
java.lang.InstantiationException: java.io.InputStream java.lang.InstantiationException: java.io.InputStream

View File

@ -1,10 +1,10 @@
abc abc
[jdk.internal.dynalink.beans.SimpleDynamicMethod String java.lang.String.java.lang.String(char[],int,int)] [jdk.internal.dynalink.beans.SimpleDynamicMethod java.lang.String(char[],int,int)]
ava ava
TypeError: null is not a function TypeError: null is not a function
TypeError: null is not a function TypeError: null is not a function
TypeError: null is not a function TypeError: null is not a function
TypeError: Constructor [jdk.internal.dynalink.beans.SimpleDynamicMethod String java.lang.String.java.lang.String(char[],int,int)] requires "new". TypeError: Constructor [jdk.internal.dynalink.beans.SimpleDynamicMethod java.lang.String(char[],int,int)] requires "new".
TypeError: null is not a function TypeError: null is not a function
TypeError: null is not a function TypeError: null is not a function
java.lang.InstantiationException: java.io.InputStream java.lang.InstantiationException: java.io.InputStream

View File

@ -0,0 +1,49 @@
/*
* Copyright (c) 2015, 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-8068901: Surprising behavior with more than one functional interface on a class
*
* @test
* @run
*/
var Consumer = java.util.function.Consumer;
var JFunction = java.util.function.Function;
var fc = new (Java.extend(JFunction, Consumer))({
apply: function(x) { print("fc invoked as a function") },
accept: function(x) { print("fc invoked as a consumer") }
});
var c = new Consumer(function(x) { print("c invoked as a consumer") });
var cf = new (Java.extend(Consumer, JFunction))({
apply: function(x) { print("cf invoked as a function") },
accept: function(x) { print("cf invoked as a consumer") }
});
var f = new JFunction(function(x) { print("f invoked as a function") });
for each(x in [fc, c, fc, cf, f, cf, c, fc, f, cf]) { x(null); }

View File

@ -0,0 +1,10 @@
fc invoked as a function
c invoked as a consumer
fc invoked as a function
cf invoked as a consumer
f invoked as a function
cf invoked as a consumer
c invoked as a consumer
fc invoked as a function
f invoked as a function
cf invoked as a consumer

View File

@ -0,0 +1,40 @@
/*
* Copyright (c) 2015, 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-8068903: Can't invoke vararg @FunctionalInterface methods
*
* @test
* @run
*/
var vc = new (Java.type("jdk.nashorn.test.models.VarArgConsumer"))(
function(x) {
Assert.assertTrue(x.length == 3);
Assert.assertTrue(x[0] == 1);
Assert.assertTrue(x[1] == 2);
Assert.assertTrue(x[2] == 3);
}
);
vc(1, 2, 3);

View File

@ -1,2 +1,2 @@
TypeError: Can not create new object with constructor [jdk.internal.dynalink.beans.SimpleDynamicMethod File java.io.File.java.io.File(String,String)] with the passed arguments; they do not match any of its method signatures. TypeError: Can not create new object with constructor [jdk.internal.dynalink.beans.SimpleDynamicMethod java.io.File(String,String)] with the passed arguments; they do not match any of its method signatures.
TypeError: Can not create new object with constructor [jdk.internal.dynalink.beans.SimpleDynamicMethod Color java.awt.Color.java.awt.Color(int,int,int)] with the passed arguments; they do not match any of its method signatures. TypeError: Can not create new object with constructor [jdk.internal.dynalink.beans.SimpleDynamicMethod java.awt.Color(int,int,int)] with the passed arguments; they do not match any of its method signatures.

View File

@ -0,0 +1,46 @@
/*
* Copyright (c) 2015, 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-8134731: `Function.prototype.apply` interacts incorrectly with `arguments`
*
* @test
* @run
*/
function func() {
return (function(f){
return function(a1, a2, a3, a4){
return (f.apply(this, arguments));
}
})(function(){
return arguments.length;
})
}
Assert.assertTrue(func()() == 0);
Assert.assertTrue(func()(33) == 1);
Assert.assertTrue(func()(33, true) == 2);
Assert.assertTrue(func()(33, true, "hello") == 3);
Assert.assertTrue(func()(33, true, "hello", "world") == 4);
Assert.assertTrue(func()(33, true, "hello", "world", 42) == 5);

View File

@ -0,0 +1,36 @@
/*
* Copyright (c) 2015, 2015, 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-8134865: Need to restore for container block from lexical context in finally
*
* @test
* @option --language=es6
*/
try {
eval("function f() { for (x : y) { } }");
throw "should not reach here";
} catch (e) {
if (!(e instanceof SyntaxError)) throw e;
}

View File

@ -0,0 +1,43 @@
/*
* Copyright (c) 2015 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-8134939: Improve toString method of Dynalink OverloadedDynamicMethod
*
* @test
* @run
*/
var overloadedSetter = new (Java.type("jdk.nashorn.test.models.OverloadedSetter"));
Assert.assertEquals(String(overloadedSetter.foo),
"[jdk.internal.dynalink.beans.OverloadedDynamicMethod\n" +
" String jdk.nashorn.test.models.OverloadedSetter.foo(String)\n" +
" void jdk.nashorn.test.models.OverloadedSetter.foo(int)\n" +
"]");
Assert.assertEquals(String(overloadedSetter.setColor),
"[jdk.internal.dynalink.beans.OverloadedDynamicMethod\n" +
" void jdk.nashorn.test.models.OverloadedSetter.setColor(int)\n" +
" void jdk.nashorn.test.models.OverloadedSetter.setColor(String)\n" +
"]");

View File

@ -0,0 +1,49 @@
/*
* Copyright (c) 2015, 2015, 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-8134873: ECMAScript 6 Numeric Literals
*
* @test
* @option --language=es6
*/
function assertEquals(expected, actual) {
if (expected !== actual) {
throw new Error("expected: " + expected + ", actual: " + actual);
}
}
assertEquals(0b0, 0);
assertEquals(0B0, 0);
assertEquals(0b01, 1);
assertEquals(0B10, 2);
assertEquals(0b11111111, 255);
assertEquals(0b11111111111111111111111111111111, 4294967295);
assertEquals(0o0, 0);
assertEquals(0O0, 0);
assertEquals(0o01, 1);
assertEquals(0O10, 8);
assertEquals(0o777, 511);

View File

@ -0,0 +1,54 @@
/*
* Copyright (c) 2015, 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-8087292: nashorn should have a "fail-fast" option for scripting, analog to bash "set -e"
*
* @test
* @option -scripting
* @run
*/
function tryExec() {
try {
`java`
} catch (e) {
print(e);
}
// make sure we got non-zero ("failure") exit code!
if ($EXIT == 0) {
print("Error: expected $EXIT code to be non-zero");
}
}
// no exception now!
tryExec();
// turn on error with non-zero exit code
$EXEC.throwOnError = true;
tryExec();
// no exception after this
$EXEC.throwOnError = false;
tryExec();

View File

@ -0,0 +1 @@
RangeError: $EXEC returned non-zero exit code: 1

View File

@ -4,7 +4,18 @@ typeof java.util.Vector evalutes to function
typeof java.util.Map evalutes to function typeof java.util.Map evalutes to function
typeof java.util.HashMap evalutes to function typeof java.util.HashMap evalutes to function
var m = new java.util.HashMap(); m.put('foo', 42); m evalutes to {foo=42} var m = new java.util.HashMap(); m.put('foo', 42); m evalutes to {foo=42}
java.lang.System.out.println evalutes to [jdk.internal.dynalink.beans.OverloadedDynamicMethod java.io.PrintStream.println] java.lang.System.out.println evalutes to [jdk.internal.dynalink.beans.OverloadedDynamicMethod
void java.io.PrintStream.println()
void java.io.PrintStream.println(boolean)
void java.io.PrintStream.println(char)
void java.io.PrintStream.println(char[])
void java.io.PrintStream.println(double)
void java.io.PrintStream.println(float)
void java.io.PrintStream.println(int)
void java.io.PrintStream.println(long)
void java.io.PrintStream.println(Object)
void java.io.PrintStream.println(String)
]
java.lang.System.exit evalutes to [jdk.internal.dynalink.beans.SimpleDynamicMethod void java.lang.System.exit(int)] java.lang.System.exit evalutes to [jdk.internal.dynalink.beans.SimpleDynamicMethod void java.lang.System.exit(int)]
new javax.script.SimpleBindings throws java.lang.RuntimeException: java.lang.ClassNotFoundException: javax.script.SimpleBindings new javax.script.SimpleBindings throws java.lang.RuntimeException: java.lang.ClassNotFoundException: javax.script.SimpleBindings
Java.type('javax.script.ScriptContext') throws java.lang.RuntimeException: java.lang.ClassNotFoundException: javax.script.ScriptContext Java.type('javax.script.ScriptContext') throws java.lang.RuntimeException: java.lang.ClassNotFoundException: javax.script.ScriptContext

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -23,44 +23,13 @@
* questions. * questions.
*/ */
package jdk.nashorn.internal.runtime.arrays; package jdk.nashorn.test.models;
/** /**
* Mechanism for communicating that something isn't a plain * Simple function interface with a varargs SAM method.
* numeric integer array index. This enables things like
* array getters for the fast case in a try, basically
* just consisting of an "array[index]" access without
* any checks of boundary conditions that rarely happen
*/ */
@SuppressWarnings("serial") @FunctionalInterface
class InvalidArrayIndexException extends Exception { public interface VarArgConsumer {
public void apply(Object... o);
private final Object index;
InvalidArrayIndexException(final Object index) {
super(index == null ? "null" : index.toString());
this.index = index;
}
InvalidArrayIndexException(final int index) {
this(Integer.valueOf(index));
}
InvalidArrayIndexException(final long index) {
this(Long.valueOf(index));
}
InvalidArrayIndexException(final double index) {
this(Double.valueOf(index));
}
@Override
public String toString() {
return index.toString();
}
Object getIndex() {
return index;
}
} }