8011630: JSON parsing performance issue

Fri, 24 May 2013 13:54:18 +0200

author
hannesw
date
Fri, 24 May 2013 13:54:18 +0200
changeset 291
fdfb4edd78d6
parent 290
6fc7b51e83d6
child 292
4d2eca4d4d66

8011630: JSON parsing performance issue
Reviewed-by: lagergren, sundar

src/jdk/nashorn/internal/objects/NativeArguments.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/ScriptObject.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/runtime/SetMethodCreator.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/runtime/UserAccessorProperty.java file | annotate | diff | comparison | revisions
     1.1 --- a/src/jdk/nashorn/internal/objects/NativeArguments.java	Thu May 23 15:51:08 2013 +0200
     1.2 +++ b/src/jdk/nashorn/internal/objects/NativeArguments.java	Fri May 24 13:54:18 2013 +0200
     1.3 @@ -603,6 +603,11 @@
     1.4          }
     1.5      }
     1.6  
     1.7 +    @Override
     1.8 +    public Object getLength() {
     1.9 +        return length;
    1.10 +    }
    1.11 +
    1.12      private Object getArgumentsLength() {
    1.13          return length;
    1.14      }
     2.1 --- a/src/jdk/nashorn/internal/runtime/AccessorProperty.java	Thu May 23 15:51:08 2013 +0200
     2.2 +++ b/src/jdk/nashorn/internal/runtime/AccessorProperty.java	Fri May 24 13:54:18 2013 +0200
     2.3 @@ -75,7 +75,23 @@
     2.4  
     2.5      private static final MethodType[] ACCESSOR_GETTER_TYPES = new MethodType[NOOF_TYPES];
     2.6      private static final MethodType[] ACCESSOR_SETTER_TYPES = new MethodType[NOOF_TYPES];
     2.7 -    private static final MethodHandle SPILLGETTER = MH.asType(MH.getter(MethodHandles.lookup(), ScriptObject.class, "spill", Object[].class), Lookup.GET_OBJECT_TYPE);
     2.8 +    private static final MethodHandle SPILL_ELEMENT_GETTER;
     2.9 +    private static final MethodHandle SPILL_ELEMENT_SETTER;
    2.10 +
    2.11 +    private static final int SPILL_CACHE_SIZE = 8;
    2.12 +    private static final MethodHandle[] SPILL_ACCESSORS = new MethodHandle[SPILL_CACHE_SIZE * 2];
    2.13 +
    2.14 +    static {
    2.15 +        for (int i = 0; i < NOOF_TYPES; i++) {
    2.16 +            final Type type = ACCESSOR_TYPES.get(i);
    2.17 +            ACCESSOR_GETTER_TYPES[i] = MH.type(type.getTypeClass(), Object.class);
    2.18 +            ACCESSOR_SETTER_TYPES[i] = MH.type(void.class, Object.class, type.getTypeClass());
    2.19 +        }
    2.20 +
    2.21 +        final MethodHandle spillGetter = MH.getter(MethodHandles.lookup(), ScriptObject.class, "spill", Object[].class);
    2.22 +        SPILL_ELEMENT_GETTER = MH.filterArguments(MH.arrayElementGetter(Object[].class), 0, spillGetter);
    2.23 +        SPILL_ELEMENT_SETTER = MH.filterArguments(MH.arrayElementSetter(Object[].class), 0, spillGetter);
    2.24 +    }
    2.25  
    2.26      /** Seed getter for the primitive version of this field (in -Dnashorn.fields.dual=true mode) */
    2.27      private MethodHandle primitiveGetter;
    2.28 @@ -96,14 +112,6 @@
    2.29       */
    2.30      private Class<?> currentType;
    2.31  
    2.32 -    static {
    2.33 -        for (int i = 0; i < NOOF_TYPES; i++) {
    2.34 -            final Type type = ACCESSOR_TYPES.get(i);
    2.35 -            ACCESSOR_GETTER_TYPES[i] = MH.type(type.getTypeClass(), Object.class);
    2.36 -            ACCESSOR_SETTER_TYPES[i] = MH.type(void.class, Object.class, type.getTypeClass());
    2.37 -        }
    2.38 -    }
    2.39 -
    2.40      /**
    2.41       * Delegate constructor. This is used when adding properties to the Global scope, which
    2.42       * is necessary for outermost levels in a script (the ScriptObject is represented by
    2.43 @@ -114,19 +122,31 @@
    2.44       * @param delegate  delegate script object to rebind receiver to
    2.45       */
    2.46      public AccessorProperty(final AccessorProperty property, final ScriptObject delegate) {
    2.47 -        this(property);
    2.48 +        super(property);
    2.49  
    2.50 -        this.getters = new MethodHandle[NOOF_TYPES];
    2.51 -
    2.52 -        this.primitiveGetter = bindTo(primitiveGetter, delegate);
    2.53 -        this.primitiveSetter = bindTo(primitiveSetter, delegate);
    2.54 -        this.objectGetter    = bindTo(objectGetter, delegate);
    2.55 -        this.objectSetter    = bindTo(objectSetter, delegate);
    2.56 +        this.primitiveGetter = bindTo(property.primitiveGetter, delegate);
    2.57 +        this.primitiveSetter = bindTo(property.primitiveSetter, delegate);
    2.58 +        this.objectGetter    = bindTo(property.objectGetter, delegate);
    2.59 +        this.objectSetter    = bindTo(property.objectSetter, delegate);
    2.60  
    2.61          setCurrentType(property.getCurrentType());
    2.62      }
    2.63  
    2.64      /**
    2.65 +     * Constructor for spill properties. Array getters and setters will be created on demand.
    2.66 +     *
    2.67 +     * @param key    the property key
    2.68 +     * @param flags  the property flags
    2.69 +     * @param slot   spill slot
    2.70 +     */
    2.71 +    public AccessorProperty(final String key, final int flags, final int slot) {
    2.72 +        super(key, flags, slot);
    2.73 +        assert (flags & IS_SPILL) == IS_SPILL;
    2.74 +
    2.75 +        setCurrentType(Object.class);
    2.76 +    }
    2.77 +
    2.78 +    /**
    2.79       * Constructor. Similar to the constructor with both primitive getters and setters, the difference
    2.80       * here being that only one getter and setter (setter is optional for non writable fields) is given
    2.81       * to the constructor, and the rest are created from those. Used e.g. by Nasgen classes
    2.82 @@ -268,7 +288,40 @@
    2.83      }
    2.84  
    2.85      @Override
    2.86 +    protected void setObjectValue(final ScriptObject self, final ScriptObject owner, final Object value, final boolean strict)  {
    2.87 +        if (isSpill()) {
    2.88 +            self.spill[getSlot()] = value;
    2.89 +        } else {
    2.90 +            try {
    2.91 +                getSetter(Object.class, self.getMap()).invokeExact((Object)self, value);
    2.92 +            } catch (final Error|RuntimeException e) {
    2.93 +                throw e;
    2.94 +            } catch (final Throwable e) {
    2.95 +                throw new RuntimeException(e);
    2.96 +            }
    2.97 +        }
    2.98 +    }
    2.99 +
   2.100 +    @Override
   2.101 +    protected Object getObjectValue(final ScriptObject self, final ScriptObject owner) {
   2.102 +        if (isSpill()) {
   2.103 +            return self.spill[getSlot()];
   2.104 +        } else {
   2.105 +            try {
   2.106 +                return getGetter(Object.class).invokeExact((Object)self);
   2.107 +            } catch (final Error|RuntimeException e) {
   2.108 +                throw e;
   2.109 +            } catch (final Throwable e) {
   2.110 +                throw new RuntimeException(e);
   2.111 +            }
   2.112 +        }
   2.113 +    }
   2.114 +
   2.115 +    @Override
   2.116      public MethodHandle getGetter(final Class<?> type) {
   2.117 +        if (isSpill() && objectGetter == null) {
   2.118 +            objectGetter = getSpillGetter();
   2.119 +        }
   2.120          final int i = getAccessorTypeIndex(type);
   2.121          if (getters[i] == null) {
   2.122              getters[i] = debug(
   2.123 @@ -284,7 +337,7 @@
   2.124                  "get");
   2.125          }
   2.126  
   2.127 -        return isSpill() ? MH.filterArguments(getters[i], 0, SPILLGETTER) : getters[i];
   2.128 +        return getters[i];
   2.129      }
   2.130  
   2.131      private Property getWiderProperty(final Class<?> type) {
   2.132 @@ -313,6 +366,9 @@
   2.133      }
   2.134  
   2.135      private MethodHandle generateSetter(final Class<?> forType, final Class<?> type) {
   2.136 +        if (isSpill() && objectSetter == null) {
   2.137 +            objectSetter = getSpillSetter();
   2.138 +        }
   2.139          MethodHandle mh = createSetter(forType, type, primitiveSetter, objectSetter);
   2.140          mh = MH.asType(mh, ACCESSOR_SETTER_TYPES[getAccessorTypeIndex(type)]); //has to be the case for invokeexact to work in ScriptObject
   2.141          mh = debug(mh, currentType, type, "set");
   2.142 @@ -343,7 +399,7 @@
   2.143              mh = generateSetter(forType, type);
   2.144          }
   2.145  
   2.146 -        return isSpill() ? MH.filterArguments(mh, 0, SPILLGETTER) : mh;
   2.147 +        return mh;
   2.148      }
   2.149  
   2.150      @Override
   2.151 @@ -363,6 +419,30 @@
   2.152          setCurrentType(newType);
   2.153      }
   2.154  
   2.155 +    private MethodHandle getSpillGetter() {
   2.156 +        final int slot = getSlot();
   2.157 +        MethodHandle getter = slot < SPILL_CACHE_SIZE ? SPILL_ACCESSORS[slot * 2] : null;
   2.158 +        if (getter == null) {
   2.159 +            getter = MH.asType(MH.insertArguments(SPILL_ELEMENT_GETTER, 1, slot), Lookup.GET_OBJECT_TYPE);
   2.160 +            if (slot < SPILL_CACHE_SIZE) {
   2.161 +                SPILL_ACCESSORS[slot * 2] = getter;
   2.162 +            }
   2.163 +        }
   2.164 +        return getter;
   2.165 +    }
   2.166 +
   2.167 +    private MethodHandle getSpillSetter() {
   2.168 +        final int slot = getSlot();
   2.169 +        MethodHandle setter = slot < SPILL_CACHE_SIZE ? SPILL_ACCESSORS[slot * 2 + 1] : null;
   2.170 +        if (setter == null) {
   2.171 +            setter = MH.asType(MH.insertArguments(SPILL_ELEMENT_SETTER, 1, slot), Lookup.SET_OBJECT_TYPE);
   2.172 +            if (slot < SPILL_CACHE_SIZE) {
   2.173 +                SPILL_ACCESSORS[slot * 2 + 1] = setter;
   2.174 +            }
   2.175 +        }
   2.176 +        return setter;
   2.177 +    }
   2.178 +
   2.179      private static void finest(final String str) {
   2.180          if (DEBUG_FIELDS) {
   2.181              LOG.finest(str);
     3.1 --- a/src/jdk/nashorn/internal/runtime/FindProperty.java	Thu May 23 15:51:08 2013 +0200
     3.2 +++ b/src/jdk/nashorn/internal/runtime/FindProperty.java	Fri May 24 13:54:18 2013 +0200
     3.3 @@ -153,5 +153,24 @@
     3.4          return prototype.isScope();
     3.5      }
     3.6  
     3.7 +    /**
     3.8 +     * Get the property value from self as object.
     3.9 +     *
    3.10 +     * @return the property value
    3.11 +     */
    3.12 +    public Object getObjectValue() {
    3.13 +        return property.getObjectValue(getGetterReceiver(), getOwner());
    3.14 +    }
    3.15 +
    3.16 +    /**
    3.17 +     * Set the property value in self.
    3.18 +     *
    3.19 +     * @param value the new value
    3.20 +     * @param strict strict flag
    3.21 +     */
    3.22 +    public void setObjectValue(final Object value, final boolean strict) {
    3.23 +        property.setObjectValue(getSetterReceiver(), getOwner(), value, strict);
    3.24 +    }
    3.25 +
    3.26  }
    3.27  
     4.1 --- a/src/jdk/nashorn/internal/runtime/Property.java	Thu May 23 15:51:08 2013 +0200
     4.2 +++ b/src/jdk/nashorn/internal/runtime/Property.java	Fri May 24 13:54:18 2013 +0200
     4.3 @@ -352,6 +352,26 @@
     4.4      }
     4.5  
     4.6      /**
     4.7 +     * Set the value of this property in {@code owner}. This allows to bypass creation of the
     4.8 +     * setter MethodHandle for spill and user accessor properties.
     4.9 +     *
    4.10 +     * @param self the this object
    4.11 +     * @param owner the owner object
    4.12 +     * @param value the new property value
    4.13 +     */
    4.14 +    protected abstract void setObjectValue(ScriptObject self, ScriptObject owner, Object value, boolean strict);
    4.15 +
    4.16 +    /**
    4.17 +     * Set the Object value of this property from {@code owner}. This allows to bypass creation of the
    4.18 +     * getter MethodHandle for spill and user accessor properties.
    4.19 +     *
    4.20 +     * @param self the this object
    4.21 +     * @param owner the owner object
    4.22 +     * @return  the property value
    4.23 +     */
    4.24 +    protected abstract Object getObjectValue(ScriptObject self, ScriptObject owner);
    4.25 +
    4.26 +    /**
    4.27       * Abstract method for retrieving the setter for the property. We do not know
    4.28       * anything about the internal representation when we request the setter, we only
    4.29       * know that the setter will take the property as a parameter of the given type.
     5.1 --- a/src/jdk/nashorn/internal/runtime/ScriptObject.java	Thu May 23 15:51:08 2013 +0200
     5.2 +++ b/src/jdk/nashorn/internal/runtime/ScriptObject.java	Fri May 24 13:54:18 2013 +0200
     5.3 @@ -25,7 +25,6 @@
     5.4  
     5.5  package jdk.nashorn.internal.runtime;
     5.6  
     5.7 -import static jdk.nashorn.internal.codegen.CompilerConstants.staticCall;
     5.8  import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCall;
     5.9  import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup;
    5.10  import static jdk.nashorn.internal.lookup.Lookup.MH;
    5.11 @@ -151,17 +150,6 @@
    5.12      /** Method handle for setting the user accessors of a ScriptObject */
    5.13      public static final Call SET_USER_ACCESSORS = virtualCall(ScriptObject.class, "setUserAccessors", void.class, String.class, ScriptFunction.class, ScriptFunction.class);
    5.14  
    5.15 -    /** Method handle for getter for {@link UserAccessorProperty}, given a slot */
    5.16 -    static final Call USER_ACCESSOR_GETTER = staticCall(MethodHandles.lookup(), ScriptObject.class, "userAccessorGetter", Object.class, ScriptObject.class, int.class, Object.class);
    5.17 -
    5.18 -    /** Method handle for setter for {@link UserAccessorProperty}, given a slot */
    5.19 -    static final Call USER_ACCESSOR_SETTER = staticCall(MethodHandles.lookup(), ScriptObject.class, "userAccessorSetter", void.class, ScriptObject.class, int.class, String.class, Object.class, Object.class);
    5.20 -
    5.21 -    private static final MethodHandle INVOKE_UA_GETTER = Bootstrap.createDynamicInvoker("dyn:call", Object.class,
    5.22 -            Object.class, Object.class);
    5.23 -    private static final MethodHandle INVOKE_UA_SETTER = Bootstrap.createDynamicInvoker("dyn:call", void.class,
    5.24 -            Object.class, Object.class, Object.class);
    5.25 -
    5.26      /**
    5.27       * Constructor
    5.28       */
    5.29 @@ -699,17 +687,9 @@
    5.30       * @return New property.
    5.31       */
    5.32      public final Property addOwnProperty(final String key, final int propertyFlags, final Object value) {
    5.33 -        final MethodHandle setter = addSpill(key, propertyFlags);
    5.34 -
    5.35 -        try {
    5.36 -            setter.invokeExact((Object)this, value);
    5.37 -        } catch (final Error|RuntimeException e) {
    5.38 -            throw e;
    5.39 -        } catch (final Throwable e) {
    5.40 -            throw new RuntimeException(e);
    5.41 -        }
    5.42 -
    5.43 -        return getMap().findProperty(key);
    5.44 +        final Property property = addSpillProperty(key, propertyFlags);
    5.45 +        property.setObjectValue(this, this, value, false);
    5.46 +        return property;
    5.47      }
    5.48  
    5.49      /**
    5.50 @@ -744,15 +724,7 @@
    5.51          // Erase the property field value with undefined. If the property is defined
    5.52          // by user-defined accessors, we don't want to call the setter!!
    5.53          if (!(property instanceof UserAccessorProperty)) {
    5.54 -            try {
    5.55 -                // make the property value to be undefined
    5.56 -                //TODO specproperties
    5.57 -                property.getSetter(Object.class, getMap()).invokeExact((Object)this, (Object)UNDEFINED);
    5.58 -            } catch (final RuntimeException | Error e) {
    5.59 -                throw e;
    5.60 -            } catch (final Throwable t) {
    5.61 -                throw new RuntimeException(t);
    5.62 -            }
    5.63 +            property.setObjectValue(this, this, UNDEFINED, false);
    5.64          }
    5.65      }
    5.66  
    5.67 @@ -948,18 +920,7 @@
    5.68        * @return the value of the property
    5.69        */
    5.70      protected static Object getObjectValue(final FindProperty find) {
    5.71 -        final MethodHandle getter = find.getGetter(Object.class);
    5.72 -        if (getter != null) {
    5.73 -            try {
    5.74 -                return getter.invokeExact((Object)find.getGetterReceiver());
    5.75 -            } catch (final Error|RuntimeException e) {
    5.76 -                throw e;
    5.77 -            } catch (final Throwable e) {
    5.78 -                throw new RuntimeException(e);
    5.79 -            }
    5.80 -        }
    5.81 -
    5.82 -        return UNDEFINED;
    5.83 +        return find.getObjectValue();
    5.84      }
    5.85  
    5.86      /**
    5.87 @@ -2087,11 +2048,7 @@
    5.88              property = addOwnProperty(property);
    5.89          } else {
    5.90              int i = getMap().getSpillLength();
    5.91 -            MethodHandle getter = MH.arrayElementGetter(Object[].class);
    5.92 -            MethodHandle setter = MH.arrayElementSetter(Object[].class);
    5.93 -            getter = MH.asType(MH.insertArguments(getter, 1, i), Lookup.GET_OBJECT_TYPE);
    5.94 -            setter = MH.asType(MH.insertArguments(setter, 1, i), Lookup.SET_OBJECT_TYPE);
    5.95 -            property = new AccessorProperty(key, propertyFlags | Property.IS_SPILL, i, getter, setter);
    5.96 +            property = new AccessorProperty(key, propertyFlags | Property.IS_SPILL, i);
    5.97              notifyPropertyAdded(this, property);
    5.98              property = addOwnProperty(property);
    5.99              i = property.getSlot();
   5.100 @@ -2115,20 +2072,15 @@
   5.101  
   5.102      /**
   5.103       * Add a spill entry for the given key.
   5.104 -     * @param key           Property key.
   5.105 -     * @param propertyFlags Property flags.
   5.106 +     * @param key Property key.
   5.107       * @return Setter method handle.
   5.108       */
   5.109 -    private MethodHandle addSpill(final String key, final int propertyFlags) {
   5.110 -        final Property spillProperty = addSpillProperty(key, propertyFlags);
   5.111 +    MethodHandle addSpill(final String key) {
   5.112 +        final Property spillProperty = addSpillProperty(key, 0);
   5.113          final Class<?> type = Object.class;
   5.114          return spillProperty.getSetter(type, getMap()); //TODO specfields
   5.115      }
   5.116  
   5.117 -    MethodHandle addSpill(final String key) {
   5.118 -        return addSpill(key, 0);
   5.119 -    }
   5.120 -
   5.121      /**
   5.122       * Make sure arguments are paired correctly, with respect to more parameters than declared,
   5.123       * fewer parameters than declared and other things that JavaScript allows. This might involve
   5.124 @@ -2659,14 +2611,8 @@
   5.125                  return;
   5.126              }
   5.127  
   5.128 -            try {
   5.129 -                final MethodHandle setter = f.getSetter(Object.class, strict); //TODO specfields
   5.130 -                setter.invokeExact((Object)f.getSetterReceiver(), value);
   5.131 -            } catch (final Error|RuntimeException e) {
   5.132 -                throw e;
   5.133 -            } catch (final Throwable e) {
   5.134 -                throw new RuntimeException(e);
   5.135 -            }
   5.136 +            f.setObjectValue(value, strict);
   5.137 +
   5.138          } else if (!isExtensible()) {
   5.139              if (strict) {
   5.140                  throw typeError("object.non.extensible", key, ScriptRuntime.safeToString(this));
   5.141 @@ -2677,13 +2623,7 @@
   5.142      }
   5.143  
   5.144      private void spill(final String key, final Object value) {
   5.145 -        try {
   5.146 -            addSpill(key).invokeExact((Object)this, value);
   5.147 -        } catch (final Error|RuntimeException e) {
   5.148 -            throw e;
   5.149 -        } catch (final Throwable e) {
   5.150 -            throw new RuntimeException(e);
   5.151 -        }
   5.152 +        addSpillProperty(key, 0).setObjectValue(this, this, value, false);
   5.153      }
   5.154  
   5.155  
   5.156 @@ -3217,46 +3157,6 @@
   5.157          return (index < 0 || (index >= spill.length)) ? null : spill[index];
   5.158      }
   5.159  
   5.160 -    // User defined getter and setter are always called by "dyn:call". Note that the user
   5.161 -    // getter/setter may be inherited. If so, proto is bound during lookup. In either
   5.162 -    // inherited or self case, slot is also bound during lookup. Actual ScriptFunction
   5.163 -    // to be called is retrieved everytime and applied.
   5.164 -    @SuppressWarnings("unused")
   5.165 -    private static Object userAccessorGetter(final ScriptObject proto, final int slot, final Object self) {
   5.166 -        final ScriptObject container = (proto != null) ? proto : (ScriptObject)self;
   5.167 -        final Object       func      = container.getSpill(slot);
   5.168 -
   5.169 -        if (func instanceof ScriptFunction) {
   5.170 -            try {
   5.171 -                return INVOKE_UA_GETTER.invokeExact(func, self);
   5.172 -            } catch(final Error|RuntimeException t) {
   5.173 -                throw t;
   5.174 -            } catch(final Throwable t) {
   5.175 -                throw new RuntimeException(t);
   5.176 -            }
   5.177 -        }
   5.178 -
   5.179 -        return UNDEFINED;
   5.180 -    }
   5.181 -
   5.182 -    @SuppressWarnings("unused")
   5.183 -    private static void userAccessorSetter(final ScriptObject proto, final int slot, final String name, final Object self, final Object value) {
   5.184 -        final ScriptObject container = (proto != null) ? proto : (ScriptObject)self;
   5.185 -        final Object       func      = container.getSpill(slot);
   5.186 -
   5.187 -        if (func instanceof ScriptFunction) {
   5.188 -            try {
   5.189 -                INVOKE_UA_SETTER.invokeExact(func, self, value);
   5.190 -            } catch(final Error|RuntimeException t) {
   5.191 -                throw t;
   5.192 -            } catch(final Throwable t) {
   5.193 -                throw new RuntimeException(t);
   5.194 -            }
   5.195 -        }  else if (name != null) {
   5.196 -            throw typeError("property.has.no.setter", name, ScriptRuntime.safeToString(self));
   5.197 -        }
   5.198 -    }
   5.199 -
   5.200      private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
   5.201          final Class<?>   own = ScriptObject.class;
   5.202          final MethodType mt  = MH.type(rtype, types);
     6.1 --- a/src/jdk/nashorn/internal/runtime/SetMethodCreator.java	Thu May 23 15:51:08 2013 +0200
     6.2 +++ b/src/jdk/nashorn/internal/runtime/SetMethodCreator.java	Fri May 24 13:54:18 2013 +0200
     6.3 @@ -183,17 +183,10 @@
     6.4      private SetMethod createNewSpillPropertySetter() {
     6.5          final int nextSpill = getMap().getSpillLength();
     6.6  
     6.7 -        final Property property = createSpillProperty(nextSpill);
     6.8 +        final Property property = new AccessorProperty(getName(), Property.IS_SPILL, nextSpill);
     6.9          return new SetMethod(createSpillMethodHandle(nextSpill, property), property);
    6.10      }
    6.11  
    6.12 -    private Property createSpillProperty(final int nextSpill) {
    6.13 -        final MethodHandle getter = MH.asType(MH.insertArguments(MH.arrayElementGetter(Object[].class), 1, nextSpill), Lookup.GET_OBJECT_TYPE);
    6.14 -        final MethodHandle setter = MH.asType(MH.insertArguments(MH.arrayElementSetter(Object[].class), 1, nextSpill), Lookup.SET_OBJECT_TYPE);
    6.15 -
    6.16 -        return new AccessorProperty(getName(), Property.IS_SPILL, nextSpill, getter, setter);
    6.17 -    }
    6.18 -
    6.19      private MethodHandle createSpillMethodHandle(final int nextSpill, Property property) {
    6.20          final PropertyMap oldMap = getMap();
    6.21          final PropertyMap newMap = getNewMap(property);
     7.1 --- a/src/jdk/nashorn/internal/runtime/UserAccessorProperty.java	Thu May 23 15:51:08 2013 +0200
     7.2 +++ b/src/jdk/nashorn/internal/runtime/UserAccessorProperty.java	Fri May 24 13:54:18 2013 +0200
     7.3 @@ -26,7 +26,15 @@
     7.4  package jdk.nashorn.internal.runtime;
     7.5  
     7.6  import java.lang.invoke.MethodHandle;
     7.7 +import java.lang.invoke.MethodHandles;
     7.8 +
     7.9 +import jdk.nashorn.internal.codegen.CompilerConstants;
    7.10  import jdk.nashorn.internal.lookup.Lookup;
    7.11 +import jdk.nashorn.internal.runtime.linker.Bootstrap;
    7.12 +
    7.13 +import static jdk.nashorn.internal.codegen.CompilerConstants.staticCall;
    7.14 +import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
    7.15 +import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
    7.16  
    7.17  /**
    7.18   * Property with user defined getters/setters. Actual getter and setter
    7.19 @@ -51,6 +59,22 @@
    7.20      /** User defined setter function slot. */
    7.21      private final int setterSlot;
    7.22  
    7.23 +    /** Getter method handle */
    7.24 +    private final static CompilerConstants.Call USER_ACCESSOR_GETTER = staticCall(MethodHandles.lookup(), UserAccessorProperty.class,
    7.25 +            "userAccessorGetter", Object.class, ScriptObject.class, int.class, Object.class);
    7.26 +
    7.27 +    /** Setter method handle */
    7.28 +    private final static CompilerConstants.Call USER_ACCESSOR_SETTER = staticCall(MethodHandles.lookup(), UserAccessorProperty.class,
    7.29 +            "userAccessorSetter", void.class, ScriptObject.class, int.class, String.class, Object.class, Object.class);
    7.30 +
    7.31 +    /** Dynamic invoker for getter */
    7.32 +    private static final MethodHandle INVOKE_UA_GETTER = Bootstrap.createDynamicInvoker("dyn:call", Object.class,
    7.33 +            Object.class, Object.class);
    7.34 +
    7.35 +    /** Dynamic invoker for setter */
    7.36 +    private static final MethodHandle INVOKE_UA_SETTER = Bootstrap.createDynamicInvoker("dyn:call", void.class,
    7.37 +            Object.class, Object.class, Object.class);
    7.38 +
    7.39      /**
    7.40       * Constructor
    7.41       *
    7.42 @@ -134,8 +158,18 @@
    7.43      }
    7.44  
    7.45      @Override
    7.46 +    protected Object getObjectValue(final ScriptObject self, final ScriptObject owner) {
    7.47 +        return userAccessorGetter(owner, getGetterSlot(), self);
    7.48 +    }
    7.49 +
    7.50 +    @Override
    7.51 +    protected void setObjectValue(final ScriptObject self, final ScriptObject owner, final Object value, final boolean strict) {
    7.52 +        userAccessorSetter(owner, getSetterSlot(), strict ? getKey() : null, self, value);
    7.53 +    }
    7.54 +
    7.55 +    @Override
    7.56      public MethodHandle getGetter(final Class<?> type) {
    7.57 -        return Lookup.filterReturnType(ScriptObject.USER_ACCESSOR_GETTER.methodHandle(), type);
    7.58 +        return Lookup.filterReturnType(USER_ACCESSOR_GETTER.methodHandle(), type);
    7.59      }
    7.60  
    7.61      @Override
    7.62 @@ -146,7 +180,7 @@
    7.63  
    7.64      @Override
    7.65      public MethodHandle getSetter(final Class<?> type, final PropertyMap currentMap) {
    7.66 -        return ScriptObject.USER_ACCESSOR_SETTER.methodHandle();
    7.67 +        return USER_ACCESSOR_SETTER.methodHandle();
    7.68      }
    7.69  
    7.70      @Override
    7.71 @@ -155,4 +189,44 @@
    7.72          return (value instanceof ScriptFunction) ? (ScriptFunction) value : null;
    7.73      }
    7.74  
    7.75 +    // User defined getter and setter are always called by "dyn:call". Note that the user
    7.76 +    // getter/setter may be inherited. If so, proto is bound during lookup. In either
    7.77 +    // inherited or self case, slot is also bound during lookup. Actual ScriptFunction
    7.78 +    // to be called is retrieved everytime and applied.
    7.79 +    @SuppressWarnings("unused")
    7.80 +    static Object userAccessorGetter(final ScriptObject proto, final int slot, final Object self) {
    7.81 +        final ScriptObject container = (proto != null) ? proto : (ScriptObject)self;
    7.82 +        final Object       func      = container.getSpill(slot);
    7.83 +
    7.84 +        if (func instanceof ScriptFunction) {
    7.85 +            try {
    7.86 +                return INVOKE_UA_GETTER.invokeExact(func, self);
    7.87 +            } catch(final Error|RuntimeException t) {
    7.88 +                throw t;
    7.89 +            } catch(final Throwable t) {
    7.90 +                throw new RuntimeException(t);
    7.91 +            }
    7.92 +        }
    7.93 +
    7.94 +        return UNDEFINED;
    7.95 +    }
    7.96 +
    7.97 +    @SuppressWarnings("unused")
    7.98 +    static void userAccessorSetter(final ScriptObject proto, final int slot, final String name, final Object self, final Object value) {
    7.99 +        final ScriptObject container = (proto != null) ? proto : (ScriptObject)self;
   7.100 +        final Object       func      = container.getSpill(slot);
   7.101 +
   7.102 +        if (func instanceof ScriptFunction) {
   7.103 +            try {
   7.104 +                INVOKE_UA_SETTER.invokeExact(func, self, value);
   7.105 +            } catch(final Error|RuntimeException t) {
   7.106 +                throw t;
   7.107 +            } catch(final Throwable t) {
   7.108 +                throw new RuntimeException(t);
   7.109 +            }
   7.110 +        }  else if (name != null) {
   7.111 +            throw typeError("property.has.no.setter", name, ScriptRuntime.safeToString(self));
   7.112 +        }
   7.113 +    }
   7.114 +
   7.115  }

mercurial