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