1 /* 2 * Copyright (c) 2010, 2013, 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.internal.dynalink.beans.StaticClass; 50 import jdk.internal.dynalink.support.LinkRequestImpl; 51 import jdk.nashorn.internal.runtime.Context; 52 import jdk.nashorn.internal.runtime.ECMAException; 53 import jdk.nashorn.internal.runtime.ScriptFunction; 54 import jdk.nashorn.internal.runtime.ScriptObject; 55 56 /** 57 * <p>A factory class that generates adapter classes. Adapter classes allow implementation of Java interfaces and 58 * extending of Java classes from JavaScript. For every combination of a superclass to extend and interfaces to 59 * implement (collectively: "original types"), exactly one adapter class is generated that extends the specified 60 * superclass and implements the specified interfaces. (But see the discussion of class-based overrides for exceptions.) 61 * </p><p> 62 * The adapter class is generated in a new secure class loader that inherits Nashorn's protection domain, and has either 63 * one of the original types' class loader or the Nashorn's class loader as its parent - the parent class loader 64 * is chosen so that all the original types and the Nashorn core classes are visible from it (as the adapter will have 65 * constant pool references to ScriptObject and ScriptFunction classes). In case none of the candidate class loaders has 66 * visibility of all the required types, an error is thrown. The class uses {@link JavaAdapterBytecodeGenerator} to 67 * generate the adapter class itself; see its documentation for details about the generated class. 68 * </p><p> 69 * You normally don't use this class directly, but rather either create adapters from script using 70 * {@link jdk.nashorn.internal.objects.NativeJava#extend(Object, Object...)}, using the {@code new} operator on abstract classes and interfaces (see 71 * {@link jdk.nashorn.internal.objects.NativeJava#type(Object, Object)}), or implicitly when passing script functions to Java methods expecting SAM 72 * types. 73 * </p> 74 */ 75 76 @SuppressWarnings("javadoc") 77 public final class JavaAdapterFactory { 78 private static final ProtectionDomain MINIMAL_PERMISSION_DOMAIN = createMinimalPermissionDomain(); 79 80 // context with permissions needs for AdapterInfo creation 81 private static final AccessControlContext CREATE_ADAPTER_INFO_ACC_CTXT = 82 ClassAndLoader.createPermAccCtxt("createClassLoader", "getClassLoader", 83 "accessDeclaredMembers", "accessClassInPackage.jdk.nashorn.internal.runtime"); 84 85 /** 86 * A mapping from an original Class object to AdapterInfo representing the adapter for the class it represents. 87 */ 88 private static final ClassValue<Map<List<Class<?>>, AdapterInfo>> ADAPTER_INFO_MAPS = new ClassValue<Map<List<Class<?>>, AdapterInfo>>() { 89 @Override 90 protected Map<List<Class<?>>, AdapterInfo> computeValue(final Class<?> type) { 91 return new HashMap<>(); 92 } 93 }; 94 95 /** 96 * Returns an adapter class for the specified original types. The adapter class extends/implements the original 97 * class/interfaces. 98 * @param types the original types. The caller must pass at least one Java type representing either a public 99 * interface or a non-final public class with at least one public or protected constructor. If more than one type is 100 * specified, at most one can be a class and the rest have to be interfaces. The class can be in any position in the 101 * array. Invoking the method twice with exactly the same types in the same order will return the same adapter 102 * class, any reordering of types or even addition or removal of redundant types (i.e. interfaces that other types 103 * in the list already implement/extend, or {@code java.lang.Object} in a list of types consisting purely of 104 * interfaces) will result in a different adapter class, even though those adapter classes are functionally 105 * identical; we deliberately don't want to incur the additional processing cost of canonicalizing type lists. 106 * @param classOverrides a JavaScript object with functions serving as the class-level overrides and 107 * implementations. These overrides are defined for all instances of the class, and can be further overridden on a 108 * per-instance basis by passing additional objects in the constructor. 109 * @param lookup the lookup object identifying the caller class. The generated adapter class will have the 110 * protection domain of the caller class iff the lookup object is full-strength, otherwise it will be completely 111 * unprivileged. 112 * @return an adapter class. See this class' documentation for details on the generated adapter class. 113 * @throws ECMAException with a TypeError if the adapter class can not be generated because the original class is 114 * final, non-public, or has no public or protected constructors. 115 */ 116 public static StaticClass getAdapterClassFor(final Class<?>[] types, ScriptObject classOverrides, final MethodHandles.Lookup lookup) { 117 return getAdapterClassFor(types, classOverrides, getProtectionDomain(lookup)); 118 } 119 120 private static StaticClass getAdapterClassFor(final Class<?>[] types, ScriptObject classOverrides, final ProtectionDomain protectionDomain) { 121 assert types != null && types.length > 0; 122 final SecurityManager sm = System.getSecurityManager(); 123 if (sm != null) { 124 for (Class<?> type : types) { 125 // check for restricted package access 126 Context.checkPackageAccess(type); 127 // check for classes, interfaces in reflection 128 ReflectionCheckLinker.checkReflectionAccess(type, true); 129 } 130 } 131 return getAdapterInfo(types).getAdapterClass(classOverrides, protectionDomain); 132 } 133 134 private static ProtectionDomain getProtectionDomain(final MethodHandles.Lookup lookup) { 135 if((lookup.lookupModes() & Lookup.PRIVATE) == 0) { 136 return MINIMAL_PERMISSION_DOMAIN; 137 } 138 return getProtectionDomain(lookup.lookupClass()); 139 } 140 141 private static ProtectionDomain getProtectionDomain(final Class<?> clazz) { 142 return AccessController.doPrivileged(new PrivilegedAction<ProtectionDomain>() { 143 @Override 144 public ProtectionDomain run() { 145 return clazz.getProtectionDomain(); 146 } 147 }); 148 } 149 150 /** 151 * Returns a method handle representing a constructor that takes a single argument of the source type (which, 152 * really, should be one of {@link ScriptObject}, {@link ScriptFunction}, or {@link Object}, and returns an instance 153 * of the adapter for the target type. Used to implement the function autoconverters as well as the Nashorn's 154 * JSR-223 script engine's {@code getInterface()} method. 155 * @param sourceType the source type; should be either {@link ScriptObject}, {@link ScriptFunction}, or 156 * {@link Object}. In case of {@code Object}, it will return a method handle that dispatches to either the script 157 * object or function constructor at invocation based on the actual argument. 158 * @param targetType the target type, for which adapter instances will be created 159 * @return the constructor method handle. 160 * @throws Exception if anything goes wrong 161 */ 162 public static MethodHandle getConstructor(final Class<?> sourceType, final Class<?> targetType, final MethodHandles.Lookup lookup) throws Exception { 163 final StaticClass adapterClass = getAdapterClassFor(new Class<?>[] { targetType }, null, lookup); 164 return MH.bindTo(Bootstrap.getLinkerServices().getGuardedInvocation(new LinkRequestImpl( 165 NashornCallSiteDescriptor.get(lookup, "dyn:new", 166 MethodType.methodType(targetType, StaticClass.class, sourceType), 0), false, 167 adapterClass, null)).getInvocation(), adapterClass); 168 } 169 170 /** 171 * Returns whether an instance of the specified class/interface can be generated from a ScriptFunction. Returns true 172 * iff: the adapter for the class/interface can be created, it is abstract (this includes interfaces), it has at 173 * least one abstract method, all the abstract methods share the same name, and it has a public or protected default 174 * constructor. Note that invoking this class will most likely result in the adapter class being defined in the JVM 175 * if it hasn't been already. 176 * @param clazz the inspected class 177 * @return true iff an instance of the specified class/interface can be generated from a ScriptFunction. 178 */ 179 static boolean isAutoConvertibleFromFunction(final Class<?> clazz) { 180 return getAdapterInfo(new Class<?>[] { clazz }).autoConvertibleFromFunction; 181 } 182 183 private static AdapterInfo getAdapterInfo(final Class<?>[] types) { 184 final ClassAndLoader definingClassAndLoader = ClassAndLoader.getDefiningClassAndLoader(types); 185 186 final Map<List<Class<?>>, AdapterInfo> adapterInfoMap = ADAPTER_INFO_MAPS.get(definingClassAndLoader.getRepresentativeClass()); 187 final List<Class<?>> typeList = types.length == 1 ? getSingletonClassList(types[0]) : Arrays.asList(types.clone()); 188 AdapterInfo adapterInfo; 189 synchronized(adapterInfoMap) { 190 adapterInfo = adapterInfoMap.get(typeList); 191 if(adapterInfo == null) { 192 adapterInfo = createAdapterInfo(types, definingClassAndLoader); 193 adapterInfoMap.put(typeList, adapterInfo); 194 } 195 } 196 return adapterInfo; 197 } 198 199 @SuppressWarnings({ "unchecked", "rawtypes" }) 200 private static List<Class<?>> getSingletonClassList(final Class<?> clazz) { 201 return (List)Collections.singletonList(clazz); 202 } 203 204 /** 205 * For a given class, create its adapter class and associated info. 206 * @param type the class for which the adapter is created 207 * @return the adapter info for the class. 208 */ 209 private static AdapterInfo createAdapterInfo(final Class<?>[] types, final ClassAndLoader definingClassAndLoader) { 210 Class<?> superClass = null; 211 final List<Class<?>> interfaces = new ArrayList<>(types.length); 212 for(final Class<?> t: types) { 213 final int mod = t.getModifiers(); 214 if(!t.isInterface()) { 215 if(superClass != null) { 216 return new AdapterInfo(AdaptationResult.Outcome.ERROR_MULTIPLE_SUPERCLASSES, t.getCanonicalName() + " and " + superClass.getCanonicalName()); 217 } 218 if (Modifier.isFinal(mod)) { 219 return new AdapterInfo(AdaptationResult.Outcome.ERROR_FINAL_CLASS, t.getCanonicalName()); 220 } 221 superClass = t; 222 } else { 223 if (interfaces.size() > 65535) { 224 throw new IllegalArgumentException("interface limit exceeded"); 225 } 226 227 interfaces.add(t); 228 } 229 230 if(!Modifier.isPublic(mod)) { 231 return new AdapterInfo(AdaptationResult.Outcome.ERROR_NON_PUBLIC_CLASS, t.getCanonicalName()); 232 } 233 } 234 235 236 final Class<?> effectiveSuperClass = superClass == null ? Object.class : superClass; 237 return AccessController.doPrivileged(new PrivilegedAction<AdapterInfo>() { 238 @Override 239 public AdapterInfo run() { 240 try { 241 return new AdapterInfo(effectiveSuperClass, interfaces, definingClassAndLoader); 242 } catch (final AdaptationException e) { 243 return new AdapterInfo(e.getAdaptationResult()); 244 } 245 } 246 }, CREATE_ADAPTER_INFO_ACC_CTXT); 247 } 248 249 private static class AdapterInfo { 250 private static final ClassAndLoader SCRIPT_OBJECT_LOADER = new ClassAndLoader(ScriptObject.class, true); 251 252 private final ClassLoader commonLoader; 253 // TODO: soft reference the JavaAdapterClassLoader objects. They can be recreated when needed. 254 private final JavaAdapterClassLoader classAdapterGenerator; 255 private final JavaAdapterClassLoader instanceAdapterGenerator; 256 private final Map<CodeSource, StaticClass> instanceAdapters = new ConcurrentHashMap<>(); 257 final boolean autoConvertibleFromFunction; 258 final AdaptationResult adaptationResult; 259 260 AdapterInfo(Class<?> superClass, List<Class<?>> interfaces, ClassAndLoader definingLoader) throws AdaptationException { 261 this.commonLoader = findCommonLoader(definingLoader); 262 final JavaAdapterBytecodeGenerator gen = new JavaAdapterBytecodeGenerator(superClass, interfaces, commonLoader, false); 263 this.autoConvertibleFromFunction = gen.isAutoConvertibleFromFunction(); 264 instanceAdapterGenerator = gen.createAdapterClassLoader(); 265 this.classAdapterGenerator = new JavaAdapterBytecodeGenerator(superClass, interfaces, commonLoader, true).createAdapterClassLoader(); 266 this.adaptationResult = AdaptationResult.SUCCESSFUL_RESULT; 267 } 268 269 AdapterInfo(final AdaptationResult.Outcome outcome, final String classList) { 270 this(new AdaptationResult(outcome, classList)); 271 } 272 273 AdapterInfo(final AdaptationResult adaptationResult) { 274 this.commonLoader = null; 275 this.classAdapterGenerator = null; 276 this.instanceAdapterGenerator = null; 277 this.autoConvertibleFromFunction = false; 278 this.adaptationResult = adaptationResult; 279 } 280 281 StaticClass getAdapterClass(final ScriptObject classOverrides, final ProtectionDomain protectionDomain) { 282 if(adaptationResult.getOutcome() != AdaptationResult.Outcome.SUCCESS) { 283 throw adaptationResult.typeError(); 284 } 285 return classOverrides == null ? getInstanceAdapterClass(protectionDomain) : 286 getClassAdapterClass(classOverrides, protectionDomain); 287 } 288 289 private StaticClass getInstanceAdapterClass(final ProtectionDomain protectionDomain) { 290 CodeSource codeSource = protectionDomain.getCodeSource(); 291 if(codeSource == null) { 292 codeSource = MINIMAL_PERMISSION_DOMAIN.getCodeSource(); 293 } 294 StaticClass instanceAdapterClass = instanceAdapters.get(codeSource); 295 if(instanceAdapterClass != null) { 296 return instanceAdapterClass; 297 } 298 // Any "unknown source" code source will default to no permission domain. 299 final ProtectionDomain effectiveDomain = codeSource.equals(MINIMAL_PERMISSION_DOMAIN.getCodeSource()) ? 300 MINIMAL_PERMISSION_DOMAIN : protectionDomain; 301 302 instanceAdapterClass = instanceAdapterGenerator.generateClass(commonLoader, effectiveDomain); 303 final StaticClass existing = instanceAdapters.putIfAbsent(codeSource, instanceAdapterClass); 304 return existing == null ? instanceAdapterClass : existing; 305 } 306 307 private StaticClass getClassAdapterClass(final ScriptObject classOverrides, final ProtectionDomain protectionDomain) { 308 JavaAdapterServices.setClassOverrides(classOverrides); 309 try { 310 return classAdapterGenerator.generateClass(commonLoader, protectionDomain); 311 } finally { 312 JavaAdapterServices.setClassOverrides(null); 313 } 314 } 315 316 /** 317 * Choose between the passed class loader and the class loader that defines the ScriptObject class, based on which 318 * of the two can see the classes in both. 319 * @param classAndLoader the loader and a representative class from it that will be used to add the generated 320 * adapter to its ADAPTER_INFO_MAPS. 321 * @return the class loader that sees both the specified class and Nashorn classes. 322 * @throws IllegalStateException if no such class loader is found. 323 */ 324 private static ClassLoader findCommonLoader(final ClassAndLoader classAndLoader) throws AdaptationException { 325 if(classAndLoader.canSee(SCRIPT_OBJECT_LOADER)) { 326 return classAndLoader.getLoader(); 327 } 328 if (SCRIPT_OBJECT_LOADER.canSee(classAndLoader)) { 329 return SCRIPT_OBJECT_LOADER.getLoader(); 330 } 331 332 throw new AdaptationException(AdaptationResult.Outcome.ERROR_NO_COMMON_LOADER, classAndLoader.getRepresentativeClass().getCanonicalName()); 333 } 334 } 335 336 private static ProtectionDomain createMinimalPermissionDomain() { 337 // Generated classes need to have at least the permission to access Nashorn runtime and runtime.linker packages. 338 final Permissions permissions = new Permissions(); 339 permissions.add(new RuntimePermission("accessClassInPackage.jdk.nashorn.internal.objects")); 340 permissions.add(new RuntimePermission("accessClassInPackage.jdk.nashorn.internal.runtime")); 341 permissions.add(new RuntimePermission("accessClassInPackage.jdk.nashorn.internal.runtime.linker")); 342 return new ProtectionDomain(new CodeSource(null, (CodeSigner[])null), permissions); 343 } 344 }