This commit is contained in:
Lana Steuck 2014-07-01 14:27:28 -07:00
commit 113a7f0c05
80 changed files with 2421 additions and 431 deletions

25
nashorn/bin/jjsdebug.sh Normal file
View 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 $*

View File

@ -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>

View File

@ -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>

View File

@ -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

View File

@ -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();

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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);
}
}

View File

@ -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) {

View File

@ -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;
}

View File

@ -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]));
}
/**

View File

@ -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

View File

@ -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);

View File

@ -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;
}

View File

@ -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;
}
}
}

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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) {

View File

@ -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;

View File

@ -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);
}

View File

@ -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
*/

View File

@ -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);

View File

@ -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();

View File

@ -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)) {

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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();
}
}

View 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();
}

View File

@ -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 ";
}

View File

@ -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;
}

View File

@ -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) {

View File

@ -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);
}
/**

View File

@ -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);

View File

@ -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) {

View File

@ -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();
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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
*

View File

@ -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();

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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()) {

View File

@ -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.

View File

@ -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

View File

@ -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) {

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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)

View 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();
}

View File

@ -0,0 +1,4 @@
f = 33
foo = 42
bar = hello
this.f = 33

View 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]]]]]]]]]))
}

View 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

View File

@ -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/])');

View 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{[]}");

View 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; } })());

View File

@ -0,0 +1,2 @@
undefined
undefined

View 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);
}
}

View 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"))

View 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 })());

View File

@ -0,0 +1 @@
false

View 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");
}

View 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);

View 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

View 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")
}
})();

View 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

View 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');

View File

@ -0,0 +1,3 @@
parsed and compiled ok prototype.js
parsed and compiled ok yui-min.js
parsed and compiled ok yui.js

View 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');

View File

@ -0,0 +1,3 @@
parsed and compiled ok prototype.js
parsed and compiled ok yui-min.js
parsed and compiled ok yui.js

View 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());

View File

@ -0,0 +1,7 @@
string
string
string
string
object
true
true

View 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';
}

View 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);
}
}

View File

@ -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);

View File

@ -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) {

View File

@ -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], "");
}
}