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