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