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

Fri, 31 Oct 2014 16:27:58 +0100

author
hannesw
date
Fri, 31 Oct 2014 16:27:58 +0100
changeset 1074
29a4cd3d1f7a
parent 1073
a54353b34d24
child 1075
a8e6c9feecfb

8062401: User accessors require boxing and do not support optimistic types
Reviewed-by: jlaskey, lagergren

src/jdk/nashorn/internal/codegen/SpillObjectCreator.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/codegen/TypeEvaluator.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/objects/NativeObject.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/runtime/AccessorProperty.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/runtime/FindProperty.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/runtime/Property.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/runtime/PropertyMap.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/runtime/ScriptObject.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/runtime/SpillProperty.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/runtime/UserAccessorProperty.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/runtime/linker/Bootstrap.java file | annotate | diff | comparison | revisions
test/examples/getter-setter-micro.js file | annotate | diff | comparison | revisions
     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 +}

mercurial