Merge
This commit is contained in:
commit
21ad3ccd0d
@ -302,7 +302,7 @@ run.test.jvmargs.common=\
|
||||
-XX:+HeapDumpOnOutOfMemoryError
|
||||
|
||||
# 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
|
||||
# and performance improvements/monitoring
|
||||
|
@ -107,7 +107,7 @@ class CallerSensitiveDynamicMethod extends SingleDynamicMethod {
|
||||
private final AccessibleObject target;
|
||||
private final MethodType type;
|
||||
|
||||
public CallerSensitiveDynamicMethod(final AccessibleObject target) {
|
||||
CallerSensitiveDynamicMethod(final AccessibleObject target) {
|
||||
super(getName(target));
|
||||
this.target = target;
|
||||
this.type = getMethodType(target);
|
||||
@ -115,8 +115,9 @@ class CallerSensitiveDynamicMethod extends SingleDynamicMethod {
|
||||
|
||||
private static String getName(final AccessibleObject target) {
|
||||
final Member m = (Member)target;
|
||||
return getMethodNameWithSignature(getMethodType(target), getClassAndMethodName(m.getDeclaringClass(),
|
||||
m.getName()));
|
||||
final boolean constructor = m instanceof Constructor;
|
||||
return getMethodNameWithSignature(getMethodType(target), constructor ? m.getName() :
|
||||
getClassAndMethodName(m.getDeclaringClass(), m.getName()), !constructor);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -86,7 +86,9 @@ package jdk.internal.dynalink.beans;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.text.Collator;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
@ -242,6 +244,35 @@ class OverloadedDynamicMethod extends DynamicMethod {
|
||||
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() {
|
||||
return classLoader;
|
||||
}
|
||||
|
@ -122,13 +122,13 @@ class SimpleDynamicMethod extends SingleDynamicMethod {
|
||||
* @param constructor does this represent a 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.constructor = constructor;
|
||||
}
|
||||
|
||||
private static String getName(final MethodHandle target, final Class<?> clazz, final String name) {
|
||||
return getMethodNameWithSignature(target.type(), getClassAndMethodName(clazz, name));
|
||||
private static String getName(final MethodHandle target, final Class<?> clazz, final String name, final boolean constructor) {
|
||||
return getMethodNameWithSignature(target.type(), constructor ? name : getClassAndMethodName(clazz, name), !constructor);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -143,14 +143,18 @@ abstract class SingleDynamicMethod extends DynamicMethod {
|
||||
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 int retTypeIndex = typeStr.lastIndexOf(')') + 1;
|
||||
int secondParamIndex = typeStr.indexOf(',') + 1;
|
||||
if(secondParamIndex == 0) {
|
||||
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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -51,6 +51,8 @@ public abstract class NashornException extends RuntimeException {
|
||||
private String fileName;
|
||||
// script line number
|
||||
private int line;
|
||||
// are the line and fileName unknown?
|
||||
private boolean lineAndFileNameUnknown;
|
||||
// script column number
|
||||
private int column;
|
||||
// underlying ECMA error object - lazily initialized
|
||||
@ -92,27 +94,10 @@ public abstract class NashornException extends RuntimeException {
|
||||
*/
|
||||
protected NashornException(final String msg, final Throwable 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
|
||||
this.column = -1;
|
||||
|
||||
// Find the first JavaScript frame by walking and set file, line from it
|
||||
// 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;
|
||||
// We can retrieve the line number and file name from the stack trace if needed
|
||||
this.lineAndFileNameUnknown = true;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -121,6 +106,7 @@ public abstract class NashornException extends RuntimeException {
|
||||
* @return the file name
|
||||
*/
|
||||
public final String getFileName() {
|
||||
ensureLineAndFileName();
|
||||
return fileName;
|
||||
}
|
||||
|
||||
@ -131,6 +117,7 @@ public abstract class NashornException extends RuntimeException {
|
||||
*/
|
||||
public final void setFileName(final String fileName) {
|
||||
this.fileName = fileName;
|
||||
lineAndFileNameUnknown = false;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -139,6 +126,7 @@ public abstract class NashornException extends RuntimeException {
|
||||
* @return the line number
|
||||
*/
|
||||
public final int getLineNumber() {
|
||||
ensureLineAndFileName();
|
||||
return line;
|
||||
}
|
||||
|
||||
@ -148,6 +136,7 @@ public abstract class NashornException extends RuntimeException {
|
||||
* @param line the line number
|
||||
*/
|
||||
public final void setLineNumber(final int line) {
|
||||
lineAndFileNameUnknown = false;
|
||||
this.line = line;
|
||||
}
|
||||
|
||||
@ -274,4 +263,19 @@ public abstract class NashornException extends RuntimeException {
|
||||
public void setEcmaError(final Object 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -283,17 +283,13 @@ public final class ApplySpecialization extends NodeVisitor<LexicalContext> imple
|
||||
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 long to = Math.max(params.size(), actualCallSiteType.parameterCount() - start);
|
||||
final long to = actualCallSiteType.parameterCount() - start;
|
||||
for (int i = 0; i < to; i++) {
|
||||
if (i >= params.size()) {
|
||||
newParams.add(new IdentNode(functionNode.getToken(), functionNode.getFinish(), EXPLODED_ARGUMENT_PREFIX.symbolName() + (i)));
|
||||
} else {
|
||||
newParams.add(params.get(i));
|
||||
}
|
||||
newParams.add(new IdentNode(functionNode.getToken(), functionNode.getFinish(), EXPLODED_ARGUMENT_PREFIX.symbolName() + (i)));
|
||||
}
|
||||
|
||||
callSiteTypes.push(actualCallSiteType);
|
||||
@ -316,6 +312,10 @@ public final class ApplySpecialization extends NodeVisitor<LexicalContext> imple
|
||||
return false;
|
||||
}
|
||||
|
||||
if (functionNode.getNumOfParams() != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (functionNode.hasEval()) {
|
||||
return false;
|
||||
}
|
||||
|
@ -149,12 +149,14 @@ final class AssignSymbols extends NodeVisitor<LexicalContext> implements Loggabl
|
||||
private final Deque<Set<String>> thisProperties = new ArrayDeque<>();
|
||||
private final Map<String, Symbol> globalSymbols = new HashMap<>(); //reuse the same global symbol
|
||||
private final Compiler compiler;
|
||||
private final boolean isOnDemand;
|
||||
|
||||
public AssignSymbols(final Compiler compiler) {
|
||||
super(new LexicalContext());
|
||||
this.compiler = compiler;
|
||||
this.log = initLogger(compiler.getContext());
|
||||
this.debug = log.isEnabled();
|
||||
this.isOnDemand = compiler.isOnDemandCompilation();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -390,7 +392,7 @@ final class AssignSymbols extends NodeVisitor<LexicalContext> implements Loggabl
|
||||
|
||||
// Create and add to appropriate block.
|
||||
symbol = createSymbol(name, flags);
|
||||
symbolBlock.putSymbol(lc, symbol);
|
||||
symbolBlock.putSymbol(symbol);
|
||||
|
||||
if ((flags & IS_SCOPE) == 0) {
|
||||
// Initial assumption; symbol can lose its slot later
|
||||
@ -440,7 +442,7 @@ final class AssignSymbols extends NodeVisitor<LexicalContext> implements Loggabl
|
||||
start(block);
|
||||
|
||||
if (lc.isFunctionBody()) {
|
||||
block.clearSymbols();
|
||||
assert !block.hasSymbols();
|
||||
final FunctionNode fn = lc.getCurrentFunction();
|
||||
if (isUnparsedFunction(fn)) {
|
||||
// It's a skipped nested function. Just mark the symbols being used by it as being in use.
|
||||
@ -459,7 +461,7 @@ final class AssignSymbols extends NodeVisitor<LexicalContext> implements Loggabl
|
||||
}
|
||||
|
||||
private boolean isUnparsedFunction(final FunctionNode fn) {
|
||||
return compiler.isOnDemandCompilation() && fn != lc.getOutermostFunction();
|
||||
return isOnDemand && fn != lc.getOutermostFunction();
|
||||
}
|
||||
|
||||
@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) {
|
||||
final FunctionNode currentFunctionNode = lc.getCurrentFunction();
|
||||
final boolean strictMode = currentFunctionNode.isStrict();
|
||||
@ -786,9 +766,9 @@ final class AssignSymbols extends NodeVisitor<LexicalContext> implements Loggabl
|
||||
|
||||
if (symbol.isThis()) {
|
||||
// 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())));
|
||||
|
||||
if (!failDelete) {
|
||||
@ -799,7 +779,7 @@ final class AssignSymbols extends NodeVisitor<LexicalContext> implements Loggabl
|
||||
|
||||
if (failDelete) {
|
||||
request = Request.FAIL_DELETE;
|
||||
} else if (symbol.isGlobal() && !symbol.isFunctionDeclaration()) {
|
||||
} else if ((symbol.isGlobal() && !symbol.isFunctionDeclaration()) || symbol.isProgramLevel()) {
|
||||
request = Request.SLOW_DELETE;
|
||||
}
|
||||
} else if (rhs instanceof AccessNode) {
|
||||
@ -807,7 +787,7 @@ final class AssignSymbols extends NodeVisitor<LexicalContext> implements Loggabl
|
||||
final String property = ((AccessNode)rhs).getProperty();
|
||||
|
||||
args.add(base);
|
||||
args.add((Expression)LiteralNode.newInstance(unaryNode, property).accept(this));
|
||||
args.add(LiteralNode.newInstance(unaryNode, property));
|
||||
args.add(strictFlagNode);
|
||||
|
||||
} else if (rhs instanceof IndexNode) {
|
||||
@ -820,15 +800,15 @@ final class AssignSymbols extends NodeVisitor<LexicalContext> implements Loggabl
|
||||
args.add(strictFlagNode);
|
||||
|
||||
} 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
|
||||
public Node leaveForNode(final ForNode forNode) {
|
||||
if (forNode.isForIn()) {
|
||||
forNode.setIterator(newObjectInternal(ITERATOR_PREFIX)); //NASHORN-73
|
||||
return forNode.setIterator(lc, newObjectInternal(ITERATOR_PREFIX)); //NASHORN-73
|
||||
}
|
||||
|
||||
return end(forNode);
|
||||
@ -904,19 +884,18 @@ final class AssignSymbols extends NodeVisitor<LexicalContext> implements Loggabl
|
||||
public Node leaveSwitchNode(final SwitchNode switchNode) {
|
||||
// We only need a symbol for the tag if it's not an integer switch node
|
||||
if(!switchNode.isUniqueInteger()) {
|
||||
switchNode.setTag(newObjectInternal(SWITCH_TAG_PREFIX));
|
||||
return switchNode.setTag(lc, newObjectInternal(SWITCH_TAG_PREFIX));
|
||||
}
|
||||
return switchNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveTryNode(final TryNode tryNode) {
|
||||
tryNode.setException(exceptionSymbol());
|
||||
assert tryNode.getFinallyBody() == null;
|
||||
|
||||
end(tryNode);
|
||||
|
||||
return tryNode;
|
||||
return tryNode.setException(lc, exceptionSymbol());
|
||||
}
|
||||
|
||||
private Node leaveTYPEOF(final UnaryNode unaryNode) {
|
||||
@ -925,13 +904,13 @@ final class AssignSymbols extends NodeVisitor<LexicalContext> implements Loggabl
|
||||
final List<Expression> args = new ArrayList<>();
|
||||
if (rhs instanceof IdentNode && !isParamOrVar((IdentNode)rhs)) {
|
||||
args.add(compilerConstantIdentifier(SCOPE));
|
||||
args.add((Expression)LiteralNode.newInstance(rhs, ((IdentNode)rhs).getName()).accept(this)); //null
|
||||
args.add(LiteralNode.newInstance(rhs, ((IdentNode)rhs).getName())); //null
|
||||
} else {
|
||||
args.add(rhs);
|
||||
args.add((Expression)LiteralNode.newInstance(unaryNode).accept(this)); //null, do not reuse token of identifier rhs, it can be e.g. 'this'
|
||||
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);
|
||||
|
||||
@ -939,7 +918,7 @@ final class AssignSymbols extends NodeVisitor<LexicalContext> implements Loggabl
|
||||
}
|
||||
|
||||
private FunctionNode markProgramBlock(final FunctionNode functionNode) {
|
||||
if (compiler.isOnDemandCompilation() || !functionNode.isProgram()) {
|
||||
if (isOnDemand || !functionNode.isProgram()) {
|
||||
return functionNode;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -48,11 +48,13 @@ import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import jdk.nashorn.internal.AssertsEnabled;
|
||||
import jdk.nashorn.internal.codegen.Compiler.CompilationPhases;
|
||||
import jdk.nashorn.internal.ir.Block;
|
||||
import jdk.nashorn.internal.ir.FunctionNode;
|
||||
import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
|
||||
import jdk.nashorn.internal.ir.LexicalContext;
|
||||
import jdk.nashorn.internal.ir.LiteralNode;
|
||||
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.PrintVisitor;
|
||||
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
@ -189,7 +191,7 @@ enum CompilationPhase {
|
||||
}
|
||||
},
|
||||
|
||||
SERIALIZE_SPLIT_PHASE(
|
||||
CACHE_AST(
|
||||
EnumSet.of(
|
||||
INITIALIZED,
|
||||
PARSED,
|
||||
@ -199,20 +201,21 @@ enum CompilationPhase {
|
||||
SPLIT)) {
|
||||
@Override
|
||||
FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
|
||||
return transformFunction(fn, new NodeVisitor<LexicalContext>(new LexicalContext()) {
|
||||
@Override
|
||||
public boolean enterFunctionNode(final FunctionNode functionNode) {
|
||||
if (functionNode.isSplit()) {
|
||||
compiler.serializeAst(functionNode);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
if (!compiler.isOnDemandCompilation()) {
|
||||
// Only do this on initial preprocessing of the source code. For on-demand compilations from
|
||||
// source, FindScopeDepths#leaveFunctionNode() calls data.setCachedAst() for the sole function
|
||||
// being compiled.
|
||||
transformFunction(fn, new CacheAst(compiler));
|
||||
}
|
||||
// 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
|
||||
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(
|
||||
EnumSet.of(
|
||||
INITIALIZED,
|
||||
@ -382,7 +430,7 @@ enum CompilationPhase {
|
||||
}
|
||||
},
|
||||
|
||||
REINITIALIZE_SERIALIZED(
|
||||
REINITIALIZE_CACHED(
|
||||
EnumSet.of(
|
||||
INITIALIZED,
|
||||
PARSED,
|
||||
@ -430,7 +478,7 @@ enum CompilationPhase {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "'Deserialize'";
|
||||
return "'Reinitialize cached'";
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -160,42 +160,41 @@ public final class Compiler implements Loggable {
|
||||
*/
|
||||
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
|
||||
*/
|
||||
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",
|
||||
CompilationPhase.CONSTANT_FOLDING_PHASE,
|
||||
CompilationPhase.LOWERING_PHASE,
|
||||
CompilationPhase.TRANSFORM_BUILTINS_PHASE,
|
||||
CompilationPhase.SPLITTING_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.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.LOCAL_VARIABLE_TYPE_CALCULATION_PHASE
|
||||
);
|
||||
|
||||
/**
|
||||
* Singleton that describes additional steps to be taken after deserializing, all the way up to (but not
|
||||
* including) generating and installing code.
|
||||
* Singleton that describes additional steps to be taken after retrieving a cached function, all the
|
||||
* way up to (but not including) generating and installing code.
|
||||
*/
|
||||
public final static CompilationPhases RECOMPILE_SERIALIZED_UPTO_BYTECODE = new CompilationPhases(
|
||||
"Recompile serialized function up to bytecode",
|
||||
CompilationPhase.REINITIALIZE_SERIALIZED,
|
||||
COMPILE_SERIALIZABLE_UPTO_BYTECODE
|
||||
public final static CompilationPhases RECOMPILE_CACHED_UPTO_BYTECODE = new CompilationPhases(
|
||||
"Recompile cached function up to bytecode",
|
||||
CompilationPhase.REINITIALIZE_CACHED,
|
||||
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 */
|
||||
public final static CompilationPhases COMPILE_UPTO_BYTECODE = new CompilationPhases(
|
||||
"Compile upto bytecode",
|
||||
COMPILE_UPTO_SERIALIZABLE,
|
||||
COMPILE_SERIALIZABLE_UPTO_BYTECODE);
|
||||
COMPILE_UPTO_CACHED,
|
||||
COMPILE_CACHED_UPTO_BYTECODE);
|
||||
|
||||
/** 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(
|
||||
@ -227,9 +226,9 @@ public final class Compiler implements Loggable {
|
||||
GENERATE_BYTECODE_AND_INSTALL);
|
||||
|
||||
/** 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",
|
||||
RECOMPILE_SERIALIZED_UPTO_BYTECODE,
|
||||
RECOMPILE_CACHED_UPTO_BYTECODE,
|
||||
GENERATE_BYTECODE_AND_INSTALL);
|
||||
|
||||
/**
|
||||
@ -248,9 +247,9 @@ public final class Compiler implements Loggable {
|
||||
GENERATE_BYTECODE_AND_INSTALL_RESTOF);
|
||||
|
||||
/** 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",
|
||||
RECOMPILE_SERIALIZED_UPTO_BYTECODE,
|
||||
RECOMPILE_CACHED_UPTO_BYTECODE,
|
||||
GENERATE_BYTECODE_AND_INSTALL_RESTOF);
|
||||
|
||||
private final List<CompilationPhase> phases;
|
||||
@ -313,7 +312,7 @@ public final class Compiler implements Loggable {
|
||||
}
|
||||
|
||||
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() {
|
||||
@ -766,14 +765,6 @@ public final class Compiler implements Loggable {
|
||||
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) {
|
||||
for (final CompileUnit unit : compileUnits) {
|
||||
if (unit.canHold(weight)) {
|
||||
|
@ -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.");
|
||||
newFunctionNode = newFunctionNode.setInDynamicContext(lc);
|
||||
}
|
||||
if (newFunctionNode == lc.getOutermostFunction() && !newFunctionNode.hasApplyToCallSpecialization()) {
|
||||
data.setCachedAst(newFunctionNode);
|
||||
}
|
||||
return newFunctionNode;
|
||||
}
|
||||
|
||||
@ -208,8 +211,7 @@ final class FindScopeDepths extends NodeVisitor<LexicalContext> implements Logga
|
||||
ObjectClassGenerator.createAllocationStrategy(newFunctionNode.getThisProperties(), compiler.getContext().useDualFields()),
|
||||
nestedFunctions,
|
||||
externalSymbolDepths.get(fnId),
|
||||
internalSymbols.get(fnId),
|
||||
compiler.removeSerializedAst(fnId));
|
||||
internalSymbols.get(fnId));
|
||||
|
||||
if (lc.getOutermostFunction() != newFunctionNode) {
|
||||
final FunctionNode parentFn = lc.getParentFunction(newFunctionNode);
|
||||
|
@ -497,7 +497,7 @@ public final class Label implements Serializable {
|
||||
private transient Label.Stack stack;
|
||||
|
||||
/** 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 */
|
||||
private final int id;
|
||||
|
@ -27,21 +27,18 @@ package jdk.nashorn.internal.codegen;
|
||||
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.NoSuchElementException;
|
||||
import jdk.nashorn.internal.codegen.types.Type;
|
||||
import jdk.nashorn.internal.ir.FunctionNode;
|
||||
import jdk.nashorn.internal.runtime.ScriptFunction;
|
||||
|
||||
/**
|
||||
* A data structure that maps one or several function nodes (by their unique id:s, not by
|
||||
* the FunctionNode object itself, due to copy on write changing it several times through
|
||||
* code generation.
|
||||
* A tuple containing function id, parameter types, return type and needsCallee flag.
|
||||
*/
|
||||
public class TypeMap {
|
||||
private final Map<Integer, Type[]> paramTypeMap = new HashMap<>();
|
||||
private final Map<Integer, Type> returnTypeMap = new HashMap<>();
|
||||
public final class TypeMap {
|
||||
private final int functionNodeId;
|
||||
private final Type[] paramTypes;
|
||||
private final Type returnType;
|
||||
private final boolean needsCallee;
|
||||
|
||||
/**
|
||||
@ -56,9 +53,10 @@ public class TypeMap {
|
||||
for (final Class<?> p : type.parameterArray()) {
|
||||
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;
|
||||
}
|
||||
|
||||
@ -69,20 +67,14 @@ public class TypeMap {
|
||||
* @throws NoSuchElementException if the type map has no mapping for the requested function
|
||||
*/
|
||||
public Type[] getParameterTypes(final int functionNodeId) {
|
||||
final Type[] paramTypes = paramTypeMap.get(functionNodeId);
|
||||
if (paramTypes == null) {
|
||||
throw new NoSuchElementException(Integer.toString(functionNodeId));
|
||||
}
|
||||
assert this.functionNodeId == functionNodeId;
|
||||
return paramTypes.clone();
|
||||
}
|
||||
|
||||
MethodType getCallSiteType(final FunctionNode functionNode) {
|
||||
final Type[] types = paramTypeMap.get(functionNode.getId());
|
||||
if (types == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
MethodType mt = MethodType.methodType(returnTypeMap.get(functionNode.getId()).getTypeClass());
|
||||
assert this.functionNodeId == functionNode.getId();
|
||||
final Type[] types = paramTypes;
|
||||
MethodType mt = MethodType.methodType(returnType.getTypeClass());
|
||||
if (needsCallee) {
|
||||
mt = mt.appendParameterTypes(ScriptFunction.class);
|
||||
}
|
||||
@ -116,7 +108,8 @@ public class TypeMap {
|
||||
* @return parameter type for this callsite if known
|
||||
*/
|
||||
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;
|
||||
if (types != null && pos < types.length) {
|
||||
return types[pos];
|
||||
@ -124,13 +117,6 @@ public class TypeMap {
|
||||
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
|
||||
public String toString() {
|
||||
return toString("");
|
||||
@ -139,27 +125,16 @@ public class TypeMap {
|
||||
String toString(final String prefix) {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
|
||||
if (paramTypeMap.isEmpty()) {
|
||||
sb.append(prefix).append("\t<empty>");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
for (final Map.Entry<Integer, Type[]> entry : paramTypeMap.entrySet()) {
|
||||
final int id = entry.getKey();
|
||||
sb.append(prefix).append('\t');
|
||||
sb.append("function ").append(id).append('\n');
|
||||
sb.append(prefix).append("\t\tparamTypes=");
|
||||
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');
|
||||
}
|
||||
final int id = functionNodeId;
|
||||
sb.append(prefix).append('\t');
|
||||
sb.append("function ").append(id).append('\n');
|
||||
sb.append(prefix).append("\t\tparamTypes=");
|
||||
sb.append(Arrays.toString(paramTypes));
|
||||
sb.append('\n');
|
||||
sb.append(prefix).append("\t\treturnType=");
|
||||
final Type ret = returnType;
|
||||
sb.append(ret == null ? "N/A" : ret);
|
||||
sb.append('\n');
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
@ -159,11 +159,42 @@ public class Block extends Node implements BreakableNode, Terminal, Flags<Block>
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the symbols in the block.
|
||||
* TODO: make this immutable.
|
||||
* Returns true if this block defines any symbols.
|
||||
* @return true if this block defines any symbols.
|
||||
*/
|
||||
public void clearSymbols() {
|
||||
symbols.clear();
|
||||
public boolean hasSymbols() {
|
||||
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
|
||||
@ -191,7 +222,7 @@ public class Block extends Node implements BreakableNode, Terminal, Flags<Block>
|
||||
* @return symbol iterator
|
||||
*/
|
||||
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
|
||||
*
|
||||
* @param lc get lexical context
|
||||
* @param symbol symbol
|
||||
*/
|
||||
public void putSymbol(final LexicalContext lc, final Symbol symbol) {
|
||||
public void putSymbol(final Symbol symbol) {
|
||||
symbols.put(symbol.getName(), symbol);
|
||||
}
|
||||
|
||||
|
@ -43,7 +43,7 @@ public final class ForNode extends LoopNode {
|
||||
private final JoinPredecessorExpression modify;
|
||||
|
||||
/** Iterator symbol. */
|
||||
private Symbol iterator;
|
||||
private final Symbol iterator;
|
||||
|
||||
/** Is this a normal for in loop? */
|
||||
public static final int IS_FOR_IN = 1 << 0;
|
||||
@ -86,23 +86,23 @@ public final class ForNode extends LoopNode {
|
||||
this.flags = flags;
|
||||
this.init = init;
|
||||
this.modify = modify;
|
||||
this.iterator = null;
|
||||
|
||||
}
|
||||
|
||||
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);
|
||||
this.init = init;
|
||||
this.modify = modify;
|
||||
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
|
||||
// is executed.
|
||||
this.iterator = forNode.iterator;
|
||||
this.iterator = iterator;
|
||||
}
|
||||
|
||||
@Override
|
||||
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
|
||||
@ -175,7 +175,7 @@ public final class ForNode extends LoopNode {
|
||||
if (this.init == init) {
|
||||
return this;
|
||||
}
|
||||
return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, 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
|
||||
* @param lc the current lexical context
|
||||
* @param iterator the iterator symbol
|
||||
* @return a ForNode with the iterator set
|
||||
*/
|
||||
public void setIterator(final Symbol iterator) {
|
||||
this.iterator = iterator;
|
||||
public ForNode setIterator(final LexicalContext lc, final Symbol 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) {
|
||||
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
|
||||
@ -236,7 +241,7 @@ public final class ForNode extends LoopNode {
|
||||
if (this.test == test) {
|
||||
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
|
||||
@ -249,7 +254,7 @@ public final class ForNode extends LoopNode {
|
||||
if (this.body == body) {
|
||||
return this;
|
||||
}
|
||||
return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion));
|
||||
return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion, iterator));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -257,12 +262,12 @@ public final class ForNode extends LoopNode {
|
||||
if (this.controlFlowEscapes == controlFlowEscapes) {
|
||||
return this;
|
||||
}
|
||||
return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion));
|
||||
return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion, iterator));
|
||||
}
|
||||
|
||||
@Override
|
||||
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
|
||||
|
@ -264,6 +264,11 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
||||
*/
|
||||
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 */
|
||||
public static final int EXTENSION_CALLSITE_FLAGS = IS_PRINT_PARSE |
|
||||
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 int thisProperties,
|
||||
final Class<?> rootClass,
|
||||
final Source source, Namespace namespace) {
|
||||
final Source source, final Namespace namespace) {
|
||||
super(functionNode);
|
||||
|
||||
this.endParserState = endParserState;
|
||||
@ -757,7 +762,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
||||
*/
|
||||
public boolean needsCallee() {
|
||||
// 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 this function has transformed apply to call
|
||||
*/
|
||||
public boolean hasOptimisticApplyToCall() {
|
||||
public boolean hasApplyToCallSpecialization() {
|
||||
return getFlag(HAS_APPLY_TO_CALL_SPECIALIZATION);
|
||||
}
|
||||
|
||||
@ -1025,6 +1030,14 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
||||
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.
|
||||
* @param index the parameter's position.
|
||||
@ -1161,6 +1174,24 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
||||
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
|
||||
* @see Compiler
|
||||
|
@ -53,7 +53,7 @@ public final class SwitchNode extends BreakableStatement {
|
||||
private final boolean uniqueInteger;
|
||||
|
||||
/** Tag symbol. */
|
||||
private Symbol tag;
|
||||
private final Symbol tag;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
@ -71,15 +71,16 @@ public final class SwitchNode extends BreakableStatement {
|
||||
this.cases = cases;
|
||||
this.defaultCaseIndex = defaultCase == null ? -1 : cases.indexOf(defaultCase);
|
||||
this.uniqueInteger = false;
|
||||
this.tag = null;
|
||||
}
|
||||
|
||||
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);
|
||||
this.expression = expression;
|
||||
this.cases = cases;
|
||||
this.defaultCaseIndex = defaultCaseIndex;
|
||||
this.tag = switchNode.getTag(); //TODO are symbols inherited as references?
|
||||
this.tag = tag;
|
||||
this.uniqueInteger = uniqueInteger;
|
||||
}
|
||||
|
||||
@ -89,7 +90,7 @@ public final class SwitchNode extends BreakableStatement {
|
||||
for (final CaseNode caseNode : cases) {
|
||||
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
|
||||
@ -157,7 +158,7 @@ public final class SwitchNode extends BreakableStatement {
|
||||
if (this.cases == cases) {
|
||||
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) {
|
||||
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
|
||||
* the switch expression result is stored
|
||||
* @param lc lexical context
|
||||
* @param tag a symbol
|
||||
* @return a switch node with the symbol set
|
||||
*/
|
||||
public void setTag(final Symbol tag) {
|
||||
this.tag = tag;
|
||||
public SwitchNode setTag(final LexicalContext lc, final Symbol 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) {
|
||||
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
|
||||
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));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -25,7 +25,10 @@
|
||||
|
||||
package jdk.nashorn.internal.ir;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.Serializable;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.StringTokenizer;
|
||||
@ -47,7 +50,9 @@ import jdk.nashorn.internal.runtime.options.Options;
|
||||
* 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 */
|
||||
public static final int IS_GLOBAL = 1;
|
||||
/** 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
|
||||
* 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. */
|
||||
private int fieldIndex = -1;
|
||||
private transient int fieldIndex = -1;
|
||||
|
||||
/** Number of times this symbol is used in code */
|
||||
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) {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
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()}
|
||||
* @return the symbol
|
||||
*/
|
||||
public Symbol setIsScope() {
|
||||
public Symbol setIsScope() {
|
||||
if (!isScope()) {
|
||||
if(shouldTrace()) {
|
||||
trace("SET IS SCOPE");
|
||||
@ -609,11 +623,11 @@ public final class Symbol implements Comparable<Symbol> {
|
||||
|
||||
/**
|
||||
* Increase the symbol's use count by one.
|
||||
* @return the symbol
|
||||
*/
|
||||
public Symbol increaseUseCount() {
|
||||
useCount++;
|
||||
return this;
|
||||
public void increaseUseCount() {
|
||||
if (isScope()) { // Avoid dirtying a cache line; we only need the use count for scoped symbols
|
||||
useCount++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -669,4 +683,10 @@ public final class Symbol implements Comparable<Symbol> {
|
||||
new Throwable().printStackTrace(Context.getCurrentErr());
|
||||
}
|
||||
}
|
||||
|
||||
private void readObject(final ObjectInputStream in) throws ClassNotFoundException, IOException {
|
||||
in.defaultReadObject();
|
||||
firstSlot = -1;
|
||||
fieldIndex = -1;
|
||||
}
|
||||
}
|
||||
|
@ -65,7 +65,7 @@ public final class TryNode extends LexicalContextStatement implements JoinPredec
|
||||
private final List<Block> inlinedFinallies;
|
||||
|
||||
/** Exception symbol. */
|
||||
private Symbol exception;
|
||||
private final Symbol exception;
|
||||
|
||||
private final LocalVariableConversion conversion;
|
||||
|
||||
@ -86,22 +86,23 @@ public final class TryNode extends LexicalContextStatement implements JoinPredec
|
||||
this.finallyBody = finallyBody;
|
||||
this.conversion = null;
|
||||
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);
|
||||
this.body = body;
|
||||
this.catchBlocks = catchBlocks;
|
||||
this.finallyBody = finallyBody;
|
||||
this.conversion = conversion;
|
||||
this.inlinedFinallies = inlinedFinallies;
|
||||
this.exception = tryNode.exception;
|
||||
this.exception = exception;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node ensureUniqueLabels(final LexicalContext lc) {
|
||||
//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
|
||||
@ -160,7 +161,7 @@ public final class TryNode extends LexicalContextStatement implements JoinPredec
|
||||
if (this.body == body) {
|
||||
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) {
|
||||
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
|
||||
* @param lc lexical context
|
||||
* @param exception a symbol for the compiler to store the exception in
|
||||
* @return new TryNode or same if unchanged
|
||||
*/
|
||||
public TryNode setException(final Symbol exception) {
|
||||
this.exception = exception;
|
||||
return this;
|
||||
public TryNode setException(final LexicalContext lc, final Symbol exception) {
|
||||
if (this.exception == exception) {
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
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) {
|
||||
@ -314,7 +318,7 @@ public final class TryNode extends LexicalContextStatement implements JoinPredec
|
||||
if(this.conversion == conversion) {
|
||||
return this;
|
||||
}
|
||||
return new TryNode(this, body, catchBlocks, finallyBody, conversion, inlinedFinallies);
|
||||
return new TryNode(this, body, catchBlocks, finallyBody, conversion, inlinedFinallies, exception);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -2419,7 +2419,7 @@ public final class Global extends Scope {
|
||||
}
|
||||
|
||||
private void initScripting(final ScriptEnvironment scriptEnv) {
|
||||
Object value;
|
||||
ScriptObject value;
|
||||
value = ScriptFunctionImpl.makeFunction("readLine", ScriptingFunctions.READLINE);
|
||||
addOwnProperty("readLine", Attribute.NOT_ENUMERABLE, value);
|
||||
|
||||
@ -2428,11 +2428,13 @@ public final class Global extends Scope {
|
||||
|
||||
final String execName = ScriptingFunctions.EXEC_NAME;
|
||||
value = ScriptFunctionImpl.makeFunction(execName, ScriptingFunctions.EXEC);
|
||||
value.addOwnProperty(ScriptingFunctions.THROW_ON_ERROR_NAME, Attribute.NOT_ENUMERABLE, false);
|
||||
|
||||
addOwnProperty(execName, Attribute.NOT_ENUMERABLE, value);
|
||||
|
||||
// Nashorn extension: global.echo (scripting-mode-only)
|
||||
// alias for "print"
|
||||
value = get("print");
|
||||
value = (ScriptObject)get("print");
|
||||
addOwnProperty("echo", Attribute.NOT_ENUMERABLE, value);
|
||||
|
||||
// Nashorn extension: global.$OPTIONS (scripting-mode-only)
|
||||
|
@ -26,6 +26,7 @@
|
||||
package jdk.nashorn.internal.parser;
|
||||
|
||||
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.DECIMAL;
|
||||
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.LPAREN;
|
||||
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.REGEX;
|
||||
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 java.io.Serializable;
|
||||
|
||||
import jdk.nashorn.internal.runtime.ECMAErrors;
|
||||
import jdk.nashorn.internal.runtime.ErrorManager;
|
||||
import jdk.nashorn.internal.runtime.JSErrorType;
|
||||
@ -75,6 +78,9 @@ public class Lexer extends Scanner {
|
||||
/** True if here and edit strings are supported. */
|
||||
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.) */
|
||||
private final boolean nested;
|
||||
|
||||
@ -173,7 +179,7 @@ public class Lexer extends Scanner {
|
||||
* @param stream the token stream to lex
|
||||
*/
|
||||
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 stream the token stream to lex
|
||||
* @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) {
|
||||
this(source, 0, source.getLength(), stream, scripting, false);
|
||||
public Lexer(final Source source, final TokenStream stream, final boolean scripting, final boolean es6) {
|
||||
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 stream token stream to lex
|
||||
* @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
|
||||
* function body. This is used with the feature where the parser is skipping nested function bodies to
|
||||
* avoid reading ahead unnecessarily when we skip the function bodies.
|
||||
*/
|
||||
|
||||
public Lexer(final Source source, final int start, final int len, final TokenStream stream, final boolean scripting, 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);
|
||||
this.source = source;
|
||||
this.stream = stream;
|
||||
this.scripting = scripting;
|
||||
this.es6 = es6;
|
||||
this.nested = false;
|
||||
this.pendingLine = 1;
|
||||
this.last = EOL;
|
||||
@ -218,6 +227,7 @@ public class Lexer extends Scanner {
|
||||
source = lexer.source;
|
||||
stream = lexer.stream;
|
||||
scripting = lexer.scripting;
|
||||
es6 = lexer.es6;
|
||||
nested = true;
|
||||
|
||||
pendingLine = state.pendingLine;
|
||||
@ -1088,6 +1098,24 @@ public class Lexer extends Scanner {
|
||||
}
|
||||
|
||||
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 {
|
||||
// Check for possible octal constant.
|
||||
boolean octal = digit == 0;
|
||||
@ -1105,7 +1133,7 @@ public class Lexer extends Scanner {
|
||||
}
|
||||
|
||||
if (octal && position - start > 1) {
|
||||
type = OCTAL;
|
||||
type = OCTAL_LEGACY;
|
||||
} else if (ch0 == '.' || ch0 == 'E' || ch0 == 'e') {
|
||||
// Must be a double.
|
||||
if (ch0 == '.') {
|
||||
@ -1637,10 +1665,14 @@ public class Lexer extends Scanner {
|
||||
switch (Token.descType(token)) {
|
||||
case DECIMAL:
|
||||
return Lexer.valueOf(source.getString(start, len), 10); // number
|
||||
case OCTAL:
|
||||
return Lexer.valueOf(source.getString(start, len), 8); // number
|
||||
case HEXADECIMAL:
|
||||
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:
|
||||
final String str = source.getString(start, len);
|
||||
final double value = Double.valueOf(str);
|
||||
|
@ -273,7 +273,7 @@ public class Parser extends AbstractParser implements Loggable {
|
||||
|
||||
try {
|
||||
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;
|
||||
line = lineOffset;
|
||||
|
||||
@ -309,7 +309,7 @@ public class Parser extends AbstractParser implements Loggable {
|
||||
public List<IdentNode> parseFormalParameterList() {
|
||||
try {
|
||||
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.)
|
||||
k = -1;
|
||||
@ -333,7 +333,7 @@ public class Parser extends AbstractParser implements Loggable {
|
||||
public FunctionNode parseFunctionBody() {
|
||||
try {
|
||||
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;
|
||||
|
||||
// Set up first token (skips opening EOL.)
|
||||
@ -716,7 +716,7 @@ loop:
|
||||
|
||||
restoreBlock(body);
|
||||
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);
|
||||
script.setLastToken(token);
|
||||
|
||||
@ -1216,11 +1216,10 @@ loop:
|
||||
final long forToken = token;
|
||||
final int forLine = line;
|
||||
// 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).
|
||||
final int forStart = Token.descPosition(forToken);
|
||||
// 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;
|
||||
|
||||
// Create FOR node, capturing FOR token.
|
||||
@ -1341,22 +1340,24 @@ loop:
|
||||
body = getStatement();
|
||||
} finally {
|
||||
lc.pop(forNode);
|
||||
}
|
||||
|
||||
if (vars != null) {
|
||||
for (final VarNode var : vars) {
|
||||
appendStatement(var);
|
||||
if (vars != null) {
|
||||
for (final VarNode var : vars) {
|
||||
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);
|
||||
return ident;
|
||||
case OCTAL:
|
||||
case OCTAL_LEGACY:
|
||||
if (isStrictMode) {
|
||||
throw error(AbstractParser.message("strict.no.octal"), token);
|
||||
}
|
||||
@ -1962,6 +1963,8 @@ loop:
|
||||
case ESCSTRING:
|
||||
case DECIMAL:
|
||||
case HEXADECIMAL:
|
||||
case OCTAL:
|
||||
case BINARY_NUMBER:
|
||||
case FLOATING:
|
||||
case REGEX:
|
||||
case XML:
|
||||
@ -2053,13 +2056,13 @@ loop:
|
||||
// LBRACKET tested in caller.
|
||||
next();
|
||||
|
||||
// Prepare to accummulating elements.
|
||||
// Prepare to accumulate elements.
|
||||
final List<Expression> elements = new ArrayList<>();
|
||||
// Track elisions.
|
||||
boolean elision = true;
|
||||
loop:
|
||||
while (true) {
|
||||
switch (type) {
|
||||
switch (type) {
|
||||
case RBRACKET:
|
||||
next();
|
||||
|
||||
@ -2223,7 +2226,7 @@ loop:
|
||||
switch (type) {
|
||||
case IDENT:
|
||||
return getIdent().setIsPropertyName();
|
||||
case OCTAL:
|
||||
case OCTAL_LEGACY:
|
||||
if (isStrictMode) {
|
||||
throw error(AbstractParser.message("strict.no.octal"), token);
|
||||
}
|
||||
@ -2231,6 +2234,8 @@ loop:
|
||||
case ESCSTRING:
|
||||
case DECIMAL:
|
||||
case HEXADECIMAL:
|
||||
case OCTAL:
|
||||
case BINARY_NUMBER:
|
||||
case FLOATING:
|
||||
return getLiteral();
|
||||
default:
|
||||
@ -2284,7 +2289,7 @@ loop:
|
||||
}
|
||||
}
|
||||
|
||||
propertyName = createIdentNode(propertyToken, finish, ident).setIsPropertyName();
|
||||
propertyName = createIdentNode(propertyToken, finish, ident).setIsPropertyName();
|
||||
} else {
|
||||
propertyName = propertyName();
|
||||
}
|
||||
@ -2553,7 +2558,7 @@ loop:
|
||||
final long callToken = token;
|
||||
|
||||
switch (type) {
|
||||
case LBRACKET:
|
||||
case LBRACKET: {
|
||||
next();
|
||||
|
||||
// Get array index.
|
||||
@ -2565,8 +2570,8 @@ loop:
|
||||
lhs = new IndexNode(callToken, finish, lhs, index);
|
||||
|
||||
break;
|
||||
|
||||
case PERIOD:
|
||||
}
|
||||
case PERIOD: {
|
||||
if (lhs == null) {
|
||||
throw error(AbstractParser.message("expected.operand", type.getNameOrType()));
|
||||
}
|
||||
@ -2579,7 +2584,7 @@ loop:
|
||||
lhs = new AccessNode(callToken, finish, lhs, property.getName());
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
default:
|
||||
break loop;
|
||||
}
|
||||
@ -3034,7 +3039,7 @@ loop:
|
||||
assert parserState != null;
|
||||
|
||||
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;
|
||||
linePosition = parserState.linePosition;
|
||||
// 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;
|
||||
}
|
||||
|
||||
Lexer createLexer(final Source source, final Lexer lexer, final TokenStream stream, final boolean scripting) {
|
||||
final Lexer newLexer = new Lexer(source, position, lexer.limit - position, stream, scripting, true);
|
||||
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, es6, true);
|
||||
newLexer.restoreState(new Lexer.State(position, Integer.MAX_VALUE, line, -1, linePosition, SEMICOLON));
|
||||
return newLexer;
|
||||
}
|
||||
@ -3107,15 +3112,6 @@ loop:
|
||||
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 :
|
||||
* LeftHandSideExpression
|
||||
@ -3127,7 +3123,7 @@ loop:
|
||||
* UnaryExpression :
|
||||
* PostfixExpression
|
||||
* delete UnaryExpression
|
||||
* Node UnaryExpression
|
||||
* void UnaryExpression
|
||||
* typeof UnaryExpression
|
||||
* ++ UnaryExpression
|
||||
* -- UnaryExpression
|
||||
@ -3333,7 +3329,6 @@ loop:
|
||||
// This method is protected so that subclass can get details
|
||||
// at expression start point!
|
||||
|
||||
// TODO - Destructuring array.
|
||||
// Include commas in expression parsing.
|
||||
return expression(unaryExpression(), COMMARIGHT.getPrecedence(), false);
|
||||
}
|
||||
@ -3407,7 +3402,6 @@ loop:
|
||||
// This method is protected so that subclass can get details
|
||||
// at assignment expression start point!
|
||||
|
||||
// TODO - Handle decompose.
|
||||
// Exclude commas in expression parsing.
|
||||
return expression(unaryExpression(), ASSIGN.getPrecedence(), noIn);
|
||||
}
|
||||
|
@ -170,8 +170,10 @@ public enum TokenType {
|
||||
YIELD (FUTURESTRICT, "yield"),
|
||||
|
||||
DECIMAL (LITERAL, null),
|
||||
OCTAL (LITERAL, null),
|
||||
HEXADECIMAL (LITERAL, null),
|
||||
OCTAL_LEGACY (LITERAL, null),
|
||||
OCTAL (LITERAL, null),
|
||||
BINARY_NUMBER (LITERAL, null),
|
||||
FLOATING (LITERAL, null),
|
||||
STRING (LITERAL, null),
|
||||
ESCSTRING (LITERAL, null),
|
||||
|
@ -22,20 +22,14 @@
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package jdk.nashorn.internal.codegen;
|
||||
package jdk.nashorn.internal.runtime;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.util.Collections;
|
||||
import java.util.zip.Deflater;
|
||||
import java.util.zip.DeflaterOutputStream;
|
||||
import jdk.nashorn.internal.ir.Block;
|
||||
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;
|
||||
|
||||
/**
|
||||
@ -50,7 +44,7 @@ final class AstSerializer {
|
||||
final ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
final Deflater deflater = new Deflater(COMPRESSION_LEVEL);
|
||||
try (final ObjectOutputStream oout = new ObjectOutputStream(new DeflaterOutputStream(out, deflater))) {
|
||||
oout.writeObject(removeInnerFunctionBodies(fn));
|
||||
oout.writeObject(fn);
|
||||
} catch (final IOException e) {
|
||||
throw new AssertionError("Unexpected exception serializing function", e);
|
||||
} finally {
|
||||
@ -58,16 +52,4 @@ final class AstSerializer {
|
||||
}
|
||||
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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -27,6 +27,7 @@ package jdk.nashorn.internal.runtime;
|
||||
import static jdk.nashorn.internal.lookup.Lookup.MH;
|
||||
import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
|
||||
import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.isValid;
|
||||
|
||||
import java.lang.invoke.CallSite;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
@ -820,7 +821,7 @@ final class CompiledFunction {
|
||||
// isn't available, we'll use the old one bound into the call site.
|
||||
final OptimismInfo effectiveOptInfo = currentOptInfo != null ? currentOptInfo : oldOptInfo;
|
||||
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
|
||||
|
||||
if (!shouldRecompile) {
|
||||
@ -828,11 +829,11 @@ final class CompiledFunction {
|
||||
// recompiled a deoptimized version for an inner invocation.
|
||||
// We still need to do the rest of from the beginning
|
||||
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);
|
||||
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");
|
||||
|
||||
// compile the rest of the function, and install it
|
||||
@ -956,10 +957,6 @@ final class CompiledFunction {
|
||||
FunctionNode reparse() {
|
||||
return data.reparse();
|
||||
}
|
||||
|
||||
boolean isSerialized() {
|
||||
return data.isSerialized();
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
|
@ -26,16 +26,24 @@
|
||||
package jdk.nashorn.internal.runtime;
|
||||
|
||||
import static jdk.nashorn.internal.lookup.Lookup.MH;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.ref.Reference;
|
||||
import java.lang.ref.SoftReference;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
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.nashorn.internal.codegen.Compiler;
|
||||
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.TypeMap;
|
||||
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.IdentNode;
|
||||
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.objects.Global;
|
||||
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.Loggable;
|
||||
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,
|
||||
* 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 */
|
||||
public static final String RECOMPILATION_PREFIX = "Recompilation$";
|
||||
|
||||
private static final ExecutorService astSerializerExecutorService = createAstSerializerExecutorService();
|
||||
|
||||
/** Unique function node id for this function node */
|
||||
private final int functionNodeId;
|
||||
|
||||
@ -77,8 +95,12 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
|
||||
/** Source from which FunctionNode was parsed. */
|
||||
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. */
|
||||
private final long token;
|
||||
@ -128,7 +150,6 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
|
||||
* @param nestedFunctions nested function map
|
||||
* @param externalScopeDepths external scope depths
|
||||
* @param internalSymbols internal symbols to method, defined in its scope
|
||||
* @param serializedAst a serialized AST representation. Normally only used for split functions.
|
||||
*/
|
||||
public RecompilableScriptFunctionData(
|
||||
final FunctionNode functionNode,
|
||||
@ -136,8 +157,7 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
|
||||
final AllocationStrategy allocationStrategy,
|
||||
final Map<Integer, RecompilableScriptFunctionData> nestedFunctions,
|
||||
final Map<String, Integer> externalScopeDepths,
|
||||
final Set<String> internalSymbols,
|
||||
final byte[] serializedAst) {
|
||||
final Set<String> internalSymbols) {
|
||||
|
||||
super(functionName(functionNode),
|
||||
Math.min(functionNode.getParameters().size(), MAX_ARITY),
|
||||
@ -161,7 +181,6 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
|
||||
nfn.setParent(this);
|
||||
}
|
||||
|
||||
this.serializedAst = serializedAst;
|
||||
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.
|
||||
*/
|
||||
public RecompilableScriptFunctionData getParent() {
|
||||
return parent;
|
||||
return parent;
|
||||
}
|
||||
|
||||
void setParent(final RecompilableScriptFunctionData parent) {
|
||||
@ -358,13 +377,11 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
|
||||
return allocationStrategy.allocate(map);
|
||||
}
|
||||
|
||||
boolean isSerialized() {
|
||||
return serializedAst != null;
|
||||
}
|
||||
|
||||
FunctionNode reparse() {
|
||||
if (isSerialized()) {
|
||||
return deserialize();
|
||||
final FunctionNode cachedFunction = getCachedAst();
|
||||
if (cachedFunction != null) {
|
||||
assert cachedFunction.isCached();
|
||||
return cachedFunction;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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 Timing timing = env._timing;
|
||||
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) {
|
||||
return (functionFlags & flag) != 0;
|
||||
}
|
||||
@ -512,9 +721,9 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
|
||||
final FunctionNode fn = reparse();
|
||||
final Compiler compiler = getCompiler(fn, actualCallSiteType, runtimeScope);
|
||||
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);
|
||||
}
|
||||
return new FunctionInitializer(compiledFn, compiler.getInvalidatedProgramPoints());
|
||||
|
@ -26,6 +26,7 @@
|
||||
package jdk.nashorn.internal.runtime;
|
||||
|
||||
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.ScriptRuntime.UNDEFINED;
|
||||
|
||||
@ -70,6 +71,9 @@ public final class ScriptingFunctions {
|
||||
/** EXIT name - special property used by $EXEC API. */
|
||||
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. */
|
||||
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 out;
|
||||
}
|
||||
|
@ -30,6 +30,7 @@ final class AdaptationException extends Exception {
|
||||
private final AdaptationResult adaptationResult;
|
||||
|
||||
AdaptationException(final AdaptationResult.Outcome outcome, final String classList) {
|
||||
super(null, null, false, false);
|
||||
this.adaptationResult = new AdaptationResult(outcome, classList);
|
||||
}
|
||||
|
||||
|
@ -189,7 +189,7 @@ public final class Bootstrap {
|
||||
* @return true if the obj is an instance of @FunctionalInterface interface
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -79,10 +79,10 @@ public class NashornBeansLinker implements GuardingDynamicLinker {
|
||||
}
|
||||
|
||||
// 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
|
||||
protected Method computeValue(final Class<?> type) {
|
||||
return findFunctionalInterfaceMethod(type);
|
||||
protected String computeValue(final Class<?> type) {
|
||||
return findFunctionalInterfaceMethodName(type);
|
||||
}
|
||||
};
|
||||
|
||||
@ -107,19 +107,21 @@ public class NashornBeansLinker implements GuardingDynamicLinker {
|
||||
// annotated interface. This way Java method, constructor references or
|
||||
// implementations of java.util.function.* interfaces can be called as though
|
||||
// those are script functions.
|
||||
final Method m = getFunctionalInterfaceMethod(self.getClass());
|
||||
if (m != null) {
|
||||
final String name = getFunctionalInterfaceMethodName(self.getClass());
|
||||
if (name != null) {
|
||||
final MethodType callType = desc.getMethodType();
|
||||
// 'callee' and 'thiz' passed from script + actual arguments
|
||||
if (callType.parameterCount() != m.getParameterCount() + 2) {
|
||||
throw typeError("no.method.matches.args", ScriptRuntime.safeToString(self));
|
||||
}
|
||||
return new GuardedInvocation(
|
||||
// drop 'thiz' passed from the script.
|
||||
MH.dropArguments(linkerServices.filterInternalObjects(desc.getLookup().unreflect(m)), 1,
|
||||
callType.parameterType(1)), Guards.getInstanceOfGuard(
|
||||
m.getDeclaringClass())).asTypeSafeReturn(
|
||||
new NashornBeansLinkerServices(linkerServices), callType);
|
||||
// drop callee (Undefined ScriptFunction) and change the request to be dyn:callMethod:<name>
|
||||
final NashornCallSiteDescriptor newDesc = NashornCallSiteDescriptor.get(desc.getLookup(),
|
||||
"dyn:callMethod:" + name, desc.getMethodType().dropParameterTypes(1, 2),
|
||||
NashornCallSiteDescriptor.getFlags(desc));
|
||||
final GuardedInvocation gi = getGuardedInvocation(beansLinker,
|
||||
linkRequest.replaceArguments(newDesc, linkRequest.getArguments()),
|
||||
new NashornBeansLinkerServices(linkerServices));
|
||||
|
||||
// drop 'thiz' passed from the script.
|
||||
return gi.replaceMethods(
|
||||
MH.dropArguments(linkerServices.filterInternalObjects(gi.getInvocation()), 1, callType.parameterType(1)),
|
||||
gi.getGuard());
|
||||
}
|
||||
}
|
||||
return getGuardedInvocation(beansLinker, linkRequest, linkerServices);
|
||||
@ -163,7 +165,7 @@ public class NashornBeansLinker implements GuardingDynamicLinker {
|
||||
return arg instanceof ConsString ? arg.toString() : arg;
|
||||
}
|
||||
|
||||
private static Method findFunctionalInterfaceMethod(final Class<?> clazz) {
|
||||
private static String findFunctionalInterfaceMethodName(final Class<?> clazz) {
|
||||
if (clazz == null) {
|
||||
return null;
|
||||
}
|
||||
@ -179,20 +181,20 @@ public class NashornBeansLinker implements GuardingDynamicLinker {
|
||||
// return the first abstract method
|
||||
for (final Method m : iface.getMethods()) {
|
||||
if (Modifier.isAbstract(m.getModifiers())) {
|
||||
return m;
|
||||
return m.getName();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// did not find here, try super class
|
||||
return findFunctionalInterfaceMethod(clazz.getSuperclass());
|
||||
return findFunctionalInterfaceMethodName(clazz.getSuperclass());
|
||||
}
|
||||
|
||||
// Returns @FunctionalInterface annotated interface's single abstract
|
||||
// method. If not found, returns null.
|
||||
static Method getFunctionalInterfaceMethod(final Class<?> clazz) {
|
||||
return FUNCTIONAL_IFACE_METHOD.get(clazz);
|
||||
// method name. If not found, returns null.
|
||||
static String getFunctionalInterfaceMethodName(final Class<?> clazz) {
|
||||
return FUNCTIONAL_IFACE_METHOD_NAME.get(clazz);
|
||||
}
|
||||
|
||||
static MethodHandleTransformer createHiddenObjectFilter() {
|
||||
|
@ -24,6 +24,6 @@ public class JOniException extends RuntimeException{
|
||||
private static final long serialVersionUID = -6027192180014164667L;
|
||||
|
||||
public JOniException(final String message) {
|
||||
super(message);
|
||||
super(message, null, false, false);
|
||||
}
|
||||
}
|
||||
|
@ -163,6 +163,7 @@ range.error.invalid.radix=radix argument must be in [2, 36]
|
||||
range.error.invalid.date=Invalid Date
|
||||
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.exec.returned.non.zero=$EXEC returned non-zero exit code: {0}
|
||||
|
||||
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
|
||||
|
@ -1,14 +1,28 @@
|
||||
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
|
||||
TypeError: No such Java class: java.lang.NonExistent
|
||||
TypeError: No such Java constructor: Object(String)
|
||||
TypeError: Java constructor signature invalid: Object()xxxxx
|
||||
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 java.io.PrintStream.println] cannot be used as a constructor.
|
||||
TypeError: Constructor [jdk.internal.dynalink.beans.SimpleDynamicMethod String java.lang.String.java.lang.String(char[],int,int)] requires "new".
|
||||
TypeError: Java method [jdk.internal.dynalink.beans.OverloadedDynamicMethod
|
||||
String java.lang.System.getProperty(String,String)
|
||||
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(int)
|
||||
java.lang.InstantiationException: java.io.InputStream
|
||||
|
@ -1,10 +1,10 @@
|
||||
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
|
||||
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
|
||||
java.lang.InstantiationException: java.io.InputStream
|
||||
|
49
nashorn/test/script/basic/JDK-8068901.js
Normal file
49
nashorn/test/script/basic/JDK-8068901.js
Normal 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); }
|
||||
|
10
nashorn/test/script/basic/JDK-8068901.js.EXPECTED
Normal file
10
nashorn/test/script/basic/JDK-8068901.js.EXPECTED
Normal 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
|
40
nashorn/test/script/basic/JDK-8068903.js
Normal file
40
nashorn/test/script/basic/JDK-8068903.js
Normal 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);
|
@ -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 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.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.awt.Color(int,int,int)] with the passed arguments; they do not match any of its method signatures.
|
||||
|
46
nashorn/test/script/basic/JDK-8134731.js
Normal file
46
nashorn/test/script/basic/JDK-8134731.js
Normal 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);
|
36
nashorn/test/script/basic/JDK-8134865.js
Normal file
36
nashorn/test/script/basic/JDK-8134865.js
Normal 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;
|
||||
}
|
43
nashorn/test/script/basic/JDK-8134939.js
Normal file
43
nashorn/test/script/basic/JDK-8134939.js
Normal 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" +
|
||||
"]");
|
49
nashorn/test/script/basic/es6/numeric-literals.js
Normal file
49
nashorn/test/script/basic/es6/numeric-literals.js
Normal 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);
|
||||
|
54
nashorn/test/script/trusted/JDK-8087292.js
Normal file
54
nashorn/test/script/trusted/JDK-8087292.js
Normal 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();
|
1
nashorn/test/script/trusted/JDK-8087292.js.EXPECTED
Normal file
1
nashorn/test/script/trusted/JDK-8087292.js.EXPECTED
Normal file
@ -0,0 +1 @@
|
||||
RangeError: $EXEC returned non-zero exit code: 1
|
@ -4,7 +4,18 @@ typeof java.util.Vector evalutes to function
|
||||
typeof java.util.Map 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}
|
||||
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)]
|
||||
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
|
||||
|
@ -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.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -23,44 +23,13 @@
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.nashorn.internal.runtime.arrays;
|
||||
package jdk.nashorn.test.models;
|
||||
|
||||
/**
|
||||
* Mechanism for communicating that something isn't a plain
|
||||
* 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
|
||||
* Simple function interface with a varargs SAM method.
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
class InvalidArrayIndexException extends Exception {
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface VarArgConsumer {
|
||||
public void apply(Object... o);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user