diff options
Diffstat (limited to 'src/jdk/nashorn/internal/runtime/SetMethodCreator.java')
-rw-r--r-- | src/jdk/nashorn/internal/runtime/SetMethodCreator.java | 145 |
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); } } |