Merge
This commit is contained in:
commit
113a7f0c05
25
nashorn/bin/jjsdebug.sh
Normal file
25
nashorn/bin/jjsdebug.sh
Normal file
@ -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 $*
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -279,6 +279,7 @@ run.test.jvmargs.common=\
|
||||
-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
|
||||
|
||||
|
@ -152,7 +152,7 @@ class OverloadedMethod {
|
||||
|
||||
@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();
|
||||
|
@ -111,7 +111,7 @@ public class CompositeTypeBasedGuardingDynamicLinker implements TypeBasedGuardin
|
||||
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 @@ public class CompositeTypeBasedGuardingDynamicLinker implements TypeBasedGuardin
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("fallthrough")
|
||||
@Override
|
||||
protected List<TypeBasedGuardingDynamicLinker> computeValue(final Class<?> clazz) {
|
||||
List<TypeBasedGuardingDynamicLinker> list = NO_LINKER;
|
||||
|
@ -209,7 +209,7 @@ final class AssignSymbols extends NodeOperatorVisitor<LexicalContext> implements
|
||||
if (varNode.isFunctionDeclaration()) {
|
||||
symbol.setIsFunctionDeclaration();
|
||||
}
|
||||
return varNode.setName((IdentNode)ident.setSymbol(symbol));
|
||||
return varNode.setName(ident.setSymbol(symbol));
|
||||
}
|
||||
return varNode;
|
||||
}
|
||||
@ -217,7 +217,7 @@ final class AssignSymbols extends NodeOperatorVisitor<LexicalContext> implements
|
||||
}
|
||||
|
||||
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 class AssignSymbols extends NodeOperatorVisitor<LexicalContext> implements
|
||||
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 class AssignSymbols extends NodeOperatorVisitor<LexicalContext> implements
|
||||
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 @@ final class AssignSymbols extends NodeOperatorVisitor<LexicalContext> implements
|
||||
// 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);
|
||||
|
@ -145,6 +145,7 @@ import jdk.nashorn.internal.runtime.PropertyMap;
|
||||
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 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
* 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 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
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());
|
||||
@ -289,6 +294,14 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
return lc.getCurrentFunction().isStrict() ? callSiteFlags | CALLSITE_STRICT : callSiteFlags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Are we generating code for 'eval' code?
|
||||
* @return true if currently compiled code is 'eval' code.
|
||||
*/
|
||||
boolean isEvalCode() {
|
||||
return evalCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load an identity node
|
||||
*
|
||||
@ -1084,7 +1097,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
|
||||
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 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
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 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
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 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
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);
|
||||
|
@ -173,7 +173,18 @@ enum CompilationPhase {
|
||||
@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 @@ enum CompilationPhase {
|
||||
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 @@ enum CompilationPhase {
|
||||
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 @@ enum CompilationPhase {
|
||||
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 @@ enum CompilationPhase {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -46,10 +46,10 @@ import java.util.LinkedList;
|
||||
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;
|
||||
@ -122,6 +122,11 @@ public final class Compiler implements Loggable {
|
||||
*/
|
||||
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 @@ public final class Compiler implements Loggable {
|
||||
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 @@ public final class Compiler implements Loggable {
|
||||
* @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 @@ public final class Compiler implements Loggable {
|
||||
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 @@ public final class Compiler implements Loggable {
|
||||
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 @@ public final class Compiler implements Loggable {
|
||||
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 @@ public final class Compiler implements Loggable {
|
||||
time += (env.isTimingEnabled() ? phase.getEndTime() - phase.getStartTime() : 0L);
|
||||
}
|
||||
|
||||
if(typeInformationFile != null && !phases.isRestOfCompilation()) {
|
||||
OptimisticTypesPersistence.store(typeInformationFile, invalidatedProgramPoints);
|
||||
}
|
||||
|
||||
log.unindent();
|
||||
|
||||
if (info) {
|
||||
|
@ -62,6 +62,8 @@ public abstract class FieldObjectCreator<T> extends ObjectCreator<T> {
|
||||
|
||||
/** 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 @@ public abstract class FieldObjectCreator<T> extends ObjectCreator<T> {
|
||||
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 @@ public abstract class FieldObjectCreator<T> extends ObjectCreator<T> {
|
||||
@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;
|
||||
}
|
||||
|
||||
|
@ -141,7 +141,7 @@ public final class FunctionSignature {
|
||||
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]));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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.LabelNode;
|
||||
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.ReturnNode;
|
||||
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 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
|
||||
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 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
|
||||
}
|
||||
}
|
||||
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 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
|
||||
|
||||
@Override
|
||||
public boolean enterThrowNode(final ThrowNode throwNode) {
|
||||
if(!reachable) {
|
||||
return false;
|
||||
}
|
||||
|
||||
throwNode.getExpression().accept(this);
|
||||
jumpToCatchBlock(throwNode);
|
||||
doesNotContinueSequentially();
|
||||
@ -1027,6 +1039,15 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
|
||||
@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 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
|
||||
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 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
|
||||
} 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;
|
||||
}
|
||||
@ -1173,6 +1213,16 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
|
||||
return node;
|
||||
}
|
||||
|
||||
@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;
|
||||
@ -1207,10 +1257,10 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
|
||||
|
||||
@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
|
||||
|
@ -75,7 +75,6 @@ import jdk.nashorn.internal.parser.TokenType;
|
||||
import jdk.nashorn.internal.runtime.CodeInstaller;
|
||||
import jdk.nashorn.internal.runtime.Context;
|
||||
import jdk.nashorn.internal.runtime.JSType;
|
||||
import jdk.nashorn.internal.runtime.ScriptRuntime;
|
||||
import jdk.nashorn.internal.runtime.Source;
|
||||
import jdk.nashorn.internal.runtime.logging.DebugLogger;
|
||||
import jdk.nashorn.internal.runtime.logging.Loggable;
|
||||
@ -159,30 +158,6 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> implements Lo
|
||||
return context.getLogger(this.getClass());
|
||||
}
|
||||
|
||||
@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);
|
||||
|
@ -63,13 +63,13 @@ public class MapCreator<T> {
|
||||
/**
|
||||
* 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 @@ public class MapCreator<T> {
|
||||
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 @@ public class MapCreator<T> {
|
||||
|
||||
//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 @@ public class MapCreator<T> {
|
||||
*
|
||||
* @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 @@ public class MapCreator<T> {
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -307,7 +307,7 @@ final class Splitter extends NodeVisitor<LexicalContext> {
|
||||
units.add(new ArrayUnit(unit, lo, postsets.length));
|
||||
}
|
||||
|
||||
arrayLiteralNode.setUnits(units);
|
||||
return arrayLiteralNode.setUnits(lc, units);
|
||||
}
|
||||
|
||||
return literal;
|
||||
|
@ -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 @@ import jdk.nashorn.internal.runtime.ScriptRuntime;
|
||||
* 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 @@ final class TypeEvaluator {
|
||||
" 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -29,6 +29,7 @@ import java.lang.invoke.MethodType;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.NoSuchElementException;
|
||||
import jdk.nashorn.internal.codegen.types.Type;
|
||||
import jdk.nashorn.internal.ir.FunctionNode;
|
||||
import jdk.nashorn.internal.runtime.ScriptFunction;
|
||||
@ -61,6 +62,20 @@ public class TypeMap {
|
||||
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) {
|
||||
|
@ -173,7 +173,6 @@ final class WeighNodes extends NodeOperatorVisitor<LexicalContext> {
|
||||
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;
|
||||
|
@ -171,6 +171,8 @@ class ObjectType extends Type {
|
||||
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);
|
||||
}
|
||||
|
@ -416,6 +416,15 @@ public abstract class Type implements Comparable<Type>, BytecodeOps {
|
||||
return this.equals(Type.STRING);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
@ -799,6 +808,13 @@ public abstract class Type implements Comparable<Type>, BytecodeOps {
|
||||
*/
|
||||
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
|
||||
*/
|
||||
|
@ -41,7 +41,7 @@ import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
* 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 @@ public class Block extends Node implements BreakableNode, Flags<Block> {
|
||||
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);
|
||||
|
@ -36,7 +36,7 @@ import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
* 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 @@ public final class CaseNode extends Node implements JoinPredecessor, Labels {
|
||||
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();
|
||||
|
@ -56,11 +56,6 @@ public final class ExpressionStatement extends Statement {
|
||||
this.expression = expression;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTerminal() {
|
||||
return expression.isTerminal();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
|
||||
if (visitor.enterExpressionStatement(this)) {
|
||||
|
@ -110,7 +110,7 @@ public final class IdentNode extends Expression implements PropertyKey, Function
|
||||
* @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 @@ public final class IdentNode extends Expression implements PropertyKey, Function
|
||||
* @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 @@ public final class IdentNode extends Expression implements PropertyKey, Function
|
||||
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;
|
||||
|
@ -29,6 +29,7 @@ import java.util.Arrays;
|
||||
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;
|
||||
@ -86,6 +87,17 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey {
|
||||
this.value = newValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey {
|
||||
/**
|
||||
* 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 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey {
|
||||
}
|
||||
}
|
||||
|
||||
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 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey {
|
||||
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
|
||||
@ -781,6 +824,19 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey {
|
||||
* @return element type
|
||||
*/
|
||||
public Type getElementType() {
|
||||
assert !elementType.isUnknown();
|
||||
assert !elementType.isUnknown() : this + " has elementType=unknown";
|
||||
return elementType;
|
||||
}
|
||||
|
||||
@ -821,38 +877,28 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey {
|
||||
* @return post set indices
|
||||
*/
|
||||
public int[] getPostsets() {
|
||||
if(postsets == null) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
postsets = Arrays.copyOf(computed, nComputed);
|
||||
}
|
||||
assert postsets != null : this + " elementType=" + elementType + " has no postsets";
|
||||
return postsets;
|
||||
}
|
||||
|
||||
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[];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get presets constant array
|
||||
* @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 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey {
|
||||
|
||||
/**
|
||||
* 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 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey {
|
||||
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
|
||||
|
@ -143,15 +143,6 @@ public abstract class Node implements Cloneable {
|
||||
*/
|
||||
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
|
||||
@ -168,15 +159,6 @@ public abstract class Node implements Cloneable {
|
||||
this.finish = finish;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 @@ public abstract class Node implements Cloneable {
|
||||
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;
|
||||
|
@ -30,7 +30,7 @@ package jdk.nashorn.internal.ir;
|
||||
* 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 @@ public abstract class Statement extends Node {
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
|
37
nashorn/src/jdk/nashorn/internal/ir/Terminal.java
Normal file
37
nashorn/src/jdk/nashorn/internal/ir/Terminal.java
Normal file
@ -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();
|
||||
}
|
@ -38,7 +38,9 @@ import jdk.nashorn.internal.ir.Block;
|
||||
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 @@ public final class ASTWriter {
|
||||
|
||||
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 ";
|
||||
}
|
||||
|
||||
|
@ -443,7 +443,7 @@ public class NashornClassReader extends ClassReader {
|
||||
@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;
|
||||
}
|
||||
|
||||
|
@ -182,9 +182,9 @@ public final class PrintVisitor extends NodeVisitor<LexicalContext> {
|
||||
|
||||
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 @@ public final class PrintVisitor extends NodeVisitor<LexicalContext> {
|
||||
|
||||
statement.accept(this);
|
||||
|
||||
if (statement instanceof FunctionNode) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int lastIndex = sb.length() - 1;
|
||||
char lastChar = sb.charAt(lastIndex);
|
||||
while (Character.isWhitespace(lastChar) && lastIndex >= 0) {
|
||||
|
@ -883,7 +883,7 @@ public final class Global extends ScriptObject implements Scope {
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -909,6 +909,7 @@ public final class NativeDate extends ScriptObject {
|
||||
sb.append(n);
|
||||
}
|
||||
|
||||
@SuppressWarnings("fallthrough")
|
||||
private static String toStringImpl(final Object self, final int format) {
|
||||
final NativeDate nd = getNativeDate(self);
|
||||
|
||||
|
@ -716,6 +716,32 @@ public final class NativeObject {
|
||||
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) {
|
||||
|
@ -89,11 +89,12 @@ final class CompiledFunction {
|
||||
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 @@ final class CompiledFunction {
|
||||
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();
|
||||
}
|
||||
|
||||
|
@ -36,8 +36,12 @@ import java.util.Deque;
|
||||
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 @@ public final class ConsString implements CharSequence {
|
||||
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 @@ public final class ConsString implements CharSequence {
|
||||
|
||||
@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 @@ public final class ConsString implements CharSequence {
|
||||
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 @@ public final class ConsString implements CharSequence {
|
||||
|
||||
left = new String(chars);
|
||||
right = "";
|
||||
flat = true;
|
||||
state = STATE_FLATTENED;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -444,15 +444,11 @@ public final class Context {
|
||||
}
|
||||
|
||||
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 @@ public final class Context {
|
||||
* @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 @@ public final class Context {
|
||||
|
||||
for (final Object constant : constants) {
|
||||
if (constant instanceof RecompilableScriptFunctionData) {
|
||||
((RecompilableScriptFunctionData) constant).setCodeAndSource(installedClasses, source);
|
||||
((RecompilableScriptFunctionData) constant).initTransients(source, installer);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -130,6 +130,9 @@ public enum JSType {
|
||||
/** 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);
|
||||
|
||||
@ -487,6 +490,16 @@ public enum JSType {
|
||||
return toString(toPrimitive(obj));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
|
@ -28,7 +28,6 @@ package jdk.nashorn.internal.runtime;
|
||||
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.Collections;
|
||||
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.parser.TokenType;
|
||||
import jdk.nashorn.internal.runtime.logging.DebugLogger;
|
||||
import jdk.nashorn.internal.runtime.logging.Loggable;
|
||||
import jdk.nashorn.internal.runtime.logging.Logger;
|
||||
import jdk.nashorn.internal.scripts.JS;
|
||||
|
||||
/**
|
||||
* This is a subclass that represents a script function that may be regenerated,
|
||||
@ -83,9 +83,6 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
|
||||
/** 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 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
|
||||
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 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
|
||||
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 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
|
||||
}
|
||||
|
||||
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 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
|
||||
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 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
|
||||
* 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 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
|
||||
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 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
|
||||
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 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
|
||||
}
|
||||
|
||||
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 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
|
||||
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();
|
||||
|
@ -35,6 +35,7 @@ import java.lang.invoke.MethodHandle;
|
||||
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 @@ public abstract class ScriptFunction extends ScriptObject {
|
||||
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 @@ public abstract class ScriptFunction extends ScriptObject {
|
||||
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 @@ public abstract class ScriptFunction extends ScriptObject {
|
||||
}
|
||||
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 @@ public abstract class ScriptFunction extends ScriptObject {
|
||||
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);
|
||||
|
@ -691,6 +691,7 @@ public abstract class ScriptObject implements PropertyAccess {
|
||||
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 @@ public abstract class ScriptObject implements PropertyAccess {
|
||||
}
|
||||
|
||||
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 @@ public abstract class ScriptObject implements PropertyAccess {
|
||||
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;
|
||||
|
@ -27,6 +27,7 @@ package jdk.nashorn.internal.runtime;
|
||||
|
||||
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;
|
||||
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 @@ public final class ScriptRuntime {
|
||||
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 @@ public final class ScriptRuntime {
|
||||
|
||||
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);
|
||||
|
@ -107,8 +107,8 @@ public final class ScriptingFunctions {
|
||||
|
||||
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()) {
|
||||
|
@ -142,29 +142,34 @@ public final class Source implements Loggable {
|
||||
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 @@ public final class Source implements Loggable {
|
||||
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 @@ public final class Source implements Loggable {
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isEvalCode() {
|
||||
return evalCode;
|
||||
}
|
||||
}
|
||||
|
||||
private static class URLData implements Data {
|
||||
@ -287,10 +297,16 @@ public final class Source implements Loggable {
|
||||
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
|
||||
@ -368,6 +384,18 @@ public final class Source implements Loggable {
|
||||
return data.array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a Source instance
|
||||
*
|
||||
* @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
|
||||
*
|
||||
@ -377,7 +405,7 @@ public final class Source implements Loggable {
|
||||
* @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 @@ public final class Source implements Loggable {
|
||||
*
|
||||
* @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);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -554,6 +593,15 @@ public final class Source implements Loggable {
|
||||
return data.url();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
|
@ -201,7 +201,7 @@ public final class UserAccessorProperty extends SpillProperty {
|
||||
|
||||
@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 @@ public final class UserAccessorProperty extends SpillProperty {
|
||||
|
||||
@Override
|
||||
public void setValue(final ScriptObject self, final ScriptObject owner, final Object value, final boolean strict) {
|
||||
userAccessorSetter(getAccessors((owner != null) ? owner : (ScriptObject)self), strict ? getKey() : null, self, value);
|
||||
userAccessorSetter(getAccessors((owner != null) ? owner : self), strict ? getKey() : null, self, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -31,6 +31,8 @@ import java.lang.invoke.MethodHandle;
|
||||
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 @@ public final class WithObject extends ScriptObject implements Scope {
|
||||
|
||||
@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) {
|
||||
|
@ -150,7 +150,7 @@ final class ByteBufferArrayData extends ArrayData {
|
||||
|
||||
@Override
|
||||
public Object getObject(final int index) {
|
||||
return (int)(0x0ff & buf.get(index));
|
||||
return 0x0ff & buf.get(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -90,7 +90,7 @@ public final class InvokeByName {
|
||||
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);
|
||||
|
@ -151,6 +151,7 @@ range.error.invalid.precision=precision argument toPrecision() must be in [1, 21
|
||||
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
|
||||
|
@ -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)
|
||||
|
57
nashorn/test/script/basic/JDK-8046013.js
Normal file
57
nashorn/test/script/basic/JDK-8046013.js
Normal file
@ -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();
|
||||
}
|
4
nashorn/test/script/basic/JDK-8046013.js.EXPECTED
Normal file
4
nashorn/test/script/basic/JDK-8046013.js.EXPECTED
Normal file
@ -0,0 +1,4 @@
|
||||
f = 33
|
||||
foo = 42
|
||||
bar = hello
|
||||
this.f = 33
|
91
nashorn/test/script/basic/JDK-8046905.js
Normal file
91
nashorn/test/script/basic/JDK-8046905.js
Normal file
@ -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]]]]]]]]]))
|
||||
}
|
41
nashorn/test/script/basic/JDK-8046905.js.EXPECTED
Normal file
41
nashorn/test/script/basic/JDK-8046905.js.EXPECTED
Normal file
@ -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
|
@ -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 @@ function makeFuncExpectError(code, ErrorType) {
|
||||
}
|
||||
}
|
||||
|
||||
// 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/])');
|
||||
|
38
nashorn/test/script/basic/JDK-8047078.js
Normal file
38
nashorn/test/script/basic/JDK-8047078.js
Normal file
@ -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{[]}");
|
32
nashorn/test/script/basic/JDK-8047357.js
Normal file
32
nashorn/test/script/basic/JDK-8047357.js
Normal file
@ -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; } })());
|
2
nashorn/test/script/basic/JDK-8047357.js.EXPECTED
Normal file
2
nashorn/test/script/basic/JDK-8047357.js.EXPECTED
Normal file
@ -0,0 +1,2 @@
|
||||
undefined
|
||||
undefined
|
47
nashorn/test/script/basic/JDK-8047359.js
Normal file
47
nashorn/test/script/basic/JDK-8047359.js
Normal file
@ -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);
|
||||
}
|
||||
}
|
186
nashorn/test/script/basic/JDK-8047369.js
Normal file
186
nashorn/test/script/basic/JDK-8047369.js
Normal file
@ -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"))
|
32
nashorn/test/script/basic/JDK-8047371.js
Normal file
32
nashorn/test/script/basic/JDK-8047371.js
Normal file
@ -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 })());
|
||||
|
1
nashorn/test/script/basic/JDK-8047371.js.EXPECTED
Normal file
1
nashorn/test/script/basic/JDK-8047371.js.EXPECTED
Normal file
@ -0,0 +1 @@
|
||||
false
|
54
nashorn/test/script/basic/JDK-8047728.js
Normal file
54
nashorn/test/script/basic/JDK-8047728.js
Normal file
@ -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");
|
||||
}
|
59
nashorn/test/script/basic/JDK-8047959.js
Normal file
59
nashorn/test/script/basic/JDK-8047959.js
Normal file
@ -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);
|
15
nashorn/test/script/basic/JDK-8047959.js.EXPECTED
Normal file
15
nashorn/test/script/basic/JDK-8047959.js.EXPECTED
Normal file
@ -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
|
85
nashorn/test/script/basic/JDK-8048071.js
Normal file
85
nashorn/test/script/basic/JDK-8048071.js
Normal file
@ -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")
|
||||
}
|
||||
})();
|
11
nashorn/test/script/basic/JDK-8048071.js.EXPECTED
Normal file
11
nashorn/test/script/basic/JDK-8048071.js.EXPECTED
Normal file
@ -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
|
35
nashorn/test/script/basic/JDK-8048079_1.js
Normal file
35
nashorn/test/script/basic/JDK-8048079_1.js
Normal file
@ -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');
|
3
nashorn/test/script/basic/JDK-8048079_1.js.EXPECTED
Normal file
3
nashorn/test/script/basic/JDK-8048079_1.js.EXPECTED
Normal file
@ -0,0 +1,3 @@
|
||||
parsed and compiled ok prototype.js
|
||||
parsed and compiled ok yui-min.js
|
||||
parsed and compiled ok yui.js
|
35
nashorn/test/script/basic/JDK-8048079_2.js
Normal file
35
nashorn/test/script/basic/JDK-8048079_2.js
Normal file
@ -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');
|
3
nashorn/test/script/basic/JDK-8048079_2.js.EXPECTED
Normal file
3
nashorn/test/script/basic/JDK-8048079_2.js.EXPECTED
Normal file
@ -0,0 +1,3 @@
|
||||
parsed and compiled ok prototype.js
|
||||
parsed and compiled ok yui-min.js
|
||||
parsed and compiled ok yui.js
|
52
nashorn/test/script/basic/JDK-8048505.js
Normal file
52
nashorn/test/script/basic/JDK-8048505.js
Normal file
@ -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());
|
||||
|
7
nashorn/test/script/basic/JDK-8048505.js.EXPECTED
Normal file
7
nashorn/test/script/basic/JDK-8048505.js.EXPECTED
Normal file
@ -0,0 +1,7 @@
|
||||
string
|
||||
string
|
||||
string
|
||||
string
|
||||
object
|
||||
true
|
||||
true
|
41
nashorn/test/script/basic/JDK-8048586.js
Normal file
41
nashorn/test/script/basic/JDK-8048586.js
Normal file
@ -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';
|
||||
}
|
52
nashorn/test/script/basic/JDK-8048718.js
Normal file
52
nashorn/test/script/basic/JDK-8048718.js
Normal file
@ -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);
|
||||
}
|
||||
}
|
@ -593,6 +593,20 @@ public class ScriptEngineTest {
|
||||
}
|
||||
}
|
||||
|
||||
// @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);
|
||||
|
@ -96,7 +96,7 @@ public class CodeStoreAndPathTest {
|
||||
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) {
|
||||
|
@ -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], "");
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user