aboutsummaryrefslogtreecommitdiff
path: root/src/jdk/nashorn/internal/runtime/SetMethodCreator.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/jdk/nashorn/internal/runtime/SetMethodCreator.java')
-rw-r--r--src/jdk/nashorn/internal/runtime/SetMethodCreator.java145
1 files changed, 96 insertions, 49 deletions
diff --git a/src/jdk/nashorn/internal/runtime/SetMethodCreator.java b/src/jdk/nashorn/internal/runtime/SetMethodCreator.java
index edee3748..90bbf43c 100644
--- a/src/jdk/nashorn/internal/runtime/SetMethodCreator.java
+++ b/src/jdk/nashorn/internal/runtime/SetMethodCreator.java
@@ -25,17 +25,17 @@
package jdk.nashorn.internal.runtime;
-import static jdk.nashorn.internal.runtime.ECMAErrors.referenceError;
import static jdk.nashorn.internal.lookup.Lookup.MH;
-
+import static jdk.nashorn.internal.runtime.ECMAErrors.referenceError;
+import static jdk.nashorn.internal.runtime.JSType.getAccessorTypeIndex;
import java.lang.invoke.MethodHandle;
+import java.lang.invoke.SwitchPoint;
import jdk.internal.dynalink.CallSiteDescriptor;
import jdk.internal.dynalink.linker.GuardedInvocation;
-import jdk.nashorn.internal.lookup.Lookup;
+import jdk.internal.dynalink.linker.LinkRequest;
import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;
import jdk.nashorn.internal.runtime.linker.NashornGuards;
-
/**
* Instances of this class are quite ephemeral; they only exist for the duration of an invocation of
* {@link ScriptObject#findSetMethod(CallSiteDescriptor, jdk.internal.dynalink.linker.LinkRequest)} and
@@ -43,10 +43,12 @@ import jdk.nashorn.internal.runtime.linker.NashornGuards;
*/
final class SetMethodCreator {
// See constructor parameters for description of fields
- private final ScriptObject sobj;
- private final PropertyMap map;
- private final FindProperty find;
+ private final ScriptObject sobj;
+ private final PropertyMap map;
+ private final FindProperty find;
private final CallSiteDescriptor desc;
+ private final Class<?> type;
+ private final LinkRequest request;
/**
* Creates a new property setter method creator.
@@ -54,12 +56,16 @@ final class SetMethodCreator {
* @param find a result of a {@link ScriptObject#findProperty(String, boolean)} on the object for the property we
* want to create a setter for. Can be null if the property does not yet exist on the object.
* @param desc the descriptor of the call site that triggered the property setter lookup
+ * @param request the link request
*/
- SetMethodCreator(final ScriptObject sobj, final FindProperty find, final CallSiteDescriptor desc) {
- this.sobj = sobj;
- this.map = sobj.getMap();
- this.find = find;
- this.desc = desc;
+ SetMethodCreator(final ScriptObject sobj, final FindProperty find, final CallSiteDescriptor desc, final LinkRequest request) {
+ this.sobj = sobj;
+ this.map = sobj.getMap();
+ this.find = find;
+ this.desc = desc;
+ this.type = desc.getMethodType().parameterType(1);
+ this.request = request;
+
}
private String getName() {
@@ -74,8 +80,8 @@ final class SetMethodCreator {
* Creates the actual guarded invocation that represents the dynamic setter method for the property.
* @return the actual guarded invocation that represents the dynamic setter method for the property.
*/
- GuardedInvocation createGuardedInvocation() {
- return createSetMethod().createGuardedInvocation();
+ GuardedInvocation createGuardedInvocation(final SwitchPoint builtinSwitchPoint) {
+ return createSetMethod(builtinSwitchPoint).createGuardedInvocation();
}
/**
@@ -95,7 +101,7 @@ final class SetMethodCreator {
SetMethod(final MethodHandle methodHandle, final Property property) {
assert methodHandle != null;
this.methodHandle = methodHandle;
- this.property = property;
+ this.property = property;
}
/**
@@ -103,12 +109,16 @@ final class SetMethodCreator {
* @return the composed guarded invocation that represents the dynamic setter method for the property.
*/
GuardedInvocation createGuardedInvocation() {
- return new GuardedInvocation(methodHandle, NashornGuards.getGuard(sobj, property, desc));
+ // getGuard() and getException() either both return null, or neither does. The reason for that is that now
+ // getGuard returns a map guard that casts its argument to ScriptObject, and if that fails, we need to
+ // relink on ClassCastException.
+ final boolean explicitInstanceOfCheck = NashornGuards.explicitInstanceOfCheck(desc, request);
+ return new GuardedInvocation(methodHandle, NashornGuards.getGuard(sobj, property, desc, explicitInstanceOfCheck),
+ (SwitchPoint)null, explicitInstanceOfCheck ? null : ClassCastException.class);
}
-
}
- private SetMethod createSetMethod() {
+ private SetMethod createSetMethod(final SwitchPoint builtinSwitchPoint) {
if (find != null) {
return createExistingPropertySetter();
}
@@ -119,7 +129,7 @@ final class SetMethodCreator {
return createGlobalPropertySetter();
}
- return createNewPropertySetter();
+ return createNewPropertySetter(builtinSwitchPoint);
}
private void checkStrictCreateNewVariable() {
@@ -132,14 +142,36 @@ final class SetMethodCreator {
private SetMethod createExistingPropertySetter() {
final Property property = find.getProperty();
- final Class<?> type = desc.getMethodType().parameterType(1);
- final MethodHandle methodHandle = find.getSetter(type, NashornCallSiteDescriptor.isStrict(desc));
+ final boolean isStrict = NashornCallSiteDescriptor.isStrict(desc);
+ final MethodHandle methodHandle;
+
+ if (NashornCallSiteDescriptor.isDeclaration(desc)) {
+ assert property.needsDeclaration();
+ // This is a LET or CONST being declared. The property is already there but flagged as needing declaration.
+ // We create a new PropertyMap with the flag removed. The map is installed with a fast compare-and-set
+ // method if the pre-callsite map is stable (which should be the case for function scopes except for
+ // non-strict functions containing eval() with var). Otherwise we have to use a slow setter that creates
+ // a new PropertyMap on the fly.
+ final PropertyMap oldMap = getMap();
+ final Property newProperty = property.removeFlags(Property.NEEDS_DECLARATION);
+ final PropertyMap newMap = oldMap.replaceProperty(property, newProperty);
+ final MethodHandle fastSetter = find.replaceProperty(newProperty).getSetter(type, isStrict, request);
+ final MethodHandle slowSetter = MH.insertArguments(ScriptObject.DECLARE_AND_SET, 1, getName()).asType(fastSetter.type());
+
+ // cas map used as guard, if true that means we can do the set fast
+ MethodHandle casMap = MH.insertArguments(ScriptObject.CAS_MAP, 1, oldMap, newMap);
+ casMap = MH.dropArguments(casMap, 1, type);
+ casMap = MH.asType(casMap, casMap.type().changeParameterType(0, Object.class));
+ methodHandle = MH.guardWithTest(casMap, fastSetter, slowSetter);
+ } else {
+ methodHandle = find.getSetter(type, isStrict, request);
+ }
assert methodHandle != null;
assert property != null;
final MethodHandle boundHandle;
- if (!property.hasSetterFunction(find.getOwner()) && find.isInherited()) {
+ if (!(property instanceof UserAccessorProperty) && find.isInherited()) {
boundHandle = ScriptObject.addProtoFilter(methodHandle, find.getProtoChainLength());
} else {
boundHandle = methodHandle;
@@ -149,11 +181,11 @@ final class SetMethodCreator {
private SetMethod createGlobalPropertySetter() {
final ScriptObject global = Context.getGlobal();
- return new SetMethod(MH.filterArguments(global.addSpill(getName()), 0, ScriptObject.GLOBALFILTER), null);
+ return new SetMethod(MH.filterArguments(global.addSpill(type, getName()), 0, ScriptObject.GLOBALFILTER), null);
}
- private SetMethod createNewPropertySetter() {
- final SetMethod sm = map.getFieldCount() < map.getFieldMaximum() ? createNewFieldSetter() : createNewSpillPropertySetter();
+ private SetMethod createNewPropertySetter(final SwitchPoint builtinSwitchPoint) {
+ final SetMethod sm = map.getFreeFieldSlot() > -1 ? createNewFieldSetter(builtinSwitchPoint) : createNewSpillPropertySetter(builtinSwitchPoint);
final PropertyListeners listeners = map.getListeners();
if (listeners != null) {
listeners.propertyAdded(sm.property);
@@ -161,38 +193,53 @@ final class SetMethodCreator {
return sm;
}
- private SetMethod createNewFieldSetter() {
- final PropertyMap oldMap = getMap();
- final Property property = new AccessorProperty(getName(), 0, sobj.getClass(), oldMap.getFieldCount());
- final PropertyMap newMap = oldMap.addProperty(property);
- MethodHandle setter = MH.insertArguments(ScriptObject.SETFIELD, 0, desc, oldMap, newMap, property.getSetter(Object.class, newMap));
+ private SetMethod createNewSetter(final Property property, final SwitchPoint builtinSwitchPoint) {
+ property.setBuiltinSwitchPoint(builtinSwitchPoint);
- return new SetMethod(MH.asType(setter, Lookup.SET_OBJECT_TYPE), property);
- }
+ final PropertyMap oldMap = getMap();
+ final PropertyMap newMap = getNewMap(property);
+ final boolean isStrict = NashornCallSiteDescriptor.isStrict(desc);
+ final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
- private SetMethod createNewSpillPropertySetter() {
- final int nextSpill = getMap().getSpillLength();
+ //fast type specific setter
+ final MethodHandle fastSetter = property.getSetter(type, newMap); //0 sobj, 1 value, slot folded for spill property already
- final Property property = new AccessorProperty(getName(), Property.IS_SPILL, nextSpill);
- return new SetMethod(createSpillMethodHandle(nextSpill, property), property);
+ //slow setter, that calls ScriptObject.set with appropraite type and key name
+ MethodHandle slowSetter = ScriptObject.SET_SLOW[getAccessorTypeIndex(type)];
+ slowSetter = MH.insertArguments(slowSetter, 3, NashornCallSiteDescriptor.getFlags(desc));
+ slowSetter = MH.insertArguments(slowSetter, 1, name);
+ slowSetter = MH.asType(slowSetter, slowSetter.type().changeParameterType(0, Object.class));
+
+ assert slowSetter.type().equals(fastSetter.type()) : "slow=" + slowSetter + " != fast=" + fastSetter;
+
+ //cas map used as guard, if true that means we can do the set fast
+ MethodHandle casMap = MH.insertArguments(ScriptObject.CAS_MAP, 1, oldMap, newMap);
+ casMap = MH.dropArguments(casMap, 1, type);
+ casMap = MH.asType(casMap, casMap.type().changeParameterType(0, Object.class));
+ final MethodHandle casGuard = MH.guardWithTest(casMap, fastSetter, slowSetter);
+
+ //outermost level needs an extendable check. if object can be extended, guard is true and
+ //we can run the cas setter. The setter goes to "nop" VOID_RETURN if false or throws an
+ //exception if we are in strict mode and object is not extensible
+ MethodHandle extCheck = MH.insertArguments(ScriptObject.EXTENSION_CHECK, 1, isStrict, name);
+ extCheck = MH.asType(extCheck, extCheck.type().changeParameterType(0, Object.class));
+ extCheck = MH.dropArguments(extCheck, 1, type);
+
+ MethodHandle nop = JSType.VOID_RETURN.methodHandle();
+ nop = MH.dropArguments(nop, 0, Object.class, type);
+
+ return new SetMethod(MH.asType(MH.guardWithTest(extCheck, casGuard, nop), fastSetter.type()), property);
}
- private MethodHandle createSpillMethodHandle(final int nextSpill, Property property) {
- final PropertyMap oldMap = getMap();
- final PropertyMap newMap = getNewMap(property);
+ private SetMethod createNewFieldSetter(final SwitchPoint builtinSwitchPoint) {
+ return createNewSetter(new AccessorProperty(getName(), 0, sobj.getClass(), getMap().getFreeFieldSlot(), type), builtinSwitchPoint);
+ }
- final Object[] spill = sobj.spill;
- if (spill == null) {
- return MH.insertArguments(ScriptObject.SETSPILLWITHNEW, 0, desc, oldMap, newMap, nextSpill);
- } else if (nextSpill < spill.length) {
- return MH.insertArguments(ScriptObject.SETSPILL, 0, desc, oldMap, newMap, nextSpill);
- } else {
- final int newLength = (nextSpill + ScriptObject.SPILL_RATE) / ScriptObject.SPILL_RATE * ScriptObject.SPILL_RATE;
- return MH.insertArguments(ScriptObject.SETSPILLWITHGROW, 0, desc, oldMap, newMap, nextSpill, newLength);
- }
+ private SetMethod createNewSpillPropertySetter(final SwitchPoint builtinSwitchPoint) {
+ return createNewSetter(new SpillProperty(getName(), 0, getMap().getFreeSpillSlot(), type), builtinSwitchPoint);
}
- private PropertyMap getNewMap(Property property) {
+ private PropertyMap getNewMap(final Property property) {
return getMap().addProperty(property);
}
}