提交 333ac25a 编写于 作者: H hannesw

8062401: User accessors require boxing and do not support optimistic types

Reviewed-by: jlaskey, lagergren
上级 81a80707
...@@ -88,7 +88,7 @@ public final class SpillObjectCreator extends ObjectCreator<Expression> { ...@@ -88,7 +88,7 @@ public final class SpillObjectCreator extends ObjectCreator<Expression> {
final Property property = propertyMap.findProperty(key); final Property property = propertyMap.findProperty(key);
if (property != null) { if (property != null) {
// normal property key // normal property key
property.setCurrentType(JSType.unboxedFieldType(constantValue)); property.setType(JSType.unboxedFieldType(constantValue));
final int slot = property.getSlot(); final int slot = property.getSlot();
if (!OBJECT_FIELDS_ONLY && constantValue instanceof Number) { if (!OBJECT_FIELDS_ONLY && constantValue instanceof Number) {
jpresetValues[slot] = ObjectClassGenerator.pack((Number)constantValue); jpresetValues[slot] = ObjectClassGenerator.pack((Number)constantValue);
......
...@@ -117,7 +117,7 @@ final class TypeEvaluator { ...@@ -117,7 +117,7 @@ final class TypeEvaluator {
} }
final Property property = find.getProperty(); final Property property = find.getProperty();
final Class<?> propertyClass = property.getCurrentType(); final Class<?> propertyClass = property.getType();
if (propertyClass == null) { if (propertyClass == null) {
// propertyClass == null means its value is Undefined. It is probably not initialized yet, so we won't make // propertyClass == null means its value is Undefined. It is probably not initialized yet, so we won't make
// a type assumption yet. // a type assumption yet.
......
...@@ -672,7 +672,7 @@ public final class NativeObject { ...@@ -672,7 +672,7 @@ public final class NativeObject {
for (final Property prop : properties) { for (final Property prop : properties) {
if (prop.isEnumerable()) { if (prop.isEnumerable()) {
final Object value = sourceObj.get(prop.getKey()); final Object value = sourceObj.get(prop.getKey());
prop.setCurrentType(Object.class); prop.setType(Object.class);
prop.setValue(sourceObj, sourceObj, value, false); prop.setValue(sourceObj, sourceObj, value, false);
propList.add(prop); propList.add(prop);
} }
......
...@@ -144,13 +144,6 @@ public class AccessorProperty extends Property { ...@@ -144,13 +144,6 @@ public class AccessorProperty extends Property {
/** Seed setter for the Object version of this field */ /** Seed setter for the Object version of this field */
transient MethodHandle objectSetter; 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 * Delegate constructor for bound properties. This is used for properties created by
* {@link ScriptRuntime#mergeScope} and the Nashorn {@code Object.bindProperties} method. * {@link ScriptRuntime#mergeScope} and the Nashorn {@code Object.bindProperties} method.
...@@ -171,7 +164,7 @@ public class AccessorProperty extends Property { ...@@ -171,7 +164,7 @@ public class AccessorProperty extends Property {
this.objectSetter = bindTo(property.objectSetter, delegate); this.objectSetter = bindTo(property.objectSetter, delegate);
property.GETTER_CACHE = new MethodHandle[NOOF_TYPES]; property.GETTER_CACHE = new MethodHandle[NOOF_TYPES];
// Properties created this way are bound to a delegate // Properties created this way are bound to a delegate
setCurrentType(property.getCurrentType()); setType(property.getType());
} }
/** /**
...@@ -248,7 +241,7 @@ public class AccessorProperty extends Property { ...@@ -248,7 +241,7 @@ public class AccessorProperty extends Property {
objectGetter = getter.type() != Lookup.GET_OBJECT_TYPE ? MH.asType(getter, Lookup.GET_OBJECT_TYPE) : getter; 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; 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 class AccessorProperty extends Property { ...@@ -317,7 +310,7 @@ public class AccessorProperty extends Property {
*/ */
public AccessorProperty(final String key, final int flags, final Class<?> structure, final int slot, final Class<?> initialType) { public AccessorProperty(final String key, final int flags, final Class<?> structure, final int slot, final Class<?> initialType) {
this(key, flags, structure, slot); this(key, flags, structure, slot);
setCurrentType(OBJECT_FIELDS_ONLY ? Object.class : initialType); setType(OBJECT_FIELDS_ONLY ? Object.class : initialType);
} }
/** /**
...@@ -330,13 +323,13 @@ public class AccessorProperty extends Property { ...@@ -330,13 +323,13 @@ public class AccessorProperty extends Property {
protected AccessorProperty(final AccessorProperty property, final Class<?> newType) { protected AccessorProperty(final AccessorProperty property, final Class<?> newType) {
super(property, property.getFlags()); 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.primitiveGetter = property.primitiveGetter;
this.primitiveSetter = property.primitiveSetter; this.primitiveSetter = property.primitiveSetter;
this.objectGetter = property.objectGetter; this.objectGetter = property.objectGetter;
this.objectSetter = property.objectSetter; this.objectSetter = property.objectSetter;
setCurrentType(newType); setType(newType);
} }
/** /**
...@@ -345,7 +338,7 @@ public class AccessorProperty extends Property { ...@@ -345,7 +338,7 @@ public class AccessorProperty extends Property {
* @param property source property * @param property source property
*/ */
protected AccessorProperty(final AccessorProperty property) { protected AccessorProperty(final AccessorProperty property) {
this(property, property.getCurrentType()); this(property, property.getLocalType());
} }
/** /**
...@@ -354,7 +347,7 @@ public class AccessorProperty extends Property { ...@@ -354,7 +347,7 @@ public class AccessorProperty extends Property {
* @param initialValue initial value * @param initialValue initial value
*/ */
protected final void setInitialValue(final ScriptObject owner, final Object initialValue) { protected final void setInitialValue(final ScriptObject owner, final Object initialValue) {
setCurrentType(JSType.unboxedFieldType(initialValue)); setType(JSType.unboxedFieldType(initialValue));
if (initialValue instanceof Integer) { if (initialValue instanceof Integer) {
invokeSetter(owner, ((Integer)initialValue).intValue()); invokeSetter(owner, ((Integer)initialValue).intValue());
} else if (initialValue instanceof Long) { } else if (initialValue instanceof Long) {
...@@ -370,7 +363,7 @@ public class AccessorProperty extends Property { ...@@ -370,7 +363,7 @@ public class AccessorProperty extends Property {
* Initialize the type of a property * Initialize the type of a property
*/ */
protected final void initializeType() { 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 { private void readObject(final ObjectInputStream s) throws IOException, ClassNotFoundException {
...@@ -557,12 +550,12 @@ public class AccessorProperty extends Property { ...@@ -557,12 +550,12 @@ public class AccessorProperty extends Property {
} else { } else {
getter = debug( getter = debug(
createGetter( createGetter(
getCurrentType(), getLocalType(),
type, type,
primitiveGetter, primitiveGetter,
objectGetter, objectGetter,
INVALID_PROGRAM_POINT), INVALID_PROGRAM_POINT),
getCurrentType(), getLocalType(),
type, type,
"get"); "get");
getterCache[i] = getter; getterCache[i] = getter;
...@@ -582,18 +575,18 @@ public class AccessorProperty extends Property { ...@@ -582,18 +575,18 @@ public class AccessorProperty extends Property {
return debug( return debug(
createGetter( createGetter(
getCurrentType(), getLocalType(),
type, type,
primitiveGetter, primitiveGetter,
objectGetter, objectGetter,
programPoint), programPoint),
getCurrentType(), getLocalType(),
type, type,
"get"); "get");
} }
private MethodHandle getOptimisticPrimitiveGetter(final Class<?> type, final int programPoint) { 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)); return MH.asType(OptimisticReturnFilters.filterOptimisticReturnValue(g, type, programPoint), g.type().changeReturnType(type));
} }
...@@ -631,7 +624,7 @@ public class AccessorProperty extends Property { ...@@ -631,7 +624,7 @@ public class AccessorProperty extends Property {
} }
private MethodHandle generateSetter(final Class<?> forType, final Class<?> type) { 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 @@ public class AccessorProperty extends Property { ...@@ -639,7 +632,7 @@ public class AccessorProperty extends Property {
* @return true if undefined * @return true if undefined
*/ */
protected final boolean isUndefined() { protected final boolean isUndefined() {
return getCurrentType() == null; return getLocalType() == null;
} }
@Override @Override
...@@ -647,7 +640,7 @@ public class AccessorProperty extends Property { ...@@ -647,7 +640,7 @@ public class AccessorProperty extends Property {
checkUndeclared(); checkUndeclared();
final int typeIndex = getAccessorTypeIndex(type); 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 //if we are asking for an object setter, but are still a primitive type, we might try to box it
MethodHandle mh; MethodHandle mh;
...@@ -656,13 +649,13 @@ public class AccessorProperty extends Property { ...@@ -656,13 +649,13 @@ public class AccessorProperty extends Property {
final PropertyMap newMap = getWiderMap(currentMap, newProperty); final PropertyMap newMap = getWiderMap(currentMap, newProperty);
final MethodHandle widerSetter = newProperty.getSetter(type, newMap); 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)); mh = MH.filterArguments(widerSetter, 0, MH.insertArguments(debugReplace(ct, type, currentMap, newMap) , 1, newMap));
if (ct != null && ct.isPrimitive() && !type.isPrimitive()) { if (ct != null && ct.isPrimitive() && !type.isPrimitive()) {
mh = ObjectClassGenerator.createGuardBoxedPrimitiveSetter(ct, generateSetter(ct, ct), mh); mh = ObjectClassGenerator.createGuardBoxedPrimitiveSetter(ct, generateSetter(ct, ct), mh);
} }
} else { } else {
final Class<?> forType = isUndefined() ? type : getCurrentType(); final Class<?> forType = isUndefined() ? type : getLocalType();
mh = generateSetter(!forType.isPrimitive() ? Object.class : forType, type); mh = generateSetter(!forType.isPrimitive() ? Object.class : forType, type);
} }
...@@ -681,24 +674,13 @@ public class AccessorProperty extends Property { ...@@ -681,24 +674,13 @@ public class AccessorProperty extends Property {
return false; return false;
} }
// Return true for currently undefined even if non-writable/configurable to allow initialization of ES6 CONST. // 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) { private boolean needsInvalidator(final int typeIndex, final int currentTypeIndex) {
return canChangeType() && typeIndex > 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) { private MethodHandle debug(final MethodHandle mh, final Class<?> forType, final Class<?> type, final String tag) {
if (!Context.DEBUG || !Global.hasInstance()) { if (!Context.DEBUG || !Global.hasInstance()) {
return mh; return mh;
......
...@@ -84,13 +84,18 @@ public final class FindProperty { ...@@ -84,13 +84,18 @@ public final class FindProperty {
* @return method handle for the getter * @return method handle for the getter
*/ */
public MethodHandle getGetter(final Class<?> type, final int programPoint, final LinkRequest request) { public MethodHandle getGetter(final Class<?> type, final int programPoint, final LinkRequest request) {
final MethodHandle getter; MethodHandle getter;
if (isValid(programPoint)) { if (isValid(programPoint)) {
getter = property.getOptimisticGetter(type, programPoint); getter = property.getOptimisticGetter(type, programPoint);
} else { } else {
getter = property.getGetter(type); getter = property.getGetter(type);
} }
if (property instanceof UserAccessorProperty) { 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 insertAccessorsGetter((UserAccessorProperty) property, request, getter);
} }
return getter; return getter;
...@@ -111,7 +116,8 @@ public final class FindProperty { ...@@ -111,7 +116,8 @@ public final class FindProperty {
public MethodHandle getSetter(final Class<?> type, final boolean strict, final LinkRequest request) { public MethodHandle getSetter(final Class<?> type, final boolean strict, final LinkRequest request) {
MethodHandle setter = property.getSetter(type, getOwner().getMap()); MethodHandle setter = property.getSetter(type, getOwner().getMap());
if (property instanceof UserAccessorProperty) { 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); return insertAccessorsGetter((UserAccessorProperty) property, request, setter);
} }
......
...@@ -102,6 +102,13 @@ public abstract class Property implements Serializable { ...@@ -102,6 +102,13 @@ public abstract class Property implements Serializable {
/** Property field number or spill slot. */ /** Property field number or spill slot. */
private final int 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 */ /** SwitchPoint that is invalidated when property is changed, optional */
protected transient SwitchPoint builtinSwitchPoint; protected transient SwitchPoint builtinSwitchPoint;
...@@ -536,7 +543,7 @@ public abstract class Property implements Serializable { ...@@ -536,7 +543,7 @@ public abstract class Property implements Serializable {
* <p> * <p>
* see {@link ObjectClassGenerator#createSetter(Class, Class, MethodHandle, MethodHandle)} * see {@link ObjectClassGenerator#createSetter(Class, Class, MethodHandle, MethodHandle)}
* if you are interested in the internal details of this. Note that if you * 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, * will currently never change, as all properties are represented as Object field,
* the Object fields are Initialized to {@code ScriptRuntime.UNDEFINED} and primitives are * the Object fields are Initialized to {@code ScriptRuntime.UNDEFINED} and primitives are
* boxed/unboxed upon every access, which is not necessarily optimal * boxed/unboxed upon every access, which is not necessarily optimal
...@@ -569,7 +576,7 @@ public abstract class Property implements Serializable { ...@@ -569,7 +576,7 @@ public abstract class Property implements Serializable {
@Override @Override
public int hashCode() { public int hashCode() {
final Class<?> type = getCurrentType(); final Class<?> type = getLocalType();
return Objects.hashCode(this.key) ^ flags ^ getSlot() ^ (type == null ? 0 : type.hashCode()); return Objects.hashCode(this.key) ^ flags ^ getSlot() ^ (type == null ? 0 : type.hashCode());
} }
...@@ -586,7 +593,7 @@ public abstract class Property implements Serializable { ...@@ -586,7 +593,7 @@ public abstract class Property implements Serializable {
final Property otherProperty = (Property)other; final Property otherProperty = (Property)other;
return equalsWithoutType(otherProperty) && return equalsWithoutType(otherProperty) &&
getCurrentType() == otherProperty.getCurrentType(); getLocalType() == otherProperty.getLocalType();
} }
boolean equalsWithoutType(final Property otherProperty) { boolean equalsWithoutType(final Property otherProperty) {
...@@ -615,7 +622,7 @@ public abstract class Property implements Serializable { ...@@ -615,7 +622,7 @@ public abstract class Property implements Serializable {
*/ */
public final String toStringShort() { public final String toStringShort() {
final StringBuilder sb = new StringBuilder(); final StringBuilder sb = new StringBuilder();
final Class<?> type = getCurrentType(); final Class<?> type = getLocalType();
sb.append(getKey()).append(" (").append(type(type)).append(')'); sb.append(getKey()).append(" (").append(type(type)).append(')');
return sb.toString(); return sb.toString();
} }
...@@ -632,7 +639,7 @@ public abstract class Property implements Serializable { ...@@ -632,7 +639,7 @@ public abstract class Property implements Serializable {
@Override @Override
public String toString() { public String toString() {
final StringBuilder sb = new StringBuilder(); final StringBuilder sb = new StringBuilder();
final Class<?> type = getCurrentType(); final Class<?> type = getLocalType();
sb.append(indent(getKey(), 20)). sb.append(indent(getKey(), 20)).
append(" id="). append(" id=").
...@@ -656,20 +663,40 @@ public abstract class Property implements Serializable { ...@@ -656,20 +663,40 @@ public abstract class Property implements Serializable {
} }
/** /**
* 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 * this will always be Object.class. See the value representation explanation in
* {@link Property#getSetter(Class, PropertyMap)} and {@link ObjectClassGenerator} * {@link Property#getSetter(Class, PropertyMap)} and {@link ObjectClassGenerator}
* for more information. * for more information.
* *
* <p>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.</p>
*
* @return current type of property, null means undefined * @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 * Set the type of this property.
* @param currentType new current type * @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 * Check whether this Property can ever change its type. The default is false, and if
......
...@@ -512,7 +512,7 @@ public final class PropertyMap implements Iterable<Object>, Serializable { ...@@ -512,7 +512,7 @@ public final class PropertyMap implements Iterable<Object>, Serializable {
assert sameType || assert sameType ||
oldProperty instanceof AccessorProperty && oldProperty instanceof AccessorProperty &&
newProperty instanceof UserAccessorProperty : 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; newMap.flags = flags;
......
...@@ -969,7 +969,7 @@ public abstract class ScriptObject implements PropertyAccess { ...@@ -969,7 +969,7 @@ public abstract class ScriptObject implements PropertyAccess {
final UserAccessorProperty uc = (UserAccessorProperty)oldProperty; final UserAccessorProperty uc = (UserAccessorProperty)oldProperty;
final int slot = uc.getSlot(); final int slot = uc.getSlot();
assert uc.getCurrentType() == Object.class; assert uc.getLocalType() == Object.class;
if (slot >= spillLength) { if (slot >= spillLength) {
uc.setAccessors(this, getMap(), new UserAccessorProperty.Accessors(getter, setter)); uc.setAccessors(this, getMap(), new UserAccessorProperty.Accessors(getter, setter));
} else { } else {
......
...@@ -161,12 +161,12 @@ public class SpillProperty extends AccessorProperty { ...@@ -161,12 +161,12 @@ public class SpillProperty extends AccessorProperty {
*/ */
public SpillProperty(final String key, final int flags, final int slot) { public SpillProperty(final String key, final int flags, final int slot) {
super(key, flags, slot, primitiveGetter(slot), primitiveSetter(slot), objectGetter(slot), objectSetter(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) { SpillProperty(final String key, final int flags, final int slot, final Class<?> initialType) {
this(key, flags, slot); 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) { SpillProperty(final String key, final int flags, final int slot, final ScriptObject owner, final Object initialValue) {
......
...@@ -27,16 +27,16 @@ package jdk.nashorn.internal.runtime; ...@@ -27,16 +27,16 @@ package jdk.nashorn.internal.runtime;
import static jdk.nashorn.internal.lookup.Lookup.MH; import static jdk.nashorn.internal.lookup.Lookup.MH;
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; 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.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.MethodHandle;
import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType; import java.lang.invoke.MethodType;
import java.util.concurrent.Callable;
import jdk.nashorn.internal.lookup.Lookup; import jdk.nashorn.internal.lookup.Lookup;
import jdk.nashorn.internal.runtime.linker.Bootstrap; import jdk.nashorn.internal.runtime.linker.Bootstrap;
import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;
/** /**
* Property with user defined getters/setters. Actual getter and setter * Property with user defined getters/setters. Actual getter and setter
...@@ -69,38 +69,29 @@ public final class UserAccessorProperty extends SpillProperty { ...@@ -69,38 +69,29 @@ public final class UserAccessorProperty extends SpillProperty {
private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup(); private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
/** Getter method handle */ /** 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 */ /** 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);
/** Dynamic invoker for getter */ private final static MethodHandle INVOKE_LONG_SETTER = findOwnMH_S("invokeLongSetter", void.class, Accessors.class, MethodHandle.class, String.class, Object.class, long.class);
private static final Object GETTER_INVOKER_KEY = new Object(); private final static MethodHandle INVOKE_NUMBER_SETTER = findOwnMH_S("invokeNumberSetter", void.class, Accessors.class, MethodHandle.class, String.class, Object.class, double.class);
private static MethodHandle getINVOKE_UA_GETTER() {
static MethodHandle getINVOKE_UA_GETTER(final Class<?> returnType, final int programPoint) {
return Context.getGlobal().getDynamicInvoker(GETTER_INVOKER_KEY, if (UnwarrantedOptimismException.isValid(programPoint)) {
new Callable<MethodHandle>() { final int flags = NashornCallSiteDescriptor.CALLSITE_OPTIMISTIC | programPoint << CALLSITE_PROGRAM_POINT_SHIFT;
@Override return Bootstrap.createDynamicInvoker("dyn:call", flags, returnType, Object.class, Object.class);
public MethodHandle call() { } else {
return Bootstrap.createDynamicInvoker("dyn:call", Object.class, return Bootstrap.createDynamicInvoker("dyn:call", Object.class, Object.class, Object.class);
Object.class, Object.class); }
}
});
} }
/** Dynamic invoker for setter */ static MethodHandle getINVOKE_UA_SETTER(final Class<?> valueType) {
private static Object SETTER_INVOKER_KEY = new Object(); return Bootstrap.createDynamicInvoker("dyn:call", void.class, Object.class, Object.class, valueType);
private static MethodHandle getINVOKE_UA_SETTER() {
return Context.getGlobal().getDynamicInvoker(SETTER_INVOKER_KEY,
new Callable<MethodHandle>() {
@Override
public MethodHandle call() {
return Bootstrap.createDynamicInvoker("dyn:call", void.class,
Object.class, Object.class, Object.class);
}
});
} }
/** /**
...@@ -158,7 +149,7 @@ public final class UserAccessorProperty extends SpillProperty { ...@@ -158,7 +149,7 @@ public final class UserAccessorProperty extends SpillProperty {
} }
@Override @Override
public Class<?> getCurrentType() { protected Class<?> getLocalType() {
return Object.class; return Object.class;
} }
...@@ -189,7 +180,13 @@ public final class UserAccessorProperty extends SpillProperty { ...@@ -189,7 +180,13 @@ public final class UserAccessorProperty extends SpillProperty {
@Override @Override
public Object getObjectValue(final ScriptObject self, final ScriptObject owner) { public Object getObjectValue(final ScriptObject self, final ScriptObject owner) {
return invokeGetterAccessor(getAccessors((owner != null) ? owner : self), self); try {
return invokeObjectGetter(getAccessors((owner != null) ? owner : self), getINVOKE_UA_GETTER(Object.class, INVALID_PROGRAM_POINT), self);
} catch (final Error | RuntimeException t) {
throw t;
} catch (final Throwable t) {
throw new RuntimeException(t);
}
} }
@Override @Override
...@@ -209,41 +206,33 @@ public final class UserAccessorProperty extends SpillProperty { ...@@ -209,41 +206,33 @@ public final class UserAccessorProperty extends SpillProperty {
@Override @Override
public void setValue(final ScriptObject self, final ScriptObject owner, final Object value, final boolean strict) { public void setValue(final ScriptObject self, final ScriptObject owner, final Object value, final boolean strict) {
invokeSetterAccessor(getAccessors((owner != null) ? owner : self), strict ? getKey() : null, self, value); try {
invokeObjectSetter(getAccessors((owner != null) ? owner : self), getINVOKE_UA_SETTER(Object.class), strict ? getKey() : null, self, value);
} catch (final Error | RuntimeException t) {
throw t;
} catch (final Throwable t) {
throw new RuntimeException(t);
}
} }
@Override @Override
public MethodHandle getGetter(final Class<?> type) { public MethodHandle getGetter(final Class<?> type) {
//this returns a getter on the format (Accessors, Object receiver) //this returns a getter on the format (Accessors, Object receiver)
return Lookup.filterReturnType(INVOKE_GETTER_ACCESSOR, type); return Lookup.filterReturnType(INVOKE_OBJECT_GETTER, type);
} }
@Override @Override
public MethodHandle getOptimisticGetter(final Class<?> type, final int programPoint) { public MethodHandle getOptimisticGetter(final Class<?> type, final int programPoint) {
//fortype is always object, but in the optimistic world we have to throw if (type == int.class) {
//unwarranted optimism exception for narrower types. We can improve this return INVOKE_INT_GETTER;
//by checking for boxed types and unboxing them, but it is doubtful that } else if (type == long.class) {
//this gives us any performance, as UserAccessorProperties are typically not return INVOKE_LONG_GETTER;
//primitives. Are there? TODO: investigate later. For now we just throw an } else if (type == double.class) {
//exception for narrower types than object return INVOKE_NUMBER_GETTER;
} else {
if (type.isPrimitive()) { assert type == Object.class;
final MethodHandle getter = getGetter(Object.class); return INVOKE_OBJECT_GETTER;
final MethodHandle mh =
MH.asType(
MH.filterReturnValue(
getter,
MH.insertArguments(
CONVERT_OBJECT_OPTIMISTIC.get(getAccessorTypeIndex(type)),
1,
programPoint)),
getter.type().changeReturnType(type));
return mh;
} }
assert type == Object.class;
return getGetter(type);
} }
@Override @Override
...@@ -259,7 +248,16 @@ public final class UserAccessorProperty extends SpillProperty { ...@@ -259,7 +248,16 @@ public final class UserAccessorProperty extends SpillProperty {
@Override @Override
public MethodHandle getSetter(final Class<?> type, final PropertyMap currentMap) { public MethodHandle getSetter(final Class<?> type, final PropertyMap currentMap) {
return INVOKE_SETTER_ACCESSOR; if (type == int.class) {
return INVOKE_INT_SETTER;
} else if (type == long.class) {
return INVOKE_LONG_SETTER;
} else if (type == double.class) {
return INVOKE_NUMBER_SETTER;
} else {
assert type == Object.class;
return INVOKE_OBJECT_SETTER;
}
} }
@Override @Override
...@@ -282,31 +280,81 @@ public final class UserAccessorProperty extends SpillProperty { ...@@ -282,31 +280,81 @@ public final class UserAccessorProperty extends SpillProperty {
// getter/setter may be inherited. If so, proto is bound during lookup. In either // getter/setter may be inherited. If so, proto is bound during lookup. In either
// inherited or self case, slot is also bound during lookup. Actual ScriptFunction // inherited or self case, slot is also bound during lookup. Actual ScriptFunction
// to be called is retrieved everytime and applied. // to be called is retrieved everytime and applied.
private static Object invokeGetterAccessor(final Accessors gs, final Object self) { @SuppressWarnings("unused")
private static Object invokeObjectGetter(final Accessors gs, final MethodHandle invoker, final Object self) throws Throwable {
final Object func = gs.getter; final Object func = gs.getter;
if (func instanceof ScriptFunction) { if (func instanceof ScriptFunction) {
try { return invoker.invokeExact(func, self);
return getINVOKE_UA_GETTER().invokeExact(func, self);
} catch (final Error | RuntimeException t) {
throw t;
} catch (final Throwable t) {
throw new RuntimeException(t);
}
} }
return UNDEFINED; return UNDEFINED;
} }
private static void invokeSetterAccessor(final Accessors gs, final String name, final Object self, final Object value) { @SuppressWarnings("unused")
private static int invokeIntGetter(final Accessors gs, final MethodHandle invoker, final int programPoint, final Object self) throws Throwable {
final Object func = gs.getter;
if (func instanceof ScriptFunction) {
return (int) invoker.invokeExact(func, self);
}
throw new UnwarrantedOptimismException(UNDEFINED, programPoint);
}
@SuppressWarnings("unused")
private static long invokeLongGetter(final Accessors gs, final MethodHandle invoker, final int programPoint, final Object self) throws Throwable {
final Object func = gs.getter;
if (func instanceof ScriptFunction) {
return (long) invoker.invokeExact(func, self);
}
throw new UnwarrantedOptimismException(UNDEFINED, programPoint);
}
@SuppressWarnings("unused")
private static double invokeNumberGetter(final Accessors gs, final MethodHandle invoker, final int programPoint, final Object self) throws Throwable {
final Object func = gs.getter;
if (func instanceof ScriptFunction) {
return (double) invoker.invokeExact(func, self);
}
throw new UnwarrantedOptimismException(UNDEFINED, programPoint);
}
@SuppressWarnings("unused")
private static void invokeObjectSetter(final Accessors gs, final MethodHandle invoker, final String name, final Object self, final Object value) throws Throwable {
final Object func = gs.setter;
if (func instanceof ScriptFunction) {
invoker.invokeExact(func, self, value);
} else if (name != null) {
throw typeError("property.has.no.setter", name, ScriptRuntime.safeToString(self));
}
}
@SuppressWarnings("unused")
private static void invokeIntSetter(final Accessors gs, final MethodHandle invoker, final String name, final Object self, final int value) throws Throwable {
final Object func = gs.setter;
if (func instanceof ScriptFunction) {
invoker.invokeExact(func, self, value);
} else if (name != null) {
throw typeError("property.has.no.setter", name, ScriptRuntime.safeToString(self));
}
}
@SuppressWarnings("unused")
private static void invokeLongSetter(final Accessors gs, final MethodHandle invoker, final String name, final Object self, final long value) throws Throwable {
final Object func = gs.setter;
if (func instanceof ScriptFunction) {
invoker.invokeExact(func, self, value);
} else if (name != null) {
throw typeError("property.has.no.setter", name, ScriptRuntime.safeToString(self));
}
}
@SuppressWarnings("unused")
private static void invokeNumberSetter(final Accessors gs, final MethodHandle invoker, final String name, final Object self, final double value) throws Throwable {
final Object func = gs.setter; final Object func = gs.setter;
if (func instanceof ScriptFunction) { if (func instanceof ScriptFunction) {
try { invoker.invokeExact(func, self, value);
getINVOKE_UA_SETTER().invokeExact(func, self, value);
} catch (final Error | RuntimeException t) {
throw t;
} catch (final Throwable t) {
throw new RuntimeException(t);
}
} else if (name != null) { } else if (name != null) {
throw typeError("property.has.no.setter", name, ScriptRuntime.safeToString(self)); throw typeError("property.has.no.setter", name, ScriptRuntime.safeToString(self));
} }
......
...@@ -335,6 +335,20 @@ public final class Bootstrap { ...@@ -335,6 +335,20 @@ public final class Bootstrap {
return createDynamicInvoker(opDesc, MethodType.methodType(rtype, ptypes)); return createDynamicInvoker(opDesc, MethodType.methodType(rtype, ptypes));
} }
/**
* Returns a dynamic invoker for a specified dynamic operation using the public lookup. Similar to
* {@link #createDynamicInvoker(String, Class, Class...)} but with an additional parameter to
* set the call site flags of the dynamic invoker.
* @param opDesc Dynalink dynamic operation descriptor.
* @param flags the call site flags for the operation
* @param rtype the return type for the operation
* @param ptypes the parameter types for the operation
* @return MethodHandle for invoking the operation.
*/
public static MethodHandle createDynamicInvoker(final String opDesc, final int flags, final Class<?> rtype, final Class<?>... ptypes) {
return bootstrap(MethodHandles.publicLookup(), opDesc, MethodType.methodType(rtype, ptypes), flags).dynamicInvoker();
}
/** /**
* Returns a dynamic invoker for a specified dynamic operation using the public lookup. Similar to * Returns a dynamic invoker for a specified dynamic operation using the public lookup. Similar to
* {@link #createDynamicInvoker(String, Class, Class...)} but with return and parameter types composed into a * {@link #createDynamicInvoker(String, Class, Class...)} but with return and parameter types composed into a
......
/*
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of Oracle nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* A micro-benchmark for getters and setters with primitive values,
* alternating between ints and doubles. Introduction of primitive
* and optimistic user accessors in JDK-8062401 make this faster by
* 10x or more by allowing inlining and other optimizations to take place.
*/
var x = {
get m() {
return this._m;
},
set m(v) {
this._m = v;
},
get n() {
return this._n;
},
set n(v) {
this._n = v;
}
};
function bench(v1, v2, result) {
var start = Date.now();
x.n = v1;
for (var i = 0; i < 1e8; i++) {
x.m = v2;
if (x.m + x.n !== result) {
throw "wrong result";
}
}
print("done in", Date.now() - start, "millis");
}
for (var i = 0; i < 10; i++) {
bench(i, 4, 4 + i);
}
for (var i = 0; i < 10; i++) {
bench(i, 4.5, 4.5 + i);
}
for (var i = 0; i < 10; i++) {
bench(i, 5, 5 + i);
}
for (var i = 0; i < 10; i++) {
bench(i, 5.5, 5.5 + i);
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册