PrimitiveLookup.java revision 953:221a84ef44c0
1/*
2 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package jdk.nashorn.internal.runtime.linker;
27
28import static jdk.nashorn.internal.lookup.Lookup.MH;
29
30import java.lang.invoke.MethodHandle;
31import java.lang.invoke.MethodType;
32import jdk.internal.dynalink.CallSiteDescriptor;
33import jdk.internal.dynalink.linker.GuardedInvocation;
34import jdk.internal.dynalink.linker.LinkRequest;
35import jdk.internal.dynalink.support.CallSiteDescriptorFactory;
36import jdk.internal.dynalink.support.Guards;
37import jdk.nashorn.internal.lookup.Lookup;
38import jdk.nashorn.internal.runtime.FindProperty;
39import jdk.nashorn.internal.runtime.ScriptObject;
40
41/**
42 * Implements lookup of methods to link for dynamic operations on JavaScript primitive values (booleans, strings, and
43 * numbers). This class is only public so it can be accessed by classes in the {@code jdk.nashorn.internal.objects}
44 * package.
45 */
46public final class PrimitiveLookup {
47
48    private PrimitiveLookup() {
49    }
50
51    /**
52     * Returns a guarded invocation representing the linkage for a dynamic operation on a primitive Java value.
53     * @param request the link request for the dynamic call site.
54     * @param receiverClass the class of the receiver value (e.g., {@link java.lang.Boolean}, {@link java.lang.String} etc.)
55     * @param wrappedReceiver a transient JavaScript native wrapper object created as the object proxy for the primitive
56     * value; see ECMAScript 5.1, section 8.7.1 for discussion of using {@code [[Get]]} on a property reference with a
57     * primitive base value. This instance will be used to delegate actual lookup.
58     * @param wrapFilter A method handle that takes a primitive value of type specified in the {@code receiverClass} and
59     * creates a transient native wrapper of the same type as {@code wrappedReceiver} for subsequent invocations of the
60     * method - it will be combined into the returned invocation as an argument filter on the receiver.
61     * @return a guarded invocation representing the operation at the call site when performed on a JavaScript primitive
62     * @param protoFilter A method handle that walks up the proto chain of this receiver object
63     * type {@code receiverClass}.
64     */
65    public static GuardedInvocation lookupPrimitive(final LinkRequest request, final Class<?> receiverClass,
66                                                    final ScriptObject wrappedReceiver, final MethodHandle wrapFilter,
67                                                    final MethodHandle protoFilter) {
68        return lookupPrimitive(request, Guards.getInstanceOfGuard(receiverClass), wrappedReceiver, wrapFilter, protoFilter);
69    }
70
71    /**
72     * Returns a guarded invocation representing the linkage for a dynamic operation on a primitive Java value.
73     * @param request the link request for the dynamic call site.
74     * @param guard an explicit guard that will be used for the returned guarded invocation.
75     * @param wrappedReceiver a transient JavaScript native wrapper object created as the object proxy for the primitive
76     * value; see ECMAScript 5.1, section 8.7.1 for discussion of using {@code [[Get]]} on a property reference with a
77     * primitive base value. This instance will be used to delegate actual lookup.
78     * @param wrapFilter A method handle that takes a primitive value of type guarded by the {@code guard} and
79     * creates a transient native wrapper of the same type as {@code wrappedReceiver} for subsequent invocations of the
80     * method - it will be combined into the returned invocation as an argument filter on the receiver.
81     * @param protoFilter A method handle that walks up the proto chain of this receiver object
82     * @return a guarded invocation representing the operation at the call site when performed on a JavaScript primitive
83     * type (that is implied by both {@code guard} and {@code wrappedReceiver}).
84     */
85    public static GuardedInvocation lookupPrimitive(final LinkRequest request, final MethodHandle guard,
86                                                    final ScriptObject wrappedReceiver, final MethodHandle wrapFilter,
87                                                    final MethodHandle protoFilter) {
88        final CallSiteDescriptor desc = request.getCallSiteDescriptor();
89        final String operator = CallSiteDescriptorFactory.tokenizeOperators(desc).get(0);
90        if ("setProp".equals(operator) || "setElem".equals(operator)) {
91            final MethodType type = desc.getMethodType();
92            MethodHandle method = MH.asType(Lookup.EMPTY_SETTER, MH.type(void.class, Object.class, type.parameterType(1)));
93            if (type.parameterCount() == 3) {
94                method = MH.dropArguments(method, 2, type.parameterType(2));
95            }
96            return new GuardedInvocation(method, guard);
97        }
98
99        if(desc.getNameTokenCount() > 2) {
100            final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
101            final FindProperty find = wrappedReceiver.findProperty(name, true);
102            if(find == null) {
103                // Give up early, give chance to BeanLinker and NashornBottomLinker to deal with it.
104                return null;
105            } else if (find.isInherited() && !find.getProperty().hasGetterFunction(find.getOwner())) {
106                // If property is found in the prototype object bind the method handle directly to
107                // the proto filter instead of going through wrapper instantiation below.
108                final ScriptObject proto = wrappedReceiver.getProto();
109                final GuardedInvocation link = proto.lookup(desc, request);
110
111                if (link != null) {
112                    final MethodHandle invocation = link.getInvocation();
113                    final MethodHandle adaptedInvocation = MH.asType(invocation, invocation.type().changeParameterType(0, Object.class));
114                    final MethodHandle method = MH.filterArguments(adaptedInvocation, 0, protoFilter);
115                    final MethodHandle protoGuard = MH.filterArguments(link.getGuard(), 0, protoFilter);
116                    return new GuardedInvocation(method, NashornGuards.combineGuards(guard, protoGuard));
117                }
118            }
119        }
120        final GuardedInvocation link = wrappedReceiver.lookup(desc, request);
121        if (link != null) {
122            MethodHandle method = link.getInvocation();
123            final Class<?> receiverType = method.type().parameterType(0);
124            if (receiverType != Object.class) {
125                final MethodType wrapType = wrapFilter.type();
126                assert receiverType.isAssignableFrom(wrapType.returnType());
127                method = MH.filterArguments(method, 0, MH.asType(wrapFilter, wrapType.changeReturnType(receiverType)));
128            }
129            return new GuardedInvocation(method, guard, link.getSwitchPoints(), null);
130        }
131        return null;
132    }
133}
134