--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/bin/jjsdebug.sh Tue Jul 01 14:27:28 2014 -0700
@@ -0,0 +1,25 @@
+#!/bin/sh
+#
+# Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# This code is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License version 2 only, as
+# published by the Free Software Foundation.
+#
+# This code is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# version 2 for more details (a copy is included in the LICENSE file that
+# accompanied this code).
+#
+# You should have received a copy of the GNU General Public License version
+# 2 along with this work; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+# or visit www.oracle.com if you need additional information or have any
+# questions.
+#
+
+$JAVA_HOME/bin/jjs -J-Djava.ext.dirs=`dirname $0`/../dist -J-agentlib:jdwp=transport=dt_socket,address=localhost:9009,server=y,suspend=y $*
--- a/nashorn/make/build.xml Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/make/build.xml Tue Jul 01 14:27:28 2014 -0700
@@ -125,8 +125,7 @@
encoding="${javac.encoding}"
includeantruntime="false" fork="true">
<compilerarg value="-J-Djava.ext.dirs="/>
- <compilerarg value="-Xlint:unchecked"/>
- <compilerarg value="-Xlint:deprecation"/>
+ <compilerarg value="-Xlint:all"/>
<compilerarg value="-XDignore.symbol.file"/>
<compilerarg value="-Xdiags:verbose"/>
</javac>
--- a/nashorn/make/nbproject/ide-targets.xml Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/make/nbproject/ide-targets.xml Tue Jul 01 14:27:28 2014 -0700
@@ -34,7 +34,7 @@
<jvmarg line="-Dnashorn.optimistic"/>
<jvmarg line="${ext.class.path}"/>
<jvmarg line="${run.test.jvmargs}"/>
- <arg value="../make/str.js"/>
+ <arg value="../samples/test.js"/>
<jvmarg value="-Xdebug"/>
<jvmarg value="-Xrunjdwp:transport=dt_socket,address=${jpda.address}"/>
</java>
--- a/nashorn/make/project.properties Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/make/project.properties Tue Jul 01 14:27:28 2014 -0700
@@ -279,6 +279,7 @@
-Dfile.encoding=UTF-8 \
-Duser.language=${run.test.user.language} \
-Duser.country=${run.test.user.country} \
+ -Dnashorn.typeInfo.cacheDir=${build.dir}${file.separator}test${file.separator}type_info_cache \
${jfr.args} \
-XX:+HeapDumpOnOutOfMemoryError
--- a/nashorn/src/jdk/internal/dynalink/beans/OverloadedMethod.java Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/internal/dynalink/beans/OverloadedMethod.java Tue Jul 01 14:27:28 2014 -0700
@@ -152,7 +152,7 @@
@SuppressWarnings("unused")
private MethodHandle selectMethod(final Object[] args) throws NoSuchMethodException {
- final Class<?>[] argTypes = new Class[args.length];
+ final Class<?>[] argTypes = new Class<?>[args.length];
for(int i = 0; i < argTypes.length; ++i) {
final Object arg = args[i];
argTypes[i] = arg == null ? ClassString.NULL_CLASS : arg.getClass();
--- a/nashorn/src/jdk/internal/dynalink/support/CompositeTypeBasedGuardingDynamicLinker.java Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/internal/dynalink/support/CompositeTypeBasedGuardingDynamicLinker.java Tue Jul 01 14:27:28 2014 -0700
@@ -111,7 +111,7 @@
private final TypeBasedGuardingDynamicLinker[] linkers;
private final List<TypeBasedGuardingDynamicLinker>[] singletonLinkers;
- @SuppressWarnings("unchecked")
+ @SuppressWarnings(value={"unchecked", "rawtypes"})
ClassToLinker(final TypeBasedGuardingDynamicLinker[] linkers) {
this.linkers = linkers;
singletonLinkers = new List[linkers.length];
@@ -120,6 +120,7 @@
}
}
+ @SuppressWarnings("fallthrough")
@Override
protected List<TypeBasedGuardingDynamicLinker> computeValue(final Class<?> clazz) {
List<TypeBasedGuardingDynamicLinker> list = NO_LINKER;
--- a/nashorn/src/jdk/nashorn/internal/codegen/AssignSymbols.java Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/codegen/AssignSymbols.java Tue Jul 01 14:27:28 2014 -0700
@@ -209,7 +209,7 @@
if (varNode.isFunctionDeclaration()) {
symbol.setIsFunctionDeclaration();
}
- return varNode.setName((IdentNode)ident.setSymbol(symbol));
+ return varNode.setName(ident.setSymbol(symbol));
}
return varNode;
}
@@ -217,7 +217,7 @@
}
private IdentNode compilerConstantIdentifier(final CompilerConstants cc) {
- return (IdentNode)createImplicitIdentifier(cc.symbolName()).setSymbol(lc.getCurrentFunction().compilerConstant(cc));
+ return createImplicitIdentifier(cc.symbolName()).setSymbol(lc.getCurrentFunction().compilerConstant(cc));
}
/**
@@ -263,7 +263,7 @@
final Symbol nameSymbol = fn.getBody().getExistingSymbol(name.getName());
assert nameSymbol != null;
- return (VarNode)synthVar.setName((IdentNode)name.setSymbol(nameSymbol)).accept(this);
+ return (VarNode)synthVar.setName(name.setSymbol(nameSymbol)).accept(this);
}
private FunctionNode createSyntheticInitializers(final FunctionNode functionNode) {
@@ -522,7 +522,7 @@
final Symbol paramSymbol = body.getExistingSymbol(param.getName());
assert paramSymbol != null;
assert paramSymbol.isParam() : paramSymbol + " " + paramSymbol.getFlags();
- newParams.add((IdentNode)param.setSymbol(paramSymbol));
+ newParams.add(param.setSymbol(paramSymbol));
// parameters should not be slots for a function that uses variable arity signature
if (isVarArg) {
@@ -702,7 +702,7 @@
// If this is a declared variable or a function parameter, delete always fails (except for globals).
final String name = ident.getName();
final Symbol symbol = ident.getSymbol();
- final boolean failDelete = strictMode || symbol.isParam() || (symbol.isVar() && !symbol.isProgramLevel());
+ final boolean failDelete = strictMode || (!symbol.isScope() && (symbol.isParam() || (symbol.isVar() && !symbol.isProgramLevel())));
if (failDelete && symbol.isThis()) {
return LiteralNode.newInstance(unaryNode, true).accept(this);
--- a/nashorn/src/jdk/nashorn/internal/codegen/CodeGenerator.java Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/codegen/CodeGenerator.java Tue Jul 01 14:27:28 2014 -0700
@@ -145,6 +145,7 @@
import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData;
import jdk.nashorn.internal.runtime.RewriteException;
import jdk.nashorn.internal.runtime.Scope;
+import jdk.nashorn.internal.runtime.ScriptEnvironment;
import jdk.nashorn.internal.runtime.ScriptFunction;
import jdk.nashorn.internal.runtime.ScriptObject;
import jdk.nashorn.internal.runtime.ScriptRuntime;
@@ -210,6 +211,9 @@
* by reflection in class installation */
private final Compiler compiler;
+ /** Is the current code submitted by 'eval' call? */
+ private final boolean evalCode;
+
/** Call site flags given to the code generator to be used for all generated call sites */
private final int callSiteFlags;
@@ -264,6 +268,7 @@
CodeGenerator(final Compiler compiler, final int[] continuationEntryPoints) {
super(new CodeGeneratorLexicalContext());
this.compiler = compiler;
+ this.evalCode = compiler.getSource().isEvalCode();
this.continuationEntryPoints = continuationEntryPoints;
this.callSiteFlags = compiler.getScriptEnvironment()._callsite_flags;
this.log = initLogger(compiler.getContext());
@@ -290,6 +295,14 @@
}
/**
+ * Are we generating code for 'eval' code?
+ * @return true if currently compiled code is 'eval' code.
+ */
+ boolean isEvalCode() {
+ return evalCode;
+ }
+
+ /**
* Load an identity node
*
* @param identNode an identity node to load
@@ -1084,7 +1097,7 @@
closeBlockVariables(block);
lc.releaseSlots();
- assert !method.isReachable() || lc.getUsedSlotCount() == method.getFirstTemp();
+ assert !method.isReachable() || (lc.isFunctionBody() ? 0 : lc.getUsedSlotCount()) == method.getFirstTemp();
return block;
}
@@ -1277,13 +1290,26 @@
int argsCount;
@Override
void loadStack() {
- loadExpressionAsObject(ident); // Type.OBJECT as foo() makes no sense if foo == 3
- method.dup();
+ /**
+ * We want to load 'eval' to check if it is indeed global builtin eval.
+ * If this eval call is inside a 'with' statement, dyn:getMethod|getProp|getElem
+ * would be generated if ident is a "isFunction". But, that would result in a
+ * bound function from WithObject. We don't want that as bound function as that
+ * won't be detected as builtin eval. So, we make ident as "not a function" which
+ * results in "dyn:getProp|getElem|getMethod" being generated and so WithObject
+ * would return unbounded eval function.
+ *
+ * Example:
+ *
+ * var global = this;
+ * function func() {
+ * with({ eval: global.eval) { eval("var x = 10;") }
+ * }
+ */
+ loadExpressionAsObject(ident.setIsNotFunction()); // Type.OBJECT as foo() makes no sense if foo == 3
globalIsEval();
method.ifeq(is_not_eval);
- // We don't need ScriptFunction object for 'eval'
- method.pop();
// Load up self (scope).
method.loadCompilerConstant(SCOPE);
final CallNode.EvalArgs evalArgs = callNode.getEvalArgs();
@@ -1303,6 +1329,8 @@
method._goto(invoke_direct_eval);
method.label(is_not_eval);
+ // load this time but with dyn:getMethod|getProp|getElem
+ loadExpressionAsObject(ident); // Type.OBJECT as foo() makes no sense if foo == 3
// This is some scope 'eval' or global eval replaced by user
// but not the built-in ECMAScript 'eval' function call
method.loadNull();
@@ -1821,19 +1849,40 @@
method.storeCompilerConstant(ARGUMENTS);
}
- /**
- * Should this code generator skip generating code for inner functions? If lazy compilation is on, or we're
- * doing an on-demand ("just-in-time") compilation, then we aren't generating code for inner functions.
- */
- private boolean compileOutermostOnly() {
- return compiler.isOnDemandCompilation() || compiler.getScriptEnvironment()._lazy_compilation;
+ private boolean skipFunction(final FunctionNode functionNode) {
+ final ScriptEnvironment env = compiler.getScriptEnvironment();
+ final boolean lazy = env._lazy_compilation;
+ final boolean onDemand = compiler.isOnDemandCompilation();
+
+ // If this is on-demand or lazy compilation, don't compile a nested (not topmost) function.
+ if((onDemand || lazy) && lc.getOutermostFunction() != functionNode) {
+ return true;
+ }
+
+ // If lazy compiling with optimistic types, don't compile the program eagerly either. It will soon be
+ // invalidated anyway. In presence of a class cache, this further means that an obsoleted program version
+ // lingers around. Also, currently loading previously persisted optimistic types information only works if
+ // we're on-demand compiling a function, so with this strategy the :program method can also have the warmup
+ // benefit of using previously persisted types.
+ // NOTE that this means the first compiled class will effectively just have a :createProgramFunction method, and
+ // the RecompilableScriptFunctionData (RSFD) object in its constants array. It won't even have the :program
+ // method. This is by design. It does mean that we're wasting one compiler execution (and we could minimize this
+ // by just running it up to scope depth calculation, which creates the RSFDs and then this limited codegen).
+ // We could emit an initial separate compile unit with the initial version of :program in it to better utilize
+ // the compilation pipeline, but that would need more invasive changes, as currently the assumption that
+ // :program is emitted into the first compilation unit of the function lives in many places.
+ if(!onDemand && lazy && env._optimistic_types && functionNode.isProgram()) {
+ return true;
+ }
+
+ return false;
}
@Override
public boolean enterFunctionNode(final FunctionNode functionNode) {
final int fnId = functionNode.getId();
- if (compileOutermostOnly() && lc.getOutermostFunction() != functionNode) {
+ if (skipFunction(functionNode)) {
// In case we are not generating code for the function, we must create or retrieve the function object and
// load it on the stack here.
newFunctionObject(functionNode, false);
--- a/nashorn/src/jdk/nashorn/internal/codegen/CompilationPhase.java Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/codegen/CompilationPhase.java Tue Jul 01 14:27:28 2014 -0700
@@ -173,7 +173,18 @@
@Override
FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
final CompileUnit outermostCompileUnit = compiler.addCompileUnit(0L);
- final FunctionNode newFunctionNode = new Splitter(compiler, fn, outermostCompileUnit).split(fn, true);
+
+ FunctionNode newFunctionNode;
+
+ //ensure elementTypes, postsets and presets exist for splitter and arraynodes
+ newFunctionNode = (FunctionNode)fn.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
+ @Override
+ public LiteralNode<?> leaveLiteralNode(final LiteralNode<?> literalNode) {
+ return literalNode.initialize(lc);
+ }
+ });
+
+ newFunctionNode = new Splitter(compiler, newFunctionNode, outermostCompileUnit).split(newFunctionNode, true);
assert newFunctionNode.getCompileUnit() == outermostCompileUnit : "fn=" + fn.getName() + ", fn.compileUnit (" + newFunctionNode.getCompileUnit() + ") != " + outermostCompileUnit;
assert newFunctionNode.isStrict() == compiler.isStrict() : "functionNode.isStrict() != compiler.isStrict() for " + quote(newFunctionNode.getName());
@@ -374,7 +385,7 @@
assert newUnit != null;
newArrayUnits.add(new ArrayUnit(newUnit, au.getLo(), au.getHi()));
}
- aln.setUnits(newArrayUnits);
+ return aln.setUnits(lc, newArrayUnits);
}
return node;
}
@@ -421,7 +432,9 @@
compiler.getLogger().fine("Starting bytecode generation for ", quote(fn.getName()), " - restOf=", phases.isRestOfCompilation());
final CodeGenerator codegen = new CodeGenerator(compiler, phases.isRestOfCompilation() ? compiler.getContinuationEntryPoints() : null);
try {
- newFunctionNode = (FunctionNode)newFunctionNode.accept(codegen);
+ // Explicitly set BYTECODE_GENERATED here; it can not be set in case of skipping codegen for :program
+ // in the lazy + optimistic world. See CodeGenerator.skipFunction().
+ newFunctionNode = ((FunctionNode)newFunctionNode.accept(codegen)).setState(null, BYTECODE_GENERATED);
codegen.generateScopeCalls();
} catch (final VerifyError e) {
if (senv._verify_code || senv._print_code) {
@@ -489,7 +502,7 @@
Class<?> rootClass = null;
long length = 0L;
- final CodeInstaller<?> codeInstaller = compiler.getCodeInstaller();
+ final CodeInstaller<ScriptEnvironment> codeInstaller = compiler.getCodeInstaller();
final Map<String, byte[]> bytecode = compiler.getBytecode();
@@ -514,12 +527,10 @@
final Object[] constants = compiler.getConstantData().toArray();
codeInstaller.initialize(installedClasses.values(), compiler.getSource(), constants);
- // index recompilable script function datas in the constant pool
- final Map<RecompilableScriptFunctionData, RecompilableScriptFunctionData> rfns = new IdentityHashMap<>();
+ // initialize transient fields on recompilable script function data
for (final Object constant: constants) {
if (constant instanceof RecompilableScriptFunctionData) {
- final RecompilableScriptFunctionData rfn = (RecompilableScriptFunctionData)constant;
- rfns.put(rfn, rfn);
+ ((RecompilableScriptFunctionData)constant).initTransients(compiler.getSource(), codeInstaller);
}
}
--- a/nashorn/src/jdk/nashorn/internal/codegen/Compiler.java Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/codegen/Compiler.java Tue Jul 01 14:27:28 2014 -0700
@@ -46,10 +46,10 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.TreeMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import java.util.logging.Level;
-
import jdk.internal.dynalink.support.NameCodec;
import jdk.nashorn.internal.codegen.ClassEmitter.Flag;
import jdk.nashorn.internal.codegen.types.Type;
@@ -123,6 +123,11 @@
private final Map<Integer, Type> invalidatedProgramPoints;
/**
+ * Descriptor of the location where we write the type information after compilation.
+ */
+ private final Object typeInformationFile;
+
+ /**
* Compile unit name of first compile unit - this prefix will be used for all
* classes that a compilation generates.
*/
@@ -317,7 +322,7 @@
final Source source,
final String sourceURL,
final boolean isStrict) {
- this(context, env, installer, source, sourceURL, isStrict, false, null, null, null, null, null);
+ this(context, env, installer, source, sourceURL, isStrict, false, null, null, null, null, null, null);
}
/**
@@ -333,6 +338,7 @@
* @param compiledFunction compiled function, if any
* @param types parameter and return value type information, if any is known
* @param invalidatedProgramPoints invalidated program points for recompilation
+ * @param typeInformationFile descriptor of the location where type information is persisted
* @param continuationEntryPoints continuation entry points for restof method
* @param runtimeScope runtime scope for recompilation type lookup in {@code TypeEvaluator}
*/
@@ -347,6 +353,7 @@
final RecompilableScriptFunctionData compiledFunction,
final TypeMap types,
final Map<Integer, Type> invalidatedProgramPoints,
+ final Object typeInformationFile,
final int[] continuationEntryPoints,
final ScriptObject runtimeScope) {
this.context = context;
@@ -363,6 +370,7 @@
this.compiledFunction = compiledFunction;
this.types = types;
this.invalidatedProgramPoints = invalidatedProgramPoints == null ? new HashMap<Integer, Type>() : invalidatedProgramPoints;
+ this.typeInformationFile = typeInformationFile;
this.continuationEntryPoints = continuationEntryPoints == null ? null: continuationEntryPoints.clone();
this.typeEvaluator = new TypeEvaluator(this, runtimeScope);
this.firstCompileUnitName = firstCompileUnitName();
@@ -457,6 +465,16 @@
invalidatedProgramPoints.put(programPoint, type);
}
+
+ /**
+ * Returns a copy of this compiler's current mapping of invalidated optimistic program points to their types. The
+ * copy is not live with regard to changes in state in this compiler instance, and is mutable.
+ * @return a copy of this compiler's current mapping of invalidated optimistic program points to their types.
+ */
+ public Map<Integer, Type> getInvalidatedProgramPoints() {
+ return invalidatedProgramPoints == null ? null : new TreeMap<>(invalidatedProgramPoints);
+ }
+
TypeMap getTypeMap() {
return types;
}
@@ -513,6 +531,10 @@
time += (env.isTimingEnabled() ? phase.getEndTime() - phase.getStartTime() : 0L);
}
+ if(typeInformationFile != null && !phases.isRestOfCompilation()) {
+ OptimisticTypesPersistence.store(typeInformationFile, invalidatedProgramPoints);
+ }
+
log.unindent();
if (info) {
--- a/nashorn/src/jdk/nashorn/internal/codegen/FieldObjectCreator.java Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/codegen/FieldObjectCreator.java Tue Jul 01 14:27:28 2014 -0700
@@ -62,6 +62,8 @@
/** call site flags to be used for invocations */
private final int callSiteFlags;
+ /** are we creating this field object from 'eval' code? */
+ private final boolean evalCode;
/**
* Constructor
@@ -88,7 +90,7 @@
FieldObjectCreator(final CodeGenerator codegen, final List<MapTuple<T>> tuples, final boolean isScope, final boolean hasArguments) {
super(codegen, tuples, isScope, hasArguments);
this.callSiteFlags = codegen.getCallSiteFlags();
-
+ this.evalCode = codegen.isEvalCode();
countFields();
findClass();
}
@@ -153,7 +155,7 @@
@Override
protected PropertyMap makeMap() {
assert propertyMap == null : "property map already initialized";
- propertyMap = newMapCreator(fieldObjectClass).makeFieldMap(hasArguments(), fieldCount, paddedFieldCount);
+ propertyMap = newMapCreator(fieldObjectClass).makeFieldMap(hasArguments(), fieldCount, paddedFieldCount, evalCode);
return propertyMap;
}
--- a/nashorn/src/jdk/nashorn/internal/codegen/FunctionSignature.java Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/codegen/FunctionSignature.java Tue Jul 01 14:27:28 2014 -0700
@@ -141,7 +141,7 @@
paramTypeList.add(paramType.getTypeClass());
}
- this.methodType = MH.type(returnType.getTypeClass(), paramTypeList.toArray(new Class[paramTypes.length]));
+ this.methodType = MH.type(returnType.getTypeClass(), paramTypeList.toArray(new Class<?>[paramTypes.length]));
}
/**
--- a/nashorn/src/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java Tue Jul 01 14:27:28 2014 -0700
@@ -25,10 +25,12 @@
package jdk.nashorn.internal.codegen;
+import static jdk.nashorn.internal.codegen.CompilerConstants.RETURN;
import static jdk.nashorn.internal.ir.Expression.isAlwaysFalse;
import static jdk.nashorn.internal.ir.Expression.isAlwaysTrue;
import java.util.ArrayDeque;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.Deque;
import java.util.HashSet;
@@ -63,7 +65,6 @@
import jdk.nashorn.internal.ir.LexicalContext;
import jdk.nashorn.internal.ir.LexicalContextNode;
import jdk.nashorn.internal.ir.LiteralNode;
-import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode;
import jdk.nashorn.internal.ir.LocalVariableConversion;
import jdk.nashorn.internal.ir.LoopNode;
import jdk.nashorn.internal.ir.Node;
@@ -72,6 +73,7 @@
import jdk.nashorn.internal.ir.RuntimeNode;
import jdk.nashorn.internal.ir.RuntimeNode.Request;
import jdk.nashorn.internal.ir.SplitNode;
+import jdk.nashorn.internal.ir.Statement;
import jdk.nashorn.internal.ir.SwitchNode;
import jdk.nashorn.internal.ir.Symbol;
import jdk.nashorn.internal.ir.TernaryNode;
@@ -356,6 +358,8 @@
private boolean reachable = true;
// Return type of the function
private Type returnType = Type.UNKNOWN;
+ // Synthetic return node that we must insert at the end of the function if it's end is reachable.
+ private ReturnNode syntheticReturn;
// Topmost current split node (if any)
private SplitNode topSplit;
@@ -583,7 +587,11 @@
}
}
setCompilerConstantAsObject(functionNode, CompilerConstants.THIS);
- if(functionNode.needsParentScope()) {
+
+ // TODO: coarse-grained. If we wanted to solve it completely precisely,
+ // we'd also need to push/pop its type when handling WithNode (so that
+ // it can go back to undefined after a 'with' block.
+ if(functionNode.hasScopeBlock() || functionNode.needsParentScope()) {
setCompilerConstantAsObject(functionNode, CompilerConstants.SCOPE);
}
if(functionNode.needsCallee()) {
@@ -841,6 +849,10 @@
@Override
public boolean enterThrowNode(final ThrowNode throwNode) {
+ if(!reachable) {
+ return false;
+ }
+
throwNode.getExpression().accept(this);
jumpToCatchBlock(throwNode);
doesNotContinueSequentially();
@@ -1027,6 +1039,15 @@
@Override
public Node leaveBlock(final Block block) {
if(lc.isFunctionBody()) {
+ if(reachable) {
+ // reachable==true means we can reach the end of the function without an explicit return statement. We
+ // need to insert a synthetic one then. This logic used to be in Lower.leaveBlock(), but Lower's
+ // reachability analysis (through Terminal.isTerminal() flags) is not precise enough so
+ // Lower$BlockLexicalContext.afterSetStatements will sometimes think the control flow terminates even
+ // when it didn't. Example: function() { switch((z)) { default: {break; } throw x; } }.
+ createSyntheticReturn(block);
+ assert !reachable;
+ }
// We must calculate the return type here (and not in leaveFunctionNode) as it can affect the liveness of
// the :return symbol and thus affect conversion type liveness calculations for it.
calculateReturnType();
@@ -1085,6 +1106,23 @@
retSymbol.setNeedsSlot(true);
}
}
+
+ private void createSyntheticReturn(final Block body) {
+ final FunctionNode functionNode = lc.getCurrentFunction();
+ final long token = functionNode.getToken();
+ final int finish = functionNode.getFinish();
+ final List<Statement> statements = body.getStatements();
+ final int lineNumber = statements.isEmpty() ? functionNode.getLineNumber() : statements.get(statements.size() - 1).getLineNumber();
+ final IdentNode returnExpr;
+ if(functionNode.isProgram()) {
+ returnExpr = new IdentNode(token, finish, RETURN.symbolName()).setSymbol(getCompilerConstantSymbol(functionNode, RETURN));
+ } else {
+ returnExpr = null;
+ }
+ syntheticReturn = new ReturnNode(lineNumber, token, finish, returnExpr);
+ syntheticReturn.accept(this);
+ }
+
/**
* Leave a breakable node. If there's a join point associated with its break label (meaning there was at least one
* break statement to the end of the node), insert the join point into the flow.
@@ -1158,7 +1196,9 @@
} else if(binaryNode.isOptimisticUndecidedType()) {
// At this point, we can assign a static type to the optimistic binary ADD operator as now we know
// the types of its operands.
- return binaryNode.setType(Type.widest(binaryNode.lhs().getType(), binaryNode.rhs().getType()));
+ final Type type = Type.widest(binaryNode.lhs().getType(), binaryNode.rhs().getType());
+ // Use Type.CHARSEQUENCE instead of Type.STRING to avoid conversion of ConsStrings to Strings.
+ return binaryNode.setType(type.equals(Type.STRING) ? Type.CHARSEQUENCE : type);
}
return binaryNode;
}
@@ -1174,6 +1214,16 @@
}
@Override
+ public Node leaveBlock(final Block block) {
+ if(inOuterFunction && syntheticReturn != null && lc.isFunctionBody()) {
+ final ArrayList<Statement> stmts = new ArrayList<>(block.getStatements());
+ stmts.add((ReturnNode)syntheticReturn.accept(this));
+ return block.setStatements(lc, stmts);
+ }
+ return super.leaveBlock(block);
+ }
+
+ @Override
public Node leaveFunctionNode(final FunctionNode nestedFunctionNode) {
inOuterFunction = true;
final FunctionNode newNestedFunction = (FunctionNode)nestedFunctionNode.accept(
@@ -1207,10 +1257,10 @@
@Override
public Node leaveLiteralNode(final LiteralNode<?> literalNode) {
- if(literalNode instanceof ArrayLiteralNode) {
- ((ArrayLiteralNode)literalNode).analyze();
- }
- return literalNode;
+ //for e.g. ArrayLiteralNodes the initial types may have been narrowed due to the
+ //introduction of optimistic behavior - hence ensure that all literal nodes are
+ //reinitialized
+ return literalNode.initialize(lc);
}
@Override
--- a/nashorn/src/jdk/nashorn/internal/codegen/Lower.java Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/codegen/Lower.java Tue Jul 01 14:27:28 2014 -0700
@@ -75,7 +75,6 @@
import jdk.nashorn.internal.runtime.CodeInstaller;
import jdk.nashorn.internal.runtime.Context;
import jdk.nashorn.internal.runtime.JSType;
-import jdk.nashorn.internal.runtime.ScriptRuntime;
import jdk.nashorn.internal.runtime.Source;
import jdk.nashorn.internal.runtime.logging.DebugLogger;
import jdk.nashorn.internal.runtime.logging.Loggable;
@@ -160,30 +159,6 @@
}
@Override
- public Node leaveBlock(final Block block) {
- //now we have committed the entire statement list to the block, but we need to truncate
- //whatever is after the last terminal. block append won't append past it
-
-
- if (lc.isFunctionBody()) {
- final FunctionNode currentFunction = lc.getCurrentFunction();
- final boolean isProgram = currentFunction.isProgram();
- final Statement last = lc.getLastStatement();
- final ReturnNode returnNode = new ReturnNode(
- last == null ? currentFunction.getLineNumber() : last.getLineNumber(), //TODO?
- currentFunction.getToken(),
- currentFunction.getFinish(),
- isProgram ?
- compilerConstant(RETURN) :
- LiteralNode.newInstance(block, ScriptRuntime.UNDEFINED));
-
- returnNode.accept(this);
- }
-
- return block;
- }
-
- @Override
public boolean enterBreakNode(final BreakNode breakNode) {
addStatement(breakNode);
return false;
--- a/nashorn/src/jdk/nashorn/internal/codegen/MapCreator.java Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/codegen/MapCreator.java Tue Jul 01 14:27:28 2014 -0700
@@ -63,13 +63,13 @@
/**
* Constructs a property map based on a set of fields.
*
- * @param hasArguments does the created object have an "arguments" property
+ * @param hasArguments does the created object have an "arguments" property
* @param fieldCount Number of fields in use.
- * @param fieldMaximum Number of fields available.
- *
+ * @param fieldMaximum Number of fields available.
+ * @param evalCode is this property map created for 'eval' code?
* @return New map populated with accessor properties.
*/
- PropertyMap makeFieldMap(final boolean hasArguments, final int fieldCount, final int fieldMaximum) {
+ PropertyMap makeFieldMap(final boolean hasArguments, final int fieldCount, final int fieldMaximum, final boolean evalCode) {
final List<Property> properties = new ArrayList<>();
assert tuples != null;
@@ -79,7 +79,7 @@
final Class<?> initialType = tuple.getValueType();
if (symbol != null && !isValidArrayIndex(getArrayIndex(key))) {
- final int flags = getPropertyFlags(symbol, hasArguments);
+ final int flags = getPropertyFlags(symbol, hasArguments, evalCode);
final Property property = new AccessorProperty(
key,
flags,
@@ -104,7 +104,7 @@
//TODO initial type is object here no matter what. Is that right?
if (symbol != null && !isValidArrayIndex(getArrayIndex(key))) {
- final int flags = getPropertyFlags(symbol, hasArguments);
+ final int flags = getPropertyFlags(symbol, hasArguments, false);
properties.add(
new SpillProperty(
key,
@@ -124,7 +124,7 @@
*
* @return flags to use for fields
*/
- static int getPropertyFlags(final Symbol symbol, final boolean hasArguments) {
+ static int getPropertyFlags(final Symbol symbol, final boolean hasArguments, final boolean evalCode) {
int flags = 0;
if (symbol.isParam()) {
@@ -135,7 +135,13 @@
flags |= Property.HAS_ARGUMENTS;
}
- if (symbol.isScope()) {
+ // See ECMA 5.1 10.5 Declaration Binding Instantiation.
+ // Step 2 If code is eval code, then let configurableBindings
+ // be true else let configurableBindings be false.
+ // We have to make vars, functions declared in 'eval' code
+ // configurable. But vars, functions from any other code is
+ // not configurable.
+ if (symbol.isScope() && !evalCode) {
flags |= Property.NOT_CONFIGURABLE;
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk/nashorn/internal/codegen/OptimisticTypesPersistence.java Tue Jul 01 14:27:28 2014 -0700
@@ -0,0 +1,345 @@
+/*
+ * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. 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.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.net.URL;
+import java.security.AccessController;
+import java.security.MessageDigest;
+import java.security.PrivilegedAction;
+import java.sql.Date;
+import java.text.SimpleDateFormat;
+import java.util.Base64;
+import java.util.Map;
+import java.util.TreeMap;
+import jdk.nashorn.internal.codegen.types.Type;
+import jdk.nashorn.internal.runtime.Context;
+import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData;
+import jdk.nashorn.internal.runtime.Source;
+import jdk.nashorn.internal.runtime.logging.DebugLogger;
+import jdk.nashorn.internal.runtime.options.Options;
+
+/**
+ * Static utility that encapsulates persistence of decompilation information for functions. Normally, the type info
+ * persistence feature is enabled and operates in an operating-system specific per-user cache directory. You can
+ * override the directory by specifying it in the {@code nashorn.typeInfo.cacheDir} directory. Also, you can disable the
+ * type info persistence altogether by specifying the {@code nashorn.typeInfo.disabled} system property.
+ */
+public final class OptimisticTypesPersistence {
+ private static final File cacheDir = createCacheDir();
+ // In-process locks to make sure we don't have a cross-thread race condition manipulating any file.
+ private static final Object[] locks = cacheDir == null ? null : createLockArray();
+
+ // Only report one read/write error every minute
+ private static final long ERROR_REPORT_THRESHOLD = 60000L;
+
+ private static volatile long lastReportedError;
+
+ /**
+ * Retrieves an opaque descriptor for the persistence location for a given function. It should be passed to
+ * {@link #load(Object)} and {@link #store(Object, Map)} methods.
+ * @param source the source where the function comes from
+ * @param functionId the unique ID number of the function within the source
+ * @param paramTypes the types of the function parameters (as persistence is per parameter type specialization).
+ * @return an opaque descriptor for the persistence location. Can be null if persistence is disabled.
+ */
+ public static Object getLocationDescriptor(final Source source, final int functionId, final Type[] paramTypes) {
+ if(cacheDir == null) {
+ return null;
+ }
+ final StringBuilder b = new StringBuilder(48);
+ // Base64-encode the digest of the source, and append the function id.
+ b.append(Base64.getUrlEncoder().encodeToString(source.getDigest())).append('-').append(functionId);
+ // Finally, if this is a parameter-type specialized version of the function, add the parameter types to the file
+ // name.
+ if(paramTypes != null && paramTypes.length > 0) {
+ b.append('-');
+ for(final Type t: paramTypes) {
+ b.append(t.getBytecodeStackType());
+ }
+ }
+ return new LocationDescriptor(new File(cacheDir, b.toString()));
+ }
+
+ private static final class LocationDescriptor {
+ private final File file;
+
+ LocationDescriptor(final File file) {
+ this.file = file;
+ }
+ }
+
+
+ /**
+ * Stores the map of optimistic types for a given function.
+ * @param locationDescriptor the opaque persistence location descriptor, retrieved by calling
+ * {@link #getLocationDescriptor(Source, int, Type[])}.
+ * @param optimisticTypes the map of optimistic types.
+ */
+ @SuppressWarnings("resource")
+ public static void store(final Object locationDescriptor, final Map<Integer, Type> optimisticTypes) {
+ if(locationDescriptor == null) {
+ return;
+ }
+ final File file = ((LocationDescriptor)locationDescriptor).file;
+ AccessController.doPrivileged(new PrivilegedAction<Void>() {
+ @Override
+ public Void run() {
+ synchronized(getFileLock(file)) {
+ try (final FileOutputStream out = new FileOutputStream(file);) {
+ out.getChannel().lock(); // lock exclusive
+ final DataOutputStream dout = new DataOutputStream(new BufferedOutputStream(out));
+ dout.writeInt(optimisticTypes.size());
+ for(Map.Entry<Integer, Type> e: optimisticTypes.entrySet()) {
+ dout.writeInt(e.getKey());
+ final byte typeChar;
+ final Type type = e.getValue();
+ if(type == Type.OBJECT) {
+ typeChar = 'L';
+ } else if(type == Type.NUMBER) {
+ typeChar = 'D';
+ } else if(type == Type.LONG) {
+ typeChar = 'J';
+ } else {
+ throw new AssertionError();
+ }
+ dout.write(typeChar);
+ }
+ dout.flush();
+ } catch(final Exception e) {
+ reportError("write", file, e);
+ }
+ }
+ return null;
+ }
+ });
+ }
+
+ /**
+ * Loads the map of optimistic types for a given function.
+ * @param locationDescriptor the opaque persistence location descriptor, retrieved by calling
+ * {@link #getLocationDescriptor(Source, int, Type[])}.
+ * @return the map of optimistic types, or null if persisted type information could not be retrieved.
+ */
+ @SuppressWarnings("resource")
+ public static Map<Integer, Type> load(final Object locationDescriptor) {
+ if (locationDescriptor == null) {
+ return null;
+ }
+ final File file = ((LocationDescriptor)locationDescriptor).file;
+
+ return AccessController.doPrivileged(new PrivilegedAction<Map<Integer, Type>>() {
+ @Override
+ public Map<Integer, Type> run() {
+ try {
+ if(!file.isFile()) {
+ return null;
+ }
+ synchronized(getFileLock(file)) {
+ try (final FileInputStream in = new FileInputStream(file);) {
+ in.getChannel().lock(0, Long.MAX_VALUE, true); // lock shared
+ final DataInputStream din = new DataInputStream(new BufferedInputStream(in));
+ final Map<Integer, Type> map = new TreeMap<>();
+ final int size = din.readInt();
+ for(int i = 0; i < size; ++i) {
+ final int pp = din.readInt();
+ final int typeChar = din.read();
+ final Type type;
+ switch(typeChar) {
+ case 'L': type = Type.OBJECT; break;
+ case 'D': type = Type.NUMBER; break;
+ case 'J': type = Type.LONG; break;
+ default: throw new AssertionError();
+ }
+ map.put(pp, type);
+ }
+ return map;
+ }
+ }
+ } catch (final Exception e) {
+ reportError("read", file, e);
+ return null;
+ }
+ }
+ });
+ }
+
+ private static void reportError(final String msg, final File file, final Exception e) {
+ final long now = System.currentTimeMillis();
+ if(now - lastReportedError > ERROR_REPORT_THRESHOLD) {
+ getLogger().warning(String.format("Failed to %s %s", msg, file), e);
+ lastReportedError = now;
+ }
+ }
+
+ private static File createCacheDir() {
+ if(Options.getBooleanProperty("nashorn.typeInfo.disabled")) {
+ return null;
+ }
+ try {
+ return createCacheDirPrivileged();
+ } catch(final Exception e) {
+ getLogger().warning("Failed to create cache dir", e);
+ return null;
+ }
+ }
+
+ private static File createCacheDirPrivileged() {
+ return AccessController.doPrivileged(new PrivilegedAction<File>() {
+ @Override
+ public File run() {
+ final String explicitDir = System.getProperty("nashorn.typeInfo.cacheDir");
+ final File dir;
+ if(explicitDir != null) {
+ dir = new File(explicitDir);
+ } else {
+ // When no directory is explicitly specified, get an operating system specific cache directory,
+ // and create "com.oracle.java.NashornTypeInfo" in it.
+ dir = new File(getCacheDirBase(), "com.oracle.java.NashornTypeInfo");
+ }
+ final String versionDirName;
+ try {
+ versionDirName = getVersionDirName();
+ } catch(Exception e) {
+ getLogger().warning("Failed to calculate version dir name", e);
+ return null;
+ }
+ final File versionDir = new File(dir, versionDirName);
+ versionDir.mkdirs();
+ if(versionDir.isDirectory()) {
+ getLogger().info("Optimistic type persistence directory is " + versionDir);
+ return versionDir;
+ }
+ getLogger().warning("Could not create optimistic type persistence directory " + versionDir);
+ return null;
+ }
+ });
+ }
+
+ /**
+ * Returns an operating system specific root directory for cache files.
+ * @return an operating system specific root directory for cache files.
+ */
+ private static File getCacheDirBase() {
+ final String os = System.getProperty("os.name", "generic");
+ if("Mac OS X".equals(os)) {
+ // Mac OS X stores caches in ~/Library/Caches
+ return new File(new File(System.getProperty("user.home"), "Library"), "Caches");
+ } else if(os.startsWith("Windows")) {
+ // On Windows, temp directory is the best approximation of a cache directory, as its contents persist across
+ // reboots and various cleanup utilities know about it. java.io.tmpdir normally points to a user-specific
+ // temp directory, %HOME%\LocalSettings\Temp.
+ return new File(System.getProperty("java.io.tmpdir"));
+ } else {
+ // In all other cases we're presumably dealing with a UNIX flavor (Linux, Solaris, etc.); "~/.cache"
+ return new File(System.getProperty("user.home"), ".cache");
+ }
+ }
+
+ /**
+ * In order to ensure that changes in Nashorn code don't cause corruption in the data, we'll create a
+ * per-code-version directory. Normally, this will create the SHA-1 digest of the nashorn.jar. In case the classpath
+ * for nashorn is local directory (e.g. during development), this will create the string "dev-" followed by the
+ * timestamp of the most recent .class file.
+ * @return
+ */
+ private static String getVersionDirName() throws Exception {
+ final URL url = OptimisticTypesPersistence.class.getResource("");
+ final String protocol = url.getProtocol();
+ if(protocol.equals("jar")) {
+ // Normal deployment: nashorn.jar
+ final String jarUrlFile = url.getFile();
+ final String filePath = jarUrlFile.substring(0, jarUrlFile.indexOf('!'));
+ final URL file = new URL(filePath);
+ try (final InputStream in = file.openStream()) {
+ final byte[] buf = new byte[128*1024];
+ final MessageDigest digest = MessageDigest.getInstance("SHA-1");
+ for(;;) {
+ final int l = in.read(buf);
+ if(l == -1) {
+ return Base64.getUrlEncoder().encodeToString(digest.digest());
+ }
+ digest.update(buf, 0, l);
+ }
+ }
+ } else if(protocol.equals("file")) {
+ // Development
+ final String fileStr = url.getFile();
+ final String className = OptimisticTypesPersistence.class.getName();
+ final int packageNameLen = className.lastIndexOf('.');
+ final String dirStr = fileStr.substring(0, fileStr.length() - packageNameLen - 1);
+ final File dir = new File(dirStr);
+ return "dev-" + new SimpleDateFormat("yyyyMMdd-HHmmss").format(new Date(getLastModifiedClassFile(dir, 0L)));
+ } else {
+ throw new AssertionError();
+ }
+ }
+
+ private static long getLastModifiedClassFile(final File dir, final long max) {
+ long currentMax = max;
+ for(File f: dir.listFiles()) {
+ if(f.getName().endsWith(".class")) {
+ final long lastModified = f.lastModified();
+ if(lastModified > currentMax) {
+ currentMax = lastModified;
+ }
+ } else if(f.isDirectory()) {
+ final long lastModified = getLastModifiedClassFile(f, currentMax);
+ if(lastModified > currentMax) {
+ currentMax = lastModified;
+ }
+ }
+ }
+ return currentMax;
+ }
+
+ private static Object[] createLockArray() {
+ final Object[] lockArray = new Object[Runtime.getRuntime().availableProcessors() * 2];
+ for(int i = 0; i < lockArray.length; ++i) {
+ lockArray[i] = new Object();
+ }
+ return lockArray;
+ }
+
+ private static Object getFileLock(final File file) {
+ return locks[(file.hashCode() & Integer.MAX_VALUE) % locks.length];
+ }
+
+ private static DebugLogger getLogger() {
+ try {
+ return Context.getContext().getLogger(RecompilableScriptFunctionData.class);
+ } catch (final Exception e) {
+ e.printStackTrace();
+ return DebugLogger.DISABLED_LOGGER;
+ }
+ }
+}
--- a/nashorn/src/jdk/nashorn/internal/codegen/Splitter.java Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/codegen/Splitter.java Tue Jul 01 14:27:28 2014 -0700
@@ -307,7 +307,7 @@
units.add(new ArrayUnit(unit, lo, postsets.length));
}
- arrayLiteralNode.setUnits(units);
+ return arrayLiteralNode.setUnits(lc, units);
}
return literal;
--- a/nashorn/src/jdk/nashorn/internal/codegen/TypeEvaluator.java Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/codegen/TypeEvaluator.java Tue Jul 01 14:27:28 2014 -0700
@@ -25,6 +25,10 @@
package jdk.nashorn.internal.codegen;
+import static jdk.nashorn.internal.runtime.Property.NOT_CONFIGURABLE;
+import static jdk.nashorn.internal.runtime.Property.NOT_ENUMERABLE;
+import static jdk.nashorn.internal.runtime.Property.NOT_WRITABLE;
+
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.AccessNode;
import jdk.nashorn.internal.ir.Expression;
@@ -43,8 +47,8 @@
* Used during recompilation.
*/
final class TypeEvaluator {
- final Compiler compiler;
- final ScriptObject runtimeScope;
+ private final Compiler compiler;
+ private final ScriptObject runtimeScope;
TypeEvaluator(final Compiler compiler, final ScriptObject runtimeScope) {
this.compiler = compiler;
@@ -123,7 +127,7 @@
" scope="+runtimeScope;
if (runtimeScope.findProperty(symbolName, false) == null) {
- runtimeScope.set(symbolName, ScriptRuntime.UNDEFINED, true);
+ runtimeScope.addOwnProperty(symbolName, NOT_WRITABLE | NOT_ENUMERABLE | NOT_CONFIGURABLE, ScriptRuntime.UNDEFINED);
}
}
--- a/nashorn/src/jdk/nashorn/internal/codegen/TypeMap.java Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/codegen/TypeMap.java Tue Jul 01 14:27:28 2014 -0700
@@ -29,6 +29,7 @@
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;
@@ -61,6 +62,20 @@
this.needsCallee = needsCallee;
}
+ /**
+ * Returns the array of parameter types for a particular function node
+ * @param functionNodeId the ID of the function node
+ * @return an array of parameter types
+ * @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));
+ }
+ return paramTypes.clone();
+ }
+
MethodType getCallSiteType(final FunctionNode functionNode) {
final Type[] types = paramTypeMap.get(functionNode.getId());
if (types == null) {
--- a/nashorn/src/jdk/nashorn/internal/codegen/WeighNodes.java Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/codegen/WeighNodes.java Tue Jul 01 14:27:28 2014 -0700
@@ -173,7 +173,6 @@
if (functionNode == topFunction) {
// the function being weighted; descend into its statements
return true;
-// functionNode.visitStatements(this);
}
// just a reference to inner function from outer function
weight += FUNC_EXPR_WEIGHT;
--- a/nashorn/src/jdk/nashorn/internal/codegen/types/ObjectType.java Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/codegen/types/ObjectType.java Tue Jul 01 14:27:28 2014 -0700
@@ -171,6 +171,8 @@
invokestatic(method, JSType.TO_BOOLEAN);
} else if (to.isString()) {
invokestatic(method, JSType.TO_PRIMITIVE_TO_STRING);
+ } else if (to.isCharSequence()) {
+ invokestatic(method, JSType.TO_PRIMITIVE_TO_CHARSEQUENCE);
} else {
throw new UnsupportedOperationException("Illegal conversion " + this + " -> " + to + " " + isString() + " " + toString);
}
--- a/nashorn/src/jdk/nashorn/internal/codegen/types/Type.java Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/codegen/types/Type.java Tue Jul 01 14:27:28 2014 -0700
@@ -417,6 +417,15 @@
}
/**
+ * Determines whether a type is a CHARSEQUENCE type used internally strings
+ *
+ * @return true if CharSequence (internal string) type, false otherwise
+ */
+ public boolean isCharSequence() {
+ return this.equals(Type.CHARSEQUENCE);
+ }
+
+ /**
* Determine if two types are equivalent, i.e. need no conversion
*
* @param type the second type to check
@@ -800,6 +809,13 @@
public static final Type STRING = putInCache(new ObjectType(String.class));
/**
+ * This is the CharSequence singleton used to represent JS strings internally
+ * (either a {@code java.lang.String} or {@code jdk.nashorn.internal.runtime.ConsString}.
+ */
+ public static final Type CHARSEQUENCE = putInCache(new ObjectType(CharSequence.class));
+
+
+ /**
* This is the object singleton, used for all object types
*/
public static final Type OBJECT = putInCache(new ObjectType());
--- a/nashorn/src/jdk/nashorn/internal/ir/Block.java Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/Block.java Tue Jul 01 14:27:28 2014 -0700
@@ -41,7 +41,7 @@
* IR representation for a list of statements.
*/
@Immutable
-public class Block extends Node implements BreakableNode, Flags<Block> {
+public class Block extends Node implements BreakableNode, Terminal, Flags<Block> {
/** List of statements */
protected final List<Statement> statements;
@@ -231,6 +231,11 @@
return flags;
}
+ /**
+ * Is this a terminal block, i.e. does it end control flow like ending with a throw or return?
+ *
+ * @return true if this node statement is terminal
+ */
@Override
public boolean isTerminal() {
return getFlag(IS_TERMINAL);
--- a/nashorn/src/jdk/nashorn/internal/ir/CaseNode.java Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/CaseNode.java Tue Jul 01 14:27:28 2014 -0700
@@ -36,7 +36,7 @@
* Case nodes are not BreakableNodes, but the SwitchNode is
*/
@Immutable
-public final class CaseNode extends Node implements JoinPredecessor, Labels {
+public final class CaseNode extends Node implements JoinPredecessor, Labels, Terminal {
/** Test expression. */
private final Expression test;
@@ -77,6 +77,11 @@
this.conversion = conversion;
}
+ /**
+ * Is this a terminal case node, i.e. does it end control flow like having a throw or return?
+ *
+ * @return true if this node statement is terminal
+ */
@Override
public boolean isTerminal() {
return body.isTerminal();
--- a/nashorn/src/jdk/nashorn/internal/ir/ExpressionStatement.java Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/ExpressionStatement.java Tue Jul 01 14:27:28 2014 -0700
@@ -57,11 +57,6 @@
}
@Override
- public boolean isTerminal() {
- return expression.isTerminal();
- }
-
- @Override
public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
if (visitor.enterExpressionStatement(this)) {
return visitor.leaveExpressionStatement(setExpression((Expression)expression.accept(visitor)));
--- a/nashorn/src/jdk/nashorn/internal/ir/IdentNode.java Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/IdentNode.java Tue Jul 01 14:27:28 2014 -0700
@@ -110,7 +110,7 @@
* @return a temporary identifier for the symbol.
*/
public static IdentNode createInternalIdentifier(final Symbol symbol) {
- return (IdentNode)new IdentNode(Token.toDesc(TokenType.IDENT, 0, 0), 0, symbol.getName()).setSymbol(symbol);
+ return new IdentNode(Token.toDesc(TokenType.IDENT, 0, 0), 0, symbol.getName()).setSymbol(symbol);
}
@Override
@@ -180,7 +180,7 @@
* @param symbol the symbol
* @return new node
*/
- public Expression setSymbol(final Symbol symbol) {
+ public IdentNode setSymbol(final Symbol symbol) {
if (this.symbol == symbol) {
return this;
}
@@ -280,6 +280,17 @@
return new IdentNode(this, name, type, flags | FUNCTION, programPoint, conversion);
}
+ /**
+ * Mark this node as not being the callee operand of a {@link CallNode}.
+ * @return an ident node identical to this one in all aspects except with its function flag unset.
+ */
+ public IdentNode setIsNotFunction() {
+ if (! isFunction()) {
+ return this;
+ }
+ return new IdentNode(this, name, type, flags & ~FUNCTION, programPoint, conversion);
+ }
+
@Override
public int getProgramPoint() {
return programPoint;
--- a/nashorn/src/jdk/nashorn/internal/ir/LiteralNode.java Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/LiteralNode.java Tue Jul 01 14:27:28 2014 -0700
@@ -29,6 +29,7 @@
import java.util.Collections;
import java.util.List;
import java.util.function.Function;
+
import jdk.nashorn.internal.codegen.CompileUnit;
import jdk.nashorn.internal.codegen.types.ArrayType;
import jdk.nashorn.internal.codegen.types.Type;
@@ -87,6 +88,17 @@
}
/**
+ * Initialization setter, if required for immutable state. This is used for
+ * things like ArrayLiteralNodes that need to carry state for the splitter.
+ * Default implementation is just a nop.
+ * @param lc lexical context
+ * @return new literal node with initialized state, or same if nothing changed
+ */
+ public LiteralNode<?> initialize(final LexicalContext lc) {
+ return this;
+ }
+
+ /**
* Check if the literal value is null
* @return true if literal value is null
*/
@@ -573,24 +585,26 @@
/**
* Array literal node class.
*/
+ @Immutable
public static final class ArrayLiteralNode extends LiteralNode<Expression[]> implements LexicalContextNode {
/** Array element type. */
- private Type elementType;
+ private final Type elementType;
/** Preset constant array. */
- private Object presets;
+ private final Object presets;
/** Indices of array elements requiring computed post sets. */
- private int[] postsets;
+ private final int[] postsets;
- private List<ArrayUnit> units;
+ /** Sub units with indexes ranges, in which to split up code generation, for large literals */
+ private final List<ArrayUnit> units;
/**
* An ArrayUnit is a range in an ArrayLiteral. ArrayLiterals can
* be split if they are too large, for bytecode generation reasons
*/
- public static class ArrayUnit {
+ public static final class ArrayUnit {
/** Compile unit associated with the postsets range. */
private final CompileUnit compileUnit;
@@ -634,6 +648,150 @@
}
}
+ private static final class ArrayLiteralInitializer {
+
+ static ArrayLiteralNode initialize(final ArrayLiteralNode node) {
+ final Type elementType = computeElementType(node.value, node.elementType);
+ final int[] postsets = computePostsets(node.value);
+ final Object presets = computePresets(node.value, elementType, postsets);
+ return new ArrayLiteralNode(node, node.value, elementType, postsets, presets, node.units);
+ }
+
+ private static Type computeElementType(final Expression[] value, final Type elementType) {
+ Type widestElementType = Type.INT;
+
+ for (final Expression elem : value) {
+ if (elem == null) {
+ widestElementType = widestElementType.widest(Type.OBJECT); //no way to represent undefined as number
+ break;
+ }
+
+ final Type type = elem.getType().isUnknown() ? Type.OBJECT : elem.getType();
+ if (type.isBoolean()) {
+ //TODO fix this with explicit boolean types
+ widestElementType = widestElementType.widest(Type.OBJECT);
+ break;
+ }
+
+ widestElementType = widestElementType.widest(type);
+ if (widestElementType.isObject()) {
+ break;
+ }
+ }
+ return widestElementType;
+ }
+
+ private static int[] computePostsets(final Expression[] value) {
+ final int[] computed = new int[value.length];
+ int nComputed = 0;
+
+ for (int i = 0; i < value.length; i++) {
+ final Expression element = value[i];
+ if (element == null || objectAsConstant(element) == POSTSET_MARKER) {
+ computed[nComputed++] = i;
+ }
+ }
+ return Arrays.copyOf(computed, nComputed);
+ }
+
+ private static boolean setArrayElement(final int[] array, final int i, final Object n) {
+ if (n instanceof Number) {
+ array[i] = ((Number)n).intValue();
+ return true;
+ }
+ return false;
+ }
+
+ private static boolean setArrayElement(final long[] array, final int i, final Object n) {
+ if (n instanceof Number) {
+ array[i] = ((Number)n).longValue();
+ return true;
+ }
+ return false;
+ }
+
+ private static boolean setArrayElement(final double[] array, final int i, final Object n) {
+ if (n instanceof Number) {
+ array[i] = ((Number)n).doubleValue();
+ return true;
+ }
+ return false;
+ }
+
+ private static int[] presetIntArray(final Expression[] value, final int[] postsets) {
+ final int[] array = new int[value.length];
+ int nComputed = 0;
+ for (int i = 0; i < value.length; i++) {
+ if (!setArrayElement(array, i, objectAsConstant(value[i]))) {
+ assert postsets[nComputed++] == i;
+ }
+ }
+ assert postsets.length == nComputed;
+ return array;
+ }
+
+ private static long[] presetLongArray(final Expression[] value, final int[] postsets) {
+ final long[] array = new long[value.length];
+ int nComputed = 0;
+ for (int i = 0; i < value.length; i++) {
+ if (!setArrayElement(array, i, objectAsConstant(value[i]))) {
+ assert postsets[nComputed++] == i;
+ }
+ }
+ assert postsets.length == nComputed;
+ return array;
+ }
+
+ private static double[] presetDoubleArray(final Expression[] value, final int[] postsets) {
+ final double[] array = new double[value.length];
+ int nComputed = 0;
+ for (int i = 0; i < value.length; i++) {
+ if (!setArrayElement(array, i, objectAsConstant(value[i]))) {
+ assert postsets[nComputed++] == i;
+ }
+ }
+ assert postsets.length == nComputed;
+ return array;
+ }
+
+ private static Object[] presetObjectArray(final Expression[] value, final int[] postsets) {
+ final Object[] array = new Object[value.length];
+ int nComputed = 0;
+
+ for (int i = 0; i < value.length; i++) {
+ final Node node = value[i];
+
+ if (node == null) {
+ assert postsets[nComputed++] == i;
+ continue;
+ }
+ final Object element = objectAsConstant(node);
+
+ if (element != POSTSET_MARKER) {
+ array[i] = element;
+ } else {
+ assert postsets[nComputed++] == i;
+ }
+ }
+
+ assert postsets.length == nComputed;
+ return array;
+ }
+
+ static Object computePresets(final Expression[] value, final Type elementType, final int[] postsets) {
+ assert !elementType.isUnknown();
+ if (elementType.isInteger()) {
+ return presetIntArray(value, postsets);
+ } else if (elementType.isLong()) {
+ return presetLongArray(value, postsets);
+ } else if (elementType.isNumeric()) {
+ return presetDoubleArray(value, postsets);
+ } else {
+ return presetObjectArray(value, postsets);
+ }
+ }
+ }
+
/**
* Constructor
*
@@ -644,136 +802,21 @@
protected ArrayLiteralNode(final long token, final int finish, final Expression[] value) {
super(Token.recast(token, TokenType.ARRAY), finish, value);
this.elementType = Type.UNKNOWN;
+ this.presets = null;
+ this.postsets = null;
+ this.units = null;
}
/**
* Copy constructor
* @param node source array literal node
*/
- private ArrayLiteralNode(final ArrayLiteralNode node, final Expression[] value) {
+ private ArrayLiteralNode(final ArrayLiteralNode node, final Expression[] value, final Type elementType, final int[] postsets, final Object presets, final List<ArrayUnit> units) {
super(node, value);
- this.elementType = node.elementType;
- this.presets = node.presets;
- this.postsets = node.postsets;
- this.units = node.units;
- }
-
- /**
- * Compute things like widest element type needed. Internal use from compiler only
- */
- public void analyze() {
- assert elementType.isUnknown();
- elementType = getNarrowestElementType(value);
- }
-
- private int[] presetIntArray() {
- final int[] array = new int[value.length];
- int nComputed = 0;
-
- for (int i = 0; i < value.length; i++) {
- final Object element = objectAsConstant(value[i]);
-
- if (element instanceof Number) {
- array[i] = ((Number)element).intValue();
- } else {
- assert getPostsets()[nComputed++] == i;
- }
- }
-
- assert getPostsets().length == nComputed;
- return array;
- }
-
- private long[] presetLongArray() {
- final long[] array = new long[value.length];
- int nComputed = 0;
-
- for (int i = 0; i < value.length; i++) {
- final Object element = objectAsConstant(value[i]);
-
- if (element instanceof Number) {
- array[i] = ((Number)element).longValue();
- } else {
- assert getPostsets()[nComputed++] == i;
- }
- }
-
- assert getPostsets().length == nComputed;
- return array;
- }
-
- private double[] presetNumberArray() {
- final double[] array = new double[value.length];
- int nComputed = 0;
-
- for (int i = 0; i < value.length; i++) {
- final Object element = objectAsConstant(value[i]);
-
- if (element instanceof Number) {
- array[i] = ((Number)element).doubleValue();
- } else {
- assert getPostsets()[nComputed++] == i;
- }
- }
-
- assert getPostsets().length == nComputed;
- return array;
- }
-
- private Object[] presetObjectArray() {
- final Object[] array = new Object[value.length];
- int nComputed = 0;
-
- for (int i = 0; i < value.length; i++) {
- final Node node = value[i];
-
- if (node == null) {
- assert getPostsets()[nComputed++] == i;
- } else {
- final Object element = objectAsConstant(node);
-
- if (element != POSTSET_MARKER) {
- array[i] = element;
- } else {
- assert getPostsets()[nComputed++] == i;
- }
- }
- }
-
- assert getPostsets().length == nComputed;
- return array;
- }
-
- /**
- * Returns the narrowest element type that is wide enough to represent all the expressions in the array.
- * @param elementExpressions the array of expressions
- * @return the narrowest element type that is wide enough to represent all the expressions in the array.
- */
- private static Type getNarrowestElementType(final Expression[] elementExpressions) {
- Type widestElementType = Type.INT;
- for (final Expression element : elementExpressions) {
- if (element == null) {
- widestElementType = widestElementType.widest(Type.OBJECT); //no way to represent undefined as number
- break;
- }
-
- Type elementType = element.getType();
- if (elementType.isUnknown()) {
- elementType = Type.OBJECT;
- }
-
- if (elementType.isBoolean()) {
- widestElementType = widestElementType.widest(Type.OBJECT);
- break;
- }
-
- widestElementType = widestElementType.widest(elementType);
-
- if (widestElementType.isObject()) {
- break;
- }
- }
- return widestElementType;
+ this.elementType = elementType;
+ this.postsets = postsets;
+ this.presets = presets;
+ this.units = units;
}
@Override
@@ -782,6 +825,19 @@
}
/**
+ * Setter that initializes all code generation meta data for an
+ * ArrayLiteralNode. This acts a setter, so the return value may
+ * return a new node and must be handled
+ *
+ * @param lc lexical context
+ * @return new array literal node with postsets, presets and element types initialized
+ */
+ @Override
+ public ArrayLiteralNode initialize(final LexicalContext lc) {
+ return Node.replaceInLexicalContext(lc, this, ArrayLiteralInitializer.initialize(this));
+ }
+
+ /**
* Get the array element type as Java format, e.g. [I
* @return array element type
*/
@@ -811,7 +867,7 @@
* @return element type
*/
public Type getElementType() {
- assert !elementType.isUnknown();
+ assert !elementType.isUnknown() : this + " has elementType=unknown";
return elementType;
}
@@ -821,19 +877,20 @@
* @return post set indices
*/
public int[] getPostsets() {
- if(postsets == null) {
- final int[] computed = new int[value.length];
- int nComputed = 0;
+ assert postsets != null : this + " elementType=" + elementType + " has no postsets";
+ return postsets;
+ }
- for (int i = 0; i < value.length; i++) {
- final Expression element = value[i];
- if(element == null || objectAsConstant(element) == POSTSET_MARKER) {
- computed[nComputed++] = i;
- }
- }
- postsets = Arrays.copyOf(computed, nComputed);
+ private boolean presetsMatchElementType() {
+ if (elementType == Type.INT) {
+ return presets instanceof int[];
+ } else if (elementType == Type.LONG) {
+ return presets instanceof long[];
+ } else if (elementType == Type.NUMBER) {
+ return presets instanceof double[];
+ } else {
+ return presets instanceof Object[];
}
- return postsets;
}
/**
@@ -841,18 +898,7 @@
* @return presets array, always returns an array type
*/
public Object getPresets() {
- if(presets == null) {
- final Type type = getElementType();
- if (type.isInteger()) {
- presets = presetIntArray();
- } else if (type.isLong()) {
- presets = presetLongArray();
- } else if (type.isNumeric()) {
- presets = presetNumberArray();
- } else {
- presets = presetObjectArray();
- }
- }
+ assert presets != null && presetsMatchElementType() : this + " doesn't have presets, or invalid preset type: " + presets;
return presets;
}
@@ -867,11 +913,16 @@
/**
* Set the ArrayUnits that make up this ArrayLiteral
+ * @param lc lexical context
* @see ArrayUnit
* @param units list of array units
+ * @return new or changed arrayliteralnode
*/
- public void setUnits(final List<ArrayUnit> units) {
- this.units = units;
+ public ArrayLiteralNode setUnits(final LexicalContext lc, final List<ArrayUnit> units) {
+ if (this.units == units) {
+ return this;
+ }
+ return Node.replaceInLexicalContext(lc, this, new ArrayLiteralNode(this, value, elementType, postsets, presets, units));
}
@Override
@@ -889,8 +940,15 @@
return this;
}
+ private ArrayLiteralNode setValue(final LexicalContext lc, final Expression[] value) {
+ if (this.value == value) {
+ return this;
+ }
+ return Node.replaceInLexicalContext(lc, this, new ArrayLiteralNode(this, value, elementType, postsets, presets, units));
+ }
+
private ArrayLiteralNode setValue(final LexicalContext lc, final List<Expression> value) {
- return (ArrayLiteralNode)lc.replace(this, new ArrayLiteralNode(this, value.toArray(new Expression[value.size()])));
+ return setValue(lc, value.toArray(new Expression[value.size()]));
}
@Override
--- a/nashorn/src/jdk/nashorn/internal/ir/Node.java Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/Node.java Tue Jul 01 14:27:28 2014 -0700
@@ -144,15 +144,6 @@
public abstract void toString(final StringBuilder sb, final boolean printType);
/**
- * Check if this node has terminal flags, i.e. ends or breaks control flow
- *
- * @return true if terminal
- */
- public boolean hasTerminalFlags() {
- return isTerminal() || hasGoto();
- }
-
- /**
* Get the finish position for this node in the source string
* @return finish
*/
@@ -169,15 +160,6 @@
}
/**
- * Check if this function repositions control flow with goto like
- * semantics, for example {@link BreakNode} or a {@link ForNode} with no test
- * @return true if node has goto semantics
- */
- public boolean hasGoto() {
- return false;
- }
-
- /**
* Get start position for node
* @return start position
*/
@@ -249,16 +231,6 @@
return token;
}
- /**
- * Is this a terminal Node, i.e. does it end control flow like a throw or return
- * expression does?
- *
- * @return true if this node is terminal
- */
- public boolean isTerminal() {
- return false;
- }
-
//on change, we have to replace the entire list, that's we can't simple do ListIterator.set
static <T extends Node> List<T> accept(final NodeVisitor<? extends LexicalContext> visitor, final Class<T> clazz, final List<T> list) {
boolean changed = false;
--- a/nashorn/src/jdk/nashorn/internal/ir/Statement.java Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/Statement.java Tue Jul 01 14:27:28 2014 -0700
@@ -30,7 +30,7 @@
* made up of statements. The only node subclass that needs to keep token and
* location information is the Statement
*/
-public abstract class Statement extends Node {
+public abstract class Statement extends Node implements Terminal {
private final int lineNumber;
@@ -77,4 +77,32 @@
return lineNumber;
}
+ /**
+ * Is this a terminal statement, i.e. does it end control flow like a throw or return?
+ *
+ * @return true if this node statement is terminal
+ */
+ @Override
+ public boolean isTerminal() {
+ return false;
+ }
+
+ /**
+ * Check if this statement repositions control flow with goto like
+ * semantics, for example {@link BreakNode} or a {@link ForNode} with no test
+ * @return true if statement has goto semantics
+ */
+ public boolean hasGoto() {
+ return false;
+ }
+
+ /**
+ * Check if this statement has terminal flags, i.e. ends or breaks control flow
+ *
+ * @return true if has terminal flags
+ */
+ public final boolean hasTerminalFlags() {
+ return isTerminal() || hasGoto();
+ }
}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk/nashorn/internal/ir/Terminal.java Tue Jul 01 14:27:28 2014 -0700
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.nashorn.internal.ir;
+
+/**
+ * Interface for AST nodes that can have a flag determining if they can terminate function control flow.
+ */
+public interface Terminal {
+ /**
+ * Returns true if this AST node is (or contains) a statement that terminates function control flow.
+ * @return true if this AST node is (or contains) a statement that terminates function control flow.
+ */
+ public boolean isTerminal();
+}
--- a/nashorn/src/jdk/nashorn/internal/ir/debug/ASTWriter.java Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/debug/ASTWriter.java Tue Jul 01 14:27:28 2014 -0700
@@ -38,7 +38,9 @@
import jdk.nashorn.internal.ir.Expression;
import jdk.nashorn.internal.ir.IdentNode;
import jdk.nashorn.internal.ir.Node;
+import jdk.nashorn.internal.ir.Statement;
import jdk.nashorn.internal.ir.Symbol;
+import jdk.nashorn.internal.ir.Terminal;
import jdk.nashorn.internal.ir.TernaryNode;
import jdk.nashorn.internal.ir.annotations.Ignore;
import jdk.nashorn.internal.ir.annotations.Reference;
@@ -144,11 +146,11 @@
String status = "";
- if (node.isTerminal()) {
+ if (node instanceof Terminal && ((Terminal)node).isTerminal()) {
status += " Terminal";
}
- if (node.hasGoto()) {
+ if (node instanceof Statement && ((Statement)node).hasGoto()) {
status += " Goto ";
}
--- a/nashorn/src/jdk/nashorn/internal/ir/debug/NashornClassReader.java Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/debug/NashornClassReader.java Tue Jul 01 14:27:28 2014 -0700
@@ -443,7 +443,7 @@
@Override
protected Label readLabel(final int offset, final Label[] labels) {
final Label label = super.readLabel(offset, labels);
- label.info = (int)offset;
+ label.info = offset;
return label;
}
--- a/nashorn/src/jdk/nashorn/internal/ir/debug/PrintVisitor.java Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/debug/PrintVisitor.java Tue Jul 01 14:27:28 2014 -0700
@@ -182,9 +182,9 @@
final List<Statement> statements = block.getStatements();
- for (final Node statement : statements) {
- if (printLineNumbers && (statement instanceof Statement)) {
- final int lineNumber = ((Statement)statement).getLineNumber();
+ for (final Statement statement : statements) {
+ if (printLineNumbers) {
+ final int lineNumber = statement.getLineNumber();
sb.append('\n');
if (lineNumber != lastLineNumber) {
indent();
@@ -196,10 +196,6 @@
statement.accept(this);
- if (statement instanceof FunctionNode) {
- continue;
- }
-
int lastIndex = sb.length() - 1;
char lastChar = sb.charAt(lastIndex);
while (Character.isWhitespace(lastChar) && lastIndex >= 0) {
--- a/nashorn/src/jdk/nashorn/internal/objects/Global.java Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/objects/Global.java Tue Jul 01 14:27:28 2014 -0700
@@ -883,7 +883,7 @@
final Global global = Global.instance();
final ScriptObject scope = self instanceof ScriptObject ? (ScriptObject)self : global;
- return global.getContext().eval(scope, str.toString(), callThis, location, Boolean.TRUE.equals(strict));
+ return global.getContext().eval(scope, str.toString(), callThis, location, Boolean.TRUE.equals(strict), true);
}
/**
--- a/nashorn/src/jdk/nashorn/internal/objects/NativeDate.java Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/objects/NativeDate.java Tue Jul 01 14:27:28 2014 -0700
@@ -909,6 +909,7 @@
sb.append(n);
}
+ @SuppressWarnings("fallthrough")
private static String toStringImpl(final Object self, final int format) {
final NativeDate nd = getNativeDate(self);
--- a/nashorn/src/jdk/nashorn/internal/objects/NativeObject.java Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/objects/NativeObject.java Tue Jul 01 14:27:28 2014 -0700
@@ -716,6 +716,32 @@
return target;
}
+ /*
+ * Binds the source mirror object's properties to the target object. Binding
+ * properties allows two-way read/write for the properties of the source object.
+ * All inherited, enumerable properties are also bound. This method is used to
+ * to make 'with' statement work with ScriptObjectMirror as scope object.
+ *
+ * @param target the target object to which the source object's properties are bound
+ * @param source the source object whose properties are bound to the target
+ * @return the target object after property binding
+ */
+ public static Object bindAllProperties(final ScriptObject target, final ScriptObjectMirror source) {
+ final Set<String> keys = source.keySet();
+ // make accessor properties using dynamic invoker getters and setters
+ final AccessorProperty[] props = new AccessorProperty[keys.size()];
+ int idx = 0;
+ for (String name : keys) {
+ final MethodHandle getter = Bootstrap.createDynamicInvoker("dyn:getMethod|getProp|getElem:" + name, MIRROR_GETTER_TYPE);
+ final MethodHandle setter = Bootstrap.createDynamicInvoker("dyn:setProp|setElem:" + name, MIRROR_SETTER_TYPE);
+ props[idx] = AccessorProperty.create(name, 0, getter, setter);
+ idx++;
+ }
+
+ target.addBoundProperties(source, props);
+ return target;
+ }
+
private static void bindBeanProperties(final ScriptObject targetObj, final Object source,
final Collection<String> readablePropertyNames, final Collection<String> writablePropertyNames,
final Collection<String> methodNames) {
--- a/nashorn/src/jdk/nashorn/internal/runtime/CompiledFunction.java Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/CompiledFunction.java Tue Jul 01 14:27:28 2014 -0700
@@ -89,11 +89,12 @@
this.log = log;
}
- CompiledFunction(final MethodHandle invoker, final RecompilableScriptFunctionData functionData, final int flags) {
+ CompiledFunction(final MethodHandle invoker, final RecompilableScriptFunctionData functionData,
+ final Map<Integer, Type> invalidatedProgramPoints, final int flags) {
this(invoker, null, functionData.getLogger());
this.flags = flags;
if ((flags & FunctionNode.IS_DEOPTIMIZABLE) != 0) {
- optimismInfo = new OptimismInfo(functionData);
+ optimismInfo = new OptimismInfo(functionData, invalidatedProgramPoints);
} else {
optimismInfo = null;
}
@@ -691,13 +692,14 @@
private static class OptimismInfo {
// TODO: this is pointing to its owning ScriptFunctionData. Re-evaluate if that's okay.
private final RecompilableScriptFunctionData data;
- private final Map<Integer, Type> invalidatedProgramPoints = new TreeMap<>();
+ private final Map<Integer, Type> invalidatedProgramPoints;
private SwitchPoint optimisticAssumptions;
private final DebugLogger log;
- OptimismInfo(final RecompilableScriptFunctionData data) {
+ OptimismInfo(final RecompilableScriptFunctionData data, final Map<Integer, Type> invalidatedProgramPoints) {
this.data = data;
this.log = data.getLogger();
+ this.invalidatedProgramPoints = invalidatedProgramPoints == null ? new TreeMap<Integer, Type>() : invalidatedProgramPoints;
newOptimisticAssumptions();
}
--- a/nashorn/src/jdk/nashorn/internal/runtime/ConsString.java Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/ConsString.java Tue Jul 01 14:27:28 2014 -0700
@@ -36,8 +36,12 @@
public final class ConsString implements CharSequence {
private CharSequence left, right;
- final private int length;
- private boolean flat = false;
+ private final int length;
+ private volatile int state = STATE_NEW;
+
+ private final static int STATE_NEW = 0;
+ private final static int STATE_THRESHOLD = 2;
+ private final static int STATE_FLATTENED = -1;
/**
* Constructor
@@ -53,11 +57,14 @@
this.left = left;
this.right = right;
length = left.length() + right.length();
+ if (length < 0) {
+ throw new IllegalArgumentException("too big concatenated String");
+ }
}
@Override
public String toString() {
- return (String) flattened();
+ return (String) flattened(true);
}
@Override
@@ -67,22 +74,31 @@
@Override
public char charAt(final int index) {
- return flattened().charAt(index);
+ return flattened(true).charAt(index);
}
@Override
public CharSequence subSequence(final int start, final int end) {
- return flattened().subSequence(start, end);
+ return flattened(true).subSequence(start, end);
}
- private CharSequence flattened() {
- if (!flat) {
- flatten();
+ /**
+ * Returns the components of this ConsString as a {@code CharSequence} array with two elements.
+ * The elements will be either {@code Strings} or other {@code ConsStrings}.
+ * @return CharSequence array of length 2
+ */
+ public synchronized CharSequence[] getComponents() {
+ return new CharSequence[] { left, right };
+ }
+
+ private CharSequence flattened(boolean flattenNested) {
+ if (state != STATE_FLATTENED) {
+ flatten(flattenNested);
}
return left;
}
- private void flatten() {
+ private synchronized void flatten(boolean flattenNested) {
// We use iterative traversal as recursion may exceed the stack size limit.
final char[] chars = new char[length];
int pos = length;
@@ -97,8 +113,14 @@
do {
if (cs instanceof ConsString) {
final ConsString cons = (ConsString) cs;
- stack.addFirst(cons.left);
- cs = cons.right;
+ // Count the times a cons-string is traversed as part of other cons-strings being flattened.
+ // If it crosses a threshold we flatten the nested cons-string internally.
+ if (cons.state == STATE_FLATTENED || (flattenNested && ++cons.state >= STATE_THRESHOLD)) {
+ cs = cons.flattened(false);
+ } else {
+ stack.addFirst(cons.left);
+ cs = cons.right;
+ }
} else {
final String str = (String) cs;
pos -= str.length();
@@ -109,7 +131,7 @@
left = new String(chars);
right = "";
- flat = true;
+ state = STATE_FLATTENED;
}
}
--- a/nashorn/src/jdk/nashorn/internal/runtime/Context.java Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/Context.java Tue Jul 01 14:27:28 2014 -0700
@@ -444,15 +444,11 @@
}
if (env._persistent_cache) {
- if (env._lazy_compilation || env._optimistic_types) {
- getErr().println("Can not use persistent class caching with lazy compilation or optimistic compilation.");
- } else {
- try {
- final String cacheDir = Options.getStringProperty("nashorn.persistent.code.cache", "nashorn_code_cache");
- codeStore = new CodeStore(cacheDir);
- } catch (final IOException e) {
- throw new RuntimeException("Error initializing code cache", e);
- }
+ try {
+ final String cacheDir = Options.getStringProperty("nashorn.persistent.code.cache", "nashorn_code_cache");
+ codeStore = new CodeStore(cacheDir);
+ } catch (final IOException e) {
+ throw new RuntimeException("Error initializing code cache", e);
}
}
@@ -560,12 +556,29 @@
* @param callThis "this" to be passed to the evaluated code
* @param location location of the eval call
* @param strict is this {@code eval} call from a strict mode code?
+ * @return the return value of the {@code eval}
+ */
+ public Object eval(final ScriptObject initialScope, final String string,
+ final Object callThis, final Object location, final boolean strict) {
+ return eval(initialScope, string, callThis, location, strict, false);
+ }
+
+ /**
+ * Entry point for {@code eval}
+ *
+ * @param initialScope The scope of this eval call
+ * @param string Evaluated code as a String
+ * @param callThis "this" to be passed to the evaluated code
+ * @param location location of the eval call
+ * @param strict is this {@code eval} call from a strict mode code?
+ * @param evalCall is this called from "eval" builtin?
*
* @return the return value of the {@code eval}
*/
- public Object eval(final ScriptObject initialScope, final String string, final Object callThis, final Object location, final boolean strict) {
+ public Object eval(final ScriptObject initialScope, final String string,
+ final Object callThis, final Object location, final boolean strict, final boolean evalCall) {
final String file = location == UNDEFINED || location == null ? "<eval>" : location.toString();
- final Source source = sourceFor(file, string);
+ final Source source = sourceFor(file, string, evalCall);
final boolean directEval = location != UNDEFINED; // is this direct 'eval' call or indirectly invoked eval?
final Global global = Context.getGlobal();
ScriptObject scope = initialScope;
@@ -1162,7 +1175,7 @@
for (final Object constant : constants) {
if (constant instanceof RecompilableScriptFunctionData) {
- ((RecompilableScriptFunctionData) constant).setCodeAndSource(installedClasses, source);
+ ((RecompilableScriptFunctionData) constant).initTransients(source, installer);
}
}
--- a/nashorn/src/jdk/nashorn/internal/runtime/JSType.java Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/JSType.java Tue Jul 01 14:27:28 2014 -0700
@@ -130,6 +130,9 @@
/** Combined call to toPrimitive followed by toString. */
public static final Call TO_PRIMITIVE_TO_STRING = staticCall(JSTYPE_LOOKUP, JSType.class, "toPrimitiveToString", String.class, Object.class);
+ /** Combined call to toPrimitive followed by toCharSequence. */
+ public static final Call TO_PRIMITIVE_TO_CHARSEQUENCE = staticCall(JSTYPE_LOOKUP, JSType.class, "toPrimitiveToCharSequence", CharSequence.class, Object.class);
+
/** Throw an unwarranted optimism exception */
public static final Call THROW_UNWARRANTED = staticCall(JSTYPE_LOOKUP, JSType.class, "throwUnwarrantedOptimismException", Object.class, Object.class, int.class);
@@ -488,6 +491,16 @@
}
/**
+ * Like {@link #toPrimitiveToString(Object)}, but avoids conversion of ConsString to String.
+ *
+ * @param obj an object
+ * @return the CharSequence form of the primitive form of the object
+ */
+ public static CharSequence toPrimitiveToCharSequence(final Object obj) {
+ return toCharSequence(toPrimitive(obj));
+ }
+
+ /**
* JavaScript compliant conversion of number to boolean
*
* @param num a number
--- a/nashorn/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java Tue Jul 01 14:27:28 2014 -0700
@@ -28,7 +28,6 @@
import static jdk.nashorn.internal.lookup.Lookup.MH;
import java.io.IOException;
-import java.io.Serializable;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
@@ -36,12 +35,14 @@
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
+import java.util.TreeMap;
import jdk.internal.dynalink.support.NameCodec;
import jdk.nashorn.internal.codegen.CompileUnit;
import jdk.nashorn.internal.codegen.Compiler;
import jdk.nashorn.internal.codegen.Compiler.CompilationPhases;
import jdk.nashorn.internal.codegen.CompilerConstants;
import jdk.nashorn.internal.codegen.FunctionSignature;
+import jdk.nashorn.internal.codegen.OptimisticTypesPersistence;
import jdk.nashorn.internal.codegen.TypeMap;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.FunctionNode;
@@ -54,7 +55,6 @@
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.scripts.JS;
/**
* This is a subclass that represents a script function that may be regenerated,
@@ -83,9 +83,6 @@
/** Source from which FunctionNode was parsed. */
private transient Source source;
- /** Allows us to retrieve the method handle for this function once the code is compiled */
- private MethodLocator methodLocator;
-
/** Token of this function within the source. */
private final long token;
@@ -237,15 +234,18 @@
}
/**
- * Setter for code and source
+ * Initialize transient fields on deserialized instances
*
- * @param code map of code, class name to class
* @param source source
+ * @param installer code installer
*/
- public void setCodeAndSource(final Map<String, Class<?>> code, final Source source) {
- this.source = source;
- if (methodLocator != null) {
- methodLocator.setClass(code.get(methodLocator.getClassName()));
+ public void initTransients(final Source source, final CodeInstaller<ScriptEnvironment> installer) {
+ if (this.source == null && this.installer == null) {
+ this.source = source;
+ this.installer = installer;
+ } else if (this.source != source || this.installer != installer) {
+ // Existing values must be same as those passed as parameters
+ throw new IllegalArgumentException();
}
}
@@ -390,7 +390,11 @@
return getCompiler(fn, actualCallSiteType, newLocals(runtimeScope), null, null);
}
- Compiler getCompiler(final FunctionNode functionNode, final MethodType actualCallSiteType, final ScriptObject runtimeScope, final Map<Integer, Type> ipp, final int[] cep) {
+ Compiler getCompiler(final FunctionNode functionNode, final MethodType actualCallSiteType,
+ final ScriptObject runtimeScope, final Map<Integer, Type> invalidatedProgramPoints,
+ final int[] continuationEntryPoints) {
+ final TypeMap typeMap = typeMap(actualCallSiteType);
+ final Object typeInformationFile = OptimisticTypesPersistence.getLocationDescriptor(source, functionNodeId, typeMap == null ? null : typeMap.getParameterTypes(functionNodeId));
final Context context = Context.getContextTrusted();
return new Compiler(
context,
@@ -402,12 +406,31 @@
true, // is on demand
this, // compiledFunction, i.e. this RecompilableScriptFunctionData
typeMap(actualCallSiteType), // type map
- ipp, // invalidated program points
- cep, // continuation entry points
+ getEffectiveInvalidatedProgramPoints(invalidatedProgramPoints, typeInformationFile), // invalidated program points
+ typeInformationFile,
+ continuationEntryPoints, // continuation entry points
runtimeScope); // runtime scope
}
- private FunctionNode compileTypeSpecialization(final MethodType actualCallSiteType, final ScriptObject runtimeScope) {
+ /**
+ * If the function being compiled already has its own invalidated program points map, use it. Otherwise, attempt to
+ * load invalidated program points map from the persistent type info cache.
+ * @param invalidatedProgramPoints the function's current invalidated program points map. Null if the function
+ * doesn't have it.
+ * @param typeInformationFile the object describing the location of the persisted type information.
+ * @return either the existing map, or a loaded map from the persistent type info cache, or a new empty map if
+ * neither an existing map or a persistent cached type info is available.
+ */
+ private static Map<Integer, Type> getEffectiveInvalidatedProgramPoints(
+ final Map<Integer, Type> invalidatedProgramPoints, final Object typeInformationFile) {
+ if(invalidatedProgramPoints != null) {
+ return invalidatedProgramPoints;
+ }
+ final Map<Integer, Type> loadedProgramPoints = OptimisticTypesPersistence.load(typeInformationFile);
+ return loadedProgramPoints != null ? loadedProgramPoints : new TreeMap<Integer, Type>();
+ }
+
+ private TypeSpecializedFunction compileTypeSpecialization(final MethodType actualCallSiteType, final ScriptObject runtimeScope) {
// We're creating an empty script object for holding local variables. AssignSymbols will populate it with
// explicit Undefined values for undefined local variables (see AssignSymbols#defineSymbol() and
// CompilationEnvironment#declareLocalSymbol()).
@@ -417,7 +440,20 @@
}
final FunctionNode fn = reparse();
- return getCompiler(fn, actualCallSiteType, runtimeScope).compile(fn, CompilationPhases.COMPILE_ALL);
+ final Compiler compiler = getCompiler(fn, actualCallSiteType, runtimeScope);
+
+ final FunctionNode compiledFn = compiler.compile(fn, CompilationPhases.COMPILE_ALL);
+ return new TypeSpecializedFunction(compiledFn, compiler.getInvalidatedProgramPoints());
+ }
+
+ private static class TypeSpecializedFunction {
+ private final FunctionNode fn;
+ private final Map<Integer, Type> invalidatedProgramPoints;
+
+ TypeSpecializedFunction(final FunctionNode fn, final Map<Integer, Type> invalidatedProgramPoints) {
+ this.fn = fn;
+ this.invalidatedProgramPoints = invalidatedProgramPoints;
+ }
}
private MethodType explicitParams(final MethodType callSiteType) {
@@ -491,17 +527,16 @@
throw new IllegalStateException(functionNode.getName() + " id=" + functionNode.getId());
}
addCode(functionNode);
- methodLocator = new MethodLocator(functionNode);
}
- private CompiledFunction addCode(final MethodHandle target, final int fnFlags) {
- final CompiledFunction cfn = new CompiledFunction(target, this, fnFlags);
+ private CompiledFunction addCode(final MethodHandle target, final Map<Integer, Type> invalidatedProgramPoints, final int fnFlags) {
+ final CompiledFunction cfn = new CompiledFunction(target, this, invalidatedProgramPoints, fnFlags);
code.add(cfn);
return cfn;
}
private CompiledFunction addCode(final FunctionNode fn) {
- return addCode(lookup(fn), fn.getFlags());
+ return addCode(lookup(fn), null, fn.getFlags());
}
/**
@@ -510,11 +545,12 @@
* up getting one that takes a "double" etc. because of internal function logic causes widening (e.g. assignment of
* a wider value to the parameter variable). However, we use the method handle type for matching subsequent lookups
* for the same specialization, so we must adapt the handle to the expected type.
- * @param fn the function
+ * @param tfn the function
* @param callSiteType the call site type
* @return the compiled function object, with its type matching that of the call site type.
*/
- private CompiledFunction addCode(final FunctionNode fn, final MethodType callSiteType) {
+ private CompiledFunction addCode(final TypeSpecializedFunction tfn, final MethodType callSiteType) {
+ final FunctionNode fn = tfn.fn;
if (fn.isVarArg()) {
return addCode(fn);
}
@@ -544,7 +580,7 @@
toType = toType.dropParameterTypes(fromCount, toCount);
}
- return addCode(lookup(fn).asType(toType), fn.getFlags());
+ return addCode(lookup(fn).asType(toType), tfn.invalidatedProgramPoints, fn.getFlags());
}
@@ -553,12 +589,7 @@
synchronized (code) {
CompiledFunction existingBest = super.getBest(callSiteType, runtimeScope);
if (existingBest == null) {
- if(code.isEmpty() && methodLocator != null) {
- // This is a deserialized object, reconnect from method handle
- existingBest = addCode(methodLocator.getMethodHandle(), methodLocator.getFunctionFlags());
- } else {
- existingBest = addCode(compileTypeSpecialization(callSiteType, runtimeScope), callSiteType);
- }
+ existingBest = addCode(compileTypeSpecialization(callSiteType, runtimeScope), callSiteType);
}
assert existingBest != null;
@@ -576,9 +607,9 @@
}
if (applyToCall) {
- final FunctionNode fn = compileTypeSpecialization(callSiteType, runtimeScope);
- if (fn.hasOptimisticApplyToCall()) { //did the specialization work
- existingBest = addCode(fn, callSiteType);
+ final TypeSpecializedFunction tfn = compileTypeSpecialization(callSiteType, runtimeScope);
+ if (tfn.fn.hasOptimisticApplyToCall()) { //did the specialization work
+ existingBest = addCode(tfn, callSiteType);
}
}
@@ -670,48 +701,6 @@
return true;
}
- /**
- * Helper class that allows us to retrieve the method handle for this function once it has been generated.
- */
- private static class MethodLocator implements Serializable {
- private transient Class<?> clazz;
- private final String className;
- private final String methodName;
- private final MethodType methodType;
- private final int functionFlags;
-
- private static final long serialVersionUID = -5420835725902966692L;
-
- MethodLocator(final FunctionNode functionNode) {
- this.className = functionNode.getCompileUnit().getUnitClassName();
- this.methodName = functionNode.getName();
- this.methodType = new FunctionSignature(functionNode).getMethodType();
- this.functionFlags = functionNode.getFlags();
-
- assert className != null;
- assert methodName != null;
- }
-
- void setClass(final Class<?> clazz) {
- if (!JS.class.isAssignableFrom(clazz)) {
- throw new IllegalArgumentException();
- }
- this.clazz = clazz;
- }
-
- String getClassName() {
- return className;
- }
-
- MethodHandle getMethodHandle() {
- return MH.findStatic(LOOKUP, clazz, methodName, methodType);
- }
-
- int getFunctionFlags() {
- return functionFlags;
- }
- }
-
private void readObject(final java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
createLogger();
--- a/nashorn/src/jdk/nashorn/internal/runtime/ScriptFunction.java Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/ScriptFunction.java Tue Jul 01 14:27:28 2014 -0700
@@ -35,6 +35,7 @@
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.invoke.SwitchPoint;
+import java.util.Collections;
import jdk.internal.dynalink.CallSiteDescriptor;
import jdk.internal.dynalink.linker.GuardedInvocation;
import jdk.internal.dynalink.linker.LinkRequest;
@@ -593,6 +594,12 @@
private GuardedInvocation createApplyOrCallCall(final boolean isApply, final CallSiteDescriptor desc, final LinkRequest request, final Object[] args) {
final MethodType descType = desc.getMethodType();
final int paramCount = descType.parameterCount();
+ if(descType.parameterType(paramCount - 1).isArray()) {
+ // This is vararg invocation of apply or call. This can normally only happen when we do a recursive
+ // invocation of createApplyOrCallCall (because we're doing apply-of-apply). In this case, create delegate
+ // linkage by unpacking the vararg invocation and use pairArguments to introduce the necessary spreader.
+ return createVarArgApplyOrCallCall(isApply, desc, request, args);
+ }
final boolean passesThis = paramCount > 2;
final boolean passesArgs = paramCount > 3;
@@ -647,6 +654,7 @@
System.arraycopy(args, 3, tmp, 0, tmp.length);
appliedArgs[2] = NativeFunction.toApplyArgs(tmp);
} else {
+ assert !isApply;
System.arraycopy(args, 3, appliedArgs, 2, args.length - 3);
}
} else if (isFailedApplyToCall) {
@@ -705,8 +713,7 @@
}
final MethodType guardType = guard.type();
- // Original function guard will expect the invoked function in parameter position 0, but we're passing it in
- // position 1.
+ // We need to account for the dropped (apply|call) function argument.
guard = MH.dropArguments(guard, 0, descType.parameterType(0));
// Take the "isApplyFunction" guard, and bind it to this function.
MethodHandle applyFnGuard = MH.insertArguments(IS_APPLY_FUNCTION, 2, this);
@@ -718,7 +725,72 @@
return appliedInvocation.replaceMethods(inv, guard);
}
- private static MethodHandle bindImplicitThis(final Object fn, final MethodHandle mh) {
+ /*
+ * This method is used for linking nested apply. Specialized apply and call linking will create a variable arity
+ * call site for an apply call; when createApplyOrCallCall sees a linking request for apply or call with
+ * Nashorn-style variable arity call site (last argument type is Object[]) it'll delegate to this method.
+ * This method converts the link request from a vararg to a non-vararg one (unpacks the array), then delegates back
+ * to createApplyOrCallCall (with which it is thus mutually recursive), and adds appropriate argument spreaders to
+ * invocation and the guard of whatever createApplyOrCallCall returned to adapt it back into a variable arity
+ * invocation. It basically reduces the problem of vararg call site linking of apply and call back to the (already
+ * solved by createApplyOrCallCall) non-vararg call site linking.
+ */
+ private GuardedInvocation createVarArgApplyOrCallCall(final boolean isApply, final CallSiteDescriptor desc,
+ final LinkRequest request, final Object[] args) {
+ final MethodType descType = desc.getMethodType();
+ final int paramCount = descType.parameterCount();
+ final Object[] varArgs = (Object[])args[paramCount - 1];
+ // -1 'cause we're not passing the vararg array itself
+ final int copiedArgCount = args.length - 1;
+ int varArgCount = varArgs.length;
+
+ // Spread arguments for the delegate createApplyOrCallCall invocation.
+ final Object[] spreadArgs = new Object[copiedArgCount + varArgCount];
+ System.arraycopy(args, 0, spreadArgs, 0, copiedArgCount);
+ System.arraycopy(varArgs, 0, spreadArgs, copiedArgCount, varArgCount);
+
+ // Spread call site descriptor for the delegate createApplyOrCallCall invocation. We drop vararg array and
+ // replace it with a list of Object.class.
+ final MethodType spreadType = descType.dropParameterTypes(paramCount - 1, paramCount).appendParameterTypes(
+ Collections.<Class<?>>nCopies(varArgCount, Object.class));
+ final CallSiteDescriptor spreadDesc = desc.changeMethodType(spreadType);
+
+ // Delegate back to createApplyOrCallCall with the spread (that is, reverted to non-vararg) request/
+ final LinkRequest spreadRequest = request.replaceArguments(spreadDesc, spreadArgs);
+ final GuardedInvocation spreadInvocation = createApplyOrCallCall(isApply, spreadDesc, spreadRequest, spreadArgs);
+
+ // Add spreader combinators to returned invocation and guard.
+ return spreadInvocation.replaceMethods(
+ // Use standard ScriptObject.pairArguments on the invocation
+ pairArguments(spreadInvocation.getInvocation(), descType),
+ // Use our specialized spreadGuardArguments on the guard (see below).
+ spreadGuardArguments(spreadInvocation.getGuard(), descType));
+ }
+
+ private static MethodHandle spreadGuardArguments(final MethodHandle guard, final MethodType descType) {
+ final MethodType guardType = guard.type();
+ final int guardParamCount = guardType.parameterCount();
+ final int descParamCount = descType.parameterCount();
+ final int spreadCount = guardParamCount - descParamCount + 1;
+ if (spreadCount <= 0) {
+ // Guard doesn't dip into the varargs
+ return guard;
+ }
+
+ final MethodHandle arrayConvertingGuard;
+ // If the last parameter type of the guard is an array, then it is already itself a guard for a vararg apply
+ // invocation. We must filter the last argument with toApplyArgs otherwise deeper levels of nesting will fail
+ // with ClassCastException of NativeArray to Object[].
+ if(guardType.parameterType(guardParamCount - 1).isArray()) {
+ arrayConvertingGuard = MH.filterArguments(guard, guardParamCount - 1, NativeFunction.TO_APPLY_ARGS);
+ } else {
+ arrayConvertingGuard = guard;
+ }
+
+ return ScriptObject.adaptHandleToVarArgCallSite(arrayConvertingGuard, descParamCount);
+ }
+
+ private static MethodHandle bindImplicitThis(final Object fn, final MethodHandle mh) {
final MethodHandle bound;
if(fn instanceof ScriptFunction && ((ScriptFunction)fn).needsWrappedThis()) {
bound = MH.filterArguments(mh, 1, SCRIPTFUNCTION_GLOBALFILTER);
--- a/nashorn/src/jdk/nashorn/internal/runtime/ScriptObject.java Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/ScriptObject.java Tue Jul 01 14:27:28 2014 -0700
@@ -691,6 +691,7 @@
assert isValidArrayIndex(index) : "invalid array index";
final long longIndex = ArrayIndex.toLongIndex(index);
doesNotHaveEnsureDelete(longIndex, getArray().length(), false);
+ setArray(getArray().ensure(longIndex));
setArray(getArray().set(index, value, false));
}
@@ -2499,18 +2500,7 @@
}
if (isCallerVarArg) {
- final int spreadArgs = parameterCount - callCount + 1;
- return MH.filterArguments(
- MH.asSpreader(
- methodHandle,
- Object[].class,
- spreadArgs),
- callCount - 1,
- MH.insertArguments(
- TRUNCATINGFILTER,
- 0,
- spreadArgs)
- );
+ return adaptHandleToVarArgCallSite(methodHandle, callCount);
}
if (callCount < parameterCount) {
@@ -2541,6 +2531,21 @@
return methodHandle;
}
+ static MethodHandle adaptHandleToVarArgCallSite(final MethodHandle mh, final int callSiteParamCount) {
+ final int spreadArgs = mh.type().parameterCount() - callSiteParamCount + 1;
+ return MH.filterArguments(
+ MH.asSpreader(
+ mh,
+ Object[].class,
+ spreadArgs),
+ callSiteParamCount - 1,
+ MH.insertArguments(
+ TRUNCATINGFILTER,
+ 0,
+ spreadArgs)
+ );
+ }
+
@SuppressWarnings("unused")
private static Object[] truncatingFilter(final int n, final Object[] array) {
final int length = array == null ? 0 : array.length;
--- a/nashorn/src/jdk/nashorn/internal/runtime/ScriptRuntime.java Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/ScriptRuntime.java Tue Jul 01 14:27:28 2014 -0700
@@ -27,6 +27,7 @@
import static jdk.nashorn.internal.codegen.CompilerConstants.staticCall;
import static jdk.nashorn.internal.codegen.CompilerConstants.staticCallNoLookup;
+import static jdk.nashorn.internal.runtime.ECMAErrors.rangeError;
import static jdk.nashorn.internal.runtime.ECMAErrors.referenceError;
import static jdk.nashorn.internal.runtime.ECMAErrors.syntaxError;
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
@@ -49,6 +50,7 @@
import jdk.nashorn.internal.codegen.CompilerConstants.Call;
import jdk.nashorn.internal.ir.debug.JSONWriter;
import jdk.nashorn.internal.objects.Global;
+import jdk.nashorn.internal.objects.NativeObject;
import jdk.nashorn.internal.parser.Lexer;
import jdk.nashorn.internal.runtime.linker.Bootstrap;
@@ -478,9 +480,21 @@
throw typeError(global, "cant.apply.with.to.null");
}
- final Object wrappedExpr = JSType.toScriptObject(global, expression);
- if (wrappedExpr instanceof ScriptObject) {
- return new WithObject(scope, (ScriptObject)wrappedExpr);
+ if (expression instanceof ScriptObjectMirror) {
+ final Object unwrapped = ScriptObjectMirror.unwrap(expression, global);
+ if (unwrapped instanceof ScriptObject) {
+ return new WithObject(scope, (ScriptObject)unwrapped);
+ } else {
+ // foreign ScriptObjectMirror
+ ScriptObject exprObj = global.newObject();
+ NativeObject.bindAllProperties(exprObj, (ScriptObjectMirror)expression);
+ return new WithObject(scope, exprObj);
+ }
+ } else {
+ final Object wrappedExpr = JSType.toScriptObject(global, expression);
+ if (wrappedExpr instanceof ScriptObject) {
+ return new WithObject(scope, (ScriptObject)wrappedExpr);
+ }
}
throw typeError(global, "cant.apply.with.to.non.scriptobject");
@@ -518,7 +532,11 @@
if (xPrim instanceof String || yPrim instanceof String
|| xPrim instanceof ConsString || yPrim instanceof ConsString) {
- return new ConsString(JSType.toCharSequence(xPrim), JSType.toCharSequence(yPrim));
+ try {
+ return new ConsString(JSType.toCharSequence(xPrim), JSType.toCharSequence(yPrim));
+ } catch (final IllegalArgumentException iae) {
+ throw rangeError(iae, "concat.string.too.big");
+ }
}
return JSType.toNumber(xPrim) + JSType.toNumber(yPrim);
--- a/nashorn/src/jdk/nashorn/internal/runtime/ScriptingFunctions.java Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/ScriptingFunctions.java Tue Jul 01 14:27:28 2014 -0700
@@ -107,8 +107,8 @@
if (file instanceof File) {
f = (File)file;
- } else if (file instanceof String) {
- f = new java.io.File((String)file);
+ } else if (file instanceof String || file instanceof ConsString) {
+ f = new java.io.File(((CharSequence)file).toString());
}
if (f == null || !f.isFile()) {
--- a/nashorn/src/jdk/nashorn/internal/runtime/Source.java Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/Source.java Tue Jul 01 14:27:28 2014 -0700
@@ -142,29 +142,34 @@
long lastModified();
char[] array();
+
+ boolean isEvalCode();
}
private static class RawData implements Data {
private final char[] array;
+ private final boolean evalCode;
private int hash;
- private RawData(final char[] array) {
+ private RawData(final char[] array, final boolean evalCode) {
this.array = Objects.requireNonNull(array);
+ this.evalCode = evalCode;
}
- private RawData(final String source) {
+ private RawData(final String source, final boolean evalCode) {
this.array = Objects.requireNonNull(source).toCharArray();
+ this.evalCode = evalCode;
}
private RawData(final Reader reader) throws IOException {
- this(readFully(reader));
+ this(readFully(reader), false);
}
@Override
public int hashCode() {
int h = hash;
if (h == 0) {
- h = hash = Arrays.hashCode(array);
+ h = hash = Arrays.hashCode(array) ^ (evalCode? 1 : 0);
}
return h;
}
@@ -175,7 +180,8 @@
return true;
}
if (obj instanceof RawData) {
- return Arrays.equals(array, ((RawData)obj).array);
+ final RawData other = (RawData)obj;
+ return Arrays.equals(array, other.array) && evalCode == other.evalCode;
}
return false;
}
@@ -206,6 +212,10 @@
}
+ @Override
+ public boolean isEvalCode() {
+ return evalCode;
+ }
}
private static class URLData implements Data {
@@ -287,10 +297,16 @@
return array;
}
+ @Override
+ public boolean isEvalCode() {
+ return false;
+ }
+
boolean isDeferred() {
return array == null;
}
+ @SuppressWarnings("try")
protected void checkPermissionAndClose() throws IOException {
try (InputStream in = url.openStream()) {
// empty
@@ -373,11 +389,23 @@
*
* @param name source name
* @param content contents as char array
+ * @param isEval does this represent code from 'eval' call?
+ * @return source instance
+ */
+ public static Source sourceFor(final String name, final char[] content, final boolean isEval) {
+ return new Source(name, baseName(name), new RawData(content, isEval));
+ }
+
+ /**
+ * Returns a Source instance
+ *
+ * @param name source name
+ * @param content contents as char array
*
* @return source instance
*/
public static Source sourceFor(final String name, final char[] content) {
- return new Source(name, baseName(name), new RawData(content));
+ return sourceFor(name, content, false);
}
/**
@@ -385,11 +413,22 @@
*
* @param name source name
* @param content contents as string
+ * @param isEval does this represent code from 'eval' call?
+ * @return source instance
+ */
+ public static Source sourceFor(final String name, final String content, final boolean isEval) {
+ return new Source(name, baseName(name), new RawData(content, isEval));
+ }
+
+ /**
+ * Returns a Source instance
*
+ * @param name source name
+ * @param content contents as string
* @return source instance
*/
public static Source sourceFor(final String name, final String content) {
- return new Source(name, baseName(name), new RawData(content));
+ return sourceFor(name, content, false);
}
/**
@@ -555,6 +594,15 @@
}
/**
+ * Returns whether this source was submitted via 'eval' call or not.
+ *
+ * @return true if this source represents code submitted via 'eval'
+ */
+ public boolean isEvalCode() {
+ return data.isEvalCode();
+ }
+
+ /**
* Find the beginning of the line containing position.
* @param position Index to offending token.
* @return Index of first character of line.
--- a/nashorn/src/jdk/nashorn/internal/runtime/UserAccessorProperty.java Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/UserAccessorProperty.java Tue Jul 01 14:27:28 2014 -0700
@@ -201,7 +201,7 @@
@Override
public Object getObjectValue(final ScriptObject self, final ScriptObject owner) {
- return userAccessorGetter(getAccessors((owner != null) ? owner : (ScriptObject)self), self);
+ return userAccessorGetter(getAccessors((owner != null) ? owner : self), self);
}
@Override
@@ -221,7 +221,7 @@
@Override
public void setValue(final ScriptObject self, final ScriptObject owner, final Object value, final boolean strict) {
- userAccessorSetter(getAccessors((owner != null) ? owner : (ScriptObject)self), strict ? getKey() : null, self, value);
+ userAccessorSetter(getAccessors((owner != null) ? owner : self), strict ? getKey() : null, self, value);
}
@Override
--- a/nashorn/src/jdk/nashorn/internal/runtime/WithObject.java Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/WithObject.java Tue Jul 01 14:27:28 2014 -0700
@@ -31,6 +31,8 @@
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.invoke.SwitchPoint;
+import jdk.nashorn.api.scripting.AbstractJSObject;
+import jdk.nashorn.api.scripting.ScriptObjectMirror;
import jdk.internal.dynalink.CallSiteDescriptor;
import jdk.internal.dynalink.linker.GuardedInvocation;
import jdk.internal.dynalink.linker.LinkRequest;
@@ -312,7 +314,22 @@
@SuppressWarnings("unused")
private static Object bindToExpression(final Object fn, final Object receiver) {
- return fn instanceof ScriptFunction ? bindToExpression((ScriptFunction) fn, receiver) : fn;
+ if (fn instanceof ScriptFunction) {
+ return bindToExpression((ScriptFunction) fn, receiver);
+ } else if (fn instanceof ScriptObjectMirror) {
+ final ScriptObjectMirror mirror = (ScriptObjectMirror)fn;
+ if (mirror.isFunction()) {
+ // We need to make sure correct 'this' is used for calls with Ident call
+ // expressions. We do so here using an AbstractJSObject instance.
+ return new AbstractJSObject() {
+ public Object call(final Object thiz, final Object... args) {
+ return mirror.call(withFilterExpression(receiver), args);
+ }
+ };
+ }
+ }
+
+ return fn;
}
private static Object bindToExpression(final ScriptFunction fn, final Object receiver) {
--- a/nashorn/src/jdk/nashorn/internal/runtime/arrays/ByteBufferArrayData.java Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/arrays/ByteBufferArrayData.java Tue Jul 01 14:27:28 2014 -0700
@@ -150,7 +150,7 @@
@Override
public Object getObject(final int index) {
- return (int)(0x0ff & buf.get(index));
+ return 0x0ff & buf.get(index);
}
@Override
--- a/nashorn/src/jdk/nashorn/internal/runtime/linker/InvokeByName.java Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/linker/InvokeByName.java Tue Jul 01 14:27:28 2014 -0700
@@ -90,7 +90,7 @@
if(plength == 0) {
finalPtypes = new Class<?>[] { Object.class, targetClass };
} else {
- finalPtypes = new Class[plength + 2];
+ finalPtypes = new Class<?>[plength + 2];
finalPtypes[0] = Object.class;
finalPtypes[1] = targetClass;
System.arraycopy(ptypes, 0, finalPtypes, 2, plength);
--- a/nashorn/src/jdk/nashorn/internal/runtime/resources/Messages.properties Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/resources/Messages.properties Tue Jul 01 14:27:28 2014 -0700
@@ -151,6 +151,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
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
--- a/nashorn/test/script/basic/JDK-8030182_2.js.EXPECTED Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/test/script/basic/JDK-8030182_2.js.EXPECTED Tue Jul 01 14:27:28 2014 -0700
@@ -1,3 +1,3 @@
ReferenceError: "g" is not defined
- at <program> (test/script/basic/JDK-8030182_2.js#42:4<eval>@0:-1)
+ at <program> (test/script/basic/JDK-8030182_2.js#42:4<eval>@1:-1)
at <program> (test/script/basic/JDK-8030182_2.js:42)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8046013.js Tue Jul 01 14:27:28 2014 -0700
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * JDK-8046013: TypeError: Cannot apply "with" to non script object
+ *
+ * @test
+ * @run
+ */
+
+var obj = loadWithNewGlobal({
+ script: "({ f: 33 })",
+ name: "test"
+});
+
+with (obj) {
+ print("f = " + f);
+}
+
+var obj2 = loadWithNewGlobal({
+ script: "var obj = Object.create({ foo: 42 }); obj.bar = 'hello'; obj",
+ name: "test2"
+});
+
+with (obj2) {
+ print("foo = " + foo);
+ print("bar = " + bar);
+}
+
+var obj3 = loadWithNewGlobal({
+ script: "({ f: 33, func: function() { print('this.f =', this.f); } })",
+ name: "test"
+});
+
+with(obj3) {
+ func();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8046013.js.EXPECTED Tue Jul 01 14:27:28 2014 -0700
@@ -0,0 +1,4 @@
+f = 33
+foo = 42
+bar = hello
+this.f = 33
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8046905.js Tue Jul 01 14:27:28 2014 -0700
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * JDK-8046905: apply on apply is broken
+ *
+ * @test
+ * @run
+ */
+
+var apply = Function.prototype.apply;
+var call = Function.prototype.call;
+var sort = Array.prototype.sort;
+var join = Array.prototype.join;
+
+// Running three times so that we test an already linked call site too:
+// i==0: linking initially with assumed optimistic returned type int.
+// i==1: linking after deoptimization with returned type Object.
+// i==2: re-running code linked in previous iteration. This will
+// properly exercise the guards too.
+print("1 level of apply")
+for(i = 0; i < 3; ++i) {
+ print(sort.apply([4,3,2,1]))
+}
+print("2 levels of apply")
+for(i = 0; i < 3; ++i) {
+ print(apply.apply(sort,[[4,3,2,1]]))
+}
+print("3 levels of apply")
+for(i = 0; i < 3; ++i) {
+ print(apply.apply(apply,[sort,[[4,3,2,1]]]))
+}
+print("4 levels of apply")
+for(i = 0; i < 3; ++i) {
+ print(apply.apply(apply,[apply,[sort,[[4,3,2,1]]]]))
+}
+print("5 levels of apply")
+for(i = 0; i < 3; ++i) {
+ print(apply.apply(apply,[apply,[apply,[sort,[[4,3,2,1]]]]]))
+}
+print("Many levels of apply!")
+for(i = 0; i < 3; ++i) {
+ print(apply.apply(apply,[apply,[apply,[apply,[apply,[apply,[apply,[apply,[apply,[apply,[apply,[apply,[apply,[apply,[apply,[apply,[apply,[apply,[apply,[apply,[sort,[[4,3,2,1]]]]]]]]]]]]]]]]]]]]]]))
+}
+
+print("different invocations that'll trigger relinking")
+var invocation = [sort,[[4,3,2,1]]];
+for(i = 0; i < 4; ++i) {
+ print(apply.apply(apply,[apply,invocation]))
+ // First change after i==1, so it relinks an otherwise stable linkage
+ if(i == 1) {
+ invocation = [sort,[[8,7,6,5]]];
+ } else if(i == 2) {
+ invocation = [join,[[8,7,6,5],["-"]]];
+ }
+}
+
+print("Many levels of call!")
+for(i = 0; i < 3; ++i) {
+ print(call.call(call,call,call,call,call,call,call,call,call,call,call,call,call,call,call,call,call,call,call,call,sort,[4,3,2,1]))
+}
+
+print("call apply call apply call... a lot");
+for(i = 0; i < 3; ++i) {
+ print(apply.call(call, apply, [call, apply, [call, apply, [call, apply, [call, apply, [call, apply, [sort, [4,3,2,1]]]]]]]))
+}
+
+print("apply call apply call apply... a lot");
+for(i = 0; i < 3; ++i) {
+ print(call.apply(apply, [call, apply, [call, apply, [call, apply, [call, apply, [call, apply, [call, apply, [call, sort, [[4,3,2,1]]]]]]]]]))
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8046905.js.EXPECTED Tue Jul 01 14:27:28 2014 -0700
@@ -0,0 +1,41 @@
+1 level of apply
+1,2,3,4
+1,2,3,4
+1,2,3,4
+2 levels of apply
+1,2,3,4
+1,2,3,4
+1,2,3,4
+3 levels of apply
+1,2,3,4
+1,2,3,4
+1,2,3,4
+4 levels of apply
+1,2,3,4
+1,2,3,4
+1,2,3,4
+5 levels of apply
+1,2,3,4
+1,2,3,4
+1,2,3,4
+Many levels of apply!
+1,2,3,4
+1,2,3,4
+1,2,3,4
+different invocations that'll trigger relinking
+1,2,3,4
+1,2,3,4
+5,6,7,8
+8-7-6-5
+Many levels of call!
+1,2,3,4
+1,2,3,4
+1,2,3,4
+call apply call apply call... a lot
+1,2,3,4
+1,2,3,4
+1,2,3,4
+apply call apply call apply... a lot
+1,2,3,4
+1,2,3,4
+1,2,3,4
--- a/nashorn/test/script/basic/JDK-8047057.js Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/test/script/basic/JDK-8047057.js Tue Jul 01 14:27:28 2014 -0700
@@ -29,8 +29,7 @@
*/
// commented out makeFuncAndCall calls are still result in crash
-// Tests commented with //** fail only within test framework.
-// Pass fine with standalone "jjs" mode.
+// Tests commented with //** fail only when assertions are turned on
function makeFuncAndCall(code) {
Function(code)();
@@ -46,30 +45,31 @@
}
}
-// makeFuncAndCall("switch(0) { default: {break;} return }");
-// makeFuncAndCall("L: { { break L; } return; }");
+makeFuncAndCall("switch(0) { default: {break;} return }");
+makeFuncAndCall("L: { { break L; } return; }");
makeFuncAndCall("L: { while(0) break L; return; }");
makeFuncExpectError("L: {while(0) break L; return [](); }", TypeError);
// makeFuncAndCall("do with({}) break ; while(0);");
makeFuncAndCall("while(0) with({}) continue ;");
-//** makeFuncAndCall("eval([]);");
-//** makeFuncAndCall("try{} finally{[]}");
+makeFuncAndCall("eval([]);");
+makeFuncAndCall("try{} finally{[]}");
makeFuncAndCall("try { } catch(x if 1) { try { } catch(x2) { } }");
makeFuncAndCall("try { } catch(x if 1) { try { return; } catch(x2) { { } } }");
makeFuncAndCall("Error() * (false)[-0]--");
makeFuncAndCall("try { var x = 1, x = null; } finally { }");
makeFuncAndCall("try { var x = {}, x = []; } catch(x3) { }");
-//** makeFuncAndCall("[delete this]");
+makeFuncAndCall("[delete this]");
// makeFuncAndCall("if(eval('', eval('', function() {}))) { }");
// makeFuncAndCall("if(eval('', eval('', function() {}))) { }");
// makeFuncAndCall("eval(\"[,,];\", [11,12,13,14].some)");
// makeFuncAndCall("eval(\"1.2e3\", ({})[ /x/ ])");
-// makeFuncAndCall("eval(\"x4\", x3);");
+makeFuncExpectError("eval(\"x4\", x3);", ReferenceError);
makeFuncAndCall("with({5.0000000000000000000000: String()}){(false); }");
makeFuncAndCall("try { var x = undefined, x = 5.0000000000000000000000; } catch(x) { x = undefined; }");
makeFuncAndCall("(function (x){ x %= this}(false))");
-// makeFuncAndCall("eval.apply.apply(function(){ eval('') })");
+makeFuncAndCall("eval.apply.apply(function(){ eval('') })");
makeFuncAndCall("(false % !this) && 0");
makeFuncAndCall("with({8: 'fafafa'.replace()}){ }");
makeFuncAndCall("(function (x) '' )(true)");
makeFuncExpectError("new eval(function(){})", TypeError);
+//** makeFuncAndCall('eval("23", ({})[/x/])');
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8047078.js Tue Jul 01 14:27:28 2014 -0700
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * JDK-8047078: ArrayLiteral mutability caused trouble in optimistic types
+ *
+ * @test
+ * @run
+ */
+
+function makeFuncAndCall(code) {
+ Function(code)();
+}
+
+makeFuncAndCall("eval([]);");
+makeFuncAndCall("eval([1]);");
+makeFuncAndCall("eval([1,2,3,,4]);");
+makeFuncAndCall("try{} finally{[]}");
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8047357.js Tue Jul 01 14:27:28 2014 -0700
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * JDK-8047357: More precise synthetic return + unreachable throw
+ *
+ * @test
+ * @run
+ */
+
+print((function() { switch(0) { default: {var x; break ; } throw x; } })());
+print((function() { switch(0) { default: {break;} return; } })());
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8047357.js.EXPECTED Tue Jul 01 14:27:28 2014 -0700
@@ -0,0 +1,2 @@
+undefined
+undefined
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8047359.js Tue Jul 01 14:27:28 2014 -0700
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * JDK-8047359: large string size RangeError should be thrown rather than reporting negative length
+ *
+ * @test
+ * @run
+ */
+
+try {
+ var s = " "; for (var i=0;i<31;++i) s+=s; s.length;
+ throw new Error("should have thrown RangeError!");
+} catch (e) {
+ if (! (e instanceof RangeError)) {
+ fail("RangeError expected, got " + e);
+ }
+}
+
+try {
+ var s = " "; for (var i=0;i<31;++i) s+=s;
+ throw new Error("should have thrown RangeError!");
+} catch (e) {
+ if (! (e instanceof RangeError)) {
+ fail("RangeError expected, got " + e);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8047369.js Tue Jul 01 14:27:28 2014 -0700
@@ -0,0 +1,186 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * JDK-8047369: Add regression tests for passing test cases of JDK-8024971
+ *
+ * @test
+ * @run
+ * @option -scripting
+ */
+
+function makeFuncAndCall(code) {
+ Function(code)();
+}
+
+function makeFuncExpectError(code, ErrorType) {
+ try {
+ makeFuncAndCall(code);
+ } catch (e) {
+ if (! (e instanceof ErrorType)) {
+ fail(ErrorType.name + " expected, got " + e);
+ }
+ }
+}
+
+function evalExpectError(code, ErrorType) {
+ try {
+ eval(code)();
+ } catch (e) {
+ if (! (e instanceof ErrorType)) {
+ fail(ErrorType.name + " expected, got " + e);
+ }
+ }
+}
+
+function evalExpectValue(code, value) {
+ if (eval(code) != value) {
+ fail("Expected " + value + " with eval of " + code);
+ }
+}
+
+makeFuncAndCall("for(x.x in 0) {}");
+// bug JDK-8047357
+// makeFuncAndCall("switch((null >> x3)) { default: {var x; break ; }\nthrow x; }");
+makeFuncExpectError("switch(x) { case 8: break; case false: }", ReferenceError);
+makeFuncAndCall("try { return true; } finally { return false; } ");
+makeFuncAndCall("({ get 1e81(){} })");
+makeFuncAndCall("{var x, x3;try { return 0; } finally { return 3/0; } }");
+makeFuncExpectError("with(x ? 1e81 : (x2.constructor = 0.1)) {}", ReferenceError);
+makeFuncAndCall("while(x-=1) {var x=0; }");
+makeFuncAndCall("while((x-=false) && 0) { var x = this; }");
+makeFuncAndCall("/*infloop*/while(x4-=x) var x, x4 = x1;");
+makeFuncAndCall("/*infloop*/L:while(x+=null) { this;var x = /x/g ; }");
+makeFuncAndCall("while((x1|=0.1) && 0) { var x1 = -0, functional; }");
+makeFuncAndCall("with({}) return (eval(\"arguments\"));");
+
+evalExpectValue(<<CODE
+ var s = "(function() { return y })()";
+ (function() {
+ with({ y:1 })
+ eval(s)
+ })();
+ (function() {
+ with({
+ get y() { return "get"; }
+ })
+ return eval(s)
+ })();
+CODE, "get");
+
+// bug JDK-8047359
+// evalExpectValue("s = ' '; for (var i=0;i<31;++i) s+=s; s.length", RangeError);
+
+evalExpectValue(<<CODE
+ function f(o) {
+ var eval=0;
+ with({
+ get eval() { return o.eval }
+ })
+ return eval("1+2");
+ }
+ f(this);
+CODE, 3)
+
+evalExpectValue(<<CODE
+ function f() {
+ var a=1,e=2;
+ try {
+ throw 3
+ } catch(e) {
+ return + function g(){return eval('a+e')}()
+ }
+ }
+ f();
+CODE, 4);
+
+//evalExpectValue(
+// "function f(){var a=1; with({get a(){return false}}) return a}; f()", false);
+
+evalExpectError("function public() {\"use strict\"}", SyntaxError);
+evalExpectError("function f(public) {\"use strict\"}", SyntaxError);
+evalExpectError("function f() { switch(x) {} } f()", ReferenceError);
+
+// bug JDK-8047364
+// makeFuncAndCall("L1:try { return } finally { break L1 }");
+
+evalExpectValue(<<CODE
+ function f() {
+ function g() { return 0 }
+ function g() { return 1 }
+ function g$1() { return 2 }
+ return g$1()
+ }
+
+ f();
+CODE, 2);
+
+evalExpectValue(<<CODE
+ function f() {
+ function g() {return 0 }
+ var h = function g() { return 1 };
+ function g$1() { return 2 };
+ return h()
+ }
+
+ f()
+CODE, 1);
+
+evalExpectValue(<<CODE
+ function f() {
+ var obj = { get ":"() {} }
+ var desc = Object.getOwnPropertyDescriptor(obj, ":")
+ return desc.get.name
+ }
+
+ f()
+CODE, ":");
+
+evalExpectValue(<<CODE
+ function f() {
+ var obj = { set ":"(a) {} };
+ var desc = Object.getOwnPropertyDescriptor(obj, ":");
+ return desc.set;
+ }
+
+ f()
+CODE, "set \":\"(a) {}");
+
+// bug JDK-8047366
+// evalExpectValue("(1000000000000000128).toString()", "1000000000000000100");
+// evalExpectValue("(1000000000000000128).toFixed().toString()", "1000000000000000128");
+
+try {
+ Function("-", {
+ toString: function() {
+ throw "err"
+ }
+ })();
+} catch (e) {
+ if (e != "err") {
+ fail("Expected 'err' here, got " + e);
+ }
+}
+evalExpectError("function f() { switch(x) {} } f()", ReferenceError);
+Array.prototype.splice.call(Java.type("java.util.HashMap"))
+Array.prototype.slice.call(Java.type("java.util.HashMap"))
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8047371.js Tue Jul 01 14:27:28 2014 -0700
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * JDK-8047371: local variable declaration in TypeEvaluator should use ScriptObject.addOwnProperty instead of .set
+ *
+ * @test
+ * @run
+ */
+
+print((function(){ var a=1; with({ get a() { return false } }) return a })());
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8047371.js.EXPECTED Tue Jul 01 14:27:28 2014 -0700
@@ -0,0 +1,1 @@
+false
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8047728.js Tue Jul 01 14:27:28 2014 -0700
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * JDK-8047728: (function(x){var o={x:0}; with(o){delete x} return o.x})() evaluates to 0 instead of undefined
+ *
+ * @test
+ * @run
+ */
+
+function func(x) {
+ var o = {x:0};
+ with(o){
+ delete x;
+ }
+ return o.x
+}
+
+if (typeof func() != 'undefined') {
+ fail("expected undefined from 'func' call");
+}
+
+function func2() {
+ var x;
+ var o = {x:0};
+ with(o){
+ delete x;
+ }
+ return o.x
+}
+
+if (typeof func2() != 'undefined') {
+ fail("expected undefined from 'func2' call");
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8047959.js Tue Jul 01 14:27:28 2014 -0700
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * JDK-8047959: bindings created for declarations in eval code are not mutable
+ *
+ * @test
+ * @run
+ */
+
+eval("var x=10;");
+print('delete x? ' + delete x);
+print('typeof x = ' + typeof x);
+
+eval("function f() {}");
+print('delete f? ' + delete f);
+print('typeof f = ' + typeof f);
+
+var foo = 223;
+print('delete foo? ' + delete foo);
+print('typeof foo = ' + typeof foo);
+
+function func() {}
+print('delete func? ' + delete func);
+print('typeof func = ' + typeof func);
+
+eval("var foo = 33;");
+print("delete foo? " + delete foo);
+print("typeof foo? " + typeof foo);
+print("foo = " + foo);
+
+var x = "global";
+(function(){
+ eval("var x='local'");
+ print("x in function = "+ x);
+ print("delete x? = " + delete x);
+ print("x after delete = " + x);
+})();
+print("x = " + x);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8047959.js.EXPECTED Tue Jul 01 14:27:28 2014 -0700
@@ -0,0 +1,15 @@
+delete x? false
+typeof x = number
+delete f? true
+typeof f = undefined
+delete foo? false
+typeof foo = number
+delete func? false
+typeof func = function
+delete foo? false
+typeof foo? number
+foo = 33
+x in function = local
+delete x? = true
+x after delete = global
+x = global
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8048071.js Tue Jul 01 14:27:28 2014 -0700
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * JDK-8048071: eval within 'with' statement does not use correct scope if with scope expression has a copy of eval
+ *
+ * @test
+ * @run
+ */
+
+function func() {
+ var x = 1;
+ with ({ eval: this.eval }) {
+ eval("var x = 23");
+ }
+
+ return x;
+}
+
+print(func());
+print("typeof x? " + typeof x);
+
+print((function(global){
+ var x = 1;
+ with(global) {
+ eval("eval('var x=0')");
+ }
+ return x;
+})(this));
+print("typeof x? " + typeof x);
+
+print((function(global){
+ var x = 1;
+ with({eval: global.eval}) {
+ eval("eval('var x=0')");
+ }
+ return x;
+})(this));
+print("typeof x? " + typeof x);
+
+// not-builtin eval cases
+
+(function () {
+ function eval(str) {
+ print("local eval called: " + str);
+ print(this);
+ }
+
+ with({}) {
+ eval("hello");
+ }
+})();
+
+(function () {
+ with({
+ eval:function(str) {
+ print("with's eval called: " + str);
+ print("this = " + this);
+ print("this.foo = " + this.foo);
+ },
+ foo: 42
+ }) {
+ eval("hello")
+ }
+})();
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8048071.js.EXPECTED Tue Jul 01 14:27:28 2014 -0700
@@ -0,0 +1,11 @@
+23
+typeof x? undefined
+0
+typeof x? undefined
+0
+typeof x? undefined
+local eval called: hello
+[object global]
+with's eval called: hello
+this = [object Object]
+this.foo = 42
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8048079_1.js Tue Jul 01 14:27:28 2014 -0700
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * JDK-8048079: Persistent code store is broken after optimistic types merge
+ *
+ * @test
+ * @run
+ * @option -pcc
+ * @option -Dnashorn.persistent.code.cache=build/nashorn_code_cache
+ * @fork
+ */
+
+load(__DIR__ + 'prototype.js');
+load(__DIR__ + 'yui.js');
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8048079_1.js.EXPECTED Tue Jul 01 14:27:28 2014 -0700
@@ -0,0 +1,3 @@
+parsed and compiled ok prototype.js
+parsed and compiled ok yui-min.js
+parsed and compiled ok yui.js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8048079_2.js Tue Jul 01 14:27:28 2014 -0700
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * JDK-8048079: Persistent code store is broken after optimistic types merge
+ *
+ * @test
+ * @run
+ * @option -pcc
+ * @option -Dnashorn.persistent.code.cache=build/nashorn_code_cache
+ * @fork
+ */
+
+load(__DIR__ + 'prototype.js');
+load(__DIR__ + 'yui.js');
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8048079_2.js.EXPECTED Tue Jul 01 14:27:28 2014 -0700
@@ -0,0 +1,3 @@
+parsed and compiled ok prototype.js
+parsed and compiled ok yui-min.js
+parsed and compiled ok yui.js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8048505.js Tue Jul 01 14:27:28 2014 -0700
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * Read fully parameter test
+ *
+ * @test
+ * @option -scripting
+ * @run
+ */
+
+var str = __FILE__;
+var first = readFully(str);
+print(typeof str);
+
+var str2 = __FILE__.substring(0,5);
+var str3 = __FILE__.substring(5);
+print(typeof str2);
+print(typeof str3);
+
+var cons = str2 + str3;
+print(typeof cons);
+
+var second = readFully(cons);
+
+var f = new java.io.File(str);
+print(typeof f);
+var third = readFully(f);
+
+print(first.length() == second.length());
+print(first.length() == third.length());
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8048505.js.EXPECTED Tue Jul 01 14:27:28 2014 -0700
@@ -0,0 +1,7 @@
+string
+string
+string
+string
+object
+true
+true
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8048586.js Tue Jul 01 14:27:28 2014 -0700
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * JDK-8048586: String concatenation with optimistic types is slow
+ *
+ * @test
+ * @run
+ */
+
+var body = '';
+
+for (var i = 0; i < 1024 * 1024; i++) {
+ body += 'hello world\n';
+}
+
+body = '';
+
+for (var i = 0; i < 1024 * 1024; i++) {
+ body = body + 'hello world\n';
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8048718.js Tue Jul 01 14:27:28 2014 -0700
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * JDK-8048718: JSON.parse('{"0":0, "64":0}') throws ArrayindexOutOfBoundsException
+ *
+ * @test
+ * @run
+ */
+
+var obj = JSON.parse('{"0":0, "64":0}');
+if ("1" in obj) {
+ fail("found element at index 1");
+}
+
+if ("63" in obj) {
+ fail("found element at index 63");
+}
+
+if (obj[0] != 0) {
+ fail("expected obj[0] to be 0");
+}
+
+if (obj[64] != 0) {
+ fail("expected obj[64] to be 0");
+}
+
+for (var i in obj) {
+ if (i != "0" && i != "64") {
+ fail("invalid property " + i);
+ }
+}
--- a/nashorn/test/src/jdk/nashorn/api/scripting/ScriptEngineTest.java Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/test/src/jdk/nashorn/api/scripting/ScriptEngineTest.java Tue Jul 01 14:27:28 2014 -0700
@@ -593,6 +593,20 @@
}
}
+ // @bug 8046013: TypeError: Cannot apply "with" to non script object
+ @Test
+ public void withOnMirrorTest() throws ScriptException {
+ final ScriptEngineManager m = new ScriptEngineManager();
+ final ScriptEngine e = m.getEngineByName("nashorn");
+
+ final Object obj = e.eval("({ foo: 'hello'})");
+ final Object[] arr = new Object[1];
+ arr[0] = obj;
+ e.put("arr", arr);
+ final Object res = e.eval("var res; with(arr[0]) { res = foo; }; res");
+ assertEquals(res, "hello");
+ }
+
private static void checkProperty(final ScriptEngine e, final String name)
throws ScriptException {
final String value = System.getProperty(name);
--- a/nashorn/test/src/jdk/nashorn/internal/runtime/CodeStoreAndPathTest.java Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/test/src/jdk/nashorn/internal/runtime/CodeStoreAndPathTest.java Tue Jul 01 14:27:28 2014 -0700
@@ -96,7 +96,7 @@
final String codeCache = "build/nashorn_code_cache";
final String oldUserDir = System.getProperty("user.dir");
- private static final String[] ENGINE_OPTIONS = new String[]{"--persistent-code-cache", "--optimistic-types=false", "--lazy-compilation=false"};
+ private static final String[] ENGINE_OPTIONS = new String[]{"--persistent-code-cache"};
public void checkCompiledScripts(final DirectoryStream<Path> stream, int numberOfScripts) throws IOException {
for (final Path file : stream) {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/src/jdk/nashorn/internal/runtime/ConsStringTest.java Tue Jul 01 14:27:28 2014 -0700
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.nashorn.internal.runtime;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+
+import jdk.nashorn.internal.runtime.JSType;
+import jdk.nashorn.internal.runtime.ScriptRuntime;
+import org.testng.annotations.Test;
+
+/**
+ * Tests for JSType methods.
+ *
+ * @test
+ * @run testng jdk.nashorn.internal.runtime.ConsStringTest
+ */
+public class ConsStringTest {
+
+ /**
+ * Test toString conversion
+ */
+ @Test
+ public void testConsStringToString() {
+ final ConsString cs1 = new ConsString("b", "c");
+ final ConsString cs2 = new ConsString("d", "e");
+ final ConsString cs3 = new ConsString(cs1, cs2);
+ final ConsString cs4 = new ConsString(cs3, "f");
+ final ConsString cs5 = new ConsString("a", cs4);
+ assertEquals(cs5.toString(), "abcdef");
+ assertEquals(cs4.toString(), "bcdef");
+ assertEquals(cs3.toString(), "bcde");
+ assertEquals(cs2.toString(), "de");
+ assertEquals(cs1.toString(), "bc");
+ // ConsStrings should be flattened now
+ assertEquals(cs1.getComponents()[0], "bc");
+ assertEquals(cs1.getComponents()[1], "");
+ assertEquals(cs2.getComponents()[0], "de");
+ assertEquals(cs2.getComponents()[1], "");
+ assertEquals(cs3.getComponents()[0], "bcde");
+ assertEquals(cs3.getComponents()[1], "");
+ assertEquals(cs4.getComponents()[0], "bcdef");
+ assertEquals(cs4.getComponents()[1], "");
+ assertEquals(cs5.getComponents()[0], "abcdef");
+ assertEquals(cs5.getComponents()[1], "");
+ }
+
+ /**
+ * Test charAt
+ */
+ @Test
+ public void testConsStringCharAt() {
+ final ConsString cs1 = new ConsString("b", "c");
+ final ConsString cs2 = new ConsString("d", "e");
+ final ConsString cs3 = new ConsString(cs1, cs2);
+ final ConsString cs4 = new ConsString(cs3, "f");
+ final ConsString cs5 = new ConsString("a", cs4);
+ assertEquals(cs1.charAt(1), 'c');
+ assertEquals(cs2.charAt(0), 'd');
+ assertEquals(cs3.charAt(3), 'e');
+ assertEquals(cs4.charAt(1), 'c');
+ assertEquals(cs5.charAt(2), 'c');
+ // ConsStrings should be flattened now
+ assertEquals(cs1.getComponents()[0], "bc");
+ assertEquals(cs1.getComponents()[1], "");
+ assertEquals(cs2.getComponents()[0], "de");
+ assertEquals(cs2.getComponents()[1], "");
+ assertEquals(cs3.getComponents()[0], "bcde");
+ assertEquals(cs3.getComponents()[1], "");
+ assertEquals(cs4.getComponents()[0], "bcdef");
+ assertEquals(cs4.getComponents()[1], "");
+ assertEquals(cs5.getComponents()[0], "abcdef");
+ assertEquals(cs5.getComponents()[1], "");
+ }
+
+
+ /**
+ * Test flattening of top-level and internal ConsStrings
+ */
+ @Test
+ public void testConsStringFlattening() {
+ final ConsString cs1 = new ConsString("b", "c");
+ final ConsString cs2 = new ConsString("d", "e");
+ final ConsString cs3 = new ConsString(cs1, cs2);
+ final ConsString cs4 = new ConsString(cs3, "f");
+
+ final ConsString cs5 = new ConsString("a", cs4);
+ // top-level ConsString should not yet be flattened
+ assert(cs5.getComponents()[0] == "a");
+ assert(cs5.getComponents()[1] == cs4);
+ assertEquals(cs5.toString(), "abcdef");
+ // top-level ConsString should be flattened
+ assertEquals(cs5.getComponents()[0], "abcdef");
+ assertEquals(cs5.getComponents()[1], "");
+ // internal ConsString should not yet be flattened after first traversal
+ assertEquals(cs4.getComponents()[0], cs3);
+ assertEquals(cs4.getComponents()[1], "f");
+
+ final ConsString cs6 = new ConsString("a", cs4);
+ // top-level ConsString should not yet be flattened
+ assertEquals(cs6.getComponents()[0], "a");
+ assertEquals(cs6.getComponents()[1], cs4);
+ assertEquals(cs6.toString(), "abcdef");
+ // top-level ConsString should be flattened
+ assertEquals(cs6.getComponents()[0], "abcdef");
+ assertEquals(cs6.getComponents()[1], "");
+ // internal ConsString should have been flattened after second traversal
+ assertEquals(cs4.getComponents()[0], "bcdef");
+ assertEquals(cs4.getComponents()[1], "");
+ }
+}