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      */
getAdapterClassFor(final Class<?>[] types, final ScriptObject classOverrides, final MethodHandles.Lookup lookup)138     public static StaticClass getAdapterClassFor(final Class<?>[] types, final ScriptObject classOverrides, final MethodHandles.Lookup lookup) {
139         return getAdapterClassFor(types, classOverrides, getProtectionDomain(lookup));
140     }
141 
getAdapterClassFor(final Class<?>[] types, final ScriptObject classOverrides, final ProtectionDomain protectionDomain)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 
getProtectionDomain(final MethodHandles.Lookup lookup)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 
getProtectionDomain(final Class<?> clazz)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 }
388