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