JavaAdapterFactory.java revision 1472:7dd80d7f47c3
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 26package jdk.nashorn.internal.runtime.linker; 27 28import static jdk.nashorn.internal.lookup.Lookup.MH; 29 30import java.lang.invoke.MethodHandle; 31import java.lang.invoke.MethodHandles; 32import java.lang.invoke.MethodHandles.Lookup; 33import java.lang.invoke.MethodType; 34import java.lang.reflect.Modifier; 35import java.security.AccessControlContext; 36import java.security.AccessController; 37import java.security.CodeSigner; 38import java.security.CodeSource; 39import java.security.Permissions; 40import java.security.PrivilegedAction; 41import java.security.ProtectionDomain; 42import java.util.ArrayList; 43import java.util.Arrays; 44import java.util.Collections; 45import java.util.HashMap; 46import java.util.List; 47import java.util.Map; 48import java.util.concurrent.ConcurrentHashMap; 49import jdk.internal.dynalink.beans.StaticClass; 50import jdk.internal.dynalink.support.SimpleLinkRequest; 51import jdk.nashorn.internal.runtime.Context; 52import jdk.nashorn.internal.runtime.ECMAException; 53import jdk.nashorn.internal.runtime.ScriptFunction; 54import 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") 82public 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 SimpleLinkRequest( 192 NashornCallSiteDescriptor.get(lookup, "dyn:new", 193 MethodType.methodType(targetType, StaticClass.class, sourceType), 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} 379