mirror of
https://github.com/openjdk/jdk.git
synced 2026-02-20 07:15:31 +00:00
8011630: JSON parsing performance issue
Reviewed-by: lagergren, sundar
This commit is contained in:
parent
3111019493
commit
cdf1276490
@ -603,6 +603,11 @@ public final class NativeArguments extends ScriptObject {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getLength() {
|
||||
return length;
|
||||
}
|
||||
|
||||
private Object getArgumentsLength() {
|
||||
return length;
|
||||
}
|
||||
|
||||
@ -75,7 +75,23 @@ public class AccessorProperty extends Property {
|
||||
|
||||
private static final MethodType[] ACCESSOR_GETTER_TYPES = new MethodType[NOOF_TYPES];
|
||||
private static final MethodType[] ACCESSOR_SETTER_TYPES = new MethodType[NOOF_TYPES];
|
||||
private static final MethodHandle SPILLGETTER = MH.asType(MH.getter(MethodHandles.lookup(), ScriptObject.class, "spill", Object[].class), Lookup.GET_OBJECT_TYPE);
|
||||
private static final MethodHandle SPILL_ELEMENT_GETTER;
|
||||
private static final MethodHandle SPILL_ELEMENT_SETTER;
|
||||
|
||||
private static final int SPILL_CACHE_SIZE = 8;
|
||||
private static final MethodHandle[] SPILL_ACCESSORS = new MethodHandle[SPILL_CACHE_SIZE * 2];
|
||||
|
||||
static {
|
||||
for (int i = 0; i < NOOF_TYPES; i++) {
|
||||
final Type type = ACCESSOR_TYPES.get(i);
|
||||
ACCESSOR_GETTER_TYPES[i] = MH.type(type.getTypeClass(), Object.class);
|
||||
ACCESSOR_SETTER_TYPES[i] = MH.type(void.class, Object.class, type.getTypeClass());
|
||||
}
|
||||
|
||||
final MethodHandle spillGetter = MH.getter(MethodHandles.lookup(), ScriptObject.class, "spill", Object[].class);
|
||||
SPILL_ELEMENT_GETTER = MH.filterArguments(MH.arrayElementGetter(Object[].class), 0, spillGetter);
|
||||
SPILL_ELEMENT_SETTER = MH.filterArguments(MH.arrayElementSetter(Object[].class), 0, spillGetter);
|
||||
}
|
||||
|
||||
/** Seed getter for the primitive version of this field (in -Dnashorn.fields.dual=true mode) */
|
||||
private MethodHandle primitiveGetter;
|
||||
@ -96,14 +112,6 @@ public class AccessorProperty extends Property {
|
||||
*/
|
||||
private Class<?> currentType;
|
||||
|
||||
static {
|
||||
for (int i = 0; i < NOOF_TYPES; i++) {
|
||||
final Type type = ACCESSOR_TYPES.get(i);
|
||||
ACCESSOR_GETTER_TYPES[i] = MH.type(type.getTypeClass(), Object.class);
|
||||
ACCESSOR_SETTER_TYPES[i] = MH.type(void.class, Object.class, type.getTypeClass());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate constructor. This is used when adding properties to the Global scope, which
|
||||
* is necessary for outermost levels in a script (the ScriptObject is represented by
|
||||
@ -114,18 +122,30 @@ public class AccessorProperty extends Property {
|
||||
* @param delegate delegate script object to rebind receiver to
|
||||
*/
|
||||
public AccessorProperty(final AccessorProperty property, final ScriptObject delegate) {
|
||||
this(property);
|
||||
super(property);
|
||||
|
||||
this.getters = new MethodHandle[NOOF_TYPES];
|
||||
|
||||
this.primitiveGetter = bindTo(primitiveGetter, delegate);
|
||||
this.primitiveSetter = bindTo(primitiveSetter, delegate);
|
||||
this.objectGetter = bindTo(objectGetter, delegate);
|
||||
this.objectSetter = bindTo(objectSetter, delegate);
|
||||
this.primitiveGetter = bindTo(property.primitiveGetter, delegate);
|
||||
this.primitiveSetter = bindTo(property.primitiveSetter, delegate);
|
||||
this.objectGetter = bindTo(property.objectGetter, delegate);
|
||||
this.objectSetter = bindTo(property.objectSetter, delegate);
|
||||
|
||||
setCurrentType(property.getCurrentType());
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for spill properties. Array getters and setters will be created on demand.
|
||||
*
|
||||
* @param key the property key
|
||||
* @param flags the property flags
|
||||
* @param slot spill slot
|
||||
*/
|
||||
public AccessorProperty(final String key, final int flags, final int slot) {
|
||||
super(key, flags, slot);
|
||||
assert (flags & IS_SPILL) == IS_SPILL;
|
||||
|
||||
setCurrentType(Object.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor. Similar to the constructor with both primitive getters and setters, the difference
|
||||
* here being that only one getter and setter (setter is optional for non writable fields) is given
|
||||
@ -267,8 +287,41 @@ public class AccessorProperty extends Property {
|
||||
return new AccessorProperty(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setObjectValue(final ScriptObject self, final ScriptObject owner, final Object value, final boolean strict) {
|
||||
if (isSpill()) {
|
||||
self.spill[getSlot()] = value;
|
||||
} else {
|
||||
try {
|
||||
getSetter(Object.class, self.getMap()).invokeExact((Object)self, value);
|
||||
} catch (final Error|RuntimeException e) {
|
||||
throw e;
|
||||
} catch (final Throwable e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object getObjectValue(final ScriptObject self, final ScriptObject owner) {
|
||||
if (isSpill()) {
|
||||
return self.spill[getSlot()];
|
||||
} else {
|
||||
try {
|
||||
return getGetter(Object.class).invokeExact((Object)self);
|
||||
} catch (final Error|RuntimeException e) {
|
||||
throw e;
|
||||
} catch (final Throwable e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodHandle getGetter(final Class<?> type) {
|
||||
if (isSpill() && objectGetter == null) {
|
||||
objectGetter = getSpillGetter();
|
||||
}
|
||||
final int i = getAccessorTypeIndex(type);
|
||||
if (getters[i] == null) {
|
||||
getters[i] = debug(
|
||||
@ -284,7 +337,7 @@ public class AccessorProperty extends Property {
|
||||
"get");
|
||||
}
|
||||
|
||||
return isSpill() ? MH.filterArguments(getters[i], 0, SPILLGETTER) : getters[i];
|
||||
return getters[i];
|
||||
}
|
||||
|
||||
private Property getWiderProperty(final Class<?> type) {
|
||||
@ -313,6 +366,9 @@ public class AccessorProperty extends Property {
|
||||
}
|
||||
|
||||
private MethodHandle generateSetter(final Class<?> forType, final Class<?> type) {
|
||||
if (isSpill() && objectSetter == null) {
|
||||
objectSetter = getSpillSetter();
|
||||
}
|
||||
MethodHandle mh = createSetter(forType, type, primitiveSetter, objectSetter);
|
||||
mh = MH.asType(mh, ACCESSOR_SETTER_TYPES[getAccessorTypeIndex(type)]); //has to be the case for invokeexact to work in ScriptObject
|
||||
mh = debug(mh, currentType, type, "set");
|
||||
@ -343,7 +399,7 @@ public class AccessorProperty extends Property {
|
||||
mh = generateSetter(forType, type);
|
||||
}
|
||||
|
||||
return isSpill() ? MH.filterArguments(mh, 0, SPILLGETTER) : mh;
|
||||
return mh;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -363,6 +419,30 @@ public class AccessorProperty extends Property {
|
||||
setCurrentType(newType);
|
||||
}
|
||||
|
||||
private MethodHandle getSpillGetter() {
|
||||
final int slot = getSlot();
|
||||
MethodHandle getter = slot < SPILL_CACHE_SIZE ? SPILL_ACCESSORS[slot * 2] : null;
|
||||
if (getter == null) {
|
||||
getter = MH.asType(MH.insertArguments(SPILL_ELEMENT_GETTER, 1, slot), Lookup.GET_OBJECT_TYPE);
|
||||
if (slot < SPILL_CACHE_SIZE) {
|
||||
SPILL_ACCESSORS[slot * 2] = getter;
|
||||
}
|
||||
}
|
||||
return getter;
|
||||
}
|
||||
|
||||
private MethodHandle getSpillSetter() {
|
||||
final int slot = getSlot();
|
||||
MethodHandle setter = slot < SPILL_CACHE_SIZE ? SPILL_ACCESSORS[slot * 2 + 1] : null;
|
||||
if (setter == null) {
|
||||
setter = MH.asType(MH.insertArguments(SPILL_ELEMENT_SETTER, 1, slot), Lookup.SET_OBJECT_TYPE);
|
||||
if (slot < SPILL_CACHE_SIZE) {
|
||||
SPILL_ACCESSORS[slot * 2 + 1] = setter;
|
||||
}
|
||||
}
|
||||
return setter;
|
||||
}
|
||||
|
||||
private static void finest(final String str) {
|
||||
if (DEBUG_FIELDS) {
|
||||
LOG.finest(str);
|
||||
|
||||
@ -153,5 +153,24 @@ public final class FindProperty {
|
||||
return prototype.isScope();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the property value from self as object.
|
||||
*
|
||||
* @return the property value
|
||||
*/
|
||||
public Object getObjectValue() {
|
||||
return property.getObjectValue(getGetterReceiver(), getOwner());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the property value in self.
|
||||
*
|
||||
* @param value the new value
|
||||
* @param strict strict flag
|
||||
*/
|
||||
public void setObjectValue(final Object value, final boolean strict) {
|
||||
property.setObjectValue(getSetterReceiver(), getOwner(), value, strict);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -351,6 +351,26 @@ public abstract class Property {
|
||||
return slot;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of this property in {@code owner}. This allows to bypass creation of the
|
||||
* setter MethodHandle for spill and user accessor properties.
|
||||
*
|
||||
* @param self the this object
|
||||
* @param owner the owner object
|
||||
* @param value the new property value
|
||||
*/
|
||||
protected abstract void setObjectValue(ScriptObject self, ScriptObject owner, Object value, boolean strict);
|
||||
|
||||
/**
|
||||
* Set the Object value of this property from {@code owner}. This allows to bypass creation of the
|
||||
* getter MethodHandle for spill and user accessor properties.
|
||||
*
|
||||
* @param self the this object
|
||||
* @param owner the owner object
|
||||
* @return the property value
|
||||
*/
|
||||
protected abstract Object getObjectValue(ScriptObject self, ScriptObject owner);
|
||||
|
||||
/**
|
||||
* Abstract method for retrieving the setter for the property. We do not know
|
||||
* anything about the internal representation when we request the setter, we only
|
||||
|
||||
@ -25,7 +25,6 @@
|
||||
|
||||
package jdk.nashorn.internal.runtime;
|
||||
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.staticCall;
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCall;
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup;
|
||||
import static jdk.nashorn.internal.lookup.Lookup.MH;
|
||||
@ -151,17 +150,6 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
|
||||
/** Method handle for setting the user accessors of a ScriptObject */
|
||||
public static final Call SET_USER_ACCESSORS = virtualCall(ScriptObject.class, "setUserAccessors", void.class, String.class, ScriptFunction.class, ScriptFunction.class);
|
||||
|
||||
/** Method handle for getter for {@link UserAccessorProperty}, given a slot */
|
||||
static final Call USER_ACCESSOR_GETTER = staticCall(MethodHandles.lookup(), ScriptObject.class, "userAccessorGetter", Object.class, ScriptObject.class, int.class, Object.class);
|
||||
|
||||
/** Method handle for setter for {@link UserAccessorProperty}, given a slot */
|
||||
static final Call USER_ACCESSOR_SETTER = staticCall(MethodHandles.lookup(), ScriptObject.class, "userAccessorSetter", void.class, ScriptObject.class, int.class, String.class, Object.class, Object.class);
|
||||
|
||||
private static final MethodHandle INVOKE_UA_GETTER = Bootstrap.createDynamicInvoker("dyn:call", Object.class,
|
||||
Object.class, Object.class);
|
||||
private static final MethodHandle INVOKE_UA_SETTER = Bootstrap.createDynamicInvoker("dyn:call", void.class,
|
||||
Object.class, Object.class, Object.class);
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
@ -699,17 +687,9 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
|
||||
* @return New property.
|
||||
*/
|
||||
public final Property addOwnProperty(final String key, final int propertyFlags, final Object value) {
|
||||
final MethodHandle setter = addSpill(key, propertyFlags);
|
||||
|
||||
try {
|
||||
setter.invokeExact((Object)this, value);
|
||||
} catch (final Error|RuntimeException e) {
|
||||
throw e;
|
||||
} catch (final Throwable e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
return getMap().findProperty(key);
|
||||
final Property property = addSpillProperty(key, propertyFlags);
|
||||
property.setObjectValue(this, this, value, false);
|
||||
return property;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -744,15 +724,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
|
||||
// Erase the property field value with undefined. If the property is defined
|
||||
// by user-defined accessors, we don't want to call the setter!!
|
||||
if (!(property instanceof UserAccessorProperty)) {
|
||||
try {
|
||||
// make the property value to be undefined
|
||||
//TODO specproperties
|
||||
property.getSetter(Object.class, getMap()).invokeExact((Object)this, (Object)UNDEFINED);
|
||||
} catch (final RuntimeException | Error e) {
|
||||
throw e;
|
||||
} catch (final Throwable t) {
|
||||
throw new RuntimeException(t);
|
||||
}
|
||||
property.setObjectValue(this, this, UNDEFINED, false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -948,18 +920,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
|
||||
* @return the value of the property
|
||||
*/
|
||||
protected static Object getObjectValue(final FindProperty find) {
|
||||
final MethodHandle getter = find.getGetter(Object.class);
|
||||
if (getter != null) {
|
||||
try {
|
||||
return getter.invokeExact((Object)find.getGetterReceiver());
|
||||
} catch (final Error|RuntimeException e) {
|
||||
throw e;
|
||||
} catch (final Throwable e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
return UNDEFINED;
|
||||
return find.getObjectValue();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2087,11 +2048,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
|
||||
property = addOwnProperty(property);
|
||||
} else {
|
||||
int i = getMap().getSpillLength();
|
||||
MethodHandle getter = MH.arrayElementGetter(Object[].class);
|
||||
MethodHandle setter = MH.arrayElementSetter(Object[].class);
|
||||
getter = MH.asType(MH.insertArguments(getter, 1, i), Lookup.GET_OBJECT_TYPE);
|
||||
setter = MH.asType(MH.insertArguments(setter, 1, i), Lookup.SET_OBJECT_TYPE);
|
||||
property = new AccessorProperty(key, propertyFlags | Property.IS_SPILL, i, getter, setter);
|
||||
property = new AccessorProperty(key, propertyFlags | Property.IS_SPILL, i);
|
||||
notifyPropertyAdded(this, property);
|
||||
property = addOwnProperty(property);
|
||||
i = property.getSlot();
|
||||
@ -2115,20 +2072,15 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
|
||||
|
||||
/**
|
||||
* Add a spill entry for the given key.
|
||||
* @param key Property key.
|
||||
* @param propertyFlags Property flags.
|
||||
* @param key Property key.
|
||||
* @return Setter method handle.
|
||||
*/
|
||||
private MethodHandle addSpill(final String key, final int propertyFlags) {
|
||||
final Property spillProperty = addSpillProperty(key, propertyFlags);
|
||||
MethodHandle addSpill(final String key) {
|
||||
final Property spillProperty = addSpillProperty(key, 0);
|
||||
final Class<?> type = Object.class;
|
||||
return spillProperty.getSetter(type, getMap()); //TODO specfields
|
||||
}
|
||||
|
||||
MethodHandle addSpill(final String key) {
|
||||
return addSpill(key, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure arguments are paired correctly, with respect to more parameters than declared,
|
||||
* fewer parameters than declared and other things that JavaScript allows. This might involve
|
||||
@ -2659,14 +2611,8 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
final MethodHandle setter = f.getSetter(Object.class, strict); //TODO specfields
|
||||
setter.invokeExact((Object)f.getSetterReceiver(), value);
|
||||
} catch (final Error|RuntimeException e) {
|
||||
throw e;
|
||||
} catch (final Throwable e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
f.setObjectValue(value, strict);
|
||||
|
||||
} else if (!isExtensible()) {
|
||||
if (strict) {
|
||||
throw typeError("object.non.extensible", key, ScriptRuntime.safeToString(this));
|
||||
@ -2677,13 +2623,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
|
||||
}
|
||||
|
||||
private void spill(final String key, final Object value) {
|
||||
try {
|
||||
addSpill(key).invokeExact((Object)this, value);
|
||||
} catch (final Error|RuntimeException e) {
|
||||
throw e;
|
||||
} catch (final Throwable e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
addSpillProperty(key, 0).setObjectValue(this, this, value, false);
|
||||
}
|
||||
|
||||
|
||||
@ -3217,46 +3157,6 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
|
||||
return (index < 0 || (index >= spill.length)) ? null : spill[index];
|
||||
}
|
||||
|
||||
// User defined getter and setter are always called by "dyn:call". Note that the user
|
||||
// getter/setter may be inherited. If so, proto is bound during lookup. In either
|
||||
// inherited or self case, slot is also bound during lookup. Actual ScriptFunction
|
||||
// to be called is retrieved everytime and applied.
|
||||
@SuppressWarnings("unused")
|
||||
private static Object userAccessorGetter(final ScriptObject proto, final int slot, final Object self) {
|
||||
final ScriptObject container = (proto != null) ? proto : (ScriptObject)self;
|
||||
final Object func = container.getSpill(slot);
|
||||
|
||||
if (func instanceof ScriptFunction) {
|
||||
try {
|
||||
return INVOKE_UA_GETTER.invokeExact(func, self);
|
||||
} catch(final Error|RuntimeException t) {
|
||||
throw t;
|
||||
} catch(final Throwable t) {
|
||||
throw new RuntimeException(t);
|
||||
}
|
||||
}
|
||||
|
||||
return UNDEFINED;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static void userAccessorSetter(final ScriptObject proto, final int slot, final String name, final Object self, final Object value) {
|
||||
final ScriptObject container = (proto != null) ? proto : (ScriptObject)self;
|
||||
final Object func = container.getSpill(slot);
|
||||
|
||||
if (func instanceof ScriptFunction) {
|
||||
try {
|
||||
INVOKE_UA_SETTER.invokeExact(func, self, value);
|
||||
} catch(final Error|RuntimeException t) {
|
||||
throw t;
|
||||
} catch(final Throwable t) {
|
||||
throw new RuntimeException(t);
|
||||
}
|
||||
} else if (name != null) {
|
||||
throw typeError("property.has.no.setter", name, ScriptRuntime.safeToString(self));
|
||||
}
|
||||
}
|
||||
|
||||
private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
|
||||
final Class<?> own = ScriptObject.class;
|
||||
final MethodType mt = MH.type(rtype, types);
|
||||
|
||||
@ -183,17 +183,10 @@ final class SetMethodCreator {
|
||||
private SetMethod createNewSpillPropertySetter() {
|
||||
final int nextSpill = getMap().getSpillLength();
|
||||
|
||||
final Property property = createSpillProperty(nextSpill);
|
||||
final Property property = new AccessorProperty(getName(), Property.IS_SPILL, nextSpill);
|
||||
return new SetMethod(createSpillMethodHandle(nextSpill, property), property);
|
||||
}
|
||||
|
||||
private Property createSpillProperty(final int nextSpill) {
|
||||
final MethodHandle getter = MH.asType(MH.insertArguments(MH.arrayElementGetter(Object[].class), 1, nextSpill), Lookup.GET_OBJECT_TYPE);
|
||||
final MethodHandle setter = MH.asType(MH.insertArguments(MH.arrayElementSetter(Object[].class), 1, nextSpill), Lookup.SET_OBJECT_TYPE);
|
||||
|
||||
return new AccessorProperty(getName(), Property.IS_SPILL, nextSpill, getter, setter);
|
||||
}
|
||||
|
||||
private MethodHandle createSpillMethodHandle(final int nextSpill, Property property) {
|
||||
final PropertyMap oldMap = getMap();
|
||||
final PropertyMap newMap = getNewMap(property);
|
||||
|
||||
@ -26,7 +26,15 @@
|
||||
package jdk.nashorn.internal.runtime;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
|
||||
import jdk.nashorn.internal.codegen.CompilerConstants;
|
||||
import jdk.nashorn.internal.lookup.Lookup;
|
||||
import jdk.nashorn.internal.runtime.linker.Bootstrap;
|
||||
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.staticCall;
|
||||
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
|
||||
import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
|
||||
|
||||
/**
|
||||
* Property with user defined getters/setters. Actual getter and setter
|
||||
@ -51,6 +59,22 @@ public final class UserAccessorProperty extends Property {
|
||||
/** User defined setter function slot. */
|
||||
private final int setterSlot;
|
||||
|
||||
/** Getter method handle */
|
||||
private final static CompilerConstants.Call USER_ACCESSOR_GETTER = staticCall(MethodHandles.lookup(), UserAccessorProperty.class,
|
||||
"userAccessorGetter", Object.class, ScriptObject.class, int.class, Object.class);
|
||||
|
||||
/** Setter method handle */
|
||||
private final static CompilerConstants.Call USER_ACCESSOR_SETTER = staticCall(MethodHandles.lookup(), UserAccessorProperty.class,
|
||||
"userAccessorSetter", void.class, ScriptObject.class, int.class, String.class, Object.class, Object.class);
|
||||
|
||||
/** Dynamic invoker for getter */
|
||||
private static final MethodHandle INVOKE_UA_GETTER = Bootstrap.createDynamicInvoker("dyn:call", Object.class,
|
||||
Object.class, Object.class);
|
||||
|
||||
/** Dynamic invoker for setter */
|
||||
private static final MethodHandle INVOKE_UA_SETTER = Bootstrap.createDynamicInvoker("dyn:call", void.class,
|
||||
Object.class, Object.class, Object.class);
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
@ -133,9 +157,19 @@ public final class UserAccessorProperty extends Property {
|
||||
return setterSlot > -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object getObjectValue(final ScriptObject self, final ScriptObject owner) {
|
||||
return userAccessorGetter(owner, getGetterSlot(), self);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setObjectValue(final ScriptObject self, final ScriptObject owner, final Object value, final boolean strict) {
|
||||
userAccessorSetter(owner, getSetterSlot(), strict ? getKey() : null, self, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodHandle getGetter(final Class<?> type) {
|
||||
return Lookup.filterReturnType(ScriptObject.USER_ACCESSOR_GETTER.methodHandle(), type);
|
||||
return Lookup.filterReturnType(USER_ACCESSOR_GETTER.methodHandle(), type);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -146,7 +180,7 @@ public final class UserAccessorProperty extends Property {
|
||||
|
||||
@Override
|
||||
public MethodHandle getSetter(final Class<?> type, final PropertyMap currentMap) {
|
||||
return ScriptObject.USER_ACCESSOR_SETTER.methodHandle();
|
||||
return USER_ACCESSOR_SETTER.methodHandle();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -155,4 +189,44 @@ public final class UserAccessorProperty extends Property {
|
||||
return (value instanceof ScriptFunction) ? (ScriptFunction) value : null;
|
||||
}
|
||||
|
||||
// User defined getter and setter are always called by "dyn:call". Note that the user
|
||||
// getter/setter may be inherited. If so, proto is bound during lookup. In either
|
||||
// inherited or self case, slot is also bound during lookup. Actual ScriptFunction
|
||||
// to be called is retrieved everytime and applied.
|
||||
@SuppressWarnings("unused")
|
||||
static Object userAccessorGetter(final ScriptObject proto, final int slot, final Object self) {
|
||||
final ScriptObject container = (proto != null) ? proto : (ScriptObject)self;
|
||||
final Object func = container.getSpill(slot);
|
||||
|
||||
if (func instanceof ScriptFunction) {
|
||||
try {
|
||||
return INVOKE_UA_GETTER.invokeExact(func, self);
|
||||
} catch(final Error|RuntimeException t) {
|
||||
throw t;
|
||||
} catch(final Throwable t) {
|
||||
throw new RuntimeException(t);
|
||||
}
|
||||
}
|
||||
|
||||
return UNDEFINED;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
static void userAccessorSetter(final ScriptObject proto, final int slot, final String name, final Object self, final Object value) {
|
||||
final ScriptObject container = (proto != null) ? proto : (ScriptObject)self;
|
||||
final Object func = container.getSpill(slot);
|
||||
|
||||
if (func instanceof ScriptFunction) {
|
||||
try {
|
||||
INVOKE_UA_SETTER.invokeExact(func, self, value);
|
||||
} catch(final Error|RuntimeException t) {
|
||||
throw t;
|
||||
} catch(final Throwable t) {
|
||||
throw new RuntimeException(t);
|
||||
}
|
||||
} else if (name != null) {
|
||||
throw typeError("property.has.no.setter", name, ScriptRuntime.safeToString(self));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user