mirror of
https://github.com/openjdk/jdk.git
synced 2026-03-15 18:33:41 +00:00
8017084: Use spill properties for large object literals
Reviewed-by: lagergren, sundar
This commit is contained in:
parent
1853f28ab3
commit
8f092c733a
@ -25,6 +25,7 @@
|
||||
|
||||
package jdk.nashorn.internal.tools.nasgen;
|
||||
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.ACC_FINAL;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PRIVATE;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PUBLIC;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.ACC_STATIC;
|
||||
@ -164,7 +165,6 @@ public class ClassGenerator {
|
||||
mi.visitCode();
|
||||
mi.pushNull();
|
||||
mi.putStatic(className, MAP_FIELD_NAME, MAP_DESC);
|
||||
mi.loadClass(className);
|
||||
mi.invokeStatic(MAP_TYPE, MAP_NEWMAP, MAP_NEWMAP_DESC);
|
||||
// stack: PropertyMap
|
||||
}
|
||||
@ -236,7 +236,7 @@ public class ClassGenerator {
|
||||
|
||||
static void addMapField(final ClassVisitor cv) {
|
||||
// add a MAP static field
|
||||
final FieldVisitor fv = cv.visitField(ACC_PRIVATE | ACC_STATIC,
|
||||
final FieldVisitor fv = cv.visitField(ACC_PRIVATE | ACC_STATIC | ACC_FINAL,
|
||||
MAP_FIELD_NAME, MAP_DESC, null, null);
|
||||
if (fv != null) {
|
||||
fv.visitEnd();
|
||||
|
||||
@ -96,12 +96,10 @@ public interface StringConstants {
|
||||
static final String MAP_TYPE = TYPE_PROPERTYMAP.getInternalName();
|
||||
static final String MAP_DESC = TYPE_PROPERTYMAP.getDescriptor();
|
||||
static final String MAP_NEWMAP = "newMap";
|
||||
static final String MAP_NEWMAP_DESC = Type.getMethodDescriptor(TYPE_PROPERTYMAP, TYPE_CLASS);
|
||||
static final String MAP_NEWMAP_DESC = Type.getMethodDescriptor(TYPE_PROPERTYMAP);
|
||||
static final String MAP_DUPLICATE = "duplicate";
|
||||
static final String MAP_DUPLICATE_DESC = Type.getMethodDescriptor(TYPE_PROPERTYMAP);
|
||||
static final String MAP_SETFLAGS = "setFlags";
|
||||
static final String LOOKUP_TYPE = TYPE_LOOKUP.getInternalName();
|
||||
static final String LOOKUP_GETMETHOD = "getMethod";
|
||||
static final String LOOKUP_NEWPROPERTY = "newProperty";
|
||||
static final String LOOKUP_NEWPROPERTY_DESC =
|
||||
Type.getMethodDescriptor(TYPE_PROPERTYMAP, TYPE_PROPERTYMAP, TYPE_STRING, Type.INT_TYPE, TYPE_METHODHANDLE, TYPE_METHODHANDLE);
|
||||
|
||||
@ -179,6 +179,8 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
|
||||
private static final DebugLogger LOG = new DebugLogger("codegen", "nashorn.codegen.debug");
|
||||
|
||||
/** From what size should we use spill instead of fields for JavaScript objects? */
|
||||
private static final int OBJECT_SPILL_THRESHOLD = 300;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
@ -942,7 +944,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
* Create a new object based on the symbols and values, generate
|
||||
* bootstrap code for object
|
||||
*/
|
||||
final FieldObjectCreator<Symbol> foc = new FieldObjectCreator<Symbol>(this, nameList, newSymbols, values, true, hasArguments) {
|
||||
new FieldObjectCreator<Symbol>(this, nameList, newSymbols, values, true, hasArguments) {
|
||||
@Override
|
||||
protected void loadValue(final Symbol value) {
|
||||
method.load(value);
|
||||
@ -956,8 +958,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
m.loadNull();
|
||||
}
|
||||
}
|
||||
};
|
||||
foc.makeObject(method);
|
||||
}.makeObject(method);
|
||||
|
||||
// runScript(): merge scope into global
|
||||
if (isFunctionBody && function.isProgram()) {
|
||||
@ -1320,7 +1321,6 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
return method;
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
@Override
|
||||
public boolean enterLiteralNode(final LiteralNode literalNode) {
|
||||
assert literalNode.getSymbol() != null : literalNode + " has no symbol";
|
||||
@ -1352,73 +1352,71 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
values.add(value);
|
||||
}
|
||||
|
||||
new FieldObjectCreator<Node>(this, keys, symbols, values) {
|
||||
@Override
|
||||
protected void loadValue(final Node node) {
|
||||
load(node);
|
||||
}
|
||||
if (elements.size() > OBJECT_SPILL_THRESHOLD) {
|
||||
new SpillObjectCreator(this, keys, symbols, values).makeObject(method);
|
||||
} else {
|
||||
new FieldObjectCreator<Node>(this, keys, symbols, values) {
|
||||
@Override
|
||||
protected void loadValue(final Node node) {
|
||||
load(node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that the properties start out as object types so that
|
||||
* we can do putfield initializations instead of dynamicSetIndex
|
||||
* which would be the case to determine initial property type
|
||||
* otherwise.
|
||||
*
|
||||
* Use case, it's very expensive to do a million var x = {a:obj, b:obj}
|
||||
* just to have to invalidate them immediately on initialization
|
||||
*
|
||||
* see NASHORN-594
|
||||
*/
|
||||
@Override
|
||||
protected MapCreator newMapCreator(final Class<?> fieldObjectClass) {
|
||||
return new MapCreator(fieldObjectClass, keys, symbols) {
|
||||
@Override
|
||||
protected int getPropertyFlags(final Symbol symbol, final boolean isVarArg) {
|
||||
return super.getPropertyFlags(symbol, isVarArg) | Property.IS_ALWAYS_OBJECT;
|
||||
}
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Ensure that the properties start out as object types so that
|
||||
* we can do putfield initializations instead of dynamicSetIndex
|
||||
* which would be the case to determine initial property type
|
||||
* otherwise.
|
||||
*
|
||||
* Use case, it's very expensive to do a million var x = {a:obj, b:obj}
|
||||
* just to have to invalidate them immediately on initialization
|
||||
*
|
||||
* see NASHORN-594
|
||||
*/
|
||||
@Override
|
||||
protected MapCreator newMapCreator(final Class<?> fieldObjectClass) {
|
||||
return new MapCreator(fieldObjectClass, keys, symbols) {
|
||||
@Override
|
||||
protected int getPropertyFlags(final Symbol symbol, final boolean hasArguments) {
|
||||
return super.getPropertyFlags(symbol, hasArguments) | Property.IS_ALWAYS_OBJECT;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}.makeObject(method);
|
||||
}.makeObject(method);
|
||||
}
|
||||
|
||||
method.dup();
|
||||
globalObjectPrototype();
|
||||
method.invoke(ScriptObject.SET_PROTO);
|
||||
|
||||
if (!hasGettersSetters) {
|
||||
method.store(objectNode.getSymbol());
|
||||
return false;
|
||||
}
|
||||
if (hasGettersSetters) {
|
||||
for (final PropertyNode propertyNode : elements) {
|
||||
final FunctionNode getter = propertyNode.getGetter();
|
||||
final FunctionNode setter = propertyNode.getSetter();
|
||||
|
||||
for (final Node element : elements) {
|
||||
final PropertyNode propertyNode = (PropertyNode)element;
|
||||
final Object key = propertyNode.getKey();
|
||||
final FunctionNode getter = propertyNode.getGetter();
|
||||
final FunctionNode setter = propertyNode.getSetter();
|
||||
if (getter == null && setter == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (getter == null && setter == null) {
|
||||
continue;
|
||||
method.dup().loadKey(propertyNode.getKey());
|
||||
|
||||
if (getter == null) {
|
||||
method.loadNull();
|
||||
} else {
|
||||
getter.accept(this);
|
||||
}
|
||||
|
||||
if (setter == null) {
|
||||
method.loadNull();
|
||||
} else {
|
||||
setter.accept(this);
|
||||
}
|
||||
|
||||
method.invoke(ScriptObject.SET_USER_ACCESSORS);
|
||||
}
|
||||
|
||||
method.dup().loadKey(key);
|
||||
|
||||
if (getter == null) {
|
||||
method.loadNull();
|
||||
} else {
|
||||
getter.accept(this);
|
||||
}
|
||||
|
||||
if (setter == null) {
|
||||
method.loadNull();
|
||||
} else {
|
||||
setter.accept(this);
|
||||
}
|
||||
|
||||
method.invoke(ScriptObject.SET_USER_ACCESSORS);
|
||||
}
|
||||
|
||||
method.store(objectNode.getSymbol());
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -3183,24 +3181,21 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
return;
|
||||
}
|
||||
|
||||
final boolean isLazy = functionNode.isLazy();
|
||||
// Generate the object class and property map in case this function is ever used as constructor
|
||||
final String className = SCRIPTFUNCTION_IMPL_OBJECT;
|
||||
final int fieldCount = ObjectClassGenerator.getPaddedFieldCount(functionNode.countThisProperties());
|
||||
final String allocatorClassName = Compiler.binaryName(ObjectClassGenerator.getClassName(fieldCount));
|
||||
final PropertyMap allocatorMap = PropertyMap.newMap(null, 0, fieldCount, 0);
|
||||
|
||||
new ObjectCreator(this, new ArrayList<String>(), new ArrayList<Symbol>(), false, false) {
|
||||
@Override
|
||||
protected void makeObject(final MethodEmitter m) {
|
||||
final String className = SCRIPTFUNCTION_IMPL_OBJECT;
|
||||
method._new(className).dup();
|
||||
loadConstant(new RecompilableScriptFunctionData(functionNode, compiler.getCodeInstaller(), allocatorClassName, allocatorMap));
|
||||
|
||||
m._new(className).dup();
|
||||
loadConstant(new RecompilableScriptFunctionData(functionNode, compiler.getCodeInstaller(), Compiler.binaryName(getClassName()), makeMap()));
|
||||
|
||||
if (isLazy || functionNode.needsParentScope()) {
|
||||
m.loadCompilerConstant(SCOPE);
|
||||
} else {
|
||||
m.loadNull();
|
||||
}
|
||||
m.invoke(constructorNoLookup(className, RecompilableScriptFunctionData.class, ScriptObject.class));
|
||||
}
|
||||
}.makeObject(method);
|
||||
if (functionNode.isLazy() || functionNode.needsParentScope()) {
|
||||
method.loadCompilerConstant(SCOPE);
|
||||
} else {
|
||||
method.loadNull();
|
||||
}
|
||||
method.invoke(constructorNoLookup(className, RecompilableScriptFunctionData.class, ScriptObject.class));
|
||||
}
|
||||
|
||||
// calls on Global class.
|
||||
|
||||
@ -26,15 +26,16 @@
|
||||
package jdk.nashorn.internal.codegen;
|
||||
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS;
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
|
||||
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 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.PropertyMap;
|
||||
import jdk.nashorn.internal.runtime.ScriptObject;
|
||||
import jdk.nashorn.internal.runtime.arrays.ArrayIndex;
|
||||
@ -48,6 +49,13 @@ import jdk.nashorn.internal.runtime.arrays.ArrayIndex;
|
||||
* @see jdk.nashorn.internal.ir.Node
|
||||
*/
|
||||
public abstract class FieldObjectCreator<T> extends ObjectCreator {
|
||||
|
||||
private String fieldObjectClassName;
|
||||
private Class<?> fieldObjectClass;
|
||||
private int fieldCount;
|
||||
private int paddedFieldCount;
|
||||
private int paramCount;
|
||||
|
||||
/** array of corresponding values to symbols (null for no values) */
|
||||
private final List<T> values;
|
||||
|
||||
@ -80,14 +88,9 @@ public abstract class FieldObjectCreator<T> extends ObjectCreator {
|
||||
super(codegen, keys, symbols, isScope, hasArguments);
|
||||
this.values = values;
|
||||
this.callSiteFlags = codegen.getCallSiteFlags();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the scope on the stack through the passed method emitter.
|
||||
* @param method the method emitter to use
|
||||
*/
|
||||
protected void loadScope(final MethodEmitter method) {
|
||||
method.loadCompilerConstant(SCOPE);
|
||||
countFields();
|
||||
findClass();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -137,6 +140,13 @@ public abstract class FieldObjectCreator<T> extends ObjectCreator {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PropertyMap makeMap() {
|
||||
assert propertyMap == null : "property map already initialized";
|
||||
propertyMap = newMapCreator(fieldObjectClass).makeFieldMap(hasArguments(), fieldCount, paddedFieldCount);
|
||||
return propertyMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Technique for loading an initial value. Defined by anonymous subclasses in code gen.
|
||||
*
|
||||
@ -173,4 +183,47 @@ public abstract class FieldObjectCreator<T> extends ObjectCreator {
|
||||
loadValue(value);
|
||||
method.dynamicSetIndex(callSiteFlags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Locate (or indirectly create) the object container class.
|
||||
*/
|
||||
private void findClass() {
|
||||
fieldObjectClassName = isScope() ?
|
||||
ObjectClassGenerator.getClassName(fieldCount, paramCount) :
|
||||
ObjectClassGenerator.getClassName(paddedFieldCount);
|
||||
|
||||
try {
|
||||
this.fieldObjectClass = Context.forStructureClass(Compiler.binaryName(fieldObjectClassName));
|
||||
} catch (final ClassNotFoundException e) {
|
||||
throw new AssertionError("Nashorn has encountered an internal error. Structure can not be created.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the class name for the object class,
|
||||
* e.g. {@code com.nashorn.oracle.scripts.JO2P0}
|
||||
*
|
||||
* @return script class name
|
||||
*/
|
||||
String getClassName() {
|
||||
return fieldObjectClassName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tally the number of fields and parameters.
|
||||
*/
|
||||
private void countFields() {
|
||||
for (final Symbol symbol : this.symbols) {
|
||||
if (symbol != null) {
|
||||
if (hasArguments() && symbol.isParam()) {
|
||||
symbol.setFieldIndex(paramCount++);
|
||||
} else {
|
||||
symbol.setFieldIndex(fieldCount++);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
paddedFieldCount = getPaddedFieldCount(fieldCount);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -175,6 +175,14 @@ final class FinalizeTypes extends NodeOperatorVisitor<LexicalContext> {
|
||||
if (destType == null) {
|
||||
destType = specBinaryNode.getType();
|
||||
}
|
||||
// Register assignments to this object in case this is used as constructor
|
||||
if (binaryNode.lhs() instanceof AccessNode) {
|
||||
AccessNode accessNode = (AccessNode) binaryNode.lhs();
|
||||
|
||||
if (accessNode.getBase().getSymbol().isThis()) {
|
||||
lc.getCurrentFunction().addThisProperty(accessNode.getProperty().getName());
|
||||
}
|
||||
}
|
||||
return specBinaryNode.setRHS(convert(specBinaryNode.rhs(), destType));
|
||||
}
|
||||
|
||||
|
||||
@ -41,10 +41,10 @@ public class MapCreator {
|
||||
private final Class<?> structure;
|
||||
|
||||
/** key set for object map */
|
||||
private final String[] keys;
|
||||
final List<String> keys;
|
||||
|
||||
/** corresponding symbol set for object map */
|
||||
private final Symbol[] symbols;
|
||||
final List<Symbol> symbols;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
@ -54,11 +54,9 @@ public class MapCreator {
|
||||
* @param symbols list of symbols for map
|
||||
*/
|
||||
MapCreator(final Class<?> structure, final List<String> keys, final List<Symbol> symbols) {
|
||||
final int size = keys.size();
|
||||
|
||||
this.structure = structure;
|
||||
this.keys = keys.toArray(new String[size]);
|
||||
this.symbols = symbols.toArray(new Symbol[size]);
|
||||
this.keys = keys;
|
||||
this.symbols = symbols;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -70,21 +68,37 @@ public class MapCreator {
|
||||
*
|
||||
* @return New map populated with accessor properties.
|
||||
*/
|
||||
PropertyMap makeMap(final boolean hasArguments, final int fieldCount, final int fieldMaximum) {
|
||||
PropertyMap makeFieldMap(final boolean hasArguments, final int fieldCount, final int fieldMaximum) {
|
||||
final List<Property> properties = new ArrayList<>();
|
||||
|
||||
assert keys != null;
|
||||
|
||||
for (int i = 0; i < keys.length; i++) {
|
||||
final String key = keys[i];
|
||||
final Symbol symbol = symbols[i];
|
||||
for (int i = 0, length = keys.size(); i < length; i++) {
|
||||
final String key = keys.get(i);
|
||||
final Symbol symbol = symbols.get(i);
|
||||
|
||||
if (symbol != null && !ArrayIndex.isIntArrayIndex(key)) {
|
||||
properties.add(new AccessorProperty(key, getPropertyFlags(symbol, hasArguments), structure, symbol.getFieldIndex()));
|
||||
}
|
||||
}
|
||||
|
||||
return PropertyMap.newMap(structure, properties, fieldCount, fieldMaximum);
|
||||
return PropertyMap.newMap(properties, fieldCount, fieldMaximum, 0);
|
||||
}
|
||||
|
||||
PropertyMap makeSpillMap(final boolean hasArguments) {
|
||||
final List<Property> properties = new ArrayList<>();
|
||||
int spillIndex = 0;
|
||||
assert keys != null;
|
||||
|
||||
for (int i = 0, length = keys.size(); i < length; i++) {
|
||||
final String key = keys.get(i);
|
||||
final Symbol symbol = symbols.get(i);
|
||||
|
||||
if (symbol != null && !ArrayIndex.isIntArrayIndex(key)) {
|
||||
properties.add(new AccessorProperty(key, getPropertyFlags(symbol, hasArguments), spillIndex++));
|
||||
}
|
||||
}
|
||||
|
||||
return PropertyMap.newMap(properties, 0, 0, spillIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -73,11 +73,6 @@ public final class ObjectClassGenerator {
|
||||
*/
|
||||
static final int FIELD_PADDING = 4;
|
||||
|
||||
/**
|
||||
* Rounding when calculating the number of fields.
|
||||
*/
|
||||
static final int FIELD_ROUNDING = 4;
|
||||
|
||||
/**
|
||||
* Debug field logger
|
||||
* Should we print debugging information for fields when they are generated and getters/setters are called?
|
||||
@ -325,7 +320,6 @@ public final class ObjectClassGenerator {
|
||||
final List<String> initFields = addFields(classEmitter, fieldCount);
|
||||
|
||||
final MethodEmitter init = newInitMethod(classEmitter);
|
||||
initializeToUndefined(init, className, initFields);
|
||||
init.returnVoid();
|
||||
init.end();
|
||||
|
||||
@ -709,6 +703,15 @@ public final class ObjectClassGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add padding to field count to avoid creating too many classes and have some spare fields
|
||||
* @param count the field count
|
||||
* @return the padded field count
|
||||
*/
|
||||
static int getPaddedFieldCount(final int count) {
|
||||
return count / FIELD_PADDING * FIELD_PADDING + FIELD_PADDING;
|
||||
}
|
||||
|
||||
//
|
||||
// Provide generic getters and setters for undefined types. If a type is undefined, all
|
||||
// and marshals the set to the correct setter depending on the type of the value being set.
|
||||
|
||||
@ -25,10 +25,10 @@
|
||||
|
||||
package jdk.nashorn.internal.codegen;
|
||||
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
|
||||
|
||||
import java.util.List;
|
||||
import static jdk.nashorn.internal.codegen.ObjectClassGenerator.FIELD_PADDING;
|
||||
import jdk.nashorn.internal.ir.Symbol;
|
||||
import jdk.nashorn.internal.runtime.Context;
|
||||
import jdk.nashorn.internal.runtime.PropertyMap;
|
||||
|
||||
/**
|
||||
@ -36,9 +36,6 @@ import jdk.nashorn.internal.runtime.PropertyMap;
|
||||
*/
|
||||
public abstract class ObjectCreator {
|
||||
|
||||
/** Compile unit for this ObjectCreator, see CompileUnit */
|
||||
//protected final CompileUnit compileUnit;
|
||||
|
||||
/** List of keys to initiate in this ObjectCreator */
|
||||
protected final List<String> keys;
|
||||
|
||||
@ -50,12 +47,7 @@ public abstract class ObjectCreator {
|
||||
|
||||
private final boolean isScope;
|
||||
private final boolean hasArguments;
|
||||
private int fieldCount;
|
||||
private int paddedFieldCount;
|
||||
private int paramCount;
|
||||
private String fieldObjectClassName;
|
||||
private Class<?> fieldObjectClass;
|
||||
private PropertyMap propertyMap;
|
||||
protected PropertyMap propertyMap;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
@ -72,41 +64,6 @@ public abstract class ObjectCreator {
|
||||
this.symbols = symbols;
|
||||
this.isScope = isScope;
|
||||
this.hasArguments = hasArguments;
|
||||
|
||||
countFields();
|
||||
findClass();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tally the number of fields and parameters.
|
||||
*/
|
||||
private void countFields() {
|
||||
for (final Symbol symbol : this.symbols) {
|
||||
if (symbol != null) {
|
||||
if (hasArguments() && symbol.isParam()) {
|
||||
symbol.setFieldIndex(paramCount++);
|
||||
} else {
|
||||
symbol.setFieldIndex(fieldCount++);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
paddedFieldCount = fieldCount + FIELD_PADDING;
|
||||
}
|
||||
|
||||
/**
|
||||
* Locate (or indirectly create) the object container class.
|
||||
*/
|
||||
private void findClass() {
|
||||
fieldObjectClassName = isScope() ?
|
||||
ObjectClassGenerator.getClassName(fieldCount, paramCount) :
|
||||
ObjectClassGenerator.getClassName(paddedFieldCount);
|
||||
|
||||
try {
|
||||
this.fieldObjectClass = Context.forStructureClass(Compiler.binaryName(fieldObjectClassName));
|
||||
} catch (final ClassNotFoundException e) {
|
||||
throw new AssertionError("Nashorn has encountered an internal error. Structure can not be created.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -115,6 +72,12 @@ public abstract class ObjectCreator {
|
||||
*/
|
||||
protected abstract void makeObject(final MethodEmitter method);
|
||||
|
||||
/**
|
||||
* Construct the property map appropriate for the object.
|
||||
* @return the newly created property map
|
||||
*/
|
||||
protected abstract PropertyMap makeMap();
|
||||
|
||||
/**
|
||||
* Create a new MapCreator
|
||||
* @param clazz type of MapCreator
|
||||
@ -125,12 +88,11 @@ public abstract class ObjectCreator {
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct the property map appropriate for the object.
|
||||
* @return the newly created property map
|
||||
* Loads the scope on the stack through the passed method emitter.
|
||||
* @param method the method emitter to use
|
||||
*/
|
||||
protected PropertyMap makeMap() {
|
||||
propertyMap = newMapCreator(fieldObjectClass).makeMap(hasArguments(), fieldCount, paddedFieldCount);
|
||||
return propertyMap;
|
||||
protected void loadScope(final MethodEmitter method) {
|
||||
method.loadCompilerConstant(SCOPE);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -143,16 +105,6 @@ public abstract class ObjectCreator {
|
||||
return method;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the class name for the object class,
|
||||
* e.g. {@code com.nashorn.oracle.scripts.JO2P0}
|
||||
*
|
||||
* @return script class name
|
||||
*/
|
||||
String getClassName() {
|
||||
return fieldObjectClassName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this a scope object
|
||||
* @return true if scope
|
||||
|
||||
134
nashorn/src/jdk/nashorn/internal/codegen/SpillObjectCreator.java
Normal file
134
nashorn/src/jdk/nashorn/internal/codegen/SpillObjectCreator.java
Normal file
@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.nashorn.internal.codegen;
|
||||
|
||||
import jdk.nashorn.internal.codegen.types.Type;
|
||||
import jdk.nashorn.internal.ir.LiteralNode;
|
||||
import jdk.nashorn.internal.ir.Node;
|
||||
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.scripts.JO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.constructorNoLookup;
|
||||
import static jdk.nashorn.internal.codegen.types.Type.OBJECT;
|
||||
|
||||
/**
|
||||
* An object creator that uses spill properties.
|
||||
*/
|
||||
public class SpillObjectCreator extends ObjectCreator {
|
||||
|
||||
private final List<Node> values;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param codegen code generator
|
||||
* @param keys keys for fields in object
|
||||
* @param symbols symbols for fields in object
|
||||
* @param values list of values corresponding to keys
|
||||
*/
|
||||
protected SpillObjectCreator(final CodeGenerator codegen, final List<String> keys, final List<Symbol> symbols, final List<Node> values) {
|
||||
super(codegen, keys, symbols, false, false);
|
||||
this.values = values;
|
||||
makeMap();
|
||||
}
|
||||
|
||||
@Override
|
||||
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;
|
||||
|
||||
// Compute constant values
|
||||
for (int i = 0; i < length; i++) {
|
||||
final String key = keys.get(i);
|
||||
final Property property = propertyMap.findProperty(key);
|
||||
|
||||
if (property != null) {
|
||||
presetValues[property.getSlot()] = LiteralNode.objectAsConstant(values.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
method._new(clazz).dup();
|
||||
codegen.loadConstant(propertyMap);
|
||||
|
||||
method.invoke(constructorNoLookup(JO.class, PropertyMap.class));
|
||||
|
||||
method.dup();
|
||||
codegen.loadConstant(presetValues);
|
||||
|
||||
// Create properties with non-constant values
|
||||
for (int i = 0; i < length; i++) {
|
||||
final String key = keys.get(i);
|
||||
final Property property = propertyMap.findProperty(key);
|
||||
|
||||
if (property != null && presetValues[property.getSlot()] == LiteralNode.POSTSET_MARKER) {
|
||||
method.dup();
|
||||
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 Node value = values.get(i);
|
||||
|
||||
if (property == null && value != null) {
|
||||
method.dup();
|
||||
method.load(keys.get(i));
|
||||
codegen.load(value);
|
||||
method.dynamicSetIndex(callSiteFlags);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PropertyMap makeMap() {
|
||||
assert propertyMap == null : "property map already initialized";
|
||||
|
||||
propertyMap = new MapCreator(JO.class, keys, symbols) {
|
||||
@Override
|
||||
protected int getPropertyFlags(Symbol symbol, boolean hasArguments) {
|
||||
return super.getPropertyFlags(symbol, hasArguments) | Property.IS_SPILL | Property.IS_ALWAYS_OBJECT;
|
||||
}
|
||||
}.makeSpillMap(false);
|
||||
|
||||
return propertyMap;
|
||||
}
|
||||
}
|
||||
@ -131,6 +131,10 @@ public final class FunctionNode extends LexicalContextNode implements Flags<Func
|
||||
@Ignore
|
||||
private final Compiler.Hints hints;
|
||||
|
||||
/** Properties of this object assigned in this function */
|
||||
@Ignore
|
||||
private HashSet<String> thisProperties;
|
||||
|
||||
/** Function flags. */
|
||||
private final int flags;
|
||||
|
||||
@ -277,6 +281,7 @@ public final class FunctionNode extends LexicalContextNode implements Flags<Func
|
||||
this.declaredSymbols = functionNode.declaredSymbols;
|
||||
this.kind = functionNode.kind;
|
||||
this.firstToken = functionNode.firstToken;
|
||||
this.thisProperties = functionNode.thisProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -613,6 +618,25 @@ public final class FunctionNode extends LexicalContextNode implements Flags<Func
|
||||
return getFlag(NEEDS_PARENT_SCOPE) || isProgram();
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a property assigned to the this object in this function.
|
||||
* @param key the property name
|
||||
*/
|
||||
public void addThisProperty(final String key) {
|
||||
if (thisProperties == null) {
|
||||
thisProperties = new HashSet<>();
|
||||
}
|
||||
thisProperties.add(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of properties assigned to the this object in this function.
|
||||
* @return number of properties
|
||||
*/
|
||||
public int countThisProperties() {
|
||||
return thisProperties == null ? 0 : thisProperties.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the kind of this function
|
||||
* @see FunctionNode.Kind
|
||||
|
||||
@ -49,6 +49,9 @@ public abstract class LiteralNode<T> extends Node implements PropertyKey {
|
||||
/** Literal value */
|
||||
protected final T value;
|
||||
|
||||
/** Marker for values that must be computed at runtime */
|
||||
public static final Object POSTSET_MARKER = new Object();
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
@ -495,6 +498,30 @@ public abstract class LiteralNode<T> extends Node implements PropertyKey {
|
||||
return new LexerTokenLiteralNode(parent.getToken(), parent.getFinish(), value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the constant value for an object, or {@link #POSTSET_MARKER} if the value can't be statically computed.
|
||||
*
|
||||
* @param object a node or value object
|
||||
* @return the constant value or {@code POSTSET_MARKER}
|
||||
*/
|
||||
public static Object objectAsConstant(final Object object) {
|
||||
if (object == null) {
|
||||
return null;
|
||||
} else if (object instanceof Number || object instanceof String || object instanceof Boolean) {
|
||||
return object;
|
||||
} else if (object instanceof LiteralNode) {
|
||||
return objectAsConstant(((LiteralNode<?>)object).getValue());
|
||||
} else if (object instanceof UnaryNode) {
|
||||
final UnaryNode unaryNode = (UnaryNode)object;
|
||||
|
||||
if (unaryNode.isTokenType(TokenType.CONVERT) && unaryNode.getType().isObject()) {
|
||||
return objectAsConstant(unaryNode.rhs());
|
||||
}
|
||||
}
|
||||
|
||||
return POSTSET_MARKER;
|
||||
}
|
||||
|
||||
private static final class NullLiteralNode extends LiteralNode<Object> {
|
||||
|
||||
private NullLiteralNode(final long token, final int finish) {
|
||||
@ -525,11 +552,6 @@ public abstract class LiteralNode<T> extends Node implements PropertyKey {
|
||||
* Array literal node class.
|
||||
*/
|
||||
public static final class ArrayLiteralNode extends LiteralNode<Node[]> {
|
||||
private static class PostsetMarker {
|
||||
//empty
|
||||
}
|
||||
|
||||
private static PostsetMarker POSTSET_MARKER = new PostsetMarker();
|
||||
|
||||
/** Array element type. */
|
||||
private Type elementType;
|
||||
@ -740,24 +762,6 @@ public abstract class LiteralNode<T> extends Node implements PropertyKey {
|
||||
}
|
||||
}
|
||||
|
||||
private Object objectAsConstant(final Object object) {
|
||||
if (object == null) {
|
||||
return null;
|
||||
} else if (object instanceof Number || object instanceof String || object instanceof Boolean) {
|
||||
return object;
|
||||
} else if (object instanceof LiteralNode) {
|
||||
return objectAsConstant(((LiteralNode<?>)object).getValue());
|
||||
} else if (object instanceof UnaryNode) {
|
||||
final UnaryNode unaryNode = (UnaryNode)object;
|
||||
|
||||
if (unaryNode.isTokenType(TokenType.CONVERT) && unaryNode.getType().isObject()) {
|
||||
return objectAsConstant(unaryNode.rhs());
|
||||
}
|
||||
}
|
||||
|
||||
return POSTSET_MARKER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node[] getArray() {
|
||||
return value;
|
||||
|
||||
@ -514,7 +514,7 @@ public final class JSONWriter extends NodeVisitor<LexicalContext> {
|
||||
type("ArrayExpression");
|
||||
comma();
|
||||
|
||||
final Node[] value = (Node[])literalNode.getValue();
|
||||
final Node[] value = literalNode.getArray();
|
||||
array("elements", Arrays.asList(value));
|
||||
} else {
|
||||
type("Literal");
|
||||
|
||||
@ -64,7 +64,7 @@ public final class NativeArguments extends ScriptObject {
|
||||
private static final PropertyMap map$;
|
||||
|
||||
static {
|
||||
PropertyMap map = PropertyMap.newMap(NativeArguments.class);
|
||||
PropertyMap map = PropertyMap.newMap();
|
||||
map = Lookup.newProperty(map, "length", Property.NOT_ENUMERABLE, G$LENGTH, S$LENGTH);
|
||||
map = Lookup.newProperty(map, "callee", Property.NOT_ENUMERABLE, G$CALLEE, S$CALLEE);
|
||||
map$ = map;
|
||||
|
||||
@ -54,7 +54,7 @@ public final class NativeStrictArguments extends ScriptObject {
|
||||
private static final PropertyMap map$;
|
||||
|
||||
static {
|
||||
PropertyMap map = PropertyMap.newMap(NativeStrictArguments.class);
|
||||
PropertyMap map = PropertyMap.newMap();
|
||||
map = Lookup.newProperty(map, "length", Property.NOT_ENUMERABLE, G$LENGTH, S$LENGTH);
|
||||
// In strict mode, the caller and callee properties should throw TypeError
|
||||
// Need to add properties directly to map since slots are assigned speculatively by newUserAccessors.
|
||||
|
||||
@ -52,7 +52,7 @@ public class PrototypeObject extends ScriptObject {
|
||||
private static final MethodHandle SET_CONSTRUCTOR = findOwnMH("setConstructor", void.class, Object.class, Object.class);
|
||||
|
||||
static {
|
||||
PropertyMap map = PropertyMap.newMap(PrototypeObject.class);
|
||||
PropertyMap map = PropertyMap.newMap();
|
||||
map = Lookup.newProperty(map, "constructor", Property.NOT_ENUMERABLE, GET_CONSTRUCTOR, SET_CONSTRUCTOR);
|
||||
map$ = map;
|
||||
}
|
||||
|
||||
@ -149,7 +149,7 @@ public class ScriptFunctionImpl extends ScriptFunction {
|
||||
}
|
||||
|
||||
static {
|
||||
PropertyMap map = PropertyMap.newMap(ScriptFunctionImpl.class);
|
||||
PropertyMap map = PropertyMap.newMap();
|
||||
map = Lookup.newProperty(map, "prototype", Property.NOT_ENUMERABLE | Property.NOT_CONFIGURABLE, G$PROTOTYPE, S$PROTOTYPE);
|
||||
map = Lookup.newProperty(map, "length", Property.NOT_ENUMERABLE | Property.NOT_CONFIGURABLE | Property.NOT_WRITABLE, G$LENGTH, null);
|
||||
map = Lookup.newProperty(map, "name", Property.NOT_ENUMERABLE | Property.NOT_CONFIGURABLE | Property.NOT_WRITABLE, G$NAME, null);
|
||||
@ -201,7 +201,7 @@ public class ScriptFunctionImpl extends ScriptFunction {
|
||||
// Instance of this class is used as global anonymous function which
|
||||
// serves as Function.prototype object.
|
||||
private static class AnonymousFunction extends ScriptFunctionImpl {
|
||||
private static final PropertyMap nasgenmap$$ = PropertyMap.newMap(AnonymousFunction.class);
|
||||
private static final PropertyMap nasgenmap$$ = PropertyMap.newMap();
|
||||
|
||||
AnonymousFunction() {
|
||||
super("", GlobalFunctions.ANONYMOUS, nasgenmap$$, null);
|
||||
|
||||
@ -2009,7 +2009,7 @@ loop:
|
||||
}
|
||||
|
||||
if (!redefinitionOk) {
|
||||
throw error(AbstractParser.message("property.redefinition", key.toString()), property.getToken());
|
||||
throw error(AbstractParser.message("property.redefinition", key), property.getToken());
|
||||
}
|
||||
|
||||
PropertyNode newProperty = existingProperty;
|
||||
|
||||
@ -140,8 +140,8 @@ public class AccessorProperty extends Property {
|
||||
|
||||
this.primitiveGetter = bindTo(property.primitiveGetter, delegate);
|
||||
this.primitiveSetter = bindTo(property.primitiveSetter, delegate);
|
||||
this.objectGetter = bindTo(property.objectGetter, delegate);
|
||||
this.objectSetter = bindTo(property.objectSetter, delegate);
|
||||
this.objectGetter = bindTo(property.ensureObjectGetter(), delegate);
|
||||
this.objectSetter = bindTo(property.ensureObjectSetter(), delegate);
|
||||
|
||||
setCurrentType(property.getCurrentType());
|
||||
}
|
||||
@ -331,12 +331,26 @@ public class AccessorProperty extends Property {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodHandle getGetter(final Class<?> type) {
|
||||
// Spill getters and setters are lazily initialized, see JDK-8011630
|
||||
private MethodHandle ensureObjectGetter() {
|
||||
if (isSpill() && objectGetter == null) {
|
||||
objectGetter = getSpillGetter();
|
||||
}
|
||||
return objectGetter;
|
||||
}
|
||||
|
||||
private MethodHandle ensureObjectSetter() {
|
||||
if (isSpill() && objectSetter == null) {
|
||||
objectSetter = getSpillSetter();
|
||||
}
|
||||
return objectSetter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodHandle getGetter(final Class<?> type) {
|
||||
final int i = getAccessorTypeIndex(type);
|
||||
ensureObjectGetter();
|
||||
|
||||
if (getters[i] == null) {
|
||||
getters[i] = debug(
|
||||
createGetter(currentType, type, primitiveGetter, objectGetter),
|
||||
@ -372,9 +386,7 @@ public class AccessorProperty extends Property {
|
||||
}
|
||||
|
||||
private MethodHandle generateSetter(final Class<?> forType, final Class<?> type) {
|
||||
if (isSpill() && objectSetter == null) {
|
||||
objectSetter = getSpillSetter();
|
||||
}
|
||||
ensureObjectSetter();
|
||||
MethodHandle mh = createSetter(forType, type, primitiveSetter, objectSetter);
|
||||
mh = debug(mh, currentType, type, "set");
|
||||
return mh;
|
||||
|
||||
@ -91,14 +91,16 @@ public final class PropertyMap implements Iterable<Object>, PropertyListener {
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param properties A {@link PropertyHashMap} with initial contents.
|
||||
* @param fieldCount Number of fields in use.
|
||||
* @param properties A {@link PropertyHashMap} with initial contents.
|
||||
* @param fieldCount Number of fields in use.
|
||||
* @param fieldMaximum Number of fields available.
|
||||
* @param spillLength Number of spill slots used.
|
||||
*/
|
||||
private PropertyMap(final PropertyHashMap properties, final int fieldCount, final int fieldMaximum) {
|
||||
private PropertyMap(final PropertyHashMap properties, final int fieldCount, final int fieldMaximum, final int spillLength) {
|
||||
this.properties = properties;
|
||||
this.fieldCount = fieldCount;
|
||||
this.fieldMaximum = fieldMaximum;
|
||||
this.spillLength = spillLength;
|
||||
|
||||
if (Context.DEBUG) {
|
||||
count++;
|
||||
@ -111,7 +113,7 @@ public final class PropertyMap implements Iterable<Object>, PropertyListener {
|
||||
* @param properties A {@link PropertyHashMap} with initial contents.
|
||||
*/
|
||||
private PropertyMap(final PropertyHashMap properties) {
|
||||
this(properties, 0, 0);
|
||||
this(properties, 0, 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -159,42 +161,23 @@ public final class PropertyMap implements Iterable<Object>, PropertyListener {
|
||||
/**
|
||||
* Public property map allocator.
|
||||
*
|
||||
* @param structure Class the map's {@link AccessorProperty}s apply to.
|
||||
* @param properties Collection of initial properties.
|
||||
* @param fieldCount Number of fields in use.
|
||||
* @param properties Collection of initial properties.
|
||||
* @param fieldCount Number of fields in use.
|
||||
* @param fieldMaximum Number of fields available.
|
||||
*
|
||||
* @param spillLength Number of used spill slots.
|
||||
* @return New {@link PropertyMap}.
|
||||
*/
|
||||
public static PropertyMap newMap(final Class<?> structure, final Collection<Property> properties, final int fieldCount, final int fieldMaximum) {
|
||||
// Reduce the number of empty maps in the context.
|
||||
if (structure == JO.class) {
|
||||
return EMPTY_MAP;
|
||||
}
|
||||
|
||||
public static PropertyMap newMap(final Collection<Property> properties, final int fieldCount, final int fieldMaximum, final int spillLength) {
|
||||
PropertyHashMap newProperties = EMPTY_HASHMAP.immutableAdd(properties);
|
||||
|
||||
return new PropertyMap(newProperties, fieldCount, fieldMaximum);
|
||||
}
|
||||
|
||||
/**
|
||||
* Public property map factory allocator
|
||||
*
|
||||
* @param structure Class the map's {@link AccessorProperty}s apply to.
|
||||
*
|
||||
* @return New {@link PropertyMap}.
|
||||
*/
|
||||
public static PropertyMap newMap(final Class<?> structure) {
|
||||
return newMap(structure, null, 0, 0);
|
||||
return new PropertyMap(newProperties, fieldCount, fieldMaximum, spillLength);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a sharable empty map.
|
||||
*
|
||||
* @param context the context
|
||||
* @return New empty {@link PropertyMap}.
|
||||
*/
|
||||
public static PropertyMap newEmptyMap(final Context context) {
|
||||
public static PropertyMap newMap() {
|
||||
return new PropertyMap(EMPTY_HASHMAP);
|
||||
}
|
||||
|
||||
|
||||
@ -170,7 +170,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
|
||||
}
|
||||
|
||||
this.arrayData = ArrayData.EMPTY_ARRAY;
|
||||
this.setMap(map == null ? PropertyMap.newMap(getClass()) : map);
|
||||
this.setMap(map == null ? PropertyMap.newMap() : map);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -188,7 +188,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
|
||||
}
|
||||
|
||||
this.arrayData = ArrayData.EMPTY_ARRAY;
|
||||
this.setMap(map == null ? PropertyMap.newMap(getClass()) : map);
|
||||
this.setMap(map == null ? PropertyMap.newMap() : map);
|
||||
this.proto = proto;
|
||||
|
||||
if (proto != null) {
|
||||
|
||||
@ -33,7 +33,7 @@ import jdk.nashorn.internal.runtime.ScriptObject;
|
||||
*/
|
||||
public class JO extends ScriptObject {
|
||||
|
||||
private static final PropertyMap map$ = PropertyMap.newMap(JO.class);
|
||||
private static final PropertyMap map$ = PropertyMap.newMap();
|
||||
|
||||
/**
|
||||
* Returns the initial property map to be used.
|
||||
|
||||
17625
nashorn/test/script/basic/JDK-8017084.js
Normal file
17625
nashorn/test/script/basic/JDK-8017084.js
Normal file
File diff suppressed because it is too large
Load Diff
13
nashorn/test/script/basic/JDK-8017084.js.EXPECTED
Normal file
13
nashorn/test/script/basic/JDK-8017084.js.EXPECTED
Normal file
@ -0,0 +1,13 @@
|
||||
703
|
||||
5624
|
||||
17575
|
||||
111
|
||||
getting X
|
||||
X
|
||||
setting X
|
||||
17577
|
||||
111
|
||||
aaa
|
||||
hhh
|
||||
yyy
|
||||
X
|
||||
Loading…
x
Reference in New Issue
Block a user