Fri, 10 Apr 2015 14:18:31 +0200
8067215: Disable dual fields when not using optimistic types
Reviewed-by: attila, lagergren
1.1 --- a/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/PrototypeGenerator.java Thu Apr 16 17:31:32 2015 +0200 1.2 +++ b/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/PrototypeGenerator.java Fri Apr 10 14:18:31 2015 +0200 1.3 @@ -124,8 +124,6 @@ 1.4 if (memberCount > 0) { 1.5 // call "super(map$)" 1.6 mi.getStatic(className, PROPERTYMAP_FIELD_NAME, PROPERTYMAP_DESC); 1.7 - // make sure we use duplicated PropertyMap so that original map 1.8 - // stays intact and so can be used for many global. 1.9 mi.invokeSpecial(PROTOTYPEOBJECT_TYPE, INIT, SCRIPTOBJECT_INIT_DESC); 1.10 // initialize Function type fields 1.11 initFunctionFields(mi);
2.1 --- a/docs/DEVELOPER_README Thu Apr 16 17:31:32 2015 +0200 2.2 +++ b/docs/DEVELOPER_README Fri Apr 10 14:18:31 2015 +0200 2.3 @@ -63,16 +63,19 @@ 2.4 See the description of the codegen logger below. 2.5 2.6 2.7 -SYSTEM PROPERTY: -Dnashorn.fields.objects 2.8 +SYSTEM PROPERTY: -Dnashorn.fields.objects, -Dnashorn.fields.dual 2.9 2.10 -When this property is true, Nashorn will only use object fields for 2.11 -AccessorProperties. This means that primitive values must be boxed 2.12 -when stored in a field, which is significantly slower than using 2.13 -primitive fields. 2.14 +When the nashorn.fields.objects property is true, Nashorn will always 2.15 +use object fields for AccessorProperties, requiring boxing for all 2.16 +primitive property values. When nashorn.fields.dual is set, Nashorn 2.17 +will always use dual long/object fields, which allows primitives to be 2.18 +stored without boxing. When neither system property is set, Nashorn 2.19 +chooses a setting depending on the optimistic types setting (dual 2.20 +fields when optimistic types are enabled, object-only fields otherwise). 2.21 2.22 -By default, Nashorn uses dual object and long fields. Ints are 2.23 -represented as the 32 low bits of the long fields. Doubles are 2.24 -represented as the doubleToLongBits of their value. This way a 2.25 +With dual fields, Nashorn uses long fields to store primitive values. 2.26 +Ints are represented as the 32 low bits of the long fields. Doubles 2.27 +are represented as the doubleToLongBits of their value. This way a 2.28 single field can be used for all primitive types. Packing and 2.29 unpacking doubles to their bit representation is intrinsified by 2.30 the JVM and extremely fast.
3.1 --- a/src/jdk/nashorn/internal/codegen/CodeGenerator.java Thu Apr 16 17:31:32 2015 +0200 3.2 +++ b/src/jdk/nashorn/internal/codegen/CodeGenerator.java Fri Apr 10 14:18:31 2015 +0200 3.3 @@ -43,7 +43,6 @@ 3.4 import static jdk.nashorn.internal.codegen.CompilerConstants.staticCallNoLookup; 3.5 import static jdk.nashorn.internal.codegen.CompilerConstants.typeDescriptor; 3.6 import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup; 3.7 -import static jdk.nashorn.internal.codegen.ObjectClassGenerator.OBJECT_FIELDS_ONLY; 3.8 import static jdk.nashorn.internal.ir.Symbol.HAS_SLOT; 3.9 import static jdk.nashorn.internal.ir.Symbol.IS_INTERNAL; 3.10 import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT; 3.11 @@ -320,6 +319,14 @@ 3.12 } 3.13 3.14 /** 3.15 + * Are we using dual primitive/object field representation? 3.16 + * @return true if using dual field representation, false for object-only fields 3.17 + */ 3.18 + boolean useDualFields() { 3.19 + return compiler.getContext().useDualFields(); 3.20 + } 3.21 + 3.22 + /** 3.23 * Load an identity node 3.24 * 3.25 * @param identNode an identity node to load 3.26 @@ -1909,10 +1916,10 @@ 3.27 //this symbol will be put fielded, we can't initialize it as undefined with a known type 3.28 @Override 3.29 public Class<?> getValueType() { 3.30 - if (OBJECT_FIELDS_ONLY || value == null || paramType == null) { 3.31 + if (!useDualFields() || value == null || paramType == null || paramType.isBoolean()) { 3.32 return Object.class; 3.33 } 3.34 - return paramType.isBoolean() ? Object.class : paramType.getTypeClass(); 3.35 + return paramType.getTypeClass(); 3.36 } 3.37 }); 3.38 } 3.39 @@ -2568,7 +2575,7 @@ 3.40 3.41 //for literals, a value of null means object type, i.e. the value null or getter setter function 3.42 //(I think) 3.43 - final Class<?> valueType = (OBJECT_FIELDS_ONLY || value == null || value.getType().isBoolean()) ? Object.class : value.getType().getTypeClass(); 3.44 + final Class<?> valueType = (!useDualFields() || value == null || value.getType().isBoolean()) ? Object.class : value.getType().getTypeClass(); 3.45 tuples.add(new MapTuple<Expression>(key, symbol, Type.typeFor(valueType), value) { 3.46 @Override 3.47 public Class<?> getValueType() {
4.1 --- a/src/jdk/nashorn/internal/codegen/CompilerConstants.java Thu Apr 16 17:31:32 2015 +0200 4.2 +++ b/src/jdk/nashorn/internal/codegen/CompilerConstants.java Fri Apr 10 14:18:31 2015 +0200 4.3 @@ -149,8 +149,11 @@ 4.4 /** Arguments parameter in scope object constructors; in slot 3 when present */ 4.5 INIT_ARGUMENTS(null, 3), 4.6 4.7 - /** prefix for all ScriptObject subclasses with fields, @see ObjectGenerator */ 4.8 - JS_OBJECT_PREFIX("JO"), 4.9 + /** prefix for all ScriptObject subclasses with dual object/primitive fields, see {@link ObjectClassGenerator} */ 4.10 + JS_OBJECT_DUAL_FIELD_PREFIX("JD"), 4.11 + 4.12 + /** prefix for all ScriptObject subclasses with object fields only, see {@link ObjectClassGenerator} */ 4.13 + JS_OBJECT_SINGLE_FIELD_PREFIX("JO"), 4.14 4.15 /** name for allocate method in JO objects */ 4.16 ALLOCATE("allocate"),
5.1 --- a/src/jdk/nashorn/internal/codegen/FieldObjectCreator.java Thu Apr 16 17:31:32 2015 +0200 5.2 +++ b/src/jdk/nashorn/internal/codegen/FieldObjectCreator.java Fri Apr 10 14:18:31 2015 +0200 5.3 @@ -151,7 +151,7 @@ 5.4 @Override 5.5 protected PropertyMap makeMap() { 5.6 assert propertyMap == null : "property map already initialized"; 5.7 - propertyMap = newMapCreator(fieldObjectClass).makeFieldMap(hasArguments(), fieldCount, paddedFieldCount, evalCode); 5.8 + propertyMap = newMapCreator(fieldObjectClass).makeFieldMap(hasArguments(), codegen.useDualFields(), fieldCount, paddedFieldCount, evalCode); 5.9 return propertyMap; 5.10 } 5.11 5.12 @@ -166,7 +166,7 @@ 5.13 private void putField(final MethodEmitter method, final String key, final int fieldIndex, final MapTuple<T> tuple) { 5.14 method.dup(); 5.15 5.16 - final Type fieldType = tuple.isPrimitive() ? PRIMITIVE_FIELD_TYPE : Type.OBJECT; 5.17 + final Type fieldType = codegen.useDualFields() && tuple.isPrimitive() ? PRIMITIVE_FIELD_TYPE : Type.OBJECT; 5.18 final String fieldClass = getClassName(); 5.19 final String fieldName = getFieldName(fieldIndex, fieldType); 5.20 final String fieldDesc = typeDescriptor(fieldType.getTypeClass()); 5.21 @@ -202,8 +202,8 @@ 5.22 */ 5.23 private void findClass() { 5.24 fieldObjectClassName = isScope() ? 5.25 - ObjectClassGenerator.getClassName(fieldCount, paramCount) : 5.26 - ObjectClassGenerator.getClassName(paddedFieldCount); 5.27 + ObjectClassGenerator.getClassName(fieldCount, paramCount, codegen.useDualFields()) : 5.28 + ObjectClassGenerator.getClassName(paddedFieldCount, codegen.useDualFields()); 5.29 5.30 try { 5.31 this.fieldObjectClass = Context.forStructureClass(Compiler.binaryName(fieldObjectClassName));
6.1 --- a/src/jdk/nashorn/internal/codegen/FindScopeDepths.java Thu Apr 16 17:31:32 2015 +0200 6.2 +++ b/src/jdk/nashorn/internal/codegen/FindScopeDepths.java Fri Apr 10 14:18:31 2015 +0200 6.3 @@ -207,7 +207,7 @@ 6.4 final RecompilableScriptFunctionData data = new RecompilableScriptFunctionData( 6.5 newFunctionNode, 6.6 compiler.getCodeInstaller(), 6.7 - ObjectClassGenerator.createAllocationStrategy(newFunctionNode.getThisProperties()), 6.8 + ObjectClassGenerator.createAllocationStrategy(newFunctionNode.getThisProperties(), compiler.getContext().useDualFields()), 6.9 nestedFunctions, 6.10 externalSymbolDepths.get(fnId), 6.11 internalSymbols.get(fnId),
7.1 --- a/src/jdk/nashorn/internal/codegen/MapCreator.java Thu Apr 16 17:31:32 2015 +0200 7.2 +++ b/src/jdk/nashorn/internal/codegen/MapCreator.java Fri Apr 10 14:18:31 2015 +0200 7.3 @@ -68,17 +68,17 @@ 7.4 * @param evalCode is this property map created for 'eval' code? 7.5 * @return New map populated with accessor properties. 7.6 */ 7.7 - PropertyMap makeFieldMap(final boolean hasArguments, final int fieldCount, final int fieldMaximum, final boolean evalCode) { 7.8 + PropertyMap makeFieldMap(final boolean hasArguments, final boolean dualFields, final int fieldCount, final int fieldMaximum, final boolean evalCode) { 7.9 final List<Property> properties = new ArrayList<>(); 7.10 assert tuples != null; 7.11 7.12 for (final MapTuple<T> tuple : tuples) { 7.13 final String key = tuple.key; 7.14 final Symbol symbol = tuple.symbol; 7.15 - final Class<?> initialType = tuple.getValueType(); 7.16 + final Class<?> initialType = dualFields ? tuple.getValueType() : Object.class; 7.17 7.18 if (symbol != null && !isValidArrayIndex(getArrayIndex(key))) { 7.19 - final int flags = getPropertyFlags(symbol, hasArguments, evalCode); 7.20 + final int flags = getPropertyFlags(symbol, hasArguments, evalCode, dualFields); 7.21 final Property property = new AccessorProperty( 7.22 key, 7.23 flags, 7.24 @@ -92,7 +92,7 @@ 7.25 return PropertyMap.newMap(properties, structure.getName(), fieldCount, fieldMaximum, 0); 7.26 } 7.27 7.28 - PropertyMap makeSpillMap(final boolean hasArguments) { 7.29 + PropertyMap makeSpillMap(final boolean hasArguments, final boolean dualFields) { 7.30 final List<Property> properties = new ArrayList<>(); 7.31 int spillIndex = 0; 7.32 assert tuples != null; 7.33 @@ -100,10 +100,10 @@ 7.34 for (final MapTuple<T> tuple : tuples) { 7.35 final String key = tuple.key; 7.36 final Symbol symbol = tuple.symbol; 7.37 - final Class<?> initialType = tuple.getValueType(); 7.38 + final Class<?> initialType = dualFields ? tuple.getValueType() : Object.class; 7.39 7.40 if (symbol != null && !isValidArrayIndex(getArrayIndex(key))) { 7.41 - final int flags = getPropertyFlags(symbol, hasArguments, false); 7.42 + final int flags = getPropertyFlags(symbol, hasArguments, false, dualFields); 7.43 properties.add( 7.44 new SpillProperty( 7.45 key, 7.46 @@ -124,7 +124,7 @@ 7.47 * 7.48 * @return flags to use for fields 7.49 */ 7.50 - static int getPropertyFlags(final Symbol symbol, final boolean hasArguments, final boolean evalCode) { 7.51 + static int getPropertyFlags(final Symbol symbol, final boolean hasArguments, final boolean evalCode, final boolean dualFields) { 7.52 int flags = 0; 7.53 7.54 if (symbol.isParam()) { 7.55 @@ -162,6 +162,10 @@ 7.56 flags |= Property.NEEDS_DECLARATION; 7.57 } 7.58 7.59 + if (dualFields) { 7.60 + flags |= Property.DUAL_FIELDS; 7.61 + } 7.62 + 7.63 return flags; 7.64 } 7.65 }
8.1 --- a/src/jdk/nashorn/internal/codegen/MapTuple.java Thu Apr 16 17:31:32 2015 +0200 8.2 +++ b/src/jdk/nashorn/internal/codegen/MapTuple.java Fri Apr 10 14:18:31 2015 +0200 8.3 @@ -25,8 +25,6 @@ 8.4 8.5 package jdk.nashorn.internal.codegen; 8.6 8.7 -import static jdk.nashorn.internal.codegen.ObjectClassGenerator.OBJECT_FIELDS_ONLY; 8.8 - 8.9 import jdk.nashorn.internal.codegen.types.Type; 8.10 import jdk.nashorn.internal.ir.Symbol; 8.11 8.12 @@ -52,11 +50,11 @@ 8.13 } 8.14 8.15 public Class<?> getValueType() { 8.16 - return OBJECT_FIELDS_ONLY ? Object.class : null; //until proven otherwise we are undefined, see NASHORN-592 int.class; 8.17 + return null; //until proven otherwise we are undefined, see NASHORN-592 int.class; 8.18 } 8.19 8.20 boolean isPrimitive() { 8.21 - return !OBJECT_FIELDS_ONLY && getValueType().isPrimitive() && getValueType() != boolean.class; 8.22 + return getValueType() != null && getValueType().isPrimitive() && getValueType() != boolean.class; 8.23 } 8.24 8.25 @Override
9.1 --- a/src/jdk/nashorn/internal/codegen/ObjectClassGenerator.java Thu Apr 16 17:31:32 2015 +0200 9.2 +++ b/src/jdk/nashorn/internal/codegen/ObjectClassGenerator.java Fri Apr 10 14:18:31 2015 +0200 9.3 @@ -31,7 +31,8 @@ 9.4 import static jdk.nashorn.internal.codegen.CompilerConstants.INIT_MAP; 9.5 import static jdk.nashorn.internal.codegen.CompilerConstants.INIT_SCOPE; 9.6 import static jdk.nashorn.internal.codegen.CompilerConstants.JAVA_THIS; 9.7 -import static jdk.nashorn.internal.codegen.CompilerConstants.JS_OBJECT_PREFIX; 9.8 +import static jdk.nashorn.internal.codegen.CompilerConstants.JS_OBJECT_DUAL_FIELD_PREFIX; 9.9 +import static jdk.nashorn.internal.codegen.CompilerConstants.JS_OBJECT_SINGLE_FIELD_PREFIX; 9.10 import static jdk.nashorn.internal.codegen.CompilerConstants.className; 9.11 import static jdk.nashorn.internal.codegen.CompilerConstants.constructorNoLookup; 9.12 import static jdk.nashorn.internal.lookup.Lookup.MH; 9.13 @@ -99,18 +100,10 @@ 9.14 */ 9.15 private final DebugLogger log; 9.16 9.17 - /** 9.18 - * Should the runtime only use java.lang.Object slots for fields? If this is false, the representation 9.19 - * will be a primitive 64-bit long value used for all primitives and a java.lang.Object for references. 9.20 - * This introduces a larger number of method handles in the system, as we need to have different getters 9.21 - * and setters for the different fields. 9.22 - * 9.23 - * This is engineered to plug into the TaggedArray implementation, when it's done. 9.24 - */ 9.25 - public static final boolean OBJECT_FIELDS_ONLY = Options.getBooleanProperty("nashorn.fields.objects"); 9.26 - 9.27 - /** The field types in the system */ 9.28 - private static final List<Type> FIELD_TYPES = new LinkedList<>(); 9.29 + /** Field types for object-only fields */ 9.30 + private static final Type[] FIELD_TYPES_OBJECT = new Type[] { Type.OBJECT }; 9.31 + /** Field types for dual primitive/object fields */ 9.32 + private static final Type[] FIELD_TYPES_DUAL = new Type[] { Type.LONG, Type.OBJECT }; 9.33 9.34 /** What type is the primitive type in dual representation */ 9.35 public static final Type PRIMITIVE_FIELD_TYPE = Type.LONG; 9.36 @@ -118,33 +111,27 @@ 9.37 private static final MethodHandle GET_DIFFERENT = findOwnMH("getDifferent", Object.class, Object.class, Class.class, MethodHandle.class, MethodHandle.class, int.class); 9.38 private static final MethodHandle GET_DIFFERENT_UNDEFINED = findOwnMH("getDifferentUndefined", Object.class, int.class); 9.39 9.40 - /** 9.41 - * The list of field types that we support - one type creates one field. This is currently either 9.42 - * LONG + OBJECT or just OBJECT for classic mode. 9.43 - */ 9.44 - static { 9.45 - if (!OBJECT_FIELDS_ONLY) { 9.46 - FIELD_TYPES.add(PRIMITIVE_FIELD_TYPE); 9.47 - } 9.48 - FIELD_TYPES.add(Type.OBJECT); 9.49 - } 9.50 private static boolean initialized = false; 9.51 9.52 /** The context */ 9.53 private final Context context; 9.54 9.55 + private final boolean dualFields; 9.56 + 9.57 /** 9.58 * Constructor 9.59 * 9.60 * @param context a context 9.61 + * @param dualFields whether to use dual fields representation 9.62 */ 9.63 - public ObjectClassGenerator(final Context context) { 9.64 + public ObjectClassGenerator(final Context context, final boolean dualFields) { 9.65 this.context = context; 9.66 + this.dualFields = dualFields; 9.67 assert context != null; 9.68 this.log = initLogger(context); 9.69 if (!initialized) { 9.70 initialized = true; 9.71 - if (OBJECT_FIELDS_ONLY) { 9.72 + if (!dualFields) { 9.73 log.warning("Running with object fields only - this is a deprecated configuration."); 9.74 } 9.75 } 9.76 @@ -176,16 +163,30 @@ 9.77 throw new AssertionError("cannot pack" + n); 9.78 } 9.79 9.80 + private static String getPrefixName(final boolean dualFields) { 9.81 + return dualFields ? JS_OBJECT_DUAL_FIELD_PREFIX.symbolName() : JS_OBJECT_SINGLE_FIELD_PREFIX.symbolName(); 9.82 + } 9.83 + 9.84 + private static String getPrefixName(final String className) { 9.85 + if (className.startsWith(JS_OBJECT_DUAL_FIELD_PREFIX.symbolName())) { 9.86 + return getPrefixName(true); 9.87 + } else if (className.startsWith(JS_OBJECT_SINGLE_FIELD_PREFIX.symbolName())) { 9.88 + return getPrefixName(false); 9.89 + } 9.90 + throw new AssertionError("Not a structure class: " + className); 9.91 + } 9.92 + 9.93 /** 9.94 * Returns the class name for JavaScript objects with fieldCount fields. 9.95 * 9.96 * @param fieldCount Number of fields to allocate. 9.97 - * 9.98 + * @param dualFields whether to use dual fields representation 9.99 * @return The class name. 9.100 */ 9.101 - public static String getClassName(final int fieldCount) { 9.102 - return fieldCount != 0 ? SCRIPTS_PACKAGE + '/' + JS_OBJECT_PREFIX.symbolName() + fieldCount : 9.103 - SCRIPTS_PACKAGE + '/' + JS_OBJECT_PREFIX.symbolName(); 9.104 + public static String getClassName(final int fieldCount, final boolean dualFields) { 9.105 + final String prefix = getPrefixName(dualFields); 9.106 + return fieldCount != 0 ? SCRIPTS_PACKAGE + '/' + prefix + fieldCount : 9.107 + SCRIPTS_PACKAGE + '/' + prefix; 9.108 } 9.109 9.110 /** 9.111 @@ -194,22 +195,23 @@ 9.112 * 9.113 * @param fieldCount Number of fields to allocate. 9.114 * @param paramCount Number of parameters to allocate 9.115 - * 9.116 + * @param dualFields whether to use dual fields representation 9.117 * @return The class name. 9.118 */ 9.119 - public static String getClassName(final int fieldCount, final int paramCount) { 9.120 - return SCRIPTS_PACKAGE + '/' + JS_OBJECT_PREFIX.symbolName() + fieldCount + SCOPE_MARKER + paramCount; 9.121 + public static String getClassName(final int fieldCount, final int paramCount, final boolean dualFields) { 9.122 + return SCRIPTS_PACKAGE + '/' + getPrefixName(dualFields) + fieldCount + SCOPE_MARKER + paramCount; 9.123 } 9.124 9.125 /** 9.126 * Returns the number of fields in the JavaScript scope class. Its name had to be generated using either 9.127 - * {@link #getClassName(int)} or {@link #getClassName(int, int)}. 9.128 + * {@link #getClassName(int, boolean)} or {@link #getClassName(int, int, boolean)}. 9.129 * @param clazz the JavaScript scope class. 9.130 * @return the number of fields in the scope class. 9.131 */ 9.132 public static int getFieldCount(final Class<?> clazz) { 9.133 final String name = clazz.getSimpleName(); 9.134 - final String prefix = JS_OBJECT_PREFIX.symbolName(); 9.135 + final String prefix = getPrefixName(name); 9.136 + 9.137 if (prefix.equals(name)) { 9.138 return 0; 9.139 } 9.140 @@ -238,8 +240,8 @@ 9.141 * @param className name of class 9.142 * @param fieldNames fields to initialize to undefined, where applicable 9.143 */ 9.144 - private static void initializeToUndefined(final MethodEmitter init, final String className, final List<String> fieldNames) { 9.145 - if (!OBJECT_FIELDS_ONLY) { 9.146 + private void initializeToUndefined(final MethodEmitter init, final String className, final List<String> fieldNames) { 9.147 + if (dualFields) { 9.148 // no need to initialize anything to undefined in the dual field world 9.149 // - then we have a constant getter for undefined for any unknown type 9.150 return; 9.151 @@ -292,7 +294,7 @@ 9.152 * @return Byte codes for generated class. 9.153 */ 9.154 public byte[] generate(final int fieldCount) { 9.155 - final String className = getClassName(fieldCount); 9.156 + final String className = getClassName(fieldCount, dualFields); 9.157 final String superName = className(ScriptObject.class); 9.158 final ClassEmitter classEmitter = newClassEmitter(className, superName); 9.159 9.160 @@ -322,7 +324,7 @@ 9.161 * @return Byte codes for generated class. 9.162 */ 9.163 public byte[] generate(final int fieldCount, final int paramCount) { 9.164 - final String className = getClassName(fieldCount, paramCount); 9.165 + final String className = getClassName(fieldCount, paramCount, dualFields); 9.166 final String superName = className(FunctionScope.class); 9.167 final ClassEmitter classEmitter = newClassEmitter(className, superName); 9.168 final List<String> initFields = addFields(classEmitter, fieldCount); 9.169 @@ -353,11 +355,11 @@ 9.170 * 9.171 * @return List fields that need to be initialized. 9.172 */ 9.173 - private static List<String> addFields(final ClassEmitter classEmitter, final int fieldCount) { 9.174 + private List<String> addFields(final ClassEmitter classEmitter, final int fieldCount) { 9.175 final List<String> initFields = new LinkedList<>(); 9.176 - 9.177 + final Type[] fieldTypes = dualFields ? FIELD_TYPES_DUAL : FIELD_TYPES_OBJECT; 9.178 for (int i = 0; i < fieldCount; i++) { 9.179 - for (final Type type : FIELD_TYPES) { 9.180 + for (final Type type : fieldTypes) { 9.181 final String fieldName = getFieldName(i, type); 9.182 classEmitter.field(fieldName, type.getTypeClass()); 9.183 9.184 @@ -533,13 +535,10 @@ 9.185 private static MethodHandle getterForType(final Class<?> forType, final MethodHandle primitiveGetter, final MethodHandle objectGetter) { 9.186 switch (getAccessorTypeIndex(forType)) { 9.187 case TYPE_INT_INDEX: 9.188 - assert !OBJECT_FIELDS_ONLY : "this can only happen with dual fields"; 9.189 return MH.explicitCastArguments(primitiveGetter, primitiveGetter.type().changeReturnType(int.class)); 9.190 case TYPE_LONG_INDEX: 9.191 - assert !OBJECT_FIELDS_ONLY : "this can only happen with dual fields"; 9.192 return primitiveGetter; 9.193 case TYPE_DOUBLE_INDEX: 9.194 - assert !OBJECT_FIELDS_ONLY : "this can only happen with dual fields"; 9.195 return MH.filterReturnValue(primitiveGetter, UNPACK_DOUBLE); 9.196 case TYPE_OBJECT_INDEX: 9.197 return objectGetter; 9.198 @@ -557,7 +556,7 @@ 9.199 final boolean isPrimitiveStorage = forType != null && forType.isPrimitive(); 9.200 9.201 //which is the primordial getter 9.202 - final MethodHandle getter = OBJECT_FIELDS_ONLY ? objectGetter : isPrimitiveStorage ? primitiveGetter : objectGetter; 9.203 + final MethodHandle getter = primitiveGetter == null ? objectGetter : isPrimitiveStorage ? primitiveGetter : objectGetter; 9.204 9.205 if (forType == null) { 9.206 if (isOptimistic) { 9.207 @@ -580,8 +579,7 @@ 9.208 return MH.dropArguments(GET_UNDEFINED.get(ti), 0, Object.class); 9.209 } 9.210 9.211 - assert forType != null; 9.212 - assert !OBJECT_FIELDS_ONLY || forType == Object.class : forType; 9.213 + assert primitiveGetter != null || forType == Object.class : forType; 9.214 9.215 if (isOptimistic) { 9.216 if (fti < ti) { 9.217 @@ -635,8 +633,6 @@ 9.218 return tgetter; 9.219 } 9.220 9.221 - assert !OBJECT_FIELDS_ONLY; 9.222 - //final MethodType pmt = primitiveGetter.type(); 9.223 assert primitiveGetter != null; 9.224 final MethodType tgetterType = tgetter.type(); 9.225 switch (fti) { 9.226 @@ -727,7 +723,7 @@ 9.227 final int fti = getAccessorTypeIndex(forType); 9.228 final int ti = getAccessorTypeIndex(type); 9.229 9.230 - if (fti == TYPE_OBJECT_INDEX || OBJECT_FIELDS_ONLY) { 9.231 + if (fti == TYPE_OBJECT_INDEX || primitiveSetter == null) { 9.232 if (ti == TYPE_OBJECT_INDEX) { 9.233 return objectSetter; 9.234 } 9.235 @@ -735,8 +731,6 @@ 9.236 return MH.asType(objectSetter, objectSetter.type().changeParameterType(1, type)); 9.237 } 9.238 9.239 - assert !OBJECT_FIELDS_ONLY; 9.240 - 9.241 final MethodType pmt = primitiveSetter.type(); 9.242 9.243 switch (fti) { 9.244 @@ -832,8 +826,8 @@ 9.245 * @param thisProperties number of properties assigned to "this" 9.246 * @return the allocation strategy 9.247 */ 9.248 - static AllocationStrategy createAllocationStrategy(final int thisProperties) { 9.249 + static AllocationStrategy createAllocationStrategy(final int thisProperties, final boolean dualFields) { 9.250 final int paddedFieldCount = getPaddedFieldCount(thisProperties); 9.251 - return new AllocationStrategy(paddedFieldCount); 9.252 + return new AllocationStrategy(paddedFieldCount, dualFields); 9.253 } 9.254 }
10.1 --- a/src/jdk/nashorn/internal/codegen/ObjectCreator.java Thu Apr 16 17:31:32 2015 +0200 10.2 +++ b/src/jdk/nashorn/internal/codegen/ObjectCreator.java Fri Apr 10 14:18:31 2015 +0200 10.3 @@ -134,7 +134,7 @@ 10.4 10.5 MethodEmitter loadTuple(final MethodEmitter method, final MapTuple<T> tuple, final boolean pack) { 10.6 loadValue(tuple.value, tuple.type); 10.7 - if (pack && tuple.isPrimitive()) { 10.8 + if (pack && codegen.useDualFields() && tuple.isPrimitive()) { 10.9 method.pack(); 10.10 } else { 10.11 method.convert(Type.OBJECT);
11.1 --- a/src/jdk/nashorn/internal/codegen/SpillObjectCreator.java Thu Apr 16 17:31:32 2015 +0200 11.2 +++ b/src/jdk/nashorn/internal/codegen/SpillObjectCreator.java Fri Apr 10 14:18:31 2015 +0200 11.3 @@ -27,7 +27,6 @@ 11.4 11.5 import static jdk.nashorn.internal.codegen.CompilerConstants.constructorNoLookup; 11.6 import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup; 11.7 -import static jdk.nashorn.internal.codegen.ObjectClassGenerator.OBJECT_FIELDS_ONLY; 11.8 11.9 import java.util.LinkedHashSet; 11.10 import java.util.List; 11.11 @@ -42,6 +41,7 @@ 11.12 import jdk.nashorn.internal.runtime.ScriptRuntime; 11.13 import jdk.nashorn.internal.runtime.arrays.ArrayData; 11.14 import jdk.nashorn.internal.runtime.arrays.ArrayIndex; 11.15 +import jdk.nashorn.internal.scripts.JD; 11.16 import jdk.nashorn.internal.scripts.JO; 11.17 11.18 /** 11.19 @@ -65,10 +65,13 @@ 11.20 assert !isScope() : "spill scope objects are not currently supported"; 11.21 11.22 final int length = tuples.size(); 11.23 - final long[] jpresetValues = new long[ScriptObject.spillAllocationLength(length)]; 11.24 - final Object[] opresetValues = new Object[ScriptObject.spillAllocationLength(length)]; 11.25 + final boolean dualFields = codegen.useDualFields(); 11.26 + final int spillLength = ScriptObject.spillAllocationLength(length); 11.27 + final long[] jpresetValues = dualFields ? new long[spillLength] : null; 11.28 + final Object[] opresetValues = new Object[spillLength]; 11.29 final Set<Integer> postsetValues = new LinkedHashSet<>(); 11.30 final int callSiteFlags = codegen.getCallSiteFlags(); 11.31 + final Class<?> objectClass = dualFields ? JD.class : JO.class; 11.32 ArrayData arrayData = ArrayData.allocate(ScriptRuntime.EMPTY_ARRAY); 11.33 11.34 // Compute constant property values 11.35 @@ -88,9 +91,9 @@ 11.36 final Property property = propertyMap.findProperty(key); 11.37 if (property != null) { 11.38 // normal property key 11.39 - property.setType(JSType.unboxedFieldType(constantValue)); 11.40 + property.setType(dualFields ? JSType.unboxedFieldType(constantValue) : Object.class); 11.41 final int slot = property.getSlot(); 11.42 - if (!OBJECT_FIELDS_ONLY && constantValue instanceof Number) { 11.43 + if (dualFields && constantValue instanceof Number) { 11.44 jpresetValues[slot] = ObjectClassGenerator.pack((Number)constantValue); 11.45 } else { 11.46 opresetValues[slot] = constantValue; 11.47 @@ -130,28 +133,32 @@ 11.48 //assert postsetValues.isEmpty() : "test me " + postsetValues; 11.49 11.50 // create object and invoke constructor 11.51 - method._new(JO.class).dup(); 11.52 + method._new(objectClass).dup(); 11.53 codegen.loadConstant(propertyMap); 11.54 11.55 //load primitive values to j spill array 11.56 - codegen.loadConstant(jpresetValues); 11.57 - for (final int i : postsetValues) { 11.58 - final MapTuple<Expression> tuple = tuples.get(i); 11.59 - final Property property = propertyMap.findProperty(tuple.key); 11.60 - if (property != null && tuple.isPrimitive()) { 11.61 - method.dup(); 11.62 - method.load(property.getSlot()); 11.63 - loadTuple(method, tuple); 11.64 - method.arraystore(); 11.65 + if (dualFields) { 11.66 + codegen.loadConstant(jpresetValues); 11.67 + for (final int i : postsetValues) { 11.68 + final MapTuple<Expression> tuple = tuples.get(i); 11.69 + final Property property = propertyMap.findProperty(tuple.key); 11.70 + if (property != null && tuple.isPrimitive()) { 11.71 + method.dup(); 11.72 + method.load(property.getSlot()); 11.73 + loadTuple(method, tuple); 11.74 + method.arraystore(); 11.75 + } 11.76 } 11.77 + } else { 11.78 + method.loadNull(); 11.79 } 11.80 11.81 //load object values to o spill array 11.82 codegen.loadConstant(opresetValues); 11.83 for (final int i : postsetValues) { 11.84 - final MapTuple<Expression> tuple = tuples.get(i); 11.85 - final Property property = propertyMap.findProperty(tuple.key); 11.86 - if (property != null && !tuple.isPrimitive()) { 11.87 + final MapTuple<Expression> tuple = tuples.get(i); 11.88 + final Property property = propertyMap.findProperty(tuple.key); 11.89 + if (property != null && (!dualFields || !tuple.isPrimitive())) { 11.90 method.dup(); 11.91 method.load(property.getSlot()); 11.92 loadTuple(method, tuple); 11.93 @@ -160,7 +167,7 @@ 11.94 } 11.95 11.96 //instantiate the script object with spill objects 11.97 - method.invoke(constructorNoLookup(JO.class, PropertyMap.class, long[].class, Object[].class)); 11.98 + method.invoke(constructorNoLookup(objectClass, PropertyMap.class, long[].class, Object[].class)); 11.99 11.100 // Set prefix array data if any 11.101 if (arrayData.length() > 0) { 11.102 @@ -171,8 +178,8 @@ 11.103 11.104 // set postfix 11.105 for (final int i : postsetValues) { 11.106 - final MapTuple<Expression> tuple = tuples.get(i); 11.107 - final Property property = propertyMap.findProperty(tuple.key); 11.108 + final MapTuple<Expression> tuple = tuples.get(i); 11.109 + final Property property = propertyMap.findProperty(tuple.key); 11.110 if (property == null) { 11.111 final int index = ArrayIndex.getArrayIndex(tuple.key); 11.112 assert ArrayIndex.isValidArrayIndex(index); 11.113 @@ -188,7 +195,9 @@ 11.114 @Override 11.115 protected PropertyMap makeMap() { 11.116 assert propertyMap == null : "property map already initialized"; 11.117 - propertyMap = new MapCreator<>(JO.class, tuples).makeSpillMap(false); 11.118 + final boolean dualFields = codegen.useDualFields(); 11.119 + final Class<? extends ScriptObject> clazz = dualFields ? JD.class : JO.class; 11.120 + propertyMap = new MapCreator<>(clazz, tuples).makeSpillMap(false, codegen.useDualFields()); 11.121 return propertyMap; 11.122 } 11.123
12.1 --- a/src/jdk/nashorn/internal/objects/Global.java Thu Apr 16 17:31:32 2015 +0200 12.2 +++ b/src/jdk/nashorn/internal/objects/Global.java Fri Apr 10 14:18:31 2015 +0200 12.3 @@ -79,6 +79,7 @@ 12.4 import jdk.nashorn.internal.runtime.linker.InvokeByName; 12.5 import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor; 12.6 import jdk.nashorn.internal.runtime.regexp.RegExpResult; 12.7 +import jdk.nashorn.internal.scripts.JD; 12.8 import jdk.nashorn.internal.scripts.JO; 12.9 import jdk.nashorn.tools.ShellFunctions; 12.10 12.11 @@ -718,7 +719,7 @@ 12.12 private static final MethodHandle LOAD = findOwnMH_S("load", Object.class, Object.class, Object.class); 12.13 private static final MethodHandle LOAD_WITH_NEW_GLOBAL = findOwnMH_S("loadWithNewGlobal", Object.class, Object.class, Object[].class); 12.14 private static final MethodHandle EXIT = findOwnMH_S("exit", Object.class, Object.class, Object.class); 12.15 - private static final MethodHandle LEXICAL_SCOPE_FILTER = findOwnMH_S("lexicalScopeFilter", Object.class, Object.class); 12.16 + private static final MethodHandle LEXICAL_SCOPE_FILTER = findOwnMH_S("lexicalScopeFilter", Object.class, Object.class); 12.17 12.18 // initialized by nasgen 12.19 private static PropertyMap $nasgenmap$; 12.20 @@ -750,6 +751,11 @@ 12.21 return context; 12.22 } 12.23 12.24 + @Override 12.25 + protected boolean useDualFields() { 12.26 + return context.useDualFields(); 12.27 + } 12.28 + 12.29 // performs initialization checks for Global constructor and returns the 12.30 // PropertyMap, if everything is fine. 12.31 private static PropertyMap checkAndGetMap(final Context context) { 12.32 @@ -934,7 +940,7 @@ 12.33 * @return the new ScriptObject 12.34 */ 12.35 public ScriptObject newObject() { 12.36 - return new JO(getObjectPrototype(), JO.getInitialMap()); 12.37 + return useDualFields() ? new JD(getObjectPrototype()) : new JO(getObjectPrototype()); 12.38 } 12.39 12.40 /** 12.41 @@ -2746,8 +2752,8 @@ 12.42 */ 12.43 private static class LexicalScope extends ScriptObject { 12.44 12.45 - LexicalScope(final ScriptObject proto) { 12.46 - super(proto, PropertyMap.newMap()); 12.47 + LexicalScope(final Global global) { 12.48 + super(global, PropertyMap.newMap()); 12.49 } 12.50 12.51 @Override
13.1 --- a/src/jdk/nashorn/internal/objects/NativeJSAdapter.java Thu Apr 16 17:31:32 2015 +0200 13.2 +++ b/src/jdk/nashorn/internal/objects/NativeJSAdapter.java Fri Apr 10 14:18:31 2015 +0200 13.3 @@ -160,7 +160,7 @@ 13.4 } 13.5 13.6 private static ScriptObject wrapAdaptee(final ScriptObject adaptee) { 13.7 - return new JO(adaptee, JO.getInitialMap()); 13.8 + return new JO(adaptee); 13.9 } 13.10 13.11 @Override
14.1 --- a/src/jdk/nashorn/internal/parser/JSONParser.java Thu Apr 16 17:31:32 2015 +0200 14.2 +++ b/src/jdk/nashorn/internal/parser/JSONParser.java Fri Apr 10 14:18:31 2015 +0200 14.3 @@ -41,6 +41,7 @@ 14.4 import jdk.nashorn.internal.runtime.SpillProperty; 14.5 import jdk.nashorn.internal.runtime.arrays.ArrayData; 14.6 import jdk.nashorn.internal.runtime.arrays.ArrayIndex; 14.7 +import jdk.nashorn.internal.scripts.JD; 14.8 import jdk.nashorn.internal.scripts.JO; 14.9 14.10 import static jdk.nashorn.internal.parser.TokenType.STRING; 14.11 @@ -54,11 +55,10 @@ 14.12 14.13 final private String source; 14.14 final private Global global; 14.15 + final private boolean dualFields; 14.16 final int length; 14.17 int pos = 0; 14.18 14.19 - private static PropertyMap EMPTY_MAP = PropertyMap.newMap(); 14.20 - 14.21 private static final int EOF = -1; 14.22 14.23 private static final String TRUE = "true"; 14.24 @@ -74,10 +74,11 @@ 14.25 * @param source the source 14.26 * @param global the global object 14.27 */ 14.28 - public JSONParser(final String source, final Global global ) { 14.29 + public JSONParser(final String source, final Global global, final boolean dualFields) { 14.30 this.source = source; 14.31 this.global = global; 14.32 this.length = source.length(); 14.33 + this.dualFields = dualFields; 14.34 } 14.35 14.36 /** 14.37 @@ -180,7 +181,7 @@ 14.38 } 14.39 14.40 private Object parseObject() { 14.41 - PropertyMap propertyMap = EMPTY_MAP; 14.42 + PropertyMap propertyMap = dualFields ? JD.getInitialMap() : JO.getInitialMap(); 14.43 ArrayData arrayData = ArrayData.EMPTY_ARRAY; 14.44 final ArrayList<Object> values = new ArrayList<>(); 14.45 int state = STATE_EMPTY; 14.46 @@ -241,36 +242,45 @@ 14.47 return newArrayData.set(index, value, false); 14.48 } 14.49 14.50 - private static PropertyMap addObjectProperty(final PropertyMap propertyMap, final List<Object> values, 14.51 + private PropertyMap addObjectProperty(final PropertyMap propertyMap, final List<Object> values, 14.52 final String id, final Object value) { 14.53 final Property oldProperty = propertyMap.findProperty(id); 14.54 final PropertyMap newMap; 14.55 - final Class<?> type = ObjectClassGenerator.OBJECT_FIELDS_ONLY ? Object.class : getType(value); 14.56 + final Class<?> type; 14.57 + final int flags; 14.58 + if (dualFields) { 14.59 + type = getType(value); 14.60 + flags = Property.DUAL_FIELDS; 14.61 + } else { 14.62 + type = Object.class; 14.63 + flags = 0; 14.64 + } 14.65 14.66 if (oldProperty != null) { 14.67 values.set(oldProperty.getSlot(), value); 14.68 - newMap = propertyMap.replaceProperty(oldProperty, new SpillProperty(id, 0, oldProperty.getSlot(), type));; 14.69 + newMap = propertyMap.replaceProperty(oldProperty, new SpillProperty(id, flags, oldProperty.getSlot(), type));; 14.70 } else { 14.71 values.add(value); 14.72 - newMap = propertyMap.addProperty(new SpillProperty(id, 0, propertyMap.size(), type)); 14.73 + newMap = propertyMap.addProperty(new SpillProperty(id, flags, propertyMap.size(), type)); 14.74 } 14.75 14.76 return newMap; 14.77 } 14.78 14.79 private Object createObject(final PropertyMap propertyMap, final List<Object> values, final ArrayData arrayData) { 14.80 - final long[] primitiveSpill = new long[values.size()]; 14.81 + final long[] primitiveSpill = dualFields ? new long[values.size()] : null; 14.82 final Object[] objectSpill = new Object[values.size()]; 14.83 14.84 for (final Property property : propertyMap.getProperties()) { 14.85 - if (property.getType() == Object.class) { 14.86 + if (!dualFields || property.getType() == Object.class) { 14.87 objectSpill[property.getSlot()] = values.get(property.getSlot()); 14.88 } else { 14.89 primitiveSpill[property.getSlot()] = ObjectClassGenerator.pack((Number) values.get(property.getSlot())); 14.90 } 14.91 } 14.92 14.93 - final ScriptObject object = new JO(propertyMap, primitiveSpill, objectSpill); 14.94 + final ScriptObject object = dualFields ? 14.95 + new JD(propertyMap, primitiveSpill, objectSpill) : new JO(propertyMap, null, objectSpill); 14.96 object.setInitialProto(global.getObjectPrototype()); 14.97 object.setArray(arrayData); 14.98 return object;
15.1 --- a/src/jdk/nashorn/internal/runtime/AccessorProperty.java Thu Apr 16 17:31:32 2015 +0200 15.2 +++ b/src/jdk/nashorn/internal/runtime/AccessorProperty.java Fri Apr 10 14:18:31 2015 +0200 15.3 @@ -25,7 +25,6 @@ 15.4 15.5 package jdk.nashorn.internal.runtime; 15.6 15.7 -import static jdk.nashorn.internal.codegen.ObjectClassGenerator.OBJECT_FIELDS_ONLY; 15.8 import static jdk.nashorn.internal.codegen.ObjectClassGenerator.PRIMITIVE_FIELD_TYPE; 15.9 import static jdk.nashorn.internal.codegen.ObjectClassGenerator.createGetter; 15.10 import static jdk.nashorn.internal.codegen.ObjectClassGenerator.createSetter; 15.11 @@ -98,7 +97,7 @@ 15.12 objectSetters[i] = MH.asType(MH.setter(LOOKUP, structure, fieldName, typeClass), Lookup.SET_OBJECT_TYPE); 15.13 } 15.14 15.15 - if (!OBJECT_FIELDS_ONLY) { 15.16 + if (!StructureLoader.isSingleFieldStructure(structure.getName())) { 15.17 for (int i = 0; i < fieldCount; i++) { 15.18 final String fieldNamePrimitive = getFieldName(i, PRIMITIVE_FIELD_TYPE); 15.19 final Class<?> typeClass = PRIMITIVE_FIELD_TYPE.getTypeClass(); 15.20 @@ -211,7 +210,7 @@ 15.21 * @param setter the property setter or null if non writable, non configurable 15.22 */ 15.23 private AccessorProperty(final String key, final int flags, final int slot, final MethodHandle getter, final MethodHandle setter) { 15.24 - super(key, flags | IS_BUILTIN | (getter.type().returnType().isPrimitive() ? IS_NASGEN_PRIMITIVE : 0), slot); 15.25 + super(key, flags | IS_BUILTIN | DUAL_FIELDS | (getter.type().returnType().isPrimitive() ? IS_NASGEN_PRIMITIVE : 0), slot); 15.26 assert !isSpill(); 15.27 15.28 // we don't need to prep the setters these will never be invalidated as this is a nasgen 15.29 @@ -221,18 +220,15 @@ 15.30 final Class<?> setterType = setter == null ? null : setter.type().parameterType(1); 15.31 15.32 assert setterType == null || setterType == getterType; 15.33 - if (OBJECT_FIELDS_ONLY) { 15.34 + 15.35 + if (getterType == int.class || getterType == long.class) { 15.36 + primitiveGetter = MH.asType(getter, Lookup.GET_PRIMITIVE_TYPE); 15.37 + primitiveSetter = setter == null ? null : MH.asType(setter, Lookup.SET_PRIMITIVE_TYPE); 15.38 + } else if (getterType == double.class) { 15.39 + primitiveGetter = MH.asType(MH.filterReturnValue(getter, ObjectClassGenerator.PACK_DOUBLE), Lookup.GET_PRIMITIVE_TYPE); 15.40 + primitiveSetter = setter == null ? null : MH.asType(MH.filterArguments(setter, 1, ObjectClassGenerator.UNPACK_DOUBLE), Lookup.SET_PRIMITIVE_TYPE); 15.41 + } else { 15.42 primitiveGetter = primitiveSetter = null; 15.43 - } else { 15.44 - if (getterType == int.class || getterType == long.class) { 15.45 - primitiveGetter = MH.asType(getter, Lookup.GET_PRIMITIVE_TYPE); 15.46 - primitiveSetter = setter == null ? null : MH.asType(setter, Lookup.SET_PRIMITIVE_TYPE); 15.47 - } else if (getterType == double.class) { 15.48 - primitiveGetter = MH.asType(MH.filterReturnValue(getter, ObjectClassGenerator.PACK_DOUBLE), Lookup.GET_PRIMITIVE_TYPE); 15.49 - primitiveSetter = setter == null ? null : MH.asType(MH.filterArguments(setter, 1, ObjectClassGenerator.UNPACK_DOUBLE), Lookup.SET_PRIMITIVE_TYPE); 15.50 - } else { 15.51 - primitiveGetter = primitiveSetter = null; 15.52 - } 15.53 } 15.54 15.55 assert primitiveGetter == null || primitiveGetter.type() == Lookup.GET_PRIMITIVE_TYPE : primitiveGetter + "!=" + Lookup.GET_PRIMITIVE_TYPE; 15.56 @@ -241,7 +237,7 @@ 15.57 objectGetter = getter.type() != Lookup.GET_OBJECT_TYPE ? MH.asType(getter, Lookup.GET_OBJECT_TYPE) : getter; 15.58 objectSetter = setter != null && setter.type() != Lookup.SET_OBJECT_TYPE ? MH.asType(setter, Lookup.SET_OBJECT_TYPE) : setter; 15.59 15.60 - setType(OBJECT_FIELDS_ONLY ? Object.class : getterType); 15.61 + setType(getterType); 15.62 } 15.63 15.64 /** 15.65 @@ -282,6 +278,9 @@ 15.66 objectSetter = gs.objectSetters[slot]; 15.67 primitiveSetter = gs.primitiveSetters[slot]; 15.68 } 15.69 + 15.70 + // Always use dual fields except for single field structures 15.71 + assert hasDualFields() != StructureLoader.isSingleFieldStructure(structure.getName()); 15.72 } 15.73 15.74 /** 15.75 @@ -310,7 +309,7 @@ 15.76 */ 15.77 public AccessorProperty(final String key, final int flags, final Class<?> structure, final int slot, final Class<?> initialType) { 15.78 this(key, flags, structure, slot); 15.79 - setType(OBJECT_FIELDS_ONLY ? Object.class : initialType); 15.80 + setType(hasDualFields() ? initialType : Object.class); 15.81 } 15.82 15.83 /** 15.84 @@ -347,7 +346,7 @@ 15.85 * @param initialValue initial value 15.86 */ 15.87 protected final void setInitialValue(final ScriptObject owner, final Object initialValue) { 15.88 - setType(JSType.unboxedFieldType(initialValue)); 15.89 + setType(hasDualFields() ? JSType.unboxedFieldType(initialValue) : Object.class); 15.90 if (initialValue instanceof Integer) { 15.91 invokeSetter(owner, ((Integer)initialValue).intValue()); 15.92 } else if (initialValue instanceof Long) { 15.93 @@ -363,7 +362,7 @@ 15.94 * Initialize the type of a property 15.95 */ 15.96 protected final void initializeType() { 15.97 - setType(OBJECT_FIELDS_ONLY ? Object.class : null); 15.98 + setType(!hasDualFields() ? Object.class : null); 15.99 } 15.100 15.101 private void readObject(final ObjectInputStream s) throws IOException, ClassNotFoundException { 15.102 @@ -670,7 +669,7 @@ 15.103 15.104 @Override 15.105 public final boolean canChangeType() { 15.106 - if (OBJECT_FIELDS_ONLY) { 15.107 + if (!hasDualFields()) { 15.108 return false; 15.109 } 15.110 // Return true for currently undefined even if non-writable/configurable to allow initialization of ES6 CONST.
16.1 --- a/src/jdk/nashorn/internal/runtime/AllocationStrategy.java Thu Apr 16 17:31:32 2015 +0200 16.2 +++ b/src/jdk/nashorn/internal/runtime/AllocationStrategy.java Fri Apr 10 14:18:31 2015 +0200 16.3 @@ -44,6 +44,9 @@ 16.4 /** Number of fields in the allocated object */ 16.5 private final int fieldCount; 16.6 16.7 + /** Whether to use dual field representation */ 16.8 + private final boolean dualFields; 16.9 + 16.10 /** Name of class where allocator function resides */ 16.11 private transient String allocatorClassName; 16.12 16.13 @@ -53,15 +56,17 @@ 16.14 /** 16.15 * Construct an allocation strategy with the given map and class name. 16.16 * @param fieldCount number of fields in the allocated object 16.17 + * @param dualFields whether to use dual field representation 16.18 */ 16.19 - public AllocationStrategy(final int fieldCount) { 16.20 + public AllocationStrategy(final int fieldCount, final boolean dualFields) { 16.21 this.fieldCount = fieldCount; 16.22 + this.dualFields = dualFields; 16.23 } 16.24 16.25 private String getAllocatorClassName() { 16.26 if (allocatorClassName == null) { 16.27 // These classes get loaded, so an interned variant of their name is most likely around anyway. 16.28 - allocatorClassName = Compiler.binaryName(ObjectClassGenerator.getClassName(fieldCount)).intern(); 16.29 + allocatorClassName = Compiler.binaryName(ObjectClassGenerator.getClassName(fieldCount, dualFields)).intern(); 16.30 } 16.31 return allocatorClassName; 16.32 }
17.1 --- a/src/jdk/nashorn/internal/runtime/Context.java Thu Apr 16 17:31:32 2015 +0200 17.2 +++ b/src/jdk/nashorn/internal/runtime/Context.java Fri Apr 10 14:18:31 2015 +0200 17.3 @@ -131,6 +131,23 @@ 17.4 private static MethodType CREATE_PROGRAM_FUNCTION_TYPE = MethodType.methodType(ScriptFunction.class, ScriptObject.class); 17.5 17.6 /** 17.7 + * Should scripts use only object slots for fields, or dual long/object slots? The default 17.8 + * behaviour is to couple this to optimistic types, using dual representation if optimistic types are enabled 17.9 + * and single field representation otherwise. This can be overridden by setting either the "nashorn.fields.objects" 17.10 + * or "nashorn.fields.dual" system property. 17.11 + */ 17.12 + private final FieldMode fieldMode; 17.13 + 17.14 + private static enum FieldMode { 17.15 + /** Value for automatic field representation depending on optimistic types setting */ 17.16 + AUTO, 17.17 + /** Value for object field representation regardless of optimistic types setting */ 17.18 + OBJECTS, 17.19 + /** Value for dual primitive/object field representation regardless of optimistic types setting */ 17.20 + DUAL 17.21 + } 17.22 + 17.23 + /** 17.24 * Keeps track of which builtin prototypes and properties have been relinked 17.25 * Currently we are conservative and associate the name of a builtin class with all 17.26 * its properties, so it's enough to invalidate a property to break all assumptions 17.27 @@ -434,7 +451,7 @@ 17.28 * @param appLoader application class loader 17.29 */ 17.30 public Context(final Options options, final ErrorManager errors, final ClassLoader appLoader) { 17.31 - this(options, errors, appLoader, (ClassFilter)null); 17.32 + this(options, errors, appLoader, null); 17.33 } 17.34 17.35 /** 17.36 @@ -522,6 +539,14 @@ 17.37 getErr().println("nashorn full version " + Version.fullVersion()); 17.38 } 17.39 17.40 + if (Options.getBooleanProperty("nashorn.fields.dual")) { 17.41 + fieldMode = FieldMode.DUAL; 17.42 + } else if (Options.getBooleanProperty("nashorn.fields.objects")) { 17.43 + fieldMode = FieldMode.OBJECTS; 17.44 + } else { 17.45 + fieldMode = FieldMode.AUTO; 17.46 + } 17.47 + 17.48 initLoggers(); 17.49 } 17.50 17.51 @@ -576,6 +601,14 @@ 17.52 } 17.53 17.54 /** 17.55 + * Should scripts compiled by this context use dual field representation? 17.56 + * @return true if using dual fields, false for object-only fields 17.57 + */ 17.58 + public boolean useDualFields() { 17.59 + return fieldMode == FieldMode.DUAL || (fieldMode == FieldMode.AUTO && env._optimistic_types); 17.60 + } 17.61 + 17.62 + /** 17.63 * Get the PropertyMap of the current global scope 17.64 * @return the property map of the current global scope 17.65 */
18.1 --- a/src/jdk/nashorn/internal/runtime/JSONFunctions.java Thu Apr 16 17:31:32 2015 +0200 18.2 +++ b/src/jdk/nashorn/internal/runtime/JSONFunctions.java Fri Apr 10 14:18:31 2015 +0200 18.3 @@ -72,7 +72,8 @@ 18.4 public static Object parse(final Object text, final Object reviver) { 18.5 final String str = JSType.toString(text); 18.6 final Global global = Context.getGlobal(); 18.7 - final JSONParser parser = new JSONParser(str, global); 18.8 + final boolean dualFields = ((ScriptObject) global).useDualFields(); 18.9 + final JSONParser parser = new JSONParser(str, global, dualFields); 18.10 final Object value; 18.11 18.12 try {
19.1 --- a/src/jdk/nashorn/internal/runtime/JSType.java Thu Apr 16 17:31:32 2015 +0200 19.2 +++ b/src/jdk/nashorn/internal/runtime/JSType.java Fri Apr 10 14:18:31 2015 +0200 19.3 @@ -26,7 +26,6 @@ 19.4 package jdk.nashorn.internal.runtime; 19.5 19.6 import static jdk.nashorn.internal.codegen.CompilerConstants.staticCall; 19.7 -import static jdk.nashorn.internal.codegen.ObjectClassGenerator.OBJECT_FIELDS_ONLY; 19.8 import static jdk.nashorn.internal.lookup.Lookup.MH; 19.9 import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; 19.10 19.11 @@ -1973,10 +1972,6 @@ 19.12 * @return primive type or Object.class if not primitive 19.13 */ 19.14 public static Class<?> unboxedFieldType(final Object o) { 19.15 - if (OBJECT_FIELDS_ONLY) { 19.16 - return Object.class; 19.17 - } 19.18 - 19.19 if (o == null) { 19.20 return Object.class; 19.21 } else if (o.getClass() == Integer.class) {
20.1 --- a/src/jdk/nashorn/internal/runtime/Property.java Thu Apr 16 17:31:32 2015 +0200 20.2 +++ b/src/jdk/nashorn/internal/runtime/Property.java Fri Apr 10 14:18:31 2015 +0200 20.3 @@ -96,6 +96,9 @@ 20.4 /** Is this property an ES6 lexical binding? */ 20.5 public static final int IS_LEXICAL_BINDING = 1 << 10; 20.6 20.7 + /** Does this property support dual field representation? */ 20.8 + public static final int DUAL_FIELDS = 1 << 11; 20.9 + 20.10 /** Property key. */ 20.11 private final String key; 20.12 20.13 @@ -286,7 +289,7 @@ 20.14 * @return true if parameter 20.15 */ 20.16 public boolean isParameter() { 20.17 - return (flags & IS_PARAMETER) == IS_PARAMETER; 20.18 + return (flags & IS_PARAMETER) != 0; 20.19 } 20.20 20.21 /** 20.22 @@ -294,7 +297,7 @@ 20.23 * @return true if has arguments 20.24 */ 20.25 public boolean hasArguments() { 20.26 - return (flags & HAS_ARGUMENTS) == HAS_ARGUMENTS; 20.27 + return (flags & HAS_ARGUMENTS) != 0; 20.28 } 20.29 20.30 /** 20.31 @@ -316,7 +319,7 @@ 20.32 * @return true if this is a bound property 20.33 */ 20.34 public boolean isBound() { 20.35 - return (flags & IS_BOUND) == IS_BOUND; 20.36 + return (flags & IS_BOUND) != 0; 20.37 } 20.38 20.39 /** 20.40 @@ -325,7 +328,7 @@ 20.41 * @return true if this is a block-scoped variable 20.42 */ 20.43 public boolean needsDeclaration() { 20.44 - return (flags & NEEDS_DECLARATION) == NEEDS_DECLARATION; 20.45 + return (flags & NEEDS_DECLARATION) != 0; 20.46 } 20.47 20.48 /** 20.49 @@ -346,16 +349,6 @@ 20.50 } 20.51 20.52 /** 20.53 - * Check if a flag is set for a property 20.54 - * @param property property 20.55 - * @param flag flag to check 20.56 - * @return true if flag is set 20.57 - */ 20.58 - public static boolean checkFlag(final Property property, final int flag) { 20.59 - return (property.getFlags() & flag) == flag; 20.60 - } 20.61 - 20.62 - /** 20.63 * Get the flags for this property 20.64 * @return property flags 20.65 */ 20.66 @@ -364,16 +357,6 @@ 20.67 } 20.68 20.69 /** 20.70 - * Get the modify flags for this property. The modify flags are the ECMA 8.6.1 20.71 - * flags that decide if the Property is writable, configurable and/or enumerable. 20.72 - * 20.73 - * @return modify flags for property 20.74 - */ 20.75 - public int getModifyFlags() { 20.76 - return flags & MODIFY_MASK; 20.77 - } 20.78 - 20.79 - /** 20.80 * Remove property flags from the property. Properties are immutable here, 20.81 * so any property change that results in a smaller flag set results in the 20.82 * property being cloned. Use only the return value 20.83 @@ -715,7 +698,7 @@ 20.84 * @return whether this property is a function declaration or not. 20.85 */ 20.86 public boolean isFunctionDeclaration() { 20.87 - return (flags & IS_FUNCTION_DECLARATION) == IS_FUNCTION_DECLARATION; 20.88 + return (flags & IS_FUNCTION_DECLARATION) != 0; 20.89 } 20.90 20.91 /** 20.92 @@ -723,6 +706,14 @@ 20.93 * @return true if this property represents a lexical binding. 20.94 */ 20.95 public boolean isLexicalBinding() { 20.96 - return (flags & IS_LEXICAL_BINDING) == IS_LEXICAL_BINDING; 20.97 + return (flags & IS_LEXICAL_BINDING) != 0; 20.98 + } 20.99 + 20.100 + /** 20.101 + * Does this property support dual fields for both primitive and object values? 20.102 + * @return true if supports dual fields 20.103 + */ 20.104 + public boolean hasDualFields() { 20.105 + return (flags & DUAL_FIELDS) != 0; 20.106 } 20.107 }
21.1 --- a/src/jdk/nashorn/internal/runtime/PropertyMap.java Thu Apr 16 17:31:32 2015 +0200 21.2 +++ b/src/jdk/nashorn/internal/runtime/PropertyMap.java Fri Apr 10 14:18:31 2015 +0200 21.3 @@ -199,12 +199,21 @@ 21.4 } 21.5 21.6 /** 21.7 + * Return a sharable empty map for the given object class. 21.8 + * @param clazz the base object class 21.9 + * @return New empty {@link PropertyMap}. 21.10 + */ 21.11 + public static PropertyMap newMap(final Class<? extends ScriptObject> clazz) { 21.12 + return new PropertyMap(EMPTY_HASHMAP, clazz.getName(), 0, 0, 0, false); 21.13 + } 21.14 + 21.15 + /** 21.16 * Return a sharable empty map. 21.17 * 21.18 * @return New empty {@link PropertyMap}. 21.19 */ 21.20 public static PropertyMap newMap() { 21.21 - return new PropertyMap(EMPTY_HASHMAP, JO.class.getName(), 0, 0, 0, false); 21.22 + return newMap(JO.class); 21.23 } 21.24 21.25 /**
22.1 --- a/src/jdk/nashorn/internal/runtime/ScriptObject.java Thu Apr 16 17:31:32 2015 +0200 22.2 +++ b/src/jdk/nashorn/internal/runtime/ScriptObject.java Fri Apr 10 14:18:31 2015 +0200 22.3 @@ -28,7 +28,6 @@ 22.4 import static jdk.nashorn.internal.codegen.CompilerConstants.staticCallNoLookup; 22.5 import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCall; 22.6 import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup; 22.7 -import static jdk.nashorn.internal.codegen.ObjectClassGenerator.OBJECT_FIELDS_ONLY; 22.8 import static jdk.nashorn.internal.lookup.Lookup.MH; 22.9 import static jdk.nashorn.internal.runtime.ECMAErrors.referenceError; 22.10 import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; 22.11 @@ -146,12 +145,6 @@ 22.12 /** Area for reference properties added to object after instantiation, see {@link AccessorProperty} */ 22.13 protected Object[] objectSpill; 22.14 22.15 - /** 22.16 - * Number of elements in the spill. This may be less than the spill array lengths, if not all of 22.17 - * the allocated memory is in use 22.18 - */ 22.19 - private int spillLength; 22.20 - 22.21 /** Indexed array data. */ 22.22 private ArrayData arrayData; 22.23 22.24 @@ -171,12 +164,6 @@ 22.25 /** Method handle for getting the array data */ 22.26 public static final Call GET_ARRAY = virtualCall(MethodHandles.lookup(), ScriptObject.class, "getArray", ArrayData.class); 22.27 22.28 - /** Method handle for getting the property map - debugging purposes */ 22.29 - public static final Call GET_MAP = virtualCall(MethodHandles.lookup(), ScriptObject.class, "getMap", PropertyMap.class); 22.30 - 22.31 - /** Method handle for setting the array data */ 22.32 - public static final Call SET_ARRAY = virtualCall(MethodHandles.lookup(), ScriptObject.class, "setArray", void.class, ArrayData.class); 22.33 - 22.34 /** Method handle for getting a function argument at a given index. Used from MapCreator */ 22.35 public static final Call GET_ARGUMENT = virtualCall(MethodHandles.lookup(), ScriptObject.class, "getArgument", Object.class, int.class); 22.36 22.37 @@ -259,8 +246,7 @@ 22.38 this(map); 22.39 this.primitiveSpill = primitiveSpill; 22.40 this.objectSpill = objectSpill; 22.41 - assert primitiveSpill.length == objectSpill.length : " primitive spill pool size is not the same length as object spill pool size"; 22.42 - this.spillLength = spillAllocationLength(primitiveSpill.length); 22.43 + assert primitiveSpill == null || primitiveSpill.length == objectSpill.length : " primitive spill pool size is not the same length as object spill pool size"; 22.44 } 22.45 22.46 /** 22.47 @@ -727,7 +713,7 @@ 22.48 setArray(getArray().ensure(longIndex)); 22.49 doesNotHaveEnsureDelete(longIndex, oldLength, false); 22.50 } 22.51 - setArray(getArray().set(index,value, false)); 22.52 + setArray(getArray().set(index, value, false)); 22.53 } 22.54 22.55 private void checkIntegerKey(final String key) { 22.56 @@ -976,10 +962,10 @@ 22.57 * @param setter setter for {@link UserAccessorProperty}, null if not present or N/A 22.58 */ 22.59 protected final void initUserAccessors(final String key, final int propertyFlags, final ScriptFunction getter, final ScriptFunction setter) { 22.60 - final int slot = spillLength; 22.61 - ensureSpillSize(spillLength); //arguments=slot0, caller=slot0 22.62 + final PropertyMap oldMap = getMap(); 22.63 + final int slot = oldMap.getFreeSpillSlot(); 22.64 + ensureSpillSize(slot); 22.65 objectSpill[slot] = new UserAccessorProperty.Accessors(getter, setter); 22.66 - final PropertyMap oldMap = getMap(); 22.67 Property newProperty; 22.68 PropertyMap newMap; 22.69 do { 22.70 @@ -1006,19 +992,12 @@ 22.71 final int slot = uc.getSlot(); 22.72 22.73 assert uc.getLocalType() == Object.class; 22.74 - if (slot >= spillLength) { 22.75 - uc.setAccessors(this, getMap(), new UserAccessorProperty.Accessors(getter, setter)); 22.76 - } else { 22.77 - final UserAccessorProperty.Accessors gs = uc.getAccessors(this); //this crashes 22.78 - if (gs == null) { 22.79 - uc.setAccessors(this, getMap(), new UserAccessorProperty.Accessors(getter, setter)); 22.80 - } else { 22.81 - //reuse existing getter setter for speed 22.82 - gs.set(getter, setter); 22.83 - if (uc.getFlags() == propertyFlags) { 22.84 - return oldProperty; 22.85 - } 22.86 - } 22.87 + final UserAccessorProperty.Accessors gs = uc.getAccessors(this); //this crashes 22.88 + assert gs != null; 22.89 + //reuse existing getter setter for speed 22.90 + gs.set(getter, setter); 22.91 + if (uc.getFlags() == propertyFlags) { 22.92 + return oldProperty; 22.93 } 22.94 newProperty = new UserAccessorProperty(uc.getKey(), propertyFlags, slot); 22.95 } else { 22.96 @@ -2053,8 +2032,6 @@ 22.97 protoSwitchPoint = null; 22.98 } 22.99 22.100 - assert OBJECT_FIELDS_ONLY || guard != null : "we always need a map guard here"; 22.101 - 22.102 final GuardedInvocation inv = new GuardedInvocation(mh, guard, protoSwitchPoint, exception); 22.103 return inv.addSwitchPoint(findBuiltinSwitchPoint(name)); 22.104 } 22.105 @@ -2531,13 +2508,14 @@ 22.106 22.107 /** 22.108 * Add a spill property for the given key. 22.109 - * @param key Property key. 22.110 - * @param propertyFlags Property flags. 22.111 + * @param key Property key. 22.112 + * @param flags Property flags. 22.113 * @return Added property. 22.114 */ 22.115 - private Property addSpillProperty(final String key, final int propertyFlags, final Object value, final boolean hasInitialValue) { 22.116 + private Property addSpillProperty(final String key, final int flags, final Object value, final boolean hasInitialValue) { 22.117 final PropertyMap propertyMap = getMap(); 22.118 final int fieldSlot = propertyMap.getFreeFieldSlot(); 22.119 + final int propertyFlags = flags | (useDualFields() ? Property.DUAL_FIELDS : 0); 22.120 22.121 Property property; 22.122 if (fieldSlot > -1) { 22.123 @@ -2562,7 +2540,7 @@ 22.124 * @return Setter method handle. 22.125 */ 22.126 MethodHandle addSpill(final Class<?> type, final String key) { 22.127 - return addSpillProperty(key, 0, null, false).getSetter(OBJECT_FIELDS_ONLY ? Object.class : type, getMap()); 22.128 + return addSpillProperty(key, 0, null, false).getSetter(type, getMap()); 22.129 } 22.130 22.131 /** 22.132 @@ -2649,9 +2627,9 @@ 22.133 final int spreadArgs = mh.type().parameterCount() - callSiteParamCount + 1; 22.134 return MH.filterArguments( 22.135 MH.asSpreader( 22.136 - mh, 22.137 - Object[].class, 22.138 - spreadArgs), 22.139 + mh, 22.140 + Object[].class, 22.141 + spreadArgs), 22.142 callSiteParamCount - 1, 22.143 MH.insertArguments( 22.144 TRUNCATINGFILTER, 22.145 @@ -3739,24 +3717,32 @@ 22.146 return uc; 22.147 } 22.148 22.149 + /** 22.150 + * Returns {@code true} if properties for this object should use dual field mode, {@code false} otherwise. 22.151 + * @return {@code true} if dual fields should be used. 22.152 + */ 22.153 + protected boolean useDualFields() { 22.154 + return !StructureLoader.isSingleFieldStructure(getClass().getName()); 22.155 + } 22.156 + 22.157 Object ensureSpillSize(final int slot) { 22.158 - if (slot < spillLength) { 22.159 + final int oldLength = objectSpill == null ? 0 : objectSpill.length; 22.160 + if (slot < oldLength) { 22.161 return this; 22.162 } 22.163 final int newLength = alignUp(slot + 1, SPILL_RATE); 22.164 final Object[] newObjectSpill = new Object[newLength]; 22.165 - final long[] newPrimitiveSpill = OBJECT_FIELDS_ONLY ? null : new long[newLength]; 22.166 + final long[] newPrimitiveSpill = useDualFields() ? new long[newLength] : null; 22.167 22.168 if (objectSpill != null) { 22.169 - System.arraycopy(objectSpill, 0, newObjectSpill, 0, spillLength); 22.170 - if (!OBJECT_FIELDS_ONLY) { 22.171 - System.arraycopy(primitiveSpill, 0, newPrimitiveSpill, 0, spillLength); 22.172 + System.arraycopy(objectSpill, 0, newObjectSpill, 0, oldLength); 22.173 + if (primitiveSpill != null && newPrimitiveSpill != null) { 22.174 + System.arraycopy(primitiveSpill, 0, newPrimitiveSpill, 0, oldLength); 22.175 } 22.176 } 22.177 22.178 this.primitiveSpill = newPrimitiveSpill; 22.179 this.objectSpill = newObjectSpill; 22.180 - this.spillLength = newLength; 22.181 22.182 return this; 22.183 }
23.1 --- a/src/jdk/nashorn/internal/runtime/SetMethodCreator.java Thu Apr 16 17:31:32 2015 +0200 23.2 +++ b/src/jdk/nashorn/internal/runtime/SetMethodCreator.java Fri Apr 10 14:18:31 2015 +0200 23.3 @@ -232,14 +232,18 @@ 23.4 } 23.5 23.6 private SetMethod createNewFieldSetter(final SwitchPoint builtinSwitchPoint) { 23.7 - return createNewSetter(new AccessorProperty(getName(), 0, sobj.getClass(), getMap().getFreeFieldSlot(), type), builtinSwitchPoint); 23.8 + return createNewSetter(new AccessorProperty(getName(), getFlags(sobj), sobj.getClass(), getMap().getFreeFieldSlot(), type), builtinSwitchPoint); 23.9 } 23.10 23.11 private SetMethod createNewSpillPropertySetter(final SwitchPoint builtinSwitchPoint) { 23.12 - return createNewSetter(new SpillProperty(getName(), 0, getMap().getFreeSpillSlot(), type), builtinSwitchPoint); 23.13 + return createNewSetter(new SpillProperty(getName(), getFlags(sobj), getMap().getFreeSpillSlot(), type), builtinSwitchPoint); 23.14 } 23.15 23.16 private PropertyMap getNewMap(final Property property) { 23.17 return getMap().addProperty(property); 23.18 } 23.19 + 23.20 + private static int getFlags(final ScriptObject scriptObject) { 23.21 + return scriptObject.useDualFields() ? Property.DUAL_FIELDS : 0; 23.22 + } 23.23 }
24.1 --- a/src/jdk/nashorn/internal/runtime/SpillProperty.java Thu Apr 16 17:31:32 2015 +0200 24.2 +++ b/src/jdk/nashorn/internal/runtime/SpillProperty.java Fri Apr 10 14:18:31 2015 +0200 24.3 @@ -25,7 +25,6 @@ 24.4 24.5 package jdk.nashorn.internal.runtime; 24.6 24.7 -import static jdk.nashorn.internal.codegen.ObjectClassGenerator.OBJECT_FIELDS_ONLY; 24.8 import static jdk.nashorn.internal.lookup.Lookup.MH; 24.9 24.10 import java.lang.invoke.MethodHandle; 24.11 @@ -139,11 +138,11 @@ 24.12 } 24.13 } 24.14 24.15 - private static MethodHandle primitiveGetter(final int slot) { 24.16 - return OBJECT_FIELDS_ONLY ? null : Accessors.getCached(slot, true, true); 24.17 + private static MethodHandle primitiveGetter(final int slot, final int flags) { 24.18 + return (flags & DUAL_FIELDS) == DUAL_FIELDS ? Accessors.getCached(slot, true, true) : null; 24.19 } 24.20 - private static MethodHandle primitiveSetter(final int slot) { 24.21 - return OBJECT_FIELDS_ONLY ? null : Accessors.getCached(slot, true, false); 24.22 + private static MethodHandle primitiveSetter(final int slot, final int flags) { 24.23 + return (flags & DUAL_FIELDS) == DUAL_FIELDS ? Accessors.getCached(slot, true, false) : null; 24.24 } 24.25 private static MethodHandle objectGetter(final int slot) { 24.26 return Accessors.getCached(slot, false, true); 24.27 @@ -160,8 +159,7 @@ 24.28 * @param slot spill slot 24.29 */ 24.30 public SpillProperty(final String key, final int flags, final int slot) { 24.31 - super(key, flags, slot, primitiveGetter(slot), primitiveSetter(slot), objectGetter(slot), objectSetter(slot)); 24.32 - assert !OBJECT_FIELDS_ONLY || getLocalType() == Object.class; 24.33 + super(key, flags, slot, primitiveGetter(slot, flags), primitiveSetter(slot, flags), objectGetter(slot), objectSetter(slot)); 24.34 } 24.35 24.36 /** 24.37 @@ -173,7 +171,7 @@ 24.38 */ 24.39 public SpillProperty(final String key, final int flags, final int slot, final Class<?> initialType) { 24.40 this(key, flags, slot); 24.41 - setType(OBJECT_FIELDS_ONLY ? Object.class : initialType); 24.42 + setType(hasDualFields() ? initialType : Object.class); 24.43 } 24.44 24.45 SpillProperty(final String key, final int flags, final int slot, final ScriptObject owner, final Object initialValue) { 24.46 @@ -216,8 +214,8 @@ 24.47 @Override 24.48 void initMethodHandles(final Class<?> structure) { 24.49 final int slot = getSlot(); 24.50 - primitiveGetter = primitiveGetter(slot); 24.51 - primitiveSetter = primitiveSetter(slot); 24.52 + primitiveGetter = primitiveGetter(slot, getFlags()); 24.53 + primitiveSetter = primitiveSetter(slot, getFlags()); 24.54 objectGetter = objectGetter(slot); 24.55 objectSetter = objectSetter(slot); 24.56 }
25.1 --- a/src/jdk/nashorn/internal/runtime/StructureLoader.java Thu Apr 16 17:31:32 2015 +0200 25.2 +++ b/src/jdk/nashorn/internal/runtime/StructureLoader.java Fri Apr 10 14:18:31 2015 +0200 25.3 @@ -27,7 +27,8 @@ 25.4 25.5 import static jdk.nashorn.internal.codegen.Compiler.SCRIPTS_PACKAGE; 25.6 import static jdk.nashorn.internal.codegen.Compiler.binaryName; 25.7 -import static jdk.nashorn.internal.codegen.CompilerConstants.JS_OBJECT_PREFIX; 25.8 +import static jdk.nashorn.internal.codegen.CompilerConstants.JS_OBJECT_DUAL_FIELD_PREFIX; 25.9 +import static jdk.nashorn.internal.codegen.CompilerConstants.JS_OBJECT_SINGLE_FIELD_PREFIX; 25.10 25.11 import java.security.ProtectionDomain; 25.12 import jdk.nashorn.internal.codegen.ObjectClassGenerator; 25.13 @@ -36,7 +37,8 @@ 25.14 * Responsible for on the fly construction of structure classes. 25.15 */ 25.16 final class StructureLoader extends NashornLoader { 25.17 - private static final String JS_OBJECT_PREFIX_EXTERNAL = binaryName(SCRIPTS_PACKAGE) + '.' + JS_OBJECT_PREFIX.symbolName(); 25.18 + private static final String SINGLE_FIELD_PREFIX = binaryName(SCRIPTS_PACKAGE) + '.' + JS_OBJECT_SINGLE_FIELD_PREFIX.symbolName(); 25.19 + private static final String DUAL_FIELD_PREFIX = binaryName(SCRIPTS_PACKAGE) + '.' + JS_OBJECT_DUAL_FIELD_PREFIX.symbolName(); 25.20 25.21 /** 25.22 * Constructor. 25.23 @@ -45,14 +47,39 @@ 25.24 super(parent); 25.25 } 25.26 25.27 + /** 25.28 + * Returns true if the class name represents a structure object with dual primitive/object fields. 25.29 + * @param name a class name 25.30 + * @return true if a dual field structure class 25.31 + */ 25.32 + private static boolean isDualFieldStructure(final String name) { 25.33 + return name.startsWith(DUAL_FIELD_PREFIX); 25.34 + } 25.35 + 25.36 + /** 25.37 + * Returns true if the class name represents a structure object with single object-only fields. 25.38 + * @param name a class name 25.39 + * @return true if a single field structure class 25.40 + */ 25.41 + static boolean isSingleFieldStructure(final String name) { 25.42 + return name.startsWith(SINGLE_FIELD_PREFIX); 25.43 + } 25.44 + 25.45 + /** 25.46 + * Returns true if the class name represents a Nashorn structure object. 25.47 + * @param name a class name 25.48 + * @return true if a structure class 25.49 + */ 25.50 static boolean isStructureClass(final String name) { 25.51 - return name.startsWith(JS_OBJECT_PREFIX_EXTERNAL); 25.52 + return isDualFieldStructure(name) || isSingleFieldStructure(name); 25.53 } 25.54 25.55 @Override 25.56 protected Class<?> findClass(final String name) throws ClassNotFoundException { 25.57 - if (isStructureClass(name)) { 25.58 - return generateClass(name, name.substring(JS_OBJECT_PREFIX_EXTERNAL.length())); 25.59 + if (isDualFieldStructure(name)) { 25.60 + return generateClass(name, name.substring(DUAL_FIELD_PREFIX.length()), true); 25.61 + } else if (isSingleFieldStructure(name)) { 25.62 + return generateClass(name, name.substring(SINGLE_FIELD_PREFIX.length()), false); 25.63 } 25.64 return super.findClass(name); 25.65 } 25.66 @@ -63,10 +90,10 @@ 25.67 * @param descriptor Layout descriptor. 25.68 * @return Generated class. 25.69 */ 25.70 - private Class<?> generateClass(final String name, final String descriptor) { 25.71 + private Class<?> generateClass(final String name, final String descriptor, final boolean dualFields) { 25.72 final Context context = Context.getContextTrusted(); 25.73 25.74 - final byte[] code = new ObjectClassGenerator(context).generate(descriptor); 25.75 + final byte[] code = new ObjectClassGenerator(context, dualFields).generate(descriptor); 25.76 return defineClass(name, code, 0, code.length, new ProtectionDomain(null, getPermissions(null))); 25.77 } 25.78 }
26.1 --- a/src/jdk/nashorn/internal/runtime/linker/Bootstrap.java Thu Apr 16 17:31:32 2015 +0200 26.2 +++ b/src/jdk/nashorn/internal/runtime/linker/Bootstrap.java Fri Apr 10 14:18:31 2015 +0200 26.3 @@ -74,17 +74,16 @@ 26.4 * of object fields only, it is fine. However, with dual fields, in order to get 26.5 * performance on benchmarks with a lot of object instantiation and then field 26.6 * reassignment, it can take slightly more relinks to become stable with type 26.7 - * changes swapping out an entire proprety map and making a map guard fail. 26.8 - * Therefore the relink threshold is set to 16 for dual fields (now the default). 26.9 - * This doesn't seem to have any other negative performance implication. 26.10 + * changes swapping out an entire property map and making a map guard fail. 26.11 + * Since we need to set this value statically it must work with possibly changing 26.12 + * optimistic types and dual fields settings. A higher value does not seem to have 26.13 + * any other negative performance implication when running with object-only fields, 26.14 + * so we choose a higher value here. 26.15 * 26.16 * See for example octane.gbemu, run with --log=fields:warning to study 26.17 * megamorphic behavior 26.18 */ 26.19 - private static final int NASHORN_DEFAULT_UNSTABLE_RELINK_THRESHOLD = 26.20 - ObjectClassGenerator.OBJECT_FIELDS_ONLY ? 26.21 - 8 : 26.22 - 16; 26.23 + private static final int NASHORN_DEFAULT_UNSTABLE_RELINK_THRESHOLD = 16; 26.24 26.25 // do not create me!! 26.26 private Bootstrap() {
27.1 --- a/src/jdk/nashorn/internal/runtime/linker/NashornGuards.java Thu Apr 16 17:31:32 2015 +0200 27.2 +++ b/src/jdk/nashorn/internal/runtime/linker/NashornGuards.java Fri Apr 10 14:18:31 2015 +0200 27.3 @@ -33,7 +33,6 @@ 27.4 import jdk.internal.dynalink.CallSiteDescriptor; 27.5 import jdk.internal.dynalink.linker.LinkRequest; 27.6 import jdk.nashorn.api.scripting.JSObject; 27.7 -import jdk.nashorn.internal.codegen.ObjectClassGenerator; 27.8 import jdk.nashorn.internal.objects.Global; 27.9 import jdk.nashorn.internal.runtime.Property; 27.10 import jdk.nashorn.internal.runtime.PropertyMap; 27.11 @@ -123,7 +122,7 @@ 27.12 */ 27.13 static boolean needsGuard(final Property property, final CallSiteDescriptor desc) { 27.14 return property == null || property.isConfigurable() 27.15 - || property.isBound() || !ObjectClassGenerator.OBJECT_FIELDS_ONLY 27.16 + || property.isBound() || property.hasDualFields() 27.17 || !NashornCallSiteDescriptor.isFastScope(desc) || property.canChangeType(); 27.18 } 27.19
28.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 28.2 +++ b/src/jdk/nashorn/internal/scripts/JD.java Fri Apr 10 14:18:31 2015 +0200 28.3 @@ -0,0 +1,88 @@ 28.4 +/* 28.5 + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. 28.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 28.7 + * 28.8 + * This code is free software; you can redistribute it and/or modify it 28.9 + * under the terms of the GNU General Public License version 2 only, as 28.10 + * published by the Free Software Foundation. Oracle designates this 28.11 + * particular file as subject to the "Classpath" exception as provided 28.12 + * by Oracle in the LICENSE file that accompanied this code. 28.13 + * 28.14 + * This code is distributed in the hope that it will be useful, but WITHOUT 28.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 28.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 28.17 + * version 2 for more details (a copy is included in the LICENSE file that 28.18 + * accompanied this code). 28.19 + * 28.20 + * You should have received a copy of the GNU General Public License version 28.21 + * 2 along with this work; if not, write to the Free Software Foundation, 28.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 28.23 + * 28.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 28.25 + * or visit www.oracle.com if you need additional information or have any 28.26 + * questions. 28.27 + */ 28.28 + 28.29 +package jdk.nashorn.internal.scripts; 28.30 + 28.31 +import jdk.nashorn.internal.runtime.PropertyMap; 28.32 +import jdk.nashorn.internal.runtime.ScriptObject; 28.33 + 28.34 +/** 28.35 + * Empty object class for dual primitive-object fields. 28.36 + */ 28.37 +public class JD extends ScriptObject { 28.38 + 28.39 + private static final PropertyMap map$ = PropertyMap.newMap(JD.class); 28.40 + 28.41 + /** 28.42 + * Returns the initial property map to be used. 28.43 + * @return the initial property map. 28.44 + */ 28.45 + public static PropertyMap getInitialMap() { 28.46 + return map$; 28.47 + } 28.48 + 28.49 + /** 28.50 + * Constructor given an initial property map 28.51 + * 28.52 + * @param map the property map 28.53 + */ 28.54 + public JD(final PropertyMap map) { 28.55 + super(map); 28.56 + } 28.57 + 28.58 + /** 28.59 + * Constructor given an initial prototype and the default initial property map. 28.60 + * 28.61 + * @param proto the prototype object 28.62 + */ 28.63 + public JD(final ScriptObject proto) { 28.64 + super(proto, getInitialMap()); 28.65 + } 28.66 + 28.67 + /** 28.68 + * Constructor that takes a pre-initialized spill pool. Used by 28.69 + * {@link jdk.nashorn.internal.codegen.SpillObjectCreator} and 28.70 + * {@link jdk.nashorn.internal.parser.JSONParser} for initializing object literals 28.71 + * 28.72 + * @param map property map 28.73 + * @param primitiveSpill primitive spill pool 28.74 + * @param objectSpill reference spill pool 28.75 + */ 28.76 + public JD(final PropertyMap map, final long[] primitiveSpill, final Object[] objectSpill) { 28.77 + super(map, primitiveSpill, objectSpill); 28.78 + } 28.79 + 28.80 + /** 28.81 + * A method handle of this method is passed to the ScriptFunction constructor. 28.82 + * 28.83 + * @param map the property map to use for allocatorMap 28.84 + * 28.85 + * @return newly allocated ScriptObject 28.86 + */ 28.87 + public static ScriptObject allocate(final PropertyMap map) { 28.88 + return new JD(map); 28.89 + } 28.90 +} 28.91 +
29.1 --- a/src/jdk/nashorn/internal/scripts/JO.java Thu Apr 16 17:31:32 2015 +0200 29.2 +++ b/src/jdk/nashorn/internal/scripts/JO.java Fri Apr 10 14:18:31 2015 +0200 29.3 @@ -1,5 +1,5 @@ 29.4 /* 29.5 - * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. 29.6 + * Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. 29.7 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 29.8 * 29.9 * This code is free software; you can redistribute it and/or modify it 29.10 @@ -29,11 +29,11 @@ 29.11 import jdk.nashorn.internal.runtime.ScriptObject; 29.12 29.13 /** 29.14 - * Empty object class. 29.15 + * Empty object class for object-only fields. 29.16 */ 29.17 public class JO extends ScriptObject { 29.18 29.19 - private static final PropertyMap map$ = PropertyMap.newMap(); 29.20 + private static final PropertyMap map$ = PropertyMap.newMap(JO.class); 29.21 29.22 /** 29.23 * Returns the initial property map to be used. 29.24 @@ -53,13 +53,12 @@ 29.25 } 29.26 29.27 /** 29.28 - * Constructor given an initial prototype and an initial property map. 29.29 + * Constructor given an initial prototype and the default initial property map. 29.30 * 29.31 * @param proto the prototype object 29.32 - * @param map the property map 29.33 */ 29.34 - public JO(final ScriptObject proto, final PropertyMap map) { 29.35 - super(proto, map); 29.36 + public JO(final ScriptObject proto) { 29.37 + super(proto, getInitialMap()); 29.38 } 29.39 29.40 /** 29.41 @@ -86,3 +85,4 @@ 29.42 return new JO(map); 29.43 } 29.44 } 29.45 +
30.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 30.2 +++ b/test/script/nosecurity/JDK-8067215.js Fri Apr 10 14:18:31 2015 +0200 30.3 @@ -0,0 +1,104 @@ 30.4 +/* 30.5 + * Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. 30.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 30.7 + * 30.8 + * This code is free software; you can redistribute it and/or modify it 30.9 + * under the terms of the GNU General Public License version 2 only, as 30.10 + * published by the Free Software Foundation. 30.11 + * 30.12 + * This code is distributed in the hope that it will be useful, but WITHOUT 30.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 30.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 30.15 + * version 2 for more details (a copy is included in the LICENSE file that 30.16 + * accompanied this code). 30.17 + * 30.18 + * You should have received a copy of the GNU General Public License version 30.19 + * 2 along with this work; if not, write to the Free Software Foundation, 30.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 30.21 + * 30.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 30.23 + * or visit www.oracle.com if you need additional information or have any 30.24 + * questions. 30.25 + */ 30.26 + 30.27 +/** 30.28 + * JDK-8067215: Disable dual fields when not using optimistic types 30.29 + * 30.30 + * @test 30.31 + * @run 30.32 + * @option -Dnashorn.debug=true 30.33 + * @fork 30.34 + */ 30.35 + 30.36 +var intType = Java.type("int"); 30.37 +var doubleType = Java.type("double"); 30.38 +var longType = Java.type("long"); 30.39 +var objectType = Java.type("java.lang.Object"); 30.40 + 30.41 +var Context = Java.type("jdk.nashorn.internal.runtime.Context"); 30.42 +var JSType = Java.type("jdk.nashorn.internal.runtime.JSType"); 30.43 + 30.44 +var context = Context.getContext(); 30.45 +var dualFields = context.useDualFields(); 30.46 +var optimisticTypes = context.getEnv()._optimistic_types; 30.47 + 30.48 +if (dualFields != optimisticTypes) { 30.49 + throw new Error("Wrong dual fields setting"); 30.50 +} 30.51 + 30.52 +function testMap(obj) { 30.53 + obj.x = "foo"; 30.54 + obj["y"] = 0; 30.55 + Object.defineProperty(obj, "z", {value: 0.5}); 30.56 + var map = Debug.map(obj); 30.57 + for (var key in obj) { 30.58 + var prop = map.findProperty(key); 30.59 + if (prop.hasDualFields() !== dualFields) { 30.60 + throw new Error("Wrong property flags: " + prop); 30.61 + } 30.62 + if (prop.getType() != getExpectedType(obj[key])) { 30.63 + throw new Error("Wrong property type: " + prop.getType() + " // " + getExpectedType(obj[key])); 30.64 + 30.65 + } 30.66 + } 30.67 +} 30.68 + 30.69 +function getExpectedType(value) { 30.70 + if (!dualFields) { 30.71 + return objectType.class; 30.72 + } 30.73 + if (JSType.isRepresentableAsInt(value)) { 30.74 + return intType.class; 30.75 + } 30.76 + if (JSType.isRepresentableAsLong(value)) { 30.77 + return longType.class; 30.78 + } 30.79 + if (JSType.isNumber(value)) { 30.80 + return doubleType.class; 30.81 + } 30.82 + return objectType.class; 30.83 +} 30.84 + 30.85 +var o = { 30.86 + a: 1, 30.87 + b: 2.5, 30.88 + c: 0x10000000000, 30.89 + d: true 30.90 +}; 30.91 + 30.92 +function C() { 30.93 + this.a = 1; 30.94 + this.b = 2.5; 30.95 + this.c = 0x10000000000; 30.96 + this.d = true; 30.97 +} 30.98 + 30.99 +var a = 1; 30.100 +var b = 2.5; 30.101 +var c = 0x10000000000; 30.102 +var d = true; 30.103 + 30.104 +testMap(o); 30.105 +testMap(new C()); 30.106 +testMap(JSON.parse('{ "a": 1, "b": 2.5, "c": 1099511627776, "d": true }')); 30.107 +testMap(this);