8020132: Big object literal with numerical keys exceeds method size

Reviewed-by: lagergren, sundar
This commit is contained in:
Hannes Wallnöfer 2013-08-01 12:23:38 +02:00
parent c9efbba43f
commit 24adb234a8
23 changed files with 24198 additions and 88 deletions

View File

@ -45,6 +45,7 @@ import static jdk.nashorn.internal.codegen.CompilerConstants.methodDescriptor;
import static jdk.nashorn.internal.codegen.CompilerConstants.staticCallNoLookup;
import static jdk.nashorn.internal.codegen.CompilerConstants.staticField;
import static jdk.nashorn.internal.codegen.CompilerConstants.typeDescriptor;
import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup;
import static jdk.nashorn.internal.ir.Symbol.IS_INTERNAL;
import static jdk.nashorn.internal.ir.Symbol.IS_TEMP;
import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_FAST_SCOPE;
@ -131,6 +132,7 @@ import jdk.nashorn.internal.runtime.ScriptObject;
import jdk.nashorn.internal.runtime.ScriptRuntime;
import jdk.nashorn.internal.runtime.Source;
import jdk.nashorn.internal.runtime.Undefined;
import jdk.nashorn.internal.runtime.arrays.ArrayData;
import jdk.nashorn.internal.runtime.linker.LinkerCallSite;
/**
@ -1262,7 +1264,11 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
classEmitter.needGetConstantMethod(cls);
} else {
method.loadConstants().load(index).arrayload();
if (cls != Object.class) {
if (object instanceof ArrayData) {
// avoid cast to non-public ArrayData subclass
method.checkcast(ArrayData.class);
method.invoke(virtualCallNoLookup(ArrayData.class, "copy", ArrayData.class));
} else if (cls != Object.class) {
method.checkcast(cls);
}
}

View File

@ -30,12 +30,15 @@ import static jdk.nashorn.internal.codegen.CompilerConstants.constructorNoLookup
import static jdk.nashorn.internal.codegen.CompilerConstants.typeDescriptor;
import static jdk.nashorn.internal.codegen.ObjectClassGenerator.getPaddedFieldCount;
import static jdk.nashorn.internal.codegen.types.Type.OBJECT;
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;
import jdk.nashorn.internal.runtime.Context;
import jdk.nashorn.internal.runtime.JSType;
import jdk.nashorn.internal.runtime.PropertyMap;
import jdk.nashorn.internal.runtime.ScriptObject;
import jdk.nashorn.internal.runtime.arrays.ArrayIndex;
@ -129,12 +132,12 @@ public abstract class FieldObjectCreator<T> extends ObjectCreator {
final T value = valueIter.next();
if (symbol != null && value != null) {
final int index = ArrayIndex.getArrayIndex(key);
final int index = getArrayIndex(key);
if (index < 0) {
if (!isValidArrayIndex(index)) {
putField(method, key, symbol.getFieldIndex(), value);
} else {
putSlot(method, index, value);
putSlot(method, ArrayIndex.toLongIndex(index), value);
}
}
}
@ -177,9 +180,13 @@ public abstract class FieldObjectCreator<T> extends ObjectCreator {
* @param index Slot index.
* @param value Value to store.
*/
private void putSlot(final MethodEmitter method, final int index, final T value) {
private void putSlot(final MethodEmitter method, final long index, final T value) {
method.dup();
method.load(index);
if (JSType.isRepresentableAsInt(index)) {
method.load((int) index);
} else {
method.load(index);
}
loadValue(value);
method.dynamicSetIndex(callSiteFlags);
}

View File

@ -25,13 +25,15 @@
package jdk.nashorn.internal.codegen;
import static jdk.nashorn.internal.runtime.arrays.ArrayIndex.getArrayIndex;
import static jdk.nashorn.internal.runtime.arrays.ArrayIndex.isValidArrayIndex;
import java.util.ArrayList;
import java.util.List;
import jdk.nashorn.internal.ir.Symbol;
import jdk.nashorn.internal.runtime.AccessorProperty;
import jdk.nashorn.internal.runtime.Property;
import jdk.nashorn.internal.runtime.PropertyMap;
import jdk.nashorn.internal.runtime.arrays.ArrayIndex;
/**
* Class that creates PropertyMap sent to script object constructors.
@ -76,7 +78,7 @@ public class MapCreator {
final String key = keys.get(i);
final Symbol symbol = symbols.get(i);
if (symbol != null && !ArrayIndex.isIntArrayIndex(key)) {
if (symbol != null && !isValidArrayIndex(getArrayIndex(key))) {
properties.add(new AccessorProperty(key, getPropertyFlags(symbol, hasArguments), structure, symbol.getFieldIndex()));
}
}
@ -93,7 +95,7 @@ public class MapCreator {
final String key = keys.get(i);
final Symbol symbol = symbols.get(i);
if (symbol != null && !ArrayIndex.isIntArrayIndex(key)) {
if (symbol != null && !isValidArrayIndex(getArrayIndex(key))) {
properties.add(new AccessorProperty(key, getPropertyFlags(symbol, hasArguments), spillIndex++));
}
}

View File

@ -26,9 +26,13 @@
package jdk.nashorn.internal.codegen;
import static jdk.nashorn.internal.codegen.CompilerConstants.constructorNoLookup;
import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup;
import static jdk.nashorn.internal.codegen.types.Type.OBJECT;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.Expression;
import jdk.nashorn.internal.ir.LiteralNode;
@ -36,6 +40,8 @@ import jdk.nashorn.internal.ir.Symbol;
import jdk.nashorn.internal.runtime.Property;
import jdk.nashorn.internal.runtime.PropertyMap;
import jdk.nashorn.internal.runtime.ScriptObject;
import jdk.nashorn.internal.runtime.arrays.ArrayData;
import jdk.nashorn.internal.runtime.arrays.ArrayIndex;
import jdk.nashorn.internal.scripts.JO;
/**
@ -63,56 +69,82 @@ public class SpillObjectCreator extends ObjectCreator {
protected void makeObject(final MethodEmitter method) {
assert !isScope() : "spill scope objects are not currently supported";
final int length = keys.size();
final Object[] presetValues = new Object[propertyMap.size()];
final Class<?> clazz = JO.class;
final int length = keys.size();
final Object[] presetValues = new Object[length];
final Set<Integer> postsetValues = new LinkedHashSet<>();
final int callSiteFlags = codegen.getCallSiteFlags();
ArrayData arrayData = ArrayData.allocate(new Object[0]);
// Compute constant values
// Compute constant property values
for (int i = 0; i < length; i++) {
final String key = keys.get(i);
final Property property = propertyMap.findProperty(key);
final Expression value = values.get(i);
if (value == null) {
continue; // getter or setter
}
final Object constantValue = LiteralNode.objectAsConstant(value);
if (constantValue == LiteralNode.POSTSET_MARKER) {
postsetValues.add(i);
continue;
}
final Property property = propertyMap.findProperty(key);
if (property != null) {
presetValues[property.getSlot()] = LiteralNode.objectAsConstant(values.get(i));
// normal property key
presetValues[property.getSlot()] = constantValue;
} else {
// array index key
final long oldLength = arrayData.length();
final int index = ArrayIndex.getArrayIndex(key);
assert ArrayIndex.isValidArrayIndex(index);
final long longIndex = ArrayIndex.toLongIndex(index);
if (longIndex >= oldLength) {
arrayData = arrayData.ensure(longIndex);
}
arrayData = arrayData.set(index, constantValue, false);
if (longIndex > oldLength) {
arrayData = arrayData.delete(oldLength, longIndex - 1);
}
}
}
method._new(clazz).dup();
// create object and invoke constructor
method._new(JO.class).dup();
codegen.loadConstant(propertyMap);
method.invoke(constructorNoLookup(JO.class, PropertyMap.class));
// Set spill array with preset values
method.dup();
codegen.loadConstant(presetValues);
method.putField(Type.getInternalName(ScriptObject.class), "spill", Type.OBJECT_ARRAY.getDescriptor());
// Set array data if any
if (arrayData.length() > 0) {
method.dup();
codegen.loadConstant(arrayData);
method.invoke(virtualCallNoLookup(ScriptObject.class, "setArray",void.class, ArrayData.class));
}
// Create properties with non-constant values
for (int i = 0; i < length; i++) {
for (int i : postsetValues) {
final String key = keys.get(i);
final Property property = propertyMap.findProperty(key);
if (property != null && presetValues[property.getSlot()] == LiteralNode.POSTSET_MARKER) {
if (property == null) {
final int index = ArrayIndex.getArrayIndex(key);
assert ArrayIndex.isValidArrayIndex(index);
method.dup();
method.load(ArrayIndex.toLongIndex(index));
codegen.load(values.get(i));
method.dynamicSetIndex(callSiteFlags);
} else {
method.dup();
method.getField(Type.getInternalName(ScriptObject.class), "spill", Type.OBJECT_ARRAY.getDescriptor());
method.load(property.getSlot());
codegen.load(values.get(i)).convert(OBJECT);
method.arraystore();
presetValues[property.getSlot()] = null;
}
}
method.putField(Type.typeFor(ScriptObject.class).getInternalName(), "spill", Type.OBJECT_ARRAY.getDescriptor());
final int callSiteFlags = codegen.getCallSiteFlags();
// Assign properties with valid array index keys
for (int i = 0; i < length; i++) {
final String key = keys.get(i);
final Property property = propertyMap.findProperty(key);
final Expression value = values.get(i);
if (property == null && value != null) {
method.dup();
method.load(keys.get(i));
codegen.load(value);
method.dynamicSetIndex(callSiteFlags);
}
}
}

View File

@ -115,6 +115,11 @@ abstract class ArrayBufferView extends ScriptObject {
this.elementLength = elementLength;
}
@Override
public ArrayData copy() {
throw new UnsupportedOperationException(); // Not used for ArrayBuffers
}
@Override
public Object[] asObjectArray() {
final Object[] array = new Object[elementLength];

View File

@ -44,26 +44,29 @@ import jdk.nashorn.tools.Shell;
*
*/
abstract class NashornLoader extends SecureClassLoader {
private static final String OBJECTS_PKG = "jdk.nashorn.internal.objects";
private static final String RUNTIME_PKG = "jdk.nashorn.internal.runtime";
private static final String OBJECTS_PKG = "jdk.nashorn.internal.objects";
private static final String RUNTIME_PKG = "jdk.nashorn.internal.runtime";
private static final String RUNTIME_ARRAYS_PKG = "jdk.nashorn.internal.runtime.arrays";
private static final String RUNTIME_LINKER_PKG = "jdk.nashorn.internal.runtime.linker";
private static final String SCRIPTS_PKG = "jdk.nashorn.internal.scripts";
private static final String SCRIPTS_PKG = "jdk.nashorn.internal.scripts";
private static final Permission[] SCRIPT_PERMISSIONS;
static {
SCRIPT_PERMISSIONS = new Permission[4];
static {
/*
* Generated classes get access to runtime, runtime.linker, objects, scripts packages.
* Note that the actual scripts can not access these because Java.type, Packages
* prevent these restricted packages. And Java reflection and JSR292 access is prevented
* for scripts. In other words, nashorn generated portions of script classes can access
* clases in these implementation packages.
* classes in these implementation packages.
*/
SCRIPT_PERMISSIONS[0] = new RuntimePermission("accessClassInPackage." + RUNTIME_PKG);
SCRIPT_PERMISSIONS[1] = new RuntimePermission("accessClassInPackage." + RUNTIME_LINKER_PKG);
SCRIPT_PERMISSIONS[2] = new RuntimePermission("accessClassInPackage." + OBJECTS_PKG);
SCRIPT_PERMISSIONS[3] = new RuntimePermission("accessClassInPackage." + SCRIPTS_PKG);
SCRIPT_PERMISSIONS = new Permission[] {
new RuntimePermission("accessClassInPackage." + RUNTIME_PKG),
new RuntimePermission("accessClassInPackage." + RUNTIME_LINKER_PKG),
new RuntimePermission("accessClassInPackage." + OBJECTS_PKG),
new RuntimePermission("accessClassInPackage." + SCRIPTS_PKG),
new RuntimePermission("accessClassInPackage." + RUNTIME_ARRAYS_PKG)
};
}
private final Context context;
@ -97,6 +100,7 @@ abstract class NashornLoader extends SecureClassLoader {
final String pkgName = name.substring(0, i);
switch (pkgName) {
case RUNTIME_PKG:
case RUNTIME_ARRAYS_PKG:
case RUNTIME_LINKER_PKG:
case OBJECTS_PKG:
case SCRIPTS_PKG:

View File

@ -56,7 +56,7 @@ public abstract class ArrayData {
* Constructor
* @param length Virtual length of the array.
*/
public ArrayData(final long length) {
protected ArrayData(final long length) {
this.length = length;
}
@ -182,6 +182,14 @@ public abstract class ArrayData {
return length;
}
/**
* Return a copy of the array that can be modified without affecting this instance.
* It is safe to return themselves for immutable subclasses.
*
* @return a new array
*/
public abstract ArrayData copy();
/**
* Return a copy of the array data as an Object array.
*

View File

@ -177,15 +177,5 @@ public final class ArrayIndex {
return index & JSType.MAX_UINT;
}
/**
* Check whether a key string represents a valid array index in JavaScript and is small enough
* to fit into a positive int.
*
* @param key the key
* @return true if key works as a valid int array index
*/
public static boolean isIntArrayIndex(final String key) {
return getArrayIndex(key) >= 0;
}
}

View File

@ -30,7 +30,7 @@ import jdk.nashorn.internal.runtime.ScriptObject;
/**
* Iterator over a NativeArray
*/
public class ArrayIterator extends ArrayLikeIterator<Object> {
class ArrayIterator extends ArrayLikeIterator<Object> {
/** Array {@link ScriptObject} to iterate over */
protected final ScriptObject array;

View File

@ -43,6 +43,13 @@ final class DeletedArrayFilter extends ArrayFilter {
this.deleted = new BitVector(underlying.length());
}
@Override
public ArrayData copy() {
DeletedArrayFilter copy = new DeletedArrayFilter(underlying.copy());
copy.getDeleted().copy(deleted);
return copy;
}
@Override
public Object[] asObjectArray() {
final Object[] value = super.asObjectArray();

View File

@ -49,6 +49,11 @@ final class DeletedRangeArrayFilter extends ArrayFilter {
return lo <= index && index <= hi;
}
@Override
public ArrayData copy() {
return new DeletedRangeArrayFilter(underlying.copy(), lo, hi);
}
@Override
public Object[] asObjectArray() {
final Object[] value = super.asObjectArray();
@ -191,11 +196,7 @@ final class DeletedRangeArrayFilter extends ArrayFilter {
private ArrayData getDeletedArrayFilter() {
final ArrayData deleteFilter = new DeletedArrayFilter(getUnderlying());
for (long i = lo; i <= hi; i++) {
deleteFilter.delete((int) i);
}
deleteFilter.delete(lo, hi);
return deleteFilter;
}

View File

@ -38,6 +38,11 @@ final class FrozenArrayFilter extends SealedArrayFilter {
super(underlying);
}
@Override
public ArrayData copy() {
return this;
}
@Override
public PropertyDescriptor getDescriptor(final GlobalObject global, final int index) {
return global.newDataDescriptor(getObject(index), false, true, false);

View File

@ -33,7 +33,7 @@ import jdk.nashorn.internal.runtime.ScriptRuntime;
* Implementation of {@link ArrayData} as soon as an int has been
* written to the array. This is the default data for new arrays
*/
public final class IntArrayData extends ArrayData {
final class IntArrayData extends ArrayData {
/**
* The wrapped array
*/
@ -55,10 +55,13 @@ public final class IntArrayData extends ArrayData {
*/
IntArrayData(final int array[], final int length) {
super(length);
assert array.length >= length;
this.array = array;
if (array.length > length) {
Arrays.fill(array, length, array.length, 0);
}
}
@Override
public ArrayData copy() {
return new IntArrayData(array.clone(), (int) length());
}
@Override

View File

@ -46,9 +46,15 @@ final class LongArrayData extends ArrayData {
*/
LongArrayData(final long array[], final int length) {
super(length);
assert array.length >= length;
this.array = array;
}
@Override
public ArrayData copy() {
return new LongArrayData(array.clone(), (int) length());
}
@Override
public Object[] asObjectArray() {
return toObjectArray(array, (int) length());

View File

@ -46,6 +46,11 @@ final class NoTypeArrayData extends ArrayData {
return new Object[0];
}
@Override
public ArrayData copy() {
return new NoTypeArrayData();
}
@Override
public Object asArrayOfType(final Class<?> componentType) {
return Array.newInstance(componentType, 0);

View File

@ -46,10 +46,13 @@ final class NumberArrayData extends ArrayData {
*/
NumberArrayData(final double array[], final int length) {
super(length);
assert array.length >= length;
this.array = array;
if (array.length > length) {
Arrays.fill(array, length, array.length, 0.0);
}
}
@Override
public ArrayData copy() {
return new NumberArrayData(array.clone(), (int) length());
}
@Override

View File

@ -47,10 +47,13 @@ final class ObjectArrayData extends ArrayData {
*/
ObjectArrayData(final Object array[], final int length) {
super(length);
assert array.length >= length;
this.array = array;
if (array.length > length) {
Arrays.fill(array, length, array.length, ScriptRuntime.UNDEFINED);
}
}
@Override
public ArrayData copy() {
return new ObjectArrayData(array.clone(), (int) length());
}
@Override
@ -109,9 +112,6 @@ final class ObjectArrayData extends ArrayData {
@Override
public ArrayData set(final int index, final Object value, final boolean strict) {
if (value == ScriptRuntime.UNDEFINED) {
return new UndefinedArrayFilter(this).set(index, value, strict);
}
array[index] = value;
setLength(Math.max(index + 1, length()));
return this;

View File

@ -30,7 +30,7 @@ import jdk.nashorn.internal.runtime.ScriptObject;
/**
* Reverse iterator over a NativeArray
*/
public final class ReverseArrayIterator extends ArrayIterator {
final class ReverseArrayIterator extends ArrayIterator {
/**
* Constructor

View File

@ -38,6 +38,11 @@ class SealedArrayFilter extends ArrayFilter {
super(underlying);
}
@Override
public ArrayData copy() {
return new SealedArrayFilter(underlying.copy());
}
@Override
public ArrayData slice(final long from, final long to) {
return getUnderlying().slice(from, to);

View File

@ -41,22 +41,26 @@ class SparseArrayData extends ArrayData {
private ArrayData underlying;
/** Maximum length to be stored in the array. */
private final long maxDenseLength;
/** Sparse elements. */
private TreeMap<Long, Object> sparseMap = new TreeMap<>();
private TreeMap<Long, Object> sparseMap;
SparseArrayData(final ArrayData underlying) {
super(underlying.length());
this.underlying = underlying;
this.maxDenseLength = Math.max(MAX_DENSE_LENGTH, underlying.length());
SparseArrayData(final ArrayData underlying, final long length) {
this(underlying, length, new TreeMap<Long, Object>());
}
SparseArrayData(final ArrayData array, final long length) {
this(array);
assert array.length() <= length;
super.setLength(length);
SparseArrayData(final ArrayData underlying, final long length, final TreeMap<Long, Object> sparseMap) {
super(length);
assert underlying.length() <= length;
this.underlying = underlying;
this.maxDenseLength = Math.max(MAX_DENSE_LENGTH, underlying.length());
this.sparseMap = sparseMap;
}
@Override
public ArrayData copy() {
return new SparseArrayData(underlying.copy(), length(), new TreeMap<Long, Object>(sparseMap));
}
@Override

View File

@ -43,6 +43,13 @@ final class UndefinedArrayFilter extends ArrayFilter {
this.undefined = new BitVector(underlying.length());
}
@Override
public ArrayData copy() {
UndefinedArrayFilter copy = new UndefinedArrayFilter(underlying.copy());
copy.getUndefined().copy(undefined);
return copy;
}
@Override
public Object[] asObjectArray() {
final Object[] value = super.asObjectArray();

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,18 @@
19970
58562
undefined
null
58565
19970
58565
23940
0
19970
58562
undefined
null
58565
19970
58565
23940
0