Fri, 31 Oct 2014 16:27:58 +0100
8062401: User accessors require boxing and do not support optimistic types
Reviewed-by: jlaskey, lagergren
1.1 --- a/src/jdk/nashorn/internal/codegen/SpillObjectCreator.java Tue Oct 28 17:22:17 2014 +0530 1.2 +++ b/src/jdk/nashorn/internal/codegen/SpillObjectCreator.java Fri Oct 31 16:27:58 2014 +0100 1.3 @@ -88,7 +88,7 @@ 1.4 final Property property = propertyMap.findProperty(key); 1.5 if (property != null) { 1.6 // normal property key 1.7 - property.setCurrentType(JSType.unboxedFieldType(constantValue)); 1.8 + property.setType(JSType.unboxedFieldType(constantValue)); 1.9 final int slot = property.getSlot(); 1.10 if (!OBJECT_FIELDS_ONLY && constantValue instanceof Number) { 1.11 jpresetValues[slot] = ObjectClassGenerator.pack((Number)constantValue);
2.1 --- a/src/jdk/nashorn/internal/codegen/TypeEvaluator.java Tue Oct 28 17:22:17 2014 +0530 2.2 +++ b/src/jdk/nashorn/internal/codegen/TypeEvaluator.java Fri Oct 31 16:27:58 2014 +0100 2.3 @@ -117,7 +117,7 @@ 2.4 } 2.5 2.6 final Property property = find.getProperty(); 2.7 - final Class<?> propertyClass = property.getCurrentType(); 2.8 + final Class<?> propertyClass = property.getType(); 2.9 if (propertyClass == null) { 2.10 // propertyClass == null means its value is Undefined. It is probably not initialized yet, so we won't make 2.11 // a type assumption yet.
3.1 --- a/src/jdk/nashorn/internal/objects/NativeObject.java Tue Oct 28 17:22:17 2014 +0530 3.2 +++ b/src/jdk/nashorn/internal/objects/NativeObject.java Fri Oct 31 16:27:58 2014 +0100 3.3 @@ -672,7 +672,7 @@ 3.4 for (final Property prop : properties) { 3.5 if (prop.isEnumerable()) { 3.6 final Object value = sourceObj.get(prop.getKey()); 3.7 - prop.setCurrentType(Object.class); 3.8 + prop.setType(Object.class); 3.9 prop.setValue(sourceObj, sourceObj, value, false); 3.10 propList.add(prop); 3.11 }
4.1 --- a/src/jdk/nashorn/internal/runtime/AccessorProperty.java Tue Oct 28 17:22:17 2014 +0530 4.2 +++ b/src/jdk/nashorn/internal/runtime/AccessorProperty.java Fri Oct 31 16:27:58 2014 +0100 4.3 @@ -145,13 +145,6 @@ 4.4 transient MethodHandle objectSetter; 4.5 4.6 /** 4.7 - * Current type of this object, in object only mode, this is an Object.class. In dual-fields mode 4.8 - * null means undefined, and primitive types are allowed. The reason a special type is used for 4.9 - * undefined, is that are no bits left to represent it in primitive types 4.10 - */ 4.11 - private Class<?> currentType; 4.12 - 4.13 - /** 4.14 * Delegate constructor for bound properties. This is used for properties created by 4.15 * {@link ScriptRuntime#mergeScope} and the Nashorn {@code Object.bindProperties} method. 4.16 * The former is used to add a script's defined globals to the current global scope while 4.17 @@ -171,7 +164,7 @@ 4.18 this.objectSetter = bindTo(property.objectSetter, delegate); 4.19 property.GETTER_CACHE = new MethodHandle[NOOF_TYPES]; 4.20 // Properties created this way are bound to a delegate 4.21 - setCurrentType(property.getCurrentType()); 4.22 + setType(property.getType()); 4.23 } 4.24 4.25 /** 4.26 @@ -248,7 +241,7 @@ 4.27 objectGetter = getter.type() != Lookup.GET_OBJECT_TYPE ? MH.asType(getter, Lookup.GET_OBJECT_TYPE) : getter; 4.28 objectSetter = setter != null && setter.type() != Lookup.SET_OBJECT_TYPE ? MH.asType(setter, Lookup.SET_OBJECT_TYPE) : setter; 4.29 4.30 - setCurrentType(OBJECT_FIELDS_ONLY ? Object.class : getterType); 4.31 + setType(OBJECT_FIELDS_ONLY ? Object.class : getterType); 4.32 } 4.33 4.34 /** 4.35 @@ -317,7 +310,7 @@ 4.36 */ 4.37 public AccessorProperty(final String key, final int flags, final Class<?> structure, final int slot, final Class<?> initialType) { 4.38 this(key, flags, structure, slot); 4.39 - setCurrentType(OBJECT_FIELDS_ONLY ? Object.class : initialType); 4.40 + setType(OBJECT_FIELDS_ONLY ? Object.class : initialType); 4.41 } 4.42 4.43 /** 4.44 @@ -330,13 +323,13 @@ 4.45 protected AccessorProperty(final AccessorProperty property, final Class<?> newType) { 4.46 super(property, property.getFlags()); 4.47 4.48 - this.GETTER_CACHE = newType != property.getCurrentType() ? new MethodHandle[NOOF_TYPES] : property.GETTER_CACHE; 4.49 + this.GETTER_CACHE = newType != property.getLocalType() ? new MethodHandle[NOOF_TYPES] : property.GETTER_CACHE; 4.50 this.primitiveGetter = property.primitiveGetter; 4.51 this.primitiveSetter = property.primitiveSetter; 4.52 this.objectGetter = property.objectGetter; 4.53 this.objectSetter = property.objectSetter; 4.54 4.55 - setCurrentType(newType); 4.56 + setType(newType); 4.57 } 4.58 4.59 /** 4.60 @@ -345,7 +338,7 @@ 4.61 * @param property source property 4.62 */ 4.63 protected AccessorProperty(final AccessorProperty property) { 4.64 - this(property, property.getCurrentType()); 4.65 + this(property, property.getLocalType()); 4.66 } 4.67 4.68 /** 4.69 @@ -354,7 +347,7 @@ 4.70 * @param initialValue initial value 4.71 */ 4.72 protected final void setInitialValue(final ScriptObject owner, final Object initialValue) { 4.73 - setCurrentType(JSType.unboxedFieldType(initialValue)); 4.74 + setType(JSType.unboxedFieldType(initialValue)); 4.75 if (initialValue instanceof Integer) { 4.76 invokeSetter(owner, ((Integer)initialValue).intValue()); 4.77 } else if (initialValue instanceof Long) { 4.78 @@ -370,7 +363,7 @@ 4.79 * Initialize the type of a property 4.80 */ 4.81 protected final void initializeType() { 4.82 - setCurrentType(OBJECT_FIELDS_ONLY ? Object.class : null); 4.83 + setType(OBJECT_FIELDS_ONLY ? Object.class : null); 4.84 } 4.85 4.86 private void readObject(final ObjectInputStream s) throws IOException, ClassNotFoundException { 4.87 @@ -557,12 +550,12 @@ 4.88 } else { 4.89 getter = debug( 4.90 createGetter( 4.91 - getCurrentType(), 4.92 + getLocalType(), 4.93 type, 4.94 primitiveGetter, 4.95 objectGetter, 4.96 INVALID_PROGRAM_POINT), 4.97 - getCurrentType(), 4.98 + getLocalType(), 4.99 type, 4.100 "get"); 4.101 getterCache[i] = getter; 4.102 @@ -582,18 +575,18 @@ 4.103 4.104 return debug( 4.105 createGetter( 4.106 - getCurrentType(), 4.107 + getLocalType(), 4.108 type, 4.109 primitiveGetter, 4.110 objectGetter, 4.111 programPoint), 4.112 - getCurrentType(), 4.113 + getLocalType(), 4.114 type, 4.115 "get"); 4.116 } 4.117 4.118 private MethodHandle getOptimisticPrimitiveGetter(final Class<?> type, final int programPoint) { 4.119 - final MethodHandle g = getGetter(getCurrentType()); 4.120 + final MethodHandle g = getGetter(getLocalType()); 4.121 return MH.asType(OptimisticReturnFilters.filterOptimisticReturnValue(g, type, programPoint), g.type().changeReturnType(type)); 4.122 } 4.123 4.124 @@ -631,7 +624,7 @@ 4.125 } 4.126 4.127 private MethodHandle generateSetter(final Class<?> forType, final Class<?> type) { 4.128 - return debug(createSetter(forType, type, primitiveSetter, objectSetter), getCurrentType(), type, "set"); 4.129 + return debug(createSetter(forType, type, primitiveSetter, objectSetter), getLocalType(), type, "set"); 4.130 } 4.131 4.132 /** 4.133 @@ -639,7 +632,7 @@ 4.134 * @return true if undefined 4.135 */ 4.136 protected final boolean isUndefined() { 4.137 - return getCurrentType() == null; 4.138 + return getLocalType() == null; 4.139 } 4.140 4.141 @Override 4.142 @@ -647,7 +640,7 @@ 4.143 checkUndeclared(); 4.144 4.145 final int typeIndex = getAccessorTypeIndex(type); 4.146 - final int currentTypeIndex = getAccessorTypeIndex(getCurrentType()); 4.147 + final int currentTypeIndex = getAccessorTypeIndex(getLocalType()); 4.148 4.149 //if we are asking for an object setter, but are still a primitive type, we might try to box it 4.150 MethodHandle mh; 4.151 @@ -656,13 +649,13 @@ 4.152 final PropertyMap newMap = getWiderMap(currentMap, newProperty); 4.153 4.154 final MethodHandle widerSetter = newProperty.getSetter(type, newMap); 4.155 - final Class<?> ct = getCurrentType(); 4.156 + final Class<?> ct = getLocalType(); 4.157 mh = MH.filterArguments(widerSetter, 0, MH.insertArguments(debugReplace(ct, type, currentMap, newMap) , 1, newMap)); 4.158 if (ct != null && ct.isPrimitive() && !type.isPrimitive()) { 4.159 mh = ObjectClassGenerator.createGuardBoxedPrimitiveSetter(ct, generateSetter(ct, ct), mh); 4.160 } 4.161 } else { 4.162 - final Class<?> forType = isUndefined() ? type : getCurrentType(); 4.163 + final Class<?> forType = isUndefined() ? type : getLocalType(); 4.164 mh = generateSetter(!forType.isPrimitive() ? Object.class : forType, type); 4.165 } 4.166 4.167 @@ -681,24 +674,13 @@ 4.168 return false; 4.169 } 4.170 // Return true for currently undefined even if non-writable/configurable to allow initialization of ES6 CONST. 4.171 - return getCurrentType() == null || (getCurrentType() != Object.class && (isConfigurable() || isWritable())); 4.172 + return getLocalType() == null || (getLocalType() != Object.class && (isConfigurable() || isWritable())); 4.173 } 4.174 4.175 private boolean needsInvalidator(final int typeIndex, final int currentTypeIndex) { 4.176 return canChangeType() && typeIndex > currentTypeIndex; 4.177 } 4.178 4.179 - @Override 4.180 - public final void setCurrentType(final Class<?> currentType) { 4.181 - assert currentType != boolean.class : "no boolean storage support yet - fix this"; 4.182 - this.currentType = currentType == null ? null : currentType.isPrimitive() ? currentType : Object.class; 4.183 - } 4.184 - 4.185 - @Override 4.186 - public Class<?> getCurrentType() { 4.187 - return currentType; 4.188 - } 4.189 - 4.190 private MethodHandle debug(final MethodHandle mh, final Class<?> forType, final Class<?> type, final String tag) { 4.191 if (!Context.DEBUG || !Global.hasInstance()) { 4.192 return mh;
5.1 --- a/src/jdk/nashorn/internal/runtime/FindProperty.java Tue Oct 28 17:22:17 2014 +0530 5.2 +++ b/src/jdk/nashorn/internal/runtime/FindProperty.java Fri Oct 31 16:27:58 2014 +0100 5.3 @@ -84,13 +84,18 @@ 5.4 * @return method handle for the getter 5.5 */ 5.6 public MethodHandle getGetter(final Class<?> type, final int programPoint, final LinkRequest request) { 5.7 - final MethodHandle getter; 5.8 + MethodHandle getter; 5.9 if (isValid(programPoint)) { 5.10 getter = property.getOptimisticGetter(type, programPoint); 5.11 } else { 5.12 getter = property.getGetter(type); 5.13 } 5.14 if (property instanceof UserAccessorProperty) { 5.15 + getter = MH.insertArguments(getter, 1, UserAccessorProperty.getINVOKE_UA_GETTER(type, programPoint)); 5.16 + if (isValid(programPoint) && type.isPrimitive()) { 5.17 + getter = MH.insertArguments(getter, 1, programPoint); 5.18 + } 5.19 + property.setType(type); 5.20 return insertAccessorsGetter((UserAccessorProperty) property, request, getter); 5.21 } 5.22 return getter; 5.23 @@ -111,7 +116,8 @@ 5.24 public MethodHandle getSetter(final Class<?> type, final boolean strict, final LinkRequest request) { 5.25 MethodHandle setter = property.getSetter(type, getOwner().getMap()); 5.26 if (property instanceof UserAccessorProperty) { 5.27 - setter = MH.insertArguments(setter, 1, strict ? property.getKey() : null); 5.28 + setter = MH.insertArguments(setter, 1, UserAccessorProperty.getINVOKE_UA_SETTER(type), strict ? property.getKey() : null); 5.29 + property.setType(type); 5.30 return insertAccessorsGetter((UserAccessorProperty) property, request, setter); 5.31 } 5.32
6.1 --- a/src/jdk/nashorn/internal/runtime/Property.java Tue Oct 28 17:22:17 2014 +0530 6.2 +++ b/src/jdk/nashorn/internal/runtime/Property.java Fri Oct 31 16:27:58 2014 +0100 6.3 @@ -102,6 +102,13 @@ 6.4 /** Property field number or spill slot. */ 6.5 private final int slot; 6.6 6.7 + /** 6.8 + * Current type of this object, in object only mode, this is an Object.class. In dual-fields mode 6.9 + * null means undefined, and primitive types are allowed. The reason a special type is used for 6.10 + * undefined, is that are no bits left to represent it in primitive types 6.11 + */ 6.12 + private Class<?> type; 6.13 + 6.14 /** SwitchPoint that is invalidated when property is changed, optional */ 6.15 protected transient SwitchPoint builtinSwitchPoint; 6.16 6.17 @@ -536,7 +543,7 @@ 6.18 * <p> 6.19 * see {@link ObjectClassGenerator#createSetter(Class, Class, MethodHandle, MethodHandle)} 6.20 * if you are interested in the internal details of this. Note that if you 6.21 - * are running in default mode, with {@code -Dnashorn.fields.dual=true}, disabled, the setters 6.22 + * are running with {@code -Dnashorn.fields.objects=true}, the setters 6.23 * will currently never change, as all properties are represented as Object field, 6.24 * the Object fields are Initialized to {@code ScriptRuntime.UNDEFINED} and primitives are 6.25 * boxed/unboxed upon every access, which is not necessarily optimal 6.26 @@ -569,7 +576,7 @@ 6.27 6.28 @Override 6.29 public int hashCode() { 6.30 - final Class<?> type = getCurrentType(); 6.31 + final Class<?> type = getLocalType(); 6.32 return Objects.hashCode(this.key) ^ flags ^ getSlot() ^ (type == null ? 0 : type.hashCode()); 6.33 } 6.34 6.35 @@ -586,7 +593,7 @@ 6.36 final Property otherProperty = (Property)other; 6.37 6.38 return equalsWithoutType(otherProperty) && 6.39 - getCurrentType() == otherProperty.getCurrentType(); 6.40 + getLocalType() == otherProperty.getLocalType(); 6.41 } 6.42 6.43 boolean equalsWithoutType(final Property otherProperty) { 6.44 @@ -615,7 +622,7 @@ 6.45 */ 6.46 public final String toStringShort() { 6.47 final StringBuilder sb = new StringBuilder(); 6.48 - final Class<?> type = getCurrentType(); 6.49 + final Class<?> type = getLocalType(); 6.50 sb.append(getKey()).append(" (").append(type(type)).append(')'); 6.51 return sb.toString(); 6.52 } 6.53 @@ -632,7 +639,7 @@ 6.54 @Override 6.55 public String toString() { 6.56 final StringBuilder sb = new StringBuilder(); 6.57 - final Class<?> type = getCurrentType(); 6.58 + final Class<?> type = getLocalType(); 6.59 6.60 sb.append(indent(getKey(), 20)). 6.61 append(" id="). 6.62 @@ -656,20 +663,40 @@ 6.63 } 6.64 6.65 /** 6.66 - * Get the current type of this field. If you are not running with dual fields enabled, 6.67 + * Get the current type of this property. If you are running with object fields enabled, 6.68 * this will always be Object.class. See the value representation explanation in 6.69 * {@link Property#getSetter(Class, PropertyMap)} and {@link ObjectClassGenerator} 6.70 * for more information. 6.71 * 6.72 + * <p>Note that for user accessor properties, this returns the type of the last observed 6.73 + * value passed to or returned by a user accessor. Use {@link #getLocalType()} to always get 6.74 + * the type of the actual value stored in the property slot.</p> 6.75 + * 6.76 * @return current type of property, null means undefined 6.77 */ 6.78 - public abstract Class<?> getCurrentType(); 6.79 + public final Class<?> getType() { 6.80 + return type; 6.81 + } 6.82 6.83 /** 6.84 - * Reset the current type of this property 6.85 - * @param currentType new current type 6.86 + * Set the type of this property. 6.87 + * @param type new type 6.88 */ 6.89 - public abstract void setCurrentType(final Class<?> currentType); 6.90 + public final void setType(final Class<?> type) { 6.91 + assert type != boolean.class : "no boolean storage support yet - fix this"; 6.92 + this.type = type == null ? null : type.isPrimitive() ? type : Object.class; 6.93 + } 6.94 + 6.95 + /** 6.96 + * Get the type of the value in the local property slot. This returns the same as 6.97 + * {@link #getType()} for normal properties, but always returns {@code Object.class} 6.98 + * for {@link UserAccessorProperty}s as their local type is a pair of accessor references. 6.99 + * 6.100 + * @return the local property type 6.101 + */ 6.102 + protected Class<?> getLocalType() { 6.103 + return getType(); 6.104 + } 6.105 6.106 /** 6.107 * Check whether this Property can ever change its type. The default is false, and if
7.1 --- a/src/jdk/nashorn/internal/runtime/PropertyMap.java Tue Oct 28 17:22:17 2014 +0530 7.2 +++ b/src/jdk/nashorn/internal/runtime/PropertyMap.java Fri Oct 31 16:27:58 2014 +0100 7.3 @@ -512,7 +512,7 @@ 7.4 assert sameType || 7.5 oldProperty instanceof AccessorProperty && 7.6 newProperty instanceof UserAccessorProperty : 7.7 - "arbitrary replaceProperty attempted " + sameType + " oldProperty=" + oldProperty.getClass() + " newProperty=" + newProperty.getClass() + " [" + oldProperty.getCurrentType() + " => " + newProperty.getCurrentType() + "]"; 7.8 + "arbitrary replaceProperty attempted " + sameType + " oldProperty=" + oldProperty.getClass() + " newProperty=" + newProperty.getClass() + " [" + oldProperty.getLocalType() + " => " + newProperty.getLocalType() + "]"; 7.9 7.10 newMap.flags = flags; 7.11
8.1 --- a/src/jdk/nashorn/internal/runtime/ScriptObject.java Tue Oct 28 17:22:17 2014 +0530 8.2 +++ b/src/jdk/nashorn/internal/runtime/ScriptObject.java Fri Oct 31 16:27:58 2014 +0100 8.3 @@ -969,7 +969,7 @@ 8.4 final UserAccessorProperty uc = (UserAccessorProperty)oldProperty; 8.5 final int slot = uc.getSlot(); 8.6 8.7 - assert uc.getCurrentType() == Object.class; 8.8 + assert uc.getLocalType() == Object.class; 8.9 if (slot >= spillLength) { 8.10 uc.setAccessors(this, getMap(), new UserAccessorProperty.Accessors(getter, setter)); 8.11 } else {
9.1 --- a/src/jdk/nashorn/internal/runtime/SpillProperty.java Tue Oct 28 17:22:17 2014 +0530 9.2 +++ b/src/jdk/nashorn/internal/runtime/SpillProperty.java Fri Oct 31 16:27:58 2014 +0100 9.3 @@ -161,12 +161,12 @@ 9.4 */ 9.5 public SpillProperty(final String key, final int flags, final int slot) { 9.6 super(key, flags, slot, primitiveGetter(slot), primitiveSetter(slot), objectGetter(slot), objectSetter(slot)); 9.7 - assert !OBJECT_FIELDS_ONLY || getCurrentType() == Object.class; 9.8 + assert !OBJECT_FIELDS_ONLY || getLocalType() == Object.class; 9.9 } 9.10 9.11 SpillProperty(final String key, final int flags, final int slot, final Class<?> initialType) { 9.12 this(key, flags, slot); 9.13 - setCurrentType(OBJECT_FIELDS_ONLY ? Object.class : initialType); 9.14 + setType(OBJECT_FIELDS_ONLY ? Object.class : initialType); 9.15 } 9.16 9.17 SpillProperty(final String key, final int flags, final int slot, final ScriptObject owner, final Object initialValue) {
10.1 --- a/src/jdk/nashorn/internal/runtime/UserAccessorProperty.java Tue Oct 28 17:22:17 2014 +0530 10.2 +++ b/src/jdk/nashorn/internal/runtime/UserAccessorProperty.java Fri Oct 31 16:27:58 2014 +0100 10.3 @@ -27,16 +27,16 @@ 10.4 10.5 import static jdk.nashorn.internal.lookup.Lookup.MH; 10.6 import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; 10.7 -import static jdk.nashorn.internal.runtime.JSType.CONVERT_OBJECT_OPTIMISTIC; 10.8 -import static jdk.nashorn.internal.runtime.JSType.getAccessorTypeIndex; 10.9 import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED; 10.10 +import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT; 10.11 +import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_PROGRAM_POINT_SHIFT; 10.12 10.13 import java.lang.invoke.MethodHandle; 10.14 import java.lang.invoke.MethodHandles; 10.15 import java.lang.invoke.MethodType; 10.16 -import java.util.concurrent.Callable; 10.17 import jdk.nashorn.internal.lookup.Lookup; 10.18 import jdk.nashorn.internal.runtime.linker.Bootstrap; 10.19 +import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor; 10.20 10.21 /** 10.22 * Property with user defined getters/setters. Actual getter and setter 10.23 @@ -69,38 +69,29 @@ 10.24 private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup(); 10.25 10.26 /** Getter method handle */ 10.27 - private final static MethodHandle INVOKE_GETTER_ACCESSOR = findOwnMH_S("invokeGetterAccessor", Object.class, Accessors.class, Object.class); 10.28 + private final static MethodHandle INVOKE_OBJECT_GETTER = findOwnMH_S("invokeObjectGetter", Object.class, Accessors.class, MethodHandle.class, Object.class); 10.29 + private final static MethodHandle INVOKE_INT_GETTER = findOwnMH_S("invokeIntGetter", int.class, Accessors.class, MethodHandle.class, int.class, Object.class); 10.30 + private final static MethodHandle INVOKE_LONG_GETTER = findOwnMH_S("invokeLongGetter", long.class, Accessors.class, MethodHandle.class, int.class, Object.class); 10.31 + private final static MethodHandle INVOKE_NUMBER_GETTER = findOwnMH_S("invokeNumberGetter", double.class, Accessors.class, MethodHandle.class, int.class, Object.class); 10.32 10.33 /** Setter method handle */ 10.34 - private final static MethodHandle INVOKE_SETTER_ACCESSOR = findOwnMH_S("invokeSetterAccessor", void.class, Accessors.class, String.class, Object.class, Object.class); 10.35 + private final static MethodHandle INVOKE_OBJECT_SETTER = findOwnMH_S("invokeObjectSetter", void.class, Accessors.class, MethodHandle.class, String.class, Object.class, Object.class); 10.36 + private final static MethodHandle INVOKE_INT_SETTER = findOwnMH_S("invokeIntSetter", void.class, Accessors.class, MethodHandle.class, String.class, Object.class, int.class); 10.37 + private final static MethodHandle INVOKE_LONG_SETTER = findOwnMH_S("invokeLongSetter", void.class, Accessors.class, MethodHandle.class, String.class, Object.class, long.class); 10.38 + private final static MethodHandle INVOKE_NUMBER_SETTER = findOwnMH_S("invokeNumberSetter", void.class, Accessors.class, MethodHandle.class, String.class, Object.class, double.class); 10.39 10.40 - /** Dynamic invoker for getter */ 10.41 - private static final Object GETTER_INVOKER_KEY = new Object(); 10.42 10.43 - private static MethodHandle getINVOKE_UA_GETTER() { 10.44 - 10.45 - return Context.getGlobal().getDynamicInvoker(GETTER_INVOKER_KEY, 10.46 - new Callable<MethodHandle>() { 10.47 - @Override 10.48 - public MethodHandle call() { 10.49 - return Bootstrap.createDynamicInvoker("dyn:call", Object.class, 10.50 - Object.class, Object.class); 10.51 - } 10.52 - }); 10.53 + static MethodHandle getINVOKE_UA_GETTER(final Class<?> returnType, final int programPoint) { 10.54 + if (UnwarrantedOptimismException.isValid(programPoint)) { 10.55 + final int flags = NashornCallSiteDescriptor.CALLSITE_OPTIMISTIC | programPoint << CALLSITE_PROGRAM_POINT_SHIFT; 10.56 + return Bootstrap.createDynamicInvoker("dyn:call", flags, returnType, Object.class, Object.class); 10.57 + } else { 10.58 + return Bootstrap.createDynamicInvoker("dyn:call", Object.class, Object.class, Object.class); 10.59 + } 10.60 } 10.61 10.62 - /** Dynamic invoker for setter */ 10.63 - private static Object SETTER_INVOKER_KEY = new Object(); 10.64 - 10.65 - private static MethodHandle getINVOKE_UA_SETTER() { 10.66 - return Context.getGlobal().getDynamicInvoker(SETTER_INVOKER_KEY, 10.67 - new Callable<MethodHandle>() { 10.68 - @Override 10.69 - public MethodHandle call() { 10.70 - return Bootstrap.createDynamicInvoker("dyn:call", void.class, 10.71 - Object.class, Object.class, Object.class); 10.72 - } 10.73 - }); 10.74 + static MethodHandle getINVOKE_UA_SETTER(final Class<?> valueType) { 10.75 + return Bootstrap.createDynamicInvoker("dyn:call", void.class, Object.class, Object.class, valueType); 10.76 } 10.77 10.78 /** 10.79 @@ -158,7 +149,7 @@ 10.80 } 10.81 10.82 @Override 10.83 - public Class<?> getCurrentType() { 10.84 + protected Class<?> getLocalType() { 10.85 return Object.class; 10.86 } 10.87 10.88 @@ -189,7 +180,13 @@ 10.89 10.90 @Override 10.91 public Object getObjectValue(final ScriptObject self, final ScriptObject owner) { 10.92 - return invokeGetterAccessor(getAccessors((owner != null) ? owner : self), self); 10.93 + try { 10.94 + return invokeObjectGetter(getAccessors((owner != null) ? owner : self), getINVOKE_UA_GETTER(Object.class, INVALID_PROGRAM_POINT), self); 10.95 + } catch (final Error | RuntimeException t) { 10.96 + throw t; 10.97 + } catch (final Throwable t) { 10.98 + throw new RuntimeException(t); 10.99 + } 10.100 } 10.101 10.102 @Override 10.103 @@ -209,41 +206,33 @@ 10.104 10.105 @Override 10.106 public void setValue(final ScriptObject self, final ScriptObject owner, final Object value, final boolean strict) { 10.107 - invokeSetterAccessor(getAccessors((owner != null) ? owner : self), strict ? getKey() : null, self, value); 10.108 + try { 10.109 + invokeObjectSetter(getAccessors((owner != null) ? owner : self), getINVOKE_UA_SETTER(Object.class), strict ? getKey() : null, self, value); 10.110 + } catch (final Error | RuntimeException t) { 10.111 + throw t; 10.112 + } catch (final Throwable t) { 10.113 + throw new RuntimeException(t); 10.114 + } 10.115 } 10.116 10.117 @Override 10.118 public MethodHandle getGetter(final Class<?> type) { 10.119 //this returns a getter on the format (Accessors, Object receiver) 10.120 - return Lookup.filterReturnType(INVOKE_GETTER_ACCESSOR, type); 10.121 + return Lookup.filterReturnType(INVOKE_OBJECT_GETTER, type); 10.122 } 10.123 10.124 @Override 10.125 public MethodHandle getOptimisticGetter(final Class<?> type, final int programPoint) { 10.126 - //fortype is always object, but in the optimistic world we have to throw 10.127 - //unwarranted optimism exception for narrower types. We can improve this 10.128 - //by checking for boxed types and unboxing them, but it is doubtful that 10.129 - //this gives us any performance, as UserAccessorProperties are typically not 10.130 - //primitives. Are there? TODO: investigate later. For now we just throw an 10.131 - //exception for narrower types than object 10.132 - 10.133 - if (type.isPrimitive()) { 10.134 - final MethodHandle getter = getGetter(Object.class); 10.135 - final MethodHandle mh = 10.136 - MH.asType( 10.137 - MH.filterReturnValue( 10.138 - getter, 10.139 - MH.insertArguments( 10.140 - CONVERT_OBJECT_OPTIMISTIC.get(getAccessorTypeIndex(type)), 10.141 - 1, 10.142 - programPoint)), 10.143 - getter.type().changeReturnType(type)); 10.144 - 10.145 - return mh; 10.146 + if (type == int.class) { 10.147 + return INVOKE_INT_GETTER; 10.148 + } else if (type == long.class) { 10.149 + return INVOKE_LONG_GETTER; 10.150 + } else if (type == double.class) { 10.151 + return INVOKE_NUMBER_GETTER; 10.152 + } else { 10.153 + assert type == Object.class; 10.154 + return INVOKE_OBJECT_GETTER; 10.155 } 10.156 - 10.157 - assert type == Object.class; 10.158 - return getGetter(type); 10.159 } 10.160 10.161 @Override 10.162 @@ -259,7 +248,16 @@ 10.163 10.164 @Override 10.165 public MethodHandle getSetter(final Class<?> type, final PropertyMap currentMap) { 10.166 - return INVOKE_SETTER_ACCESSOR; 10.167 + if (type == int.class) { 10.168 + return INVOKE_INT_SETTER; 10.169 + } else if (type == long.class) { 10.170 + return INVOKE_LONG_SETTER; 10.171 + } else if (type == double.class) { 10.172 + return INVOKE_NUMBER_SETTER; 10.173 + } else { 10.174 + assert type == Object.class; 10.175 + return INVOKE_OBJECT_SETTER; 10.176 + } 10.177 } 10.178 10.179 @Override 10.180 @@ -282,31 +280,81 @@ 10.181 // getter/setter may be inherited. If so, proto is bound during lookup. In either 10.182 // inherited or self case, slot is also bound during lookup. Actual ScriptFunction 10.183 // to be called is retrieved everytime and applied. 10.184 - private static Object invokeGetterAccessor(final Accessors gs, final Object self) { 10.185 + @SuppressWarnings("unused") 10.186 + private static Object invokeObjectGetter(final Accessors gs, final MethodHandle invoker, final Object self) throws Throwable { 10.187 final Object func = gs.getter; 10.188 if (func instanceof ScriptFunction) { 10.189 - try { 10.190 - return getINVOKE_UA_GETTER().invokeExact(func, self); 10.191 - } catch (final Error | RuntimeException t) { 10.192 - throw t; 10.193 - } catch (final Throwable t) { 10.194 - throw new RuntimeException(t); 10.195 - } 10.196 + return invoker.invokeExact(func, self); 10.197 } 10.198 10.199 return UNDEFINED; 10.200 } 10.201 10.202 - private static void invokeSetterAccessor(final Accessors gs, final String name, final Object self, final Object value) { 10.203 + @SuppressWarnings("unused") 10.204 + private static int invokeIntGetter(final Accessors gs, final MethodHandle invoker, final int programPoint, final Object self) throws Throwable { 10.205 + final Object func = gs.getter; 10.206 + if (func instanceof ScriptFunction) { 10.207 + return (int) invoker.invokeExact(func, self); 10.208 + } 10.209 + 10.210 + throw new UnwarrantedOptimismException(UNDEFINED, programPoint); 10.211 + } 10.212 + 10.213 + @SuppressWarnings("unused") 10.214 + private static long invokeLongGetter(final Accessors gs, final MethodHandle invoker, final int programPoint, final Object self) throws Throwable { 10.215 + final Object func = gs.getter; 10.216 + if (func instanceof ScriptFunction) { 10.217 + return (long) invoker.invokeExact(func, self); 10.218 + } 10.219 + 10.220 + throw new UnwarrantedOptimismException(UNDEFINED, programPoint); 10.221 + } 10.222 + 10.223 + @SuppressWarnings("unused") 10.224 + private static double invokeNumberGetter(final Accessors gs, final MethodHandle invoker, final int programPoint, final Object self) throws Throwable { 10.225 + final Object func = gs.getter; 10.226 + if (func instanceof ScriptFunction) { 10.227 + return (double) invoker.invokeExact(func, self); 10.228 + } 10.229 + 10.230 + throw new UnwarrantedOptimismException(UNDEFINED, programPoint); 10.231 + } 10.232 + 10.233 + @SuppressWarnings("unused") 10.234 + private static void invokeObjectSetter(final Accessors gs, final MethodHandle invoker, final String name, final Object self, final Object value) throws Throwable { 10.235 final Object func = gs.setter; 10.236 if (func instanceof ScriptFunction) { 10.237 - try { 10.238 - getINVOKE_UA_SETTER().invokeExact(func, self, value); 10.239 - } catch (final Error | RuntimeException t) { 10.240 - throw t; 10.241 - } catch (final Throwable t) { 10.242 - throw new RuntimeException(t); 10.243 - } 10.244 + invoker.invokeExact(func, self, value); 10.245 + } else if (name != null) { 10.246 + throw typeError("property.has.no.setter", name, ScriptRuntime.safeToString(self)); 10.247 + } 10.248 + } 10.249 + 10.250 + @SuppressWarnings("unused") 10.251 + private static void invokeIntSetter(final Accessors gs, final MethodHandle invoker, final String name, final Object self, final int value) throws Throwable { 10.252 + final Object func = gs.setter; 10.253 + if (func instanceof ScriptFunction) { 10.254 + invoker.invokeExact(func, self, value); 10.255 + } else if (name != null) { 10.256 + throw typeError("property.has.no.setter", name, ScriptRuntime.safeToString(self)); 10.257 + } 10.258 + } 10.259 + 10.260 + @SuppressWarnings("unused") 10.261 + private static void invokeLongSetter(final Accessors gs, final MethodHandle invoker, final String name, final Object self, final long value) throws Throwable { 10.262 + final Object func = gs.setter; 10.263 + if (func instanceof ScriptFunction) { 10.264 + invoker.invokeExact(func, self, value); 10.265 + } else if (name != null) { 10.266 + throw typeError("property.has.no.setter", name, ScriptRuntime.safeToString(self)); 10.267 + } 10.268 + } 10.269 + 10.270 + @SuppressWarnings("unused") 10.271 + private static void invokeNumberSetter(final Accessors gs, final MethodHandle invoker, final String name, final Object self, final double value) throws Throwable { 10.272 + final Object func = gs.setter; 10.273 + if (func instanceof ScriptFunction) { 10.274 + invoker.invokeExact(func, self, value); 10.275 } else if (name != null) { 10.276 throw typeError("property.has.no.setter", name, ScriptRuntime.safeToString(self)); 10.277 }
11.1 --- a/src/jdk/nashorn/internal/runtime/linker/Bootstrap.java Tue Oct 28 17:22:17 2014 +0530 11.2 +++ b/src/jdk/nashorn/internal/runtime/linker/Bootstrap.java Fri Oct 31 16:27:58 2014 +0100 11.3 @@ -337,6 +337,20 @@ 11.4 11.5 /** 11.6 * Returns a dynamic invoker for a specified dynamic operation using the public lookup. Similar to 11.7 + * {@link #createDynamicInvoker(String, Class, Class...)} but with an additional parameter to 11.8 + * set the call site flags of the dynamic invoker. 11.9 + * @param opDesc Dynalink dynamic operation descriptor. 11.10 + * @param flags the call site flags for the operation 11.11 + * @param rtype the return type for the operation 11.12 + * @param ptypes the parameter types for the operation 11.13 + * @return MethodHandle for invoking the operation. 11.14 + */ 11.15 + public static MethodHandle createDynamicInvoker(final String opDesc, final int flags, final Class<?> rtype, final Class<?>... ptypes) { 11.16 + return bootstrap(MethodHandles.publicLookup(), opDesc, MethodType.methodType(rtype, ptypes), flags).dynamicInvoker(); 11.17 + } 11.18 + 11.19 + /** 11.20 + * Returns a dynamic invoker for a specified dynamic operation using the public lookup. Similar to 11.21 * {@link #createDynamicInvoker(String, Class, Class...)} but with return and parameter types composed into a 11.22 * method type in the signature. See the discussion of that method for details. 11.23 * @param opDesc Dynalink dynamic operation descriptor.
12.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 12.2 +++ b/test/examples/getter-setter-micro.js Fri Oct 31 16:27:58 2014 +0100 12.3 @@ -0,0 +1,81 @@ 12.4 +/* 12.5 + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. 12.6 + * 12.7 + * Redistribution and use in source and binary forms, with or without 12.8 + * modification, are permitted provided that the following conditions 12.9 + * are met: 12.10 + * 12.11 + * - Redistributions of source code must retain the above copyright 12.12 + * notice, this list of conditions and the following disclaimer. 12.13 + * 12.14 + * - Redistributions in binary form must reproduce the above copyright 12.15 + * notice, this list of conditions and the following disclaimer in the 12.16 + * documentation and/or other materials provided with the distribution. 12.17 + * 12.18 + * - Neither the name of Oracle nor the names of its 12.19 + * contributors may be used to endorse or promote products derived 12.20 + * from this software without specific prior written permission. 12.21 + * 12.22 + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 12.23 + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 12.24 + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 12.25 + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 12.26 + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 12.27 + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 12.28 + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 12.29 + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 12.30 + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 12.31 + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 12.32 + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 12.33 + */ 12.34 + 12.35 +/* 12.36 + * A micro-benchmark for getters and setters with primitive values, 12.37 + * alternating between ints and doubles. Introduction of primitive 12.38 + * and optimistic user accessors in JDK-8062401 make this faster by 12.39 + * 10x or more by allowing inlining and other optimizations to take place. 12.40 + */ 12.41 + 12.42 +var x = { 12.43 + get m() { 12.44 + return this._m; 12.45 + }, 12.46 + set m(v) { 12.47 + this._m = v; 12.48 + }, 12.49 + get n() { 12.50 + return this._n; 12.51 + }, 12.52 + set n(v) { 12.53 + this._n = v; 12.54 + } 12.55 +}; 12.56 + 12.57 + 12.58 +function bench(v1, v2, result) { 12.59 + var start = Date.now(); 12.60 + x.n = v1; 12.61 + for (var i = 0; i < 1e8; i++) { 12.62 + x.m = v2; 12.63 + if (x.m + x.n !== result) { 12.64 + throw "wrong result"; 12.65 + } 12.66 + } 12.67 + print("done in", Date.now() - start, "millis"); 12.68 +} 12.69 + 12.70 +for (var i = 0; i < 10; i++) { 12.71 + bench(i, 4, 4 + i); 12.72 +} 12.73 + 12.74 +for (var i = 0; i < 10; i++) { 12.75 + bench(i, 4.5, 4.5 + i); 12.76 +} 12.77 + 12.78 +for (var i = 0; i < 10; i++) { 12.79 + bench(i, 5, 5 + i); 12.80 +} 12.81 + 12.82 +for (var i = 0; i < 10; i++) { 12.83 + bench(i, 5.5, 5.5 + i); 12.84 +}