JavaAdapterServices.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.internal.org.objectweb.asm.Opcodes.ACC_FINAL; 29import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PUBLIC; 30import static jdk.internal.org.objectweb.asm.Opcodes.ACC_STATIC; 31import static jdk.internal.org.objectweb.asm.Opcodes.ACC_SUPER; 32import static jdk.internal.org.objectweb.asm.Opcodes.ALOAD; 33import static jdk.internal.org.objectweb.asm.Opcodes.RETURN; 34import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; 35 36import java.lang.invoke.MethodHandle; 37import java.lang.invoke.MethodHandles; 38import java.lang.invoke.MethodType; 39import java.security.AccessController; 40import java.security.CodeSigner; 41import java.security.CodeSource; 42import java.security.Permissions; 43import java.security.PrivilegedAction; 44import java.security.ProtectionDomain; 45import java.security.SecureClassLoader; 46import jdk.internal.org.objectweb.asm.ClassWriter; 47import jdk.internal.org.objectweb.asm.Opcodes; 48import jdk.internal.org.objectweb.asm.Type; 49import jdk.internal.org.objectweb.asm.commons.InstructionAdapter; 50import jdk.nashorn.internal.runtime.Context; 51import jdk.nashorn.internal.runtime.ScriptFunction; 52import jdk.nashorn.internal.runtime.ScriptObject; 53import jdk.nashorn.internal.runtime.ScriptRuntime; 54import jdk.nashorn.internal.runtime.Undefined; 55 56/** 57 * Provides static utility services to generated Java adapter classes. 58 */ 59public final class JavaAdapterServices { 60 private static final ThreadLocal<ScriptObject> classOverrides = new ThreadLocal<>(); 61 private static final MethodHandle NO_PERMISSIONS_INVOKER = createNoPermissionsInvoker(); 62 63 private JavaAdapterServices() { 64 } 65 66 /** 67 * Given a JS script function, binds it to null JS "this", and adapts its parameter types, return types, and arity 68 * to the specified type and arity. This method is public mainly for implementation reasons, so the adapter classes 69 * can invoke it from their constructors that take a ScriptFunction in its first argument to obtain the method 70 * handles for their abstract method implementations. 71 * @param fn the script function 72 * @param type the method type it has to conform to 73 * @return the appropriately adapted method handle for invoking the script function. 74 */ 75 public static MethodHandle getHandle(final ScriptFunction fn, final MethodType type) { 76 // JS "this" will be global object or undefined depending on if 'fn' is strict or not 77 return bindAndAdaptHandle(fn, fn.isStrict()? ScriptRuntime.UNDEFINED : Context.getGlobal(), type); 78 } 79 80 /** 81 * Given a JS script object, retrieves a function from it by name, binds it to the script object as its "this", and 82 * adapts its parameter types, return types, and arity to the specified type and arity. This method is public mainly 83 * for implementation reasons, so the adapter classes can invoke it from their constructors that take a Object 84 * in its first argument to obtain the method handles for their method implementations. 85 * @param obj the script obj 86 * @param name the name of the property that contains the function 87 * @param type the method type it has to conform to 88 * @return the appropriately adapted method handle for invoking the script function, or null if the value of the 89 * property is either null or undefined, or "toString" was requested as the name, but the object doesn't directly 90 * define it but just inherits it through prototype. 91 */ 92 public static MethodHandle getHandle(final Object obj, final String name, final MethodType type) { 93 if (! (obj instanceof ScriptObject)) { 94 throw typeError("not.an.object", ScriptRuntime.safeToString(obj)); 95 } 96 97 final ScriptObject sobj = (ScriptObject)obj; 98 // Since every JS Object has a toString, we only override "String toString()" it if it's explicitly specified 99 if ("toString".equals(name) && !sobj.hasOwnProperty("toString")) { 100 return null; 101 } 102 103 final Object fnObj = sobj.get(name); 104 if (fnObj instanceof ScriptFunction) { 105 return bindAndAdaptHandle((ScriptFunction)fnObj, sobj, type); 106 } else if(fnObj == null || fnObj instanceof Undefined) { 107 return null; 108 } else { 109 throw typeError("not.a.function", name); 110 } 111 } 112 113 /** 114 * Returns a thread-local JS object used to define methods for the adapter class being initialized on the current 115 * thread. This method is public solely for implementation reasons, so the adapter classes can invoke it from their 116 * static initializers. 117 * @return the thread-local JS object used to define methods for the class being initialized. 118 */ 119 public static Object getClassOverrides() { 120 final Object overrides = classOverrides.get(); 121 assert overrides != null; 122 return overrides; 123 } 124 125 /** 126 * Takes a method handle and an argument to it, and invokes the method handle passing it the argument. Basically 127 * equivalent to {@code method.invokeExact(arg)}, except that the method handle will be invoked in a protection 128 * domain with absolutely no permissions. 129 * @param method the method handle to invoke. The handle must have the exact type of {@code void(Object)}. 130 * @param arg the argument to pass to the handle. 131 * @throws Throwable if anything goes wrong. 132 */ 133 public static void invokeNoPermissions(final MethodHandle method, final Object arg) throws Throwable { 134 NO_PERMISSIONS_INVOKER.invokeExact(method, arg); 135 } 136 137 /** 138 * Set the current global scope 139 * @param global the global scope 140 */ 141 public static void setGlobal(final Object global) { 142 Context.setGlobal((ScriptObject)global); 143 } 144 145 /** 146 * Get the current global scope 147 * @return the current global scope 148 */ 149 public static Object getGlobal() { 150 return Context.getGlobal(); 151 } 152 153 static void setClassOverrides(final ScriptObject overrides) { 154 classOverrides.set(overrides); 155 } 156 157 private static MethodHandle bindAndAdaptHandle(final ScriptFunction fn, final Object self, final MethodType type) { 158 return Bootstrap.getLinkerServices().asType(ScriptObject.pairArguments(fn.getBoundInvokeHandle(self), type, false), type); 159 } 160 161 private static MethodHandle createNoPermissionsInvoker() { 162 final String className = "NoPermissionsInvoker"; 163 164 final ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS); 165 cw.visit(Opcodes.V1_7, ACC_PUBLIC | ACC_SUPER | ACC_FINAL, className, null, "java/lang/Object", null); 166 final Type objectType = Type.getType(Object.class); 167 final Type methodHandleType = Type.getType(MethodHandle.class); 168 final InstructionAdapter mv = new InstructionAdapter(cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "invoke", 169 Type.getMethodDescriptor(Type.VOID_TYPE, methodHandleType, objectType), null, null)); 170 mv.visitCode(); 171 mv.visitVarInsn(ALOAD, 0); 172 mv.visitVarInsn(ALOAD, 1); 173 mv.invokevirtual(methodHandleType.getInternalName(), "invokeExact", Type.getMethodDescriptor( 174 Type.VOID_TYPE, objectType), false); 175 mv.visitInsn(RETURN); 176 mv.visitMaxs(0, 0); 177 mv.visitEnd(); 178 cw.visitEnd(); 179 final byte[] bytes = cw.toByteArray(); 180 181 final ClassLoader loader = AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() { 182 @Override 183 public ClassLoader run() { 184 return new SecureClassLoader(null) { 185 @Override 186 protected Class<?> findClass(final String name) throws ClassNotFoundException { 187 if(name.equals(className)) { 188 return defineClass(name, bytes, 0, bytes.length, new ProtectionDomain( 189 new CodeSource(null, (CodeSigner[])null), new Permissions())); 190 } 191 throw new ClassNotFoundException(name); 192 } 193 }; 194 } 195 }); 196 197 try { 198 return MethodHandles.lookup().findStatic(Class.forName(className, true, loader), "invoke", 199 MethodType.methodType(void.class, MethodHandle.class, Object.class)); 200 } catch(final ReflectiveOperationException e) { 201 throw new AssertionError(e.getMessage(), e); 202 } 203 } 204 205 /** 206 * Returns a method handle used to convert a return value from a delegate method (always Object) to the expected 207 * Java return type. 208 * @param returnType the return type 209 * @return the converter for the expected return type 210 */ 211 public static MethodHandle getObjectConverter(final Class<?> returnType) { 212 return Bootstrap.getLinkerServices().getTypeConverter(Object.class, returnType); 213 } 214 215 /** 216 * Invoked when returning Object from an adapted method to filter out internal Nashorn objects that must not be seen 217 * by the callers. Currently only transforms {@code ConsString} into {@code String}. 218 * @param obj the return value 219 * @return the filtered return value. 220 */ 221 public static Object exportReturnValue(final Object obj) { 222 return NashornBeansLinker.exportArgument(obj); 223 } 224 225 /** 226 * Invoked to convert a return value of a delegate function to primitive char. There's no suitable conversion in 227 * {@code JSType}, so we provide our own to adapters. 228 * @param obj the return value. 229 * @return the character value of the return value 230 */ 231 public static char toCharPrimitive(final Object obj) { 232 return JavaArgumentConverters.toCharPrimitive(obj); 233 } 234 235 /** 236 * Invoked to convert a return value of a delegate function to String. It is similar to 237 * {@code JSType.toString(Object)}, except it doesn't handle StaticClass specially, and it returns null for null 238 * input instead of the string "null". 239 * @param obj the return value. 240 * @return the String value of the return value 241 */ 242 public static String toString(final Object obj) { 243 return JavaArgumentConverters.toString(obj); 244 } 245} 246