提交 86e077f0 编写于 作者: H hannesw

8067215: Disable dual fields when not using optimistic types

Reviewed-by: attila, lagergren
上级 ffaedbb3
...@@ -124,8 +124,6 @@ public class PrototypeGenerator extends ClassGenerator { ...@@ -124,8 +124,6 @@ public class PrototypeGenerator extends ClassGenerator {
if (memberCount > 0) { if (memberCount > 0) {
// call "super(map$)" // call "super(map$)"
mi.getStatic(className, PROPERTYMAP_FIELD_NAME, PROPERTYMAP_DESC); mi.getStatic(className, PROPERTYMAP_FIELD_NAME, PROPERTYMAP_DESC);
// make sure we use duplicated PropertyMap so that original map
// stays intact and so can be used for many global.
mi.invokeSpecial(PROTOTYPEOBJECT_TYPE, INIT, SCRIPTOBJECT_INIT_DESC); mi.invokeSpecial(PROTOTYPEOBJECT_TYPE, INIT, SCRIPTOBJECT_INIT_DESC);
// initialize Function type fields // initialize Function type fields
initFunctionFields(mi); initFunctionFields(mi);
......
...@@ -63,16 +63,19 @@ SYSTEM PROPERTY: -Dnashorn.codegen.debug.trace=<x> ...@@ -63,16 +63,19 @@ SYSTEM PROPERTY: -Dnashorn.codegen.debug.trace=<x>
See the description of the codegen logger below. See the description of the codegen logger below.
SYSTEM PROPERTY: -Dnashorn.fields.objects SYSTEM PROPERTY: -Dnashorn.fields.objects, -Dnashorn.fields.dual
When this property is true, Nashorn will only use object fields for When the nashorn.fields.objects property is true, Nashorn will always
AccessorProperties. This means that primitive values must be boxed use object fields for AccessorProperties, requiring boxing for all
when stored in a field, which is significantly slower than using primitive property values. When nashorn.fields.dual is set, Nashorn
primitive fields. will always use dual long/object fields, which allows primitives to be
stored without boxing. When neither system property is set, Nashorn
By default, Nashorn uses dual object and long fields. Ints are chooses a setting depending on the optimistic types setting (dual
represented as the 32 low bits of the long fields. Doubles are fields when optimistic types are enabled, object-only fields otherwise).
represented as the doubleToLongBits of their value. This way a
With dual fields, Nashorn uses long fields to store primitive values.
Ints are represented as the 32 low bits of the long fields. Doubles
are represented as the doubleToLongBits of their value. This way a
single field can be used for all primitive types. Packing and single field can be used for all primitive types. Packing and
unpacking doubles to their bit representation is intrinsified by unpacking doubles to their bit representation is intrinsified by
the JVM and extremely fast. the JVM and extremely fast.
......
...@@ -43,7 +43,6 @@ import static jdk.nashorn.internal.codegen.CompilerConstants.methodDescriptor; ...@@ -43,7 +43,6 @@ import static jdk.nashorn.internal.codegen.CompilerConstants.methodDescriptor;
import static jdk.nashorn.internal.codegen.CompilerConstants.staticCallNoLookup; import static jdk.nashorn.internal.codegen.CompilerConstants.staticCallNoLookup;
import static jdk.nashorn.internal.codegen.CompilerConstants.typeDescriptor; import static jdk.nashorn.internal.codegen.CompilerConstants.typeDescriptor;
import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup; import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup;
import static jdk.nashorn.internal.codegen.ObjectClassGenerator.OBJECT_FIELDS_ONLY;
import static jdk.nashorn.internal.ir.Symbol.HAS_SLOT; import static jdk.nashorn.internal.ir.Symbol.HAS_SLOT;
import static jdk.nashorn.internal.ir.Symbol.IS_INTERNAL; import static jdk.nashorn.internal.ir.Symbol.IS_INTERNAL;
import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT; import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
...@@ -305,6 +304,14 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex ...@@ -305,6 +304,14 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
return evalCode; return evalCode;
} }
/**
* Are we using dual primitive/object field representation?
* @return true if using dual field representation, false for object-only fields
*/
boolean useDualFields() {
return compiler.getContext().useDualFields();
}
/** /**
* Load an identity node * Load an identity node
* *
...@@ -1896,10 +1903,10 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex ...@@ -1896,10 +1903,10 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
//this symbol will be put fielded, we can't initialize it as undefined with a known type //this symbol will be put fielded, we can't initialize it as undefined with a known type
@Override @Override
public Class<?> getValueType() { public Class<?> getValueType() {
if (OBJECT_FIELDS_ONLY || value == null || paramType == null) { if (!useDualFields() || value == null || paramType == null || paramType.isBoolean()) {
return Object.class; return Object.class;
} }
return paramType.isBoolean() ? Object.class : paramType.getTypeClass(); return paramType.getTypeClass();
} }
}); });
} }
...@@ -2555,7 +2562,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex ...@@ -2555,7 +2562,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
//for literals, a value of null means object type, i.e. the value null or getter setter function //for literals, a value of null means object type, i.e. the value null or getter setter function
//(I think) //(I think)
final Class<?> valueType = (OBJECT_FIELDS_ONLY || value == null || value.getType().isBoolean()) ? Object.class : value.getType().getTypeClass(); final Class<?> valueType = (!useDualFields() || value == null || value.getType().isBoolean()) ? Object.class : value.getType().getTypeClass();
tuples.add(new MapTuple<Expression>(key, symbol, Type.typeFor(valueType), value) { tuples.add(new MapTuple<Expression>(key, symbol, Type.typeFor(valueType), value) {
@Override @Override
public Class<?> getValueType() { public Class<?> getValueType() {
......
...@@ -149,8 +149,11 @@ public enum CompilerConstants { ...@@ -149,8 +149,11 @@ public enum CompilerConstants {
/** Arguments parameter in scope object constructors; in slot 3 when present */ /** Arguments parameter in scope object constructors; in slot 3 when present */
INIT_ARGUMENTS(null, 3), INIT_ARGUMENTS(null, 3),
/** prefix for all ScriptObject subclasses with fields, @see ObjectGenerator */ /** prefix for all ScriptObject subclasses with dual object/primitive fields, see {@link ObjectClassGenerator} */
JS_OBJECT_PREFIX("JO"), JS_OBJECT_DUAL_FIELD_PREFIX("JD"),
/** prefix for all ScriptObject subclasses with object fields only, see {@link ObjectClassGenerator} */
JS_OBJECT_SINGLE_FIELD_PREFIX("JO"),
/** name for allocate method in JO objects */ /** name for allocate method in JO objects */
ALLOCATE("allocate"), ALLOCATE("allocate"),
......
...@@ -151,7 +151,7 @@ public abstract class FieldObjectCreator<T> extends ObjectCreator<T> { ...@@ -151,7 +151,7 @@ public abstract class FieldObjectCreator<T> extends ObjectCreator<T> {
@Override @Override
protected PropertyMap makeMap() { protected PropertyMap makeMap() {
assert propertyMap == null : "property map already initialized"; assert propertyMap == null : "property map already initialized";
propertyMap = newMapCreator(fieldObjectClass).makeFieldMap(hasArguments(), fieldCount, paddedFieldCount, evalCode); propertyMap = newMapCreator(fieldObjectClass).makeFieldMap(hasArguments(), codegen.useDualFields(), fieldCount, paddedFieldCount, evalCode);
return propertyMap; return propertyMap;
} }
...@@ -166,7 +166,7 @@ public abstract class FieldObjectCreator<T> extends ObjectCreator<T> { ...@@ -166,7 +166,7 @@ public abstract class FieldObjectCreator<T> extends ObjectCreator<T> {
private void putField(final MethodEmitter method, final String key, final int fieldIndex, final MapTuple<T> tuple) { private void putField(final MethodEmitter method, final String key, final int fieldIndex, final MapTuple<T> tuple) {
method.dup(); method.dup();
final Type fieldType = tuple.isPrimitive() ? PRIMITIVE_FIELD_TYPE : Type.OBJECT; final Type fieldType = codegen.useDualFields() && tuple.isPrimitive() ? PRIMITIVE_FIELD_TYPE : Type.OBJECT;
final String fieldClass = getClassName(); final String fieldClass = getClassName();
final String fieldName = getFieldName(fieldIndex, fieldType); final String fieldName = getFieldName(fieldIndex, fieldType);
final String fieldDesc = typeDescriptor(fieldType.getTypeClass()); final String fieldDesc = typeDescriptor(fieldType.getTypeClass());
...@@ -202,8 +202,8 @@ public abstract class FieldObjectCreator<T> extends ObjectCreator<T> { ...@@ -202,8 +202,8 @@ public abstract class FieldObjectCreator<T> extends ObjectCreator<T> {
*/ */
private void findClass() { private void findClass() {
fieldObjectClassName = isScope() ? fieldObjectClassName = isScope() ?
ObjectClassGenerator.getClassName(fieldCount, paramCount) : ObjectClassGenerator.getClassName(fieldCount, paramCount, codegen.useDualFields()) :
ObjectClassGenerator.getClassName(paddedFieldCount); ObjectClassGenerator.getClassName(paddedFieldCount, codegen.useDualFields());
try { try {
this.fieldObjectClass = Context.forStructureClass(Compiler.binaryName(fieldObjectClassName)); this.fieldObjectClass = Context.forStructureClass(Compiler.binaryName(fieldObjectClassName));
......
...@@ -207,7 +207,7 @@ final class FindScopeDepths extends NodeVisitor<LexicalContext> implements Logga ...@@ -207,7 +207,7 @@ final class FindScopeDepths extends NodeVisitor<LexicalContext> implements Logga
final RecompilableScriptFunctionData data = new RecompilableScriptFunctionData( final RecompilableScriptFunctionData data = new RecompilableScriptFunctionData(
newFunctionNode, newFunctionNode,
compiler.getCodeInstaller(), compiler.getCodeInstaller(),
ObjectClassGenerator.createAllocationStrategy(newFunctionNode.getThisProperties()), ObjectClassGenerator.createAllocationStrategy(newFunctionNode.getThisProperties(), compiler.getContext().useDualFields()),
nestedFunctions, nestedFunctions,
externalSymbolDepths.get(fnId), externalSymbolDepths.get(fnId),
internalSymbols.get(fnId), internalSymbols.get(fnId),
......
...@@ -68,17 +68,17 @@ public class MapCreator<T> { ...@@ -68,17 +68,17 @@ public class MapCreator<T> {
* @param evalCode is this property map created for 'eval' code? * @param evalCode is this property map created for 'eval' code?
* @return New map populated with accessor properties. * @return New map populated with accessor properties.
*/ */
PropertyMap makeFieldMap(final boolean hasArguments, final int fieldCount, final int fieldMaximum, final boolean evalCode) { PropertyMap makeFieldMap(final boolean hasArguments, final boolean dualFields, final int fieldCount, final int fieldMaximum, final boolean evalCode) {
final List<Property> properties = new ArrayList<>(); final List<Property> properties = new ArrayList<>();
assert tuples != null; assert tuples != null;
for (final MapTuple<T> tuple : tuples) { for (final MapTuple<T> tuple : tuples) {
final String key = tuple.key; final String key = tuple.key;
final Symbol symbol = tuple.symbol; final Symbol symbol = tuple.symbol;
final Class<?> initialType = tuple.getValueType(); final Class<?> initialType = dualFields ? tuple.getValueType() : Object.class;
if (symbol != null && !isValidArrayIndex(getArrayIndex(key))) { if (symbol != null && !isValidArrayIndex(getArrayIndex(key))) {
final int flags = getPropertyFlags(symbol, hasArguments, evalCode); final int flags = getPropertyFlags(symbol, hasArguments, evalCode, dualFields);
final Property property = new AccessorProperty( final Property property = new AccessorProperty(
key, key,
flags, flags,
...@@ -92,7 +92,7 @@ public class MapCreator<T> { ...@@ -92,7 +92,7 @@ public class MapCreator<T> {
return PropertyMap.newMap(properties, structure.getName(), fieldCount, fieldMaximum, 0); return PropertyMap.newMap(properties, structure.getName(), fieldCount, fieldMaximum, 0);
} }
PropertyMap makeSpillMap(final boolean hasArguments) { PropertyMap makeSpillMap(final boolean hasArguments, final boolean dualFields) {
final List<Property> properties = new ArrayList<>(); final List<Property> properties = new ArrayList<>();
int spillIndex = 0; int spillIndex = 0;
assert tuples != null; assert tuples != null;
...@@ -100,10 +100,10 @@ public class MapCreator<T> { ...@@ -100,10 +100,10 @@ public class MapCreator<T> {
for (final MapTuple<T> tuple : tuples) { for (final MapTuple<T> tuple : tuples) {
final String key = tuple.key; final String key = tuple.key;
final Symbol symbol = tuple.symbol; final Symbol symbol = tuple.symbol;
final Class<?> initialType = tuple.getValueType(); final Class<?> initialType = dualFields ? tuple.getValueType() : Object.class;
if (symbol != null && !isValidArrayIndex(getArrayIndex(key))) { if (symbol != null && !isValidArrayIndex(getArrayIndex(key))) {
final int flags = getPropertyFlags(symbol, hasArguments, false); final int flags = getPropertyFlags(symbol, hasArguments, false, dualFields);
properties.add( properties.add(
new SpillProperty( new SpillProperty(
key, key,
...@@ -124,7 +124,7 @@ public class MapCreator<T> { ...@@ -124,7 +124,7 @@ public class MapCreator<T> {
* *
* @return flags to use for fields * @return flags to use for fields
*/ */
static int getPropertyFlags(final Symbol symbol, final boolean hasArguments, final boolean evalCode) { static int getPropertyFlags(final Symbol symbol, final boolean hasArguments, final boolean evalCode, final boolean dualFields) {
int flags = 0; int flags = 0;
if (symbol.isParam()) { if (symbol.isParam()) {
...@@ -162,6 +162,10 @@ public class MapCreator<T> { ...@@ -162,6 +162,10 @@ public class MapCreator<T> {
flags |= Property.NEEDS_DECLARATION; flags |= Property.NEEDS_DECLARATION;
} }
if (dualFields) {
flags |= Property.DUAL_FIELDS;
}
return flags; return flags;
} }
} }
...@@ -25,8 +25,6 @@ ...@@ -25,8 +25,6 @@
package jdk.nashorn.internal.codegen; package jdk.nashorn.internal.codegen;
import static jdk.nashorn.internal.codegen.ObjectClassGenerator.OBJECT_FIELDS_ONLY;
import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.Symbol; import jdk.nashorn.internal.ir.Symbol;
...@@ -52,11 +50,11 @@ class MapTuple<T> { ...@@ -52,11 +50,11 @@ class MapTuple<T> {
} }
public Class<?> getValueType() { public Class<?> getValueType() {
return OBJECT_FIELDS_ONLY ? Object.class : null; //until proven otherwise we are undefined, see NASHORN-592 int.class; return null; //until proven otherwise we are undefined, see NASHORN-592 int.class;
} }
boolean isPrimitive() { boolean isPrimitive() {
return !OBJECT_FIELDS_ONLY && getValueType().isPrimitive() && getValueType() != boolean.class; return getValueType() != null && getValueType().isPrimitive() && getValueType() != boolean.class;
} }
@Override @Override
......
...@@ -31,7 +31,8 @@ import static jdk.nashorn.internal.codegen.CompilerConstants.INIT_ARGUMENTS; ...@@ -31,7 +31,8 @@ import static jdk.nashorn.internal.codegen.CompilerConstants.INIT_ARGUMENTS;
import static jdk.nashorn.internal.codegen.CompilerConstants.INIT_MAP; import static jdk.nashorn.internal.codegen.CompilerConstants.INIT_MAP;
import static jdk.nashorn.internal.codegen.CompilerConstants.INIT_SCOPE; import static jdk.nashorn.internal.codegen.CompilerConstants.INIT_SCOPE;
import static jdk.nashorn.internal.codegen.CompilerConstants.JAVA_THIS; import static jdk.nashorn.internal.codegen.CompilerConstants.JAVA_THIS;
import static jdk.nashorn.internal.codegen.CompilerConstants.JS_OBJECT_PREFIX; import static jdk.nashorn.internal.codegen.CompilerConstants.JS_OBJECT_DUAL_FIELD_PREFIX;
import static jdk.nashorn.internal.codegen.CompilerConstants.JS_OBJECT_SINGLE_FIELD_PREFIX;
import static jdk.nashorn.internal.codegen.CompilerConstants.className; import static jdk.nashorn.internal.codegen.CompilerConstants.className;
import static jdk.nashorn.internal.codegen.CompilerConstants.constructorNoLookup; import static jdk.nashorn.internal.codegen.CompilerConstants.constructorNoLookup;
import static jdk.nashorn.internal.lookup.Lookup.MH; import static jdk.nashorn.internal.lookup.Lookup.MH;
...@@ -99,18 +100,10 @@ public final class ObjectClassGenerator implements Loggable { ...@@ -99,18 +100,10 @@ public final class ObjectClassGenerator implements Loggable {
*/ */
private final DebugLogger log; private final DebugLogger log;
/** /** Field types for object-only fields */
* Should the runtime only use java.lang.Object slots for fields? If this is false, the representation private static final Type[] FIELD_TYPES_OBJECT = new Type[] { Type.OBJECT };
* will be a primitive 64-bit long value used for all primitives and a java.lang.Object for references. /** Field types for dual primitive/object fields */
* This introduces a larger number of method handles in the system, as we need to have different getters private static final Type[] FIELD_TYPES_DUAL = new Type[] { Type.LONG, Type.OBJECT };
* and setters for the different fields.
*
* This is engineered to plug into the TaggedArray implementation, when it's done.
*/
public static final boolean OBJECT_FIELDS_ONLY = Options.getBooleanProperty("nashorn.fields.objects");
/** The field types in the system */
private static final List<Type> FIELD_TYPES = new LinkedList<>();
/** What type is the primitive type in dual representation */ /** What type is the primitive type in dual representation */
public static final Type PRIMITIVE_FIELD_TYPE = Type.LONG; public static final Type PRIMITIVE_FIELD_TYPE = Type.LONG;
...@@ -118,33 +111,27 @@ public final class ObjectClassGenerator implements Loggable { ...@@ -118,33 +111,27 @@ public final class ObjectClassGenerator implements Loggable {
private static final MethodHandle GET_DIFFERENT = findOwnMH("getDifferent", Object.class, Object.class, Class.class, MethodHandle.class, MethodHandle.class, int.class); private static final MethodHandle GET_DIFFERENT = findOwnMH("getDifferent", Object.class, Object.class, Class.class, MethodHandle.class, MethodHandle.class, int.class);
private static final MethodHandle GET_DIFFERENT_UNDEFINED = findOwnMH("getDifferentUndefined", Object.class, int.class); private static final MethodHandle GET_DIFFERENT_UNDEFINED = findOwnMH("getDifferentUndefined", Object.class, int.class);
/**
* The list of field types that we support - one type creates one field. This is currently either
* LONG + OBJECT or just OBJECT for classic mode.
*/
static {
if (!OBJECT_FIELDS_ONLY) {
FIELD_TYPES.add(PRIMITIVE_FIELD_TYPE);
}
FIELD_TYPES.add(Type.OBJECT);
}
private static boolean initialized = false; private static boolean initialized = false;
/** The context */ /** The context */
private final Context context; private final Context context;
private final boolean dualFields;
/** /**
* Constructor * Constructor
* *
* @param context a context * @param context a context
* @param dualFields whether to use dual fields representation
*/ */
public ObjectClassGenerator(final Context context) { public ObjectClassGenerator(final Context context, final boolean dualFields) {
this.context = context; this.context = context;
this.dualFields = dualFields;
assert context != null; assert context != null;
this.log = initLogger(context); this.log = initLogger(context);
if (!initialized) { if (!initialized) {
initialized = true; initialized = true;
if (OBJECT_FIELDS_ONLY) { if (!dualFields) {
log.warning("Running with object fields only - this is a deprecated configuration."); log.warning("Running with object fields only - this is a deprecated configuration.");
} }
} }
...@@ -176,16 +163,30 @@ public final class ObjectClassGenerator implements Loggable { ...@@ -176,16 +163,30 @@ public final class ObjectClassGenerator implements Loggable {
throw new AssertionError("cannot pack" + n); throw new AssertionError("cannot pack" + n);
} }
private static String getPrefixName(final boolean dualFields) {
return dualFields ? JS_OBJECT_DUAL_FIELD_PREFIX.symbolName() : JS_OBJECT_SINGLE_FIELD_PREFIX.symbolName();
}
private static String getPrefixName(final String className) {
if (className.startsWith(JS_OBJECT_DUAL_FIELD_PREFIX.symbolName())) {
return getPrefixName(true);
} else if (className.startsWith(JS_OBJECT_SINGLE_FIELD_PREFIX.symbolName())) {
return getPrefixName(false);
}
throw new AssertionError("Not a structure class: " + className);
}
/** /**
* Returns the class name for JavaScript objects with fieldCount fields. * Returns the class name for JavaScript objects with fieldCount fields.
* *
* @param fieldCount Number of fields to allocate. * @param fieldCount Number of fields to allocate.
* * @param dualFields whether to use dual fields representation
* @return The class name. * @return The class name.
*/ */
public static String getClassName(final int fieldCount) { public static String getClassName(final int fieldCount, final boolean dualFields) {
return fieldCount != 0 ? SCRIPTS_PACKAGE + '/' + JS_OBJECT_PREFIX.symbolName() + fieldCount : final String prefix = getPrefixName(dualFields);
SCRIPTS_PACKAGE + '/' + JS_OBJECT_PREFIX.symbolName(); return fieldCount != 0 ? SCRIPTS_PACKAGE + '/' + prefix + fieldCount :
SCRIPTS_PACKAGE + '/' + prefix;
} }
/** /**
...@@ -194,22 +195,23 @@ public final class ObjectClassGenerator implements Loggable { ...@@ -194,22 +195,23 @@ public final class ObjectClassGenerator implements Loggable {
* *
* @param fieldCount Number of fields to allocate. * @param fieldCount Number of fields to allocate.
* @param paramCount Number of parameters to allocate * @param paramCount Number of parameters to allocate
* * @param dualFields whether to use dual fields representation
* @return The class name. * @return The class name.
*/ */
public static String getClassName(final int fieldCount, final int paramCount) { public static String getClassName(final int fieldCount, final int paramCount, final boolean dualFields) {
return SCRIPTS_PACKAGE + '/' + JS_OBJECT_PREFIX.symbolName() + fieldCount + SCOPE_MARKER + paramCount; return SCRIPTS_PACKAGE + '/' + getPrefixName(dualFields) + fieldCount + SCOPE_MARKER + paramCount;
} }
/** /**
* Returns the number of fields in the JavaScript scope class. Its name had to be generated using either * Returns the number of fields in the JavaScript scope class. Its name had to be generated using either
* {@link #getClassName(int)} or {@link #getClassName(int, int)}. * {@link #getClassName(int, boolean)} or {@link #getClassName(int, int, boolean)}.
* @param clazz the JavaScript scope class. * @param clazz the JavaScript scope class.
* @return the number of fields in the scope class. * @return the number of fields in the scope class.
*/ */
public static int getFieldCount(final Class<?> clazz) { public static int getFieldCount(final Class<?> clazz) {
final String name = clazz.getSimpleName(); final String name = clazz.getSimpleName();
final String prefix = JS_OBJECT_PREFIX.symbolName(); final String prefix = getPrefixName(name);
if (prefix.equals(name)) { if (prefix.equals(name)) {
return 0; return 0;
} }
...@@ -238,8 +240,8 @@ public final class ObjectClassGenerator implements Loggable { ...@@ -238,8 +240,8 @@ public final class ObjectClassGenerator implements Loggable {
* @param className name of class * @param className name of class
* @param fieldNames fields to initialize to undefined, where applicable * @param fieldNames fields to initialize to undefined, where applicable
*/ */
private static void initializeToUndefined(final MethodEmitter init, final String className, final List<String> fieldNames) { private void initializeToUndefined(final MethodEmitter init, final String className, final List<String> fieldNames) {
if (!OBJECT_FIELDS_ONLY) { if (dualFields) {
// no need to initialize anything to undefined in the dual field world // no need to initialize anything to undefined in the dual field world
// - then we have a constant getter for undefined for any unknown type // - then we have a constant getter for undefined for any unknown type
return; return;
...@@ -292,7 +294,7 @@ public final class ObjectClassGenerator implements Loggable { ...@@ -292,7 +294,7 @@ public final class ObjectClassGenerator implements Loggable {
* @return Byte codes for generated class. * @return Byte codes for generated class.
*/ */
public byte[] generate(final int fieldCount) { public byte[] generate(final int fieldCount) {
final String className = getClassName(fieldCount); final String className = getClassName(fieldCount, dualFields);
final String superName = className(ScriptObject.class); final String superName = className(ScriptObject.class);
final ClassEmitter classEmitter = newClassEmitter(className, superName); final ClassEmitter classEmitter = newClassEmitter(className, superName);
...@@ -322,7 +324,7 @@ public final class ObjectClassGenerator implements Loggable { ...@@ -322,7 +324,7 @@ public final class ObjectClassGenerator implements Loggable {
* @return Byte codes for generated class. * @return Byte codes for generated class.
*/ */
public byte[] generate(final int fieldCount, final int paramCount) { public byte[] generate(final int fieldCount, final int paramCount) {
final String className = getClassName(fieldCount, paramCount); final String className = getClassName(fieldCount, paramCount, dualFields);
final String superName = className(FunctionScope.class); final String superName = className(FunctionScope.class);
final ClassEmitter classEmitter = newClassEmitter(className, superName); final ClassEmitter classEmitter = newClassEmitter(className, superName);
final List<String> initFields = addFields(classEmitter, fieldCount); final List<String> initFields = addFields(classEmitter, fieldCount);
...@@ -353,11 +355,11 @@ public final class ObjectClassGenerator implements Loggable { ...@@ -353,11 +355,11 @@ public final class ObjectClassGenerator implements Loggable {
* *
* @return List fields that need to be initialized. * @return List fields that need to be initialized.
*/ */
private static List<String> addFields(final ClassEmitter classEmitter, final int fieldCount) { private List<String> addFields(final ClassEmitter classEmitter, final int fieldCount) {
final List<String> initFields = new LinkedList<>(); final List<String> initFields = new LinkedList<>();
final Type[] fieldTypes = dualFields ? FIELD_TYPES_DUAL : FIELD_TYPES_OBJECT;
for (int i = 0; i < fieldCount; i++) { for (int i = 0; i < fieldCount; i++) {
for (final Type type : FIELD_TYPES) { for (final Type type : fieldTypes) {
final String fieldName = getFieldName(i, type); final String fieldName = getFieldName(i, type);
classEmitter.field(fieldName, type.getTypeClass()); classEmitter.field(fieldName, type.getTypeClass());
...@@ -533,13 +535,10 @@ public final class ObjectClassGenerator implements Loggable { ...@@ -533,13 +535,10 @@ public final class ObjectClassGenerator implements Loggable {
private static MethodHandle getterForType(final Class<?> forType, final MethodHandle primitiveGetter, final MethodHandle objectGetter) { private static MethodHandle getterForType(final Class<?> forType, final MethodHandle primitiveGetter, final MethodHandle objectGetter) {
switch (getAccessorTypeIndex(forType)) { switch (getAccessorTypeIndex(forType)) {
case TYPE_INT_INDEX: case TYPE_INT_INDEX:
assert !OBJECT_FIELDS_ONLY : "this can only happen with dual fields";
return MH.explicitCastArguments(primitiveGetter, primitiveGetter.type().changeReturnType(int.class)); return MH.explicitCastArguments(primitiveGetter, primitiveGetter.type().changeReturnType(int.class));
case TYPE_LONG_INDEX: case TYPE_LONG_INDEX:
assert !OBJECT_FIELDS_ONLY : "this can only happen with dual fields";
return primitiveGetter; return primitiveGetter;
case TYPE_DOUBLE_INDEX: case TYPE_DOUBLE_INDEX:
assert !OBJECT_FIELDS_ONLY : "this can only happen with dual fields";
return MH.filterReturnValue(primitiveGetter, UNPACK_DOUBLE); return MH.filterReturnValue(primitiveGetter, UNPACK_DOUBLE);
case TYPE_OBJECT_INDEX: case TYPE_OBJECT_INDEX:
return objectGetter; return objectGetter;
...@@ -557,7 +556,7 @@ public final class ObjectClassGenerator implements Loggable { ...@@ -557,7 +556,7 @@ public final class ObjectClassGenerator implements Loggable {
final boolean isPrimitiveStorage = forType != null && forType.isPrimitive(); final boolean isPrimitiveStorage = forType != null && forType.isPrimitive();
//which is the primordial getter //which is the primordial getter
final MethodHandle getter = OBJECT_FIELDS_ONLY ? objectGetter : isPrimitiveStorage ? primitiveGetter : objectGetter; final MethodHandle getter = primitiveGetter == null ? objectGetter : isPrimitiveStorage ? primitiveGetter : objectGetter;
if (forType == null) { if (forType == null) {
if (isOptimistic) { if (isOptimistic) {
...@@ -580,8 +579,7 @@ public final class ObjectClassGenerator implements Loggable { ...@@ -580,8 +579,7 @@ public final class ObjectClassGenerator implements Loggable {
return MH.dropArguments(GET_UNDEFINED.get(ti), 0, Object.class); return MH.dropArguments(GET_UNDEFINED.get(ti), 0, Object.class);
} }
assert forType != null; assert primitiveGetter != null || forType == Object.class : forType;
assert !OBJECT_FIELDS_ONLY || forType == Object.class : forType;
if (isOptimistic) { if (isOptimistic) {
if (fti < ti) { if (fti < ti) {
...@@ -635,8 +633,6 @@ public final class ObjectClassGenerator implements Loggable { ...@@ -635,8 +633,6 @@ public final class ObjectClassGenerator implements Loggable {
return tgetter; return tgetter;
} }
assert !OBJECT_FIELDS_ONLY;
//final MethodType pmt = primitiveGetter.type();
assert primitiveGetter != null; assert primitiveGetter != null;
final MethodType tgetterType = tgetter.type(); final MethodType tgetterType = tgetter.type();
switch (fti) { switch (fti) {
...@@ -727,7 +723,7 @@ public final class ObjectClassGenerator implements Loggable { ...@@ -727,7 +723,7 @@ public final class ObjectClassGenerator implements Loggable {
final int fti = getAccessorTypeIndex(forType); final int fti = getAccessorTypeIndex(forType);
final int ti = getAccessorTypeIndex(type); final int ti = getAccessorTypeIndex(type);
if (fti == TYPE_OBJECT_INDEX || OBJECT_FIELDS_ONLY) { if (fti == TYPE_OBJECT_INDEX || primitiveSetter == null) {
if (ti == TYPE_OBJECT_INDEX) { if (ti == TYPE_OBJECT_INDEX) {
return objectSetter; return objectSetter;
} }
...@@ -735,8 +731,6 @@ public final class ObjectClassGenerator implements Loggable { ...@@ -735,8 +731,6 @@ public final class ObjectClassGenerator implements Loggable {
return MH.asType(objectSetter, objectSetter.type().changeParameterType(1, type)); return MH.asType(objectSetter, objectSetter.type().changeParameterType(1, type));
} }
assert !OBJECT_FIELDS_ONLY;
final MethodType pmt = primitiveSetter.type(); final MethodType pmt = primitiveSetter.type();
switch (fti) { switch (fti) {
...@@ -832,8 +826,8 @@ public final class ObjectClassGenerator implements Loggable { ...@@ -832,8 +826,8 @@ public final class ObjectClassGenerator implements Loggable {
* @param thisProperties number of properties assigned to "this" * @param thisProperties number of properties assigned to "this"
* @return the allocation strategy * @return the allocation strategy
*/ */
static AllocationStrategy createAllocationStrategy(final int thisProperties) { static AllocationStrategy createAllocationStrategy(final int thisProperties, final boolean dualFields) {
final int paddedFieldCount = getPaddedFieldCount(thisProperties); final int paddedFieldCount = getPaddedFieldCount(thisProperties);
return new AllocationStrategy(paddedFieldCount); return new AllocationStrategy(paddedFieldCount, dualFields);
} }
} }
...@@ -134,7 +134,7 @@ public abstract class ObjectCreator<T> { ...@@ -134,7 +134,7 @@ public abstract class ObjectCreator<T> {
MethodEmitter loadTuple(final MethodEmitter method, final MapTuple<T> tuple, final boolean pack) { MethodEmitter loadTuple(final MethodEmitter method, final MapTuple<T> tuple, final boolean pack) {
loadValue(tuple.value, tuple.type); loadValue(tuple.value, tuple.type);
if (pack && tuple.isPrimitive()) { if (pack && codegen.useDualFields() && tuple.isPrimitive()) {
method.pack(); method.pack();
} else { } else {
method.convert(Type.OBJECT); method.convert(Type.OBJECT);
......
...@@ -27,7 +27,6 @@ package jdk.nashorn.internal.codegen; ...@@ -27,7 +27,6 @@ package jdk.nashorn.internal.codegen;
import static jdk.nashorn.internal.codegen.CompilerConstants.constructorNoLookup; import static jdk.nashorn.internal.codegen.CompilerConstants.constructorNoLookup;
import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup; import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup;
import static jdk.nashorn.internal.codegen.ObjectClassGenerator.OBJECT_FIELDS_ONLY;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
...@@ -42,6 +41,7 @@ import jdk.nashorn.internal.runtime.ScriptObject; ...@@ -42,6 +41,7 @@ import jdk.nashorn.internal.runtime.ScriptObject;
import jdk.nashorn.internal.runtime.ScriptRuntime; import jdk.nashorn.internal.runtime.ScriptRuntime;
import jdk.nashorn.internal.runtime.arrays.ArrayData; import jdk.nashorn.internal.runtime.arrays.ArrayData;
import jdk.nashorn.internal.runtime.arrays.ArrayIndex; import jdk.nashorn.internal.runtime.arrays.ArrayIndex;
import jdk.nashorn.internal.scripts.JD;
import jdk.nashorn.internal.scripts.JO; import jdk.nashorn.internal.scripts.JO;
/** /**
...@@ -65,10 +65,13 @@ public final class SpillObjectCreator extends ObjectCreator<Expression> { ...@@ -65,10 +65,13 @@ public final class SpillObjectCreator extends ObjectCreator<Expression> {
assert !isScope() : "spill scope objects are not currently supported"; assert !isScope() : "spill scope objects are not currently supported";
final int length = tuples.size(); final int length = tuples.size();
final long[] jpresetValues = new long[ScriptObject.spillAllocationLength(length)]; final boolean dualFields = codegen.useDualFields();
final Object[] opresetValues = new Object[ScriptObject.spillAllocationLength(length)]; 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 Set<Integer> postsetValues = new LinkedHashSet<>();
final int callSiteFlags = codegen.getCallSiteFlags(); final int callSiteFlags = codegen.getCallSiteFlags();
final Class<?> objectClass = dualFields ? JD.class : JO.class;
ArrayData arrayData = ArrayData.allocate(ScriptRuntime.EMPTY_ARRAY); ArrayData arrayData = ArrayData.allocate(ScriptRuntime.EMPTY_ARRAY);
// Compute constant property values // Compute constant property values
...@@ -88,9 +91,9 @@ public final class SpillObjectCreator extends ObjectCreator<Expression> { ...@@ -88,9 +91,9 @@ public final class SpillObjectCreator extends ObjectCreator<Expression> {
final Property property = propertyMap.findProperty(key); final Property property = propertyMap.findProperty(key);
if (property != null) { if (property != null) {
// normal property key // normal property key
property.setType(JSType.unboxedFieldType(constantValue)); property.setType(dualFields ? JSType.unboxedFieldType(constantValue) : Object.class);
final int slot = property.getSlot(); final int slot = property.getSlot();
if (!OBJECT_FIELDS_ONLY && constantValue instanceof Number) { if (dualFields && constantValue instanceof Number) {
jpresetValues[slot] = ObjectClassGenerator.pack((Number)constantValue); jpresetValues[slot] = ObjectClassGenerator.pack((Number)constantValue);
} else { } else {
opresetValues[slot] = constantValue; opresetValues[slot] = constantValue;
...@@ -130,10 +133,11 @@ public final class SpillObjectCreator extends ObjectCreator<Expression> { ...@@ -130,10 +133,11 @@ public final class SpillObjectCreator extends ObjectCreator<Expression> {
//assert postsetValues.isEmpty() : "test me " + postsetValues; //assert postsetValues.isEmpty() : "test me " + postsetValues;
// create object and invoke constructor // create object and invoke constructor
method._new(JO.class).dup(); method._new(objectClass).dup();
codegen.loadConstant(propertyMap); codegen.loadConstant(propertyMap);
//load primitive values to j spill array //load primitive values to j spill array
if (dualFields) {
codegen.loadConstant(jpresetValues); codegen.loadConstant(jpresetValues);
for (final int i : postsetValues) { for (final int i : postsetValues) {
final MapTuple<Expression> tuple = tuples.get(i); final MapTuple<Expression> tuple = tuples.get(i);
...@@ -145,13 +149,16 @@ public final class SpillObjectCreator extends ObjectCreator<Expression> { ...@@ -145,13 +149,16 @@ public final class SpillObjectCreator extends ObjectCreator<Expression> {
method.arraystore(); method.arraystore();
} }
} }
} else {
method.loadNull();
}
//load object values to o spill array //load object values to o spill array
codegen.loadConstant(opresetValues); codegen.loadConstant(opresetValues);
for (final int i : postsetValues) { for (final int i : postsetValues) {
final MapTuple<Expression> tuple = tuples.get(i); final MapTuple<Expression> tuple = tuples.get(i);
final Property property = propertyMap.findProperty(tuple.key); final Property property = propertyMap.findProperty(tuple.key);
if (property != null && !tuple.isPrimitive()) { if (property != null && (!dualFields || !tuple.isPrimitive())) {
method.dup(); method.dup();
method.load(property.getSlot()); method.load(property.getSlot());
loadTuple(method, tuple); loadTuple(method, tuple);
...@@ -160,7 +167,7 @@ public final class SpillObjectCreator extends ObjectCreator<Expression> { ...@@ -160,7 +167,7 @@ public final class SpillObjectCreator extends ObjectCreator<Expression> {
} }
//instantiate the script object with spill objects //instantiate the script object with spill objects
method.invoke(constructorNoLookup(JO.class, PropertyMap.class, long[].class, Object[].class)); method.invoke(constructorNoLookup(objectClass, PropertyMap.class, long[].class, Object[].class));
// Set prefix array data if any // Set prefix array data if any
if (arrayData.length() > 0) { if (arrayData.length() > 0) {
...@@ -188,7 +195,9 @@ public final class SpillObjectCreator extends ObjectCreator<Expression> { ...@@ -188,7 +195,9 @@ public final class SpillObjectCreator extends ObjectCreator<Expression> {
@Override @Override
protected PropertyMap makeMap() { protected PropertyMap makeMap() {
assert propertyMap == null : "property map already initialized"; assert propertyMap == null : "property map already initialized";
propertyMap = new MapCreator<>(JO.class, tuples).makeSpillMap(false); final boolean dualFields = codegen.useDualFields();
final Class<? extends ScriptObject> clazz = dualFields ? JD.class : JO.class;
propertyMap = new MapCreator<>(clazz, tuples).makeSpillMap(false, codegen.useDualFields());
return propertyMap; return propertyMap;
} }
......
...@@ -79,6 +79,7 @@ import jdk.nashorn.internal.runtime.linker.Bootstrap; ...@@ -79,6 +79,7 @@ import jdk.nashorn.internal.runtime.linker.Bootstrap;
import jdk.nashorn.internal.runtime.linker.InvokeByName; import jdk.nashorn.internal.runtime.linker.InvokeByName;
import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor; import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;
import jdk.nashorn.internal.runtime.regexp.RegExpResult; import jdk.nashorn.internal.runtime.regexp.RegExpResult;
import jdk.nashorn.internal.scripts.JD;
import jdk.nashorn.internal.scripts.JO; import jdk.nashorn.internal.scripts.JO;
import jdk.nashorn.tools.ShellFunctions; import jdk.nashorn.tools.ShellFunctions;
...@@ -750,6 +751,11 @@ public final class Global extends ScriptObject implements Scope { ...@@ -750,6 +751,11 @@ public final class Global extends ScriptObject implements Scope {
return context; return context;
} }
@Override
protected boolean useDualFields() {
return context.useDualFields();
}
// performs initialization checks for Global constructor and returns the // performs initialization checks for Global constructor and returns the
// PropertyMap, if everything is fine. // PropertyMap, if everything is fine.
private static PropertyMap checkAndGetMap(final Context context) { private static PropertyMap checkAndGetMap(final Context context) {
...@@ -933,7 +939,7 @@ public final class Global extends ScriptObject implements Scope { ...@@ -933,7 +939,7 @@ public final class Global extends ScriptObject implements Scope {
* @return the new ScriptObject * @return the new ScriptObject
*/ */
public ScriptObject newObject() { public ScriptObject newObject() {
return new JO(getObjectPrototype(), JO.getInitialMap()); return useDualFields() ? new JD(getObjectPrototype()) : new JO(getObjectPrototype());
} }
/** /**
...@@ -2744,8 +2750,8 @@ public final class Global extends ScriptObject implements Scope { ...@@ -2744,8 +2750,8 @@ public final class Global extends ScriptObject implements Scope {
*/ */
private static class LexicalScope extends ScriptObject { private static class LexicalScope extends ScriptObject {
LexicalScope(final ScriptObject proto) { LexicalScope(final Global global) {
super(proto, PropertyMap.newMap()); super(global, PropertyMap.newMap());
} }
@Override @Override
......
...@@ -160,7 +160,7 @@ public final class NativeJSAdapter extends ScriptObject { ...@@ -160,7 +160,7 @@ public final class NativeJSAdapter extends ScriptObject {
} }
private static ScriptObject wrapAdaptee(final ScriptObject adaptee) { private static ScriptObject wrapAdaptee(final ScriptObject adaptee) {
return new JO(adaptee, JO.getInitialMap()); return new JO(adaptee);
} }
@Override @Override
......
...@@ -41,6 +41,7 @@ import jdk.nashorn.internal.runtime.Source; ...@@ -41,6 +41,7 @@ import jdk.nashorn.internal.runtime.Source;
import jdk.nashorn.internal.runtime.SpillProperty; import jdk.nashorn.internal.runtime.SpillProperty;
import jdk.nashorn.internal.runtime.arrays.ArrayData; import jdk.nashorn.internal.runtime.arrays.ArrayData;
import jdk.nashorn.internal.runtime.arrays.ArrayIndex; import jdk.nashorn.internal.runtime.arrays.ArrayIndex;
import jdk.nashorn.internal.scripts.JD;
import jdk.nashorn.internal.scripts.JO; import jdk.nashorn.internal.scripts.JO;
import static jdk.nashorn.internal.parser.TokenType.STRING; import static jdk.nashorn.internal.parser.TokenType.STRING;
...@@ -54,11 +55,10 @@ public class JSONParser { ...@@ -54,11 +55,10 @@ public class JSONParser {
final private String source; final private String source;
final private Global global; final private Global global;
final private boolean dualFields;
final int length; final int length;
int pos = 0; int pos = 0;
private static PropertyMap EMPTY_MAP = PropertyMap.newMap();
private static final int EOF = -1; private static final int EOF = -1;
private static final String TRUE = "true"; private static final String TRUE = "true";
...@@ -74,10 +74,11 @@ public class JSONParser { ...@@ -74,10 +74,11 @@ public class JSONParser {
* @param source the source * @param source the source
* @param global the global object * @param global the global object
*/ */
public JSONParser(final String source, final Global global ) { public JSONParser(final String source, final Global global, final boolean dualFields) {
this.source = source; this.source = source;
this.global = global; this.global = global;
this.length = source.length(); this.length = source.length();
this.dualFields = dualFields;
} }
/** /**
...@@ -180,7 +181,7 @@ public class JSONParser { ...@@ -180,7 +181,7 @@ public class JSONParser {
} }
private Object parseObject() { private Object parseObject() {
PropertyMap propertyMap = EMPTY_MAP; PropertyMap propertyMap = dualFields ? JD.getInitialMap() : JO.getInitialMap();
ArrayData arrayData = ArrayData.EMPTY_ARRAY; ArrayData arrayData = ArrayData.EMPTY_ARRAY;
final ArrayList<Object> values = new ArrayList<>(); final ArrayList<Object> values = new ArrayList<>();
int state = STATE_EMPTY; int state = STATE_EMPTY;
...@@ -241,36 +242,45 @@ public class JSONParser { ...@@ -241,36 +242,45 @@ public class JSONParser {
return newArrayData.set(index, value, false); return newArrayData.set(index, value, false);
} }
private static PropertyMap addObjectProperty(final PropertyMap propertyMap, final List<Object> values, private PropertyMap addObjectProperty(final PropertyMap propertyMap, final List<Object> values,
final String id, final Object value) { final String id, final Object value) {
final Property oldProperty = propertyMap.findProperty(id); final Property oldProperty = propertyMap.findProperty(id);
final PropertyMap newMap; final PropertyMap newMap;
final Class<?> type = ObjectClassGenerator.OBJECT_FIELDS_ONLY ? Object.class : getType(value); final Class<?> type;
final int flags;
if (dualFields) {
type = getType(value);
flags = Property.DUAL_FIELDS;
} else {
type = Object.class;
flags = 0;
}
if (oldProperty != null) { if (oldProperty != null) {
values.set(oldProperty.getSlot(), value); values.set(oldProperty.getSlot(), value);
newMap = propertyMap.replaceProperty(oldProperty, new SpillProperty(id, 0, oldProperty.getSlot(), type));; newMap = propertyMap.replaceProperty(oldProperty, new SpillProperty(id, flags, oldProperty.getSlot(), type));;
} else { } else {
values.add(value); values.add(value);
newMap = propertyMap.addProperty(new SpillProperty(id, 0, propertyMap.size(), type)); newMap = propertyMap.addProperty(new SpillProperty(id, flags, propertyMap.size(), type));
} }
return newMap; return newMap;
} }
private Object createObject(final PropertyMap propertyMap, final List<Object> values, final ArrayData arrayData) { private Object createObject(final PropertyMap propertyMap, final List<Object> values, final ArrayData arrayData) {
final long[] primitiveSpill = new long[values.size()]; final long[] primitiveSpill = dualFields ? new long[values.size()] : null;
final Object[] objectSpill = new Object[values.size()]; final Object[] objectSpill = new Object[values.size()];
for (final Property property : propertyMap.getProperties()) { for (final Property property : propertyMap.getProperties()) {
if (property.getType() == Object.class) { if (!dualFields || property.getType() == Object.class) {
objectSpill[property.getSlot()] = values.get(property.getSlot()); objectSpill[property.getSlot()] = values.get(property.getSlot());
} else { } else {
primitiveSpill[property.getSlot()] = ObjectClassGenerator.pack((Number) values.get(property.getSlot())); primitiveSpill[property.getSlot()] = ObjectClassGenerator.pack((Number) values.get(property.getSlot()));
} }
} }
final ScriptObject object = new JO(propertyMap, primitiveSpill, objectSpill); final ScriptObject object = dualFields ?
new JD(propertyMap, primitiveSpill, objectSpill) : new JO(propertyMap, null, objectSpill);
object.setInitialProto(global.getObjectPrototype()); object.setInitialProto(global.getObjectPrototype());
object.setArray(arrayData); object.setArray(arrayData);
return object; return object;
......
...@@ -25,7 +25,6 @@ ...@@ -25,7 +25,6 @@
package jdk.nashorn.internal.runtime; package jdk.nashorn.internal.runtime;
import static jdk.nashorn.internal.codegen.ObjectClassGenerator.OBJECT_FIELDS_ONLY;
import static jdk.nashorn.internal.codegen.ObjectClassGenerator.PRIMITIVE_FIELD_TYPE; import static jdk.nashorn.internal.codegen.ObjectClassGenerator.PRIMITIVE_FIELD_TYPE;
import static jdk.nashorn.internal.codegen.ObjectClassGenerator.createGetter; import static jdk.nashorn.internal.codegen.ObjectClassGenerator.createGetter;
import static jdk.nashorn.internal.codegen.ObjectClassGenerator.createSetter; import static jdk.nashorn.internal.codegen.ObjectClassGenerator.createSetter;
...@@ -98,7 +97,7 @@ public class AccessorProperty extends Property { ...@@ -98,7 +97,7 @@ public class AccessorProperty extends Property {
objectSetters[i] = MH.asType(MH.setter(LOOKUP, structure, fieldName, typeClass), Lookup.SET_OBJECT_TYPE); objectSetters[i] = MH.asType(MH.setter(LOOKUP, structure, fieldName, typeClass), Lookup.SET_OBJECT_TYPE);
} }
if (!OBJECT_FIELDS_ONLY) { if (!StructureLoader.isSingleFieldStructure(structure.getName())) {
for (int i = 0; i < fieldCount; i++) { for (int i = 0; i < fieldCount; i++) {
final String fieldNamePrimitive = getFieldName(i, PRIMITIVE_FIELD_TYPE); final String fieldNamePrimitive = getFieldName(i, PRIMITIVE_FIELD_TYPE);
final Class<?> typeClass = PRIMITIVE_FIELD_TYPE.getTypeClass(); final Class<?> typeClass = PRIMITIVE_FIELD_TYPE.getTypeClass();
...@@ -211,7 +210,7 @@ public class AccessorProperty extends Property { ...@@ -211,7 +210,7 @@ public class AccessorProperty extends Property {
* @param setter the property setter or null if non writable, non configurable * @param setter the property setter or null if non writable, non configurable
*/ */
private AccessorProperty(final String key, final int flags, final int slot, final MethodHandle getter, final MethodHandle setter) { private AccessorProperty(final String key, final int flags, final int slot, final MethodHandle getter, final MethodHandle setter) {
super(key, flags | IS_BUILTIN | (getter.type().returnType().isPrimitive() ? IS_NASGEN_PRIMITIVE : 0), slot); super(key, flags | IS_BUILTIN | DUAL_FIELDS | (getter.type().returnType().isPrimitive() ? IS_NASGEN_PRIMITIVE : 0), slot);
assert !isSpill(); assert !isSpill();
// we don't need to prep the setters these will never be invalidated as this is a nasgen // we don't need to prep the setters these will never be invalidated as this is a nasgen
...@@ -221,9 +220,7 @@ public class AccessorProperty extends Property { ...@@ -221,9 +220,7 @@ public class AccessorProperty extends Property {
final Class<?> setterType = setter == null ? null : setter.type().parameterType(1); final Class<?> setterType = setter == null ? null : setter.type().parameterType(1);
assert setterType == null || setterType == getterType; assert setterType == null || setterType == getterType;
if (OBJECT_FIELDS_ONLY) {
primitiveGetter = primitiveSetter = null;
} else {
if (getterType == int.class || getterType == long.class) { if (getterType == int.class || getterType == long.class) {
primitiveGetter = MH.asType(getter, Lookup.GET_PRIMITIVE_TYPE); primitiveGetter = MH.asType(getter, Lookup.GET_PRIMITIVE_TYPE);
primitiveSetter = setter == null ? null : MH.asType(setter, Lookup.SET_PRIMITIVE_TYPE); primitiveSetter = setter == null ? null : MH.asType(setter, Lookup.SET_PRIMITIVE_TYPE);
...@@ -233,7 +230,6 @@ public class AccessorProperty extends Property { ...@@ -233,7 +230,6 @@ public class AccessorProperty extends Property {
} else { } else {
primitiveGetter = primitiveSetter = null; primitiveGetter = primitiveSetter = null;
} }
}
assert primitiveGetter == null || primitiveGetter.type() == Lookup.GET_PRIMITIVE_TYPE : primitiveGetter + "!=" + Lookup.GET_PRIMITIVE_TYPE; assert primitiveGetter == null || primitiveGetter.type() == Lookup.GET_PRIMITIVE_TYPE : primitiveGetter + "!=" + Lookup.GET_PRIMITIVE_TYPE;
assert primitiveSetter == null || primitiveSetter.type() == Lookup.SET_PRIMITIVE_TYPE : primitiveSetter; assert primitiveSetter == null || primitiveSetter.type() == Lookup.SET_PRIMITIVE_TYPE : primitiveSetter;
...@@ -241,7 +237,7 @@ public class AccessorProperty extends Property { ...@@ -241,7 +237,7 @@ public class AccessorProperty extends Property {
objectGetter = getter.type() != Lookup.GET_OBJECT_TYPE ? MH.asType(getter, Lookup.GET_OBJECT_TYPE) : getter; objectGetter = getter.type() != Lookup.GET_OBJECT_TYPE ? MH.asType(getter, Lookup.GET_OBJECT_TYPE) : getter;
objectSetter = setter != null && setter.type() != Lookup.SET_OBJECT_TYPE ? MH.asType(setter, Lookup.SET_OBJECT_TYPE) : setter; objectSetter = setter != null && setter.type() != Lookup.SET_OBJECT_TYPE ? MH.asType(setter, Lookup.SET_OBJECT_TYPE) : setter;
setType(OBJECT_FIELDS_ONLY ? Object.class : getterType); setType(getterType);
} }
/** /**
...@@ -282,6 +278,9 @@ public class AccessorProperty extends Property { ...@@ -282,6 +278,9 @@ public class AccessorProperty extends Property {
objectSetter = gs.objectSetters[slot]; objectSetter = gs.objectSetters[slot];
primitiveSetter = gs.primitiveSetters[slot]; primitiveSetter = gs.primitiveSetters[slot];
} }
// Always use dual fields except for single field structures
assert hasDualFields() != StructureLoader.isSingleFieldStructure(structure.getName());
} }
/** /**
...@@ -310,7 +309,7 @@ public class AccessorProperty extends Property { ...@@ -310,7 +309,7 @@ public class AccessorProperty extends Property {
*/ */
public AccessorProperty(final String key, final int flags, final Class<?> structure, final int slot, final Class<?> initialType) { public AccessorProperty(final String key, final int flags, final Class<?> structure, final int slot, final Class<?> initialType) {
this(key, flags, structure, slot); this(key, flags, structure, slot);
setType(OBJECT_FIELDS_ONLY ? Object.class : initialType); setType(hasDualFields() ? initialType : Object.class);
} }
/** /**
...@@ -347,7 +346,7 @@ public class AccessorProperty extends Property { ...@@ -347,7 +346,7 @@ public class AccessorProperty extends Property {
* @param initialValue initial value * @param initialValue initial value
*/ */
protected final void setInitialValue(final ScriptObject owner, final Object initialValue) { protected final void setInitialValue(final ScriptObject owner, final Object initialValue) {
setType(JSType.unboxedFieldType(initialValue)); setType(hasDualFields() ? JSType.unboxedFieldType(initialValue) : Object.class);
if (initialValue instanceof Integer) { if (initialValue instanceof Integer) {
invokeSetter(owner, ((Integer)initialValue).intValue()); invokeSetter(owner, ((Integer)initialValue).intValue());
} else if (initialValue instanceof Long) { } else if (initialValue instanceof Long) {
...@@ -363,7 +362,7 @@ public class AccessorProperty extends Property { ...@@ -363,7 +362,7 @@ public class AccessorProperty extends Property {
* Initialize the type of a property * Initialize the type of a property
*/ */
protected final void initializeType() { protected final void initializeType() {
setType(OBJECT_FIELDS_ONLY ? Object.class : null); setType(!hasDualFields() ? Object.class : null);
} }
private void readObject(final ObjectInputStream s) throws IOException, ClassNotFoundException { private void readObject(final ObjectInputStream s) throws IOException, ClassNotFoundException {
...@@ -670,7 +669,7 @@ public class AccessorProperty extends Property { ...@@ -670,7 +669,7 @@ public class AccessorProperty extends Property {
@Override @Override
public final boolean canChangeType() { public final boolean canChangeType() {
if (OBJECT_FIELDS_ONLY) { if (!hasDualFields()) {
return false; return false;
} }
// Return true for currently undefined even if non-writable/configurable to allow initialization of ES6 CONST. // Return true for currently undefined even if non-writable/configurable to allow initialization of ES6 CONST.
......
...@@ -44,6 +44,9 @@ final public class AllocationStrategy implements Serializable { ...@@ -44,6 +44,9 @@ final public class AllocationStrategy implements Serializable {
/** Number of fields in the allocated object */ /** Number of fields in the allocated object */
private final int fieldCount; private final int fieldCount;
/** Whether to use dual field representation */
private final boolean dualFields;
/** Name of class where allocator function resides */ /** Name of class where allocator function resides */
private transient String allocatorClassName; private transient String allocatorClassName;
...@@ -53,15 +56,17 @@ final public class AllocationStrategy implements Serializable { ...@@ -53,15 +56,17 @@ final public class AllocationStrategy implements Serializable {
/** /**
* Construct an allocation strategy with the given map and class name. * Construct an allocation strategy with the given map and class name.
* @param fieldCount number of fields in the allocated object * @param fieldCount number of fields in the allocated object
* @param dualFields whether to use dual field representation
*/ */
public AllocationStrategy(final int fieldCount) { public AllocationStrategy(final int fieldCount, final boolean dualFields) {
this.fieldCount = fieldCount; this.fieldCount = fieldCount;
this.dualFields = dualFields;
} }
private String getAllocatorClassName() { private String getAllocatorClassName() {
if (allocatorClassName == null) { if (allocatorClassName == null) {
// These classes get loaded, so an interned variant of their name is most likely around anyway. // These classes get loaded, so an interned variant of their name is most likely around anyway.
allocatorClassName = Compiler.binaryName(ObjectClassGenerator.getClassName(fieldCount)).intern(); allocatorClassName = Compiler.binaryName(ObjectClassGenerator.getClassName(fieldCount, dualFields)).intern();
} }
return allocatorClassName; return allocatorClassName;
} }
......
...@@ -130,6 +130,23 @@ public final class Context { ...@@ -130,6 +130,23 @@ public final class Context {
private static MethodHandles.Lookup LOOKUP = MethodHandles.lookup(); private static MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
private static MethodType CREATE_PROGRAM_FUNCTION_TYPE = MethodType.methodType(ScriptFunction.class, ScriptObject.class); private static MethodType CREATE_PROGRAM_FUNCTION_TYPE = MethodType.methodType(ScriptFunction.class, ScriptObject.class);
/**
* Should scripts use only object slots for fields, or dual long/object slots? The default
* behaviour is to couple this to optimistic types, using dual representation if optimistic types are enabled
* and single field representation otherwise. This can be overridden by setting either the "nashorn.fields.objects"
* or "nashorn.fields.dual" system property.
*/
private final FieldMode fieldMode;
private static enum FieldMode {
/** Value for automatic field representation depending on optimistic types setting */
AUTO,
/** Value for object field representation regardless of optimistic types setting */
OBJECTS,
/** Value for dual primitive/object field representation regardless of optimistic types setting */
DUAL
}
/** /**
* Keeps track of which builtin prototypes and properties have been relinked * Keeps track of which builtin prototypes and properties have been relinked
* Currently we are conservative and associate the name of a builtin class with all * Currently we are conservative and associate the name of a builtin class with all
...@@ -434,7 +451,7 @@ public final class Context { ...@@ -434,7 +451,7 @@ public final class Context {
* @param appLoader application class loader * @param appLoader application class loader
*/ */
public Context(final Options options, final ErrorManager errors, final ClassLoader appLoader) { public Context(final Options options, final ErrorManager errors, final ClassLoader appLoader) {
this(options, errors, appLoader, (ClassFilter)null); this(options, errors, appLoader, null);
} }
/** /**
...@@ -522,6 +539,14 @@ public final class Context { ...@@ -522,6 +539,14 @@ public final class Context {
getErr().println("nashorn full version " + Version.fullVersion()); getErr().println("nashorn full version " + Version.fullVersion());
} }
if (Options.getBooleanProperty("nashorn.fields.dual")) {
fieldMode = FieldMode.DUAL;
} else if (Options.getBooleanProperty("nashorn.fields.objects")) {
fieldMode = FieldMode.OBJECTS;
} else {
fieldMode = FieldMode.AUTO;
}
initLoggers(); initLoggers();
} }
...@@ -575,6 +600,14 @@ public final class Context { ...@@ -575,6 +600,14 @@ public final class Context {
return env.getErr(); return env.getErr();
} }
/**
* Should scripts compiled by this context use dual field representation?
* @return true if using dual fields, false for object-only fields
*/
public boolean useDualFields() {
return fieldMode == FieldMode.DUAL || (fieldMode == FieldMode.AUTO && env._optimistic_types);
}
/** /**
* Get the PropertyMap of the current global scope * Get the PropertyMap of the current global scope
* @return the property map of the current global scope * @return the property map of the current global scope
......
...@@ -72,7 +72,8 @@ public final class JSONFunctions { ...@@ -72,7 +72,8 @@ public final class JSONFunctions {
public static Object parse(final Object text, final Object reviver) { public static Object parse(final Object text, final Object reviver) {
final String str = JSType.toString(text); final String str = JSType.toString(text);
final Global global = Context.getGlobal(); final Global global = Context.getGlobal();
final JSONParser parser = new JSONParser(str, global); final boolean dualFields = ((ScriptObject) global).useDualFields();
final JSONParser parser = new JSONParser(str, global, dualFields);
final Object value; final Object value;
try { try {
......
...@@ -26,7 +26,6 @@ ...@@ -26,7 +26,6 @@
package jdk.nashorn.internal.runtime; package jdk.nashorn.internal.runtime;
import static jdk.nashorn.internal.codegen.CompilerConstants.staticCall; import static jdk.nashorn.internal.codegen.CompilerConstants.staticCall;
import static jdk.nashorn.internal.codegen.ObjectClassGenerator.OBJECT_FIELDS_ONLY;
import static jdk.nashorn.internal.lookup.Lookup.MH; import static jdk.nashorn.internal.lookup.Lookup.MH;
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
...@@ -1972,10 +1971,6 @@ public enum JSType { ...@@ -1972,10 +1971,6 @@ public enum JSType {
* @return primive type or Object.class if not primitive * @return primive type or Object.class if not primitive
*/ */
public static Class<?> unboxedFieldType(final Object o) { public static Class<?> unboxedFieldType(final Object o) {
if (OBJECT_FIELDS_ONLY) {
return Object.class;
}
if (o == null) { if (o == null) {
return Object.class; return Object.class;
} else if (o.getClass() == Integer.class) { } else if (o.getClass() == Integer.class) {
......
...@@ -96,6 +96,9 @@ public abstract class Property implements Serializable { ...@@ -96,6 +96,9 @@ public abstract class Property implements Serializable {
/** Is this property an ES6 lexical binding? */ /** Is this property an ES6 lexical binding? */
public static final int IS_LEXICAL_BINDING = 1 << 10; public static final int IS_LEXICAL_BINDING = 1 << 10;
/** Does this property support dual field representation? */
public static final int DUAL_FIELDS = 1 << 11;
/** Property key. */ /** Property key. */
private final String key; private final String key;
...@@ -286,7 +289,7 @@ public abstract class Property implements Serializable { ...@@ -286,7 +289,7 @@ public abstract class Property implements Serializable {
* @return true if parameter * @return true if parameter
*/ */
public boolean isParameter() { public boolean isParameter() {
return (flags & IS_PARAMETER) == IS_PARAMETER; return (flags & IS_PARAMETER) != 0;
} }
/** /**
...@@ -294,7 +297,7 @@ public abstract class Property implements Serializable { ...@@ -294,7 +297,7 @@ public abstract class Property implements Serializable {
* @return true if has arguments * @return true if has arguments
*/ */
public boolean hasArguments() { public boolean hasArguments() {
return (flags & HAS_ARGUMENTS) == HAS_ARGUMENTS; return (flags & HAS_ARGUMENTS) != 0;
} }
/** /**
...@@ -316,7 +319,7 @@ public abstract class Property implements Serializable { ...@@ -316,7 +319,7 @@ public abstract class Property implements Serializable {
* @return true if this is a bound property * @return true if this is a bound property
*/ */
public boolean isBound() { public boolean isBound() {
return (flags & IS_BOUND) == IS_BOUND; return (flags & IS_BOUND) != 0;
} }
/** /**
...@@ -325,7 +328,7 @@ public abstract class Property implements Serializable { ...@@ -325,7 +328,7 @@ public abstract class Property implements Serializable {
* @return true if this is a block-scoped variable * @return true if this is a block-scoped variable
*/ */
public boolean needsDeclaration() { public boolean needsDeclaration() {
return (flags & NEEDS_DECLARATION) == NEEDS_DECLARATION; return (flags & NEEDS_DECLARATION) != 0;
} }
/** /**
...@@ -345,16 +348,6 @@ public abstract class Property implements Serializable { ...@@ -345,16 +348,6 @@ public abstract class Property implements Serializable {
return this; return this;
} }
/**
* Check if a flag is set for a property
* @param property property
* @param flag flag to check
* @return true if flag is set
*/
public static boolean checkFlag(final Property property, final int flag) {
return (property.getFlags() & flag) == flag;
}
/** /**
* Get the flags for this property * Get the flags for this property
* @return property flags * @return property flags
...@@ -363,16 +356,6 @@ public abstract class Property implements Serializable { ...@@ -363,16 +356,6 @@ public abstract class Property implements Serializable {
return flags; return flags;
} }
/**
* Get the modify flags for this property. The modify flags are the ECMA 8.6.1
* flags that decide if the Property is writable, configurable and/or enumerable.
*
* @return modify flags for property
*/
public int getModifyFlags() {
return flags & MODIFY_MASK;
}
/** /**
* Remove property flags from the property. Properties are immutable here, * Remove property flags from the property. Properties are immutable here,
* so any property change that results in a smaller flag set results in the * so any property change that results in a smaller flag set results in the
...@@ -715,7 +698,7 @@ public abstract class Property implements Serializable { ...@@ -715,7 +698,7 @@ public abstract class Property implements Serializable {
* @return whether this property is a function declaration or not. * @return whether this property is a function declaration or not.
*/ */
public boolean isFunctionDeclaration() { public boolean isFunctionDeclaration() {
return (flags & IS_FUNCTION_DECLARATION) == IS_FUNCTION_DECLARATION; return (flags & IS_FUNCTION_DECLARATION) != 0;
} }
/** /**
...@@ -723,6 +706,14 @@ public abstract class Property implements Serializable { ...@@ -723,6 +706,14 @@ public abstract class Property implements Serializable {
* @return true if this property represents a lexical binding. * @return true if this property represents a lexical binding.
*/ */
public boolean isLexicalBinding() { public boolean isLexicalBinding() {
return (flags & IS_LEXICAL_BINDING) == IS_LEXICAL_BINDING; return (flags & IS_LEXICAL_BINDING) != 0;
}
/**
* Does this property support dual fields for both primitive and object values?
* @return true if supports dual fields
*/
public boolean hasDualFields() {
return (flags & DUAL_FIELDS) != 0;
} }
} }
...@@ -198,13 +198,22 @@ public final class PropertyMap implements Iterable<Object>, Serializable { ...@@ -198,13 +198,22 @@ public final class PropertyMap implements Iterable<Object>, Serializable {
return properties == null || properties.isEmpty()? newMap() : newMap(properties, JO.class.getName(), 0, 0, 0); return properties == null || properties.isEmpty()? newMap() : newMap(properties, JO.class.getName(), 0, 0, 0);
} }
/**
* Return a sharable empty map for the given object class.
* @param clazz the base object class
* @return New empty {@link PropertyMap}.
*/
public static PropertyMap newMap(final Class<? extends ScriptObject> clazz) {
return new PropertyMap(EMPTY_HASHMAP, clazz.getName(), 0, 0, 0, false);
}
/** /**
* Return a sharable empty map. * Return a sharable empty map.
* *
* @return New empty {@link PropertyMap}. * @return New empty {@link PropertyMap}.
*/ */
public static PropertyMap newMap() { public static PropertyMap newMap() {
return new PropertyMap(EMPTY_HASHMAP, JO.class.getName(), 0, 0, 0, false); return newMap(JO.class);
} }
/** /**
......
...@@ -28,7 +28,6 @@ package jdk.nashorn.internal.runtime; ...@@ -28,7 +28,6 @@ package jdk.nashorn.internal.runtime;
import static jdk.nashorn.internal.codegen.CompilerConstants.staticCallNoLookup; import static jdk.nashorn.internal.codegen.CompilerConstants.staticCallNoLookup;
import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCall; import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCall;
import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup; import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup;
import static jdk.nashorn.internal.codegen.ObjectClassGenerator.OBJECT_FIELDS_ONLY;
import static jdk.nashorn.internal.lookup.Lookup.MH; import static jdk.nashorn.internal.lookup.Lookup.MH;
import static jdk.nashorn.internal.runtime.ECMAErrors.referenceError; import static jdk.nashorn.internal.runtime.ECMAErrors.referenceError;
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
...@@ -146,12 +145,6 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable { ...@@ -146,12 +145,6 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
/** Area for reference properties added to object after instantiation, see {@link AccessorProperty} */ /** Area for reference properties added to object after instantiation, see {@link AccessorProperty} */
protected Object[] objectSpill; protected Object[] objectSpill;
/**
* Number of elements in the spill. This may be less than the spill array lengths, if not all of
* the allocated memory is in use
*/
private int spillLength;
/** Indexed array data. */ /** Indexed array data. */
private ArrayData arrayData; private ArrayData arrayData;
...@@ -171,12 +164,6 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable { ...@@ -171,12 +164,6 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
/** Method handle for getting the array data */ /** Method handle for getting the array data */
public static final Call GET_ARRAY = virtualCall(MethodHandles.lookup(), ScriptObject.class, "getArray", ArrayData.class); public static final Call GET_ARRAY = virtualCall(MethodHandles.lookup(), ScriptObject.class, "getArray", ArrayData.class);
/** Method handle for getting the property map - debugging purposes */
public static final Call GET_MAP = virtualCall(MethodHandles.lookup(), ScriptObject.class, "getMap", PropertyMap.class);
/** Method handle for setting the array data */
public static final Call SET_ARRAY = virtualCall(MethodHandles.lookup(), ScriptObject.class, "setArray", void.class, ArrayData.class);
/** Method handle for getting a function argument at a given index. Used from MapCreator */ /** Method handle for getting a function argument at a given index. Used from MapCreator */
public static final Call GET_ARGUMENT = virtualCall(MethodHandles.lookup(), ScriptObject.class, "getArgument", Object.class, int.class); public static final Call GET_ARGUMENT = virtualCall(MethodHandles.lookup(), ScriptObject.class, "getArgument", Object.class, int.class);
...@@ -259,8 +246,7 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable { ...@@ -259,8 +246,7 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
this(map); this(map);
this.primitiveSpill = primitiveSpill; this.primitiveSpill = primitiveSpill;
this.objectSpill = objectSpill; this.objectSpill = objectSpill;
assert primitiveSpill.length == objectSpill.length : " primitive spill pool size is not the same length as object spill pool size"; assert primitiveSpill == null || primitiveSpill.length == objectSpill.length : " primitive spill pool size is not the same length as object spill pool size";
this.spillLength = spillAllocationLength(primitiveSpill.length);
} }
/** /**
...@@ -727,7 +713,7 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable { ...@@ -727,7 +713,7 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
setArray(getArray().ensure(longIndex)); setArray(getArray().ensure(longIndex));
doesNotHaveEnsureDelete(longIndex, oldLength, false); doesNotHaveEnsureDelete(longIndex, oldLength, false);
} }
setArray(getArray().set(index,value, false)); setArray(getArray().set(index, value, false));
} }
private void checkIntegerKey(final String key) { private void checkIntegerKey(final String key) {
...@@ -976,10 +962,10 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable { ...@@ -976,10 +962,10 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
* @param setter setter for {@link UserAccessorProperty}, null if not present or N/A * @param setter setter for {@link UserAccessorProperty}, null if not present or N/A
*/ */
protected final void initUserAccessors(final String key, final int propertyFlags, final ScriptFunction getter, final ScriptFunction setter) { protected final void initUserAccessors(final String key, final int propertyFlags, final ScriptFunction getter, final ScriptFunction setter) {
final int slot = spillLength;
ensureSpillSize(spillLength); //arguments=slot0, caller=slot0
objectSpill[slot] = new UserAccessorProperty.Accessors(getter, setter);
final PropertyMap oldMap = getMap(); final PropertyMap oldMap = getMap();
final int slot = oldMap.getFreeSpillSlot();
ensureSpillSize(slot);
objectSpill[slot] = new UserAccessorProperty.Accessors(getter, setter);
Property newProperty; Property newProperty;
PropertyMap newMap; PropertyMap newMap;
do { do {
...@@ -1006,20 +992,13 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable { ...@@ -1006,20 +992,13 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
final int slot = uc.getSlot(); final int slot = uc.getSlot();
assert uc.getLocalType() == Object.class; assert uc.getLocalType() == Object.class;
if (slot >= spillLength) {
uc.setAccessors(this, getMap(), new UserAccessorProperty.Accessors(getter, setter));
} else {
final UserAccessorProperty.Accessors gs = uc.getAccessors(this); //this crashes final UserAccessorProperty.Accessors gs = uc.getAccessors(this); //this crashes
if (gs == null) { assert gs != null;
uc.setAccessors(this, getMap(), new UserAccessorProperty.Accessors(getter, setter));
} else {
//reuse existing getter setter for speed //reuse existing getter setter for speed
gs.set(getter, setter); gs.set(getter, setter);
if (uc.getFlags() == propertyFlags) { if (uc.getFlags() == propertyFlags) {
return oldProperty; return oldProperty;
} }
}
}
newProperty = new UserAccessorProperty(uc.getKey(), propertyFlags, slot); newProperty = new UserAccessorProperty(uc.getKey(), propertyFlags, slot);
} else { } else {
// erase old property value and create new user accessor property // erase old property value and create new user accessor property
...@@ -2053,8 +2032,6 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable { ...@@ -2053,8 +2032,6 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
protoSwitchPoint = null; protoSwitchPoint = null;
} }
assert OBJECT_FIELDS_ONLY || guard != null : "we always need a map guard here";
final GuardedInvocation inv = new GuardedInvocation(mh, guard, protoSwitchPoint, exception); final GuardedInvocation inv = new GuardedInvocation(mh, guard, protoSwitchPoint, exception);
return inv.addSwitchPoint(findBuiltinSwitchPoint(name)); return inv.addSwitchPoint(findBuiltinSwitchPoint(name));
} }
...@@ -2532,12 +2509,13 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable { ...@@ -2532,12 +2509,13 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
/** /**
* Add a spill property for the given key. * Add a spill property for the given key.
* @param key Property key. * @param key Property key.
* @param propertyFlags Property flags. * @param flags Property flags.
* @return Added property. * @return Added property.
*/ */
private Property addSpillProperty(final String key, final int propertyFlags, final Object value, final boolean hasInitialValue) { private Property addSpillProperty(final String key, final int flags, final Object value, final boolean hasInitialValue) {
final PropertyMap propertyMap = getMap(); final PropertyMap propertyMap = getMap();
final int fieldSlot = propertyMap.getFreeFieldSlot(); final int fieldSlot = propertyMap.getFreeFieldSlot();
final int propertyFlags = flags | (useDualFields() ? Property.DUAL_FIELDS : 0);
Property property; Property property;
if (fieldSlot > -1) { if (fieldSlot > -1) {
...@@ -2562,7 +2540,7 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable { ...@@ -2562,7 +2540,7 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
* @return Setter method handle. * @return Setter method handle.
*/ */
MethodHandle addSpill(final Class<?> type, final String key) { MethodHandle addSpill(final Class<?> type, final String key) {
return addSpillProperty(key, 0, null, false).getSetter(OBJECT_FIELDS_ONLY ? Object.class : type, getMap()); return addSpillProperty(key, 0, null, false).getSetter(type, getMap());
} }
/** /**
...@@ -3739,24 +3717,32 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable { ...@@ -3739,24 +3717,32 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
return uc; return uc;
} }
/**
* Returns {@code true} if properties for this object should use dual field mode, {@code false} otherwise.
* @return {@code true} if dual fields should be used.
*/
protected boolean useDualFields() {
return !StructureLoader.isSingleFieldStructure(getClass().getName());
}
Object ensureSpillSize(final int slot) { Object ensureSpillSize(final int slot) {
if (slot < spillLength) { final int oldLength = objectSpill == null ? 0 : objectSpill.length;
if (slot < oldLength) {
return this; return this;
} }
final int newLength = alignUp(slot + 1, SPILL_RATE); final int newLength = alignUp(slot + 1, SPILL_RATE);
final Object[] newObjectSpill = new Object[newLength]; final Object[] newObjectSpill = new Object[newLength];
final long[] newPrimitiveSpill = OBJECT_FIELDS_ONLY ? null : new long[newLength]; final long[] newPrimitiveSpill = useDualFields() ? new long[newLength] : null;
if (objectSpill != null) { if (objectSpill != null) {
System.arraycopy(objectSpill, 0, newObjectSpill, 0, spillLength); System.arraycopy(objectSpill, 0, newObjectSpill, 0, oldLength);
if (!OBJECT_FIELDS_ONLY) { if (primitiveSpill != null && newPrimitiveSpill != null) {
System.arraycopy(primitiveSpill, 0, newPrimitiveSpill, 0, spillLength); System.arraycopy(primitiveSpill, 0, newPrimitiveSpill, 0, oldLength);
} }
} }
this.primitiveSpill = newPrimitiveSpill; this.primitiveSpill = newPrimitiveSpill;
this.objectSpill = newObjectSpill; this.objectSpill = newObjectSpill;
this.spillLength = newLength;
return this; return this;
} }
......
...@@ -232,14 +232,18 @@ final class SetMethodCreator { ...@@ -232,14 +232,18 @@ final class SetMethodCreator {
} }
private SetMethod createNewFieldSetter(final SwitchPoint builtinSwitchPoint) { private SetMethod createNewFieldSetter(final SwitchPoint builtinSwitchPoint) {
return createNewSetter(new AccessorProperty(getName(), 0, sobj.getClass(), getMap().getFreeFieldSlot(), type), builtinSwitchPoint); return createNewSetter(new AccessorProperty(getName(), getFlags(sobj), sobj.getClass(), getMap().getFreeFieldSlot(), type), builtinSwitchPoint);
} }
private SetMethod createNewSpillPropertySetter(final SwitchPoint builtinSwitchPoint) { private SetMethod createNewSpillPropertySetter(final SwitchPoint builtinSwitchPoint) {
return createNewSetter(new SpillProperty(getName(), 0, getMap().getFreeSpillSlot(), type), builtinSwitchPoint); return createNewSetter(new SpillProperty(getName(), getFlags(sobj), getMap().getFreeSpillSlot(), type), builtinSwitchPoint);
} }
private PropertyMap getNewMap(final Property property) { private PropertyMap getNewMap(final Property property) {
return getMap().addProperty(property); return getMap().addProperty(property);
} }
private static int getFlags(final ScriptObject scriptObject) {
return scriptObject.useDualFields() ? Property.DUAL_FIELDS : 0;
}
} }
...@@ -25,7 +25,6 @@ ...@@ -25,7 +25,6 @@
package jdk.nashorn.internal.runtime; package jdk.nashorn.internal.runtime;
import static jdk.nashorn.internal.codegen.ObjectClassGenerator.OBJECT_FIELDS_ONLY;
import static jdk.nashorn.internal.lookup.Lookup.MH; import static jdk.nashorn.internal.lookup.Lookup.MH;
import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandle;
...@@ -139,11 +138,11 @@ public class SpillProperty extends AccessorProperty { ...@@ -139,11 +138,11 @@ public class SpillProperty extends AccessorProperty {
} }
} }
private static MethodHandle primitiveGetter(final int slot) { private static MethodHandle primitiveGetter(final int slot, final int flags) {
return OBJECT_FIELDS_ONLY ? null : Accessors.getCached(slot, true, true); return (flags & DUAL_FIELDS) == DUAL_FIELDS ? Accessors.getCached(slot, true, true) : null;
} }
private static MethodHandle primitiveSetter(final int slot) { private static MethodHandle primitiveSetter(final int slot, final int flags) {
return OBJECT_FIELDS_ONLY ? null : Accessors.getCached(slot, true, false); return (flags & DUAL_FIELDS) == DUAL_FIELDS ? Accessors.getCached(slot, true, false) : null;
} }
private static MethodHandle objectGetter(final int slot) { private static MethodHandle objectGetter(final int slot) {
return Accessors.getCached(slot, false, true); return Accessors.getCached(slot, false, true);
...@@ -160,8 +159,7 @@ public class SpillProperty extends AccessorProperty { ...@@ -160,8 +159,7 @@ public class SpillProperty extends AccessorProperty {
* @param slot spill slot * @param slot spill slot
*/ */
public SpillProperty(final String key, final int flags, final int slot) { public SpillProperty(final String key, final int flags, final int slot) {
super(key, flags, slot, primitiveGetter(slot), primitiveSetter(slot), objectGetter(slot), objectSetter(slot)); super(key, flags, slot, primitiveGetter(slot, flags), primitiveSetter(slot, flags), objectGetter(slot), objectSetter(slot));
assert !OBJECT_FIELDS_ONLY || getLocalType() == Object.class;
} }
/** /**
...@@ -173,7 +171,7 @@ public class SpillProperty extends AccessorProperty { ...@@ -173,7 +171,7 @@ public class SpillProperty extends AccessorProperty {
*/ */
public SpillProperty(final String key, final int flags, final int slot, final Class<?> initialType) { public SpillProperty(final String key, final int flags, final int slot, final Class<?> initialType) {
this(key, flags, slot); this(key, flags, slot);
setType(OBJECT_FIELDS_ONLY ? Object.class : initialType); setType(hasDualFields() ? initialType : Object.class);
} }
SpillProperty(final String key, final int flags, final int slot, final ScriptObject owner, final Object initialValue) { SpillProperty(final String key, final int flags, final int slot, final ScriptObject owner, final Object initialValue) {
...@@ -216,8 +214,8 @@ public class SpillProperty extends AccessorProperty { ...@@ -216,8 +214,8 @@ public class SpillProperty extends AccessorProperty {
@Override @Override
void initMethodHandles(final Class<?> structure) { void initMethodHandles(final Class<?> structure) {
final int slot = getSlot(); final int slot = getSlot();
primitiveGetter = primitiveGetter(slot); primitiveGetter = primitiveGetter(slot, getFlags());
primitiveSetter = primitiveSetter(slot); primitiveSetter = primitiveSetter(slot, getFlags());
objectGetter = objectGetter(slot); objectGetter = objectGetter(slot);
objectSetter = objectSetter(slot); objectSetter = objectSetter(slot);
} }
......
...@@ -27,7 +27,8 @@ package jdk.nashorn.internal.runtime; ...@@ -27,7 +27,8 @@ package jdk.nashorn.internal.runtime;
import static jdk.nashorn.internal.codegen.Compiler.SCRIPTS_PACKAGE; import static jdk.nashorn.internal.codegen.Compiler.SCRIPTS_PACKAGE;
import static jdk.nashorn.internal.codegen.Compiler.binaryName; import static jdk.nashorn.internal.codegen.Compiler.binaryName;
import static jdk.nashorn.internal.codegen.CompilerConstants.JS_OBJECT_PREFIX; import static jdk.nashorn.internal.codegen.CompilerConstants.JS_OBJECT_DUAL_FIELD_PREFIX;
import static jdk.nashorn.internal.codegen.CompilerConstants.JS_OBJECT_SINGLE_FIELD_PREFIX;
import java.security.ProtectionDomain; import java.security.ProtectionDomain;
import jdk.nashorn.internal.codegen.ObjectClassGenerator; import jdk.nashorn.internal.codegen.ObjectClassGenerator;
...@@ -36,7 +37,8 @@ import jdk.nashorn.internal.codegen.ObjectClassGenerator; ...@@ -36,7 +37,8 @@ import jdk.nashorn.internal.codegen.ObjectClassGenerator;
* Responsible for on the fly construction of structure classes. * Responsible for on the fly construction of structure classes.
*/ */
final class StructureLoader extends NashornLoader { final class StructureLoader extends NashornLoader {
private static final String JS_OBJECT_PREFIX_EXTERNAL = binaryName(SCRIPTS_PACKAGE) + '.' + JS_OBJECT_PREFIX.symbolName(); private static final String SINGLE_FIELD_PREFIX = binaryName(SCRIPTS_PACKAGE) + '.' + JS_OBJECT_SINGLE_FIELD_PREFIX.symbolName();
private static final String DUAL_FIELD_PREFIX = binaryName(SCRIPTS_PACKAGE) + '.' + JS_OBJECT_DUAL_FIELD_PREFIX.symbolName();
/** /**
* Constructor. * Constructor.
...@@ -45,14 +47,39 @@ final class StructureLoader extends NashornLoader { ...@@ -45,14 +47,39 @@ final class StructureLoader extends NashornLoader {
super(parent); super(parent);
} }
/**
* Returns true if the class name represents a structure object with dual primitive/object fields.
* @param name a class name
* @return true if a dual field structure class
*/
private static boolean isDualFieldStructure(final String name) {
return name.startsWith(DUAL_FIELD_PREFIX);
}
/**
* Returns true if the class name represents a structure object with single object-only fields.
* @param name a class name
* @return true if a single field structure class
*/
static boolean isSingleFieldStructure(final String name) {
return name.startsWith(SINGLE_FIELD_PREFIX);
}
/**
* Returns true if the class name represents a Nashorn structure object.
* @param name a class name
* @return true if a structure class
*/
static boolean isStructureClass(final String name) { static boolean isStructureClass(final String name) {
return name.startsWith(JS_OBJECT_PREFIX_EXTERNAL); return isDualFieldStructure(name) || isSingleFieldStructure(name);
} }
@Override @Override
protected Class<?> findClass(final String name) throws ClassNotFoundException { protected Class<?> findClass(final String name) throws ClassNotFoundException {
if (isStructureClass(name)) { if (isDualFieldStructure(name)) {
return generateClass(name, name.substring(JS_OBJECT_PREFIX_EXTERNAL.length())); return generateClass(name, name.substring(DUAL_FIELD_PREFIX.length()), true);
} else if (isSingleFieldStructure(name)) {
return generateClass(name, name.substring(SINGLE_FIELD_PREFIX.length()), false);
} }
return super.findClass(name); return super.findClass(name);
} }
...@@ -63,10 +90,10 @@ final class StructureLoader extends NashornLoader { ...@@ -63,10 +90,10 @@ final class StructureLoader extends NashornLoader {
* @param descriptor Layout descriptor. * @param descriptor Layout descriptor.
* @return Generated class. * @return Generated class.
*/ */
private Class<?> generateClass(final String name, final String descriptor) { private Class<?> generateClass(final String name, final String descriptor, final boolean dualFields) {
final Context context = Context.getContextTrusted(); final Context context = Context.getContextTrusted();
final byte[] code = new ObjectClassGenerator(context).generate(descriptor); final byte[] code = new ObjectClassGenerator(context, dualFields).generate(descriptor);
return defineClass(name, code, 0, code.length, new ProtectionDomain(null, getPermissions(null))); return defineClass(name, code, 0, code.length, new ProtectionDomain(null, getPermissions(null)));
} }
} }
...@@ -74,17 +74,16 @@ public final class Bootstrap { ...@@ -74,17 +74,16 @@ public final class Bootstrap {
* of object fields only, it is fine. However, with dual fields, in order to get * of object fields only, it is fine. However, with dual fields, in order to get
* performance on benchmarks with a lot of object instantiation and then field * performance on benchmarks with a lot of object instantiation and then field
* reassignment, it can take slightly more relinks to become stable with type * reassignment, it can take slightly more relinks to become stable with type
* changes swapping out an entire proprety map and making a map guard fail. * changes swapping out an entire property map and making a map guard fail.
* Therefore the relink threshold is set to 16 for dual fields (now the default). * Since we need to set this value statically it must work with possibly changing
* This doesn't seem to have any other negative performance implication. * optimistic types and dual fields settings. A higher value does not seem to have
* any other negative performance implication when running with object-only fields,
* so we choose a higher value here.
* *
* See for example octane.gbemu, run with --log=fields:warning to study * See for example octane.gbemu, run with --log=fields:warning to study
* megamorphic behavior * megamorphic behavior
*/ */
private static final int NASHORN_DEFAULT_UNSTABLE_RELINK_THRESHOLD = private static final int NASHORN_DEFAULT_UNSTABLE_RELINK_THRESHOLD = 16;
ObjectClassGenerator.OBJECT_FIELDS_ONLY ?
8 :
16;
// do not create me!! // do not create me!!
private Bootstrap() { private Bootstrap() {
......
...@@ -33,7 +33,6 @@ import java.lang.ref.WeakReference; ...@@ -33,7 +33,6 @@ import java.lang.ref.WeakReference;
import jdk.internal.dynalink.CallSiteDescriptor; import jdk.internal.dynalink.CallSiteDescriptor;
import jdk.internal.dynalink.linker.LinkRequest; import jdk.internal.dynalink.linker.LinkRequest;
import jdk.nashorn.api.scripting.JSObject; import jdk.nashorn.api.scripting.JSObject;
import jdk.nashorn.internal.codegen.ObjectClassGenerator;
import jdk.nashorn.internal.objects.Global; import jdk.nashorn.internal.objects.Global;
import jdk.nashorn.internal.runtime.Property; import jdk.nashorn.internal.runtime.Property;
import jdk.nashorn.internal.runtime.PropertyMap; import jdk.nashorn.internal.runtime.PropertyMap;
...@@ -123,7 +122,7 @@ public final class NashornGuards { ...@@ -123,7 +122,7 @@ public final class NashornGuards {
*/ */
static boolean needsGuard(final Property property, final CallSiteDescriptor desc) { static boolean needsGuard(final Property property, final CallSiteDescriptor desc) {
return property == null || property.isConfigurable() return property == null || property.isConfigurable()
|| property.isBound() || !ObjectClassGenerator.OBJECT_FIELDS_ONLY || property.isBound() || property.hasDualFields()
|| !NashornCallSiteDescriptor.isFastScope(desc) || property.canChangeType(); || !NashornCallSiteDescriptor.isFastScope(desc) || property.canChangeType();
} }
......
/*
* 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.scripts;
import jdk.nashorn.internal.runtime.PropertyMap;
import jdk.nashorn.internal.runtime.ScriptObject;
/**
* Empty object class for dual primitive-object fields.
*/
public class JD extends ScriptObject {
private static final PropertyMap map$ = PropertyMap.newMap(JD.class);
/**
* Returns the initial property map to be used.
* @return the initial property map.
*/
public static PropertyMap getInitialMap() {
return map$;
}
/**
* Constructor given an initial property map
*
* @param map the property map
*/
public JD(final PropertyMap map) {
super(map);
}
/**
* Constructor given an initial prototype and the default initial property map.
*
* @param proto the prototype object
*/
public JD(final ScriptObject proto) {
super(proto, getInitialMap());
}
/**
* Constructor that takes a pre-initialized spill pool. Used by
* {@link jdk.nashorn.internal.codegen.SpillObjectCreator} and
* {@link jdk.nashorn.internal.parser.JSONParser} for initializing object literals
*
* @param map property map
* @param primitiveSpill primitive spill pool
* @param objectSpill reference spill pool
*/
public JD(final PropertyMap map, final long[] primitiveSpill, final Object[] objectSpill) {
super(map, primitiveSpill, objectSpill);
}
/**
* A method handle of this method is passed to the ScriptFunction constructor.
*
* @param map the property map to use for allocatorMap
*
* @return newly allocated ScriptObject
*/
public static ScriptObject allocate(final PropertyMap map) {
return new JD(map);
}
}
/* /*
* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -29,11 +29,11 @@ import jdk.nashorn.internal.runtime.PropertyMap; ...@@ -29,11 +29,11 @@ import jdk.nashorn.internal.runtime.PropertyMap;
import jdk.nashorn.internal.runtime.ScriptObject; import jdk.nashorn.internal.runtime.ScriptObject;
/** /**
* Empty object class. * Empty object class for object-only fields.
*/ */
public class JO extends ScriptObject { public class JO extends ScriptObject {
private static final PropertyMap map$ = PropertyMap.newMap(); private static final PropertyMap map$ = PropertyMap.newMap(JO.class);
/** /**
* Returns the initial property map to be used. * Returns the initial property map to be used.
...@@ -53,13 +53,12 @@ public class JO extends ScriptObject { ...@@ -53,13 +53,12 @@ public class JO extends ScriptObject {
} }
/** /**
* Constructor given an initial prototype and an initial property map. * Constructor given an initial prototype and the default initial property map.
* *
* @param proto the prototype object * @param proto the prototype object
* @param map the property map
*/ */
public JO(final ScriptObject proto, final PropertyMap map) { public JO(final ScriptObject proto) {
super(proto, map); super(proto, getInitialMap());
} }
/** /**
...@@ -86,3 +85,4 @@ public class JO extends ScriptObject { ...@@ -86,3 +85,4 @@ public class JO extends ScriptObject {
return new JO(map); return new JO(map);
} }
} }
/*
* Copyright (c) 2010, 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-8067215: Disable dual fields when not using optimistic types
*
* @test
* @run
* @option -Dnashorn.debug=true
* @fork
*/
var intType = Java.type("int");
var doubleType = Java.type("double");
var longType = Java.type("long");
var objectType = Java.type("java.lang.Object");
var Context = Java.type("jdk.nashorn.internal.runtime.Context");
var JSType = Java.type("jdk.nashorn.internal.runtime.JSType");
var context = Context.getContext();
var dualFields = context.useDualFields();
var optimisticTypes = context.getEnv()._optimistic_types;
if (dualFields != optimisticTypes) {
throw new Error("Wrong dual fields setting");
}
function testMap(obj) {
obj.x = "foo";
obj["y"] = 0;
Object.defineProperty(obj, "z", {value: 0.5});
var map = Debug.map(obj);
for (var key in obj) {
var prop = map.findProperty(key);
if (prop.hasDualFields() !== dualFields) {
throw new Error("Wrong property flags: " + prop);
}
if (prop.getType() != getExpectedType(obj[key])) {
throw new Error("Wrong property type: " + prop.getType() + " // " + getExpectedType(obj[key]));
}
}
}
function getExpectedType(value) {
if (!dualFields) {
return objectType.class;
}
if (JSType.isRepresentableAsInt(value)) {
return intType.class;
}
if (JSType.isRepresentableAsLong(value)) {
return longType.class;
}
if (JSType.isNumber(value)) {
return doubleType.class;
}
return objectType.class;
}
var o = {
a: 1,
b: 2.5,
c: 0x10000000000,
d: true
};
function C() {
this.a = 1;
this.b = 2.5;
this.c = 0x10000000000;
this.d = true;
}
var a = 1;
var b = 2.5;
var c = 0x10000000000;
var d = true;
testMap(o);
testMap(new C());
testMap(JSON.parse('{ "a": 1, "b": 2.5, "c": 1099511627776, "d": true }'));
testMap(this);
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册