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