src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/PrimitiveLookup.java

Print this page

        

*** 24,53 **** --- 24,64 ---- */ package jdk.nashorn.internal.runtime.linker; import static jdk.nashorn.internal.lookup.Lookup.MH; + import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; + import java.lang.invoke.MethodHandle; + import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; import java.lang.invoke.SwitchPoint; import jdk.internal.dynalink.CallSiteDescriptor; import jdk.internal.dynalink.linker.GuardedInvocation; import jdk.internal.dynalink.linker.LinkRequest; + import jdk.internal.dynalink.support.CallSiteDescriptorFactory; import jdk.internal.dynalink.support.Guards; import jdk.nashorn.internal.runtime.Context; import jdk.nashorn.internal.runtime.FindProperty; import jdk.nashorn.internal.runtime.GlobalConstants; + import jdk.nashorn.internal.runtime.JSType; import jdk.nashorn.internal.runtime.ScriptObject; + import jdk.nashorn.internal.runtime.ScriptRuntime; import jdk.nashorn.internal.runtime.UserAccessorProperty; /** * Implements lookup of methods to link for dynamic operations on JavaScript primitive values (booleans, strings, and * numbers). This class is only public so it can be accessed by classes in the {@code jdk.nashorn.internal.objects} * package. */ public final class PrimitiveLookup { + /** Method handle to link setters on primitive base. See ES5 8.7.2. */ + private static final MethodHandle PRIMITIVE_SETTER = findOwnMH("primitiveSetter", + MH.type(void.class, ScriptObject.class, Object.class, Object.class, boolean.class, Object.class)); + + private PrimitiveLookup() { } /** * Returns a guarded invocation representing the linkage for a dynamic operation on a primitive Java value.
*** 85,102 **** */ public static GuardedInvocation lookupPrimitive(final LinkRequest request, final MethodHandle guard, final ScriptObject wrappedReceiver, final MethodHandle wrapFilter, final MethodHandle protoFilter) { final CallSiteDescriptor desc = request.getCallSiteDescriptor(); //checks whether the property name is hard-coded in the call-site (i.e. a getProp vs a getElem, or setProp vs setElem) //if it is we can make assumptions on the property: that if it is not defined on primitive wrapper itself it never will be. //so in that case we can skip creation of primitive wrapper and start our search with the prototype. ! if (desc.getNameTokenCount() > 2) { ! final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND); ! final FindProperty find = wrappedReceiver.findProperty(name, true); ! if (find == null) { // Give up early, give chance to BeanLinker and NashornBottomLinker to deal with it. return null; } --- 96,126 ---- */ public static GuardedInvocation lookupPrimitive(final LinkRequest request, final MethodHandle guard, final ScriptObject wrappedReceiver, final MethodHandle wrapFilter, final MethodHandle protoFilter) { final CallSiteDescriptor desc = request.getCallSiteDescriptor(); + final String name; + final FindProperty find; + + if (desc.getNameTokenCount() > 2) { + name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND); + find = wrappedReceiver.findProperty(name, true); + } else { + name = null; + find = null; + } + final String firstOp = CallSiteDescriptorFactory.tokenizeOperators(desc).get(0); + + switch (firstOp) { + case "getProp": + case "getElem": + case "getMethod": //checks whether the property name is hard-coded in the call-site (i.e. a getProp vs a getElem, or setProp vs setElem) //if it is we can make assumptions on the property: that if it is not defined on primitive wrapper itself it never will be. //so in that case we can skip creation of primitive wrapper and start our search with the prototype. ! if (name != null) { if (find == null) { // Give up early, give chance to BeanLinker and NashornBottomLinker to deal with it. return null; }
*** 111,129 **** final ScriptObject proto = wrappedReceiver.getProto(); final GuardedInvocation link = proto.lookup(desc, request); if (link != null) { final MethodHandle invocation = link.getInvocation(); //this contains the builtin switchpoint - final MethodHandle adaptedInvocation = MH.asType(invocation, invocation.type().changeParameterType(0, Object.class)); final MethodHandle method = MH.filterArguments(adaptedInvocation, 0, protoFilter); final MethodHandle protoGuard = MH.filterArguments(link.getGuard(), 0, protoFilter); - return new GuardedInvocation(method, NashornGuards.combineGuards(guard, protoGuard)); } } } final GuardedInvocation link = wrappedReceiver.lookup(desc, request); if (link != null) { MethodHandle method = link.getInvocation(); final Class<?> receiverType = method.type().parameterType(0); --- 135,158 ---- final ScriptObject proto = wrappedReceiver.getProto(); final GuardedInvocation link = proto.lookup(desc, request); if (link != null) { final MethodHandle invocation = link.getInvocation(); //this contains the builtin switchpoint final MethodHandle adaptedInvocation = MH.asType(invocation, invocation.type().changeParameterType(0, Object.class)); final MethodHandle method = MH.filterArguments(adaptedInvocation, 0, protoFilter); final MethodHandle protoGuard = MH.filterArguments(link.getGuard(), 0, protoFilter); return new GuardedInvocation(method, NashornGuards.combineGuards(guard, protoGuard)); } } } + break; + case "setProp": + case "setElem": + return getPrimitiveSetter(name, guard, wrapFilter, NashornCallSiteDescriptor.isStrict(desc)); + default: + break; + } final GuardedInvocation link = wrappedReceiver.lookup(desc, request); if (link != null) { MethodHandle method = link.getInvocation(); final Class<?> receiverType = method.type().parameterType(0);
*** 136,141 **** --- 165,207 ---- return new GuardedInvocation(method, guard, link.getSwitchPoints(), null); } return null; } + + private static GuardedInvocation getPrimitiveSetter(final String name, final MethodHandle guard, + final MethodHandle wrapFilter, final boolean isStrict) { + MethodHandle filter = MH.asType(wrapFilter, wrapFilter.type().changeReturnType(ScriptObject.class)); + final MethodHandle target; + + if (name == null) { + filter = MH.dropArguments(filter, 1, Object.class, Object.class); + target = MH.insertArguments(PRIMITIVE_SETTER, 3, isStrict); + } else { + filter = MH.dropArguments(filter, 1, Object.class); + target = MH.insertArguments(PRIMITIVE_SETTER, 2, name, isStrict); + } + + return new GuardedInvocation(MH.foldArguments(target, filter), guard); + } + + + @SuppressWarnings("unused") + private static void primitiveSetter(final ScriptObject wrappedSelf, final Object self, final Object key, + final boolean strict, final Object value) { + // See ES5.1 8.7.2 PutValue (V, W) + final String name = JSType.toString(key); + final FindProperty find = wrappedSelf.findProperty(name, true); + if (find == null || !(find.getProperty() instanceof UserAccessorProperty) || !find.getProperty().isWritable()) { + if (strict) { + throw typeError("property.not.writable", name, ScriptRuntime.safeToString(self)); + } + return; + } + // property found and is a UserAccessorProperty + find.setValue(value, strict); + } + + private static MethodHandle findOwnMH(final String name, final MethodType type) { + return MH.findStatic(MethodHandles.lookup(), PrimitiveLookup.class, name, type); + } }