# HG changeset patch # User hannesw # Date 1442671468 -7200 # Node ID d8f34ffbbc7a7b940ddf14f382d2740ecd8284cf # Parent f61a63b7d1e52e307abc0bfc751203155d362ec4 8135190: Method code too large in Babel browser.js script Reviewed-by: attila, sundar diff -r f61a63b7d1e5 -r d8f34ffbbc7a nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/AssignSymbols.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/AssignSymbols.java Wed Jul 05 20:50:41 2017 +0200 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/AssignSymbols.java Sat Sep 19 16:04:28 2015 +0200 @@ -70,11 +70,10 @@ 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; @@ -984,7 +983,7 @@ boolean previousWasBlock = false; for (final Iterator 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 +1009,8 @@ throw new AssertionError(); } - private static boolean isSplitArray(final LexicalContextNode expr) { - if(!(expr instanceof ArrayLiteralNode)) { - return false; - } - final List 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) { diff -r f61a63b7d1e5 -r d8f34ffbbc7a nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CodeGenerator.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CodeGenerator.java Wed Jul 05 20:50:41 2017 +0200 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CodeGenerator.java Sat Sep 19 16:04:28 2015 +0200 @@ -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; @@ -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 emittedMethods = new HashSet<>(); @@ -2234,73 +2234,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 units = arrayLiteralNode.getUnits(); + final Expression[] nodes = arrayLiteralNode.getValue(); + final Object presets = arrayLiteralNode.getPresets(); + final int[] postsets = arrayLiteralNode.getPostsets(); + final List 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 +2272,6 @@ } method.load(arrayType, arraySlot); } - return method; } private void storeElement(final Expression[] nodes, final Type elementType, final int index) { @@ -2537,6 +2496,7 @@ final List> tuples = new ArrayList<>(); final List gettersSetters = new ArrayList<>(); final int ccp = getCurrentContinuationEntryPoint(); + final List ranges = objectNode.getSplitRanges(); Expression protoNode = null; boolean restOfProperty = false; @@ -2583,7 +2543,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 @@ -2899,6 +2865,54 @@ method.onLocalStore(type, slot); } + private void loadSplitLiteral(final SplitLiteralCreator creator, final List 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); @@ -5461,4 +5475,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); + } } diff -r f61a63b7d1e5 -r d8f34ffbbc7a nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/FieldObjectCreator.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/FieldObjectCreator.java Wed Jul 05 20:50:41 2017 +0200 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/FieldObjectCreator.java Sat Sep 19 16:04:28 2015 +0200 @@ -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> iter = tuples.iterator(); - - while (iter.hasNext()) { - final MapTuple tuple = iter.next(); + for (int i = start; i < end; i++) { + final MapTuple 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 getAllocatorClass() { + return fieldObjectClass; + } + /** * Get the class name for the object class, * e.g. {@code com.nashorn.oracle.scripts.JO2P0} diff -r f61a63b7d1e5 -r d8f34ffbbc7a nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/MethodEmitter.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/MethodEmitter.java Wed Jul 05 20:50:41 2017 +0200 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/MethodEmitter.java Sat Sep 19 16:04:28 2015 +0200 @@ -257,8 +257,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; } diff -r f61a63b7d1e5 -r d8f34ffbbc7a nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/ObjectCreator.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/ObjectCreator.java Wed Jul 05 20:50:41 2017 +0200 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/ObjectCreator.java Sat Sep 19 16:04:28 2015 +0200 @@ -36,7 +36,7 @@ * Base class for object creation code generation. * @param value type */ -public abstract class ObjectCreator { +public abstract class ObjectCreator implements CodeGenerator.SplitLiteralCreator { /** List of keys & symbols to initiate in this ObjectCreator */ final List> 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 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 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: - * var m = 1; var obj = {p0: m, p1: m = "foo", p2: m} - * @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); - } - } } diff -r f61a63b7d1e5 -r d8f34ffbbc7a nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/ReplaceCompileUnits.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/ReplaceCompileUnits.java Wed Jul 05 20:50:41 2017 +0200 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/ReplaceCompileUnits.java Sat Sep 19 16:04:28 2015 +0200 @@ -27,13 +27,15 @@ import java.util.ArrayList; 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.ObjectNode; +import jdk.nashorn.internal.ir.Splittable; import jdk.nashorn.internal.ir.visitor.NodeVisitor; /** @@ -70,15 +72,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 newArrayUnits = new ArrayList<>(); - for (final ArrayUnit au : aln.getUnits()) { - newArrayUnits.add(new ArrayUnit(getExistingReplacement(au), au.getLo(), au.getHi())); + final List 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 ranges = objectNode.getSplitRanges(); + if (ranges != null) { + final List 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); + } } diff -r f61a63b7d1e5 -r d8f34ffbbc7a nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/SpillObjectCreator.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/SpillObjectCreator.java Wed Jul 05 20:50:41 2017 +0200 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/SpillObjectCreator.java Sat Sep 19 16:04:28 2015 +0200 @@ -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 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 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 clazz = dualFields ? JD.class : JO.class; + final Class 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 getAllocatorClass() { + return codegen.useDualFields() ? JD.class : JO.class; + } } diff -r f61a63b7d1e5 -r d8f34ffbbc7a nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/Splitter.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/Splitter.java Wed Jul 05 20:50:41 2017 +0200 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/Splitter.java Sat Sep 19 16:04:28 2015 +0200 @@ -36,9 +36,11 @@ 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.runtime.Context; @@ -295,7 +297,7 @@ final ArrayLiteralNode arrayLiteralNode = (ArrayLiteralNode) literal; final Node[] value = arrayLiteralNode.getValue(); final int[] postsets = arrayLiteralNode.getPostsets(); - final List units = new ArrayList<>(); + final List ranges = new ArrayList<>(); long totalWeight = 0; int lo = 0; @@ -309,7 +311,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 +319,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 ranges = new ArrayList<>(); + final List 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; diff -r f61a63b7d1e5 -r d8f34ffbbc7a nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/WeighNodes.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/WeighNodes.java Wed Jul 05 20:50:41 2017 +0200 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/WeighNodes.java Sat Sep 19 16:04:28 2015 +0200 @@ -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 units = arrayLiteralNode.getUnits(); + final List 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 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; diff -r f61a63b7d1e5 -r d8f34ffbbc7a nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/types/Type.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/types/Type.java Wed Jul 05 20:50:41 2017 +0200 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/types/Type.java Sat Sep 19 16:04:28 2015 +0200 @@ -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)); diff -r f61a63b7d1e5 -r d8f34ffbbc7a nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/LiteralNode.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/LiteralNode.java Wed Jul 05 20:50:41 2017 +0200 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/LiteralNode.java Sat Sep 19 16:04:28 2015 +0200 @@ -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; @@ -583,6 +581,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 { private static final long serialVersionUID = 1L; @@ -614,7 +621,7 @@ * Array literal node class. */ @Immutable - public static final class ArrayLiteralNode extends LiteralNode implements LexicalContextNode { + public static final class ArrayLiteralNode extends LiteralNode implements LexicalContextNode, Splittable { private static final long serialVersionUID = 1L; /** Array element type. */ @@ -626,8 +633,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 units; + /** Ranges for splitting up large literals in code generation */ + private final List splitRanges; @Override public boolean isArray() { @@ -635,64 +642,13 @@ } - /** - * 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; - } - } - private static final class ArrayLiteralInitializer { static ArrayLiteralNode initialize(final ArrayLiteralNode node) { 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) { @@ -725,7 +681,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; } } @@ -842,19 +798,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 units) { + private ArrayLiteralNode(final ArrayLiteralNode node, final Expression[] value, final Type elementType, final int[] postsets, final Object presets, final List splitRanges) { super(node, value); this.elementType = elementType; this.postsets = postsets; this.presets = presets; - this.units = units; + this.splitRanges = splitRanges; } /** @@ -946,26 +902,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 getUnits() { - return units == null ? null : Collections.unmodifiableList(units); + @Override + public List 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 units) { - if (this.units == units) { + public ArrayLiteralNode setSplitRanges(final LexicalContext lc, final List 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 @@ -987,7 +944,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 value) { diff -r f61a63b7d1e5 -r d8f34ffbbc7a nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/ObjectNode.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/ObjectNode.java Wed Jul 05 20:50:41 2017 +0200 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/ObjectNode.java Sat Sep 19 16:04:28 2015 +0200 @@ -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 elements; + /** Ranges for splitting large literals over multiple compile units in codegen. */ + private final List splitRanges; + /** * Constructor * @@ -51,19 +55,27 @@ public ObjectNode(final long token, final int finish, final List 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 elements) { + private ObjectNode(final ObjectNode objectNode, final List elements, + final List splitRanges ) { super(objectNode); this.elements = elements; + this.splitRanges = splitRanges; } @Override public Node accept(final NodeVisitor visitor) { + return Acceptor.accept(this, visitor); + } + + @Override + public Node accept(final LexicalContext lc, final NodeVisitor 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 elements) { + private ObjectNode setElements(final LexicalContext lc, final List 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 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 getSplitRanges() { + return splitRanges == null ? null : Collections.unmodifiableList(splitRanges); + } + } diff -r f61a63b7d1e5 -r d8f34ffbbc7a nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/Splittable.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/Splittable.java Sat Sep 19 16:04:28 2015 +0200 @@ -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 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; + } + } +} diff -r f61a63b7d1e5 -r d8f34ffbbc7a nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/Context.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/Context.java Wed Jul 05 20:50:41 2017 +0200 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/Context.java Sat Sep 19 16:04:28 2015 +0200 @@ -1034,6 +1034,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 diff -r f61a63b7d1e5 -r d8f34ffbbc7a nashorn/test/script/basic/JDK-8135190.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nashorn/test/script/basic/JDK-8135190.js Sat Sep 19 16:04:28 2015 +0200 @@ -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);