changeset 1349:d82b07c9c6e3

8067215: Disable dual fields when not using optimistic types Reviewed-by: attila, lagergren
author hannesw
date Fri, 10 Apr 2015 14:18:31 +0200
parents 63fe48ca8630
children d03eb34e4b84
files buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/PrototypeGenerator.java docs/DEVELOPER_README src/jdk/nashorn/internal/codegen/CodeGenerator.java src/jdk/nashorn/internal/codegen/CompilerConstants.java src/jdk/nashorn/internal/codegen/FieldObjectCreator.java src/jdk/nashorn/internal/codegen/FindScopeDepths.java src/jdk/nashorn/internal/codegen/MapCreator.java src/jdk/nashorn/internal/codegen/MapTuple.java src/jdk/nashorn/internal/codegen/ObjectClassGenerator.java src/jdk/nashorn/internal/codegen/ObjectCreator.java src/jdk/nashorn/internal/codegen/SpillObjectCreator.java src/jdk/nashorn/internal/objects/Global.java src/jdk/nashorn/internal/objects/NativeJSAdapter.java src/jdk/nashorn/internal/parser/JSONParser.java src/jdk/nashorn/internal/runtime/AccessorProperty.java src/jdk/nashorn/internal/runtime/AllocationStrategy.java src/jdk/nashorn/internal/runtime/Context.java src/jdk/nashorn/internal/runtime/JSONFunctions.java src/jdk/nashorn/internal/runtime/JSType.java src/jdk/nashorn/internal/runtime/Property.java src/jdk/nashorn/internal/runtime/PropertyMap.java src/jdk/nashorn/internal/runtime/ScriptObject.java src/jdk/nashorn/internal/runtime/SetMethodCreator.java src/jdk/nashorn/internal/runtime/SpillProperty.java src/jdk/nashorn/internal/runtime/StructureLoader.java src/jdk/nashorn/internal/runtime/linker/Bootstrap.java src/jdk/nashorn/internal/runtime/linker/NashornGuards.java src/jdk/nashorn/internal/scripts/JD.java src/jdk/nashorn/internal/scripts/JO.java test/script/nosecurity/JDK-8067215.js
diffstat 30 files changed, 532 insertions(+), 262 deletions(-) [+]
line wrap: on
line diff
--- a/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/PrototypeGenerator.java	Thu Apr 16 17:31:32 2015 +0200
+++ b/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/PrototypeGenerator.java	Fri Apr 10 14:18:31 2015 +0200
@@ -124,8 +124,6 @@
         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);
--- a/docs/DEVELOPER_README	Thu Apr 16 17:31:32 2015 +0200
+++ b/docs/DEVELOPER_README	Fri Apr 10 14:18:31 2015 +0200
@@ -63,16 +63,19 @@
 See the description of the codegen logger below.
 
 
-SYSTEM PROPERTY: -Dnashorn.fields.objects
+SYSTEM PROPERTY: -Dnashorn.fields.objects, -Dnashorn.fields.dual
 
-When this property is true, Nashorn will only use object fields for
-AccessorProperties. This means that primitive values must be boxed
-when stored in a field, which is significantly slower than using
-primitive fields.
+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).
 
-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
+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.
--- a/src/jdk/nashorn/internal/codegen/CodeGenerator.java	Thu Apr 16 17:31:32 2015 +0200
+++ b/src/jdk/nashorn/internal/codegen/CodeGenerator.java	Fri Apr 10 14:18:31 2015 +0200
@@ -43,7 +43,6 @@
 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;
