From 86e077f07c09cbc8174d45b0e194c8f49ab10f33 Mon Sep 17 00:00:00 2001 From: hannesw Date: Fri, 10 Apr 2015 14:18:31 +0200 Subject: [PATCH] 8067215: Disable dual fields when not using optimistic types Reviewed-by: attila, lagergren --- .../tools/nasgen/PrototypeGenerator.java | 2 - nashorn/docs/DEVELOPER_README | 23 ++-- .../internal/codegen/CodeGenerator.java | 15 ++- .../internal/codegen/CompilerConstants.java | 7 +- .../internal/codegen/FieldObjectCreator.java | 8 +- .../internal/codegen/FindScopeDepths.java | 2 +- .../nashorn/internal/codegen/MapCreator.java | 18 +-- .../nashorn/internal/codegen/MapTuple.java | 6 +- .../codegen/ObjectClassGenerator.java | 102 ++++++++--------- .../internal/codegen/ObjectCreator.java | 2 +- .../internal/codegen/SpillObjectCreator.java | 53 +++++---- .../jdk/nashorn/internal/objects/Global.java | 14 ++- .../internal/objects/NativeJSAdapter.java | 2 +- .../nashorn/internal/parser/JSONParser.java | 32 ++++-- .../internal/runtime/AccessorProperty.java | 37 +++---- .../internal/runtime/AllocationStrategy.java | 9 +- .../jdk/nashorn/internal/runtime/Context.java | 35 +++++- .../internal/runtime/JSONFunctions.java | 3 +- .../jdk/nashorn/internal/runtime/JSType.java | 5 - .../nashorn/internal/runtime/Property.java | 43 +++----- .../nashorn/internal/runtime/PropertyMap.java | 11 +- .../internal/runtime/ScriptObject.java | 80 ++++++-------- .../internal/runtime/SetMethodCreator.java | 8 +- .../internal/runtime/SpillProperty.java | 18 ++- .../internal/runtime/StructureLoader.java | 41 +++++-- .../internal/runtime/linker/Bootstrap.java | 13 +-- .../runtime/linker/NashornGuards.java | 3 +- .../jdk/nashorn/internal/scripts/JD.java | 88 +++++++++++++++ .../jdk/nashorn/internal/scripts/JO.java | 14 +-- nashorn/test/script/nosecurity/JDK-8067215.js | 104 ++++++++++++++++++ 30 files changed, 534 insertions(+), 264 deletions(-) create mode 100644 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/scripts/JD.java create mode 100644 nashorn/test/script/nosecurity/JDK-8067215.js diff --git a/nashorn/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/PrototypeGenerator.java b/nashorn/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/PrototypeGenerator.java index 8eb19b7d11..115e4b19f4 100644 --- a/nashorn/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/PrototypeGenerator.java +++ b/nashorn/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/PrototypeGenerator.java @@ -124,8 +124,6 @@ public class PrototypeGenerator extends ClassGenerator { if (memberCount > 0) { // call "super(map$)" 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); // initialize Function type fields initFunctionFields(mi); diff --git a/nashorn/docs/DEVELOPER_README b/nashorn/docs/DEVELOPER_README index 5550b903b1..faff28c0ba 100644 --- a/nashorn/docs/DEVELOPER_README +++ b/nashorn/docs/DEVELOPER_README @@ -63,16 +63,19 @@ SYSTEM PROPERTY: -Dnashorn.codegen.debug.trace= See the description of the codegen logger below. -SYSTEM PROPERTY: -Dnashorn.fields.objects - -When this property is true, Nashorn will only use object fields for -AccessorProperties. This means that primitive values must be boxed -when stored in a field, which is significantly slower than using -primitive fields. - -By default, Nashorn uses dual object and long fields. Ints are -represented as the 32 low bits of the long fields. Doubles are -represented as the doubleToLongBits of their value. This way a +SYSTEM PROPERTY: -Dnashorn.fields.objects, -Dnashorn.fields.dual + +When the nashorn.fields.objects property is true, Nashorn will always +use object fields for AccessorProperties, requiring boxing for all +primitive property values. When nashorn.fields.dual is set, Nashorn +will always use dual long/object fields, which allows primitives to be +stored without boxing. When neither system property is set, Nashorn +chooses a setting depending on the optimistic types setting (dual +fields when optimistic types are enabled, object-only fields otherwise). + +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 unpacking doubles to their bit representation is intrinsified by the JVM and extremely fast. diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CodeGenerator.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CodeGenerator.java index fd627a6f1f..81daa4f228 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CodeGenerator.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CodeGenerator.java @@ -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.typeDescriptor; 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.IS_INTERNAL; import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT; @@ -305,6 +304,14 @@ final class CodeGenerator extends NodeOperatorVisitor getValueType() { - if (OBJECT_FIELDS_ONLY || value == null || paramType == null) { + if (!useDualFields() || value == null || paramType == null || paramType.isBoolean()) { return Object.class; } - return paramType.isBoolean() ? Object.class : paramType.getTypeClass(); + return paramType.getTypeClass(); } }); } @@ -2555,7 +2562,7 @@ final class CodeGenerator extends NodeOperatorVisitor 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(key, symbol, Type.typeFor(valueType), value) { @Override public Class getValueType() { diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CompilerConstants.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CompilerConstants.java index e84c9f0477..a7f31d5040 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CompilerConstants.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CompilerConstants.java @@ -149,8 +149,11 @@ public enum CompilerConstants { /** Arguments parameter in scope object constructors; in slot 3 when present */ INIT_ARGUMENTS(null, 3), - /** prefix for all ScriptObject subclasses with fields, @see ObjectGenerator */ - JS_OBJECT_PREFIX("JO"), + /** prefix for all ScriptObject subclasses with dual object/primitive fields, see {@link ObjectClassGenerator} */ + 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 */ ALLOCATE("allocate"), diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/FieldObjectCreator.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/FieldObjectCreator.java index 491af86f63..25baea0ce2 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/FieldObjectCreator.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/FieldObjectCreator.java @@ -151,7 +151,7 @@ public abstract class FieldObjectCreator extends ObjectCreator { @Override protected PropertyMap makeMap() { 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; } @@ -166,7 +166,7 @@ public abstract class FieldObjectCreator extends ObjectCreator { private void putField(final MethodEmitter method, final String key, final int fieldIndex, final MapTuple tuple) { 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 fieldName = getFieldName(fieldIndex, fieldType); final String fieldDesc = typeDescriptor(fieldType.getTypeClass()); @@ -202,8 +202,8 @@ public abstract class FieldObjectCreator extends ObjectCreator { */ private void findClass() { fieldObjectClassName = isScope() ? - ObjectClassGenerator.getClassName(fieldCount, paramCount) : - ObjectClassGenerator.getClassName(paddedFieldCount); + ObjectClassGenerator.getClassName(fieldCount, paramCount, codegen.useDualFields()) : + ObjectClassGenerator.getClassName(paddedFieldCount, codegen.useDualFields()); try { this.fieldObjectClass = Context.forStructureClass(Compiler.binaryName(fieldObjectClassName)); diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/FindScopeDepths.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/FindScopeDepths.java index b45b986711..9f9abf9c0c 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/FindScopeDepths.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/FindScopeDepths.java @@ -207,7 +207,7 @@ final class FindScopeDepths extends NodeVisitor implements Logga final RecompilableScriptFunctionData data = new RecompilableScriptFunctionData( newFunctionNode, compiler.getCodeInstaller(), - ObjectClassGenerator.createAllocationStrategy(newFunctionNode.getThisProperties()), + ObjectClassGenerator.createAllocationStrategy(newFunctionNode.getThisProperties(), compiler.getContext().useDualFields()), nestedFunctions, externalSymbolDepths.get(fnId), internalSymbols.get(fnId), diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/MapCreator.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/MapCreator.java index ae96dc4660..be99db99c7 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/MapCreator.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/MapCreator.java @@ -68,17 +68,17 @@ public class MapCreator { * @param evalCode is this property map created for 'eval' code? * @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 properties = new ArrayList<>(); assert tuples != null; for (final MapTuple tuple : tuples) { final String key = tuple.key; final Symbol symbol = tuple.symbol; - final Class initialType = tuple.getValueType(); + final Class initialType = dualFields ? tuple.getValueType() : Object.class; 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( key, flags, @@ -92,7 +92,7 @@ public class MapCreator { return PropertyMap.newMap(properties, structure.getName(), fieldCount, fieldMaximum, 0); } - PropertyMap makeSpillMap(final boolean hasArguments) { + PropertyMap makeSpillMap(final boolean hasArguments, final boolean dualFields) { final List properties = new ArrayList<>(); int spillIndex = 0; assert tuples != null; @@ -100,10 +100,10 @@ public class MapCreator { for (final MapTuple tuple : tuples) { final String key = tuple.key; final Symbol symbol = tuple.symbol; - final Class initialType = tuple.getValueType(); + final Class initialType = dualFields ? tuple.getValueType() : Object.class; if (symbol != null && !isValidArrayIndex(getArrayIndex(key))) { - final int flags = getPropertyFlags(symbol, hasArguments, false); + final int flags = getPropertyFlags(symbol, hasArguments, false, dualFields); properties.add( new SpillProperty( key, @@ -124,7 +124,7 @@ public class MapCreator { * * @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; if (symbol.isParam()) { @@ -162,6 +162,10 @@ public class MapCreator { flags |= Property.NEEDS_DECLARATION; } + if (dualFields) { + flags |= Property.DUAL_FIELDS; + } + return flags; } } diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/MapTuple.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/MapTuple.java index 024c3d6745..44cdc8b2c8 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/MapTuple.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/MapTuple.java @@ -25,8 +25,6 @@ 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.ir.Symbol; @@ -52,11 +50,11 @@ class MapTuple { } 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() { - return !OBJECT_FIELDS_ONLY && getValueType().isPrimitive() && getValueType() != boolean.class; + return getValueType() != null && getValueType().isPrimitive() && getValueType() != boolean.class; } @Override diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/ObjectClassGenerator.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/ObjectClassGenerator.java index 2205044c9e..383d2f130d 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/ObjectClassGenerator.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/ObjectClassGenerator.java @@ -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_SCOPE; 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.constructorNoLookup; import static jdk.nashorn.internal.lookup.Lookup.MH; @@ -99,18 +100,10 @@ public final class ObjectClassGenerator implements Loggable { */ private final DebugLogger log; - /** - * Should the runtime only use java.lang.Object slots for fields? If this is false, the representation - * will be a primitive 64-bit long value used for all primitives and a java.lang.Object for references. - * This introduces a larger number of method handles in the system, as we need to have different getters - * 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 FIELD_TYPES = new LinkedList<>(); + /** Field types for object-only fields */ + private static final Type[] FIELD_TYPES_OBJECT = new Type[] { Type.OBJECT }; + /** Field types for dual primitive/object fields */ + private static final Type[] FIELD_TYPES_DUAL = new Type[] { Type.LONG, Type.OBJECT }; /** What type is the primitive type in dual representation */ public static final Type PRIMITIVE_FIELD_TYPE = Type.LONG; @@ -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_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; /** The context */ private final Context context; + private final boolean dualFields; + /** * Constructor * * @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.dualFields = dualFields; assert context != null; this.log = initLogger(context); if (!initialized) { initialized = true; - if (OBJECT_FIELDS_ONLY) { + if (!dualFields) { log.warning("Running with object fields only - this is a deprecated configuration."); } } @@ -176,16 +163,30 @@ public final class ObjectClassGenerator implements Loggable { 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. * * @param fieldCount Number of fields to allocate. - * + * @param dualFields whether to use dual fields representation * @return The class name. */ - public static String getClassName(final int fieldCount) { - return fieldCount != 0 ? SCRIPTS_PACKAGE + '/' + JS_OBJECT_PREFIX.symbolName() + fieldCount : - SCRIPTS_PACKAGE + '/' + JS_OBJECT_PREFIX.symbolName(); + public static String getClassName(final int fieldCount, final boolean dualFields) { + final String prefix = getPrefixName(dualFields); + return fieldCount != 0 ? SCRIPTS_PACKAGE + '/' + prefix + fieldCount : + SCRIPTS_PACKAGE + '/' + prefix; } /** @@ -194,22 +195,23 @@ public final class ObjectClassGenerator implements Loggable { * * @param fieldCount Number of fields to allocate. * @param paramCount Number of parameters to allocate - * + * @param dualFields whether to use dual fields representation * @return The class name. */ - public static String getClassName(final int fieldCount, final int paramCount) { - return SCRIPTS_PACKAGE + '/' + JS_OBJECT_PREFIX.symbolName() + fieldCount + SCOPE_MARKER + paramCount; + public static String getClassName(final int fieldCount, final int paramCount, final boolean dualFields) { + 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 - * {@link #getClassName(int)} or {@link #getClassName(int, int)}. + * {@link #getClassName(int, boolean)} or {@link #getClassName(int, int, boolean)}. * @param clazz the JavaScript scope class. * @return the number of fields in the scope class. */ public static int getFieldCount(final Class clazz) { final String name = clazz.getSimpleName(); - final String prefix = JS_OBJECT_PREFIX.symbolName(); + final String prefix = getPrefixName(name); + if (prefix.equals(name)) { return 0; } @@ -238,8 +240,8 @@ public final class ObjectClassGenerator implements Loggable { * @param className name of class * @param fieldNames fields to initialize to undefined, where applicable */ - private static void initializeToUndefined(final MethodEmitter init, final String className, final List fieldNames) { - if (!OBJECT_FIELDS_ONLY) { + private void initializeToUndefined(final MethodEmitter init, final String className, final List fieldNames) { + if (dualFields) { // no need to initialize anything to undefined in the dual field world // - then we have a constant getter for undefined for any unknown type return; @@ -292,7 +294,7 @@ public final class ObjectClassGenerator implements Loggable { * @return Byte codes for generated class. */ public byte[] generate(final int fieldCount) { - final String className = getClassName(fieldCount); + final String className = getClassName(fieldCount, dualFields); final String superName = className(ScriptObject.class); final ClassEmitter classEmitter = newClassEmitter(className, superName); @@ -322,7 +324,7 @@ public final class ObjectClassGenerator implements Loggable { * @return Byte codes for generated class. */ 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 ClassEmitter classEmitter = newClassEmitter(className, superName); final List initFields = addFields(classEmitter, fieldCount); @@ -353,11 +355,11 @@ public final class ObjectClassGenerator implements Loggable { * * @return List fields that need to be initialized. */ - private static List addFields(final ClassEmitter classEmitter, final int fieldCount) { + private List addFields(final ClassEmitter classEmitter, final int fieldCount) { final List initFields = new LinkedList<>(); - + final Type[] fieldTypes = dualFields ? FIELD_TYPES_DUAL : FIELD_TYPES_OBJECT; for (int i = 0; i < fieldCount; i++) { - for (final Type type : FIELD_TYPES) { + for (final Type type : fieldTypes) { final String fieldName = getFieldName(i, type); classEmitter.field(fieldName, type.getTypeClass()); @@ -533,13 +535,10 @@ public final class ObjectClassGenerator implements Loggable { private static MethodHandle getterForType(final Class forType, final MethodHandle primitiveGetter, final MethodHandle objectGetter) { switch (getAccessorTypeIndex(forType)) { case TYPE_INT_INDEX: - assert !OBJECT_FIELDS_ONLY : "this can only happen with dual fields"; return MH.explicitCastArguments(primitiveGetter, primitiveGetter.type().changeReturnType(int.class)); case TYPE_LONG_INDEX: - assert !OBJECT_FIELDS_ONLY : "this can only happen with dual fields"; return primitiveGetter; case TYPE_DOUBLE_INDEX: - assert !OBJECT_FIELDS_ONLY : "this can only happen with dual fields"; return MH.filterReturnValue(primitiveGetter, UNPACK_DOUBLE); case TYPE_OBJECT_INDEX: return objectGetter; @@ -557,7 +556,7 @@ public final class ObjectClassGenerator implements Loggable { final boolean isPrimitiveStorage = forType != null && forType.isPrimitive(); //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 (isOptimistic) { @@ -580,8 +579,7 @@ public final class ObjectClassGenerator implements Loggable { return MH.dropArguments(GET_UNDEFINED.get(ti), 0, Object.class); } - assert forType != null; - assert !OBJECT_FIELDS_ONLY || forType == Object.class : forType; + assert primitiveGetter != null || forType == Object.class : forType; if (isOptimistic) { if (fti < ti) { @@ -635,8 +633,6 @@ public final class ObjectClassGenerator implements Loggable { return tgetter; } - assert !OBJECT_FIELDS_ONLY; - //final MethodType pmt = primitiveGetter.type(); assert primitiveGetter != null; final MethodType tgetterType = tgetter.type(); switch (fti) { @@ -727,7 +723,7 @@ public final class ObjectClassGenerator implements Loggable { final int fti = getAccessorTypeIndex(forType); 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) { return objectSetter; } @@ -735,8 +731,6 @@ public final class ObjectClassGenerator implements Loggable { return MH.asType(objectSetter, objectSetter.type().changeParameterType(1, type)); } - assert !OBJECT_FIELDS_ONLY; - final MethodType pmt = primitiveSetter.type(); switch (fti) { @@ -832,8 +826,8 @@ public final class ObjectClassGenerator implements Loggable { * @param thisProperties number of properties assigned to "this" * @return the allocation strategy */ - static AllocationStrategy createAllocationStrategy(final int thisProperties) { + static AllocationStrategy createAllocationStrategy(final int thisProperties, final boolean dualFields) { final int paddedFieldCount = getPaddedFieldCount(thisProperties); - return new AllocationStrategy(paddedFieldCount); + return new AllocationStrategy(paddedFieldCount, dualFields); } } diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/ObjectCreator.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/ObjectCreator.java index 1a09ca62df..782e3d2746 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/ObjectCreator.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/ObjectCreator.java @@ -134,7 +134,7 @@ public abstract class ObjectCreator { MethodEmitter loadTuple(final MethodEmitter method, final MapTuple tuple, final boolean pack) { loadValue(tuple.value, tuple.type); - if (pack && tuple.isPrimitive()) { + if (pack && codegen.useDualFields() && tuple.isPrimitive()) { method.pack(); } else { method.convert(Type.OBJECT); diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/SpillObjectCreator.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/SpillObjectCreator.java index 40d12dfbdc..567edba405 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/SpillObjectCreator.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/SpillObjectCreator.java @@ -27,7 +27,6 @@ package jdk.nashorn.internal.codegen; import static jdk.nashorn.internal.codegen.CompilerConstants.constructorNoLookup; import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup; -import static jdk.nashorn.internal.codegen.ObjectClassGenerator.OBJECT_FIELDS_ONLY; import java.util.LinkedHashSet; import java.util.List; @@ -42,6 +41,7 @@ import jdk.nashorn.internal.runtime.ScriptObject; import jdk.nashorn.internal.runtime.ScriptRuntime; import jdk.nashorn.internal.runtime.arrays.ArrayData; import jdk.nashorn.internal.runtime.arrays.ArrayIndex; +import jdk.nashorn.internal.scripts.JD; import jdk.nashorn.internal.scripts.JO; /** @@ -65,10 +65,13 @@ public final class SpillObjectCreator extends ObjectCreator { assert !isScope() : "spill scope objects are not currently supported"; final int length = tuples.size(); - final long[] jpresetValues = new long[ScriptObject.spillAllocationLength(length)]; - final Object[] opresetValues = new Object[ScriptObject.spillAllocationLength(length)]; + final boolean dualFields = codegen.useDualFields(); + final int spillLength = ScriptObject.spillAllocationLength(length); + final long[] jpresetValues = dualFields ? new long[spillLength] : null; + final Object[] opresetValues = new Object[spillLength]; final Set postsetValues = new LinkedHashSet<>(); final int callSiteFlags = codegen.getCallSiteFlags(); + final Class objectClass = dualFields ? JD.class : JO.class; ArrayData arrayData = ArrayData.allocate(ScriptRuntime.EMPTY_ARRAY); // Compute constant property values @@ -88,9 +91,9 @@ public final class SpillObjectCreator extends ObjectCreator { final Property property = propertyMap.findProperty(key); if (property != null) { // normal property key - property.setType(JSType.unboxedFieldType(constantValue)); + property.setType(dualFields ? JSType.unboxedFieldType(constantValue) : Object.class); final int slot = property.getSlot(); - if (!OBJECT_FIELDS_ONLY && constantValue instanceof Number) { + if (dualFields && constantValue instanceof Number) { jpresetValues[slot] = ObjectClassGenerator.pack((Number)constantValue); } else { opresetValues[slot] = constantValue; @@ -130,28 +133,32 @@ public final class SpillObjectCreator extends ObjectCreator { //assert postsetValues.isEmpty() : "test me " + postsetValues; // create object and invoke constructor - method._new(JO.class).dup(); + method._new(objectClass).dup(); codegen.loadConstant(propertyMap); //load primitive values to j spill array - codegen.loadConstant(jpresetValues); - for (final int i : postsetValues) { - final MapTuple tuple = tuples.get(i); - final Property property = propertyMap.findProperty(tuple.key); - if (property != null && tuple.isPrimitive()) { - method.dup(); - method.load(property.getSlot()); - loadTuple(method, tuple); - method.arraystore(); + if (dualFields) { + codegen.loadConstant(jpresetValues); + for (final int i : postsetValues) { + final MapTuple tuple = tuples.get(i); + final Property property = propertyMap.findProperty(tuple.key); + if (property != null && tuple.isPrimitive()) { + method.dup(); + method.load(property.getSlot()); + loadTuple(method, tuple); + method.arraystore(); + } } + } else { + method.loadNull(); } //load object values to o spill array codegen.loadConstant(opresetValues); for (final int i : postsetValues) { - final MapTuple tuple = tuples.get(i); - final Property property = propertyMap.findProperty(tuple.key); - if (property != null && !tuple.isPrimitive()) { + final MapTuple tuple = tuples.get(i); + final Property property = propertyMap.findProperty(tuple.key); + if (property != null && (!dualFields || !tuple.isPrimitive())) { method.dup(); method.load(property.getSlot()); loadTuple(method, tuple); @@ -160,7 +167,7 @@ public final class SpillObjectCreator extends ObjectCreator { } //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 if (arrayData.length() > 0) { @@ -171,8 +178,8 @@ public final class SpillObjectCreator extends ObjectCreator { // set postfix for (final int i : postsetValues) { - final MapTuple tuple = tuples.get(i); - final Property property = propertyMap.findProperty(tuple.key); + final MapTuple tuple = tuples.get(i); + final Property property = propertyMap.findProperty(tuple.key); if (property == null) { final int index = ArrayIndex.getArrayIndex(tuple.key); assert ArrayIndex.isValidArrayIndex(index); @@ -188,7 +195,9 @@ public final class SpillObjectCreator extends ObjectCreator { @Override protected PropertyMap makeMap() { assert propertyMap == null : "property map already initialized"; - propertyMap = new MapCreator<>(JO.class, tuples).makeSpillMap(false); + final boolean dualFields = codegen.useDualFields(); + final Class clazz = dualFields ? JD.class : JO.class; + propertyMap = new MapCreator<>(clazz, tuples).makeSpillMap(false, codegen.useDualFields()); return propertyMap; } diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/Global.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/Global.java index e6eba091c4..16ec29e1dc 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/Global.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/Global.java @@ -79,6 +79,7 @@ import jdk.nashorn.internal.runtime.linker.Bootstrap; import jdk.nashorn.internal.runtime.linker.InvokeByName; import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor; import jdk.nashorn.internal.runtime.regexp.RegExpResult; +import jdk.nashorn.internal.scripts.JD; import jdk.nashorn.internal.scripts.JO; import jdk.nashorn.tools.ShellFunctions; @@ -718,7 +719,7 @@ public final class Global extends ScriptObject implements Scope { private static final MethodHandle LOAD = findOwnMH_S("load", Object.class, Object.class, Object.class); private static final MethodHandle LOAD_WITH_NEW_GLOBAL = findOwnMH_S("loadWithNewGlobal", Object.class, Object.class, Object[].class); private static final MethodHandle EXIT = findOwnMH_S("exit", Object.class, Object.class, Object.class); - private static final MethodHandle LEXICAL_SCOPE_FILTER = findOwnMH_S("lexicalScopeFilter", Object.class, Object.class); + private static final MethodHandle LEXICAL_SCOPE_FILTER = findOwnMH_S("lexicalScopeFilter", Object.class, Object.class); // initialized by nasgen private static PropertyMap $nasgenmap$; @@ -750,6 +751,11 @@ public final class Global extends ScriptObject implements Scope { return context; } + @Override + protected boolean useDualFields() { + return context.useDualFields(); + } + // performs initialization checks for Global constructor and returns the // PropertyMap, if everything is fine. private static PropertyMap checkAndGetMap(final Context context) { @@ -933,7 +939,7 @@ public final class Global extends ScriptObject implements Scope { * @return the new ScriptObject */ 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 { */ private static class LexicalScope extends ScriptObject { - LexicalScope(final ScriptObject proto) { - super(proto, PropertyMap.newMap()); + LexicalScope(final Global global) { + super(global, PropertyMap.newMap()); } @Override diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeJSAdapter.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeJSAdapter.java index c5ca91010f..c5ece8cd00 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeJSAdapter.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeJSAdapter.java @@ -160,7 +160,7 @@ public final class NativeJSAdapter extends ScriptObject { } private static ScriptObject wrapAdaptee(final ScriptObject adaptee) { - return new JO(adaptee, JO.getInitialMap()); + return new JO(adaptee); } @Override diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/JSONParser.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/JSONParser.java index 524381873f..5360226a39 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/JSONParser.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/JSONParser.java @@ -41,6 +41,7 @@ import jdk.nashorn.internal.runtime.Source; import jdk.nashorn.internal.runtime.SpillProperty; import jdk.nashorn.internal.runtime.arrays.ArrayData; import jdk.nashorn.internal.runtime.arrays.ArrayIndex; +import jdk.nashorn.internal.scripts.JD; import jdk.nashorn.internal.scripts.JO; import static jdk.nashorn.internal.parser.TokenType.STRING; @@ -54,11 +55,10 @@ public class JSONParser { final private String source; final private Global global; + final private boolean dualFields; final int length; int pos = 0; - private static PropertyMap EMPTY_MAP = PropertyMap.newMap(); - private static final int EOF = -1; private static final String TRUE = "true"; @@ -74,10 +74,11 @@ public class JSONParser { * @param source the source * @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.global = global; this.length = source.length(); + this.dualFields = dualFields; } /** @@ -180,7 +181,7 @@ public class JSONParser { } private Object parseObject() { - PropertyMap propertyMap = EMPTY_MAP; + PropertyMap propertyMap = dualFields ? JD.getInitialMap() : JO.getInitialMap(); ArrayData arrayData = ArrayData.EMPTY_ARRAY; final ArrayList values = new ArrayList<>(); int state = STATE_EMPTY; @@ -241,36 +242,45 @@ public class JSONParser { return newArrayData.set(index, value, false); } - private static PropertyMap addObjectProperty(final PropertyMap propertyMap, final List values, + private PropertyMap addObjectProperty(final PropertyMap propertyMap, final List values, final String id, final Object value) { final Property oldProperty = propertyMap.findProperty(id); 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) { 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 { 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; } private Object createObject(final PropertyMap propertyMap, final List 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()]; for (final Property property : propertyMap.getProperties()) { - if (property.getType() == Object.class) { + if (!dualFields || property.getType() == Object.class) { objectSpill[property.getSlot()] = values.get(property.getSlot()); } else { 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.setArray(arrayData); return object; diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/AccessorProperty.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/AccessorProperty.java index a9afeb93ab..6fde66b45f 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/AccessorProperty.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/AccessorProperty.java @@ -25,7 +25,6 @@ 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.createGetter; import static jdk.nashorn.internal.codegen.ObjectClassGenerator.createSetter; @@ -98,7 +97,7 @@ public class AccessorProperty extends Property { 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++) { final String fieldNamePrimitive = getFieldName(i, PRIMITIVE_FIELD_TYPE); final Class typeClass = PRIMITIVE_FIELD_TYPE.getTypeClass(); @@ -211,7 +210,7 @@ public class AccessorProperty extends Property { * @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) { - 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(); // we don't need to prep the setters these will never be invalidated as this is a nasgen @@ -221,18 +220,15 @@ public class AccessorProperty extends Property { final Class setterType = setter == null ? null : setter.type().parameterType(1); assert setterType == null || setterType == getterType; - if (OBJECT_FIELDS_ONLY) { - primitiveGetter = primitiveSetter = null; + + if (getterType == int.class || getterType == long.class) { + primitiveGetter = MH.asType(getter, Lookup.GET_PRIMITIVE_TYPE); + primitiveSetter = setter == null ? null : MH.asType(setter, Lookup.SET_PRIMITIVE_TYPE); + } else if (getterType == double.class) { + primitiveGetter = MH.asType(MH.filterReturnValue(getter, ObjectClassGenerator.PACK_DOUBLE), Lookup.GET_PRIMITIVE_TYPE); + primitiveSetter = setter == null ? null : MH.asType(MH.filterArguments(setter, 1, ObjectClassGenerator.UNPACK_DOUBLE), Lookup.SET_PRIMITIVE_TYPE); } else { - if (getterType == int.class || getterType == long.class) { - primitiveGetter = MH.asType(getter, Lookup.GET_PRIMITIVE_TYPE); - primitiveSetter = setter == null ? null : MH.asType(setter, Lookup.SET_PRIMITIVE_TYPE); - } else if (getterType == double.class) { - primitiveGetter = MH.asType(MH.filterReturnValue(getter, ObjectClassGenerator.PACK_DOUBLE), Lookup.GET_PRIMITIVE_TYPE); - primitiveSetter = setter == null ? null : MH.asType(MH.filterArguments(setter, 1, ObjectClassGenerator.UNPACK_DOUBLE), Lookup.SET_PRIMITIVE_TYPE); - } else { - primitiveGetter = primitiveSetter = null; - } + primitiveGetter = primitiveSetter = null; } assert primitiveGetter == null || primitiveGetter.type() == Lookup.GET_PRIMITIVE_TYPE : primitiveGetter + "!=" + Lookup.GET_PRIMITIVE_TYPE; @@ -241,7 +237,7 @@ public class AccessorProperty extends Property { 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; - setType(OBJECT_FIELDS_ONLY ? Object.class : getterType); + setType(getterType); } /** @@ -282,6 +278,9 @@ public class AccessorProperty extends Property { objectSetter = gs.objectSetters[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 { */ public AccessorProperty(final String key, final int flags, final Class structure, final int slot, final Class initialType) { 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 { * @param initialValue initial value */ 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) { invokeSetter(owner, ((Integer)initialValue).intValue()); } else if (initialValue instanceof Long) { @@ -363,7 +362,7 @@ public class AccessorProperty extends Property { * Initialize the type of a property */ 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 { @@ -670,7 +669,7 @@ public class AccessorProperty extends Property { @Override public final boolean canChangeType() { - if (OBJECT_FIELDS_ONLY) { + if (!hasDualFields()) { return false; } // Return true for currently undefined even if non-writable/configurable to allow initialization of ES6 CONST. diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/AllocationStrategy.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/AllocationStrategy.java index 395aee5ce4..f25b588448 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/AllocationStrategy.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/AllocationStrategy.java @@ -44,6 +44,9 @@ final public class AllocationStrategy implements Serializable { /** Number of fields in the allocated object */ private final int fieldCount; + /** Whether to use dual field representation */ + private final boolean dualFields; + /** Name of class where allocator function resides */ private transient String allocatorClassName; @@ -53,15 +56,17 @@ final public class AllocationStrategy implements Serializable { /** * Construct an allocation strategy with the given map and class name. * @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.dualFields = dualFields; } private String getAllocatorClassName() { if (allocatorClassName == null) { // 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; } diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/Context.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/Context.java index 8c265c8a7f..5c4bbd662f 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/Context.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/Context.java @@ -130,6 +130,23 @@ public final class Context { private static MethodHandles.Lookup LOOKUP = MethodHandles.lookup(); 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 * Currently we are conservative and associate the name of a builtin class with all @@ -434,7 +451,7 @@ public final class Context { * @param appLoader application class loader */ 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 { 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(); } @@ -575,6 +600,14 @@ public final class Context { 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 * @return the property map of the current global scope diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/JSONFunctions.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/JSONFunctions.java index 9622b59a17..d5f3af77e0 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/JSONFunctions.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/JSONFunctions.java @@ -72,7 +72,8 @@ public final class JSONFunctions { public static Object parse(final Object text, final Object reviver) { final String str = JSType.toString(text); 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; try { diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/JSType.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/JSType.java index c7eae4f5c3..31be7d28b9 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/JSType.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/JSType.java @@ -26,7 +26,6 @@ package jdk.nashorn.internal.runtime; 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.runtime.ECMAErrors.typeError; @@ -1972,10 +1971,6 @@ public enum JSType { * @return primive type or Object.class if not primitive */ public static Class unboxedFieldType(final Object o) { - if (OBJECT_FIELDS_ONLY) { - return Object.class; - } - if (o == null) { return Object.class; } else if (o.getClass() == Integer.class) { diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/Property.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/Property.java index 4225c25124..7d5442088c 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/Property.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/Property.java @@ -96,6 +96,9 @@ public abstract class Property implements Serializable { /** Is this property an ES6 lexical binding? */ 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. */ private final String key; @@ -286,7 +289,7 @@ public abstract class Property implements Serializable { * @return true if parameter */ public boolean isParameter() { - return (flags & IS_PARAMETER) == IS_PARAMETER; + return (flags & IS_PARAMETER) != 0; } /** @@ -294,7 +297,7 @@ public abstract class Property implements Serializable { * @return true if has arguments */ public boolean hasArguments() { - return (flags & HAS_ARGUMENTS) == HAS_ARGUMENTS; + return (flags & HAS_ARGUMENTS) != 0; } /** @@ -316,7 +319,7 @@ public abstract class Property implements Serializable { * @return true if this is a bound property */ public boolean isBound() { - return (flags & IS_BOUND) == IS_BOUND; + return (flags & IS_BOUND) != 0; } /** @@ -325,7 +328,7 @@ public abstract class Property implements Serializable { * @return true if this is a block-scoped variable */ public boolean needsDeclaration() { - return (flags & NEEDS_DECLARATION) == NEEDS_DECLARATION; + return (flags & NEEDS_DECLARATION) != 0; } /** @@ -345,16 +348,6 @@ public abstract class Property implements Serializable { 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 * @return property flags @@ -363,16 +356,6 @@ public abstract class Property implements Serializable { 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, * so any property change that results in a smaller flag set results in the @@ -715,7 +698,7 @@ public abstract class Property implements Serializable { * @return whether this property is a function declaration or not. */ 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 { * @return true if this property represents a lexical binding. */ 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; } } diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/PropertyMap.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/PropertyMap.java index db7c5c992c..fbc0dbbf47 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/PropertyMap.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/PropertyMap.java @@ -198,13 +198,22 @@ public final class PropertyMap implements Iterable, Serializable { 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 clazz) { + return new PropertyMap(EMPTY_HASHMAP, clazz.getName(), 0, 0, 0, false); + } + /** * Return a sharable empty map. * * @return New empty {@link PropertyMap}. */ public static PropertyMap newMap() { - return new PropertyMap(EMPTY_HASHMAP, JO.class.getName(), 0, 0, 0, false); + return newMap(JO.class); } /** diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/ScriptObject.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/ScriptObject.java index 284505a9ba..daf65c2364 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/ScriptObject.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/ScriptObject.java @@ -28,7 +28,6 @@ package jdk.nashorn.internal.runtime; import static jdk.nashorn.internal.codegen.CompilerConstants.staticCallNoLookup; import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCall; 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.runtime.ECMAErrors.referenceError; import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; @@ -146,12 +145,6 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable { /** Area for reference properties added to object after instantiation, see {@link AccessorProperty} */ 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. */ private ArrayData arrayData; @@ -171,12 +164,6 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable { /** Method handle for getting the array data */ 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 */ 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 { this(map); this.primitiveSpill = primitiveSpill; this.objectSpill = objectSpill; - assert primitiveSpill.length == objectSpill.length : " primitive spill pool size is not the same length as object spill pool size"; - this.spillLength = spillAllocationLength(primitiveSpill.length); + assert primitiveSpill == null || primitiveSpill.length == objectSpill.length : " primitive spill pool size is not the same length as object spill pool size"; } /** @@ -727,7 +713,7 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable { setArray(getArray().ensure(longIndex)); doesNotHaveEnsureDelete(longIndex, oldLength, false); } - setArray(getArray().set(index,value, false)); + setArray(getArray().set(index, value, false)); } private void checkIntegerKey(final String key) { @@ -976,10 +962,10 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable { * @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) { - final int slot = spillLength; - ensureSpillSize(spillLength); //arguments=slot0, caller=slot0 - objectSpill[slot] = new UserAccessorProperty.Accessors(getter, setter); final PropertyMap oldMap = getMap(); + final int slot = oldMap.getFreeSpillSlot(); + ensureSpillSize(slot); + objectSpill[slot] = new UserAccessorProperty.Accessors(getter, setter); Property newProperty; PropertyMap newMap; do { @@ -1006,19 +992,12 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable { final int slot = uc.getSlot(); 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 - if (gs == null) { - uc.setAccessors(this, getMap(), new UserAccessorProperty.Accessors(getter, setter)); - } else { - //reuse existing getter setter for speed - gs.set(getter, setter); - if (uc.getFlags() == propertyFlags) { - return oldProperty; - } - } + final UserAccessorProperty.Accessors gs = uc.getAccessors(this); //this crashes + assert gs != null; + //reuse existing getter setter for speed + gs.set(getter, setter); + if (uc.getFlags() == propertyFlags) { + return oldProperty; } newProperty = new UserAccessorProperty(uc.getKey(), propertyFlags, slot); } else { @@ -2053,8 +2032,6 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable { protoSwitchPoint = null; } - assert OBJECT_FIELDS_ONLY || guard != null : "we always need a map guard here"; - final GuardedInvocation inv = new GuardedInvocation(mh, guard, protoSwitchPoint, exception); return inv.addSwitchPoint(findBuiltinSwitchPoint(name)); } @@ -2531,13 +2508,14 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable { /** * Add a spill property for the given key. - * @param key Property key. - * @param propertyFlags Property flags. + * @param key Property key. + * @param flags Property flags. * @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 int fieldSlot = propertyMap.getFreeFieldSlot(); + final int propertyFlags = flags | (useDualFields() ? Property.DUAL_FIELDS : 0); Property property; if (fieldSlot > -1) { @@ -2562,7 +2540,7 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable { * @return Setter method handle. */ 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()); } /** @@ -2649,9 +2627,9 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable { final int spreadArgs = mh.type().parameterCount() - callSiteParamCount + 1; return MH.filterArguments( MH.asSpreader( - mh, - Object[].class, - spreadArgs), + mh, + Object[].class, + spreadArgs), callSiteParamCount - 1, MH.insertArguments( TRUNCATINGFILTER, @@ -3739,24 +3717,32 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable { 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) { - if (slot < spillLength) { + final int oldLength = objectSpill == null ? 0 : objectSpill.length; + if (slot < oldLength) { return this; } final int newLength = alignUp(slot + 1, SPILL_RATE); 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) { - System.arraycopy(objectSpill, 0, newObjectSpill, 0, spillLength); - if (!OBJECT_FIELDS_ONLY) { - System.arraycopy(primitiveSpill, 0, newPrimitiveSpill, 0, spillLength); + System.arraycopy(objectSpill, 0, newObjectSpill, 0, oldLength); + if (primitiveSpill != null && newPrimitiveSpill != null) { + System.arraycopy(primitiveSpill, 0, newPrimitiveSpill, 0, oldLength); } } this.primitiveSpill = newPrimitiveSpill; this.objectSpill = newObjectSpill; - this.spillLength = newLength; return this; } diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/SetMethodCreator.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/SetMethodCreator.java index 90bbf43c8a..4fe1b4f548 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/SetMethodCreator.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/SetMethodCreator.java @@ -232,14 +232,18 @@ final class SetMethodCreator { } 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) { - 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) { return getMap().addProperty(property); } + + private static int getFlags(final ScriptObject scriptObject) { + return scriptObject.useDualFields() ? Property.DUAL_FIELDS : 0; + } } diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/SpillProperty.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/SpillProperty.java index 856504a3b0..9019b5609f 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/SpillProperty.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/SpillProperty.java @@ -25,7 +25,6 @@ package jdk.nashorn.internal.runtime; -import static jdk.nashorn.internal.codegen.ObjectClassGenerator.OBJECT_FIELDS_ONLY; import static jdk.nashorn.internal.lookup.Lookup.MH; import java.lang.invoke.MethodHandle; @@ -139,11 +138,11 @@ public class SpillProperty extends AccessorProperty { } } - private static MethodHandle primitiveGetter(final int slot) { - return OBJECT_FIELDS_ONLY ? null : Accessors.getCached(slot, true, true); + private static MethodHandle primitiveGetter(final int slot, final int flags) { + return (flags & DUAL_FIELDS) == DUAL_FIELDS ? Accessors.getCached(slot, true, true) : null; } - private static MethodHandle primitiveSetter(final int slot) { - return OBJECT_FIELDS_ONLY ? null : Accessors.getCached(slot, true, false); + private static MethodHandle primitiveSetter(final int slot, final int flags) { + return (flags & DUAL_FIELDS) == DUAL_FIELDS ? Accessors.getCached(slot, true, false) : null; } private static MethodHandle objectGetter(final int slot) { return Accessors.getCached(slot, false, true); @@ -160,8 +159,7 @@ public class SpillProperty extends AccessorProperty { * @param slot spill slot */ public SpillProperty(final String key, final int flags, final int slot) { - super(key, flags, slot, primitiveGetter(slot), primitiveSetter(slot), objectGetter(slot), objectSetter(slot)); - assert !OBJECT_FIELDS_ONLY || getLocalType() == Object.class; + super(key, flags, slot, primitiveGetter(slot, flags), primitiveSetter(slot, flags), objectGetter(slot), objectSetter(slot)); } /** @@ -173,7 +171,7 @@ public class SpillProperty extends AccessorProperty { */ public SpillProperty(final String key, final int flags, final int slot, final Class initialType) { 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) { @@ -216,8 +214,8 @@ public class SpillProperty extends AccessorProperty { @Override void initMethodHandles(final Class structure) { final int slot = getSlot(); - primitiveGetter = primitiveGetter(slot); - primitiveSetter = primitiveSetter(slot); + primitiveGetter = primitiveGetter(slot, getFlags()); + primitiveSetter = primitiveSetter(slot, getFlags()); objectGetter = objectGetter(slot); objectSetter = objectSetter(slot); } diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/StructureLoader.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/StructureLoader.java index 41201cde84..8324730a26 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/StructureLoader.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/StructureLoader.java @@ -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.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 jdk.nashorn.internal.codegen.ObjectClassGenerator; @@ -36,7 +37,8 @@ import jdk.nashorn.internal.codegen.ObjectClassGenerator; * Responsible for on the fly construction of structure classes. */ 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. @@ -45,14 +47,39 @@ final class StructureLoader extends NashornLoader { 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) { - return name.startsWith(JS_OBJECT_PREFIX_EXTERNAL); + return isDualFieldStructure(name) || isSingleFieldStructure(name); } @Override protected Class findClass(final String name) throws ClassNotFoundException { - if (isStructureClass(name)) { - return generateClass(name, name.substring(JS_OBJECT_PREFIX_EXTERNAL.length())); + if (isDualFieldStructure(name)) { + 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); } @@ -63,10 +90,10 @@ final class StructureLoader extends NashornLoader { * @param descriptor Layout descriptor. * @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 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))); } } diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/Bootstrap.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/Bootstrap.java index b00d3ec1f8..9cdf6d5c35 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/Bootstrap.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/Bootstrap.java @@ -74,17 +74,16 @@ public final class Bootstrap { * 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 * 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. - * Therefore the relink threshold is set to 16 for dual fields (now the default). - * This doesn't seem to have any other negative performance implication. + * changes swapping out an entire property map and making a map guard fail. + * Since we need to set this value statically it must work with possibly changing + * 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 * megamorphic behavior */ - private static final int NASHORN_DEFAULT_UNSTABLE_RELINK_THRESHOLD = - ObjectClassGenerator.OBJECT_FIELDS_ONLY ? - 8 : - 16; + private static final int NASHORN_DEFAULT_UNSTABLE_RELINK_THRESHOLD = 16; // do not create me!! private Bootstrap() { diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/NashornGuards.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/NashornGuards.java index dac36d4afb..ca7034d139 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/NashornGuards.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/NashornGuards.java @@ -33,7 +33,6 @@ import java.lang.ref.WeakReference; import jdk.internal.dynalink.CallSiteDescriptor; import jdk.internal.dynalink.linker.LinkRequest; import jdk.nashorn.api.scripting.JSObject; -import jdk.nashorn.internal.codegen.ObjectClassGenerator; import jdk.nashorn.internal.objects.Global; import jdk.nashorn.internal.runtime.Property; import jdk.nashorn.internal.runtime.PropertyMap; @@ -123,7 +122,7 @@ public final class NashornGuards { */ static boolean needsGuard(final Property property, final CallSiteDescriptor desc) { return property == null || property.isConfigurable() - || property.isBound() || !ObjectClassGenerator.OBJECT_FIELDS_ONLY + || property.isBound() || property.hasDualFields() || !NashornCallSiteDescriptor.isFastScope(desc) || property.canChangeType(); } diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/scripts/JD.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/scripts/JD.java new file mode 100644 index 0000000000..11d6cacc4b --- /dev/null +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/scripts/JD.java @@ -0,0 +1,88 @@ +/* + * 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); + } +} + diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/scripts/JO.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/scripts/JO.java index 2388a9781b..1f9aa7ca12 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/scripts/JO.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/scripts/JO.java @@ -1,5 +1,5 @@ /* - * 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. * * This code is free software; you can redistribute it and/or modify it @@ -29,11 +29,11 @@ import jdk.nashorn.internal.runtime.PropertyMap; import jdk.nashorn.internal.runtime.ScriptObject; /** - * Empty object class. + * Empty object class for object-only fields. */ 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. @@ -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 map the property map */ - public JO(final ScriptObject proto, final PropertyMap map) { - super(proto, map); + public JO(final ScriptObject proto) { + super(proto, getInitialMap()); } /** @@ -86,3 +85,4 @@ public class JO extends ScriptObject { return new JO(map); } } + diff --git a/nashorn/test/script/nosecurity/JDK-8067215.js b/nashorn/test/script/nosecurity/JDK-8067215.js new file mode 100644 index 0000000000..12877acbf4 --- /dev/null +++ b/nashorn/test/script/nosecurity/JDK-8067215.js @@ -0,0 +1,104 @@ +/* + * 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); -- GitLab