JavaAdapterServices.java revision 1819:4f90f5ae2b4a
118579Sfenner/* 2100787Sfenner * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. 318579Sfenner * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 418579Sfenner * 518579Sfenner * This code is free software; you can redistribute it and/or modify it 618579Sfenner * under the terms of the GNU General Public License version 2 only, as 718579Sfenner * published by the Free Software Foundation. Oracle designates this 818579Sfenner * particular file as subject to the "Classpath" exception as provided 918579Sfenner * by Oracle in the LICENSE file that accompanied this code. 1018579Sfenner * 1118579Sfenner * This code is distributed in the hope that it will be useful, but WITHOUT 1218579Sfenner * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 1318579Sfenner * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 1418579Sfenner * version 2 for more details (a copy is included in the LICENSE file that 1518579Sfenner * accompanied this code). 1618579Sfenner * 1718579Sfenner * You should have received a copy of the GNU General Public License version 1818579Sfenner * 2 along with this work; if not, write to the Free Software Foundation, 1918579Sfenner * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 2018579Sfenner * 2118579Sfenner * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 2218579Sfenner * or visit www.oracle.com if you need additional information or have any 2318579Sfenner * questions. 24100787Sfenner */ 2518579Sfenner 2658835Sshinpackage jdk.nashorn.internal.runtime.linker; 2718579Sfenner 28100787Sfennerimport static jdk.internal.org.objectweb.asm.Opcodes.ACC_FINAL; 2918579Sfennerimport static jdk.internal.org.objectweb.asm.Opcodes.ACC_PUBLIC; 3058835Sshinimport static jdk.internal.org.objectweb.asm.Opcodes.ACC_STATIC; 3158835Sshinimport static jdk.internal.org.objectweb.asm.Opcodes.ACC_SUPER; 3258835Sshinimport static jdk.internal.org.objectweb.asm.Opcodes.ALOAD; 3318579Sfennerimport static jdk.internal.org.objectweb.asm.Opcodes.RETURN; 3418579Sfennerimport static jdk.nashorn.internal.runtime.ECMAErrors.typeError; 3518579Sfenner 3618579Sfennerimport java.lang.invoke.CallSite; 3718579Sfennerimport java.lang.invoke.ConstantCallSite; 3818579Sfennerimport java.lang.invoke.MethodHandle; 3918579Sfennerimport java.lang.invoke.MethodHandles; 4018579Sfennerimport java.lang.invoke.MethodHandles.Lookup; 4118579Sfennerimport java.lang.invoke.MethodType; 4218579Sfennerimport java.lang.reflect.Field; 4377816Sruimport java.security.AccessController; 4477816Sruimport java.security.CodeSigner; 4577816Sruimport java.security.CodeSource; 4618579Sfennerimport java.security.Permissions; 4718579Sfennerimport java.security.PrivilegedAction; 4818579Sfennerimport java.security.ProtectionDomain; 4918579Sfennerimport java.security.SecureClassLoader; 5018579Sfennerimport java.util.Objects; 5118579Sfennerimport jdk.internal.org.objectweb.asm.ClassWriter; 5218579Sfennerimport jdk.internal.org.objectweb.asm.Opcodes; 5318579Sfennerimport jdk.internal.org.objectweb.asm.Type; 5418579Sfennerimport jdk.internal.org.objectweb.asm.commons.InstructionAdapter; 5518579Sfennerimport jdk.nashorn.api.scripting.ScriptObjectMirror; 5618579Sfennerimport jdk.nashorn.internal.objects.Global; 5718579Sfennerimport jdk.nashorn.internal.runtime.Context; 5818579Sfennerimport jdk.nashorn.internal.runtime.ECMAException; 5918579Sfennerimport jdk.nashorn.internal.runtime.JSType; 6077816Sruimport jdk.nashorn.internal.runtime.ScriptFunction; 6118579Sfennerimport jdk.nashorn.internal.runtime.ScriptObject; 6218579Sfennerimport jdk.nashorn.internal.runtime.ScriptRuntime; 6318579Sfenner 6418579Sfenner/** 6518579Sfenner * Provides static utility services to generated Java adapter classes. 6618579Sfenner */ 6718579Sfennerpublic final class JavaAdapterServices { 6818579Sfenner private static final ThreadLocal<ScriptObject> classOverrides = new ThreadLocal<>(); 6918579Sfenner private static final MethodHandle NO_PERMISSIONS_INVOKER = createNoPermissionsInvoker(); 7018579Sfenner 7118579Sfenner private JavaAdapterServices() { 7218579Sfenner } 7318579Sfenner 7418579Sfenner /** 7518579Sfenner * Given a script function used as a delegate for a SAM adapter, figure out 7618579Sfenner * the right object to use as its "this" when called. 7718579Sfenner * @param delegate the delegate function 7818579Sfenner * @param global the current global of the adapter 7918579Sfenner * @return either the passed global, or UNDEFINED if the function is strict. 8077816Sru */ 8118579Sfenner public static Object getCallThis(final ScriptFunction delegate, final Object global) { 8218579Sfenner return delegate.isStrict() ? ScriptRuntime.UNDEFINED : global; 8318579Sfenner } 8418579Sfenner 8518579Sfenner /** 8618579Sfenner * Throws a "not.an.object" type error. Used when the delegate passed to the 8718579Sfenner * adapter constructor is not a script object. 8818579Sfenner * @param obj the object that is not a script object. 8918579Sfenner */ 9018579Sfenner public static void notAnObject(final Object obj) { 9118579Sfenner throw typeError("not.an.object", ScriptRuntime.safeToString(obj)); 9218579Sfenner } 9318579Sfenner 9418579Sfenner /** 9518579Sfenner * Checks if the passed object, which is supposed to be a callee retrieved 9618579Sfenner * through applying the GET_METHOD_PROPERTY operation on the delegate, is 9718579Sfenner * a ScriptFunction, or null or undefined. These are the only allowed values 9818579Sfenner * for adapter method implementations, so in case it is neither, it throws 9918579Sfenner * a type error. Note that this restriction is somewhat artificial; as the 10018579Sfenner * CALL dynamic operation could invoke any Nashorn callable. We are 10118579Sfenner * restricting adapters to actual ScriptFunction objects for now though. 10218579Sfenner * @param callee the callee to check 10318579Sfenner * @param name the name of the function 10418579Sfenner * @return the callee cast to a ScriptFunction, or null if it was null or undefined. 10518579Sfenner * @throws ECMAException representing a JS TypeError with "not.a.function" 10618579Sfenner * message if the passed callee is neither a script function, nor null, nor 10718579Sfenner * undefined. 10818579Sfenner */ 10918579Sfenner public static ScriptFunction checkFunction(final Object callee, final String name) { 11018579Sfenner if (callee instanceof ScriptFunction) { 11118579Sfenner return (ScriptFunction)callee; 11218579Sfenner } else if (JSType.nullOrUndefined(callee)) { 11318579Sfenner return null; 11418579Sfenner } 11518579Sfenner throw typeError("not.a.function.value", name, ScriptRuntime.safeToString(callee)); 11618579Sfenner } 11718579Sfenner 11818579Sfenner /** 11918579Sfenner * Returns a thread-local JS object used to define methods for the adapter class being initialized on the current 12018579Sfenner * thread. This method is public solely for implementation reasons, so the adapter classes can invoke it from their 12118579Sfenner * static initializers. 12218579Sfenner * @return the thread-local JS object used to define methods for the class being initialized. 12318579Sfenner */ 12418579Sfenner public static ScriptObject getClassOverrides() { 12518579Sfenner final ScriptObject overrides = classOverrides.get(); 12618579Sfenner assert overrides != null; 12718579Sfenner return overrides; 12818579Sfenner } 12918579Sfenner 13018579Sfenner /** 13118579Sfenner * Takes a method handle and an argument to it, and invokes the method handle passing it the argument. Basically 13218579Sfenner * equivalent to {@code method.invokeExact(arg)}, except that the method handle will be invoked in a protection 13318579Sfenner * domain with absolutely no permissions. 13418579Sfenner * @param method the method handle to invoke. The handle must have the exact type of {@code void(Object)}. 13518579Sfenner * @param arg the argument to pass to the handle. 13618579Sfenner * @throws Throwable if anything goes wrong. 13718579Sfenner */ 13818579Sfenner public static void invokeNoPermissions(final MethodHandle method, final Object arg) throws Throwable { 13918579Sfenner NO_PERMISSIONS_INVOKER.invokeExact(method, arg); 14018579Sfenner } 14118579Sfenner 14218579Sfenner /** 14318579Sfenner * Set the current global scope to that of the adapter global 14418579Sfenner * @param adapterGlobal the adapter's global scope 14518579Sfenner * @return a Runnable that when invoked restores the previous global 14618579Sfenner */ 14718579Sfenner public static Runnable setGlobal(final ScriptObject adapterGlobal) { 14818579Sfenner final Global currentGlobal = Context.getGlobal(); 14918579Sfenner if (adapterGlobal != currentGlobal) { 15018579Sfenner Context.setGlobal(adapterGlobal); 15118579Sfenner return ()->Context.setGlobal(currentGlobal); 15218579Sfenner } 15318579Sfenner return ()->{}; 15418579Sfenner } 15518579Sfenner 15618579Sfenner /** 15718579Sfenner * Get the current non-null global scope 15818579Sfenner * @return the current global scope 15918579Sfenner * @throws NullPointerException if the current global scope is null. 16018579Sfenner */ 16118579Sfenner public static ScriptObject getNonNullGlobal() { 16218579Sfenner return Objects.requireNonNull(Context.getGlobal(), "Current global is null"); 16318579Sfenner } 16418579Sfenner 16518579Sfenner /** 16618579Sfenner * Returns true if the object has its own toString function. Used 16718579Sfenner * when implementing toString for adapters. Since every JS Object has a 16818579Sfenner * toString function, we only override "String toString()" in adapters if 16918579Sfenner * it is explicitly specified and not inherited from a prototype. 17018579Sfenner * @param sobj the object 17118579Sfenner * @return true if the object has its own toString function. 17218579Sfenner */ 17318579Sfenner public static boolean hasOwnToString(final ScriptObject sobj) { 17418579Sfenner // NOTE: we could just use ScriptObject.hasOwnProperty("toString"), but 17518579Sfenner // its logic is more complex and this is what it boils down to with a 17618579Sfenner // fixed "toString" argument. 17718579Sfenner return sobj.getMap().findProperty("toString") != null; 17818579Sfenner } 17918579Sfenner 18018579Sfenner /** 18118579Sfenner * Returns the ScriptObject or Global field value from a ScriptObjectMirror using reflection. 18218579Sfenner * 18318579Sfenner * @param mirror the mirror object 18418579Sfenner * @param getGlobal true if we want the global object, false to return the script object 18518579Sfenner * @return the script object or global object 18618579Sfenner */ 18718579Sfenner public static ScriptObject unwrapMirror(final Object mirror, final boolean getGlobal) { 18818579Sfenner assert mirror instanceof ScriptObjectMirror; 18918579Sfenner try { 19018579Sfenner final Field field = getGlobal ? MirrorFieldHolder.GLOBAL_FIELD : MirrorFieldHolder.SOBJ_FIELD; 19118579Sfenner return (ScriptObject) field.get(mirror); 19218579Sfenner } catch (final IllegalAccessException x) { 19318579Sfenner throw new RuntimeException(x); 19418579Sfenner } 19518579Sfenner } 19618579Sfenner 19718579Sfenner /** 19818579Sfenner * Delegate to {@link Bootstrap#bootstrap(Lookup, String, MethodType, int)}. 19918579Sfenner * @param lookup MethodHandle lookup. 20018579Sfenner * @param opDesc Dynalink dynamic operation descriptor. 20118579Sfenner * @param type Method type. 20218579Sfenner * @param flags flags for call type, trace/profile etc. 20318579Sfenner * @return CallSite with MethodHandle to appropriate method or null if not found. 20418579Sfenner */ 20518579Sfenner public static CallSite bootstrap(final Lookup lookup, final String opDesc, final MethodType type, final int flags) { 20618579Sfenner return Bootstrap.bootstrap(lookup, opDesc, type, flags); 20718579Sfenner } 20818579Sfenner 20918579Sfenner static void setClassOverrides(final ScriptObject overrides) { 21018579Sfenner classOverrides.set(overrides); 21118579Sfenner } 21277816Sru 21377816Sru private static MethodHandle createNoPermissionsInvoker() { 21477816Sru final String className = "NoPermissionsInvoker"; 21518579Sfenner 21618579Sfenner final ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS); 21718579Sfenner cw.visit(Opcodes.V1_7, ACC_PUBLIC | ACC_SUPER | ACC_FINAL, className, null, "java/lang/Object", null); 21818579Sfenner final Type objectType = Type.getType(Object.class); 21918579Sfenner final Type methodHandleType = Type.getType(MethodHandle.class); 22018579Sfenner final InstructionAdapter mv = new InstructionAdapter(cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "invoke", 22118579Sfenner Type.getMethodDescriptor(Type.VOID_TYPE, methodHandleType, objectType), null, null)); 222283808Stuexen mv.visitCode(); 22318579Sfenner mv.visitVarInsn(ALOAD, 0); 22446542Sarchie mv.visitVarInsn(ALOAD, 1); 225100789Sfenner mv.invokevirtual(methodHandleType.getInternalName(), "invokeExact", Type.getMethodDescriptor( 22618579Sfenner Type.VOID_TYPE, objectType), false); 22718579Sfenner mv.visitInsn(RETURN); 22818579Sfenner mv.visitMaxs(0, 0); 22958804Sshin mv.visitEnd(); 23058804Sshin cw.visitEnd(); 231171135Sgnn final byte[] bytes = cw.toByteArray(); 23258804Sshin 23358804Sshin final ClassLoader loader = AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() { 23418579Sfenner @Override 235100787Sfenner public ClassLoader run() { 23618579Sfenner return new SecureClassLoader(null) { 237100787Sfenner @Override 23818579Sfenner protected Class<?> findClass(final String name) throws ClassNotFoundException { 23918579Sfenner if(name.equals(className)) { 24018579Sfenner return defineClass(name, bytes, 0, bytes.length, new ProtectionDomain( 24118579Sfenner new CodeSource(null, (CodeSigner[])null), new Permissions())); 24218579Sfenner } 24318579Sfenner throw new ClassNotFoundException(name); 24418579Sfenner } 24518579Sfenner }; 24618579Sfenner } 24718579Sfenner }); 248100787Sfenner 249100787Sfenner try { 250100787Sfenner return MethodHandles.publicLookup().findStatic(Class.forName(className, true, loader), "invoke", 251100787Sfenner MethodType.methodType(void.class, MethodHandle.class, Object.class)); 252100787Sfenner } catch(final ReflectiveOperationException e) { 253100787Sfenner throw new AssertionError(e.getMessage(), e); 254100787Sfenner } 255100787Sfenner } 256100787Sfenner 257100787Sfenner /** 258100787Sfenner * Invoked when returning Object from an adapted method to filter out internal Nashorn objects that must not be seen 259100787Sfenner * by the callers. Currently only transforms {@code ConsString} into {@code String} and transforms {@code ScriptObject} into {@code ScriptObjectMirror}. 260100787Sfenner * @param obj the return value 261176428Srpaulo * @return the filtered return value. 262100787Sfenner */ 263100787Sfenner public static Object exportReturnValue(final Object obj) { 26418579Sfenner return NashornBeansLinker.exportArgument(obj, true); 26518579Sfenner } 26618579Sfenner 26718579Sfenner /** 26818579Sfenner * Invoked to convert a return value of a delegate function to primitive char. There's no suitable conversion in 26918579Sfenner * {@code JSType}, so we provide our own to adapters. 27018579Sfenner * @param obj the return value. 27118579Sfenner * @return the character value of the return value 27218579Sfenner */ 27318579Sfenner public static char toCharPrimitive(final Object obj) { 27446542Sarchie return JavaArgumentConverters.toCharPrimitive(obj); 27546542Sarchie } 27646542Sarchie 27746542Sarchie /** 27846542Sarchie * Returns a new {@link RuntimeException} wrapping the passed throwable. 27946542Sarchie * Makes generated bytecode smaller by doing an INVOKESTATIC to this method 28046542Sarchie * rather than the NEW/DUP_X1/SWAP/INVOKESPECIAL <init> sequence. 28146542Sarchie * @param t the original throwable to wrap 28246542Sarchie * @return a newly created runtime exception wrapping the passed throwable. 28346542Sarchie */ 28446542Sarchie public static RuntimeException wrapThrowable(final Throwable t) { 28546542Sarchie return new RuntimeException(t); 28646542Sarchie } 28746542Sarchie 288100787Sfenner /** 289100787Sfenner * Creates and returns a new {@link UnsupportedOperationException}. Makes 290100787Sfenner * generated bytecode smaller by doing INVOKESTATIC to this method rather 291100787Sfenner * than the NEW/DUP/INVOKESPECIAL <init> sequence. 292100787Sfenner * @return a newly created {@link UnsupportedOperationException}. 293100787Sfenner */ 294100787Sfenner public static UnsupportedOperationException unsupported() { 29518579Sfenner return new UnsupportedOperationException(); 29618579Sfenner } 29718579Sfenner 29818579Sfenner /** 29918579Sfenner * A bootstrap method used to collect invocation arguments into an Object array. 30018579Sfenner * for variable arity invocation. 30118579Sfenner * @param lookup the adapter's lookup (not used). 302100787Sfenner * @param name the call site name (not used). 303100787Sfenner * @param type the method type 304100787Sfenner * @return a method that takes the input parameters and packs them into a 305100787Sfenner * newly allocated Object array. 306100787Sfenner */ 30746542Sarchie public static CallSite createArrayBootstrap(final MethodHandles.Lookup lookup, final String name, final MethodType type) { 308100787Sfenner return new ConstantCallSite( 30946542Sarchie MethodHandles.identity(Object[].class) 31018579Sfenner .asCollector(Object[].class, type.parameterCount()) 31118579Sfenner .asType(type)); 31246542Sarchie } 313100787Sfenner 31418579Sfenner // Initialization on demand holder for accessible ScriptObjectMirror fields 315163387Sdwmalone private static class MirrorFieldHolder { 316163387Sdwmalone 317163387Sdwmalone private static final Field SOBJ_FIELD = getMirrorField("sobj"); 31818579Sfenner private static final Field GLOBAL_FIELD = getMirrorField("global"); 31918579Sfenner 32018579Sfenner private static Field getMirrorField(final String fieldName) { 32118579Sfenner try { 32218579Sfenner final Field field = ScriptObjectMirror.class.getDeclaredField(fieldName); 32318579Sfenner AccessController.doPrivileged((PrivilegedAction<Void>) () -> { 32418579Sfenner field.setAccessible(true); 325100787Sfenner return null; 32618579Sfenner }); 32746542Sarchie return field; 328100787Sfenner } catch (final NoSuchFieldException e) { 32918579Sfenner throw new RuntimeException(e); 330100787Sfenner } 331100787Sfenner } 33218579Sfenner } 33318579Sfenner} 33418579Sfenner