Fri, 24 May 2013 13:54:18 +0200
8011630: JSON parsing performance issue
Reviewed-by: lagergren, sundar
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 }