@@ -320,6 +319,14 @@
     }
 
     /**
+     * Are we using dual primitive/object field representation?
+     * @return true if using dual field representation, false for object-only fields
+     */
+    boolean useDualFields() {
+        return compiler.getContext().useDualFields();
+    }
+
+    /**
      * Load an identity node
      *
      * @param identNode an identity node to load
@@ -1909,10 +1916,10 @@
                         //this symbol will be put fielded, we can't initialize it as undefined with a known type
                         @Override
                         public Class<?> 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();
                         }
                     });
                 }
@@ -2568,7 +2575,7 @@
 
             //for literals, a value of null means object type, i.e. the value null or getter setter function
             //(I think)
-            final Class<?> valueType = (OBJECT_FIELDS_ONLY || value == null || value.getType().isBoolean()) ? Object.class : value.getType().getTypeClass();
+            final Class<?> valueType = (!useDualFields() || value == null || value.getType().isBoolean()) ? Object.class : value.getType().getTypeClass();
             tuples.add(new MapTuple<Expression>(key, symbol, Type.typeFor(valueType), value) {
                 @Override
                 public Class<?> getValueType() {
--- a/src/jdk/nashorn/internal/codegen/CompilerConstants.java	Thu Apr 16 17:31:32 2015 +0200
+++ b/src/jdk/nashorn/internal/codegen/CompilerConstants.java	Fri Apr 10 14:18:31 2015 +0200
@@ -149,8 +149,11 @@
     /** 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"),
--- a/src/jdk/nashorn/internal/codegen/FieldObjectCreator.java	Thu Apr 16 17:31:32 2015 +0200
+++ b/src/jdk/nashorn/internal/codegen/FieldObjectCreator.java	Fri Apr 10 14:18:31 2015 +0200
@@ -151,7 +151,7 @@
     @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 @@
     private void putField(final MethodEmitter method, final String key, final int fieldIndex, final MapTuple<T> 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 @@
      */
     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));
--- a/src/jdk/nashorn/internal/codegen/FindScopeDepths.java	Thu Apr 16 17:31:32 2015 +0200
+++ b/src/jdk/nashorn/internal/codegen/FindScopeDepths.java	Fri Apr 10 14:18:31 2015 +0200
@@ -207,7 +207,7 @@
         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),
--- a/src/jdk/nashorn/internal/codegen/MapCreator.java	Thu Apr 16 17:31:32 2015 +0200
+++ b/src/jdk/nashorn/internal/codegen/MapCreator.java	Fri Apr 10 14:18:31 2015 +0200
@@ -68,17 +68,17 @@
      * @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<Property> properties = new ArrayList<>();
         assert tuples != null;
 
         for (final MapTuple<T> 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 @@
         return PropertyMap.newMap(properties, structure.getName(), fieldCount, fieldMaximum, 0);
     }
 
-    PropertyMap makeSpillMap(final boolean hasArguments) {
+    PropertyMap makeSpillMap(final boolean hasArguments, final boolean dualFields) {
         final List<Property> properties = new ArrayList<>();
         int spillIndex = 0;
         assert tuples != null;
@@ -100,10 +100,10 @@
         for (final MapTuple<T> 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 @@
      *
      * @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 @@
             flags |= Property.NEEDS_DECLARATION;
         }
 
+        if (dualFields) {
+            flags |= Property.DUAL_FIELDS;
+        }
+
         return flags;
     }
 }
--- a/src/jdk/nashorn/internal/codegen/MapTuple.java	Thu Apr 16 17:31:32 2015 +0200
+++ b/src/jdk/nashorn/internal/codegen/MapTuple.java	Fri Apr 10 14:18:31 2015 +0200
@@ -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 @@
     }
 
     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
