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.runtime.linker; 27 28 import static jdk.internal.org.objectweb.asm.Opcodes.ACC_FINAL; 29 import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PUBLIC; 30 import static jdk.internal.org.objectweb.asm.Opcodes.ACC_STATIC; 31 import static jdk.internal.org.objectweb.asm.Opcodes.ACC_SUPER; 32 import static jdk.internal.org.objectweb.asm.Opcodes.ALOAD; 33 import static jdk.internal.org.objectweb.asm.Opcodes.RETURN; 34 import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; 35 36 import java.lang.invoke.CallSite; 37 import java.lang.invoke.ConstantCallSite; 38 import java.lang.invoke.MethodHandle; 39 import java.lang.invoke.MethodHandles; 40 import java.lang.invoke.MethodHandles.Lookup; 41 import java.lang.invoke.MethodType; 42 import java.security.AccessController; 43 import java.security.CodeSigner; 44 import java.security.CodeSource; 45 import java.security.Permissions; 46 import java.security.PrivilegedAction; 47 import java.security.ProtectionDomain; 48 import java.security.SecureClassLoader; 49 import java.util.Objects; 50 import jdk.internal.org.objectweb.asm.ClassWriter; 51 import jdk.internal.org.objectweb.asm.Opcodes; 52 import jdk.internal.org.objectweb.asm.Type; 53 import jdk.internal.org.objectweb.asm.commons.InstructionAdapter; 54 import jdk.nashorn.internal.objects.Global; 55 import jdk.nashorn.internal.runtime.Context; 56 import jdk.nashorn.internal.runtime.ECMAException; 57 import jdk.nashorn.internal.runtime.JSType; 58 import jdk.nashorn.internal.runtime.ScriptFunction; 59 import jdk.nashorn.internal.runtime.ScriptObject; 60 import jdk.nashorn.internal.runtime.ScriptRuntime; 61 62 /** 63 * Provides static utility services to generated Java adapter classes. 64 */ 65 public final class JavaAdapterServices { 66 private static final ThreadLocal<ScriptObject> classOverrides = new ThreadLocal<>(); 67 private static final MethodHandle NO_PERMISSIONS_INVOKER = createNoPermissionsInvoker(); 68 69 private JavaAdapterServices() { 70 } 71 72 /** 73 * Given a script function used as a delegate for a SAM adapter, figure out 74 * the right object to use as its "this" when called. 75 * @param delegate the delegate function 76 * @param global the current global of the adapter 77 * @return either the passed global, or UNDEFINED if the function is strict. 78 */ 79 public static Object getCallThis(final ScriptFunction delegate, final Object global) { 80 return delegate.isStrict() ? ScriptRuntime.UNDEFINED : global; 81 } 82 83 /** 84 * Throws a "not.an.object" type error. Used when the delegate passed to the 85 * adapter constructor is not a script object. 86 * @param obj the object that is not a script object. 87 */ 88 public static void notAnObject(final Object obj) { 89 throw typeError("not.an.object", ScriptRuntime.safeToString(obj)); 90 } 91 92 /** 93 * Checks if the passed object, which is supposed to be a callee retrieved 94 * through applying the GET_METHOD_PROPERTY operation on the delegate, is 95 * a ScriptFunction, or null or undefined. These are the only allowed values 96 * for adapter method implementations, so in case it is neither, it throws 97 * a type error. Note that this restriction is somewhat artificial; as the 98 * CALL dynamic operation could invoke any Nashorn callable. We are 99 * restricting adapters to actual ScriptFunction objects for now though. 100 * @param callee the callee to check 101 * @param name the name of the function 102 * @return the callee cast to a ScriptFunction, or null if it was null or undefined. 103 * @throws ECMAException representing a JS TypeError with "not.a.function" 104 * message if the passed callee is neither a script function, nor null, nor 105 * undefined. 106 */ 107 public static ScriptFunction checkFunction(final Object callee, final String name) { 108 if (callee instanceof ScriptFunction) { 109 return (ScriptFunction)callee; 110 } else if (JSType.nullOrUndefined(callee)) { 111 return null; 112 } 113 throw typeError("not.a.function.value", name, ScriptRuntime.safeToString(callee)); 114 } 115 116 /** 117 * Returns a thread-local JS object used to define methods for the adapter class being initialized on the current 118 * thread. This method is public solely for implementation reasons, so the adapter classes can invoke it from their 119 * static initializers. 120 * @return the thread-local JS object used to define methods for the class being initialized. 121 */ 122 public static ScriptObject getClassOverrides() { 123 final ScriptObject overrides = classOverrides.get(); 124 assert overrides != null; 125 return overrides; 126 } 127 128 /** 129 * Takes a method handle and an argument to it, and invokes the method handle passing it the argument. Basically 130 * equivalent to {@code method.invokeExact(arg)}, except that the method handle will be invoked in a protection 131 * domain with absolutely no permissions. 132 * @param method the method handle to invoke. The handle must have the exact type of {@code void(Object)}. 133 * @param arg the argument to pass to the handle. 134 * @throws Throwable if anything goes wrong. 135 */ 136 public static void invokeNoPermissions(final MethodHandle method, final Object arg) throws Throwable { 137 NO_PERMISSIONS_INVOKER.invokeExact(method, arg); 138 } 139 140 /** 141 * Set the current global scope to that of the adapter global 142 * @param adapterGlobal the adapter's global scope 143 * @return a Runnable that when invoked restores the previous global 144 */ 145 public static Runnable setGlobal(final ScriptObject adapterGlobal) { 146 final Global currentGlobal = Context.getGlobal(); 147 if (adapterGlobal != currentGlobal) { 148 Context.setGlobal(adapterGlobal); 149 return ()->Context.setGlobal(currentGlobal); 150 } 151 return ()->{}; 152 } 153 154 /** 155 * Get the current non-null global scope 156 * @return the current global scope 157 * @throws NullPointerException if the current global scope is null. 158 */ 159 public static ScriptObject getNonNullGlobal() { 160 return Objects.requireNonNull(Context.getGlobal(), "Current global is null"); 161 } 162 163 /** 164 * Returns true if the object has its own toString function. Used 165 * when implementing toString for adapters. Since every JS Object has a 166 * toString function, we only override "String toString()" in adapters if 167 * it is explicitly specified and not inherited from a prototype. 168 * @param sobj the object 169 * @return true if the object has its own toString function. 170 */ 171 public static boolean hasOwnToString(final ScriptObject sobj) { 172 // NOTE: we could just use ScriptObject.hasOwnProperty("toString"), but 173 // its logic is more complex and this is what it boils down to with a 174 // fixed "toString" argument. 175 return sobj.getMap().findProperty("toString") != null; 176 } 177 178 /** 179 * Delegate to {@link Bootstrap#bootstrap(Lookup, String, MethodType, int)}. 180 * @param lookup MethodHandle lookup. 181 * @param opDesc Dynalink dynamic operation descriptor. 182 * @param type Method type. 183 * @param flags flags for call type, trace/profile etc. 184 * @return CallSite with MethodHandle to appropriate method or null if not found. 185 */ 186 public static CallSite bootstrap(final Lookup lookup, final String opDesc, final MethodType type, final int flags) { 187 return Bootstrap.bootstrap(lookup, opDesc, type, flags); 188 } 189 190 static void setClassOverrides(final ScriptObject overrides) { 191 classOverrides.set(overrides); 192 } 193 194 private static MethodHandle createNoPermissionsInvoker() { 195 final String className = "NoPermissionsInvoker"; 196 197 final ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS); 198 cw.visit(Opcodes.V1_7, ACC_PUBLIC | ACC_SUPER | ACC_FINAL, className, null, "java/lang/Object", null); 199 final Type objectType = Type.getType(Object.class); 200 final Type methodHandleType = Type.getType(MethodHandle.class); 201 final InstructionAdapter mv = new InstructionAdapter(cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "invoke", 202 Type.getMethodDescriptor(Type.VOID_TYPE, methodHandleType, objectType), null, null)); 203 mv.visitCode(); 204 mv.visitVarInsn(ALOAD, 0); 205 mv.visitVarInsn(ALOAD, 1); 206 mv.invokevirtual(methodHandleType.getInternalName(), "invokeExact", Type.getMethodDescriptor( 207 Type.VOID_TYPE, objectType), false); 208 mv.visitInsn(RETURN); 209 mv.visitMaxs(0, 0); 210 mv.visitEnd(); 211 cw.visitEnd(); 212 final byte[] bytes = cw.toByteArray(); 213 214 final ClassLoader loader = AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() { 215 @Override 216 public ClassLoader run() { 217 return new SecureClassLoader(null) { 218 @Override 219 protected Class<?> findClass(final String name) throws ClassNotFoundException { 220 if(name.equals(className)) { 221 return defineClass(name, bytes, 0, bytes.length, new ProtectionDomain( 222 new CodeSource(null, (CodeSigner[])null), new Permissions())); 223 } 224 throw new ClassNotFoundException(name); 225 } 226 }; 227 } 228 }); 229 230 try { 231 return MethodHandles.publicLookup().findStatic(Class.forName(className, true, loader), "invoke", 232 MethodType.methodType(void.class, MethodHandle.class, Object.class)); 233 } catch(final ReflectiveOperationException e) { 234 throw new AssertionError(e.getMessage(), e); 235 } 236 } 237 238 /** 239 * Invoked when returning Object from an adapted method to filter out internal Nashorn objects that must not be seen 240 * by the callers. Currently only transforms {@code ConsString} into {@code String} and transforms {@code ScriptObject} into {@code ScriptObjectMirror}. 241 * @param obj the return value 242 * @return the filtered return value. 243 */ 244 public static Object exportReturnValue(final Object obj) { 245 return NashornBeansLinker.exportArgument(obj, true); 246 } 247 248 /** 249 * Invoked to convert a return value of a delegate function to primitive char. There's no suitable conversion in 250 * {@code JSType}, so we provide our own to adapters. 251 * @param obj the return value. 252 * @return the character value of the return value 253 */ 254 public static char toCharPrimitive(final Object obj) { 255 return JavaArgumentConverters.toCharPrimitive(obj); 256 } 257 258 /** 259 * Returns a new {@link RuntimeException} wrapping the passed throwable. 260 * Makes generated bytecode smaller by doing an INVOKESTATIC to this method 261 * rather than the NEW/DUP_X1/SWAP/INVOKESPECIAL <init> sequence. 262 * @param t the original throwable to wrap 263 * @return a newly created runtime exception wrapping the passed throwable. 264 */ 265 public static RuntimeException wrapThrowable(final Throwable t) { 266 return new RuntimeException(t); 267 } 268 269 /** 270 * Creates and returns a new {@link UnsupportedOperationException}. Makes 271 * generated bytecode smaller by doing INVOKESTATIC to this method rather 272 * than the NEW/DUP/INVOKESPECIAL <init> sequence. 273 * @return a newly created {@link UnsupportedOperationException}. 274 */ 275 public static UnsupportedOperationException unsupported() { 276 return new UnsupportedOperationException(); 277 } 278 279 /** 280 * A bootstrap method used to collect invocation arguments into an Object array. 281 * for variable arity invocation. 282 * @param lookup the adapter's lookup (not used). 283 * @param name the call site name (not used). 284 * @param type the method type 285 * @return a method that takes the input parameters and packs them into a 286 * newly allocated Object array. 287 */ 288 public static CallSite createArrayBootstrap(final MethodHandles.Lookup lookup, final String name, final MethodType type) { 289 return new ConstantCallSite( 290 MethodHandles.identity(Object[].class) 291 .asCollector(Object[].class, type.parameterCount()) 292 .asType(type)); 293 } 294 }