1 /*
   2  * Copyright (c) 2010, 2015, 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.nashorn.internal.lookup.Lookup.MH;
  29 
  30 import java.lang.invoke.MethodHandle;
  31 import java.lang.invoke.MethodHandles;
  32 import java.lang.invoke.MethodHandles.Lookup;
  33 import java.lang.invoke.MethodType;
  34 import java.lang.reflect.Modifier;
  35 import java.security.AccessControlContext;
  36 import java.security.AccessController;
  37 import java.security.CodeSigner;
  38 import java.security.CodeSource;
  39 import java.security.Permissions;
  40 import java.security.PrivilegedAction;
  41 import java.security.ProtectionDomain;
  42 import java.util.ArrayList;
  43 import java.util.Arrays;
  44 import java.util.Collections;
  45 import java.util.HashMap;
  46 import java.util.List;
  47 import java.util.Map;
  48 import java.util.concurrent.ConcurrentHashMap;
  49 import jdk.dynalink.CallSiteDescriptor;
  50 import jdk.dynalink.StandardOperation;
  51 import jdk.dynalink.beans.StaticClass;
  52 import jdk.dynalink.linker.support.SimpleLinkRequest;
  53 import jdk.nashorn.internal.runtime.Context;
  54 import jdk.nashorn.internal.runtime.ECMAException;
  55 import jdk.nashorn.internal.runtime.ScriptFunction;
  56 import jdk.nashorn.internal.runtime.ScriptObject;
  57 import jdk.nashorn.internal.runtime.linker.AdaptationResult.Outcome;
  58 
  59 /**
  60  * A factory class that generates adapter classes. Adapter classes allow
  61  * implementation of Java interfaces and extending of Java classes from
  62  * JavaScript. For every combination of a superclass to extend and interfaces to
  63  * implement (collectively: "original types"), exactly one adapter class is
  64  * generated that extends the specified superclass and implements the specified
  65  * interfaces. (But see the discussion of class-based overrides for exceptions.)
  66  * <p>
  67  * The adapter class is generated in a new secure class loader that inherits
  68  * Nashorn's protection domain, and has either one of the original types' class
  69  * loader or the Nashorn's class loader as its parent - the parent class loader
  70  * is chosen so that all the original types and the Nashorn core classes are
  71  * visible from it (as the adapter will have constant pool references to
  72  * ScriptObject and ScriptFunction classes). In case none of the candidate class
  73  * loaders has visibility of all the required types, an error is thrown. The
  74  * class uses {@link JavaAdapterBytecodeGenerator} to generate the adapter class
  75  * itself; see its documentation for details about the generated class.
  76  * <p>
  77  * You normally don't use this class directly, but rather either create adapters
  78  * from script using {@link jdk.nashorn.internal.objects.NativeJava#extend(Object, Object...)},
  79  * using the {@code new} operator on abstract classes and interfaces (see
  80  * {@link jdk.nashorn.internal.objects.NativeJava#type(Object, Object)}), or
  81  * implicitly when passing script functions to Java methods expecting SAM types.
  82  */
  83 
  84 @SuppressWarnings("javadoc")
  85 public final class JavaAdapterFactory {
  86     private static final ProtectionDomain MINIMAL_PERMISSION_DOMAIN = createMinimalPermissionDomain();
  87 
  88     // context with permissions needs for AdapterInfo creation
  89     private static final AccessControlContext CREATE_ADAPTER_INFO_ACC_CTXT =
  90         ClassAndLoader.createPermAccCtxt("createClassLoader", "getClassLoader",
  91             "accessDeclaredMembers", "accessClassInPackage.jdk.nashorn.internal.runtime");
  92 
  93     /**
  94      * A mapping from an original Class object to AdapterInfo representing the adapter for the class it represents.
  95      */
  96     private static final ClassValue<Map<List<Class<?>>, AdapterInfo>> ADAPTER_INFO_MAPS = new ClassValue<Map<List<Class<?>>, AdapterInfo>>() {
  97         @Override
  98         protected Map<List<Class<?>>, AdapterInfo> computeValue(final Class<?> type) {
  99             return new HashMap<>();
 100         }
 101     };
 102 
 103     /**
 104      * Returns an adapter class for the specified original types. The adapter
 105      * class extends/implements the original class/interfaces.
 106      *
 107      * @param types the original types. The caller must pass at least one Java
 108      *        type representing either a public interface or a non-final public
 109      *        class with at least one public or protected constructor. If more
 110      *        than one type is specified, at most one can be a class and the
 111      *        rest have to be interfaces. The class can be in any position in
 112      *        the array. Invoking the method twice with exactly the same types
 113      *        in the same order will return the same adapter class, any
 114      *        reordering of types or even addition or removal of redundant types
 115      *        (i.e., interfaces that other types in the list already
 116      *        implement/extend, or {@code java.lang.Object} in a list of types
 117      *        consisting purely of interfaces) will result in a different
 118      *        adapter class, even though those adapter classes are functionally
 119      *        identical; we deliberately don't want to incur the additional
 120      *        processing cost of canonicalizing type lists.
 121      * @param classOverrides a JavaScript object with functions serving as the
 122      *        class-level overrides and implementations. These overrides are
 123      *        defined for all instances of the class, and can be further
 124      *        overridden on a per-instance basis by passing additional objects
 125      *        in the constructor.
 126      * @param lookup the lookup object identifying the caller class. The
 127      *        generated adapter class will have the protection domain of the
 128      *        caller class iff the lookup object is full-strength, otherwise it
 129      *        will be completely unprivileged.
 130      *
 131      * @return an adapter class. See this class' documentation for details on
 132      *         the generated adapter class.
 133      *
 134      * @throws ECMAException with a TypeError if the adapter class can not be
 135      *         generated because the original class is final, non-public, or has
 136      *         no public or protected constructors.
 137      */
 138     public static StaticClass getAdapterClassFor(final Class<?>[] types, final ScriptObject classOverrides, final MethodHandles.Lookup lookup) {
 139         return getAdapterClassFor(types, classOverrides, getProtectionDomain(lookup));
 140     }
 141 
 142     private static StaticClass getAdapterClassFor(final Class<?>[] types, final ScriptObject classOverrides, final ProtectionDomain protectionDomain) {
 143         assert types != null && types.length > 0;
 144         final SecurityManager sm = System.getSecurityManager();
 145         if (sm != null) {
 146             for (final Class<?> type : types) {
 147                 // check for restricted package access
 148                 Context.checkPackageAccess(type);
 149                 // check for classes, interfaces in reflection
 150                 ReflectionCheckLinker.checkReflectionAccess(type, true);
 151             }
 152         }
 153         return getAdapterInfo(types).getAdapterClass(classOverrides, protectionDomain);
 154     }
 155 
 156     private static ProtectionDomain getProtectionDomain(final MethodHandles.Lookup lookup) {
 157         if((lookup.lookupModes() & Lookup.PRIVATE) == 0) {
 158             return MINIMAL_PERMISSION_DOMAIN;
 159         }
 160         return getProtectionDomain(lookup.lookupClass());
 161     }
 162 
 163     private static ProtectionDomain getProtectionDomain(final Class<?> clazz) {
 164         return AccessController.doPrivileged(new PrivilegedAction<ProtectionDomain>() {
 165             @Override
 166             public ProtectionDomain run() {
 167                 return clazz.getProtectionDomain();
 168             }
 169         });
 170     }
 171 
 172     /**
 173      * Returns a method handle representing a constructor that takes a single
 174      * argument of the source type (which, really, should be one of {@link ScriptObject},
 175      * {@link ScriptFunction}, or {@link Object}, and returns an instance of the
 176      * adapter for the target type. Used to implement the function autoconverters
 177      * as well as the Nashorn JSR-223 script engine's {@code getInterface()}
 178      * method.
 179      *
 180      * @param sourceType the source type; should be either {@link ScriptObject},
 181      *        {@link ScriptFunction}, or {@link Object}. In case of {@code Object},
 182      *        it will return a method handle that dispatches to either the script
 183      *        object or function constructor at invocation based on the actual
 184      *        argument.
 185      * @param targetType the target type, for which adapter instances will be created
 186      * @param lookup method handle lookup to use
 187      *
 188      * @return the constructor method handle.
 189      *
 190      * @throws Exception if anything goes wrong
 191      */
 192     public static MethodHandle getConstructor(final Class<?> sourceType, final Class<?> targetType, final MethodHandles.Lookup lookup) throws Exception {
 193         final StaticClass adapterClass = getAdapterClassFor(new Class<?>[] { targetType }, null, lookup);
 194         return MH.bindTo(Bootstrap.getLinkerServices().getGuardedInvocation(new SimpleLinkRequest(
 195                 new CallSiteDescriptor(lookup, StandardOperation.NEW,
 196                         MethodType.methodType(targetType, StaticClass.class, sourceType)), false,
 197                         adapterClass, null)).getInvocation(), adapterClass);
 198     }
 199 
 200     /**
 201      * Returns whether an instance of the specified class/interface can be
 202      * generated from a ScriptFunction. Returns {@code true} iff: the adapter
 203      * for the class/interface can be created, it is abstract (this includes
 204      * interfaces), it has at least one abstract method, all the abstract
 205      * methods share the same name, and it has a public or protected default
 206      * constructor. Note that invoking this class will most likely result in the
 207      * adapter class being defined in the JVM if it hasn't been already.
 208      *
 209      * @param clazz the inspected class
 210      *
 211      * @return {@code true} iff an instance of the specified class/interface can
 212      *         be generated from a ScriptFunction.
 213      */
 214     static boolean isAutoConvertibleFromFunction(final Class<?> clazz) {
 215         return getAdapterInfo(new Class<?>[] { clazz }).isAutoConvertibleFromFunction();
 216     }
 217 
 218     private static AdapterInfo getAdapterInfo(final Class<?>[] types) {
 219         final ClassAndLoader definingClassAndLoader = ClassAndLoader.getDefiningClassAndLoader(types);
 220 
 221         final Map<List<Class<?>>, AdapterInfo> adapterInfoMap = ADAPTER_INFO_MAPS.get(definingClassAndLoader.getRepresentativeClass());
 222         final List<Class<?>> typeList = types.length == 1 ? Collections.<Class<?>>singletonList(types[0]) : Arrays.asList(types.clone());
 223         AdapterInfo adapterInfo;
 224         synchronized(adapterInfoMap) {
 225             adapterInfo = adapterInfoMap.get(typeList);
 226             if(adapterInfo == null) {
 227                 adapterInfo = createAdapterInfo(types, definingClassAndLoader);
 228                 adapterInfoMap.put(typeList, adapterInfo);
 229             }
 230         }
 231         return adapterInfo;
 232     }
 233 
 234    /**
 235      * For a given class, create its adapter class and associated info.
 236      *
 237      * @param types the class and interfaces for which the adapter is created
 238      *
 239      * @return the adapter info for the class.
 240      */
 241     private static AdapterInfo createAdapterInfo(final Class<?>[] types, final ClassAndLoader definingClassAndLoader) {
 242         Class<?> superClass = null;
 243         final List<Class<?>> interfaces = new ArrayList<>(types.length);
 244         for(final Class<?> t: types) {
 245             final int mod = t.getModifiers();
 246             if(!t.isInterface()) {
 247                 if(superClass != null) {
 248                     return new AdapterInfo(AdaptationResult.Outcome.ERROR_MULTIPLE_SUPERCLASSES, t.getCanonicalName() + " and " + superClass.getCanonicalName());
 249                 }
 250                 if (Modifier.isFinal(mod)) {
 251                     return new AdapterInfo(AdaptationResult.Outcome.ERROR_FINAL_CLASS, t.getCanonicalName());
 252                 }
 253                 superClass = t;
 254             } else {
 255                 if (interfaces.size() > 65535) {
 256                     throw new IllegalArgumentException("interface limit exceeded");
 257                 }
 258 
 259                 interfaces.add(t);
 260             }
 261 
 262             if(!Modifier.isPublic(mod)) {
 263                 return new AdapterInfo(AdaptationResult.Outcome.ERROR_NON_PUBLIC_CLASS, t.getCanonicalName());
 264             }
 265         }
 266 
 267 
 268         final Class<?> effectiveSuperClass = superClass == null ? Object.class : superClass;
 269         return AccessController.doPrivileged(new PrivilegedAction<AdapterInfo>() {
 270             @Override
 271             public AdapterInfo run() {
 272                 try {
 273                     return new AdapterInfo(effectiveSuperClass, interfaces, definingClassAndLoader);
 274                 } catch (final AdaptationException e) {
 275                     return new AdapterInfo(e.getAdaptationResult());
 276                 } catch (final RuntimeException e) {
 277                     return new AdapterInfo(new AdaptationResult(Outcome.ERROR_OTHER, e, Arrays.toString(types), e.toString()));
 278                 }
 279             }
 280         }, CREATE_ADAPTER_INFO_ACC_CTXT);
 281     }
 282 
 283     private static class AdapterInfo {
 284         private static final ClassAndLoader SCRIPT_OBJECT_LOADER = new ClassAndLoader(ScriptFunction.class, true);
 285 
 286         private final ClassLoader commonLoader;
 287         // TODO: soft reference the JavaAdapterClassLoader objects. They can be recreated when needed.
 288         private final JavaAdapterClassLoader classAdapterGenerator;
 289         private final JavaAdapterClassLoader instanceAdapterGenerator;
 290         private final Map<CodeSource, StaticClass> instanceAdapters = new ConcurrentHashMap<>();
 291         final boolean autoConvertibleFromFunction;
 292         final AdaptationResult adaptationResult;
 293 
 294         AdapterInfo(final Class<?> superClass, final List<Class<?>> interfaces, final ClassAndLoader definingLoader) throws AdaptationException {
 295             this.commonLoader = findCommonLoader(definingLoader);
 296             final JavaAdapterBytecodeGenerator gen = new JavaAdapterBytecodeGenerator(superClass, interfaces, commonLoader, false);
 297             this.autoConvertibleFromFunction = gen.isAutoConvertibleFromFunction();
 298             instanceAdapterGenerator = gen.createAdapterClassLoader();
 299             this.classAdapterGenerator = new JavaAdapterBytecodeGenerator(superClass, interfaces, commonLoader, true).createAdapterClassLoader();
 300             this.adaptationResult = AdaptationResult.SUCCESSFUL_RESULT;
 301         }
 302 
 303         AdapterInfo(final AdaptationResult.Outcome outcome, final String classList) {
 304             this(new AdaptationResult(outcome, classList));
 305         }
 306 
 307         AdapterInfo(final AdaptationResult adaptationResult) {
 308             this.commonLoader = null;
 309             this.classAdapterGenerator = null;
 310             this.instanceAdapterGenerator = null;
 311             this.autoConvertibleFromFunction = false;
 312             this.adaptationResult = adaptationResult;
 313         }
 314 
 315         StaticClass getAdapterClass(final ScriptObject classOverrides, final ProtectionDomain protectionDomain) {
 316             if(adaptationResult.getOutcome() != AdaptationResult.Outcome.SUCCESS) {
 317                 throw adaptationResult.typeError();
 318             }
 319             return classOverrides == null ? getInstanceAdapterClass(protectionDomain) :
 320                 getClassAdapterClass(classOverrides, protectionDomain);
 321         }
 322 
 323         boolean isAutoConvertibleFromFunction() {
 324             if(adaptationResult.getOutcome() == AdaptationResult.Outcome.ERROR_OTHER) {
 325                 throw adaptationResult.typeError();
 326             }
 327             return autoConvertibleFromFunction;
 328         }
 329 
 330         private StaticClass getInstanceAdapterClass(final ProtectionDomain protectionDomain) {
 331             CodeSource codeSource = protectionDomain.getCodeSource();
 332             if(codeSource == null) {
 333                 codeSource = MINIMAL_PERMISSION_DOMAIN.getCodeSource();
 334             }
 335             StaticClass instanceAdapterClass = instanceAdapters.get(codeSource);
 336             if(instanceAdapterClass != null) {
 337                 return instanceAdapterClass;
 338             }
 339             // Any "unknown source" code source will default to no permission domain.
 340             final ProtectionDomain effectiveDomain = codeSource.equals(MINIMAL_PERMISSION_DOMAIN.getCodeSource()) ?
 341                     MINIMAL_PERMISSION_DOMAIN : protectionDomain;
 342 
 343             instanceAdapterClass = instanceAdapterGenerator.generateClass(commonLoader, effectiveDomain);
 344             final StaticClass existing = instanceAdapters.putIfAbsent(codeSource, instanceAdapterClass);
 345             return existing == null ? instanceAdapterClass : existing;
 346         }
 347 
 348         private StaticClass getClassAdapterClass(final ScriptObject classOverrides, final ProtectionDomain protectionDomain) {
 349             JavaAdapterServices.setClassOverrides(classOverrides);
 350             try {
 351                 return classAdapterGenerator.generateClass(commonLoader, protectionDomain);
 352             } finally {
 353                 JavaAdapterServices.setClassOverrides(null);
 354             }
 355         }
 356 
 357         /**
 358          * Choose between the passed class loader and the class loader that defines the
 359          * ScriptObject class, based on which of the two can see the classes in both.
 360          *
 361          * @param classAndLoader the loader and a representative class from it that will
 362          *        be used to add the generated adapter to its ADAPTER_INFO_MAPS.
 363          *
 364          * @return the class loader that sees both the specified class and Nashorn classes.
 365          *
 366          * @throws IllegalStateException if no such class loader is found.
 367          */
 368         private static ClassLoader findCommonLoader(final ClassAndLoader classAndLoader) throws AdaptationException {
 369             if(classAndLoader.canSee(SCRIPT_OBJECT_LOADER)) {
 370                 return classAndLoader.getLoader();
 371             }
 372             if (SCRIPT_OBJECT_LOADER.canSee(classAndLoader)) {
 373                 return SCRIPT_OBJECT_LOADER.getLoader();
 374             }
 375 
 376             throw new AdaptationException(AdaptationResult.Outcome.ERROR_NO_COMMON_LOADER, classAndLoader.getRepresentativeClass().getCanonicalName());
 377         }
 378     }
 379 
 380     private static ProtectionDomain createMinimalPermissionDomain() {
 381         // Generated classes need to have at least the permission to access Nashorn runtime and runtime.linker packages.
 382         final Permissions permissions = new Permissions();
 383         permissions.add(new RuntimePermission("accessClassInPackage.jdk.nashorn.internal.runtime"));
 384         permissions.add(new RuntimePermission("accessClassInPackage.jdk.nashorn.internal.runtime.linker"));
 385         return new ProtectionDomain(new CodeSource(null, (CodeSigner[])null), permissions);
 386     }
 387 }