--- a/src/jdk/nashorn/internal/codegen/ObjectClassGenerator.java	Thu Apr 16 17:31:32 2015 +0200
+++ b/src/jdk/nashorn/internal/codegen/ObjectClassGenerator.java	Fri Apr 10 14:18:31 2015 +0200
@@ -31,7 +31,8 @@
 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 @@
      */
     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<Type> 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 @@
     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 @@
         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 @@
      *
      * @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 @@
      * @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<String> fieldNames) {
-        if (!OBJECT_FIELDS_ONLY) {
+    private void initializeToUndefined(final MethodEmitter init, final String className, final List<String> 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 @@
      * @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 @@
      * @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<String> initFields   = addFields(classEmitter, fieldCount);
@@ -353,11 +355,11 @@
      *
      * @return List fields that need to be initialized.
      */
-    private static List<String> addFields(final ClassEmitter classEmitter, final int fieldCount) {
+    private List<String> addFields(final ClassEmitter classEmitter, final int fieldCount) {
         final List<String> initFields = new LinkedList<>();
-
+        final 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 @@
     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 @@
         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 @@
             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 @@
             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 @@
         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 @@
             return MH.asType(objectSetter, objectSetter.type().changeParameterType(1, type));
         }
 
-        assert !OBJECT_FIELDS_ONLY;
-
         final MethodType pmt = primitiveSetter.type();
 
         switch (fti) {
@@ -832,8 +826,8 @@
      * @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);
     }
 }
--- a/src/jdk/nashorn/internal/codegen/ObjectCreator.java	Thu Apr 16 17:31:32 2015 +0200
+++ b/src/jdk/nashorn/internal/codegen/ObjectCreator.java	Fri Apr 10 14:18:31 2015 +0200
@@ -134,7 +134,7 @@
 
     MethodEmitter loadTuple(final MethodEmitter method, final MapTuple<T> 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);
--- a/src/jdk/nashorn/internal/codegen/SpillObjectCreator.java	Thu Apr 16 17:31:32 2015 +0200
+++ b/src/jdk/nashorn/internal/codegen/SpillObjectCreator.java	Fri Apr 10 14:18:31 2015 +0200
@@ -27,7 +27,6 @@
 
 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.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 @@
         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<Integer> 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 @@
                     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 @@
         //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<Expression> 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<Expression> 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<Expression> tuple    = tuples.get(i);
-            final Property             property = propertyMap.findProperty(tuple.key);
-            if (property != null && !tuple.isPrimitive()) {
+            final MapTuple<Expression> 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 @@
         }
 
         //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 @@
 
         // set postfix
         for (final int i : postsetValues) {
-            final MapTuple<Expression> tuple    = tuples.get(i);
-            final Property             property = propertyMap.findProperty(tuple.key);
+            final MapTuple<Expression> 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 @@
     @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<? extends ScriptObject> clazz = dualFields ? JD.class : JO.class;
+        propertyMap = new MapCreator<>(clazz, tuples).makeSpillMap(false, codegen.useDualFields());
         return propertyMap;
     }
 
--- a/src/jdk/nashorn/internal/objects/Global.java	Thu Apr 16 17:31:32 2015 +0200
+++ b/src/jdk/nashorn/internal/objects/Global.java	Fri Apr 10 14:18:31 2015 +0200
@@ -79,6 +79,7 @@
 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 @@
     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 @@
         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) {
@@ -934,7 +940,7 @@
      * @return the new ScriptObject
      */
     public ScriptObject newObject() {
-        return new JO(getObjectPrototype(), JO.getInitialMap());
+        return useDualFields() ? new JD(getObjectPrototype()) : new JO(getObjectPrototype());
     }
 
     /**
@@ -2746,8 +2752,8 @@
      */
     private static class LexicalScope extends ScriptObject {
 
-        LexicalScope(final ScriptObject proto) {
-            super(proto, PropertyMap.newMap());
+        LexicalScope(final Global global) {
+            super(global, PropertyMap.newMap());
         }
 
         @Override
--- a/src/jdk/nashorn/internal/objects/NativeJSAdapter.java	Thu Apr 16 17:31:32 2015 +0200
+++ b/src/jdk/nashorn/internal/objects/NativeJSAdapter.java	Fri Apr 10 14:18:31 2015 +0200
@@ -160,7 +160,7 @@
     }
 
     private static ScriptObject wrapAdaptee(final ScriptObject adaptee) {
-        return new JO(adaptee, JO.getInitialMap());
+        return new JO(adaptee);
     }
 
     @Override
--- a/src/jdk/nashorn/internal/parser/JSONParser.java	Thu Apr 16 17:31:32 2015 +0200
+++ b/src/jdk/nashorn/internal/parser/JSONParser.java	Fri Apr 10 14:18:31 2015 +0200
@@ -41,6 +41,7 @@
 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 @@
 
     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 @@
      * @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 @@
     }
 
     private Object parseObject() {
-        PropertyMap propertyMap = EMPTY_MAP;
+        PropertyMap propertyMap = dualFields ? JD.getInitialMap() : JO.getInitialMap();
         ArrayData arrayData = ArrayData.EMPTY_ARRAY;
         final ArrayList<Object> values = new ArrayList<>();
         int state = STATE_EMPTY;
@@ -241,36 +242,45 @@
         return newArrayData.set(index, value, false);
     }
 
-    private static PropertyMap addObjectProperty(final PropertyMap propertyMap, final List<Object> values,
+    private PropertyMap addObjectProperty(final PropertyMap propertyMap, final List<Object> values,
                                                  final String id, final Object value) {
         final 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<Object> values, final ArrayData arrayData) {
-        final long[] primitiveSpill = new long[values.size()];
+        final long[] primitiveSpill = dualFields ? new long[values.size()] : null;
         final Object[] objectSpill = new Object[values.size()];
 
         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;
--- a/src/jdk/nashorn/internal/runtime/AccessorProperty.java	Thu Apr 16 17:31:32 2015 +0200
+++ b/src/jdk/nashorn/internal/runtime/AccessorProperty.java	Fri Apr 10 14:18:31 2015 +0200
@@ -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 @@
                 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 @@
      * @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 @@
         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 @@
         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 @@
             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 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 @@
      * @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 @@
      * 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 @@
 
     @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.
--- a/src/jdk/nashorn/internal/runtime/AllocationStrategy.java	Thu Apr 16 17:31:32 2015 +0200
+++ b/src/jdk/nashorn/internal/runtime/AllocationStrategy.java	Fri Apr 10 14:18:31 2015 +0200
@@ -44,6 +44,9 @@
     /** 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 @@
     /**
      * 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;
     }
--- a/src/jdk/nashorn/internal/runtime/Context.java	Thu Apr 16 17:31:32 2015 +0200
+++ b/src/jdk/nashorn/internal/runtime/Context.java	Fri Apr 10 14:18:31 2015 +0200
@@ -131,6 +131,23 @@
     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
      * its properties, so it's enough to invalidate a property to break all assumptions
@@ -434,7 +451,7 @@
      * @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 @@
             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();
     }
 
@@ -576,6 +601,14 @@
     }
 
     /**
+     * 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
      */
--- a/src/jdk/nashorn/internal/runtime/JSONFunctions.java	Thu Apr 16 17:31:32 2015 +0200
+++ b/src/jdk/nashorn/internal/runtime/JSONFunctions.java	Fri Apr 10 14:18:31 2015 +0200
@@ -72,7 +72,8 @@
     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 {
--- a/src/jdk/nashorn/internal/runtime/JSType.java	Thu Apr 16 17:31:32 2015 +0200
+++ b/src/jdk/nashorn/internal/runtime/JSType.java	Fri Apr 10 14:18:31 2015 +0200
@@ -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;
 
@@ -1973,10 +1972,6 @@
      * @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) {
--- a/src/jdk/nashorn/internal/runtime/Property.java	Thu Apr 16 17:31:32 2015 +0200
+++ b/src/jdk/nashorn/internal/runtime/Property.java	Fri Apr 10 14:18:31 2015 +0200
@@ -96,6 +96,9 @@
     /** 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 @@
      * @return true if parameter
      */
     public boolean isParameter() {
-        return (flags & IS_PARAMETER) == IS_PARAMETER;
+        return (flags & IS_PARAMETER) != 0;
     }
 
     /**
@@ -294,7 +297,7 @@
      * @return true if has arguments
      */
     public boolean hasArguments() {
-        return (flags & HAS_ARGUMENTS) == HAS_ARGUMENTS;
+        return (flags & HAS_ARGUMENTS) != 0;
     }
 
     /**
@@ -316,7 +319,7 @@
      * @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 @@
      * @return true if this is a block-scoped variable
      */
     public boolean needsDeclaration() {
-        return (flags & NEEDS_DECLARATION) == NEEDS_DECLARATION;
+        return (flags & NEEDS_DECLARATION) != 0;
     }
 
     /**
@@ -346,16 +349,6 @@
     }
 
     /**
-     * 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
      */
@@ -364,16 +357,6 @@
     }
 
     /**
-     * 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
      * property being cloned. Use only the return value
@@ -715,7 +698,7 @@
      * @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 @@
      * @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;
     }
 }
--- a/src/jdk/nashorn/internal/runtime/PropertyMap.java	Thu Apr 16 17:31:32 2015 +0200
+++ b/src/jdk/nashorn/internal/runtime/PropertyMap.java	Fri Apr 10 14:18:31 2015 +0200
@@ -199,12 +199,21 @@
     }
 
     /**
+     * Return a sharable empty map for the given object class.
+     * @param clazz the base object class
+     * @return New empty {@link PropertyMap}.
+     */
+    public static PropertyMap newMap(final Class<? extends ScriptObject> clazz) {
+        return new PropertyMap(EMPTY_HASHMAP, clazz.getName(), 0, 0, 0, false);
+    }
+
+    /**
      * Return a sharable empty map.
      *
      * @return New empty {@link PropertyMap}.
      */
     public static PropertyMap newMap() {
-        return new PropertyMap(EMPTY_HASHMAP, JO.class.getName(), 0, 0, 0, false);
+        return newMap(JO.class);
     }
 
     /**
--- a/src/jdk/nashorn/internal/runtime/ScriptObject.java	Thu Apr 16 17:31:32 2015 +0200
+++ b/src/jdk/nashorn/internal/runtime/ScriptObject.java	Fri Apr 10 14:18:31 2015 +0200
@@ -28,7 +28,6 @@
 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 @@
     /** 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 @@
     /** 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 @@
         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 @@
             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 @@
      * @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
+        final PropertyMap oldMap = getMap();
+        final int slot = oldMap.getFreeSpillSlot();
+        ensureSpillSize(slot);
         objectSpill[slot] = new UserAccessorProperty.Accessors(getter, setter);
-        final PropertyMap oldMap = getMap();
         Property    newProperty;
         PropertyMap newMap;
         do {
@@ -1006,19 +992,12 @@
             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 @@
             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 @@
 
     /**
      * 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 @@
      * @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 @@
         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 @@
         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;
     }
--- a/src/jdk/nashorn/internal/runtime/SetMethodCreator.java	Thu Apr 16 17:31:32 2015 +0200
+++ b/src/jdk/nashorn/internal/runtime/SetMethodCreator.java	Fri Apr 10 14:18:31 2015 +0200
@@ -232,14 +232,18 @@
     }
 
     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;
+    }
 }
--- a/src/jdk/nashorn/internal/runtime/SpillProperty.java	Thu Apr 16 17:31:32 2015 +0200
+++ b/src/jdk/nashorn/internal/runtime/SpillProperty.java	Fri Apr 10 14:18:31 2015 +0200
@@ -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 @@
         }
     }
 
-    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 @@
      * @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 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 @@
     @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);
     }
--- a/src/jdk/nashorn/internal/runtime/StructureLoader.java	Thu Apr 16 17:31:32 2015 +0200
+++ b/src/jdk/nashorn/internal/runtime/StructureLoader.java	Fri Apr 10 14:18:31 2015 +0200
@@ -27,7 +27,8 @@
 
 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 @@
  * 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 @@
         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 @@
      * @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)));
     }
 }
--- a/src/jdk/nashorn/internal/runtime/linker/Bootstrap.java	Thu Apr 16 17:31:32 2015 +0200
+++ b/src/jdk/nashorn/internal/runtime/linker/Bootstrap.java	Fri Apr 10 14:18:31 2015 +0200
@@ -74,17 +74,16 @@
      * 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() {
--- a/src/jdk/nashorn/internal/runtime/linker/NashornGuards.java	Thu Apr 16 17:31:32 2015 +0200
+++ b/src/jdk/nashorn/internal/runtime/linker/NashornGuards.java	Fri Apr 10 14:18:31 2015 +0200
@@ -33,7 +33,6 @@
 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 @@
      */
     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();
     }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk/nashorn/internal/scripts/JD.java	Fri Apr 10 14:18:31 2015 +0200
@@ -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);
+    }
+}
+
--- a/src/jdk/nashorn/internal/scripts/JO.java	Thu Apr 16 17:31:32 2015 +0200
+++ b/src/jdk/nashorn/internal/scripts/JO.java	Fri Apr 10 14:18:31 2015 +0200
@@ -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.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 @@
     }
 
     /**
-     * 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 @@
         return new JO(map);
     }
 }
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/script/nosecurity/JDK-8067215.js	Fri Apr 10 14:18:31 2015 +0200
@@ -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);