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