/*
 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

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.dynalink.CallSiteDescriptor;
import jdk.dynalink.linker.GuardedInvocation;
import jdk.dynalink.linker.LinkRequest;
import jdk.dynalink.linker.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;

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 jdk.nashorn.internal.objects package.
/** * 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.
/** 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.
Params:
  • request – the link request for the dynamic call site.
  • receiverClass – the class of the receiver value (e.g., Boolean, String etc.)
  • wrappedReceiver – a transient JavaScript native wrapper object created as the object proxy for the primitive value; see ECMAScript 5.1, section 8.7.1 for discussion of using [[Get]] on a property reference with a primitive base value. This instance will be used to delegate actual lookup.
  • wrapFilter – A method handle that takes a primitive value of type specified in the receiverClass and creates a transient native wrapper of the same type as wrappedReceiver for subsequent invocations of the method - it will be combined into the returned invocation as an argument filter on the receiver.
  • protoFilter – A method handle that walks up the proto chain of this receiver object type receiverClass.
Returns:a guarded invocation representing the operation at the call site when performed on a JavaScript primitive
/** * Returns a guarded invocation representing the linkage for a dynamic operation on a primitive Java value. * @param request the link request for the dynamic call site. * @param receiverClass the class of the receiver value (e.g., {@link java.lang.Boolean}, {@link java.lang.String} etc.) * @param wrappedReceiver a transient JavaScript native wrapper object created as the object proxy for the primitive * value; see ECMAScript 5.1, section 8.7.1 for discussion of using {@code [[Get]]} on a property reference with a * primitive base value. This instance will be used to delegate actual lookup. * @param wrapFilter A method handle that takes a primitive value of type specified in the {@code receiverClass} and * creates a transient native wrapper of the same type as {@code wrappedReceiver} for subsequent invocations of the * method - it will be combined into the returned invocation as an argument filter on the receiver. * @return a guarded invocation representing the operation at the call site when performed on a JavaScript primitive * @param protoFilter A method handle that walks up the proto chain of this receiver object * type {@code receiverClass}. */
public static GuardedInvocation lookupPrimitive(final LinkRequest request, final Class<?> receiverClass, final ScriptObject wrappedReceiver, final MethodHandle wrapFilter, final MethodHandle protoFilter) { return lookupPrimitive(request, Guards.getInstanceOfGuard(receiverClass), wrappedReceiver, wrapFilter, protoFilter); }
Returns a guarded invocation representing the linkage for a dynamic operation on a primitive Java value.
Params:
  • request – the link request for the dynamic call site.
  • guard – an explicit guard that will be used for the returned guarded invocation.
  • wrappedReceiver – a transient JavaScript native wrapper object created as the object proxy for the primitive value; see ECMAScript 5.1, section 8.7.1 for discussion of using [[Get]] on a property reference with a primitive base value. This instance will be used to delegate actual lookup.
  • wrapFilter – A method handle that takes a primitive value of type guarded by the guard and creates a transient native wrapper of the same type as wrappedReceiver for subsequent invocations of the method - it will be combined into the returned invocation as an argument filter on the receiver.
  • protoFilter – A method handle that walks up the proto chain of this receiver object
Returns:a guarded invocation representing the operation at the call site when performed on a JavaScript primitive type (that is implied by both guard and wrappedReceiver).
/** * Returns a guarded invocation representing the linkage for a dynamic operation on a primitive Java value. * @param request the link request for the dynamic call site. * @param guard an explicit guard that will be used for the returned guarded invocation. * @param wrappedReceiver a transient JavaScript native wrapper object created as the object proxy for the primitive * value; see ECMAScript 5.1, section 8.7.1 for discussion of using {@code [[Get]]} on a property reference with a * primitive base value. This instance will be used to delegate actual lookup. * @param wrapFilter A method handle that takes a primitive value of type guarded by the {@code guard} and * creates a transient native wrapper of the same type as {@code wrappedReceiver} for subsequent invocations of the * method - it will be combined into the returned invocation as an argument filter on the receiver. * @param protoFilter A method handle that walks up the proto chain of this receiver object * @return a guarded invocation representing the operation at the call site when performed on a JavaScript primitive * type (that is implied by both {@code guard} and {@code wrappedReceiver}). */
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 = NashornCallSiteDescriptor.getOperand(desc); final FindProperty find = name != null ? wrappedReceiver.findProperty(name, true) : null; switch (NashornCallSiteDescriptor.getStandardOperation(desc)) { case GET: //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; } final SwitchPoint sp = find.getProperty().getBuiltinSwitchPoint(); //can use this instead of proto filter if (sp instanceof Context.BuiltinSwitchPoint && !sp.hasBeenInvalidated()) { return new GuardedInvocation(GlobalConstants.staticConstantGetter(find.getObjectValue()), guard, sp, null); } if (find.isInheritedOrdinaryProperty()) { // If property is found in the prototype object bind the method handle directly to // the proto filter instead of going through wrapper instantiation below. 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 SET: 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); if (receiverType != Object.class) { final MethodType wrapType = wrapFilter.type(); assert receiverType.isAssignableFrom(wrapType.returnType()); method = MH.filterArguments(method, 0, MH.asType(wrapFilter, wrapType.changeReturnType(receiverType))); } 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().isAccessorProperty() || !find.getProperty().hasNativeSetter()) { if (strict) { if (find == null || !find.getProperty().isAccessorProperty()) { throw typeError("property.not.writable", name, ScriptRuntime.safeToString(self)); } else { throw typeError("property.has.no.setter", 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); } }