Mercurial > hg > release > icedtea8-forest-3.0 > nashorn
changeset 1634:f606c983f7fa
Merge
author | asaha |
---|---|
date | Tue, 06 Oct 2015 11:04:55 -0700 |
parents | 718b32884775 (current diff) f7c3d65076a0 (diff) |
children | 75bca5dc1e81 |
files | .hgtags test/src/jdk/nashorn/api/scripting/JSONCompatibleTest.java |
diffstat | 80 files changed, 2283 insertions(+), 799 deletions(-) [+] |
line wrap: on
line diff
--- a/.hgtags Mon Sep 28 15:36:24 2015 -0700 +++ b/.hgtags Tue Oct 06 11:04:55 2015 -0700 @@ -473,3 +473,4 @@ 9a3b86240761e602469c41bd720c7791997253e6 jdk8u66-b15 c0ce5c308f5e2c42ac0d2e7367355663312a3128 jdk8u66-b16 667e020da337e453eac8ecb9285c9b34a47e25fd jdk8u72-b00 +a105e7b0eff93895b82e3d372a63df4311d79821 jdk8u72-b01
--- a/make/build.xml Mon Sep 28 15:36:24 2015 -0700 +++ b/make/build.xml Tue Oct 06 11:04:55 2015 -0700 @@ -280,8 +280,8 @@ <javac srcdir="${test.src.dir}" destdir="${build.test.classes.dir}" classpath="${javac.test.classpath}" - source="${javac.source}" - target="${javac.target}" + source="${test.javac.source}" + target="${test.javac.target}" debug="${javac.debug}" encoding="${javac.encoding}" includeantruntime="false" fork="true">
--- a/make/project.properties Mon Sep 28 15:36:24 2015 -0700 +++ b/make/project.properties Tue Oct 06 11:04:55 2015 -0700 @@ -30,6 +30,8 @@ build.compiler=modern javac.source=1.7 javac.target=1.7 +test.javac.source=1.8 +test.javac.target=1.8 # nashorn version information nashorn.version=0.1
--- a/src/jdk/nashorn/api/scripting/NashornScriptEngine.java Mon Sep 28 15:36:24 2015 -0700 +++ b/src/jdk/nashorn/api/scripting/NashornScriptEngine.java Tue Oct 06 11:04:55 2015 -0700 @@ -140,7 +140,7 @@ this._global_per_engine = nashornContext.getEnv()._global_per_engine; // create new global object - this.global = createNashornGlobal(context); + this.global = createNashornGlobal(); // set the default ENGINE_SCOPE object for the default context context.setBindings(new ScriptObjectMirror(global, global), ScriptContext.ENGINE_SCOPE); } @@ -167,7 +167,7 @@ // We use same 'global' for all Bindings. return new SimpleBindings(); } - return createGlobalMirror(null); + return createGlobalMirror(); } // Compilable methods @@ -317,7 +317,7 @@ // We didn't find associated nashorn global mirror in the Bindings given! // Create new global instance mirror and associate with the Bindings. - final ScriptObjectMirror mirror = createGlobalMirror(ctxt); + final ScriptObjectMirror mirror = createGlobalMirror(); bindings.put(NASHORN_GLOBAL, mirror); return mirror.getHomeGlobal(); } @@ -333,13 +333,13 @@ } // Create a new ScriptObjectMirror wrapping a newly created Nashorn Global object - private ScriptObjectMirror createGlobalMirror(final ScriptContext ctxt) { - final Global newGlobal = createNashornGlobal(ctxt); + private ScriptObjectMirror createGlobalMirror() { + final Global newGlobal = createNashornGlobal(); return new ScriptObjectMirror(newGlobal, newGlobal); } // Create a new Nashorn Global object - private Global createNashornGlobal(final ScriptContext ctxt) { + private Global createNashornGlobal() { final Global newGlobal = AccessController.doPrivileged(new PrivilegedAction<Global>() { @Override public Global run() { @@ -354,7 +354,7 @@ } }, CREATE_GLOBAL_ACC_CTXT); - nashornContext.initGlobal(newGlobal, this, ctxt); + nashornContext.initGlobal(newGlobal, this); return newGlobal; }
--- a/src/jdk/nashorn/internal/codegen/ApplySpecialization.java Mon Sep 28 15:36:24 2015 -0700 +++ b/src/jdk/nashorn/internal/codegen/ApplySpecialization.java Tue Oct 06 11:04:55 2015 -0700 @@ -41,9 +41,8 @@ import jdk.nashorn.internal.ir.Expression; import jdk.nashorn.internal.ir.FunctionNode; import jdk.nashorn.internal.ir.IdentNode; -import jdk.nashorn.internal.ir.LexicalContext; import jdk.nashorn.internal.ir.Node; -import jdk.nashorn.internal.ir.visitor.NodeVisitor; +import jdk.nashorn.internal.ir.visitor.SimpleNodeVisitor; import jdk.nashorn.internal.objects.Global; import jdk.nashorn.internal.runtime.Context; import jdk.nashorn.internal.runtime.logging.DebugLogger; @@ -81,7 +80,7 @@ */ @Logger(name="apply2call") -public final class ApplySpecialization extends NodeVisitor<LexicalContext> implements Loggable { +public final class ApplySpecialization extends SimpleNodeVisitor implements Loggable { private static final boolean USE_APPLY2CALL = Options.getBooleanProperty("nashorn.apply2call", true); @@ -105,7 +104,6 @@ * @param compiler compiler */ public ApplySpecialization(final Compiler compiler) { - super(new LexicalContext()); this.compiler = compiler; this.log = initLogger(compiler.getContext()); } @@ -138,7 +136,7 @@ private boolean hasApplies(final FunctionNode functionNode) { try { - functionNode.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { + functionNode.accept(new SimpleNodeVisitor() { @Override public boolean enterFunctionNode(final FunctionNode fn) { return fn == functionNode; @@ -172,7 +170,7 @@ final Deque<Set<Expression>> stack = new ArrayDeque<>(); //ensure that arguments is only passed as arg to apply - functionNode.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { + functionNode.accept(new SimpleNodeVisitor() { private boolean isCurrentArg(final Expression expr) { return !stack.isEmpty() && stack.peek().contains(expr); //args to current apply call
--- a/src/jdk/nashorn/internal/codegen/AssignSymbols.java Mon Sep 28 15:36:24 2015 -0700 +++ b/src/jdk/nashorn/internal/codegen/AssignSymbols.java Tue Oct 06 11:04:55 2015 -0700 @@ -67,14 +67,12 @@ import jdk.nashorn.internal.ir.FunctionNode; import jdk.nashorn.internal.ir.IdentNode; import jdk.nashorn.internal.ir.IndexNode; -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.LiteralNode.ArrayLiteralNode.ArrayUnit; import jdk.nashorn.internal.ir.Node; import jdk.nashorn.internal.ir.RuntimeNode; import jdk.nashorn.internal.ir.RuntimeNode.Request; +import jdk.nashorn.internal.ir.Splittable; import jdk.nashorn.internal.ir.Statement; import jdk.nashorn.internal.ir.SwitchNode; import jdk.nashorn.internal.ir.Symbol; @@ -82,7 +80,7 @@ import jdk.nashorn.internal.ir.UnaryNode; import jdk.nashorn.internal.ir.VarNode; import jdk.nashorn.internal.ir.WithNode; -import jdk.nashorn.internal.ir.visitor.NodeVisitor; +import jdk.nashorn.internal.ir.visitor.SimpleNodeVisitor; import jdk.nashorn.internal.parser.TokenType; import jdk.nashorn.internal.runtime.Context; import jdk.nashorn.internal.runtime.ECMAErrors; @@ -103,7 +101,7 @@ * visitor. */ @Logger(name="symbols") -final class AssignSymbols extends NodeVisitor<LexicalContext> implements Loggable { +final class AssignSymbols extends SimpleNodeVisitor implements Loggable { private final DebugLogger log; private final boolean debug; @@ -151,7 +149,6 @@ private final boolean isOnDemand; public AssignSymbols(final Compiler compiler) { - super(new LexicalContext()); this.compiler = compiler; this.log = initLogger(compiler.getContext()); this.debug = log.isEnabled(); @@ -188,7 +185,7 @@ */ private void acceptDeclarations(final FunctionNode functionNode, final Block body) { // This visitor will assign symbol to all declared variables. - body.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { + body.accept(new SimpleNodeVisitor() { @Override protected boolean enterDefault(final Node node) { // Don't bother visiting expressions; var is a statement, it can't be inside an expression. @@ -984,7 +981,7 @@ boolean previousWasBlock = false; for (final Iterator<LexicalContextNode> it = lc.getAllNodes(); it.hasNext();) { final LexicalContextNode node = it.next(); - if (node instanceof FunctionNode || isSplitArray(node)) { + if (node instanceof FunctionNode || isSplitLiteral(node)) { // We reached the function boundary or a splitting boundary without seeing a definition for the symbol. // It needs to be in scope. return true; @@ -1010,12 +1007,8 @@ throw new AssertionError(); } - private static boolean isSplitArray(final LexicalContextNode expr) { - if(!(expr instanceof ArrayLiteralNode)) { - return false; - } - final List<ArrayUnit> units = ((ArrayLiteralNode)expr).getUnits(); - return !(units == null || units.isEmpty()); + private static boolean isSplitLiteral(final LexicalContextNode expr) { + return expr instanceof Splittable && ((Splittable) expr).getSplitRanges() != null; } private void throwUnprotectedSwitchError(final VarNode varNode) {
--- a/src/jdk/nashorn/internal/codegen/CacheAst.java Mon Sep 28 15:36:24 2015 -0700 +++ b/src/jdk/nashorn/internal/codegen/CacheAst.java Tue Oct 06 11:04:55 2015 -0700 @@ -29,19 +29,17 @@ import java.util.Collections; import java.util.Deque; import jdk.nashorn.internal.ir.FunctionNode; -import jdk.nashorn.internal.ir.LexicalContext; import jdk.nashorn.internal.ir.Node; import jdk.nashorn.internal.ir.Statement; -import jdk.nashorn.internal.ir.visitor.NodeVisitor; +import jdk.nashorn.internal.ir.visitor.SimpleNodeVisitor; import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData; -class CacheAst extends NodeVisitor<LexicalContext> { +class CacheAst extends SimpleNodeVisitor { private final Deque<RecompilableScriptFunctionData> dataStack = new ArrayDeque<>(); private final Compiler compiler; CacheAst(final Compiler compiler) { - super(new LexicalContext()); this.compiler = compiler; assert !compiler.isOnDemandCompilation(); }
--- a/src/jdk/nashorn/internal/codegen/CodeGenerator.java Mon Sep 28 15:36:24 2015 -0700 +++ b/src/jdk/nashorn/internal/codegen/CodeGenerator.java Tue Oct 06 11:04:55 2015 -0700 @@ -105,7 +105,6 @@ import jdk.nashorn.internal.ir.LexicalContextNode; import jdk.nashorn.internal.ir.LiteralNode; import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode; -import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode.ArrayUnit; import jdk.nashorn.internal.ir.LiteralNode.PrimitiveLiteralNode; import jdk.nashorn.internal.ir.LocalVariableConversion; import jdk.nashorn.internal.ir.LoopNode; @@ -118,6 +117,7 @@ import jdk.nashorn.internal.ir.RuntimeNode.Request; import jdk.nashorn.internal.ir.SetSplitState; import jdk.nashorn.internal.ir.SplitReturn; +import jdk.nashorn.internal.ir.Splittable; import jdk.nashorn.internal.ir.Statement; import jdk.nashorn.internal.ir.SwitchNode; import jdk.nashorn.internal.ir.Symbol; @@ -129,7 +129,7 @@ import jdk.nashorn.internal.ir.WhileNode; import jdk.nashorn.internal.ir.WithNode; import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor; -import jdk.nashorn.internal.ir.visitor.NodeVisitor; +import jdk.nashorn.internal.ir.visitor.SimpleNodeVisitor; import jdk.nashorn.internal.objects.Global; import jdk.nashorn.internal.parser.Lexer.RegexToken; import jdk.nashorn.internal.parser.TokenType; @@ -242,7 +242,7 @@ private final DebugLogger log; /** From what size should we use spill instead of fields for JavaScript objects? */ - private static final int OBJECT_SPILL_THRESHOLD = Options.getIntProperty("nashorn.spill.threshold", 256); + static final int OBJECT_SPILL_THRESHOLD = Options.getIntProperty("nashorn.spill.threshold", 256); private final Set<String> emittedMethods = new HashSet<>(); @@ -1433,8 +1433,7 @@ final Block currentBlock = lc.getCurrentBlock(); final CodeGeneratorLexicalContext codegenLexicalContext = lc; - function.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { - + function.accept(new SimpleNodeVisitor() { private MethodEmitter sharedScopeCall(final IdentNode identNode, final int flags) { final Symbol symbol = identNode.getSymbol(); final boolean isFastScope = isFastScope(symbol); @@ -1634,7 +1633,7 @@ @Override void consumeStack() { - dynamicCall(2 + argsCount, getCallSiteFlags(), origCallee.getName()); + dynamicCall(2 + argsCount, getCallSiteFlags(), null); } }.emit(); return false; @@ -2234,73 +2233,33 @@ * * @param arrayLiteralNode the array of contents * @param arrayType the type of the array, e.g. ARRAY_NUMBER or ARRAY_OBJECT - * - * @return the method generator that was used */ - private MethodEmitter loadArray(final ArrayLiteralNode arrayLiteralNode, final ArrayType arrayType) { + private void loadArray(final ArrayLiteralNode arrayLiteralNode, final ArrayType arrayType) { assert arrayType == Type.INT_ARRAY || arrayType == Type.LONG_ARRAY || arrayType == Type.NUMBER_ARRAY || arrayType == Type.OBJECT_ARRAY; - final Expression[] nodes = arrayLiteralNode.getValue(); - final Object presets = arrayLiteralNode.getPresets(); - final int[] postsets = arrayLiteralNode.getPostsets(); - final Class<?> type = arrayType.getTypeClass(); - final List<ArrayUnit> units = arrayLiteralNode.getUnits(); + final Expression[] nodes = arrayLiteralNode.getValue(); + final Object presets = arrayLiteralNode.getPresets(); + final int[] postsets = arrayLiteralNode.getPostsets(); + final List<Splittable.SplitRange> ranges = arrayLiteralNode.getSplitRanges(); loadConstant(presets); final Type elementType = arrayType.getElementType(); - if (units != null) { - final MethodEmitter savedMethod = method; - final FunctionNode currentFunction = lc.getCurrentFunction(); - - for (final ArrayUnit arrayUnit : units) { - unit = lc.pushCompileUnit(arrayUnit.getCompileUnit()); - - final String className = unit.getUnitClassName(); - assert unit != null; - final String name = currentFunction.uniqueName(SPLIT_PREFIX.symbolName()); - final String signature = methodDescriptor(type, ScriptFunction.class, Object.class, ScriptObject.class, type); - - pushMethodEmitter(unit.getClassEmitter().method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), name, signature)); - - method.setFunctionNode(currentFunction); - method.begin(); - - defineCommonSplitMethodParameters(); - defineSplitMethodParameter(CompilerConstants.SPLIT_ARRAY_ARG.slot(), arrayType); - - // NOTE: when this is no longer needed, SplitIntoFunctions will no longer have to add IS_SPLIT - // to synthetic functions, and FunctionNode.needsCallee() will no longer need to test for isSplit(). - final int arraySlot = fixScopeSlot(currentFunction, 3); - - lc.enterSplitNode(); - - for (int i = arrayUnit.getLo(); i < arrayUnit.getHi(); i++) { - method.load(arrayType, arraySlot); - storeElement(nodes, elementType, postsets[i]); + if (ranges != null) { + + loadSplitLiteral(new SplitLiteralCreator() { + @Override + public void populateRange(final MethodEmitter method, final Type type, final int slot, final int start, final int end) { + for (int i = start; i < end; i++) { + method.load(type, slot); + storeElement(nodes, elementType, postsets[i]); + } + method.load(type, slot); } - - method.load(arrayType, arraySlot); - method._return(); - lc.exitSplitNode(); - method.end(); - lc.releaseSlots(); - popMethodEmitter(); - - assert method == savedMethod; - method.loadCompilerConstant(CALLEE); - method.swap(); - method.loadCompilerConstant(THIS); - method.swap(); - method.loadCompilerConstant(SCOPE); - method.swap(); - method.invokestatic(className, name, signature); - - unit = lc.popCompileUnit(unit); - } - - return method; + }, ranges, arrayType); + + return; } if(postsets.length > 0) { @@ -2312,7 +2271,6 @@ } method.load(arrayType, arraySlot); } - return method; } private void storeElement(final Expression[] nodes, final Type elementType, final int index) { @@ -2502,7 +2460,7 @@ @Override public Boolean get() { - value.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { + value.accept(new SimpleNodeVisitor() { @Override public boolean enterFunctionNode(final FunctionNode functionNode) { return false; @@ -2537,6 +2495,7 @@ final List<MapTuple<Expression>> tuples = new ArrayList<>(); final List<PropertyNode> gettersSetters = new ArrayList<>(); final int ccp = getCurrentContinuationEntryPoint(); + final List<Splittable.SplitRange> ranges = objectNode.getSplitRanges(); Expression protoNode = null; boolean restOfProperty = false; @@ -2583,7 +2542,13 @@ loadExpressionAsType(node, type); }}; } - oc.makeObject(method); + + if (ranges != null) { + oc.createObject(method); + loadSplitLiteral(oc, ranges, Type.typeFor(oc.getAllocatorClass())); + } else { + oc.makeObject(method); + } //if this is a rest of method and our continuation point was found as one of the values //in the properties above, we need to reset the map to oc.getMap() in the continuation @@ -2833,7 +2798,7 @@ boolean contains; @Override public Boolean get() { - rootExpr.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { + rootExpr.accept(new SimpleNodeVisitor() { @Override public boolean enterFunctionNode(final FunctionNode functionNode) { return false; @@ -2899,6 +2864,54 @@ method.onLocalStore(type, slot); } + private void loadSplitLiteral(final SplitLiteralCreator creator, final List<Splittable.SplitRange> ranges, final Type literalType) { + assert ranges != null; + + // final Type literalType = Type.typeFor(literalClass); + final MethodEmitter savedMethod = method; + final FunctionNode currentFunction = lc.getCurrentFunction(); + + for (final Splittable.SplitRange splitRange : ranges) { + unit = lc.pushCompileUnit(splitRange.getCompileUnit()); + + assert unit != null; + final String className = unit.getUnitClassName(); + final String name = currentFunction.uniqueName(SPLIT_PREFIX.symbolName()); + final Class<?> clazz = literalType.getTypeClass(); + final String signature = methodDescriptor(clazz, ScriptFunction.class, Object.class, ScriptObject.class, clazz); + + pushMethodEmitter(unit.getClassEmitter().method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), name, signature)); + + method.setFunctionNode(currentFunction); + method.begin(); + + defineCommonSplitMethodParameters(); + defineSplitMethodParameter(CompilerConstants.SPLIT_ARRAY_ARG.slot(), literalType); + + // NOTE: when this is no longer needed, SplitIntoFunctions will no longer have to add IS_SPLIT + // to synthetic functions, and FunctionNode.needsCallee() will no longer need to test for isSplit(). + final int literalSlot = fixScopeSlot(currentFunction, 3); + + lc.enterSplitNode(); + + creator.populateRange(method, literalType, literalSlot, splitRange.getLow(), splitRange.getHigh()); + + method._return(); + lc.exitSplitNode(); + method.end(); + lc.releaseSlots(); + popMethodEmitter(); + + assert method == savedMethod; + method.loadCompilerConstant(CALLEE).swap(); + method.loadCompilerConstant(THIS).swap(); + method.loadCompilerConstant(SCOPE).swap(); + method.invokestatic(className, name, signature); + + unit = lc.popCompileUnit(unit); + } + } + private int fixScopeSlot(final FunctionNode functionNode, final int extraSlot) { // TODO hack to move the scope to the expected slot (needed because split methods reuse the same slots as the root method) final int actualScopeSlot = functionNode.compilerConstant(SCOPE).getSlot(SCOPE_TYPE); @@ -4333,7 +4346,7 @@ * on the stack throughout the store and used at the end to execute it */ - target.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { + target.accept(new SimpleNodeVisitor() { @Override public boolean enterIdentNode(final IdentNode node) { if (node.getSymbol().isScope()) { @@ -4432,7 +4445,7 @@ * need to do a conversion on non-equivalent types exists, but is * very rare. See for example test/script/basic/access-specializer.js */ - target.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { + target.accept(new SimpleNodeVisitor() { @Override protected boolean enterDefault(final Node node) { throw new AssertionError("Unexpected node " + node + " in store epilogue"); @@ -5461,4 +5474,21 @@ method.uncheckedGoto(targetCatchLabel); } } + + /** + * Interface implemented by object creators that support splitting over multiple methods. + */ + interface SplitLiteralCreator { + /** + * Generate code to populate a range of the literal object. A reference to the object + * should be left on the stack when the method terminates. + * + * @param method the method emitter + * @param type the type of the literal object + * @param slot the local slot containing the literal object + * @param start the start index (inclusive) + * @param end the end index (exclusive) + */ + void populateRange(MethodEmitter method, Type type, int slot, int start, int end); + } }
--- a/src/jdk/nashorn/internal/codegen/CompilationPhase.java Mon Sep 28 15:36:24 2015 -0700 +++ b/src/jdk/nashorn/internal/codegen/CompilationPhase.java Tue Oct 06 11:04:55 2015 -0700 @@ -36,13 +36,13 @@ import jdk.nashorn.internal.codegen.Compiler.CompilationPhases; import jdk.nashorn.internal.ir.Block; import jdk.nashorn.internal.ir.FunctionNode; -import jdk.nashorn.internal.ir.LexicalContext; import jdk.nashorn.internal.ir.LiteralNode; import jdk.nashorn.internal.ir.Node; import jdk.nashorn.internal.ir.Symbol; import jdk.nashorn.internal.ir.debug.ASTWriter; import jdk.nashorn.internal.ir.debug.PrintVisitor; import jdk.nashorn.internal.ir.visitor.NodeVisitor; +import jdk.nashorn.internal.ir.visitor.SimpleNodeVisitor; import jdk.nashorn.internal.runtime.CodeInstaller; import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData; import jdk.nashorn.internal.runtime.ScriptEnvironment; @@ -118,7 +118,7 @@ FunctionNode newFunctionNode; //ensure elementTypes, postsets and presets exist for splitter and arraynodes - newFunctionNode = transformFunction(fn, new NodeVisitor<LexicalContext>(new LexicalContext()) { + newFunctionNode = transformFunction(fn, new SimpleNodeVisitor() { @Override public LiteralNode<?> leaveLiteralNode(final LiteralNode<?> literalNode) { return literalNode.initialize(lc); @@ -217,7 +217,7 @@ // correctness, it's just an optimization -- runtime type calculation is not used when the compilation // is not an on-demand optimistic compilation, so we can skip locals marking then. if (compiler.useOptimisticTypes() && compiler.isOnDemandCompilation()) { - fn.getBody().accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { + fn.getBody().accept(new SimpleNodeVisitor() { @Override public boolean enterFunctionNode(final FunctionNode functionNode) { // OTOH, we must not declare symbols from nested functions to be locals. As we're doing on-demand @@ -483,8 +483,8 @@ Class<?> rootClass = null; long length = 0L; - final CodeInstaller<ScriptEnvironment> codeInstaller = compiler.getCodeInstaller(); - final Map<String, byte[]> bytecode = compiler.getBytecode(); + final CodeInstaller codeInstaller = compiler.getCodeInstaller(); + final Map<String, byte[]> bytecode = compiler.getBytecode(); for (final Entry<String, byte[]> entry : bytecode.entrySet()) { final String className = entry.getKey();
--- a/src/jdk/nashorn/internal/codegen/Compiler.java Mon Sep 28 15:36:24 2015 -0700 +++ b/src/jdk/nashorn/internal/codegen/Compiler.java Tue Oct 06 11:04:55 2015 -0700 @@ -101,7 +101,7 @@ private final ConstantData constantData; - private final CodeInstaller<ScriptEnvironment> installer; + private final CodeInstaller installer; /** logger for compiler, trampolines and related code generation events * that affect classes */ @@ -351,47 +351,83 @@ private static final AtomicInteger COMPILATION_ID = new AtomicInteger(0); /** - * Constructor + * Creates a new compiler instance for initial compilation of a script. * - * @param context context - * @param env script environment * @param installer code installer * @param source source to compile * @param errors error manager * @param isStrict is this a strict compilation + * @return a new compiler */ - public Compiler( - final Context context, - final ScriptEnvironment env, - final CodeInstaller<ScriptEnvironment> installer, + public static Compiler forInitialCompilation( + final CodeInstaller installer, final Source source, final ErrorManager errors, final boolean isStrict) { - this(context, env, installer, source, errors, isStrict, false, null, null, null, null, null, null); + return new Compiler(installer.getContext(), installer, source, errors, isStrict); } /** - * Constructor + * Creates a compiler without a code installer. It can only be used to compile code, not install the + * generated classes and as such it is useful only for implementation of {@code --compile-only} command + * line option. + * @param context the current context + * @param source source to compile + * @param isStrict is this a strict compilation + * @return a new compiler + */ + public static Compiler forNoInstallerCompilation( + final Context context, + final Source source, + final boolean isStrict) { + return new Compiler(context, null, source, context.getErrorManager(), isStrict); + } + + /** + * Creates a compiler for an on-demand compilation job. * - * @param context context - * @param env script environment * @param installer code installer * @param source source to compile - * @param errors error manager * @param isStrict is this a strict compilation - * @param isOnDemand is this an on demand compilation * @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} + * @return a new compiler */ - @SuppressWarnings("unused") - public Compiler( + public static Compiler forOnDemandCompilation( + final CodeInstaller installer, + final Source source, + final boolean isStrict, + final RecompilableScriptFunctionData compiledFunction, + final TypeMap types, + final Map<Integer, Type> invalidatedProgramPoints, + final Object typeInformationFile, + final int[] continuationEntryPoints, + final ScriptObject runtimeScope) { + final Context context = installer.getContext(); + return new Compiler(context, installer, source, context.getErrorManager(), isStrict, true, + compiledFunction, types, invalidatedProgramPoints, typeInformationFile, + continuationEntryPoints, runtimeScope); + } + + /** + * Convenience constructor for non on-demand compiler instances. + */ + private Compiler( final Context context, - final ScriptEnvironment env, - final CodeInstaller<ScriptEnvironment> installer, + final CodeInstaller installer, + final Source source, + final ErrorManager errors, + final boolean isStrict) { + this(context, installer, source, errors, isStrict, false, null, null, null, null, null, null); + } + + private Compiler( + final Context context, + final CodeInstaller installer, final Source source, final ErrorManager errors, final boolean isStrict, @@ -403,7 +439,7 @@ final int[] continuationEntryPoints, final ScriptObject runtimeScope) { this.context = context; - this.env = env; + this.env = context.getEnv(); this.installer = installer; this.constantData = new ConstantData(); this.compileUnits = CompileUnit.createCompileUnitSet(); @@ -425,7 +461,7 @@ this.optimistic = env._optimistic_types; } - private static String safeSourceName(final ScriptEnvironment env, final CodeInstaller<ScriptEnvironment> installer, final Source source) { + private String safeSourceName() { String baseName = new File(source.getName()).getName(); final int index = baseName.lastIndexOf(".js"); @@ -484,7 +520,7 @@ sb.append('$'); } - sb.append(Compiler.safeSourceName(env, installer, source)); + sb.append(safeSourceName()); return sb.toString(); } @@ -683,7 +719,7 @@ return constantData; } - CodeInstaller<ScriptEnvironment> getCodeInstaller() { + CodeInstaller getCodeInstaller() { return installer; }
--- a/src/jdk/nashorn/internal/codegen/ConstantData.java Mon Sep 28 15:36:24 2015 -0700 +++ b/src/jdk/nashorn/internal/codegen/ConstantData.java Tue Oct 06 11:04:55 2015 -0700 @@ -30,6 +30,8 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; + import jdk.nashorn.internal.runtime.PropertyMap; /** @@ -120,7 +122,7 @@ private final int hashCode; public PropertyMapWrapper(final PropertyMap map) { - this.hashCode = Arrays.hashCode(map.getProperties()); + this.hashCode = Arrays.hashCode(map.getProperties()) + 31 * Objects.hashCode(map.getClassName()); this.propertyMap = map; } @@ -131,8 +133,13 @@ @Override public boolean equals(final Object other) { - return other instanceof PropertyMapWrapper && - Arrays.equals(propertyMap.getProperties(), ((PropertyMapWrapper) other).propertyMap.getProperties()); + if (!(other instanceof PropertyMapWrapper)) { + return false; + } + final PropertyMap otherMap = ((PropertyMapWrapper) other).propertyMap; + return propertyMap == otherMap + || (Arrays.equals(propertyMap.getProperties(), otherMap.getProperties()) + && Objects.equals(propertyMap.getClassName(), otherMap.getClassName())); } }
--- a/src/jdk/nashorn/internal/codegen/FieldObjectCreator.java Mon Sep 28 15:36:24 2015 -0700 +++ b/src/jdk/nashorn/internal/codegen/FieldObjectCreator.java Tue Oct 06 11:04:55 2015 -0700 @@ -34,7 +34,6 @@ import static jdk.nashorn.internal.runtime.arrays.ArrayIndex.getArrayIndex; import static jdk.nashorn.internal.runtime.arrays.ArrayIndex.isValidArrayIndex; -import java.util.Iterator; import java.util.List; import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.ir.Symbol; @@ -91,27 +90,20 @@ findClass(); } - /** - * Construct an object. - * - * @param method the method emitter - */ @Override - protected void makeObject(final MethodEmitter method) { + public void createObject(final MethodEmitter method) { makeMap(); final String className = getClassName(); - try { - // NOTE: we must load the actual structure class here, because the API operates with Nashorn Type objects, - // and Type objects need a loaded class, for better or worse. We also have to be specific and use the type - // of the actual structure class, we can't generalize it to e.g. Type.typeFor(ScriptObject.class) as the - // exact type information is needed for generating continuations in rest-of methods. If we didn't do this, - // object initializers like { x: arr[i] } would fail during deoptimizing compilation on arr[i], as the - // values restored from the RewriteException would be cast to "ScriptObject" instead of to e.g. "JO4", and - // subsequently the "PUTFIELD J04.L0" instruction in the continuation code would fail bytecode verification. - method._new(Context.forStructureClass(className.replace('/', '.'))).dup(); - } catch (final ClassNotFoundException e) { - throw new AssertionError(e); - } + // NOTE: we must load the actual structure class here, because the API operates with Nashorn Type objects, + // and Type objects need a loaded class, for better or worse. We also have to be specific and use the type + // of the actual structure class, we can't generalize it to e.g. Type.typeFor(ScriptObject.class) as the + // exact type information is needed for generating continuations in rest-of methods. If we didn't do this, + // object initializers like { x: arr[i] } would fail during deoptimizing compilation on arr[i], as the + // values restored from the RewriteException would be cast to "ScriptObject" instead of to e.g. "JO4", and + // subsequently the "PUTFIELD J04.L0" instruction in the continuation code would fail bytecode verification. + assert fieldObjectClass != null; + method._new(fieldObjectClass).dup(); + loadMap(method); //load the map if (isScope()) { @@ -126,14 +118,14 @@ } else { method.invoke(constructorNoLookup(className, PropertyMap.class)); } + } - helpOptimisticRecognizeDuplicateIdentity(method); - + @Override + public void populateRange(final MethodEmitter method, final Type objectType, final int objectSlot, final int start, final int end) { + method.load(objectType, objectSlot); // Set values. - final Iterator<MapTuple<T>> iter = tuples.iterator(); - - while (iter.hasNext()) { - final MapTuple<T> tuple = iter.next(); + for (int i = start; i < end; i++) { + final MapTuple<T> tuple = tuples.get(i); //we only load when we have both symbols and values (which can be == the symbol) //if we didn't load, we need an array property if (tuple.symbol != null && tuple.value != null) { @@ -212,6 +204,11 @@ } } + @Override + protected Class<? extends ScriptObject> getAllocatorClass() { + return fieldObjectClass; + } + /** * Get the class name for the object class, * e.g. {@code com.nashorn.oracle.scripts.JO2P0}
--- a/src/jdk/nashorn/internal/codegen/FindScopeDepths.java Mon Sep 28 15:36:24 2015 -0700 +++ b/src/jdk/nashorn/internal/codegen/FindScopeDepths.java Tue Oct 06 11:04:55 2015 -0700 @@ -39,7 +39,7 @@ import jdk.nashorn.internal.ir.Node; import jdk.nashorn.internal.ir.Symbol; import jdk.nashorn.internal.ir.WithNode; -import jdk.nashorn.internal.ir.visitor.NodeVisitor; +import jdk.nashorn.internal.ir.visitor.SimpleNodeVisitor; import jdk.nashorn.internal.runtime.Context; import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData; import jdk.nashorn.internal.runtime.logging.DebugLogger; @@ -53,7 +53,7 @@ * FunctionNode being compiled */ @Logger(name="scopedepths") -final class FindScopeDepths extends NodeVisitor<LexicalContext> implements Loggable { +final class FindScopeDepths extends SimpleNodeVisitor implements Loggable { private final Compiler compiler; private final Map<Integer, Map<Integer, RecompilableScriptFunctionData>> fnIdToNestedFunctions = new HashMap<>(); @@ -66,7 +66,6 @@ private int dynamicScopeCount; FindScopeDepths(final Compiler compiler) { - super(new LexicalContext()); this.compiler = compiler; this.log = initLogger(compiler.getContext()); } @@ -275,17 +274,13 @@ //get all symbols that are referenced inside this function body final Set<Symbol> symbols = new HashSet<>(); - block.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { + block.accept(new SimpleNodeVisitor() { @Override - public final boolean enterDefault(final Node node) { - if (!compiler.isOnDemandCompilation()) { - if (node instanceof IdentNode) { - final Symbol symbol = ((IdentNode)node).getSymbol(); - if (symbol != null && symbol.isScope()) { - //if this is an internal symbol, skip it. - symbols.add(symbol); - } - } + public boolean enterIdentNode(final IdentNode identNode) { + final Symbol symbol = identNode.getSymbol(); + if (symbol != null && symbol.isScope()) { + //if this is an internal symbol, skip it. + symbols.add(symbol); } return true; }
--- a/src/jdk/nashorn/internal/codegen/FoldConstants.java Mon Sep 28 15:36:24 2015 -0700 +++ b/src/jdk/nashorn/internal/codegen/FoldConstants.java Tue Oct 06 11:04:55 2015 -0700 @@ -38,7 +38,6 @@ import jdk.nashorn.internal.ir.Expression; import jdk.nashorn.internal.ir.FunctionNode; import jdk.nashorn.internal.ir.IfNode; -import jdk.nashorn.internal.ir.LexicalContext; import jdk.nashorn.internal.ir.LiteralNode; import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode; import jdk.nashorn.internal.ir.Node; @@ -47,7 +46,7 @@ import jdk.nashorn.internal.ir.TernaryNode; import jdk.nashorn.internal.ir.UnaryNode; import jdk.nashorn.internal.ir.VarNode; -import jdk.nashorn.internal.ir.visitor.NodeVisitor; +import jdk.nashorn.internal.ir.visitor.SimpleNodeVisitor; import jdk.nashorn.internal.runtime.Context; import jdk.nashorn.internal.runtime.JSType; import jdk.nashorn.internal.runtime.ScriptRuntime; @@ -59,12 +58,11 @@ * Simple constant folding pass, executed before IR is starting to be lowered. */ @Logger(name="fold") -final class FoldConstants extends NodeVisitor<LexicalContext> implements Loggable { +final class FoldConstants extends SimpleNodeVisitor implements Loggable { private final DebugLogger log; FoldConstants(final Compiler compiler) { - super(new LexicalContext()); this.log = initLogger(compiler.getContext()); } @@ -116,7 +114,7 @@ statements.addAll(executed.getStatements()); // Get statements form executed branch } if (dropped != null) { - extractVarNodes(dropped, statements); // Get var-nodes from non-executed branch + extractVarNodesFromDeadCode(dropped, statements); // Get var-nodes from non-executed branch } if (statements.isEmpty()) { return new EmptyNode(ifNode); @@ -185,14 +183,27 @@ protected abstract LiteralNode<?> eval(); } - private static void extractVarNodes(final Block block, final List<Statement> statements) { - final LexicalContext lc = new LexicalContext(); - block.accept(lc, new NodeVisitor<LexicalContext>(lc) { + /** + * When we eliminate dead code, we must preserve var declarations as they are scoped to the whole + * function. This method gathers var nodes from code passed to it, removing their initializers. + * + * @param deadCodeRoot the root node of eliminated dead code + * @param statements a list that will be receiving the var nodes from the dead code, with their + * initializers removed. + */ + static void extractVarNodesFromDeadCode(final Node deadCodeRoot, final List<Statement> statements) { + deadCodeRoot.accept(new SimpleNodeVisitor() { @Override public boolean enterVarNode(final VarNode varNode) { statements.add(varNode.setInit(null)); return false; } + + @Override + public boolean enterFunctionNode(final FunctionNode functionNode) { + // Don't descend into nested functions + return false; + } }); }
--- a/src/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java Mon Sep 28 15:36:24 2015 -0700 +++ b/src/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java Tue Oct 06 11:04:55 2015 -0700 @@ -87,6 +87,7 @@ import jdk.nashorn.internal.ir.WhileNode; import jdk.nashorn.internal.ir.WithNode; import jdk.nashorn.internal.ir.visitor.NodeVisitor; +import jdk.nashorn.internal.ir.visitor.SimpleNodeVisitor; import jdk.nashorn.internal.parser.TokenType; /** @@ -105,7 +106,7 @@ * instances of the calculator to be run on nested functions (when not lazy compiling). * */ -final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{ +final class LocalVariableTypesCalculator extends SimpleNodeVisitor { private static class JumpOrigin { final JoinPredecessor node; @@ -425,7 +426,6 @@ private final Deque<Label> catchLabels = new ArrayDeque<>(); LocalVariableTypesCalculator(final Compiler compiler) { - super(new LexicalContext()); this.compiler = compiler; } @@ -1330,7 +1330,7 @@ // Sets the return type of the function and also performs the bottom-up pass of applying type and conversion // information to nodes as well as doing the calculation on nested functions as required. FunctionNode newFunction = functionNode; - final NodeVisitor<LexicalContext> applyChangesVisitor = new NodeVisitor<LexicalContext>(new LexicalContext()) { + final SimpleNodeVisitor applyChangesVisitor = new SimpleNodeVisitor() { private boolean inOuterFunction = true; private final Deque<JoinPredecessor> joinPredecessors = new ArrayDeque<>();
--- a/src/jdk/nashorn/internal/codegen/Lower.java Mon Sep 28 15:36:24 2015 -0700 +++ b/src/jdk/nashorn/internal/codegen/Lower.java Tue Oct 06 11:04:55 2015 -0700 @@ -73,7 +73,7 @@ import jdk.nashorn.internal.ir.WhileNode; import jdk.nashorn.internal.ir.WithNode; import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor; -import jdk.nashorn.internal.ir.visitor.NodeVisitor; +import jdk.nashorn.internal.ir.visitor.SimpleNodeVisitor; import jdk.nashorn.internal.parser.Token; import jdk.nashorn.internal.parser.TokenType; import jdk.nashorn.internal.runtime.Context; @@ -120,13 +120,7 @@ terminated = true; } } else { - statement.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { - @Override - public boolean enterVarNode(final VarNode varNode) { - newStatements.add(varNode.setInit(null)); - return false; - } - }); + FoldConstants.extractVarNodesFromDeadCode(statement, newStatements); } } return newStatements; @@ -327,7 +321,7 @@ @SuppressWarnings("unchecked") private static <T extends Node> T ensureUniqueNamesIn(final T node) { - return (T)node.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { + return (T)node.accept(new SimpleNodeVisitor() { @Override public Node leaveFunctionNode(final FunctionNode functionNode) { final String name = functionNode.getName(); @@ -392,7 +386,7 @@ final Block finallyBlock = createFinallyBlock(finallyBody); final ArrayList<Block> inlinedFinallies = new ArrayList<>(); final FunctionNode fn = lc.getCurrentFunction(); - final TryNode newTryNode = (TryNode)tryNode.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { + final TryNode newTryNode = (TryNode)tryNode.accept(new SimpleNodeVisitor() { @Override public boolean enterFunctionNode(final FunctionNode functionNode) { @@ -535,7 +529,7 @@ final Block catchAll = catchAllBlock(tryNode); final List<ThrowNode> rethrows = new ArrayList<>(1); - catchAll.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { + catchAll.accept(new SimpleNodeVisitor() { @Override public boolean enterThrowNode(final ThrowNode throwNode) { rethrows.add(throwNode); @@ -680,7 +674,7 @@ private static boolean controlFlowEscapes(final LexicalContext lex, final Block loopBody) { final List<Node> escapes = new ArrayList<>(); - loopBody.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { + loopBody.accept(new SimpleNodeVisitor() { @Override public Node leaveBreakNode(final BreakNode node) { escapes.add(node);
--- a/src/jdk/nashorn/internal/codegen/MethodEmitter.java Mon Sep 28 15:36:24 2015 -0700 +++ b/src/jdk/nashorn/internal/codegen/MethodEmitter.java Tue Oct 06 11:04:55 2015 -0700 @@ -258,8 +258,7 @@ */ private Type popType(final Type expected) { final Type type = popType(); - assert type.isObject() && expected.isObject() || - type.isEquivalentTo(expected) : type + " is not compatible with " + expected; + assert type.isEquivalentTo(expected) : type + " is not compatible with " + expected; return type; }
--- a/src/jdk/nashorn/internal/codegen/ObjectCreator.java Mon Sep 28 15:36:24 2015 -0700 +++ b/src/jdk/nashorn/internal/codegen/ObjectCreator.java Tue Oct 06 11:04:55 2015 -0700 @@ -36,7 +36,7 @@ * Base class for object creation code generation. * @param <T> value type */ -public abstract class ObjectCreator<T> { +public abstract class ObjectCreator<T> implements CodeGenerator.SplitLiteralCreator { /** List of keys & symbols to initiate in this ObjectCreator */ final List<MapTuple<T>> tuples; @@ -69,7 +69,23 @@ * Generate code for making the object. * @param method Script method. */ - protected abstract void makeObject(final MethodEmitter method); + public void makeObject(final MethodEmitter method) { + createObject(method); + // We need to store the object in a temporary slot as populateRange expects to load the + // object from a slot (as it is also invoked within split methods). Note that this also + // helps optimistic continuations to handle the stack in case an optimistic assumption + // fails during initialization (see JDK-8079269). + final int objectSlot = method.getUsedSlotsWithLiveTemporaries(); + final Type objectType = method.peekType(); + method.storeTemp(objectType, objectSlot); + populateRange(method, objectType, objectSlot, 0, tuples.size()); + } + + /** + * Generate code for creating and initializing the object. + * @param method the method emitter + */ + protected abstract void createObject(final MethodEmitter method); /** * Construct the property map appropriate for the object. @@ -125,6 +141,12 @@ } /** + * Get the class of objects created by this ObjectCreator + * @return class of created object + */ + abstract protected Class<? extends ScriptObject> getAllocatorClass(); + + /** * Technique for loading an initial value. Defined by anonymous subclasses in code gen. * * @param value Value to load. @@ -145,29 +167,4 @@ MethodEmitter loadTuple(final MethodEmitter method, final MapTuple<T> tuple) { return loadTuple(method, tuple, true); } - - /** - * If using optimistic typing, let the code generator realize that the newly created object on the stack - * when DUP-ed will be the same value. Basically: {NEW, DUP, INVOKESPECIAL init, DUP} will leave a stack - * load specification {unknown, unknown} on stack (that is "there's two values on the stack, but neither - * comes from a known local load"). If there's an optimistic operation in the literal initializer, - * OptimisticOperation.storeStack will allocate two temporary locals for it and store them as - * {ASTORE 4, ASTORE 3}. If we instead do {NEW, DUP, INVOKESPECIAL init, ASTORE 3, ALOAD 3, DUP} we end up - * with stack load specification {ALOAD 3, ALOAD 3} (as DUP can track that the value it duplicated came - * from a local load), so if/when a continuation needs to be recreated from it, it'll be - * able to emit ALOAD 3, ALOAD 3 to recreate the stack. If we didn't do this, deoptimization within an - * object literal initialization could in rare cases cause an incompatible change in the shape of the - * local variable table for the temporaries, e.g. in the following snippet where a variable is reassigned - * to a wider type in an object initializer: - * <code>var m = 1; var obj = {p0: m, p1: m = "foo", p2: m}</code> - * @param method the current method emitter. - */ - void helpOptimisticRecognizeDuplicateIdentity(final MethodEmitter method) { - if (codegen.useOptimisticTypes()) { - final Type objectType = method.peekType(); - final int tempSlot = method.defineTemporaryLocalVariable(objectType.getSlots()); - method.storeHidden(objectType, tempSlot); - method.load(objectType, tempSlot); - } - } }
--- a/src/jdk/nashorn/internal/codegen/OptimisticTypesCalculator.java Mon Sep 28 15:36:24 2015 -0700 +++ b/src/jdk/nashorn/internal/codegen/OptimisticTypesCalculator.java Tue Oct 06 11:04:55 2015 -0700 @@ -42,7 +42,6 @@ import jdk.nashorn.internal.ir.IfNode; import jdk.nashorn.internal.ir.IndexNode; import jdk.nashorn.internal.ir.JoinPredecessorExpression; -import jdk.nashorn.internal.ir.LexicalContext; import jdk.nashorn.internal.ir.LoopNode; import jdk.nashorn.internal.ir.Node; import jdk.nashorn.internal.ir.Optimistic; @@ -52,7 +51,7 @@ import jdk.nashorn.internal.ir.UnaryNode; import jdk.nashorn.internal.ir.VarNode; import jdk.nashorn.internal.ir.WhileNode; -import jdk.nashorn.internal.ir.visitor.NodeVisitor; +import jdk.nashorn.internal.ir.visitor.SimpleNodeVisitor; import jdk.nashorn.internal.parser.TokenType; import jdk.nashorn.internal.runtime.ScriptObject; @@ -61,7 +60,7 @@ * must not ever be marked as optimistic, assigning narrowest non-invalidated types to program points from the * compilation environment, as well as initializing optimistic types of global properties for scripts. */ -final class OptimisticTypesCalculator extends NodeVisitor<LexicalContext> { +final class OptimisticTypesCalculator extends SimpleNodeVisitor { final Compiler compiler; @@ -69,7 +68,6 @@ final Deque<BitSet> neverOptimistic = new ArrayDeque<>(); OptimisticTypesCalculator(final Compiler compiler) { - super(new LexicalContext()); this.compiler = compiler; }
--- a/src/jdk/nashorn/internal/codegen/ProgramPoints.java Mon Sep 28 15:36:24 2015 -0700 +++ b/src/jdk/nashorn/internal/codegen/ProgramPoints.java Tue Oct 06 11:04:55 2015 -0700 @@ -37,25 +37,20 @@ import jdk.nashorn.internal.ir.FunctionNode; import jdk.nashorn.internal.ir.IdentNode; import jdk.nashorn.internal.ir.IndexNode; -import jdk.nashorn.internal.ir.LexicalContext; import jdk.nashorn.internal.ir.Node; import jdk.nashorn.internal.ir.Optimistic; import jdk.nashorn.internal.ir.UnaryNode; import jdk.nashorn.internal.ir.VarNode; -import jdk.nashorn.internal.ir.visitor.NodeVisitor; +import jdk.nashorn.internal.ir.visitor.SimpleNodeVisitor; /** * Find program points in the code that are needed for optimistic assumptions */ -class ProgramPoints extends NodeVisitor<LexicalContext> { +class ProgramPoints extends SimpleNodeVisitor { private final IntDeque nextProgramPoint = new IntDeque(); private final Set<Node> noProgramPoint = new HashSet<>(); - ProgramPoints() { - super(new LexicalContext()); - } - private int next() { final int next = nextProgramPoint.getAndIncrement(); if(next > MAX_PROGRAM_POINT_VALUE) {
--- a/src/jdk/nashorn/internal/codegen/ReplaceCompileUnits.java Mon Sep 28 15:36:24 2015 -0700 +++ b/src/jdk/nashorn/internal/codegen/ReplaceCompileUnits.java Tue Oct 06 11:04:55 2015 -0700 @@ -29,20 +29,17 @@ import java.util.List; import jdk.nashorn.internal.ir.CompileUnitHolder; import jdk.nashorn.internal.ir.FunctionNode; -import jdk.nashorn.internal.ir.LexicalContext; import jdk.nashorn.internal.ir.LiteralNode; import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode; -import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode.ArrayUnit; import jdk.nashorn.internal.ir.Node; -import jdk.nashorn.internal.ir.visitor.NodeVisitor; +import jdk.nashorn.internal.ir.ObjectNode; +import jdk.nashorn.internal.ir.Splittable; +import jdk.nashorn.internal.ir.visitor.SimpleNodeVisitor; /** * Base class for a node visitor that replaces {@link CompileUnit}s in {@link CompileUnitHolder}s. */ -abstract class ReplaceCompileUnits extends NodeVisitor<LexicalContext> { - ReplaceCompileUnits() { - super(new LexicalContext()); - } +abstract class ReplaceCompileUnits extends SimpleNodeVisitor { /** * Override to provide a replacement for an old compile unit. @@ -70,15 +67,28 @@ public Node leaveLiteralNode(final LiteralNode<?> node) { if (node instanceof ArrayLiteralNode) { final ArrayLiteralNode aln = (ArrayLiteralNode)node; - if (aln.getUnits() == null) { + if (aln.getSplitRanges() == null) { return node; } - final List<ArrayUnit> newArrayUnits = new ArrayList<>(); - for (final ArrayUnit au : aln.getUnits()) { - newArrayUnits.add(new ArrayUnit(getExistingReplacement(au), au.getLo(), au.getHi())); + final List<Splittable.SplitRange> newArrayUnits = new ArrayList<>(); + for (final Splittable.SplitRange au : aln.getSplitRanges()) { + newArrayUnits.add(new Splittable.SplitRange(getExistingReplacement(au), au.getLow(), au.getHigh())); } - return aln.setUnits(lc, newArrayUnits); + return aln.setSplitRanges(lc, newArrayUnits); } return node; } + + @Override + public Node leaveObjectNode(final ObjectNode objectNode) { + final List<Splittable.SplitRange> ranges = objectNode.getSplitRanges(); + if (ranges != null) { + final List<Splittable.SplitRange> newRanges = new ArrayList<>(); + for (final Splittable.SplitRange range : ranges) { + newRanges.add(new Splittable.SplitRange(getExistingReplacement(range), range.getLow(), range.getHigh())); + } + return objectNode.setSplitRanges(lc, newRanges); + } + return super.leaveObjectNode(objectNode); + } }
--- a/src/jdk/nashorn/internal/codegen/SpillObjectCreator.java Mon Sep 28 15:36:24 2015 -0700 +++ b/src/jdk/nashorn/internal/codegen/SpillObjectCreator.java Tue Oct 06 11:04:55 2015 -0700 @@ -61,7 +61,7 @@ } @Override - protected void makeObject(final MethodEmitter method) { + public void createObject(final MethodEmitter method) { assert !isScope() : "spill scope objects are not currently supported"; final int length = tuples.size(); @@ -69,9 +69,7 @@ final int spillLength = ScriptObject.spillAllocationLength(length); final long[] jpresetValues = dualFields ? new long[spillLength] : null; final Object[] opresetValues = new Object[spillLength]; - final Set<Integer> postsetValues = new LinkedHashSet<>(); - final int callSiteFlags = codegen.getCallSiteFlags(); - final Class<?> objectClass = dualFields ? JD.class : JO.class; + final Class<?> objectClass = getAllocatorClass(); ArrayData arrayData = ArrayData.allocate(ScriptRuntime.EMPTY_ARRAY); // Compute constant property values @@ -85,9 +83,7 @@ if (value != null) { final Object constantValue = LiteralNode.objectAsConstant(value); - if (constantValue == LiteralNode.POSTSET_MARKER) { - postsetValues.add(pos); - } else { + if (constantValue != LiteralNode.POSTSET_MARKER) { final Property property = propertyMap.findProperty(key); if (property != null) { // normal property key @@ -146,25 +142,34 @@ // instantiate the script object with spill objects method.invoke(constructorNoLookup(objectClass, PropertyMap.class, long[].class, Object[].class)); - helpOptimisticRecognizeDuplicateIdentity(method); - // Set prefix array data if any if (arrayData.length() > 0) { method.dup(); codegen.loadConstant(arrayData); method.invoke(virtualCallNoLookup(ScriptObject.class, "setArray", void.class, ArrayData.class)); } + } + + @Override + public void populateRange(final MethodEmitter method, final Type objectType, final int objectSlot, final int start, final int end) { + final int callSiteFlags = codegen.getCallSiteFlags(); + method.load(objectType, objectSlot); // set postfix values - for (final int i : postsetValues) { + for (int i = start; i < end; i++) { final MapTuple<Expression> tuple = tuples.get(i); + + if (LiteralNode.isConstant(tuple.value)) { + continue; + } + final Property property = propertyMap.findProperty(tuple.key); + if (property == null) { final int index = ArrayIndex.getArrayIndex(tuple.key); assert ArrayIndex.isValidArrayIndex(index); method.dup(); method.load(ArrayIndex.toLongIndex(index)); - //method.println("putting " + tuple + " into arraydata"); loadTuple(method, tuple); method.dynamicSetIndex(callSiteFlags); } else { @@ -178,8 +183,7 @@ @Override protected PropertyMap makeMap() { assert propertyMap == null : "property map already initialized"; - final boolean dualFields = codegen.useDualFields(); - final Class<? extends ScriptObject> clazz = dualFields ? JD.class : JO.class; + final Class<? extends ScriptObject> clazz = getAllocatorClass(); propertyMap = new MapCreator<>(clazz, tuples).makeSpillMap(false, codegen.useDualFields()); return propertyMap; } @@ -188,4 +192,9 @@ protected void loadValue(final Expression expr, final Type type) { codegen.loadExpressionAsType(expr, type); } + + @Override + protected Class<? extends ScriptObject> getAllocatorClass() { + return codegen.useDualFields() ? JD.class : JO.class; + } }
--- a/src/jdk/nashorn/internal/codegen/Splitter.java Mon Sep 28 15:36:24 2015 -0700 +++ b/src/jdk/nashorn/internal/codegen/Splitter.java Tue Oct 06 11:04:55 2015 -0700 @@ -33,14 +33,15 @@ import java.util.Map; import jdk.nashorn.internal.ir.Block; import jdk.nashorn.internal.ir.FunctionNode; -import jdk.nashorn.internal.ir.LexicalContext; import jdk.nashorn.internal.ir.LiteralNode; import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode; -import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode.ArrayUnit; import jdk.nashorn.internal.ir.Node; +import jdk.nashorn.internal.ir.ObjectNode; +import jdk.nashorn.internal.ir.PropertyNode; import jdk.nashorn.internal.ir.SplitNode; +import jdk.nashorn.internal.ir.Splittable; import jdk.nashorn.internal.ir.Statement; -import jdk.nashorn.internal.ir.visitor.NodeVisitor; +import jdk.nashorn.internal.ir.visitor.SimpleNodeVisitor; import jdk.nashorn.internal.runtime.Context; import jdk.nashorn.internal.runtime.logging.DebugLogger; import jdk.nashorn.internal.runtime.logging.Loggable; @@ -51,7 +52,7 @@ * Split the IR into smaller compile units. */ @Logger(name="splitter") -final class Splitter extends NodeVisitor<LexicalContext> implements Loggable { +final class Splitter extends SimpleNodeVisitor implements Loggable { /** Current compiler. */ private final Compiler compiler; @@ -77,7 +78,6 @@ * @param outermostCompileUnit compile unit for outermost function, if non-lazy this is the script's compile unit */ public Splitter(final Compiler compiler, final FunctionNode functionNode, final CompileUnit outermostCompileUnit) { - super(new LexicalContext()); this.compiler = compiler; this.outermost = functionNode; this.outermostCompileUnit = outermostCompileUnit; @@ -140,7 +140,7 @@ final Block body = functionNode.getBody(); final List<FunctionNode> dc = directChildren(functionNode); - final Block newBody = (Block)body.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { + final Block newBody = (Block)body.accept(new SimpleNodeVisitor() { @Override public boolean enterFunctionNode(final FunctionNode nestedFunction) { return dc.contains(nestedFunction); @@ -162,7 +162,7 @@ private static List<FunctionNode> directChildren(final FunctionNode functionNode) { final List<FunctionNode> dc = new ArrayList<>(); - functionNode.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { + functionNode.accept(new SimpleNodeVisitor() { @Override public boolean enterFunctionNode(final FunctionNode child) { if (child == functionNode) { @@ -295,7 +295,7 @@ final ArrayLiteralNode arrayLiteralNode = (ArrayLiteralNode) literal; final Node[] value = arrayLiteralNode.getValue(); final int[] postsets = arrayLiteralNode.getPostsets(); - final List<ArrayUnit> units = new ArrayList<>(); + final List<Splittable.SplitRange> ranges = new ArrayList<>(); long totalWeight = 0; int lo = 0; @@ -309,7 +309,7 @@ if (totalWeight >= SPLIT_THRESHOLD) { final CompileUnit unit = compiler.findUnit(totalWeight - weight); - units.add(new ArrayUnit(unit, lo, i)); + ranges.add(new Splittable.SplitRange(unit, lo, i)); lo = i; totalWeight = weight; } @@ -317,16 +317,59 @@ if (lo != postsets.length) { final CompileUnit unit = compiler.findUnit(totalWeight); - units.add(new ArrayUnit(unit, lo, postsets.length)); + ranges.add(new Splittable.SplitRange(unit, lo, postsets.length)); } - return arrayLiteralNode.setUnits(lc, units); + return arrayLiteralNode.setSplitRanges(lc, ranges); } return literal; } @Override + public Node leaveObjectNode(final ObjectNode objectNode) { + long weight = WeighNodes.weigh(objectNode); + + if (weight < SPLIT_THRESHOLD) { + return objectNode; + } + + final FunctionNode functionNode = lc.getCurrentFunction(); + lc.setFlag(functionNode, FunctionNode.IS_SPLIT); + + final List<Splittable.SplitRange> ranges = new ArrayList<>(); + final List<PropertyNode> properties = objectNode.getElements(); + final boolean isSpillObject = properties.size() > CodeGenerator.OBJECT_SPILL_THRESHOLD; + long totalWeight = 0; + int lo = 0; + + for (int i = 0; i < properties.size(); i++) { + + final PropertyNode property = properties.get(i); + final boolean isConstant = LiteralNode.isConstant(property.getValue()); + + if (!isConstant || !isSpillObject) { + weight = isConstant ? 0 : WeighNodes.weigh(property.getValue()); + totalWeight += WeighNodes.AASTORE_WEIGHT + weight; + + if (totalWeight >= SPLIT_THRESHOLD) { + final CompileUnit unit = compiler.findUnit(totalWeight - weight); + ranges.add(new Splittable.SplitRange(unit, lo, i)); + lo = i; + totalWeight = weight; + } + } + } + + if (lo != properties.size()) { + final CompileUnit unit = compiler.findUnit(totalWeight); + ranges.add(new Splittable.SplitRange(unit, lo, properties.size())); + } + + return objectNode.setSplitRanges(lc, ranges); + } + + @Override public boolean enterFunctionNode(final FunctionNode node) { //only go into the function node for this splitter. any subfunctions are rejected return node == outermost;
--- a/src/jdk/nashorn/internal/codegen/WeighNodes.java Mon Sep 28 15:36:24 2015 -0700 +++ b/src/jdk/nashorn/internal/codegen/WeighNodes.java Tue Oct 06 11:04:55 2015 -0700 @@ -44,12 +44,13 @@ import jdk.nashorn.internal.ir.LexicalContext; import jdk.nashorn.internal.ir.LiteralNode; import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode; -import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode.ArrayUnit; import jdk.nashorn.internal.ir.Node; +import jdk.nashorn.internal.ir.ObjectNode; import jdk.nashorn.internal.ir.PropertyNode; import jdk.nashorn.internal.ir.ReturnNode; import jdk.nashorn.internal.ir.RuntimeNode; import jdk.nashorn.internal.ir.SplitNode; +import jdk.nashorn.internal.ir.Splittable; import jdk.nashorn.internal.ir.SwitchNode; import jdk.nashorn.internal.ir.ThrowNode; import jdk.nashorn.internal.ir.TryNode; @@ -88,6 +89,8 @@ static final long THROW_WEIGHT = 2; static final long VAR_WEIGHT = 40; static final long WITH_WEIGHT = 8; + static final long OBJECT_WEIGHT = 16; + static final long SETPROP_WEIGHT = 5; /** Accumulated weight. */ private long weight; @@ -213,7 +216,7 @@ final ArrayLiteralNode arrayLiteralNode = (ArrayLiteralNode)literalNode; final Node[] value = arrayLiteralNode.getValue(); final int[] postsets = arrayLiteralNode.getPostsets(); - final List<ArrayUnit> units = arrayLiteralNode.getUnits(); + final List<Splittable.SplitRange> units = arrayLiteralNode.getSplitRanges(); if (units == null) { for (final int postset : postsets) { @@ -233,6 +236,27 @@ } @Override + public boolean enterObjectNode(final ObjectNode objectNode) { + weight += OBJECT_WEIGHT; + final List<PropertyNode> properties = objectNode.getElements(); + final boolean isSpillObject = properties.size() > CodeGenerator.OBJECT_SPILL_THRESHOLD; + + for (final PropertyNode property : properties) { + if (!LiteralNode.isConstant(property.getValue())) { + weight += SETPROP_WEIGHT; + property.getValue().accept(this); + } else if (!isSpillObject) { + // constants in spill object are set via preset spill array, + // but fields objects need to set constants. + weight += SETPROP_WEIGHT; + } + + } + + return false; + } + + @Override public Node leavePropertyNode(final PropertyNode propertyNode) { weight += LITERAL_WEIGHT; return propertyNode;
--- a/src/jdk/nashorn/internal/codegen/types/Type.java Mon Sep 28 15:36:24 2015 -0700 +++ b/src/jdk/nashorn/internal/codegen/types/Type.java Tue Oct 06 11:04:55 2015 -0700 @@ -65,6 +65,7 @@ import jdk.internal.org.objectweb.asm.Handle; import jdk.internal.org.objectweb.asm.MethodVisitor; import jdk.nashorn.internal.codegen.CompilerConstants.Call; +import jdk.nashorn.internal.runtime.Context; import jdk.nashorn.internal.runtime.ScriptObject; import jdk.nashorn.internal.runtime.Undefined; import jdk.nashorn.internal.runtime.linker.Bootstrap; @@ -256,6 +257,9 @@ case jdk.internal.org.objectweb.asm.Type.DOUBLE: return NUMBER; case jdk.internal.org.objectweb.asm.Type.OBJECT: + if (Context.isStructureClass(itype.getClassName())) { + return SCRIPT_OBJECT; + } try { return Type.typeFor(Class.forName(itype.getClassName())); } catch(final ClassNotFoundException e) { @@ -949,7 +953,7 @@ /** * This is the singleton for integer arrays */ - public static final ArrayType INT_ARRAY = new ArrayType(int[].class) { + public static final ArrayType INT_ARRAY = putInCache(new ArrayType(int[].class) { private static final long serialVersionUID = 1L; @Override @@ -973,12 +977,12 @@ public Type getElementType() { return INT; } - }; + }); /** * This is the singleton for long arrays */ - public static final ArrayType LONG_ARRAY = new ArrayType(long[].class) { + public static final ArrayType LONG_ARRAY = putInCache(new ArrayType(long[].class) { private static final long serialVersionUID = 1L; @Override @@ -1002,12 +1006,12 @@ public Type getElementType() { return LONG; } - }; + }); /** * This is the singleton for numeric arrays */ - public static final ArrayType NUMBER_ARRAY = new ArrayType(double[].class) { + public static final ArrayType NUMBER_ARRAY = putInCache(new ArrayType(double[].class) { private static final long serialVersionUID = 1L; @Override @@ -1031,13 +1035,7 @@ public Type getElementType() { return NUMBER; } - }; - - /** Singleton for method handle arrays used for properties etc. */ - public static final ArrayType METHODHANDLE_ARRAY = putInCache(new ArrayType(MethodHandle[].class)); - - /** This is the singleton for string arrays */ - public static final ArrayType STRING_ARRAY = putInCache(new ArrayType(String[].class)); + }); /** This is the singleton for object arrays */ public static final ArrayType OBJECT_ARRAY = putInCache(new ArrayType(Object[].class));
--- a/src/jdk/nashorn/internal/ir/LiteralNode.java Mon Sep 28 15:36:24 2015 -0700 +++ b/src/jdk/nashorn/internal/ir/LiteralNode.java Tue Oct 06 11:04:55 2015 -0700 @@ -25,11 +25,9 @@ package jdk.nashorn.internal.ir; -import java.io.Serializable; import java.util.Arrays; import java.util.Collections; import java.util.List; -import jdk.nashorn.internal.codegen.CompileUnit; import jdk.nashorn.internal.codegen.types.ArrayType; import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.ir.annotations.Immutable; @@ -561,6 +559,15 @@ return POSTSET_MARKER; } + /** + * Test whether {@code object} represents a constant value. + * @param object a node or value object + * @return true if object is a constant value + */ + public static boolean isConstant(final Object object) { + return objectAsConstant(object) != POSTSET_MARKER; + } + private static final class NullLiteralNode extends PrimitiveLiteralNode<Object> { private static final long serialVersionUID = 1L; @@ -592,7 +599,7 @@ * Array literal node class. */ @Immutable - public static final class ArrayLiteralNode extends LiteralNode<Expression[]> implements LexicalContextNode { + public static final class ArrayLiteralNode extends LiteralNode<Expression[]> implements LexicalContextNode, Splittable { private static final long serialVersionUID = 1L; /** Array element type. */ @@ -604,59 +611,8 @@ /** Indices of array elements requiring computed post sets. */ private final int[] postsets; - /** 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 final class ArrayUnit implements CompileUnitHolder, Serializable { - private static final long serialVersionUID = 1L; - - /** Compile unit associated with the postsets range. */ - private final CompileUnit compileUnit; - - /** postsets range associated with the unit (hi not inclusive). */ - private final int lo, hi; - - /** - * Constructor - * @param compileUnit compile unit - * @param lo lowest array index in unit - * @param hi highest array index in unit + 1 - */ - public ArrayUnit(final CompileUnit compileUnit, final int lo, final int hi) { - this.compileUnit = compileUnit; - this.lo = lo; - this.hi = hi; - } - - /** - * Get the high index position of the ArrayUnit (non inclusive) - * @return high index position - */ - public int getHi() { - return hi; - } - - /** - * Get the low index position of the ArrayUnit (inclusive) - * @return low index position - */ - public int getLo() { - return lo; - } - - /** - * The array compile unit - * @return array compile unit - */ - @Override - public CompileUnit getCompileUnit() { - return compileUnit; - } - } + /** Ranges for splitting up large literals in code generation */ + private final List<Splittable.SplitRange> splitRanges; private static final class ArrayLiteralInitializer { @@ -664,7 +620,7 @@ final Type elementType = computeElementType(node.value); 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); + return new ArrayLiteralNode(node, node.value, elementType, postsets, presets, node.splitRanges); } private static Type computeElementType(final Expression[] value) { @@ -697,7 +653,7 @@ for (int i = 0; i < value.length; i++) { final Expression element = value[i]; - if (element == null || objectAsConstant(element) == POSTSET_MARKER) { + if (element == null || !isConstant(element)) { computed[nComputed++] = i; } } @@ -814,19 +770,19 @@ this.elementType = Type.UNKNOWN; this.presets = null; this.postsets = null; - this.units = null; + this.splitRanges = null; } /** * Copy constructor * @param node source array literal node */ - private ArrayLiteralNode(final ArrayLiteralNode node, final Expression[] value, final Type elementType, final int[] postsets, final Object presets, final List<ArrayUnit> units) { + private ArrayLiteralNode(final ArrayLiteralNode node, final Expression[] value, final Type elementType, final int[] postsets, final Object presets, final List<Splittable.SplitRange> splitRanges) { super(node, value); this.elementType = elementType; this.postsets = postsets; this.presets = presets; - this.units = units; + this.splitRanges = splitRanges; } /** @@ -917,26 +873,27 @@ } /** - * Get the array units that make up this ArrayLiteral - * @see ArrayUnit - * @return list of array units + * Get the split ranges for this ArrayLiteral, or null if this array does not have to be split. + * @see Splittable.SplitRange + * @return list of split ranges */ - public List<ArrayUnit> getUnits() { - return units == null ? null : Collections.unmodifiableList(units); + @Override + public List<Splittable.SplitRange> getSplitRanges() { + return splitRanges == null ? null : Collections.unmodifiableList(splitRanges); } /** - * Set the ArrayUnits that make up this ArrayLiteral + * Set the SplitRanges that make up this ArrayLiteral * @param lc lexical context - * @see ArrayUnit - * @param units list of array units - * @return new or changed arrayliteralnode + * @see Splittable.SplitRange + * @param splitRanges list of split ranges + * @return new or changed node */ - public ArrayLiteralNode setUnits(final LexicalContext lc, final List<ArrayUnit> units) { - if (this.units == units) { + public ArrayLiteralNode setSplitRanges(final LexicalContext lc, final List<Splittable.SplitRange> splitRanges) { + if (this.splitRanges == splitRanges) { return this; } - return Node.replaceInLexicalContext(lc, this, new ArrayLiteralNode(this, value, elementType, postsets, presets, units)); + return Node.replaceInLexicalContext(lc, this, new ArrayLiteralNode(this, value, elementType, postsets, presets, splitRanges)); } @Override @@ -958,7 +915,7 @@ if (this.value == value) { return this; } - return Node.replaceInLexicalContext(lc, this, new ArrayLiteralNode(this, value, elementType, postsets, presets, units)); + return Node.replaceInLexicalContext(lc, this, new ArrayLiteralNode(this, value, elementType, postsets, presets, splitRanges)); } private ArrayLiteralNode setValue(final LexicalContext lc, final List<Expression> value) {
--- a/src/jdk/nashorn/internal/ir/ObjectNode.java Mon Sep 28 15:36:24 2015 -0700 +++ b/src/jdk/nashorn/internal/ir/ObjectNode.java Tue Oct 06 11:04:55 2015 -0700 @@ -27,6 +27,7 @@ import java.util.Collections; import java.util.List; +import java.util.RandomAccess; import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.ir.annotations.Immutable; import jdk.nashorn.internal.ir.visitor.NodeVisitor; @@ -35,12 +36,15 @@ * IR representation of an object literal. */ @Immutable -public final class ObjectNode extends Expression { +public final class ObjectNode extends Expression implements LexicalContextNode, Splittable { private static final long serialVersionUID = 1L; /** Literal elements. */ private final List<PropertyNode> elements; + /** Ranges for splitting large literals over multiple compile units in codegen. */ + private final List<Splittable.SplitRange> splitRanges; + /** * Constructor * @@ -51,19 +55,27 @@ public ObjectNode(final long token, final int finish, final List<PropertyNode> elements) { super(token, finish); this.elements = elements; + this.splitRanges = null; + assert elements instanceof RandomAccess : "Splitting requires random access lists"; } - private ObjectNode(final ObjectNode objectNode, final List<PropertyNode> elements) { + private ObjectNode(final ObjectNode objectNode, final List<PropertyNode> elements, + final List<Splittable.SplitRange> splitRanges ) { super(objectNode); this.elements = elements; + this.splitRanges = splitRanges; } @Override public Node accept(final NodeVisitor<? extends LexicalContext> visitor) { + return Acceptor.accept(this, visitor); + } + + @Override + public Node accept(final LexicalContext lc, final NodeVisitor<? extends LexicalContext> visitor) { if (visitor.enterObjectNode(this)) { - return visitor.leaveObjectNode(setElements(Node.accept(visitor, elements))); + return visitor.leaveObjectNode(setElements(lc, Node.accept(visitor, elements))); } - return this; } @@ -102,10 +114,35 @@ return Collections.unmodifiableList(elements); } - private ObjectNode setElements(final List<PropertyNode> elements) { + private ObjectNode setElements(final LexicalContext lc, final List<PropertyNode> elements) { if (this.elements == elements) { return this; } - return new ObjectNode(this, elements); + return Node.replaceInLexicalContext(lc, this, new ObjectNode(this, elements, this.splitRanges)); } + + /** + * Set the split ranges for this ObjectNode + * @see Splittable.SplitRange + * @param lc the lexical context + * @param splitRanges list of split ranges + * @return new or changed object node + */ + public ObjectNode setSplitRanges(final LexicalContext lc, final List<Splittable.SplitRange> splitRanges) { + if (this.splitRanges == splitRanges) { + return this; + } + return Node.replaceInLexicalContext(lc, this, new ObjectNode(this, elements, splitRanges)); + } + + /** + * Get the split ranges for this ObjectNode, or null if the object is not split. + * @see Splittable.SplitRange + * @return list of split ranges + */ + @Override + public List<Splittable.SplitRange> getSplitRanges() { + return splitRanges == null ? null : Collections.unmodifiableList(splitRanges); + } + }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk/nashorn/internal/ir/Splittable.java Tue Oct 06 11:04:55 2015 -0700 @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.nashorn.internal.ir; + +import java.io.Serializable; +import java.util.List; +import jdk.nashorn.internal.codegen.CompileUnit; + +/** + * An interface for splittable expressions. + */ +public interface Splittable { + + /** + * Get a list of split ranges for this splittable expression, or null + * if the expression should not be split. + * + * @return a list of split ranges + */ + List<SplitRange> getSplitRanges(); + + /** + * A SplitRange is a range in a splittable expression. It defines the + * boundaries of the split range and provides a compile unit for code generation. + */ + final class SplitRange implements CompileUnitHolder, Serializable { + private static final long serialVersionUID = 1L; + + /** Compile unit associated with the postsets range. */ + private final CompileUnit compileUnit; + + /** postsets range associated with the unit (hi not inclusive). */ + private final int low, high; + + /** + * Constructor + * @param compileUnit compile unit + * @param low lowest array index in unit + * @param high highest array index in unit + 1 + */ + public SplitRange(final CompileUnit compileUnit, final int low, final int high) { + this.compileUnit = compileUnit; + this.low = low; + this.high = high; + } + + /** + * Get the high index position of the ArrayUnit (exclusive) + * @return high index position + */ + public int getHigh() { + return high; + } + + /** + * Get the low index position of the ArrayUnit (inclusive) + * @return low index position + */ + public int getLow() { + return low; + } + + /** + * The array compile unit + * @return array compile unit + */ + @Override + public CompileUnit getCompileUnit() { + return compileUnit; + } + } +}
--- a/src/jdk/nashorn/internal/ir/debug/JSONWriter.java Mon Sep 28 15:36:24 2015 -0700 +++ b/src/jdk/nashorn/internal/ir/debug/JSONWriter.java Tue Oct 06 11:04:55 2015 -0700 @@ -48,7 +48,6 @@ import jdk.nashorn.internal.ir.IndexNode; import jdk.nashorn.internal.ir.JoinPredecessorExpression; import jdk.nashorn.internal.ir.LabelNode; -import jdk.nashorn.internal.ir.LexicalContext; import jdk.nashorn.internal.ir.LiteralNode; import jdk.nashorn.internal.ir.Node; import jdk.nashorn.internal.ir.ObjectNode; @@ -65,7 +64,7 @@ import jdk.nashorn.internal.ir.VarNode; import jdk.nashorn.internal.ir.WhileNode; import jdk.nashorn.internal.ir.WithNode; -import jdk.nashorn.internal.ir.visitor.NodeVisitor; +import jdk.nashorn.internal.ir.visitor.SimpleNodeVisitor; import jdk.nashorn.internal.parser.JSONParser; import jdk.nashorn.internal.parser.Lexer.RegexToken; import jdk.nashorn.internal.parser.Parser; @@ -77,7 +76,7 @@ /** * This IR writer produces a JSON string that represents AST as a JSON string. */ -public final class JSONWriter extends NodeVisitor<LexicalContext> { +public final class JSONWriter extends SimpleNodeVisitor { /** * Returns AST as JSON compatible string. @@ -939,7 +938,6 @@ // Internals below private JSONWriter(final boolean includeLocation) { - super(new LexicalContext()); this.buf = new StringBuilder(); this.includeLocation = includeLocation; }
--- a/src/jdk/nashorn/internal/ir/debug/PrintVisitor.java Mon Sep 28 15:36:24 2015 -0700 +++ b/src/jdk/nashorn/internal/ir/debug/PrintVisitor.java Tue Oct 06 11:04:55 2015 -0700 @@ -41,7 +41,6 @@ import jdk.nashorn.internal.ir.JoinPredecessor; import jdk.nashorn.internal.ir.JoinPredecessorExpression; import jdk.nashorn.internal.ir.LabelNode; -import jdk.nashorn.internal.ir.LexicalContext; import jdk.nashorn.internal.ir.LocalVariableConversion; import jdk.nashorn.internal.ir.Node; import jdk.nashorn.internal.ir.SplitNode; @@ -53,7 +52,7 @@ import jdk.nashorn.internal.ir.VarNode; import jdk.nashorn.internal.ir.WhileNode; import jdk.nashorn.internal.ir.WithNode; -import jdk.nashorn.internal.ir.visitor.NodeVisitor; +import jdk.nashorn.internal.ir.visitor.SimpleNodeVisitor; /** * Print out the AST as human readable source code. @@ -61,7 +60,7 @@ * * see the flags --print-parse and --print-lower-parse */ -public final class PrintVisitor extends NodeVisitor<LexicalContext> { +public final class PrintVisitor extends SimpleNodeVisitor { /** Tab width */ private static final int TABWIDTH = 4; @@ -96,7 +95,6 @@ * @param printTypes should we print optimistic and inferred types? */ public PrintVisitor(final boolean printLineNumbers, final boolean printTypes) { - super(new LexicalContext()); this.EOLN = System.lineSeparator(); this.sb = new StringBuilder(); this.printLineNumbers = printLineNumbers;
--- a/src/jdk/nashorn/internal/ir/visitor/NodeOperatorVisitor.java Mon Sep 28 15:36:24 2015 -0700 +++ b/src/jdk/nashorn/internal/ir/visitor/NodeOperatorVisitor.java Tue Oct 06 11:04:55 2015 -0700 @@ -34,7 +34,7 @@ * Like NodeVisitor but navigating further into operators. * @param <T> Lexical context class for this NodeOperatorVisitor */ -public class NodeOperatorVisitor<T extends LexicalContext> extends NodeVisitor<T> { +public abstract class NodeOperatorVisitor<T extends LexicalContext> extends NodeVisitor<T> { /** * Constructor *
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk/nashorn/internal/ir/visitor/SimpleNodeVisitor.java Tue Oct 06 11:04:55 2015 -0700 @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.nashorn.internal.ir.visitor; + +import jdk.nashorn.internal.ir.LexicalContext; + +/** + * Convenience base class for a {@link NodeVisitor} with a plain {@link LexicalContext}. + */ +public abstract class SimpleNodeVisitor extends NodeVisitor<LexicalContext> { + + /** + * Creates a new simple node visitor. + */ + public SimpleNodeVisitor() { + super(new LexicalContext()); + } +}
--- a/src/jdk/nashorn/internal/objects/Global.java Mon Sep 28 15:36:24 2015 -0700 +++ b/src/jdk/nashorn/internal/objects/Global.java Tue Oct 06 11:04:55 2015 -0700 @@ -928,8 +928,6 @@ private ThreadLocal<ScriptContext> scontext; // current ScriptEngine associated - can be null. private ScriptEngine engine; - // initial ScriptContext - can be null - private volatile ScriptContext initscontext; // ES6 global lexical scope. private final LexicalScope lexicalScope; @@ -957,7 +955,7 @@ private ScriptContext currentContext() { final ScriptContext sc = scontext != null? scontext.get() : null; - return sc == null? initscontext : sc; + return (sc != null)? sc : (engine != null? engine.getContext() : null); } @Override @@ -1067,16 +1065,14 @@ * of the global scope object. * * @param eng ScriptEngine to initialize - * @param ctxt ScriptContext to initialize */ - public void initBuiltinObjects(final ScriptEngine eng, final ScriptContext ctxt) { + public void initBuiltinObjects(final ScriptEngine eng) { if (this.builtinObject != null) { // already initialized, just return return; } this.engine = eng; - this.initscontext = ctxt; if (this.engine != null) { this.scontext = new ThreadLocal<>(); }
--- a/src/jdk/nashorn/internal/objects/NativeJSAdapter.java Mon Sep 28 15:36:24 2015 -0700 +++ b/src/jdk/nashorn/internal/objects/NativeJSAdapter.java Tue Oct 06 11:04:55 2015 -0700 @@ -627,7 +627,7 @@ return new GuardedInvocation(MH.dropArguments(MH.constant(Object.class, func.createBound(this, new Object[] { name })), 0, Object.class), testJSAdaptor(adaptee, null, null, null), - adaptee.getProtoSwitchPoint(__call__, find.getOwner())); + adaptee.getProtoSwitchPoints(__call__, find.getOwner()), null); } } throw typeError("no.such.function", desc.getNameToken(2), ScriptRuntime.safeToString(this)); @@ -698,7 +698,7 @@ return new GuardedInvocation( methodHandle, testJSAdaptor(adaptee, findData.getGetter(Object.class, INVALID_PROGRAM_POINT, null), findData.getOwner(), func), - adaptee.getProtoSwitchPoint(hook, findData.getOwner())); + adaptee.getProtoSwitchPoints(hook, findData.getOwner()), null); } } } @@ -710,7 +710,7 @@ final MethodHandle methodHandle = hook.equals(__put__) ? MH.asType(Lookup.EMPTY_SETTER, type) : Lookup.emptyGetter(type.returnType()); - return new GuardedInvocation(methodHandle, testJSAdaptor(adaptee, null, null, null), adaptee.getProtoSwitchPoint(hook, null)); + return new GuardedInvocation(methodHandle, testJSAdaptor(adaptee, null, null, null), adaptee.getProtoSwitchPoints(hook, null), null); } }
--- a/src/jdk/nashorn/internal/objects/NativeNumber.java Mon Sep 28 15:36:24 2015 -0700 +++ b/src/jdk/nashorn/internal/objects/NativeNumber.java Tue Oct 06 11:04:55 2015 -0700 @@ -33,6 +33,7 @@ import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; +import java.math.RoundingMode; import java.text.NumberFormat; import java.util.Locale; import jdk.internal.dynalink.linker.GuardedInvocation; @@ -187,6 +188,7 @@ format.setMinimumFractionDigits(fractionDigits); format.setMaximumFractionDigits(fractionDigits); format.setGroupingUsed(false); + format.setRoundingMode(RoundingMode.HALF_UP); return format.format(x); }
--- a/src/jdk/nashorn/internal/runtime/AllocationStrategy.java Mon Sep 28 15:36:24 2015 -0700 +++ b/src/jdk/nashorn/internal/runtime/AllocationStrategy.java Tue Oct 06 11:04:55 2015 -0700 @@ -29,6 +29,7 @@ import java.io.Serializable; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; +import java.lang.ref.WeakReference; import jdk.nashorn.internal.codegen.Compiler; import jdk.nashorn.internal.codegen.CompilerConstants; import jdk.nashorn.internal.codegen.ObjectClassGenerator; @@ -53,6 +54,9 @@ /** lazily generated allocator */ private transient MethodHandle allocator; + /** Last used allocator map */ + private transient AllocatorMap lastMap; + /** * Construct an allocation strategy with the given map and class name. * @param fieldCount number of fields in the allocated object @@ -71,11 +75,49 @@ return allocatorClassName; } - PropertyMap getAllocatorMap() { - // Create a new map for each function instance - return PropertyMap.newMap(null, getAllocatorClassName(), 0, fieldCount, 0); + /** + * Get the property map for the allocated object. + * @param prototype the prototype object + * @return the property map + */ + synchronized PropertyMap getAllocatorMap(final ScriptObject prototype) { + assert prototype != null; + final PropertyMap protoMap = prototype.getMap(); + + if (lastMap != null) { + if (!lastMap.hasSharedProtoMap()) { + if (lastMap.hasSamePrototype(prototype)) { + return lastMap.allocatorMap; + } + if (lastMap.hasSameProtoMap(protoMap) && lastMap.hasUnchangedProtoMap()) { + // Convert to shared prototype map. Allocated objects will use the same property map + // that can be used as long as none of the prototypes modify the shared proto map. + final PropertyMap allocatorMap = PropertyMap.newMap(null, getAllocatorClassName(), 0, fieldCount, 0); + final SharedPropertyMap sharedProtoMap = new SharedPropertyMap(protoMap); + allocatorMap.setSharedProtoMap(sharedProtoMap); + prototype.setMap(sharedProtoMap); + lastMap = new AllocatorMap(prototype, protoMap, allocatorMap); + return allocatorMap; + } + } + + if (lastMap.hasValidSharedProtoMap() && lastMap.hasSameProtoMap(protoMap)) { + prototype.setMap(lastMap.getSharedProtoMap()); + return lastMap.allocatorMap; + } + } + + final PropertyMap allocatorMap = PropertyMap.newMap(null, getAllocatorClassName(), 0, fieldCount, 0); + lastMap = new AllocatorMap(prototype, protoMap, allocatorMap); + + return allocatorMap; } + /** + * Allocate an object with the given property map + * @param map the property map + * @return the allocated object + */ ScriptObject allocate(final PropertyMap map) { try { if (allocator == null) { @@ -94,4 +136,43 @@ public String toString() { return "AllocationStrategy[fieldCount=" + fieldCount + "]"; } + + static class AllocatorMap { + final private WeakReference<ScriptObject> prototype; + final private WeakReference<PropertyMap> prototypeMap; + + private PropertyMap allocatorMap; + + AllocatorMap(final ScriptObject prototype, final PropertyMap protoMap, final PropertyMap allocMap) { + this.prototype = new WeakReference<>(prototype); + this.prototypeMap = new WeakReference<>(protoMap); + this.allocatorMap = allocMap; + } + + boolean hasSamePrototype(final ScriptObject proto) { + return prototype.get() == proto; + } + + boolean hasSameProtoMap(final PropertyMap protoMap) { + return prototypeMap.get() == protoMap || allocatorMap.getSharedProtoMap() == protoMap; + } + + boolean hasUnchangedProtoMap() { + final ScriptObject proto = prototype.get(); + return proto != null && proto.getMap() == prototypeMap.get(); + } + + boolean hasSharedProtoMap() { + return getSharedProtoMap() != null; + } + + boolean hasValidSharedProtoMap() { + return hasSharedProtoMap() && getSharedProtoMap().isValidSharedProtoMap(); + } + + PropertyMap getSharedProtoMap() { + return allocatorMap.getSharedProtoMap(); + } + + } }
--- a/src/jdk/nashorn/internal/runtime/CodeInstaller.java Mon Sep 28 15:36:24 2015 -0700 +++ b/src/jdk/nashorn/internal/runtime/CodeInstaller.java Tue Oct 06 11:04:55 2015 -0700 @@ -38,15 +38,14 @@ * The compiler still retains most of the state around code emission * and management internally, so this is to avoid passing around any * logic that isn't directly related to installing a class - * @param <T> owner class type for this code installer * */ -public interface CodeInstaller<T> { +public interface CodeInstaller { /** - * Return the owner for the CodeInstaller, e.g. a {@link Context} - * @return owner + * Return the {@link Context} associated with this code installer. + * @return the context. */ - public T getOwner(); + public Context getContext(); /** * Install a class. @@ -106,7 +105,7 @@ * new, independent class loader. * @return a new code installer with a new independent class loader. */ - public CodeInstaller<T> withNewLoader(); + public CodeInstaller withNewLoader(); /** * Returns true if this code installer is compatible with the other code installer. Compatibility is expected to be @@ -115,6 +114,6 @@ * @param other the other code installer tested for compatibility with this code installer. * @return true if this code installer is compatible with the other code installer. */ - public boolean isCompatibleWith(CodeInstaller<T> other); + public boolean isCompatibleWith(CodeInstaller other); }
--- a/src/jdk/nashorn/internal/runtime/Context.java Mon Sep 28 15:36:24 2015 -0700 +++ b/src/jdk/nashorn/internal/runtime/Context.java Tue Oct 06 11:04:55 2015 -0700 @@ -167,7 +167,7 @@ * ContextCodeInstaller that has the privilege of installing classes in the Context. * Can only be instantiated from inside the context and is opaque to other classes */ - public static class ContextCodeInstaller implements CodeInstaller<ScriptEnvironment> { + public static class ContextCodeInstaller implements CodeInstaller { private final Context context; private final ScriptLoader loader; private final CodeSource codeSource; @@ -185,13 +185,9 @@ this.codeSource = codeSource; } - /** - * Return the script environment for this installer - * @return ScriptEnvironment - */ @Override - public ScriptEnvironment getOwner() { - return context.env; + public Context getContext() { + return context; } @Override @@ -254,7 +250,7 @@ } @Override - public CodeInstaller<ScriptEnvironment> withNewLoader() { + public CodeInstaller withNewLoader() { // Reuse this installer if we're within our limits. if (usageCount < MAX_USAGES && bytesDefined < MAX_BYTES_DEFINED) { return this; @@ -263,7 +259,7 @@ } @Override - public boolean isCompatibleWith(final CodeInstaller<ScriptEnvironment> other) { + public boolean isCompatibleWith(final CodeInstaller other) { if (other instanceof ContextCodeInstaller) { final ContextCodeInstaller cci = (ContextCodeInstaller)other; return cci.context == context && cci.codeSource == codeSource; @@ -987,6 +983,16 @@ } /** + * Is {@code className} the name of a structure class? + * + * @param className a class name + * @return true if className is a structure class name + */ + public static boolean isStructureClass(final String className) { + return StructureLoader.isStructureClass(className); + } + + /** * Checks that the given Class can be accessed from no permissions context. * * @param clazz Class object @@ -1129,17 +1135,16 @@ * * @param global the global * @param engine the associated ScriptEngine instance, can be null - * @param ctxt the initial ScriptContext, can be null * @return the initialized global scope object. */ - public Global initGlobal(final Global global, final ScriptEngine engine, final ScriptContext ctxt) { + public Global initGlobal(final Global global, final ScriptEngine engine) { // Need only minimal global object, if we are just compiling. if (!env._compile_only) { final Global oldGlobal = Context.getGlobal(); try { Context.setGlobal(global); // initialize global scope with builtin global objects - global.initBuiltinObjects(engine, ctxt); + global.initBuiltinObjects(engine); } finally { Context.setGlobal(oldGlobal); } @@ -1155,7 +1160,7 @@ * @return the initialized global scope object. */ public Global initGlobal(final Global global) { - return initGlobal(global, null, null); + return initGlobal(global, null); } /** @@ -1300,14 +1305,12 @@ final URL url = source.getURL(); final ScriptLoader loader = env._loader_per_compile ? createNewLoader() : scriptLoader; final CodeSource cs = new CodeSource(url, (CodeSigner[])null); - final CodeInstaller<ScriptEnvironment> installer = new ContextCodeInstaller(this, loader, cs); + final CodeInstaller installer = new ContextCodeInstaller(this, loader, cs); if (storedScript == null) { final CompilationPhases phases = Compiler.CompilationPhases.COMPILE_ALL; - final Compiler compiler = new Compiler( - this, - env, + final Compiler compiler = Compiler.forInitialCompilation( installer, source, errMan,
--- a/src/jdk/nashorn/internal/runtime/Debug.java Mon Sep 28 15:36:24 2015 -0700 +++ b/src/jdk/nashorn/internal/runtime/Debug.java Tue Oct 06 11:04:55 2015 -0700 @@ -26,6 +26,8 @@ package jdk.nashorn.internal.runtime; import static jdk.nashorn.internal.parser.TokenType.EOF; + +import jdk.nashorn.api.scripting.NashornException; import jdk.nashorn.internal.parser.Lexer; import jdk.nashorn.internal.parser.Token; import jdk.nashorn.internal.parser.TokenStream; @@ -63,6 +65,15 @@ } /** + * Return a formatted script stack trace string with frames information separated by '\n'. + * This is a shortcut for {@code NashornException.getScriptStackString(new Throwable())}. + * @return formatted stack trace string + */ + public static String scriptStack() { + return NashornException.getScriptStackString(new Throwable()); + } + + /** * Return the system identity hashcode for an object as a human readable * string *
--- a/src/jdk/nashorn/internal/runtime/PropertyListeners.java Mon Sep 28 15:36:24 2015 -0700 +++ b/src/jdk/nashorn/internal/runtime/PropertyListeners.java Tue Oct 06 11:04:55 2015 -0700 @@ -75,16 +75,20 @@ } /** - * Return listeners added to this ScriptObject. + * Return number of listeners added to a ScriptObject. * @param obj the object * @return the listener count */ public static int getListenerCount(final ScriptObject obj) { - final PropertyListeners propertyListeners = obj.getMap().getListeners(); - if (propertyListeners != null) { - return propertyListeners.listeners == null ? 0 : propertyListeners.listeners.size(); - } - return 0; + return obj.getMap().getListenerCount(); + } + + /** + * Return the number of listeners added to this PropertyListeners instance. + * @return the listener count; + */ + public int getListenerCount() { + return listeners == null ? 0 : listeners.size(); } // Property listener management methods @@ -156,7 +160,7 @@ final WeakPropertyMapSet set = listeners.get(prop.getKey()); if (set != null) { for (final PropertyMap propertyMap : set.elements()) { - propertyMap.propertyAdded(prop); + propertyMap.propertyAdded(prop, false); } listeners.remove(prop.getKey()); if (Context.DEBUG) { @@ -176,7 +180,7 @@ final WeakPropertyMapSet set = listeners.get(prop.getKey()); if (set != null) { for (final PropertyMap propertyMap : set.elements()) { - propertyMap.propertyDeleted(prop); + propertyMap.propertyDeleted(prop, false); } listeners.remove(prop.getKey()); if (Context.DEBUG) { @@ -198,7 +202,7 @@ final WeakPropertyMapSet set = listeners.get(oldProp.getKey()); if (set != null) { for (final PropertyMap propertyMap : set.elements()) { - propertyMap.propertyModified(oldProp, newProp); + propertyMap.propertyModified(oldProp, newProp, false); } listeners.remove(oldProp.getKey()); if (Context.DEBUG) { @@ -215,7 +219,7 @@ if (listeners != null) { for (final WeakPropertyMapSet set : listeners.values()) { for (final PropertyMap propertyMap : set.elements()) { - propertyMap.protoChanged(); + propertyMap.protoChanged(false); } } listeners.clear();
--- a/src/jdk/nashorn/internal/runtime/PropertyMap.java Mon Sep 28 15:36:24 2015 -0700 +++ b/src/jdk/nashorn/internal/runtime/PropertyMap.java Tue Oct 06 11:04:55 2015 -0700 @@ -34,7 +34,9 @@ import java.io.ObjectOutputStream; import java.io.Serializable; import java.lang.invoke.SwitchPoint; +import java.lang.ref.Reference; import java.lang.ref.SoftReference; +import java.lang.ref.WeakReference; import java.util.Arrays; import java.util.BitSet; import java.util.Collection; @@ -43,6 +45,7 @@ import java.util.NoSuchElementException; import java.util.WeakHashMap; import java.util.concurrent.atomic.LongAdder; +import jdk.nashorn.internal.runtime.options.Options; import jdk.nashorn.internal.scripts.JO; /** @@ -54,35 +57,49 @@ * All property maps are immutable. If a property is added, modified or removed, the mutator * will return a new map. */ -public final class PropertyMap implements Iterable<Object>, Serializable { +public class PropertyMap implements Iterable<Object>, Serializable { + private static final int INITIAL_SOFT_REFERENCE_DERIVATION_LIMIT = + Math.max(0, Options.getIntProperty("nashorn.propertyMap.softReferenceDerivationLimit", 32)); + /** Used for non extensible PropertyMaps, negative logic as the normal case is extensible. See {@link ScriptObject#preventExtensions()} */ - public static final int NOT_EXTENSIBLE = 0b0000_0001; + private static final int NOT_EXTENSIBLE = 0b0000_0001; /** Does this map contain valid array keys? */ - public static final int CONTAINS_ARRAY_KEYS = 0b0000_0010; + private static final int CONTAINS_ARRAY_KEYS = 0b0000_0010; /** Map status flags. */ - private int flags; + private final int flags; /** Map of properties. */ private transient PropertyHashMap properties; /** Number of fields in use. */ - private int fieldCount; + private final int fieldCount; /** Number of fields available. */ private final int fieldMaximum; /** Length of spill in use. */ - private int spillLength; + private final int spillLength; /** Structure class name */ - private String className; + private final String className; + + /** + * Countdown of number of times this property map has been derived from another property map. When it + * reaches zero, the property map will start using weak references instead of soft references to hold on + * to its history elements. + */ + private final int softReferenceDerivationLimit; + + /** A reference to the expected shared prototype property map. If this is set this + * property map should only be used if it the same as the actual prototype map. */ + private transient SharedPropertyMap sharedProtoMap; /** {@link SwitchPoint}s for gets on inherited properties. */ - private transient HashMap<String, SwitchPoint> protoGetSwitches; + private transient HashMap<String, SwitchPoint> protoSwitches; /** History of maps, used to limit map duplication. */ - private transient WeakHashMap<Property, SoftReference<PropertyMap>> history; + private transient WeakHashMap<Property, Reference<PropertyMap>> history; /** History of prototypes, used to limit map duplication. */ private transient WeakHashMap<ScriptObject, SoftReference<PropertyMap>> protoHistory; @@ -95,24 +112,22 @@ private static final long serialVersionUID = -7041836752008732533L; /** - * Constructor. + * Constructs a new property map. * * @param properties A {@link PropertyHashMap} with initial contents. * @param fieldCount Number of fields in use. * @param fieldMaximum Number of fields available. * @param spillLength Number of spill slots used. - * @param containsArrayKeys True if properties contain numeric keys */ - private PropertyMap(final PropertyHashMap properties, final String className, final int fieldCount, - final int fieldMaximum, final int spillLength, final boolean containsArrayKeys) { + private PropertyMap(final PropertyHashMap properties, final int flags, final String className, + final int fieldCount, final int fieldMaximum, final int spillLength) { this.properties = properties; this.className = className; this.fieldCount = fieldCount; this.fieldMaximum = fieldMaximum; this.spillLength = spillLength; - if (containsArrayKeys) { - setContainsArrayKeys(); - } + this.flags = flags; + this.softReferenceDerivationLimit = INITIAL_SOFT_REFERENCE_DERIVATION_LIMIT; if (Context.DEBUG) { count.increment(); @@ -120,20 +135,23 @@ } /** - * Cloning constructor. + * Constructs a clone of {@code propertyMap} with changed properties, flags, or boundaries. * * @param propertyMap Existing property map. * @param properties A {@link PropertyHashMap} with a new set of properties. */ - private PropertyMap(final PropertyMap propertyMap, final PropertyHashMap properties) { + private PropertyMap(final PropertyMap propertyMap, final PropertyHashMap properties, final int flags, final int fieldCount, final int spillLength, final int softReferenceDerivationLimit) { this.properties = properties; - this.flags = propertyMap.flags; - this.spillLength = propertyMap.spillLength; - this.fieldCount = propertyMap.fieldCount; + this.flags = flags; + this.spillLength = spillLength; + this.fieldCount = fieldCount; this.fieldMaximum = propertyMap.fieldMaximum; + this.className = propertyMap.className; // We inherit the parent property listeners instance. It will be cloned when a new listener is added. this.listeners = propertyMap.listeners; this.freeSlots = propertyMap.freeSlots; + this.sharedProtoMap = propertyMap.sharedProtoMap; + this.softReferenceDerivationLimit = softReferenceDerivationLimit; if (Context.DEBUG) { count.increment(); @@ -142,12 +160,12 @@ } /** - * Cloning constructor. + * Constructs an exact clone of {@code propertyMap}. * * @param propertyMap Existing property map. */ - private PropertyMap(final PropertyMap propertyMap) { - this(propertyMap, propertyMap.properties); + protected PropertyMap(final PropertyMap propertyMap) { + this(propertyMap, propertyMap.properties, propertyMap.flags, propertyMap.fieldCount, propertyMap.spillLength, propertyMap.softReferenceDerivationLimit); } private void writeObject(final ObjectOutputStream out) throws IOException { @@ -183,7 +201,7 @@ */ public static PropertyMap newMap(final Collection<Property> properties, final String className, final int fieldCount, final int fieldMaximum, final int spillLength) { final PropertyHashMap newProperties = EMPTY_HASHMAP.immutableAdd(properties); - return new PropertyMap(newProperties, className, fieldCount, fieldMaximum, spillLength, false); + return new PropertyMap(newProperties, 0, className, fieldCount, fieldMaximum, spillLength); } /** @@ -205,7 +223,7 @@ * @return New empty {@link PropertyMap}. */ public static PropertyMap newMap(final Class<? extends ScriptObject> clazz) { - return new PropertyMap(EMPTY_HASHMAP, clazz.getName(), 0, 0, 0, false); + return new PropertyMap(EMPTY_HASHMAP, 0, clazz.getName(), 0, 0, 0); } /** @@ -227,12 +245,12 @@ } /** - * Get the listeners of this map, or null if none exists + * Get the number of listeners of this map * - * @return the listeners + * @return the number of listeners */ - public PropertyListeners getListeners() { - return listeners; + public int getListenerCount() { + return listeners == null ? 0 : listeners.getListenerCount(); } /** @@ -253,9 +271,12 @@ * A new property is being added. * * @param property The new Property added. + * @param isSelf was the property added to this map? */ - public void propertyAdded(final Property property) { - invalidateProtoGetSwitchPoint(property); + public void propertyAdded(final Property property, final boolean isSelf) { + if (!isSelf) { + invalidateProtoSwitchPoint(property.getKey()); + } if (listeners != null) { listeners.propertyAdded(property); } @@ -265,9 +286,12 @@ * An existing property is being deleted. * * @param property The property being deleted. + * @param isSelf was the property deleted from this map? */ - public void propertyDeleted(final Property property) { - invalidateProtoGetSwitchPoint(property); + public void propertyDeleted(final Property property, final boolean isSelf) { + if (!isSelf) { + invalidateProtoSwitchPoint(property.getKey()); + } if (listeners != null) { listeners.propertyDeleted(property); } @@ -278,9 +302,12 @@ * * @param oldProperty The old property * @param newProperty The new property + * @param isSelf was the property modified on this map? */ - public void propertyModified(final Property oldProperty, final Property newProperty) { - invalidateProtoGetSwitchPoint(oldProperty); + public void propertyModified(final Property oldProperty, final Property newProperty, final boolean isSelf) { + if (!isSelf) { + invalidateProtoSwitchPoint(oldProperty.getKey()); + } if (listeners != null) { listeners.propertyModified(oldProperty, newProperty); } @@ -288,9 +315,15 @@ /** * The prototype of an object associated with this {@link PropertyMap} is changed. + * + * @param isSelf was the prototype changed on the object using this map? */ - public void protoChanged() { - invalidateAllProtoGetSwitchPoints(); + public void protoChanged(final boolean isSelf) { + if (!isSelf) { + invalidateAllProtoSwitchPoints(); + } else if (sharedProtoMap != null) { + sharedProtoMap.invalidateSwitchPoint(); + } if (listeners != null) { listeners.protoChanged(); } @@ -303,14 +336,14 @@ * @return A shared {@link SwitchPoint} for the property. */ public synchronized SwitchPoint getSwitchPoint(final String key) { - if (protoGetSwitches == null) { - protoGetSwitches = new HashMap<>(); + if (protoSwitches == null) { + protoSwitches = new HashMap<>(); } - SwitchPoint switchPoint = protoGetSwitches.get(key); + SwitchPoint switchPoint = protoSwitches.get(key); if (switchPoint == null) { switchPoint = new SwitchPoint(); - protoGetSwitches.put(key, switchPoint); + protoSwitches.put(key, switchPoint); } return switchPoint; @@ -319,19 +352,17 @@ /** * Indicate that a prototype property has changed. * - * @param property {@link Property} to invalidate. + * @param key {@link Property} key to invalidate. */ - synchronized void invalidateProtoGetSwitchPoint(final Property property) { - if (protoGetSwitches != null) { - - final String key = property.getKey(); - final SwitchPoint sp = protoGetSwitches.get(key); + synchronized void invalidateProtoSwitchPoint(final String key) { + if (protoSwitches != null) { + final SwitchPoint sp = protoSwitches.get(key); if (sp != null) { - protoGetSwitches.remove(key); + protoSwitches.remove(key); if (Context.DEBUG) { protoInvalidations.increment(); } - SwitchPoint.invalidateAll(new SwitchPoint[] { sp }); + SwitchPoint.invalidateAll(new SwitchPoint[]{sp}); } } } @@ -339,15 +370,15 @@ /** * Indicate that proto itself has changed in hierarchy somewhere. */ - synchronized void invalidateAllProtoGetSwitchPoints() { - if (protoGetSwitches != null) { - final int size = protoGetSwitches.size(); + synchronized void invalidateAllProtoSwitchPoints() { + if (protoSwitches != null) { + final int size = protoSwitches.size(); if (size > 0) { if (Context.DEBUG) { protoInvalidations.add(size); } - SwitchPoint.invalidateAll(protoGetSwitches.values().toArray(new SwitchPoint[size])); - protoGetSwitches.clear(); + SwitchPoint.invalidateAll(protoSwitches.values().toArray(new SwitchPoint[size])); + protoSwitches.clear(); } } } @@ -363,7 +394,7 @@ * @return New {@link PropertyMap} with {@link Property} added. */ PropertyMap addPropertyBind(final AccessorProperty property, final Object bindTo) { - // No need to store bound property in the history as bound properties can't be reused. + // We must not store bound property in the history as bound properties can't be reused. return addPropertyNoHistory(new AccessorProperty(property, bindTo)); } @@ -376,16 +407,16 @@ return property.isSpill() ? slot + fieldMaximum : slot; } - // Update boundaries and flags after a property has been added - private void updateFlagsAndBoundaries(final Property newProperty) { - if(newProperty.isSpill()) { - spillLength = Math.max(spillLength, newProperty.getSlot() + 1); - } else { - fieldCount = Math.max(fieldCount, newProperty.getSlot() + 1); - } - if (isValidArrayIndex(getArrayIndex(newProperty.getKey()))) { - setContainsArrayKeys(); - } + private int newSpillLength(final Property newProperty) { + return newProperty.isSpill() ? Math.max(spillLength, newProperty.getSlot() + 1) : spillLength; + } + + private int newFieldCount(final Property newProperty) { + return !newProperty.isSpill() ? Math.max(fieldCount, newProperty.getSlot() + 1) : fieldCount; + } + + private int newFlags(final Property newProperty) { + return isValidArrayIndex(getArrayIndex(newProperty.getKey())) ? flags | CONTAINS_ARRAY_KEYS : flags; } // Update the free slots bitmap for a property that has been deleted and/or added. This method is not synchronized @@ -420,16 +451,9 @@ * @param property {@link Property} being added. * @return New {@link PropertyMap} with {@link Property} added. */ - public PropertyMap addPropertyNoHistory(final Property property) { - if (listeners != null) { - listeners.propertyAdded(property); - } - final PropertyHashMap newProperties = properties.immutableAdd(property); - final PropertyMap newMap = new PropertyMap(this, newProperties); - newMap.updateFlagsAndBoundaries(property); - newMap.updateFreeSlots(null, property); - - return newMap; + public final PropertyMap addPropertyNoHistory(final Property property) { + propertyAdded(property, true); + return addPropertyInternal(property); } /** @@ -439,23 +463,29 @@ * * @return New {@link PropertyMap} with {@link Property} added. */ - public synchronized PropertyMap addProperty(final Property property) { - if (listeners != null) { - listeners.propertyAdded(property); - } + public final synchronized PropertyMap addProperty(final Property property) { + propertyAdded(property, true); PropertyMap newMap = checkHistory(property); if (newMap == null) { - final PropertyHashMap newProperties = properties.immutableAdd(property); - newMap = new PropertyMap(this, newProperties); - newMap.updateFlagsAndBoundaries(property); - newMap.updateFreeSlots(null, property); + newMap = addPropertyInternal(property); addToHistory(property, newMap); } return newMap; } + private PropertyMap deriveMap(final PropertyHashMap newProperties, final int newFlags, final int newFieldCount, final int newSpillLength) { + return new PropertyMap(this, newProperties, newFlags, newFieldCount, newSpillLength, softReferenceDerivationLimit == 0 ? 0 : softReferenceDerivationLimit - 1); + } + + private PropertyMap addPropertyInternal(final Property property) { + final PropertyHashMap newProperties = properties.immutableAdd(property); + final PropertyMap newMap = deriveMap(newProperties, newFlags(property), newFieldCount(property), newSpillLength(property)); + newMap.updateFreeSlots(null, property); + return newMap; + } + /** * Remove a property from a map. Cloning or using an existing map if available. * @@ -463,10 +493,8 @@ * * @return New {@link PropertyMap} with {@link Property} removed or {@code null} if not found. */ - public synchronized PropertyMap deleteProperty(final Property property) { - if (listeners != null) { - listeners.propertyDeleted(property); - } + public final synchronized PropertyMap deleteProperty(final Property property) { + propertyDeleted(property, true); PropertyMap newMap = checkHistory(property); final String key = property.getKey(); @@ -477,13 +505,13 @@ // If deleted property was last field or spill slot we can make it reusable by reducing field/slot count. // Otherwise mark it as free in free slots bitset. if (isSpill && slot >= 0 && slot == spillLength - 1) { - newMap = new PropertyMap(newProperties, className, fieldCount, fieldMaximum, spillLength - 1, containsArrayKeys()); + newMap = deriveMap(newProperties, flags, fieldCount, spillLength - 1); newMap.freeSlots = freeSlots; } else if (!isSpill && slot >= 0 && slot == fieldCount - 1) { - newMap = new PropertyMap(newProperties, className, fieldCount - 1, fieldMaximum, spillLength, containsArrayKeys()); + newMap = deriveMap(newProperties, flags, fieldCount - 1, spillLength); newMap.freeSlots = freeSlots; } else { - newMap = new PropertyMap(this, newProperties); + newMap = deriveMap(newProperties, flags, fieldCount, spillLength); newMap.updateFreeSlots(property, null); } addToHistory(property, newMap); @@ -500,13 +528,8 @@ * * @return New {@link PropertyMap} with {@link Property} replaced. */ - public PropertyMap replaceProperty(final Property oldProperty, final Property newProperty) { - if (listeners != null) { - listeners.propertyModified(oldProperty, newProperty); - } - // Add replaces existing property. - final PropertyHashMap newProperties = properties.immutableReplace(oldProperty, newProperty); - final PropertyMap newMap = new PropertyMap(this, newProperties); + public final PropertyMap replaceProperty(final Property oldProperty, final Property newProperty) { + propertyModified(oldProperty, newProperty, true); /* * See ScriptObject.modifyProperty and ScriptObject.setUserAccessors methods. * @@ -528,14 +551,17 @@ newProperty instanceof UserAccessorProperty : "arbitrary replaceProperty attempted " + sameType + " oldProperty=" + oldProperty.getClass() + " newProperty=" + newProperty.getClass() + " [" + oldProperty.getLocalType() + " => " + newProperty.getLocalType() + "]"; - newMap.flags = flags; - /* * spillLength remains same in case (1) and (2) because of slot reuse. Only for case (3), we need * to add spill count of the newly added UserAccessorProperty property. */ + final int newSpillLength = sameType ? spillLength : Math.max(spillLength, newProperty.getSlot() + 1); + + // Add replaces existing property. + final PropertyHashMap newProperties = properties.immutableReplace(oldProperty, newProperty); + final PropertyMap newMap = deriveMap(newProperties, flags, fieldCount, newSpillLength); + if (!sameType) { - newMap.spillLength = Math.max(spillLength, newProperty.getSlot() + 1); newMap.updateFreeSlots(oldProperty, newProperty); } return newMap; @@ -551,7 +577,7 @@ * @param propertyFlags attribute flags of the property * @return the newly created UserAccessorProperty */ - public UserAccessorProperty newUserAccessors(final String key, final int propertyFlags) { + public final UserAccessorProperty newUserAccessors(final String key, final int propertyFlags) { return new UserAccessorProperty(key, propertyFlags, getFreeSpillSlot()); } @@ -562,7 +588,7 @@ * * @return {@link Property} matching key. */ - public Property findProperty(final String key) { + public final Property findProperty(final String key) { return properties.find(key); } @@ -573,12 +599,12 @@ * * @return New {@link PropertyMap} with added properties. */ - public PropertyMap addAll(final PropertyMap other) { + public final PropertyMap addAll(final PropertyMap other) { assert this != other : "adding property map to itself"; final Property[] otherProperties = other.properties.getProperties(); final PropertyHashMap newProperties = properties.immutableAdd(otherProperties); - final PropertyMap newMap = new PropertyMap(this, newProperties); + final PropertyMap newMap = deriveMap(newProperties, flags, fieldCount, spillLength); for (final Property property : otherProperties) { // This method is only safe to use with non-slotted, native getter/setter properties assert property.getSlot() == -1; @@ -593,19 +619,26 @@ * * @return Properties as an array. */ - public Property[] getProperties() { + public final Property[] getProperties() { return properties.getProperties(); } /** + * Return the name of the class of objects using this property map. + * + * @return class name of owner objects. + */ + public final String getClassName() { + return className; + } + + /** * Prevents the map from having additional properties. * * @return New map with {@link #NOT_EXTENSIBLE} flag set. */ PropertyMap preventExtensions() { - final PropertyMap newMap = new PropertyMap(this); - newMap.flags |= NOT_EXTENSIBLE; - return newMap; + return deriveMap(properties, flags | NOT_EXTENSIBLE, fieldCount, spillLength); } /** @@ -621,10 +654,7 @@ newProperties = newProperties.immutableAdd(oldProperty.addFlags(Property.NOT_CONFIGURABLE)); } - final PropertyMap newMap = new PropertyMap(this, newProperties); - newMap.flags |= NOT_EXTENSIBLE; - - return newMap; + return deriveMap(newProperties, flags | NOT_EXTENSIBLE, fieldCount, spillLength); } /** @@ -646,10 +676,7 @@ newProperties = newProperties.immutableAdd(oldProperty.addFlags(propertyFlags)); } - final PropertyMap newMap = new PropertyMap(this, newProperties); - newMap.flags |= NOT_EXTENSIBLE; - - return newMap; + return deriveMap(newProperties, flags | NOT_EXTENSIBLE, fieldCount, spillLength); } /** @@ -736,7 +763,7 @@ history = new WeakHashMap<>(); } - history.put(property, new SoftReference<>(newMap)); + history.put(property, softReferenceDerivationLimit == 0 ? new WeakReference<>(newMap) : new SoftReference<>(newMap)); } /** @@ -749,7 +776,7 @@ private PropertyMap checkHistory(final Property property) { if (history != null) { - final SoftReference<PropertyMap> ref = history.get(property); + final Reference<PropertyMap> ref = history.get(property); final PropertyMap historicMap = ref == null ? null : ref.get(); if (historicMap != null) { @@ -821,13 +848,6 @@ } /** - * Flag this object as having array keys in defined properties - */ - private void setContainsArrayKeys() { - flags |= CONTAINS_ARRAY_KEYS; - } - - /** * Test to see if {@link PropertyMap} is extensible. * * @return {@code true} if {@link PropertyMap} can be added to. @@ -905,12 +925,72 @@ setProtoNewMapCount.increment(); } - final PropertyMap newMap = new PropertyMap(this); + final PropertyMap newMap = makeUnsharedCopy(); addToProtoHistory(newProto, newMap); return newMap; } + /** + * Make a copy of this property map with the shared prototype field set to null. Note that this is + * only necessary for shared maps of top-level objects. Shared prototype maps represented by + * {@link SharedPropertyMap} are automatically converted to plain property maps when they evolve. + * + * @return a copy with the shared proto map unset + */ + PropertyMap makeUnsharedCopy() { + final PropertyMap newMap = new PropertyMap(this); + newMap.sharedProtoMap = null; + return newMap; + } + + /** + * Set a reference to the expected parent prototype map. This is used for class-like + * structures where we only want to use a top-level property map if all of the + * prototype property maps have not been modified. + * + * @param protoMap weak reference to the prototype property map + */ + void setSharedProtoMap(final SharedPropertyMap protoMap) { + sharedProtoMap = protoMap; + } + + /** + * Get the expected prototype property map if it is known, or null. + * + * @return parent map or null + */ + public PropertyMap getSharedProtoMap() { + return sharedProtoMap; + } + + /** + * Returns {@code true} if this map has been used as a shared prototype map (i.e. as a prototype + * for a JavaScript constructor function) and has not had properties added, deleted or replaced since then. + * @return true if this is a valid shared prototype map + */ + boolean isValidSharedProtoMap() { + return false; + } + + /** + * Returns the shared prototype switch point, or null if this is not a shared prototype map. + * @return the shared prototype switch point, or null + */ + SwitchPoint getSharedProtoSwitchPoint() { + return null; + } + + /** + * Return true if this map has a shared prototype map which has either been invalidated or does + * not match the map of {@code proto}. + * @param prototype the prototype object + * @return true if this is an invalid shared map for {@code prototype} + */ + boolean isInvalidSharedMapFor(final ScriptObject prototype) { + return sharedProtoMap != null + && (!sharedProtoMap.isValidSharedProtoMap() || prototype == null || sharedProtoMap != prototype.getMap()); + } /** * {@link PropertyMap} iterator.
--- a/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java Mon Sep 28 15:36:24 2015 -0700 +++ b/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java Tue Oct 06 11:04:55 2015 -0700 @@ -63,7 +63,7 @@ import jdk.nashorn.internal.ir.SwitchNode; import jdk.nashorn.internal.ir.Symbol; import jdk.nashorn.internal.ir.TryNode; -import jdk.nashorn.internal.ir.visitor.NodeVisitor; +import jdk.nashorn.internal.ir.visitor.SimpleNodeVisitor; import jdk.nashorn.internal.objects.Global; import jdk.nashorn.internal.parser.Parser; import jdk.nashorn.internal.parser.Token; @@ -120,7 +120,7 @@ private final Object endParserState; /** Code installer used for all further recompilation/specialization of this ScriptFunction */ - private transient CodeInstaller<ScriptEnvironment> installer; + private transient CodeInstaller installer; private final Map<Integer, RecompilableScriptFunctionData> nestedFunctions; @@ -154,7 +154,7 @@ */ public RecompilableScriptFunctionData( final FunctionNode functionNode, - final CodeInstaller<ScriptEnvironment> installer, + final CodeInstaller installer, final AllocationStrategy allocationStrategy, final Map<Integer, RecompilableScriptFunctionData> nestedFunctions, final Map<String, Integer> externalScopeDepths, @@ -286,7 +286,7 @@ * @param src source * @param inst code installer */ - public void initTransients(final Source src, final CodeInstaller<ScriptEnvironment> inst) { + public void initTransients(final Source src, final CodeInstaller inst) { if (this.source == null && this.installer == null) { this.source = src; this.installer = inst; @@ -369,8 +369,8 @@ } @Override - PropertyMap getAllocatorMap() { - return allocationStrategy.getAllocatorMap(); + PropertyMap getAllocatorMap(final ScriptObject prototype) { + return allocationStrategy.getAllocatorMap(prototype); } @Override @@ -507,7 +507,7 @@ } private FunctionNode deserialize(final byte[] serializedAst) { - final ScriptEnvironment env = installer.getOwner(); + final ScriptEnvironment env = installer.getContext().getEnv(); final Timing timing = env._timing; final long t1 = System.nanoTime(); try { @@ -525,7 +525,7 @@ // don't cache non-split functions from the eager pass); those already cached, or those not split // don't need this step. final Set<Symbol> blockDefinedSymbols = fn.isSplit() && !cached ? Collections.newSetFromMap(new IdentityHashMap<Symbol, Boolean>()) : null; - FunctionNode newFn = (FunctionNode)fn.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { + FunctionNode newFn = (FunctionNode)fn.accept(new SimpleNodeVisitor() { private Symbol getReplacement(final Symbol original) { if (original == null) { @@ -654,8 +654,8 @@ * a new class loader with optimistic typing so that deoptimized code can get reclaimed by GC. * @return a code installer for installing new code. */ - private CodeInstaller<ScriptEnvironment> getInstallerForNewCode() { - final ScriptEnvironment env = installer.getOwner(); + private CodeInstaller getInstallerForNewCode() { + final ScriptEnvironment env = installer.getContext().getEnv(); return env._optimistic_types || env._loader_per_compile ? installer.withNewLoader() : installer; } @@ -665,15 +665,10 @@ final TypeMap typeMap = typeMap(actualCallSiteType); final Type[] paramTypes = typeMap == null ? null : typeMap.getParameterTypes(functionNodeId); final Object typeInformationFile = OptimisticTypesPersistence.getLocationDescriptor(source, functionNodeId, paramTypes); - final Context context = Context.getContextTrusted(); - return new Compiler( - context, - context.getEnv(), + return Compiler.forOnDemandCompilation( getInstallerForNewCode(), functionNode.getSource(), // source - context.getErrorManager(), isStrict() | functionNode.isStrict(), // is strict - true, // is on demand this, // compiledFunction, i.e. this RecompilableScriptFunctionData typeMap, // type map getEffectiveInvalidatedProgramPoints(invalidatedProgramPoints, typeInformationFile), // invalidated program points @@ -716,7 +711,7 @@ final TypeMap typeMap = typeMap(actualCallSiteType); final Type[] paramTypes = typeMap == null ? null : typeMap.getParameterTypes(functionNodeId); cacheKey = CodeStore.getCacheKey(functionNodeId, paramTypes); - final CodeInstaller<ScriptEnvironment> newInstaller = getInstallerForNewCode(); + final CodeInstaller newInstaller = getInstallerForNewCode(); final StoredScript script = newInstaller.loadScript(source, cacheKey); if (script != null) { @@ -737,7 +732,7 @@ } boolean usePersistentCodeCache() { - return installer != null && installer.getOwner()._persistent_cache; + return installer != null && installer.getContext().getEnv()._persistent_cache; } private MethodType explicitParams(final MethodType callSiteType) { @@ -769,7 +764,7 @@ private FunctionNode extractFunctionFromScript(final FunctionNode script) { final Set<FunctionNode> fns = new HashSet<>(); - script.getBody().accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { + script.getBody().accept(new SimpleNodeVisitor() { @Override public boolean enterFunctionNode(final FunctionNode fn) { fns.add(fn);
--- a/src/jdk/nashorn/internal/runtime/ScriptFunction.java Mon Sep 28 15:36:24 2015 -0700 +++ b/src/jdk/nashorn/internal/runtime/ScriptFunction.java Tue Oct 06 11:04:55 2015 -0700 @@ -521,35 +521,39 @@ assert !isBoundFunction(); // allocate never invoked on bound functions - final ScriptObject object = data.allocate(getAllocatorMap()); + final ScriptObject prototype = getAllocatorPrototype(); + final ScriptObject object = data.allocate(getAllocatorMap(prototype)); if (object != null) { - final Object prototype = getPrototype(); - if (prototype instanceof ScriptObject) { - object.setInitialProto((ScriptObject) prototype); - } - - if (object.getProto() == null) { - object.setInitialProto(getObjectPrototype()); - } + object.setInitialProto(prototype); } return object; } - private PropertyMap getAllocatorMap() { - if (allocatorMap == null) { - allocatorMap = data.getAllocatorMap(); + /** + * Get the property map used by "allocate" + * @param prototype actual prototype object + * @return property map + */ + private synchronized PropertyMap getAllocatorMap(final ScriptObject prototype) { + if (allocatorMap == null || allocatorMap.isInvalidSharedMapFor(prototype)) { + // The prototype map has changed since this function was last used as constructor. + // Get a new allocator map. + allocatorMap = data.getAllocatorMap(prototype); } return allocatorMap; } /** - * Return Object.prototype - used by "allocate" - * - * @return Object.prototype + * Return the actual prototype used by "allocate" + * @return allocator prototype */ - protected final ScriptObject getObjectPrototype() { + private ScriptObject getAllocatorPrototype() { + final Object prototype = getPrototype(); + if (prototype instanceof ScriptObject) { + return (ScriptObject) prototype; + } return Global.objectPrototype(); } @@ -591,10 +595,10 @@ * * @param newPrototype new prototype object */ - public final void setPrototype(final Object newPrototype) { + public synchronized final void setPrototype(final Object newPrototype) { if (newPrototype instanceof ScriptObject && newPrototype != this.prototype && allocatorMap != null) { - // Replace our current allocator map with one that is associated with the new prototype. - allocatorMap = allocatorMap.changeProto((ScriptObject) newPrototype); + // Unset allocator map to be replaced with one matching the new prototype. + allocatorMap = null; } this.prototype = newPrototype; }
--- a/src/jdk/nashorn/internal/runtime/ScriptFunctionData.java Mon Sep 28 15:36:24 2015 -0700 +++ b/src/jdk/nashorn/internal/runtime/ScriptFunctionData.java Tue Oct 06 11:04:55 2015 -0700 @@ -389,9 +389,10 @@ /** * Get the property map to use for objects allocated by this function. * + * @param prototype the prototype of the allocated object * @return the property map for allocated objects. */ - PropertyMap getAllocatorMap() { + PropertyMap getAllocatorMap(final ScriptObject prototype) { return null; }
--- a/src/jdk/nashorn/internal/runtime/ScriptObject.java Mon Sep 28 15:36:24 2015 -0700 +++ b/src/jdk/nashorn/internal/runtime/ScriptObject.java Tue Oct 06 11:04:55 2015 -0700 @@ -710,8 +710,7 @@ final long longIndex = ArrayIndex.toLongIndex(index); final long oldLength = getArray().length(); if (longIndex >= oldLength) { - setArray(getArray().ensure(longIndex)); - doesNotHaveEnsureDelete(longIndex, oldLength, false); + setArray(getArray().ensure(longIndex).safeDelete(oldLength, longIndex - 1, false)); } setArray(getArray().set(index, value, false)); } @@ -809,9 +808,11 @@ if (deep) { final ScriptObject myProto = getProto(); - if (myProto != null) { - return myProto.findProperty(key, deep, start); - } + final FindProperty find = myProto == null ? null : myProto.findProperty(key, true, start); + // checkSharedProtoMap must be invoked after myProto.checkSharedProtoMap to propagate + // shared proto invalidation up the prototype chain. It also must be invoked when prototype is null. + checkSharedProtoMap(); + return find; } return null; @@ -832,7 +833,7 @@ if (deep) { final ScriptObject myProto = getProto(); if (myProto != null) { - return myProto.hasProperty(key, deep); + return myProto.hasProperty(key, true); } } @@ -1258,11 +1259,8 @@ if (oldProto != newProto) { proto = newProto; - // Let current listeners know that the prototype has changed and set our map - final PropertyListeners listeners = getMap().getListeners(); - if (listeners != null) { - listeners.protoChanged(); - } + // Let current listeners know that the prototype has changed + getMap().protoChanged(true); // Replace our current allocator map with one that is associated with the new prototype. setMap(getMap().changeProto(newProto)); } @@ -1314,7 +1312,7 @@ } p = p.getProto(); } - setProto((ScriptObject)newProto); + setProto((ScriptObject) newProto); } else { throw typeError("cant.set.proto.to.non.object", ScriptRuntime.safeToString(this), ScriptRuntime.safeToString(newProto)); } @@ -1997,11 +1995,11 @@ final ScriptObject owner = find.getOwner(); final Class<ClassCastException> exception = explicitInstanceOfCheck ? null : ClassCastException.class; - final SwitchPoint protoSwitchPoint; + final SwitchPoint[] protoSwitchPoints; if (mh == null) { mh = Lookup.emptyGetter(returnType); - protoSwitchPoint = getProtoSwitchPoint(name, owner); + protoSwitchPoints = getProtoSwitchPoints(name, owner); } else if (!find.isSelf()) { assert mh.type().returnType().equals(returnType) : "return type mismatch for getter " + mh.type().returnType() + " != " + returnType; @@ -2009,12 +2007,12 @@ // Add a filter that replaces the self object with the prototype owning the property. mh = addProtoFilter(mh, find.getProtoChainLength()); } - protoSwitchPoint = getProtoSwitchPoint(name, owner); + protoSwitchPoints = getProtoSwitchPoints(name, owner); } else { - protoSwitchPoint = null; + protoSwitchPoints = null; } - final GuardedInvocation inv = new GuardedInvocation(mh, guard, protoSwitchPoint, exception); + final GuardedInvocation inv = new GuardedInvocation(mh, guard, protoSwitchPoints, exception); return inv.addSwitchPoint(findBuiltinSwitchPoint(name)); } @@ -2113,17 +2111,32 @@ * @param owner the property owner, null if property is not defined * @return a SwitchPoint or null */ - public final SwitchPoint getProtoSwitchPoint(final String name, final ScriptObject owner) { + public final SwitchPoint[] getProtoSwitchPoints(final String name, final ScriptObject owner) { if (owner == this || getProto() == null) { return null; } + final List<SwitchPoint> switchPoints = new ArrayList<>(); for (ScriptObject obj = this; obj != owner && obj.getProto() != null; obj = obj.getProto()) { final ScriptObject parent = obj.getProto(); parent.getMap().addListener(name, obj.getMap()); + final SwitchPoint sp = parent.getMap().getSharedProtoSwitchPoint(); + if (sp != null && !sp.hasBeenInvalidated()) { + switchPoints.add(sp); + } } - return getMap().getSwitchPoint(name); + switchPoints.add(getMap().getSwitchPoint(name)); + return switchPoints.toArray(new SwitchPoint[switchPoints.size()]); + } + + private void checkSharedProtoMap() { + // Check if our map has an expected shared prototype property map. If it has, make sure that + // the prototype map has not been invalidated, and that it does match the actual map of the prototype. + if (getMap().isInvalidSharedMapFor(getProto())) { + // Change our own map to one that does not assume a shared prototype map. + setMap(getMap().makeUnsharedCopy()); + } } /** @@ -2205,7 +2218,7 @@ return new GuardedInvocation( Lookup.EMPTY_SETTER, NashornGuards.getMapGuard(getMap(), explicitInstanceOfCheck), - getProtoSwitchPoint(name, null), + getProtoSwitchPoints(name, null), explicitInstanceOfCheck ? null : ClassCastException.class); } @@ -2351,7 +2364,7 @@ find.getGetter(Object.class, INVALID_PROGRAM_POINT, request), find.getProtoChainLength(), func), - getProtoSwitchPoint(NO_SUCH_PROPERTY_NAME, find.getOwner()), + getProtoSwitchPoints(NO_SUCH_PROPERTY_NAME, find.getOwner()), //TODO this doesn't need a ClassCastException as guard always checks script object null); } @@ -2424,7 +2437,7 @@ } return new GuardedInvocation(Lookup.emptyGetter(desc.getMethodType().returnType()), - NashornGuards.getMapGuard(getMap(), explicitInstanceOfCheck), getProtoSwitchPoint(name, null), + NashornGuards.getMapGuard(getMap(), explicitInstanceOfCheck), getProtoSwitchPoints(name, null), explicitInstanceOfCheck ? null : ClassCastException.class); } @@ -2664,11 +2677,7 @@ } if (newLength > arrayLength) { - data = data.ensure(newLength - 1); - if (data.canDelete(arrayLength, newLength - 1, false)) { - data = data.delete(arrayLength, newLength - 1); - } - setArray(data); + setArray(data.ensure(newLength - 1).safeDelete(arrayLength, newLength - 1, false)); return; } @@ -3089,23 +3098,12 @@ return false; } - private void doesNotHaveEnsureDelete(final long longIndex, final long oldLength, final boolean strict) { - if (longIndex > oldLength) { - ArrayData array = getArray(); - if (array.canDelete(oldLength, longIndex - 1, strict)) { - array = array.delete(oldLength, longIndex - 1); - } - setArray(array); - } - } - private void doesNotHave(final int index, final int value, final int callSiteFlags) { final long oldLength = getArray().length(); final long longIndex = ArrayIndex.toLongIndex(index); if (!doesNotHaveCheckArrayKeys(longIndex, value, callSiteFlags) && !doesNotHaveEnsureLength(longIndex, oldLength, callSiteFlags)) { final boolean strict = isStrictFlag(callSiteFlags); - setArray(getArray().set(index, value, strict)); - doesNotHaveEnsureDelete(longIndex, oldLength, strict); + setArray(getArray().set(index, value, strict).safeDelete(oldLength, longIndex - 1, strict)); } } @@ -3114,8 +3112,7 @@ final long longIndex = ArrayIndex.toLongIndex(index); if (!doesNotHaveCheckArrayKeys(longIndex, value, callSiteFlags) && !doesNotHaveEnsureLength(longIndex, oldLength, callSiteFlags)) { final boolean strict = isStrictFlag(callSiteFlags); - setArray(getArray().set(index, value, strict)); - doesNotHaveEnsureDelete(longIndex, oldLength, strict); + setArray(getArray().set(index, value, strict).safeDelete(oldLength, longIndex - 1, strict)); } } @@ -3124,8 +3121,7 @@ final long longIndex = ArrayIndex.toLongIndex(index); if (!doesNotHaveCheckArrayKeys(longIndex, value, callSiteFlags) && !doesNotHaveEnsureLength(longIndex, oldLength, callSiteFlags)) { final boolean strict = isStrictFlag(callSiteFlags); - setArray(getArray().set(index, value, strict)); - doesNotHaveEnsureDelete(longIndex, oldLength, strict); + setArray(getArray().set(index, value, strict).safeDelete(oldLength, longIndex - 1, strict)); } } @@ -3134,8 +3130,7 @@ final long longIndex = ArrayIndex.toLongIndex(index); if (!doesNotHaveCheckArrayKeys(longIndex, value, callSiteFlags) && !doesNotHaveEnsureLength(longIndex, oldLength, callSiteFlags)) { final boolean strict = isStrictFlag(callSiteFlags); - setArray(getArray().set(index, value, strict)); - doesNotHaveEnsureDelete(longIndex, oldLength, strict); + setArray(getArray().set(index, value, strict).safeDelete(oldLength, longIndex - 1, strict)); } }
--- a/src/jdk/nashorn/internal/runtime/SetMethodCreator.java Mon Sep 28 15:36:24 2015 -0700 +++ b/src/jdk/nashorn/internal/runtime/SetMethodCreator.java Tue Oct 06 11:04:55 2015 -0700 @@ -186,10 +186,7 @@ private SetMethod createNewPropertySetter(final SwitchPoint builtinSwitchPoint) { final SetMethod sm = map.getFreeFieldSlot() > -1 ? createNewFieldSetter(builtinSwitchPoint) : createNewSpillPropertySetter(builtinSwitchPoint); - final PropertyListeners listeners = map.getListeners(); - if (listeners != null) { - listeners.propertyAdded(sm.property); - } + map.propertyAdded(sm.property, true); return sm; } @@ -204,7 +201,7 @@ //fast type specific setter final MethodHandle fastSetter = property.getSetter(type, newMap); //0 sobj, 1 value, slot folded for spill property already - //slow setter, that calls ScriptObject.set with appropraite type and key name + //slow setter, that calls ScriptObject.set with appropriate type and key name MethodHandle slowSetter = ScriptObject.SET_SLOW[getAccessorTypeIndex(type)]; slowSetter = MH.insertArguments(slowSetter, 3, NashornCallSiteDescriptor.getFlags(desc)); slowSetter = MH.insertArguments(slowSetter, 1, name);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk/nashorn/internal/runtime/SharedPropertyMap.java Tue Oct 06 11:04:55 2015 -0700 @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.nashorn.internal.runtime; + +import java.lang.invoke.SwitchPoint; + +/** + * This class represents a property map that can be shared among multiple prototype objects, allowing all inheriting + * top-level objects to also share one property map. This is class is only used for prototype objects, the + * top-level objects use ordinary {@link PropertyMap}s with the {@link PropertyMap#sharedProtoMap} field + * set to the expected shared prototype map. + * + * <p>When an instance of this class is evolved because a property is added, removed, or modified in an object + * using it, the {@link #invalidateSwitchPoint()} method is invoked to signal to all callsites and inheriting + * objects that the assumption of a single shared prototype map is no longer valid. The property map resulting + * from the modification will no longer be an instance of this class.</p> + */ +public final class SharedPropertyMap extends PropertyMap { + + private SwitchPoint switchPoint; + + private static final long serialVersionUID = 2166297719721778876L; + + /** + * Create a new shared property map from the given {@code map}. + * @param map property map to copy + */ + public SharedPropertyMap(final PropertyMap map) { + super(map); + this.switchPoint = new SwitchPoint(); + } + + @Override + public void propertyAdded(final Property property, final boolean isSelf) { + if (isSelf) { + invalidateSwitchPoint(); + } + super.propertyAdded(property, isSelf); + } + + @Override + public void propertyDeleted(final Property property, final boolean isSelf) { + if (isSelf) { + invalidateSwitchPoint(); + } + super.propertyDeleted(property, isSelf); + } + + @Override + public void propertyModified(final Property oldProperty, final Property newProperty, final boolean isSelf) { + if (isSelf) { + invalidateSwitchPoint(); + } + super.propertyModified(oldProperty, newProperty, isSelf); + } + + @Override + synchronized boolean isValidSharedProtoMap() { + return switchPoint != null; + } + + @Override + synchronized SwitchPoint getSharedProtoSwitchPoint() { + return switchPoint; + } + + /** + * Invalidate the shared prototype switch point if this is a shared prototype map. + */ + synchronized void invalidateSwitchPoint() { + if (switchPoint != null) { + assert !switchPoint.hasBeenInvalidated(); + SwitchPoint.invalidateAll(new SwitchPoint[]{ switchPoint }); + switchPoint = null; + } + } +}
--- a/src/jdk/nashorn/internal/runtime/StoredScript.java Mon Sep 28 15:36:24 2015 -0700 +++ b/src/jdk/nashorn/internal/runtime/StoredScript.java Tue Oct 06 11:04:55 2015 -0700 @@ -77,7 +77,7 @@ return compilationId; } - private Map<String, Class<?>> installClasses(final Source source, final CodeInstaller<ScriptEnvironment> installer) { + private Map<String, Class<?>> installClasses(final Source source, final CodeInstaller installer) { final Map<String, Class<?>> installedClasses = new HashMap<>(); final byte[] mainClassBytes = classBytes.get(mainClassName); final Class<?> mainClass = installer.install(mainClassName, mainClassBytes); @@ -96,7 +96,7 @@ return installedClasses; } - FunctionInitializer installFunction(final RecompilableScriptFunctionData data, final CodeInstaller<ScriptEnvironment> installer) { + FunctionInitializer installFunction(final RecompilableScriptFunctionData data, final CodeInstaller installer) { final Map<String, Class<?>> installedClasses = installClasses(data.getSource(), installer); assert initializers != null; @@ -124,7 +124,7 @@ * @param installer the installer * @return main script class */ - Class<?> installScript(final Source source, final CodeInstaller<ScriptEnvironment> installer) { + Class<?> installScript(final Source source, final CodeInstaller installer) { final Map<String, Class<?>> installedClasses = installClasses(source, installer);
--- a/src/jdk/nashorn/internal/runtime/WithObject.java Mon Sep 28 15:36:24 2015 -0700 +++ b/src/jdk/nashorn/internal/runtime/WithObject.java Tue Oct 06 11:04:55 2015 -0700 @@ -46,7 +46,7 @@ * */ public final class WithObject extends Scope { - private static final MethodHandle WITHEXPRESSIONGUARD = findOwnMH("withExpressionGuard", boolean.class, Object.class, PropertyMap.class, SwitchPoint.class); + private static final MethodHandle WITHEXPRESSIONGUARD = findOwnMH("withExpressionGuard", boolean.class, Object.class, PropertyMap.class, SwitchPoint[].class); private static final MethodHandle WITHEXPRESSIONFILTER = findOwnMH("withFilterExpression", Object.class, Object.class); private static final MethodHandle WITHSCOPEFILTER = findOwnMH("withFilterScope", Object.class, Object.class); private static final MethodHandle BIND_TO_EXPRESSION_OBJ = findOwnMH("bindToExpression", Object.class, Object.class, Object.class); @@ -360,13 +360,24 @@ private MethodHandle expressionGuard(final String name, final ScriptObject owner) { final PropertyMap map = expression.getMap(); - final SwitchPoint sp = expression.getProtoSwitchPoint(name, owner); + final SwitchPoint[] sp = expression.getProtoSwitchPoints(name, owner); return MH.insertArguments(WITHEXPRESSIONGUARD, 1, map, sp); } @SuppressWarnings("unused") - private static boolean withExpressionGuard(final Object receiver, final PropertyMap map, final SwitchPoint sp) { - return ((WithObject)receiver).expression.getMap() == map && (sp == null || !sp.hasBeenInvalidated()); + private static boolean withExpressionGuard(final Object receiver, final PropertyMap map, final SwitchPoint[] sp) { + return ((WithObject)receiver).expression.getMap() == map && !hasBeenInvalidated(sp); + } + + private static boolean hasBeenInvalidated(final SwitchPoint[] switchPoints) { + if (switchPoints != null) { + for (final SwitchPoint switchPoint : switchPoints) { + if (switchPoint.hasBeenInvalidated()) { + return true; + } + } + } + return false; } /**
--- a/src/jdk/nashorn/internal/runtime/arrays/ArrayData.java Mon Sep 28 15:36:24 2015 -0700 +++ b/src/jdk/nashorn/internal/runtime/arrays/ArrayData.java Tue Oct 06 11:04:55 2015 -0700 @@ -258,7 +258,7 @@ * Factory method for unspecified array - start as int * @return ArrayData */ - public final static ArrayData initialArray() { + public static ArrayData initialArray() { return new IntArrayData(); } @@ -278,7 +278,7 @@ * @param size size required * @return size given, always >= size */ - protected final static int alignUp(final int size) { + protected static int alignUp(final int size) { return size + CHUNK_SIZE - 1 & ~(CHUNK_SIZE - 1); } @@ -288,7 +288,7 @@ * @param length the initial length * @return ArrayData */ - public static final ArrayData allocate(final int length) { + public static ArrayData allocate(final int length) { if (length == 0) { return new IntArrayData(); } else if (length >= SparseArrayData.MAX_DENSE_LENGTH) { @@ -304,7 +304,7 @@ * @param array the array * @return ArrayData wrapping this array */ - public static final ArrayData allocate(final Object array) { + public static ArrayData allocate(final Object array) { final Class<?> clazz = array.getClass(); if (clazz == int[].class) { @@ -324,7 +324,7 @@ * @param array the array to use for initial elements * @return the ArrayData */ - public static final ArrayData allocate(final int[] array) { + public static ArrayData allocate(final int[] array) { return new IntArrayData(array, array.length); } @@ -334,7 +334,7 @@ * @param array the array to use for initial elements * @return the ArrayData */ - public static final ArrayData allocate(final long[] array) { + public static ArrayData allocate(final long[] array) { return new LongArrayData(array, array.length); } @@ -344,7 +344,7 @@ * @param array the array to use for initial elements * @return the ArrayData */ - public static final ArrayData allocate(final double[] array) { + public static ArrayData allocate(final double[] array) { return new NumberArrayData(array, array.length); } @@ -354,7 +354,7 @@ * @param array the array to use for initial elements * @return the ArrayData */ - public static final ArrayData allocate(final Object[] array) { + public static ArrayData allocate(final Object[] array) { return new ObjectArrayData(array, array.length); } @@ -364,7 +364,7 @@ * @param buf the nio ByteBuffer to wrap * @return the ArrayData */ - public static final ArrayData allocate(final ByteBuffer buf) { + public static ArrayData allocate(final ByteBuffer buf) { return new ByteBufferArrayData(buf); } @@ -374,7 +374,7 @@ * @param underlying the underlying ArrayData to wrap in the freeze filter * @return the frozen ArrayData */ - public static final ArrayData freeze(final ArrayData underlying) { + public static ArrayData freeze(final ArrayData underlying) { return new FrozenArrayFilter(underlying); } @@ -384,7 +384,7 @@ * @param underlying the underlying ArrayData to wrap in the seal filter * @return the sealed ArrayData */ - public static final ArrayData seal(final ArrayData underlying) { + public static ArrayData seal(final ArrayData underlying) { return new SealedArrayFilter(underlying); } @@ -394,7 +394,7 @@ * @param underlying the underlying ArrayData to wrap in the non extensible filter * @return new array data, filtered */ - public static final ArrayData preventExtension(final ArrayData underlying) { + public static ArrayData preventExtension(final ArrayData underlying) { return new NonExtensibleArrayFilter(underlying); } @@ -404,7 +404,7 @@ * @param underlying the underlying ArrayDAta to wrap in the non extensible filter * @return new array data, filtered */ - public static final ArrayData setIsLengthNotWritable(final ArrayData underlying) { + public static ArrayData setIsLengthNotWritable(final ArrayData underlying) { return new LengthNotWritableFilter(underlying); } @@ -676,19 +676,34 @@ } /** - * Returns if element at specific index range can be deleted or not. + * Returns if element at specific index can be deleted or not. * - * @param fromIndex the start index - * @param toIndex the end index + * @param longIndex the index * @param strict are we in strict mode * * @return true if range can be deleted */ - public boolean canDelete(final long fromIndex, final long toIndex, final boolean strict) { + public boolean canDelete(final long longIndex, final boolean strict) { return true; } /** + * Delete a range from the array if {@code fromIndex} is less than or equal to {@code toIndex} + * and the array supports deletion. + * + * @param fromIndex the start index (inclusive) + * @param toIndex the end index (inclusive) + * @param strict are we in strict mode + * @return an array with the range deleted, or this array if no deletion took place + */ + public final ArrayData safeDelete(final long fromIndex, final long toIndex, final boolean strict) { + if (fromIndex <= toIndex && canDelete(fromIndex, strict)) { + return delete(fromIndex, toIndex); + } + return this; + } + + /** * Returns property descriptor for element at a given index * * @param global the global object
--- a/src/jdk/nashorn/internal/runtime/arrays/ByteBufferArrayData.java Mon Sep 28 15:36:24 2015 -0700 +++ b/src/jdk/nashorn/internal/runtime/arrays/ByteBufferArrayData.java Tue Oct 06 11:04:55 2015 -0700 @@ -164,7 +164,7 @@ } @Override - public boolean canDelete(final long fromIndex, final long toIndex, final boolean strict) { + public boolean canDelete(final long longIndex, final boolean strict) { return false; }
--- a/src/jdk/nashorn/internal/runtime/arrays/SealedArrayFilter.java Mon Sep 28 15:36:24 2015 -0700 +++ b/src/jdk/nashorn/internal/runtime/arrays/SealedArrayFilter.java Tue Oct 06 11:04:55 2015 -0700 @@ -50,15 +50,15 @@ @Override public boolean canDelete(final int index, final boolean strict) { - if (strict) { - throw typeError("cant.delete.property", Integer.toString(index), "sealed array"); - } - return false; + return canDelete(ArrayIndex.toLongIndex(index), strict); } @Override - public boolean canDelete(final long fromIndex, final long toIndex, final boolean strict) { - return canDelete((int) fromIndex, strict); + public boolean canDelete(final long longIndex, final boolean strict) { + if (strict) { + throw typeError("cant.delete.property", Long.toString(longIndex), "sealed array"); + } + return false; } @Override
--- a/src/jdk/nashorn/internal/runtime/arrays/SparseArrayData.java Mon Sep 28 15:36:24 2015 -0700 +++ b/src/jdk/nashorn/internal/runtime/arrays/SparseArrayData.java Tue Oct 06 11:04:55 2015 -0700 @@ -36,7 +36,7 @@ * Handle arrays where the index is very large. */ class SparseArrayData extends ArrayData { - static final int MAX_DENSE_LENGTH = 8 * 1024 * 1024; + static final int MAX_DENSE_LENGTH = 1024 * 1024; /** Underlying array. */ private ArrayData underlying; @@ -166,8 +166,9 @@ @Override public ArrayData set(final int index, final Object value, final boolean strict) { if (index >= 0 && index < maxDenseLength) { + final long oldLength = underlying.length(); ensure(index); - underlying = underlying.set(index, value, strict); + underlying = underlying.set(index, value, strict).safeDelete(oldLength, index - 1, strict); setLength(Math.max(underlying.length(), length())); } else { final Long longIndex = indexToKey(index); @@ -181,8 +182,9 @@ @Override public ArrayData set(final int index, final int value, final boolean strict) { if (index >= 0 && index < maxDenseLength) { + final long oldLength = underlying.length(); ensure(index); - underlying = underlying.set(index, value, strict); + underlying = underlying.set(index, value, strict).safeDelete(oldLength, index - 1, strict); setLength(Math.max(underlying.length(), length())); } else { final Long longIndex = indexToKey(index); @@ -195,8 +197,9 @@ @Override public ArrayData set(final int index, final long value, final boolean strict) { if (index >= 0 && index < maxDenseLength) { + final long oldLength = underlying.length(); ensure(index); - underlying = underlying.set(index, value, strict); + underlying = underlying.set(index, value, strict).safeDelete(oldLength, index - 1, strict); setLength(Math.max(underlying.length(), length())); } else { final Long longIndex = indexToKey(index); @@ -209,8 +212,9 @@ @Override public ArrayData set(final int index, final double value, final boolean strict) { if (index >= 0 && index < maxDenseLength) { + final long oldLength = underlying.length(); ensure(index); - underlying = underlying.set(index, value, strict); + underlying = underlying.set(index, value, strict).safeDelete(oldLength, index - 1, strict); setLength(Math.max(underlying.length(), length())); } else { final Long longIndex = indexToKey(index);
--- a/src/jdk/nashorn/internal/runtime/arrays/TypedArrayData.java Mon Sep 28 15:36:24 2015 -0700 +++ b/src/jdk/nashorn/internal/runtime/arrays/TypedArrayData.java Tue Oct 06 11:04:55 2015 -0700 @@ -83,7 +83,7 @@ } @Override - public boolean canDelete(final long fromIndex, final long toIndex, final boolean strict) { + public boolean canDelete(final long longIndex, final boolean strict) { return false; }
--- a/src/jdk/nashorn/internal/runtime/linker/BrowserJSObjectLinker.java Mon Sep 28 15:36:24 2015 -0700 +++ b/src/jdk/nashorn/internal/runtime/linker/BrowserJSObjectLinker.java Tue Oct 06 11:04:55 2015 -0700 @@ -99,9 +99,10 @@ return null; } - final GuardedInvocation inv; + GuardedInvocation inv; if (jsObjectClass.isInstance(self)) { inv = lookup(desc, request, linkerServices); + inv = inv.replaceMethods(linkerServices.filterInternalObjects(inv.getInvocation()), inv.getGuard()); } else { throw new AssertionError(); // Should never reach here. }
--- a/src/jdk/nashorn/internal/runtime/linker/JSObjectLinker.java Mon Sep 28 15:36:24 2015 -0700 +++ b/src/jdk/nashorn/internal/runtime/linker/JSObjectLinker.java Tue Oct 06 11:04:55 2015 -0700 @@ -77,9 +77,10 @@ return null; } - final GuardedInvocation inv; + GuardedInvocation inv; if (self instanceof JSObject) { inv = lookup(desc, request, linkerServices); + inv = inv.replaceMethods(linkerServices.filterInternalObjects(inv.getInvocation()), inv.getGuard()); } else if (self instanceof Map || self instanceof Bindings) { // guard to make sure the Map or Bindings does not turn into JSObject later! final GuardedInvocation beanInv = nashornBeansLinker.getGuardedInvocation(request, linkerServices);
--- a/src/jdk/nashorn/internal/runtime/linker/JavaAdapterBytecodeGenerator.java Mon Sep 28 15:36:24 2015 -0700 +++ b/src/jdk/nashorn/internal/runtime/linker/JavaAdapterBytecodeGenerator.java Tue Oct 06 11:04:55 2015 -0700 @@ -203,6 +203,8 @@ // This is the superclass for our generated adapter. private final Class<?> superClass; + // Interfaces implemented by our generated adapter. + private final List<Class<?>> interfaces; // Class loader used as the parent for the class loader we'll create to load the generated class. It will be a class // loader that has the visibility of all original types (class to extend and interfaces to implement) and of the // Nashorn classes. @@ -254,6 +256,7 @@ assert interfaces != null; this.superClass = superClass; + this.interfaces = interfaces; this.classOverride = classOverride; this.commonLoader = commonLoader; cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS) { @@ -1031,6 +1034,24 @@ endMethod(mv); } + // find the appropriate super type to use for invokespecial on the given interface + private Class<?> findInvokespecialOwnerFor(final Class<?> cl) { + assert Modifier.isInterface(cl.getModifiers()) : cl + " is not an interface"; + + if (cl.isAssignableFrom(superClass)) { + return superClass; + } + + for (final Class<?> iface : interfaces) { + if (cl.isAssignableFrom(iface)) { + return iface; + } + } + + // we better that interface that extends the given interface! + throw new AssertionError("can't find the class/interface that extends " + cl); + } + private void emitSuperCall(final InstructionAdapter mv, final Class<?> owner, final String name, final String methodDesc) { mv.visitVarInsn(ALOAD, 0); int nextParam = 1; @@ -1042,7 +1063,9 @@ // default method - non-abstract, interface method if (Modifier.isInterface(owner.getModifiers())) { - mv.invokespecial(Type.getInternalName(owner), name, methodDesc, false); + // we should call default method on the immediate "super" type - not on (possibly) + // the indirectly inherited interface class! + mv.invokespecial(Type.getInternalName(findInvokespecialOwnerFor(owner)), name, methodDesc, false); } else { mv.invokespecial(superClassName, name, methodDesc, false); }
--- a/src/jdk/nashorn/tools/Shell.java Mon Sep 28 15:36:24 2015 -0700 +++ b/src/jdk/nashorn/tools/Shell.java Tue Oct 06 11:04:55 2015 -0700 @@ -54,7 +54,6 @@ import jdk.nashorn.internal.runtime.ScriptEnvironment; import jdk.nashorn.internal.runtime.ScriptFunction; import jdk.nashorn.internal.runtime.ScriptRuntime; -import jdk.nashorn.internal.runtime.Source; import jdk.nashorn.internal.runtime.options.Options; /** @@ -255,12 +254,9 @@ return COMPILATION_ERROR; } - new Compiler( + Compiler.forNoInstallerCompilation( context, - env, - null, //null - pass no code installer - this is compile only functionNode.getSource(), - context.getErrorManager(), env._strict | functionNode.isStrict()). compile(functionNode, CompilationPhases.COMPILE_ALL_NO_INSTALL);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8087312.js Tue Oct 06 11:04:55 2015 -0700 @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8087312: PropertyMapWrapper.equals should compare className + * + * @test + * @run + * @fork + * @option -Dnashorn.debug=true + */ + +function createObject(type) { + // we want to make sure two different object literals with the same keys and types share the same property map. + if (type) { + return { + a: "a", + b: 1, + c: 0.1 + } + } else { + return { + a: "x", + b: 10, + c: 3.4 + } + } +} + +var o1 = createObject(false); +var o2 = createObject(true); +Assert.assertTrue(Debug.map(o1) === Debug.map(o2)); +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8134488.js Tue Oct 06 11:04:55 2015 -0700 @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2015 Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8134488: var statement in if(false) block incorrectly evacuated into enclosing function + * + * @test + * @run + */ + +var x = "string"; +print(x); + +(function f1() { + (function f2() { + // If it finds both 'print' and 'x', it'll print 'string'. + print(x); + })(); + + if (false) { + (function f3() { + var x; + })(); + } + +})();
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8134488.js.EXPECTED Tue Oct 06 11:04:55 2015 -0700 @@ -0,0 +1,2 @@ +string +string
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8134490.js Tue Oct 06 11:04:55 2015 -0700 @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2015 Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8134490: Dead var statement evacuation incorrectly descends into nested functions + * + * @test + * @run + */ + +var v1; + +function f1() +{ +v1 = 1; +return true; +(function () { var v1; })(); +} + +f1(); +// If it executes without throwing an exception in code generator, it's working.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8134569.js Tue Oct 06 11:04:55 2015 -0700 @@ -0,0 +1,207 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8134569: Add tests for prototype callsites + * + * @test + * @run + */ + +function create() { + function C() { + this.i1 = 1; + this.i2 = 2; + this.i3 = 3; + return this; + } + return new C(); +} + +function createEmpty() { + function C() { + return this; + } + return new C(); +} + +function createDeep() { + function C() { + this.i1 = 1; + this.i2 = 2; + this.i3 = 3; + return this; + } + function D() { + this.p1 = 1; + this.p2 = 2; + this.p3 = 3; + return this; + } + C.prototype = new D(); + return new C(); +} + +function createDeeper() { + function C() { + this.i1 = 1; + this.i2 = 2; + this.i3 = 3; + return this; + } + function D() { + this.p1 = 1; + this.p2 = 2; + this.p3 = 3; + return this; + } + function E() { + this.e1 = 1; + this.e2 = 2; + this.e3 = 3; + return this; + } + D.prototype = new E(); + C.prototype = new D(); + return new C(); +} + +function createEval() { + return eval("Object.create({})"); +} + +function p(o) { print(o.x) } + +function e(o) { print(o.e1) } + +var a, b, c; + +create(); +a = create(); +b = create(); +c = create(); +a.__proto__.x = 123; + +p(a); +p(b); +p(c); + +a = create(); +b = create(); +c = create(); +b.__proto__.x = 123; + +p(a); +p(b); +p(c); + +a = createEmpty(); +b = createEmpty(); +c = createEmpty(); +a.__proto__.x = 123; + +p(a); +p(b); +p(c); + +a = createEmpty(); +b = createEmpty(); +c = createEmpty(); +b.__proto__.x = 123; + +p(a); +p(b); +p(c); + +a = createDeep(); +b = createDeep(); +c = createDeep(); +a.__proto__.__proto__.x = 123; + +p(a); +p(b); +p(c); + +a = createDeep(); +b = createDeep(); +c = createDeep(); +b.__proto__.__proto__.x = 123; + +p(a); +p(b); +p(c); + +a = createDeeper(); +b = createDeeper(); +c = createDeeper(); +a.__proto__.__proto__.__proto__.x = 123; + +p(a); +p(b); +p(c); + +a = createDeeper(); +b = createDeeper(); +c = createDeeper(); +b.__proto__.__proto__.__proto__.x = 123; + +p(a); +p(b); +p(c); + +a = createDeeper(); +b = createDeeper(); +c = createDeeper(); +a.__proto__.__proto__ = null; + +e(a); +e(b); +e(c); + +a = createDeeper(); +b = createDeeper(); +c = createDeeper(); +b.__proto__.__proto__ = null; + +e(a); +e(b); +e(c); + + +a = createEval(); +b = createEval(); +c = createEval(); +a.__proto__.x = 123; + +p(a); +p(b); +p(c); + +a = createEval(); +b = createEval(); +c = createEval(); +b.__proto__.x = 123; + +p(a); +p(b); +p(c);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8134569.js.EXPECTED Tue Oct 06 11:04:55 2015 -0700 @@ -0,0 +1,36 @@ +123 +undefined +undefined +undefined +123 +undefined +123 +undefined +undefined +undefined +123 +undefined +123 +undefined +undefined +undefined +123 +undefined +123 +undefined +undefined +undefined +123 +undefined +undefined +1 +1 +1 +undefined +1 +123 +undefined +undefined +undefined +123 +undefined
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8134609.js Tue Oct 06 11:04:55 2015 -0700 @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8134609: Allow constructors with same prototoype map to share the allocator map + * + * @test + * @run + * @fork + * @option -Dnashorn.debug + */ + +function createProto(members) { + function P() { + for (var id in members) { + if (members.hasOwnProperty(id)) { + this[id] = members[id]; + } + } + return this; + } + return new P(); +} + +function createSubclass(prototype, members) { + function C() { + for (var id in members) { + if (members.hasOwnProperty(id)) { + this[id] = members[id]; + } + } + return this; + } + + C.prototype = prototype; + + return new C(); +} + +function assertP1(object, value) { + Assert.assertTrue(object.p1 === value); +} + +// First prototype will have non-shared proto-map. Second and third will be shared. +var proto0 = createProto({p1: 0, p2: 1}); +var proto1 = createProto({p1: 1, p2: 2}); +var proto2 = createProto({p1: 2, p2: 3}); + +Assert.assertTrue(Debug.map(proto1) === Debug.map(proto2)); + +assertP1(proto1, 1); +assertP1(proto2, 2); + +// First instantiation will have a non-shared prototype map, from the second one +// maps will be shared until a different proto map comes along. +var child0 = createSubclass(proto1, {c1: 1, c2: 2}); +var child1 = createSubclass(proto2, {c1: 2, c2: 3}); +var child2 = createSubclass(proto1, {c1: 3, c2: 4}); +var child3 = createSubclass(proto2, {c1: 1, c2: 2}); +var child4 = createSubclass(proto0, {c1: 3, c2: 2}); + +Assert.assertTrue(Debug.map(child1) === Debug.map(child2)); +Assert.assertTrue(Debug.map(child1) === Debug.map(child3)); +Assert.assertTrue(Debug.map(child3) !== Debug.map(child4)); + +assertP1(child1, 2); +assertP1(child2, 1); +assertP1(child3, 2); +assertP1(child4, 0); + +Assert.assertTrue(delete proto2.p1); + +assertP1(child3, undefined); +assertP1(child2, 1); +Assert.assertTrue(Debug.map(child1) !== Debug.map(child3));
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8135000.js Tue Oct 06 11:04:55 2015 -0700 @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8135000: Number.prototype.toFixed returns wrong string for 0.5 and -0.5 + * + * @test + * @run + */ + +print(-2.6.toFixed()); +print(-2.5.toFixed()); +print(-2.4.toFixed()); +print(-1.6.toFixed()); +print(-1.5.toFixed()); +print(-1.4.toFixed()); +print(-0.6.toFixed()); +print(-0.5.toFixed()); +print(-0.4.toFixed()); +print(0.4.toFixed()); +print(0.5.toFixed()); +print(0.6.toFixed()); +print(1.4.toFixed()); +print(1.5.toFixed()); +print(1.6.toFixed()); +print(2.4.toFixed()); +print(2.5.toFixed()); +print(2.6.toFixed());
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8135000.js.EXPECTED Tue Oct 06 11:04:55 2015 -0700 @@ -0,0 +1,18 @@ +-3 +-3 +-2 +-2 +-2 +-1 +-1 +-1 +0 +0 +1 +1 +1 +2 +2 +2 +3 +3
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8135190.js Tue Oct 06 11:04:55 2015 -0700 @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8135190: Method code too large in Babel browser.js script + * + * @test + * @run + */ + +// Make sure huge object literals are parsed correctly and don't throw +// (using buildObject -> JSON.stringify -> eval -> testObject) + +function buildObject(n, d) { + if (n < 2) { + return {name: "property", type: "identifier"}; + } + var obj = {}; + for (var i = 0; i < n; i++) { + obj["expr" + i] = buildObject(Math.floor(n / d), d); + } + return obj; +} + +function testObject(obj, n, d) { + var keys = Object.keys(obj); + if (n < 2) { + Assert.assertTrue(keys.length === 2); + Assert.assertTrue(keys[0] === "name"); + Assert.assertTrue(keys[1] === "type"); + } else { + Assert.assertTrue(keys.length === n); + for (var i = 0; i < n; i++) { + Assert.assertTrue(keys[i] === "expr" + i); + } + } + if (n >= 2) { + for (var k in keys) { + testObject(obj[keys[k]], Math.floor(n / d), d) + } + } +} + +var fieldObject = (eval("(" + JSON.stringify(buildObject(25, 2)) + ")")); +testObject(fieldObject, 25, 2); +var spillObject = (eval("(" + JSON.stringify(buildObject(1000, 100)) + ")")); +testObject(spillObject, 1000, 100);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8137134.js Tue Oct 06 11:04:55 2015 -0700 @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8137134: invokespecial on indirect super interface is generated by Java adapter generator + * + * @test + * @run + */ + +var B = Java.type("jdk.nashorn.test.models.B"); +var b1 = new B() {} +print(b1.a()); +print(b1.b()); + +var b2 = new B() { + b: function() { + return "from B.b in script"; + } +}; + +print(b2.a()); +print(b2.b()); + +var b3 = new B() { + a: function() { + return "from A.a in script"; + }, + b: function() { + return "from B.b in script"; + } +}; + +print(b3.a()); +print(b3.b());
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8137134.js.EXPECTED Tue Oct 06 11:04:55 2015 -0700 @@ -0,0 +1,6 @@ +from A.a +from B.b +from A.a +from B.b in script +from A.a in script +from B.b in script
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8137281.js Tue Oct 06 11:04:55 2015 -0700 @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8137281: OutOfMemoryError with large numeric keys in JSON.parse + * + * @test + * @run + */ + +function createObject(startKey, level1, level2) { + var root = {}; + var key = startKey; + for (var i = 0; i < level1; i++) { + var child = {}; + for (var j = 0; j < level2; j++) { + child[key++] = {}; + } + root[key++] = child; + } + return root; +} + +JSON.parse(JSON.stringify(createObject(500000, 20, 20))); +JSON.parse(JSON.stringify(createObject(1000000, 20, 20))); +JSON.parse(JSON.stringify(createObject(2000000, 20, 20))); +JSON.parse(JSON.stringify(createObject(4000000, 20, 20))); +JSON.parse(JSON.stringify(createObject(8000000, 20, 20))); +JSON.parse(JSON.stringify(createObject(16000000, 20, 20)));
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8138632.js Tue Oct 06 11:04:55 2015 -0700 @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8138632: Sparse array does not handle growth of underlying dense array + * + * @test + * @run + */ + +var x = []; +x[10000000] = 1; +x[10] = 1; +x[20] = 1; +print(Object.keys(x));
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8138632.js.EXPECTED Tue Oct 06 11:04:55 2015 -0700 @@ -0,0 +1,1 @@ +10,20,10000000
--- a/test/script/trusted/JDK-8006529.js Mon Sep 28 15:36:24 2015 -0700 +++ b/test/script/trusted/JDK-8006529.js Tue Oct 06 11:04:55 2015 -0700 @@ -120,7 +120,7 @@ var sourceForMethod = Source.class.getMethod("sourceFor", java.lang.String.class, java.lang.String.class) var ParserConstructor = Parser.class.getConstructor(ScriptEnvironment.class, Source.class, ErrorManager.class) -var CompilerConstructor = Compiler.class.getConstructor(Context.class, ScriptEnvironment.class, CodeInstaller.class, Source.class, ErrorManager.class, boolean.class); +var CompilerConstructor = Compiler.class.getMethod("forNoInstallerCompilation", Context.class, Source.class, boolean.class); // compile(script) -- compiles a script specified as a string with its // source code, returns a jdk.nashorn.internal.ir.FunctionNode object @@ -134,7 +134,7 @@ var parser = ParserConstructor.newInstance(env, source, ThrowErrorManager.class.newInstance()); var func = parseMethod.invoke(parser); - var compiler = CompilerConstructor.newInstance(ctxt, env, null, source, null, false); + var compiler = CompilerConstructor.invoke(null, ctxt, source, false); return compileMethod.invoke(compiler, func, phases); };
--- a/test/src/jdk/nashorn/api/scripting/JSONCompatibleTest.java Mon Sep 28 15:36:24 2015 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,117 +0,0 @@ -/* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.nashorn.api.scripting; - -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertTrue; - -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import javax.script.ScriptEngine; -import javax.script.ScriptException; -import org.testng.Assert; -import org.testng.annotations.Test; - -public class JSONCompatibleTest { - - /** - * Wrap a top-level array as a list. - */ - @Test - public void testWrapArray() throws ScriptException { - final ScriptEngine engine = new NashornScriptEngineFactory().getScriptEngine(); - final Object val = engine.eval("Java.asJSONCompatible([1, 2, 3])"); - assertEquals(asList(val), Arrays.asList(1, 2, 3)); - } - - /** - * Wrap an embedded array as a list. - */ - @Test - public void testWrapObjectWithArray() throws ScriptException { - final ScriptEngine engine = new NashornScriptEngineFactory().getScriptEngine(); - final Object val = engine.eval("Java.asJSONCompatible({x: [1, 2, 3]})"); - assertEquals(asList(asMap(val).get("x")), Arrays.asList(1, 2, 3)); - } - - /** - * Check it all works transitively several more levels down. - */ - @Test - public void testDeepWrapping() throws ScriptException { - final ScriptEngine engine = new NashornScriptEngineFactory().getScriptEngine(); - final Object val = engine.eval("Java.asJSONCompatible({x: [1, {y: [2, {z: [3]}]}, [4, 5]]})"); - final Map<String, Object> root = asMap(val); - final List<Object> x = asList(root.get("x")); - assertEquals(x.get(0), 1); - final Map<String, Object> x1 = asMap(x.get(1)); - final List<Object> y = asList(x1.get("y")); - assertEquals(y.get(0), 2); - final Map<String, Object> y1 = asMap(y.get(1)); - assertEquals(asList(y1.get("z")), Arrays.asList(3)); - assertEquals(asList(x.get(2)), Arrays.asList(4, 5)); - } - - /** - * Ensure that the old behaviour (every object is a Map) is unchanged. - */ - @Test - public void testNonWrapping() throws ScriptException { - final ScriptEngine engine = new NashornScriptEngineFactory().getScriptEngine(); - final Object val = engine.eval("({x: [1, {y: [2, {z: [3]}]}, [4, 5]]})"); - final Map<String, Object> root = asMap(val); - final Map<String, Object> x = asMap(root.get("x")); - assertEquals(x.get("0"), 1); - final Map<String, Object> x1 = asMap(x.get("1")); - final Map<String, Object> y = asMap(x1.get("y")); - assertEquals(y.get("0"), 2); - final Map<String, Object> y1 = asMap(y.get("1")); - final Map<String, Object> z = asMap(y1.get("z")); - assertEquals(z.get("0"), 3); - final Map<String, Object> x2 = asMap(x.get("2")); - assertEquals(x2.get("0"), 4); - assertEquals(x2.get("1"), 5); - } - - @SuppressWarnings("unchecked") - private static List<Object> asList(final Object obj) { - assertJSObject(obj); - Assert.assertTrue(obj instanceof List); - return (List)obj; - } - - @SuppressWarnings("unchecked") - private static Map<String, Object> asMap(final Object obj) { - assertJSObject(obj); - Assert.assertTrue(obj instanceof Map); - return (Map)obj; - } - - private static void assertJSObject(final Object obj) { - assertTrue(obj instanceof JSObject); - } -}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/src/jdk/nashorn/api/scripting/test/JSONCompatibleTest.java Tue Oct 06 11:04:55 2015 -0700 @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.nashorn.api.scripting.test; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import javax.script.ScriptEngine; +import javax.script.ScriptException; +import jdk.nashorn.api.scripting.JSObject; +import jdk.nashorn.api.scripting.NashornScriptEngine; +import jdk.nashorn.api.scripting.NashornScriptEngineFactory; +import org.testng.Assert; +import org.testng.annotations.Test; + +public class JSONCompatibleTest { + + /** + * Wrap a top-level array as a list. + */ + @Test + public void testWrapArray() throws ScriptException { + final ScriptEngine engine = new NashornScriptEngineFactory().getScriptEngine(); + final Object val = engine.eval("Java.asJSONCompatible([1, 2, 3])"); + assertEquals(asList(val), Arrays.asList(1, 2, 3)); + } + + /** + * Wrap an embedded array as a list. + */ + @Test + public void testWrapObjectWithArray() throws ScriptException { + final ScriptEngine engine = new NashornScriptEngineFactory().getScriptEngine(); + final Object val = engine.eval("Java.asJSONCompatible({x: [1, 2, 3]})"); + assertEquals(asList(asMap(val).get("x")), Arrays.asList(1, 2, 3)); + } + + /** + * Check it all works transitively several more levels down. + */ + @Test + public void testDeepWrapping() throws ScriptException { + final ScriptEngine engine = new NashornScriptEngineFactory().getScriptEngine(); + final Object val = engine.eval("Java.asJSONCompatible({x: [1, {y: [2, {z: [3]}]}, [4, 5]]})"); + final Map<String, Object> root = asMap(val); + final List<Object> x = asList(root.get("x")); + assertEquals(x.get(0), 1); + final Map<String, Object> x1 = asMap(x.get(1)); + final List<Object> y = asList(x1.get("y")); + assertEquals(y.get(0), 2); + final Map<String, Object> y1 = asMap(y.get(1)); + assertEquals(asList(y1.get("z")), Arrays.asList(3)); + assertEquals(asList(x.get(2)), Arrays.asList(4, 5)); + } + + /** + * Ensure that the old behaviour (every object is a Map) is unchanged. + */ + @Test + public void testNonWrapping() throws ScriptException { + final ScriptEngine engine = new NashornScriptEngineFactory().getScriptEngine(); + final Object val = engine.eval("({x: [1, {y: [2, {z: [3]}]}, [4, 5]]})"); + final Map<String, Object> root = asMap(val); + final Map<String, Object> x = asMap(root.get("x")); + assertEquals(x.get("0"), 1); + final Map<String, Object> x1 = asMap(x.get("1")); + final Map<String, Object> y = asMap(x1.get("y")); + assertEquals(y.get("0"), 2); + final Map<String, Object> y1 = asMap(y.get("1")); + final Map<String, Object> z = asMap(y1.get("z")); + assertEquals(z.get("0"), 3); + final Map<String, Object> x2 = asMap(x.get("2")); + assertEquals(x2.get("0"), 4); + assertEquals(x2.get("1"), 5); + } + + @SuppressWarnings("unchecked") + private static List<Object> asList(final Object obj) { + assertJSObject(obj); + Assert.assertTrue(obj instanceof List); + return (List)obj; + } + + @SuppressWarnings("unchecked") + private static Map<String, Object> asMap(final Object obj) { + assertJSObject(obj); + Assert.assertTrue(obj instanceof Map); + return (Map)obj; + } + + private static void assertJSObject(final Object obj) { + assertTrue(obj instanceof JSObject); + } +}
--- a/test/src/jdk/nashorn/api/scripting/test/PluggableJSObjectTest.java Mon Sep 28 15:36:24 2015 -0700 +++ b/test/src/jdk/nashorn/api/scripting/test/PluggableJSObjectTest.java Tue Oct 06 11:04:55 2015 -0700 @@ -27,6 +27,7 @@ import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; import static org.testng.Assert.fail; import java.nio.IntBuffer; @@ -34,9 +35,11 @@ import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Set; +import javax.script.Invocable; import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; import jdk.nashorn.api.scripting.AbstractJSObject; +import jdk.nashorn.api.scripting.ScriptObjectMirror; import org.testng.annotations.Test; /** @@ -286,4 +289,23 @@ fail(exp.getMessage()); } } + + // @bug 8137258: JSObjectLinker and BrowserJSObjectLinker should not expose internal JS objects + @Test + public void hidingInternalObjectsForJSObjectTest() throws Exception { + final ScriptEngineManager engineManager = new ScriptEngineManager(); + final ScriptEngine e = engineManager.getEngineByName("nashorn"); + + final String code = "function func(obj) { obj.foo = [5, 5]; obj.bar = {} }"; + e.eval(code); + + // call the exposed function but pass user defined JSObject impl as argument + ((Invocable)e).invokeFunction("func", new AbstractJSObject() { + @Override + public void setMember(final String name, final Object value) { + // make sure that wrapped objects are passed (and not internal impl. objects) + assertTrue(value.getClass() == ScriptObjectMirror.class); + } + }); + } }
--- a/test/src/jdk/nashorn/api/scripting/test/ScopeTest.java Mon Sep 28 15:36:24 2015 -0700 +++ b/test/src/jdk/nashorn/api/scripting/test/ScopeTest.java Tue Oct 06 11:04:55 2015 -0700 @@ -30,6 +30,7 @@ import static org.testng.Assert.assertTrue; import static org.testng.Assert.fail; import javax.script.Bindings; +import javax.script.Invocable; import javax.script.ScriptContext; import javax.script.ScriptEngine; import javax.script.ScriptEngineFactory; @@ -855,4 +856,59 @@ assertTrue(ret, "Expected true in iteration " + i); } } + + // @bug 8138616: invokeFunction fails if function calls a function defined in GLOBAL_SCOPE + @Test + public void invokeFunctionInGlobalScopeTest() throws Exception { + final ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn"); + final ScriptContext ctxt = engine.getContext(); + + // define a function called "func" + engine.eval("func = function() { return 42 }"); + + // move ENGINE_SCOPE Bindings to GLOBAL_SCOPE + ctxt.setBindings(ctxt.getBindings(ScriptContext.ENGINE_SCOPE), ScriptContext.GLOBAL_SCOPE); + + // create a new Bindings and set as ENGINE_SCOPE + ctxt.setBindings(engine.createBindings(), ScriptContext.ENGINE_SCOPE); + + // define new function that calls "func" now in GLOBAL_SCOPE + engine.eval("newfunc = function() { return func() }"); + + // call "newfunc" and check the return value + Object value = ((Invocable)engine).invokeFunction("newfunc"); + assertTrue(((Number)value).intValue() == 42); + } + + + // @bug 8138616: invokeFunction fails if function calls a function defined in GLOBAL_SCOPE + // variant of above that replaces default ScriptContext of the engine with a fresh instance! + @Test + public void invokeFunctionInGlobalScopeTest2() throws Exception { + final ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn"); + + // create a new ScriptContext instance + final ScriptContext ctxt = new SimpleScriptContext(); + // set it as 'default' ScriptContext + engine.setContext(ctxt); + + // create a new Bindings and set as ENGINE_SCOPE + ctxt.setBindings(engine.createBindings(), ScriptContext.ENGINE_SCOPE); + + // define a function called "func" + engine.eval("func = function() { return 42 }"); + + // move ENGINE_SCOPE Bindings to GLOBAL_SCOPE + ctxt.setBindings(ctxt.getBindings(ScriptContext.ENGINE_SCOPE), ScriptContext.GLOBAL_SCOPE); + + // create a new Bindings and set as ENGINE_SCOPE + ctxt.setBindings(engine.createBindings(), ScriptContext.ENGINE_SCOPE); + + // define new function that calls "func" now in GLOBAL_SCOPE + engine.eval("newfunc = function() { return func() }"); + + // call "newfunc" and check the return value + Object value = ((Invocable)engine).invokeFunction("newfunc"); + assertTrue(((Number)value).intValue() == 42); + } }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/src/jdk/nashorn/test/models/A.java Tue Oct 06 11:04:55 2015 -0700 @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.nashorn.test.models; + +public interface A { + default String a() { + return "from A.a"; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/src/jdk/nashorn/test/models/B.java Tue Oct 06 11:04:55 2015 -0700 @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.nashorn.test.models; + +public interface B extends A { + default String b() { + return "from B.b"; + } +}