# HG changeset patch # User hannesw # Date 1414769278 -3600 # Node ID 29a4cd3d1f7ac39f0602fc18144217df2d260cb5 # Parent a54353b34d243ab476b87aafd1a178f09c78b4e7 8062401: User accessors require boxing and do not support optimistic types Reviewed-by: jlaskey, lagergren diff -r a54353b34d24 -r 29a4cd3d1f7a src/jdk/nashorn/internal/codegen/SpillObjectCreator.java --- a/src/jdk/nashorn/internal/codegen/SpillObjectCreator.java Tue Oct 28 17:22:17 2014 +0530 +++ b/src/jdk/nashorn/internal/codegen/SpillObjectCreator.java Fri Oct 31 16:27:58 2014 +0100 @@ -88,7 +88,7 @@ final Property property = propertyMap.findProperty(key); if (property != null) { // normal property key - property.setCurrentType(JSType.unboxedFieldType(constantValue)); + property.setType(JSType.unboxedFieldType(constantValue)); final int slot = property.getSlot(); if (!OBJECT_FIELDS_ONLY && constantValue instanceof Number) { jpresetValues[slot] = ObjectClassGenerator.pack((Number)constantValue); diff -r a54353b34d24 -r 29a4cd3d1f7a src/jdk/nashorn/internal/codegen/TypeEvaluator.java --- a/src/jdk/nashorn/internal/codegen/TypeEvaluator.java Tue Oct 28 17:22:17 2014 +0530 +++ b/src/jdk/nashorn/internal/codegen/TypeEvaluator.java Fri Oct 31 16:27:58 2014 +0100 @@ -117,7 +117,7 @@ } final Property property = find.getProperty(); - final Class> propertyClass = property.getCurrentType(); + final Class> propertyClass = property.getType(); if (propertyClass == null) { // propertyClass == null means its value is Undefined. It is probably not initialized yet, so we won't make // a type assumption yet. diff -r a54353b34d24 -r 29a4cd3d1f7a src/jdk/nashorn/internal/objects/NativeObject.java --- a/src/jdk/nashorn/internal/objects/NativeObject.java Tue Oct 28 17:22:17 2014 +0530 +++ b/src/jdk/nashorn/internal/objects/NativeObject.java Fri Oct 31 16:27:58 2014 +0100 @@ -672,7 +672,7 @@ for (final Property prop : properties) { if (prop.isEnumerable()) { final Object value = sourceObj.get(prop.getKey()); - prop.setCurrentType(Object.class); + prop.setType(Object.class); prop.setValue(sourceObj, sourceObj, value, false); propList.add(prop); } diff -r a54353b34d24 -r 29a4cd3d1f7a src/jdk/nashorn/internal/runtime/AccessorProperty.java --- a/src/jdk/nashorn/internal/runtime/AccessorProperty.java Tue Oct 28 17:22:17 2014 +0530 +++ b/src/jdk/nashorn/internal/runtime/AccessorProperty.java Fri Oct 31 16:27:58 2014 +0100 @@ -145,13 +145,6 @@ transient MethodHandle objectSetter; /** - * Current type of this object, in object only mode, this is an Object.class. In dual-fields mode - * null means undefined, and primitive types are allowed. The reason a special type is used for - * undefined, is that are no bits left to represent it in primitive types - */ - private Class> currentType; - - /** * Delegate constructor for bound properties. This is used for properties created by * {@link ScriptRuntime#mergeScope} and the Nashorn {@code Object.bindProperties} method. * The former is used to add a script's defined globals to the current global scope while @@ -171,7 +164,7 @@ this.objectSetter = bindTo(property.objectSetter, delegate); property.GETTER_CACHE = new MethodHandle[NOOF_TYPES]; // Properties created this way are bound to a delegate - setCurrentType(property.getCurrentType()); + setType(property.getType()); } /** @@ -248,7 +241,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; - setCurrentType(OBJECT_FIELDS_ONLY ? Object.class : getterType); + setType(OBJECT_FIELDS_ONLY ? Object.class : getterType); } /** @@ -317,7 +310,7 @@ */ public AccessorProperty(final String key, final int flags, final Class> structure, final int slot, final Class> initialType) { this(key, flags, structure, slot); - setCurrentType(OBJECT_FIELDS_ONLY ? Object.class : initialType); + setType(OBJECT_FIELDS_ONLY ? Object.class : initialType); } /** @@ -330,13 +323,13 @@ protected AccessorProperty(final AccessorProperty property, final Class> newType) { super(property, property.getFlags()); - this.GETTER_CACHE = newType != property.getCurrentType() ? new MethodHandle[NOOF_TYPES] : property.GETTER_CACHE; + this.GETTER_CACHE = newType != property.getLocalType() ? new MethodHandle[NOOF_TYPES] : property.GETTER_CACHE; this.primitiveGetter = property.primitiveGetter; this.primitiveSetter = property.primitiveSetter; this.objectGetter = property.objectGetter; this.objectSetter = property.objectSetter; - setCurrentType(newType); + setType(newType); } /** @@ -345,7 +338,7 @@ * @param property source property */ protected AccessorProperty(final AccessorProperty property) { - this(property, property.getCurrentType()); + this(property, property.getLocalType()); } /** @@ -354,7 +347,7 @@ * @param initialValue initial value */ protected final void setInitialValue(final ScriptObject owner, final Object initialValue) { - setCurrentType(JSType.unboxedFieldType(initialValue)); + setType(JSType.unboxedFieldType(initialValue)); if (initialValue instanceof Integer) { invokeSetter(owner, ((Integer)initialValue).intValue()); } else if (initialValue instanceof Long) { @@ -370,7 +363,7 @@ * Initialize the type of a property */ protected final void initializeType() { - setCurrentType(OBJECT_FIELDS_ONLY ? Object.class : null); + setType(OBJECT_FIELDS_ONLY ? Object.class : null); } private void readObject(final ObjectInputStream s) throws IOException, ClassNotFoundException { @@ -557,12 +550,12 @@ } else { getter = debug( createGetter( - getCurrentType(), + getLocalType(), type, primitiveGetter, objectGetter, INVALID_PROGRAM_POINT), - getCurrentType(), + getLocalType(), type, "get"); getterCache[i] = getter; @@ -582,18 +575,18 @@ return debug( createGetter( - getCurrentType(), + getLocalType(), type, primitiveGetter, objectGetter, programPoint), - getCurrentType(), + getLocalType(), type, "get"); } private MethodHandle getOptimisticPrimitiveGetter(final Class> type, final int programPoint) { - final MethodHandle g = getGetter(getCurrentType()); + final MethodHandle g = getGetter(getLocalType()); return MH.asType(OptimisticReturnFilters.filterOptimisticReturnValue(g, type, programPoint), g.type().changeReturnType(type)); } @@ -631,7 +624,7 @@ } private MethodHandle generateSetter(final Class> forType, final Class> type) { - return debug(createSetter(forType, type, primitiveSetter, objectSetter), getCurrentType(), type, "set"); + return debug(createSetter(forType, type, primitiveSetter, objectSetter), getLocalType(), type, "set"); } /** @@ -639,7 +632,7 @@ * @return true if undefined */ protected final boolean isUndefined() { - return getCurrentType() == null; + return getLocalType() == null; } @Override @@ -647,7 +640,7 @@ checkUndeclared(); final int typeIndex = getAccessorTypeIndex(type); - final int currentTypeIndex = getAccessorTypeIndex(getCurrentType()); + final int currentTypeIndex = getAccessorTypeIndex(getLocalType()); //if we are asking for an object setter, but are still a primitive type, we might try to box it MethodHandle mh; @@ -656,13 +649,13 @@ final PropertyMap newMap = getWiderMap(currentMap, newProperty); final MethodHandle widerSetter = newProperty.getSetter(type, newMap); - final Class> ct = getCurrentType(); + final Class> ct = getLocalType(); mh = MH.filterArguments(widerSetter, 0, MH.insertArguments(debugReplace(ct, type, currentMap, newMap) , 1, newMap)); if (ct != null && ct.isPrimitive() && !type.isPrimitive()) { mh = ObjectClassGenerator.createGuardBoxedPrimitiveSetter(ct, generateSetter(ct, ct), mh); } } else { - final Class> forType = isUndefined() ? type : getCurrentType(); + final Class> forType = isUndefined() ? type : getLocalType(); mh = generateSetter(!forType.isPrimitive() ? Object.class : forType, type); } @@ -681,24 +674,13 @@ return false; } // Return true for currently undefined even if non-writable/configurable to allow initialization of ES6 CONST. - return getCurrentType() == null || (getCurrentType() != Object.class && (isConfigurable() || isWritable())); + return getLocalType() == null || (getLocalType() != Object.class && (isConfigurable() || isWritable())); } private boolean needsInvalidator(final int typeIndex, final int currentTypeIndex) { return canChangeType() && typeIndex > currentTypeIndex; } - @Override - public final void setCurrentType(final Class> currentType) { - assert currentType != boolean.class : "no boolean storage support yet - fix this"; - this.currentType = currentType == null ? null : currentType.isPrimitive() ? currentType : Object.class; - } - - @Override - public Class> getCurrentType() { - return currentType; - } - private MethodHandle debug(final MethodHandle mh, final Class> forType, final Class> type, final String tag) { if (!Context.DEBUG || !Global.hasInstance()) { return mh; diff -r a54353b34d24 -r 29a4cd3d1f7a src/jdk/nashorn/internal/runtime/FindProperty.java --- a/src/jdk/nashorn/internal/runtime/FindProperty.java Tue Oct 28 17:22:17 2014 +0530 +++ b/src/jdk/nashorn/internal/runtime/FindProperty.java Fri Oct 31 16:27:58 2014 +0100 @@ -84,13 +84,18 @@ * @return method handle for the getter */ public MethodHandle getGetter(final Class> type, final int programPoint, final LinkRequest request) { - final MethodHandle getter; + MethodHandle getter; if (isValid(programPoint)) { getter = property.getOptimisticGetter(type, programPoint); } else { getter = property.getGetter(type); } if (property instanceof UserAccessorProperty) { + getter = MH.insertArguments(getter, 1, UserAccessorProperty.getINVOKE_UA_GETTER(type, programPoint)); + if (isValid(programPoint) && type.isPrimitive()) { + getter = MH.insertArguments(getter, 1, programPoint); + } + property.setType(type); return insertAccessorsGetter((UserAccessorProperty) property, request, getter); } return getter; @@ -111,7 +116,8 @@ public MethodHandle getSetter(final Class> type, final boolean strict, final LinkRequest request) { MethodHandle setter = property.getSetter(type, getOwner().getMap()); if (property instanceof UserAccessorProperty) { - setter = MH.insertArguments(setter, 1, strict ? property.getKey() : null); + setter = MH.insertArguments(setter, 1, UserAccessorProperty.getINVOKE_UA_SETTER(type), strict ? property.getKey() : null); + property.setType(type); return insertAccessorsGetter((UserAccessorProperty) property, request, setter); } diff -r a54353b34d24 -r 29a4cd3d1f7a src/jdk/nashorn/internal/runtime/Property.java --- a/src/jdk/nashorn/internal/runtime/Property.java Tue Oct 28 17:22:17 2014 +0530 +++ b/src/jdk/nashorn/internal/runtime/Property.java Fri Oct 31 16:27:58 2014 +0100 @@ -102,6 +102,13 @@ /** Property field number or spill slot. */ private final int slot; + /** + * Current type of this object, in object only mode, this is an Object.class. In dual-fields mode + * null means undefined, and primitive types are allowed. The reason a special type is used for + * undefined, is that are no bits left to represent it in primitive types + */ + private Class> type; + /** SwitchPoint that is invalidated when property is changed, optional */ protected transient SwitchPoint builtinSwitchPoint; @@ -536,7 +543,7 @@ *
* see {@link ObjectClassGenerator#createSetter(Class, Class, MethodHandle, MethodHandle)} * if you are interested in the internal details of this. Note that if you - * are running in default mode, with {@code -Dnashorn.fields.dual=true}, disabled, the setters + * are running with {@code -Dnashorn.fields.objects=true}, the setters * will currently never change, as all properties are represented as Object field, * the Object fields are Initialized to {@code ScriptRuntime.UNDEFINED} and primitives are * boxed/unboxed upon every access, which is not necessarily optimal @@ -569,7 +576,7 @@ @Override public int hashCode() { - final Class> type = getCurrentType(); + final Class> type = getLocalType(); return Objects.hashCode(this.key) ^ flags ^ getSlot() ^ (type == null ? 0 : type.hashCode()); } @@ -586,7 +593,7 @@ final Property otherProperty = (Property)other; return equalsWithoutType(otherProperty) && - getCurrentType() == otherProperty.getCurrentType(); + getLocalType() == otherProperty.getLocalType(); } boolean equalsWithoutType(final Property otherProperty) { @@ -615,7 +622,7 @@ */ public final String toStringShort() { final StringBuilder sb = new StringBuilder(); - final Class> type = getCurrentType(); + final Class> type = getLocalType(); sb.append(getKey()).append(" (").append(type(type)).append(')'); return sb.toString(); } @@ -632,7 +639,7 @@ @Override public String toString() { final StringBuilder sb = new StringBuilder(); - final Class> type = getCurrentType(); + final Class> type = getLocalType(); sb.append(indent(getKey(), 20)). append(" id="). @@ -656,20 +663,40 @@ } /** - * Get the current type of this field. If you are not running with dual fields enabled, + * Get the current type of this property. If you are running with object fields enabled, * this will always be Object.class. See the value representation explanation in * {@link Property#getSetter(Class, PropertyMap)} and {@link ObjectClassGenerator} * for more information. * + *
Note that for user accessor properties, this returns the type of the last observed + * value passed to or returned by a user accessor. Use {@link #getLocalType()} to always get + * the type of the actual value stored in the property slot.
+ * * @return current type of property, null means undefined */ - public abstract Class> getCurrentType(); + public final Class> getType() { + return type; + } /** - * Reset the current type of this property - * @param currentType new current type + * Set the type of this property. + * @param type new type */ - public abstract void setCurrentType(final Class> currentType); + public final void setType(final Class> type) { + assert type != boolean.class : "no boolean storage support yet - fix this"; + this.type = type == null ? null : type.isPrimitive() ? type : Object.class; + } + + /** + * Get the type of the value in the local property slot. This returns the same as + * {@link #getType()} for normal properties, but always returns {@code Object.class} + * for {@link UserAccessorProperty}s as their local type is a pair of accessor references. + * + * @return the local property type + */ + protected Class> getLocalType() { + return getType(); + } /** * Check whether this Property can ever change its type. The default is false, and if diff -r a54353b34d24 -r 29a4cd3d1f7a src/jdk/nashorn/internal/runtime/PropertyMap.java --- a/src/jdk/nashorn/internal/runtime/PropertyMap.java Tue Oct 28 17:22:17 2014 +0530 +++ b/src/jdk/nashorn/internal/runtime/PropertyMap.java Fri Oct 31 16:27:58 2014 +0100 @@ -512,7 +512,7 @@ assert sameType || oldProperty instanceof AccessorProperty && newProperty instanceof UserAccessorProperty : - "arbitrary replaceProperty attempted " + sameType + " oldProperty=" + oldProperty.getClass() + " newProperty=" + newProperty.getClass() + " [" + oldProperty.getCurrentType() + " => " + newProperty.getCurrentType() + "]"; + "arbitrary replaceProperty attempted " + sameType + " oldProperty=" + oldProperty.getClass() + " newProperty=" + newProperty.getClass() + " [" + oldProperty.getLocalType() + " => " + newProperty.getLocalType() + "]"; newMap.flags = flags; diff -r a54353b34d24 -r 29a4cd3d1f7a src/jdk/nashorn/internal/runtime/ScriptObject.java --- a/src/jdk/nashorn/internal/runtime/ScriptObject.java Tue Oct 28 17:22:17 2014 +0530 +++ b/src/jdk/nashorn/internal/runtime/ScriptObject.java Fri Oct 31 16:27:58 2014 +0100 @@ -969,7 +969,7 @@ final UserAccessorProperty uc = (UserAccessorProperty)oldProperty; final int slot = uc.getSlot(); - assert uc.getCurrentType() == Object.class; + assert uc.getLocalType() == Object.class; if (slot >= spillLength) { uc.setAccessors(this, getMap(), new UserAccessorProperty.Accessors(getter, setter)); } else { diff -r a54353b34d24 -r 29a4cd3d1f7a src/jdk/nashorn/internal/runtime/SpillProperty.java --- a/src/jdk/nashorn/internal/runtime/SpillProperty.java Tue Oct 28 17:22:17 2014 +0530 +++ b/src/jdk/nashorn/internal/runtime/SpillProperty.java Fri Oct 31 16:27:58 2014 +0100 @@ -161,12 +161,12 @@ */ 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 || getCurrentType() == Object.class; + assert !OBJECT_FIELDS_ONLY || getLocalType() == Object.class; } SpillProperty(final String key, final int flags, final int slot, final Class> initialType) { this(key, flags, slot); - setCurrentType(OBJECT_FIELDS_ONLY ? Object.class : initialType); + setType(OBJECT_FIELDS_ONLY ? Object.class : initialType); } SpillProperty(final String key, final int flags, final int slot, final ScriptObject owner, final Object initialValue) { diff -r a54353b34d24 -r 29a4cd3d1f7a src/jdk/nashorn/internal/runtime/UserAccessorProperty.java --- a/src/jdk/nashorn/internal/runtime/UserAccessorProperty.java Tue Oct 28 17:22:17 2014 +0530 +++ b/src/jdk/nashorn/internal/runtime/UserAccessorProperty.java Fri Oct 31 16:27:58 2014 +0100 @@ -27,16 +27,16 @@ import static jdk.nashorn.internal.lookup.Lookup.MH; import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; -import static jdk.nashorn.internal.runtime.JSType.CONVERT_OBJECT_OPTIMISTIC; -import static jdk.nashorn.internal.runtime.JSType.getAccessorTypeIndex; import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED; +import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT; +import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_PROGRAM_POINT_SHIFT; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; -import java.util.concurrent.Callable; import jdk.nashorn.internal.lookup.Lookup; import jdk.nashorn.internal.runtime.linker.Bootstrap; +import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor; /** * Property with user defined getters/setters. Actual getter and setter @@ -69,38 +69,29 @@ private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup(); /** Getter method handle */ - private final static MethodHandle INVOKE_GETTER_ACCESSOR = findOwnMH_S("invokeGetterAccessor", Object.class, Accessors.class, Object.class); + private final static MethodHandle INVOKE_OBJECT_GETTER = findOwnMH_S("invokeObjectGetter", Object.class, Accessors.class, MethodHandle.class, Object.class); + private final static MethodHandle INVOKE_INT_GETTER = findOwnMH_S("invokeIntGetter", int.class, Accessors.class, MethodHandle.class, int.class, Object.class); + private final static MethodHandle INVOKE_LONG_GETTER = findOwnMH_S("invokeLongGetter", long.class, Accessors.class, MethodHandle.class, int.class, Object.class); + private final static MethodHandle INVOKE_NUMBER_GETTER = findOwnMH_S("invokeNumberGetter", double.class, Accessors.class, MethodHandle.class, int.class, Object.class); /** Setter method handle */ - private final static MethodHandle INVOKE_SETTER_ACCESSOR = findOwnMH_S("invokeSetterAccessor", void.class, Accessors.class, String.class, Object.class, Object.class); + private final static MethodHandle INVOKE_OBJECT_SETTER = findOwnMH_S("invokeObjectSetter", void.class, Accessors.class, MethodHandle.class, String.class, Object.class, Object.class); + private final static MethodHandle INVOKE_INT_SETTER = findOwnMH_S("invokeIntSetter", void.class, Accessors.class, MethodHandle.class, String.class, Object.class, int.class); + private final static MethodHandle INVOKE_LONG_SETTER = findOwnMH_S("invokeLongSetter", void.class, Accessors.class, MethodHandle.class, String.class, Object.class, long.class); + private final static MethodHandle INVOKE_NUMBER_SETTER = findOwnMH_S("invokeNumberSetter", void.class, Accessors.class, MethodHandle.class, String.class, Object.class, double.class); - /** Dynamic invoker for getter */ - private static final Object GETTER_INVOKER_KEY = new Object(); - - private static MethodHandle getINVOKE_UA_GETTER() { - return Context.getGlobal().getDynamicInvoker(GETTER_INVOKER_KEY, - new Callable