Lookup.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.lookup;
27
28import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
29import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
30
31import java.lang.invoke.MethodHandle;
32import java.lang.invoke.MethodHandles;
33import java.lang.invoke.MethodType;
34import jdk.nashorn.internal.runtime.JSType;
35import jdk.nashorn.internal.runtime.ScriptRuntime;
36
37/**
38 * MethodHandle Lookup management for Nashorn.
39 */
40public final class Lookup {
41
42    /**
43     * A global singleton that points to the {@link MethodHandleFunctionality}. This is basically
44     * a collection of wrappers to the standard methods in {@link MethodHandle}, {@link MethodHandles} and
45     * {@link java.lang.invoke.MethodHandles.Lookup}, but instrumentation and debugging purposes we need
46     * intercept points.
47     * <p>
48     * All method handle operations in Nashorn should go through this field, not directly to the classes
49     * in {@code java.lang.invoke}
50     */
51    public static final MethodHandleFunctionality MH = MethodHandleFactory.getFunctionality();
52
53    /** Method handle to the empty getter */
54    public static final MethodHandle EMPTY_GETTER = findOwnMH("emptyGetter", Object.class, Object.class);
55
56    /** Method handle to the empty setter */
57    public static final MethodHandle EMPTY_SETTER = findOwnMH("emptySetter", void.class, Object.class, Object.class);
58
59    /** Method handle to a getter that only throws type error */
60    public static final MethodHandle TYPE_ERROR_THROWER_GETTER = findOwnMH("typeErrorThrowerGetter", Object.class, Object.class);
61
62    /** Method handle to a setter that only throws type error */
63    public static final MethodHandle TYPE_ERROR_THROWER_SETTER = findOwnMH("typeErrorThrowerSetter", void.class, Object.class, Object.class);
64
65    /** Method handle to the most generic of getters, the one that returns an Object */
66    public static final MethodType GET_OBJECT_TYPE = MH.type(Object.class, Object.class);
67
68    /** Method handle to the most generic of setters, the one that takes an Object */
69    public static final MethodType SET_OBJECT_TYPE = MH.type(void.class, Object.class, Object.class);
70
71    /** Method handle to the primitive getters, the one that returns an long/int/double */
72    public static final MethodType GET_PRIMITIVE_TYPE = MH.type(long.class, Object.class);
73
74    /** Method handle to the primitive getters, the one that returns an long/int/double */
75    public static final MethodType SET_PRIMITIVE_TYPE = MH.type(void.class, Object.class, long.class);
76
77    private Lookup() {
78    }
79
80    /**
81     * Empty getter implementation. Nop
82     * @param self self reference
83     * @return undefined
84     */
85    public static Object emptyGetter(final Object self) {
86        return UNDEFINED;
87    }
88
89    /**
90     * Empty setter implementation. Nop
91     * @param self  self reference
92     * @param value value (ignored)
93     */
94    public static void emptySetter(final Object self, final Object value) {
95        // do nothing!!
96    }
97
98    /**
99     * Return a method handle to the empty getter, with a different
100     * return type value. It will still be undefined cast to whatever
101     * return value property was specified
102     *
103     * @param type return value type
104     *
105     * @return undefined as return value type
106     */
107    public static MethodHandle emptyGetter(final Class<?> type) {
108        return filterReturnType(EMPTY_GETTER, type);
109    }
110
111    /**
112     * Getter function that always throws type error
113     *
114     * @param self  self reference
115     * @return undefined (but throws error before return point)
116     */
117    public static Object typeErrorThrowerGetter(final Object self) {
118        throw typeError("strict.getter.setter.poison", ScriptRuntime.safeToString(self));
119    }
120
121    /**
122     * Getter function that always throws type error
123     *
124     * @param self  self reference
125     * @param value (ignored)
126     */
127    public static void typeErrorThrowerSetter(final Object self, final Object value) {
128        throw typeError("strict.getter.setter.poison", ScriptRuntime.safeToString(self));
129    }
130
131    /**
132     * This method filters primitive argument types using JavaScript semantics. For example,
133     * an (int) cast of a double in Java land is not the same thing as invoking toInt32 on it.
134     * If you are returning values to JavaScript that have to be of a specific type, this is
135     * the correct return value filter to use, as the explicitCastArguments just uses the
136     * Java boxing equivalents
137     *
138     * @param mh   method handle for which to filter argument value
139     * @param n    argument index
140     * @param from old argument type, the new one is given by the sent method handle
141     * @return method handle for appropriate argument type conversion
142     */
143    public static MethodHandle filterArgumentType(final MethodHandle mh, final int n, final Class<?> from) {
144        final Class<?> to = mh.type().parameterType(n);
145
146        if (from == int.class) {
147            //fallthru
148        } else if (from == long.class) {
149            if (to == int.class) {
150                return MH.filterArguments(mh, n, JSType.TO_INT32_L.methodHandle());
151            }
152            //fallthru
153        } else if (from == double.class) {
154            if (to == int.class) {
155                return MH.filterArguments(mh, n, JSType.TO_INT32_D.methodHandle());
156            } else if (to == long.class) {
157                return MH.filterArguments(mh, n, JSType.TO_UINT32_D.methodHandle());
158            }
159            //fallthru
160        } else if (!from.isPrimitive()) {
161            if (to == int.class) {
162                return MH.filterArguments(mh, n, JSType.TO_INT32.methodHandle());
163            } else if (to == long.class) {
164                return MH.filterArguments(mh, n, JSType.TO_UINT32.methodHandle());
165            } else if (to == double.class) {
166                return MH.filterArguments(mh, n, JSType.TO_NUMBER.methodHandle());
167            } else if (!to.isPrimitive()) {
168                return mh;
169            }
170
171            assert false : "unsupported Lookup.filterReturnType type " + from + " -> " + to;
172        }
173
174        //use a standard cast - we don't need to check JavaScript special cases
175        return MH.explicitCastArguments(mh, mh.type().changeParameterType(n, from));
176    }
177
178    /**
179     * This method filters primitive return types using JavaScript semantics. For example,
180     * an (int) cast of a double in Java land is not the same thing as invoking toInt32 on it.
181     * If you are returning values to JavaScript that have to be of a specific type, this is
182     * the correct return value filter to use, as the explicitCastArguments just uses the
183     * Java boxing equivalents
184     *
185     * @param mh   method handle for which to filter return value
186     * @param type new return type
187     * @return method handle for appropriate return type conversion
188     */
189    public static MethodHandle filterReturnType(final MethodHandle mh, final Class<?> type) {
190        final Class<?> retType = mh.type().returnType();
191
192        if (retType == int.class) {
193            //fallthru
194        } else if (retType == long.class) {
195            if (type == int.class) {
196                return MH.filterReturnValue(mh, JSType.TO_INT32_L.methodHandle());
197            }
198            //fallthru
199        } else if (retType == double.class) {
200            if (type == int.class) {
201                return MH.filterReturnValue(mh, JSType.TO_INT32_D.methodHandle());
202            } else if (type == long.class) {
203                return MH.filterReturnValue(mh, JSType.TO_UINT32_D.methodHandle());
204            }
205            //fallthru
206        } else if (!retType.isPrimitive()) {
207            if (type == int.class) {
208                return MH.filterReturnValue(mh, JSType.TO_INT32.methodHandle());
209            } else if (type == long.class) {
210                return MH.filterReturnValue(mh, JSType.TO_UINT32.methodHandle());
211            } else if (type == double.class) {
212                return MH.filterReturnValue(mh, JSType.TO_NUMBER.methodHandle());
213            } else if (!type.isPrimitive()) {
214                return mh;
215            }
216
217            assert false : "unsupported Lookup.filterReturnType type " + retType + " -> " + type;
218        }
219
220        //use a standard cast - we don't need to check JavaScript special cases
221        return MH.explicitCastArguments(mh, mh.type().changeReturnType(type));
222    }
223
224    private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
225        return MH.findStatic(MethodHandles.lookup(), Lookup.class, name, MH.type(rtype, types));
226    }
227
228}
229