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 &lt;init&gt; 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 &lt;init&gt; 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 }