8135190: Method code too large in Babel browser.js script
Reviewed-by: attila, sundar
This commit is contained in:
parent
2a12715485
commit
008b5c0ad9
@ -70,11 +70,10 @@ 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;
|
||||
@ -984,7 +983,7 @@ final class AssignSymbols extends NodeVisitor<LexicalContext> implements Loggabl
|
||||
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 +1009,8 @@ final class AssignSymbols extends NodeVisitor<LexicalContext> implements Loggabl
|
||||
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) {
|
||||
|
@ -105,7 +105,6 @@ import jdk.nashorn.internal.ir.LexicalContext;
|
||||
import jdk.nashorn.internal.ir.LexicalContextNode;
|
||||
import jdk.nashorn.internal.ir.LiteralNode;
|
||||
import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode;
|
||||
import jdk.nashorn.internal.ir.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;
|
||||
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 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
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<>();
|
||||
|
||||
@ -2234,73 +2234,33 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
*
|
||||
* @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();
|
||||
if (ranges != null) {
|
||||
|
||||
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]);
|
||||
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);
|
||||
}
|
||||
}, ranges, arrayType);
|
||||
|
||||
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;
|
||||
return;
|
||||
}
|
||||
|
||||
if(postsets.length > 0) {
|
||||
@ -2312,7 +2272,6 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
}
|
||||
method.load(arrayType, arraySlot);
|
||||
}
|
||||
return method;
|
||||
}
|
||||
|
||||
private void storeElement(final Expression[] nodes, final Type elementType, final int index) {
|
||||
@ -2537,6 +2496,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
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 +2543,13 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
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 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
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);
|
||||
@ -5461,4 +5475,21 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -34,7 +34,6 @@ import static jdk.nashorn.internal.codegen.ObjectClassGenerator.getPaddedFieldCo
|
||||
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 @@ public abstract class FieldObjectCreator<T> extends ObjectCreator<T> {
|
||||
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 @@ public abstract class FieldObjectCreator<T> extends ObjectCreator<T> {
|
||||
} 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 @@ public abstract class FieldObjectCreator<T> extends ObjectCreator<T> {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<? extends ScriptObject> getAllocatorClass() {
|
||||
return fieldObjectClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the class name for the object class,
|
||||
* e.g. {@code com.nashorn.oracle.scripts.JO2P0}
|
||||
|
@ -257,8 +257,7 @@ public class MethodEmitter {
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -36,7 +36,7 @@ import jdk.nashorn.internal.runtime.ScriptObject;
|
||||
* 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 @@ public abstract class ObjectCreator<T> {
|
||||
* 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.
|
||||
@ -124,6 +140,12 @@ public abstract class ObjectCreator<T> {
|
||||
return hasArguments;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
@ -145,29 +167,4 @@ public abstract class ObjectCreator<T> {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -27,13 +27,15 @@ package jdk.nashorn.internal.codegen;
|
||||
|
||||
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 @@ abstract class ReplaceCompileUnits extends NodeVisitor<LexicalContext> {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -61,7 +61,7 @@ public final class SpillObjectCreator extends ObjectCreator<Expression> {
|
||||
}
|
||||
|
||||
@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 @@ public final class SpillObjectCreator extends ObjectCreator<Expression> {
|
||||
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 @@ public final class SpillObjectCreator extends ObjectCreator<Expression> {
|
||||
|
||||
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 @@ public final class SpillObjectCreator extends ObjectCreator<Expression> {
|
||||
// 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 @@ public final class SpillObjectCreator extends ObjectCreator<Expression> {
|
||||
@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 @@ public final class SpillObjectCreator extends ObjectCreator<Expression> {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -36,9 +36,11 @@ 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.runtime.Context;
|
||||
@ -295,7 +297,7 @@ final class Splitter extends NodeVisitor<LexicalContext> implements Loggable {
|
||||
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 +311,7 @@ final class Splitter extends NodeVisitor<LexicalContext> implements Loggable {
|
||||
|
||||
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,15 +319,58 @@ final class Splitter extends NodeVisitor<LexicalContext> implements Loggable {
|
||||
|
||||
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
|
||||
|
@ -44,12 +44,13 @@ import jdk.nashorn.internal.ir.JumpToInlinedFinally;
|
||||
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 @@ final class WeighNodes extends NodeOperatorVisitor<LexicalContext> {
|
||||
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 class WeighNodes extends NodeOperatorVisitor<LexicalContext> {
|
||||
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) {
|
||||
@ -232,6 +235,27 @@ final class WeighNodes extends NodeOperatorVisitor<LexicalContext> {
|
||||
return true;
|
||||
}
|
||||
|
||||
@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;
|
||||
|
@ -65,6 +65,7 @@ import java.util.concurrent.ConcurrentMap;
|
||||
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 @@ public abstract class Type implements Comparable<Type>, BytecodeOps, Serializabl
|
||||
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 @@ public abstract class Type implements Comparable<Type>, BytecodeOps, Serializabl
|
||||
/**
|
||||
* 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 abstract class Type implements Comparable<Type>, BytecodeOps, Serializabl
|
||||
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 abstract class Type implements Comparable<Type>, BytecodeOps, Serializabl
|
||||
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 abstract class Type implements Comparable<Type>, BytecodeOps, Serializabl
|
||||
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));
|
||||
|
@ -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 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey {
|
||||
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;
|
||||
|
||||
@ -614,7 +621,7 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey {
|
||||
* 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. */
|
||||
@ -626,8 +633,8 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey {
|
||||
/** 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;
|
||||
/** Ranges for splitting up large literals in code generation */
|
||||
private final List<Splittable.SplitRange> splitRanges;
|
||||
|
||||
@Override
|
||||
public boolean isArray() {
|
||||
@ -635,64 +642,13 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey {
|
||||
|
||||
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 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey {
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -946,26 +902,27 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
@ -987,7 +944,7 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey {
|
||||
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) {
|
||||
|
@ -27,6 +27,7 @@ package jdk.nashorn.internal.ir;
|
||||
|
||||
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 @@ import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
* 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 final class ObjectNode extends Expression {
|
||||
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) {
|
||||
if (visitor.enterObjectNode(this)) {
|
||||
return visitor.leaveObjectNode(setElements(Node.accept(visitor, elements)));
|
||||
}
|
||||
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(lc, Node.accept(visitor, elements)));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -102,10 +114,35 @@ public final class ObjectNode extends Expression {
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -1033,6 +1033,16 @@ public final class Context {
|
||||
return (Class<? extends ScriptObject>)Class.forName(fullName, true, sharedLoader);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
|
67
nashorn/test/script/basic/JDK-8135190.js
Normal file
67
nashorn/test/script/basic/JDK-8135190.js
Normal file
@ -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);
|
Loading…
Reference in New Issue
Block a user