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