提交 d8080e04 编写于 作者: H hannesw

8011630: JSON parsing performance issue

Reviewed-by: lagergren, sundar
上级 d5c328f7
......@@ -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);
this.getters = new MethodHandle[NOOF_TYPES];
super(property);
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));
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册