JavaAdapterFactory.java revision 1158:ff6d1fe94b3d
150724Scg/*
250724Scg * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
350724Scg * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
450724Scg *
550724Scg * This code is free software; you can redistribute it and/or modify it
650724Scg * under the terms of the GNU General Public License version 2 only, as
750724Scg * published by the Free Software Foundation.  Oracle designates this
850724Scg * particular file as subject to the "Classpath" exception as provided
950724Scg * by Oracle in the LICENSE file that accompanied this code.
1050724Scg *
1150724Scg * This code is distributed in the hope that it will be useful, but WITHOUT
1250724Scg * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
1350724Scg * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
1450724Scg * version 2 for more details (a copy is included in the LICENSE file that
1550724Scg * accompanied this code).
1650724Scg *
1750724Scg * You should have received a copy of the GNU General Public License version
1850724Scg * 2 along with this work; if not, write to the Free Software Foundation,
1950724Scg * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
2050724Scg *
2150724Scg * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2250724Scg * or visit www.oracle.com if you need additional information or have any
2350724Scg * questions.
2450724Scg */
2550724Scg
2650724Scgpackage jdk.nashorn.internal.runtime.linker;
2750733Speter
2850724Scgimport static jdk.nashorn.internal.lookup.Lookup.MH;
2950724Scg
3050724Scgimport java.lang.invoke.MethodHandle;
3150724Scgimport java.lang.invoke.MethodHandles;
3250724Scgimport java.lang.invoke.MethodHandles.Lookup;
3350724Scgimport java.lang.invoke.MethodType;
3450724Scgimport java.lang.reflect.Modifier;
3550724Scgimport java.security.AccessControlContext;
3650724Scgimport java.security.AccessController;
3755205Speterimport java.security.CodeSigner;
3850724Scgimport java.security.CodeSource;
3950724Scgimport java.security.Permissions;
4050724Scgimport java.security.PrivilegedAction;
4150724Scgimport java.security.ProtectionDomain;
4250724Scgimport java.util.ArrayList;
4350724Scgimport java.util.Arrays;
4450724Scgimport java.util.Collections;
4550724Scgimport java.util.HashMap;
4650724Scgimport java.util.List;
4750724Scgimport java.util.Map;
4850724Scgimport java.util.concurrent.ConcurrentHashMap;
4950724Scgimport jdk.internal.dynalink.beans.StaticClass;
5050724Scgimport jdk.internal.dynalink.support.LinkRequestImpl;
5150724Scgimport jdk.nashorn.internal.runtime.Context;
5250724Scgimport jdk.nashorn.internal.runtime.ECMAException;
5350724Scgimport jdk.nashorn.internal.runtime.ScriptFunction;
5450724Scgimport jdk.nashorn.internal.runtime.ScriptObject;
5563469Scg
5660041Sphk/**
5763469Scg * <p>A factory class that generates adapter classes. Adapter classes allow implementation of Java interfaces and
5850724Scg * extending of Java classes from JavaScript. For every combination of a superclass to extend and interfaces to
5950724Scg * implement (collectively: "original types"), exactly one adapter class is generated that extends the specified
6050724Scg * superclass and implements the specified interfaces. (But see the discussion of class-based overrides for exceptions.)
6150724Scg * </p><p>
6250724Scg * The adapter class is generated in a new secure class loader that inherits Nashorn's protection domain, and has either
6350724Scg * one of the original types' class loader or the Nashorn's class loader as its parent - the parent class loader
6450724Scg * is chosen so that all the original types and the Nashorn core classes are visible from it (as the adapter will have
6550724Scg * constant pool references to ScriptObject and ScriptFunction classes). In case none of the candidate class loaders has
6670617Sjhb * visibility of all the required types, an error is thrown. The class uses {@link JavaAdapterBytecodeGenerator} to
6750724Scg * generate the adapter class itself; see its documentation for details about the generated class.
6850724Scg * </p><p>
6950724Scg * You normally don't use this class directly, but rather either create adapters from script using
7050724Scg * {@link jdk.nashorn.internal.objects.NativeJava#extend(Object, Object...)}, using the {@code new} operator on abstract classes and interfaces (see
7173126Scg * {@link jdk.nashorn.internal.objects.NativeJava#type(Object, Object)}), or implicitly when passing script functions to Java methods expecting SAM
7273126Scg * types.
7373126Scg * </p>
7473760Scg */
7573126Scg
7673126Scg@SuppressWarnings("javadoc")
7750724Scgpublic final class JavaAdapterFactory {
7850724Scg    private static final ProtectionDomain MINIMAL_PERMISSION_DOMAIN = createMinimalPermissionDomain();
7950724Scg
8050724Scg    // context with permissions needs for AdapterInfo creation
8150724Scg    private static final AccessControlContext CREATE_ADAPTER_INFO_ACC_CTXT =
8250724Scg        ClassAndLoader.createPermAccCtxt("createClassLoader", "getClassLoader",
8350724Scg            "accessDeclaredMembers", "accessClassInPackage.jdk.nashorn.internal.runtime");
8450724Scg
8555205Speter    /**
8650724Scg     * A mapping from an original Class object to AdapterInfo representing the adapter for the class it represents.
8750724Scg     */
8850724Scg    private static final ClassValue<Map<List<Class<?>>, AdapterInfo>> ADAPTER_INFO_MAPS = new ClassValue<Map<List<Class<?>>, AdapterInfo>>() {
8953465Scg        @Override
9053465Scg        protected Map<List<Class<?>>, AdapterInfo> computeValue(final Class<?> type) {
9170291Scg            return new HashMap<>();
9264881Scg        }
9353465Scg    };
9453465Scg
9550724Scg    /**
9664442Scg     * Returns an adapter class for the specified original types. The adapter class extends/implements the original
9764442Scg     * class/interfaces.
9864442Scg     * @param types the original types. The caller must pass at least one Java type representing either a public
9964442Scg     * interface or a non-final public class with at least one public or protected constructor. If more than one type is
10064442Scg     * specified, at most one can be a class and the rest have to be interfaces. The class can be in any position in the
10164442Scg     * array. Invoking the method twice with exactly the same types in the same order will return the same adapter
10262483Scg     * class, any reordering of types or even addition or removal of redundant types (i.e. interfaces that other types
10362483Scg     * in the list already implement/extend, or {@code java.lang.Object} in a list of types consisting purely of
10462483Scg     * interfaces) will result in a different adapter class, even though those adapter classes are functionally
10562483Scg     * identical; we deliberately don't want to incur the additional processing cost of canonicalizing type lists.
10662483Scg     * @param classOverrides a JavaScript object with functions serving as the class-level overrides and
10762483Scg     * implementations. These overrides are defined for all instances of the class, and can be further overridden on a
10850724Scg     * per-instance basis by passing additional objects in the constructor.
10950724Scg     * @param lookup the lookup object identifying the caller class. The generated adapter class will have the
11050724Scg     * protection domain of the caller class iff the lookup object is full-strength, otherwise it will be completely
11150724Scg     * unprivileged.
11250724Scg     * @return an adapter class. See this class' documentation for details on the generated adapter class.
11350724Scg     * @throws ECMAException with a TypeError if the adapter class can not be generated because the original class is
11450724Scg     * final, non-public, or has no public or protected constructors.
11550724Scg     */
11650724Scg    public static StaticClass getAdapterClassFor(final Class<?>[] types, final ScriptObject classOverrides, final MethodHandles.Lookup lookup) {
11750724Scg        return getAdapterClassFor(types, classOverrides, getProtectionDomain(lookup));
11850724Scg    }
11950724Scg
12055204Scg    private static StaticClass getAdapterClassFor(final Class<?>[] types, final ScriptObject classOverrides, final ProtectionDomain protectionDomain) {
12150724Scg        assert types != null && types.length > 0;
12250724Scg        final SecurityManager sm = System.getSecurityManager();
12365486Scg        if (sm != null) {
12450724Scg            for (final Class<?> type : types) {
12568405Scg                // check for restricted package access
12650724Scg                Context.checkPackageAccess(type);
12750724Scg                // check for classes, interfaces in reflection
12850724Scg                ReflectionCheckLinker.checkReflectionAccess(type, true);
12950724Scg            }
13070134Scg        }
13150724Scg        return getAdapterInfo(types).getAdapterClass(classOverrides, protectionDomain);
13250724Scg    }
13350724Scg
13450724Scg    private static ProtectionDomain getProtectionDomain(final MethodHandles.Lookup lookup) {
13550724Scg        if((lookup.lookupModes() & Lookup.PRIVATE) == 0) {
13650724Scg            return MINIMAL_PERMISSION_DOMAIN;
13750724Scg        }
13850724Scg        return getProtectionDomain(lookup.lookupClass());
13950724Scg    }
14050724Scg
14150724Scg    private static ProtectionDomain getProtectionDomain(final Class<?> clazz) {
14250724Scg        return AccessController.doPrivileged(new PrivilegedAction<ProtectionDomain>() {
14350724Scg            @Override
14450724Scg            public ProtectionDomain run() {
14550724Scg                return clazz.getProtectionDomain();
14650724Scg            }
14750724Scg        });
14850724Scg    }
14950724Scg
15050724Scg    /**
15150724Scg     * Returns a method handle representing a constructor that takes a single argument of the source type (which,
15251769Scg     * really, should be one of {@link ScriptObject}, {@link ScriptFunction}, or {@link Object}, and returns an instance
15350724Scg     * of the adapter for the target type. Used to implement the function autoconverters as well as the Nashorn's
15450724Scg     * JSR-223 script engine's {@code getInterface()} method.
15550724Scg     * @param sourceType the source type; should be either {@link ScriptObject}, {@link ScriptFunction}, or
15650724Scg     * {@link Object}. In case of {@code Object}, it will return a method handle that dispatches to either the script
15750724Scg     * object or function constructor at invocation based on the actual argument.
15850724Scg     * @param targetType the target type, for which adapter instances will be created
15955205Speter     * @return the constructor method handle.
16050724Scg     * @throws Exception if anything goes wrong
16150724Scg     */
16250724Scg    public static MethodHandle getConstructor(final Class<?> sourceType, final Class<?> targetType, final MethodHandles.Lookup lookup) throws Exception {
16350724Scg        final StaticClass adapterClass = getAdapterClassFor(new Class<?>[] { targetType }, null, lookup);
16450724Scg        return MH.bindTo(Bootstrap.getLinkerServices().getGuardedInvocation(new LinkRequestImpl(
16550724Scg                NashornCallSiteDescriptor.get(lookup, "dyn:new",
16650724Scg                        MethodType.methodType(targetType, StaticClass.class, sourceType), 0), null, 0, false,
16750724Scg                        adapterClass, null)).getInvocation(), adapterClass);
16850724Scg    }
16950724Scg
17050724Scg    /**
17150724Scg     * Returns whether an instance of the specified class/interface can be generated from a ScriptFunction. Returns true
17250724Scg     * iff: the adapter for the class/interface can be created, it is abstract (this includes interfaces), it has at
17370617Sjhb     * least one abstract method, all the abstract methods share the same name, and it has a public or protected default
17470617Sjhb     * constructor. Note that invoking this class will most likely result in the adapter class being defined in the JVM
17570134Scg     * if it hasn't been already.
17650724Scg     * @param clazz the inspected class
17765340Scg     * @return true iff an instance of the specified class/interface can be generated from a ScriptFunction.
17850724Scg     */
17950724Scg    static boolean isAutoConvertibleFromFunction(final Class<?> clazz) {
18050724Scg        return getAdapterInfo(new Class<?>[] { clazz }).autoConvertibleFromFunction;
18158384Scg    }
18250724Scg
18373131Scg    private static AdapterInfo getAdapterInfo(final Class<?>[] types) {
18473131Scg        final ClassAndLoader definingClassAndLoader = ClassAndLoader.getDefiningClassAndLoader(types);
18573131Scg
18673131Scg        final Map<List<Class<?>>, AdapterInfo> adapterInfoMap = ADAPTER_INFO_MAPS.get(definingClassAndLoader.getRepresentativeClass());
18773131Scg        final List<Class<?>> typeList = types.length == 1 ? Collections.<Class<?>>singletonList(types[0]) : Arrays.asList(types.clone());
18873131Scg        AdapterInfo adapterInfo;
18973131Scg        synchronized(adapterInfoMap) {
19073131Scg            adapterInfo = adapterInfoMap.get(typeList);
19173131Scg            if(adapterInfo == null) {
19255205Speter                adapterInfo = createAdapterInfo(types, definingClassAndLoader);
19350724Scg                adapterInfoMap.put(typeList, adapterInfo);
19450724Scg            }
19550724Scg        }
19650724Scg        return adapterInfo;
19750724Scg    }
19850724Scg
19950724Scg   /**
20050724Scg     * For a given class, create its adapter class and associated info.
201     * @param type the class for which the adapter is created
202     * @return the adapter info for the class.
203     */
204    private static AdapterInfo createAdapterInfo(final Class<?>[] types, final ClassAndLoader definingClassAndLoader) {
205        Class<?> superClass = null;
206        final List<Class<?>> interfaces = new ArrayList<>(types.length);
207        for(final Class<?> t: types) {
208            final int mod = t.getModifiers();
209            if(!t.isInterface()) {
210                if(superClass != null) {
211                    return new AdapterInfo(AdaptationResult.Outcome.ERROR_MULTIPLE_SUPERCLASSES, t.getCanonicalName() + " and " + superClass.getCanonicalName());
212                }
213                if (Modifier.isFinal(mod)) {
214                    return new AdapterInfo(AdaptationResult.Outcome.ERROR_FINAL_CLASS, t.getCanonicalName());
215                }
216                superClass = t;
217            } else {
218                if (interfaces.size() > 65535) {
219                    throw new IllegalArgumentException("interface limit exceeded");
220                }
221
222                interfaces.add(t);
223            }
224
225            if(!Modifier.isPublic(mod)) {
226                return new AdapterInfo(AdaptationResult.Outcome.ERROR_NON_PUBLIC_CLASS, t.getCanonicalName());
227            }
228        }
229
230
231        final Class<?> effectiveSuperClass = superClass == null ? Object.class : superClass;
232        return AccessController.doPrivileged(new PrivilegedAction<AdapterInfo>() {
233            @Override
234            public AdapterInfo run() {
235                try {
236                    return new AdapterInfo(effectiveSuperClass, interfaces, definingClassAndLoader);
237                } catch (final AdaptationException e) {
238                    return new AdapterInfo(e.getAdaptationResult());
239                } catch (final RuntimeException e) {
240                    return new AdapterInfo(new AdaptationResult(AdaptationResult.Outcome.ERROR_OTHER, Arrays.toString(types), e.toString()));
241                }
242            }
243        }, CREATE_ADAPTER_INFO_ACC_CTXT);
244    }
245
246    private static class AdapterInfo {
247        private static final ClassAndLoader SCRIPT_OBJECT_LOADER = new ClassAndLoader(ScriptFunction.class, true);
248
249        private final ClassLoader commonLoader;
250        // TODO: soft reference the JavaAdapterClassLoader objects. They can be recreated when needed.
251        private final JavaAdapterClassLoader classAdapterGenerator;
252        private final JavaAdapterClassLoader instanceAdapterGenerator;
253        private final Map<CodeSource, StaticClass> instanceAdapters = new ConcurrentHashMap<>();
254        final boolean autoConvertibleFromFunction;
255        final AdaptationResult adaptationResult;
256
257        AdapterInfo(final Class<?> superClass, final List<Class<?>> interfaces, final ClassAndLoader definingLoader) throws AdaptationException {
258            this.commonLoader = findCommonLoader(definingLoader);
259            final JavaAdapterBytecodeGenerator gen = new JavaAdapterBytecodeGenerator(superClass, interfaces, commonLoader, false);
260            this.autoConvertibleFromFunction = gen.isAutoConvertibleFromFunction();
261            instanceAdapterGenerator = gen.createAdapterClassLoader();
262            this.classAdapterGenerator = new JavaAdapterBytecodeGenerator(superClass, interfaces, commonLoader, true).createAdapterClassLoader();
263            this.adaptationResult = AdaptationResult.SUCCESSFUL_RESULT;
264        }
265
266        AdapterInfo(final AdaptationResult.Outcome outcome, final String classList) {
267            this(new AdaptationResult(outcome, classList));
268        }
269
270        AdapterInfo(final AdaptationResult adaptationResult) {
271            this.commonLoader = null;
272            this.classAdapterGenerator = null;
273            this.instanceAdapterGenerator = null;
274            this.autoConvertibleFromFunction = false;
275            this.adaptationResult = adaptationResult;
276        }
277
278        StaticClass getAdapterClass(final ScriptObject classOverrides, final ProtectionDomain protectionDomain) {
279            if(adaptationResult.getOutcome() != AdaptationResult.Outcome.SUCCESS) {
280                throw adaptationResult.typeError();
281            }
282            return classOverrides == null ? getInstanceAdapterClass(protectionDomain) :
283                getClassAdapterClass(classOverrides, protectionDomain);
284        }
285
286        private StaticClass getInstanceAdapterClass(final ProtectionDomain protectionDomain) {
287            CodeSource codeSource = protectionDomain.getCodeSource();
288            if(codeSource == null) {
289                codeSource = MINIMAL_PERMISSION_DOMAIN.getCodeSource();
290            }
291            StaticClass instanceAdapterClass = instanceAdapters.get(codeSource);
292            if(instanceAdapterClass != null) {
293                return instanceAdapterClass;
294            }
295            // Any "unknown source" code source will default to no permission domain.
296            final ProtectionDomain effectiveDomain = codeSource.equals(MINIMAL_PERMISSION_DOMAIN.getCodeSource()) ?
297                    MINIMAL_PERMISSION_DOMAIN : protectionDomain;
298
299            instanceAdapterClass = instanceAdapterGenerator.generateClass(commonLoader, effectiveDomain);
300            final StaticClass existing = instanceAdapters.putIfAbsent(codeSource, instanceAdapterClass);
301            return existing == null ? instanceAdapterClass : existing;
302        }
303
304        private StaticClass getClassAdapterClass(final ScriptObject classOverrides, final ProtectionDomain protectionDomain) {
305            JavaAdapterServices.setClassOverrides(classOverrides);
306            try {
307                return classAdapterGenerator.generateClass(commonLoader, protectionDomain);
308            } finally {
309                JavaAdapterServices.setClassOverrides(null);
310            }
311        }
312
313        /**
314         * Choose between the passed class loader and the class loader that defines the ScriptObject class, based on which
315         * of the two can see the classes in both.
316         * @param classAndLoader the loader and a representative class from it that will be used to add the generated
317         * adapter to its ADAPTER_INFO_MAPS.
318         * @return the class loader that sees both the specified class and Nashorn classes.
319         * @throws IllegalStateException if no such class loader is found.
320         */
321        private static ClassLoader findCommonLoader(final ClassAndLoader classAndLoader) throws AdaptationException {
322            if(classAndLoader.canSee(SCRIPT_OBJECT_LOADER)) {
323                return classAndLoader.getLoader();
324            }
325            if (SCRIPT_OBJECT_LOADER.canSee(classAndLoader)) {
326                return SCRIPT_OBJECT_LOADER.getLoader();
327            }
328
329            throw new AdaptationException(AdaptationResult.Outcome.ERROR_NO_COMMON_LOADER, classAndLoader.getRepresentativeClass().getCanonicalName());
330        }
331    }
332
333    private static ProtectionDomain createMinimalPermissionDomain() {
334        // Generated classes need to have at least the permission to access Nashorn runtime and runtime.linker packages.
335        final Permissions permissions = new Permissions();
336        permissions.add(new RuntimePermission("accessClassInPackage.jdk.nashorn.internal.objects"));
337        permissions.add(new RuntimePermission("accessClassInPackage.jdk.nashorn.internal.runtime"));
338        permissions.add(new RuntimePermission("accessClassInPackage.jdk.nashorn.internal.runtime.linker"));
339        return new ProtectionDomain(new CodeSource(null, (CodeSigner[])null), permissions);
340    }
341}
342