NashornLinker.java revision 1481:e6bb9489faac
11541Srgrimes/* 21541Srgrimes * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. 31541Srgrimes * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 41541Srgrimes * 551138Salfred * This code is free software; you can redistribute it and/or modify it 6107914Sdillon * under the terms of the GNU General Public License version 2 only, as 71541Srgrimes * published by the Free Software Foundation. Oracle designates this 81541Srgrimes * particular file as subject to the "Classpath" exception as provided 91541Srgrimes * by Oracle in the LICENSE file that accompanied this code. 1064002Speter * 111541Srgrimes * This code is distributed in the hope that it will be useful, but WITHOUT 121541Srgrimes * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 131541Srgrimes * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 141541Srgrimes * version 2 for more details (a copy is included in the LICENSE file that 151541Srgrimes * accompanied this code). 161541Srgrimes * 171541Srgrimes * You should have received a copy of the GNU General Public License version 181541Srgrimes * 2 along with this work; if not, write to the Free Software Foundation, 191541Srgrimes * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 201541Srgrimes * 211541Srgrimes * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 221541Srgrimes * or visit www.oracle.com if you need additional information or have any 231541Srgrimes * questions. 241541Srgrimes */ 251541Srgrimes 261541Srgrimespackage jdk.nashorn.internal.runtime.linker; 271541Srgrimes 281541Srgrimesimport static jdk.nashorn.internal.lookup.Lookup.MH; 291541Srgrimes 301541Srgrimesimport java.lang.invoke.MethodHandle; 311541Srgrimesimport java.lang.invoke.MethodHandles; 321541Srgrimesimport java.lang.invoke.MethodType; 331541Srgrimesimport java.lang.reflect.Modifier; 341541Srgrimesimport java.security.AccessControlContext; 351541Srgrimesimport java.security.AccessController; 361541Srgrimesimport java.security.PrivilegedAction; 371541Srgrimesimport java.util.Collection; 381541Srgrimesimport java.util.Deque; 391541Srgrimesimport java.util.List; 401541Srgrimesimport java.util.Map; 411541Srgrimesimport java.util.Queue; 421541Srgrimesimport java.util.function.Supplier; 431541Srgrimesimport javax.script.Bindings; 441541Srgrimesimport jdk.internal.dynalink.CallSiteDescriptor; 451541Srgrimesimport jdk.internal.dynalink.linker.ConversionComparator; 461541Srgrimesimport jdk.internal.dynalink.linker.GuardedInvocation; 471541Srgrimesimport jdk.internal.dynalink.linker.GuardingTypeConverterFactory; 481541Srgrimesimport jdk.internal.dynalink.linker.LinkRequest; 491541Srgrimesimport jdk.internal.dynalink.linker.LinkerServices; 501541Srgrimesimport jdk.internal.dynalink.linker.TypeBasedGuardingDynamicLinker; 511541Srgrimesimport jdk.internal.dynalink.linker.support.Guards; 521541Srgrimesimport jdk.internal.dynalink.linker.support.Lookup; 531541Srgrimesimport jdk.nashorn.api.scripting.JSObject; 541541Srgrimesimport jdk.nashorn.api.scripting.ScriptObjectMirror; 5552150Smarcelimport jdk.nashorn.api.scripting.ScriptUtils; 561541Srgrimesimport jdk.nashorn.internal.objects.NativeArray; 5752150Smarcelimport jdk.nashorn.internal.runtime.AccessControlContextFactory; 581541Srgrimesimport jdk.nashorn.internal.runtime.JSType; 591541Srgrimesimport jdk.nashorn.internal.runtime.ListAdapter; 601541Srgrimesimport jdk.nashorn.internal.runtime.ScriptFunction; 6152150Smarcelimport jdk.nashorn.internal.runtime.ScriptObject; 621541Srgrimesimport jdk.nashorn.internal.runtime.Undefined; 631541Srgrimes 641541Srgrimes/** 651541Srgrimes * This is the main dynamic linker for Nashorn. It is used for linking all {@link ScriptObject} and its subclasses (this 661541Srgrimes * includes {@link ScriptFunction} and its subclasses) as well as {@link Undefined}. 671541Srgrimes */ 681541Srgrimesfinal class NashornLinker implements TypeBasedGuardingDynamicLinker, GuardingTypeConverterFactory, ConversionComparator { 691541Srgrimes private static final AccessControlContext GET_LOOKUP_PERMISSION_CONTEXT = 701541Srgrimes AccessControlContextFactory.createAccessControlContext(CallSiteDescriptor.GET_LOOKUP_PERMISSION); 711541Srgrimes 721541Srgrimes private static final ClassValue<MethodHandle> ARRAY_CONVERTERS = new ClassValue<MethodHandle>() { 731541Srgrimes @Override 741541Srgrimes protected MethodHandle computeValue(final Class<?> type) { 751541Srgrimes return createArrayConverter(type); 761541Srgrimes } 771541Srgrimes }; 781541Srgrimes 791541Srgrimes /** 801541Srgrimes * Returns true if {@code ScriptObject} is assignable from {@code type}, or it is {@code Undefined}. 811541Srgrimes */ 821541Srgrimes @Override 831541Srgrimes public boolean canLinkType(final Class<?> type) { 841541Srgrimes return canLinkTypeStatic(type); 851541Srgrimes } 861541Srgrimes 871541Srgrimes static boolean canLinkTypeStatic(final Class<?> type) { 881541Srgrimes return ScriptObject.class.isAssignableFrom(type) || Undefined.class == type; 891541Srgrimes } 901541Srgrimes 911541Srgrimes @Override 921541Srgrimes public GuardedInvocation getGuardedInvocation(final LinkRequest request, final LinkerServices linkerServices) throws Exception { 931541Srgrimes final Object self = request.getReceiver(); 941541Srgrimes final CallSiteDescriptor desc = request.getCallSiteDescriptor(); 951541Srgrimes 961541Srgrimes if (desc.getNameTokenCount() < 2 || !"dyn".equals(desc.getNameToken(CallSiteDescriptor.SCHEME))) { 971541Srgrimes // We only support standard "dyn:*[:*]" operations 981541Srgrimes return null; 991541Srgrimes } 1001541Srgrimes 1011541Srgrimes return Bootstrap.asTypeSafeReturn(getGuardedInvocation(self, request, desc), linkerServices, desc); 1021541Srgrimes } 1031541Srgrimes 1041541Srgrimes private static GuardedInvocation getGuardedInvocation(final Object self, final LinkRequest request, final CallSiteDescriptor desc) { 1051541Srgrimes final GuardedInvocation inv; 1061541Srgrimes if (self instanceof ScriptObject) { 1071541Srgrimes inv = ((ScriptObject)self).lookup(desc, request); 1081541Srgrimes } else if (self instanceof Undefined) { 1091541Srgrimes inv = Undefined.lookup(desc); 110105950Speter } else { 1111541Srgrimes throw new AssertionError(self.getClass().getName()); // Should never reach here. 1121541Srgrimes } 1131541Srgrimes 1141541Srgrimes return inv; 1151541Srgrimes } 1161541Srgrimes 1171541Srgrimes @Override 11852150Smarcel public GuardedInvocation convertToType(final Class<?> sourceType, final Class<?> targetType, final Supplier<MethodHandles.Lookup> lookupSupplier) throws Exception { 1191541Srgrimes GuardedInvocation gi = convertToTypeNoCast(sourceType, targetType); 1201541Srgrimes if(gi == null) { 1211541Srgrimes gi = getSamTypeConverter(sourceType, targetType, lookupSupplier); 1221541Srgrimes } 1231541Srgrimes return gi == null ? null : gi.asType(MH.type(targetType, sourceType)); 1241541Srgrimes } 1251541Srgrimes 1261541Srgrimes /** 1271541Srgrimes * Main part of the implementation of {@link GuardingTypeConverterFactory#convertToType(Class, Class)} that doesn't 1281541Srgrimes * care about adapting the method signature; that's done by the invoking method. Returns either a built-in 1291541Srgrimes * conversion to primitive (or primitive wrapper) Java types or to String, or a just-in-time generated converter to 1301541Srgrimes * a SAM type (if the target type is a SAM type). 1311541Srgrimes * @param sourceType the source type 1328019Sache * @param targetType the target type 1338019Sache * @return a guarded invocation that converts from the source type to the target type. 1341541Srgrimes * @throws Exception if something goes wrong 1351541Srgrimes */ 1361541Srgrimes private static GuardedInvocation convertToTypeNoCast(final Class<?> sourceType, final Class<?> targetType) throws Exception { 1371541Srgrimes final MethodHandle mh = JavaArgumentConverters.getConverter(targetType); 1381541Srgrimes if (mh != null) { 1391541Srgrimes return new GuardedInvocation(mh, canLinkTypeStatic(sourceType) ? null : IS_NASHORN_OR_UNDEFINED_TYPE); 1401541Srgrimes } 1411541Srgrimes 1421541Srgrimes final GuardedInvocation arrayConverter = getArrayConverter(sourceType, targetType); 1431541Srgrimes if(arrayConverter != null) { 1441541Srgrimes return arrayConverter; 1451541Srgrimes } 1461541Srgrimes 1471541Srgrimes return getMirrorConverter(sourceType, targetType); 1481541Srgrimes } 1491541Srgrimes 1501541Srgrimes /** 1511541Srgrimes * Returns a guarded invocation that converts from a source type that is ScriptFunction, or a subclass or a 1521541Srgrimes * superclass of it) to a SAM type. 1531541Srgrimes * @param sourceType the source type (presumably ScriptFunction or a subclass or a superclass of it) 1541541Srgrimes * @param targetType the target type (presumably a SAM type) 1551541Srgrimes * @return a guarded invocation that converts from the source type to the target SAM type. null is returned if 1561541Srgrimes * either the source type is neither ScriptFunction, nor a subclass, nor a superclass of it, or if the target type 1571541Srgrimes * is not a SAM type. 1581541Srgrimes * @throws Exception if something goes wrong; generally, if there's an issue with creation of the SAM proxy type 1591541Srgrimes * constructor. 1601541Srgrimes */ 1611541Srgrimes private static GuardedInvocation getSamTypeConverter(final Class<?> sourceType, final Class<?> targetType, final Supplier<MethodHandles.Lookup> lookupSupplier) throws Exception { 1621549Srgrimes // If source type is more generic than ScriptFunction class, we'll need to use a guard 1631549Srgrimes final boolean isSourceTypeGeneric = sourceType.isAssignableFrom(ScriptFunction.class); 1641549Srgrimes 1651549Srgrimes if ((isSourceTypeGeneric || ScriptFunction.class.isAssignableFrom(sourceType)) && isAutoConvertibleFromFunction(targetType)) { 1662442Sdg final MethodHandle ctor = JavaAdapterFactory.getConstructor(ScriptFunction.class, targetType, getCurrentLookup(lookupSupplier)); 1672729Sdfr assert ctor != null; // if isAutoConvertibleFromFunction() returned true, then ctor must exist. 1682729Sdfr return new GuardedInvocation(ctor, isSourceTypeGeneric ? IS_SCRIPT_FUNCTION : null); 1691541Srgrimes } 17045065Salc return null; 17145065Salc } 1722297Swollman 1731541Srgrimes private static MethodHandles.Lookup getCurrentLookup(final Supplier<MethodHandles.Lookup> lookupSupplier) { 1741541Srgrimes return AccessController.doPrivileged(new PrivilegedAction<MethodHandles.Lookup>() { 1751541Srgrimes @Override 1761541Srgrimes public MethodHandles.Lookup run() { 1771541Srgrimes return lookupSupplier.get(); 1781541Srgrimes } 1791541Srgrimes }, GET_LOOKUP_PERMISSION_CONTEXT); 1801541Srgrimes } 1811541Srgrimes 1821541Srgrimes /** 1831541Srgrimes * Returns a guarded invocation that converts from a source type that is NativeArray to a Java array or List or 1841541Srgrimes * Queue or Deque or Collection type. 1851541Srgrimes * @param sourceType the source type (presumably NativeArray a superclass of it) 1861541Srgrimes * @param targetType the target type (presumably an array type, or List or Queue, or Deque, or Collection) 1871541Srgrimes * @return a guarded invocation that converts from the source type to the target type. null is returned if 1881541Srgrimes * either the source type is neither NativeArray, nor a superclass of it, or if the target type is not an array 1891541Srgrimes * type, List, Queue, Deque, or Collection. 1901541Srgrimes */ 1911541Srgrimes private static GuardedInvocation getArrayConverter(final Class<?> sourceType, final Class<?> targetType) { 19235938Sdyson final boolean isSourceTypeNativeArray = sourceType == NativeArray.class; 19335938Sdyson // If source type is more generic than NativeArray class, we'll need to use a guard 19428400Speter final boolean isSourceTypeGeneric = !isSourceTypeNativeArray && sourceType.isAssignableFrom(NativeArray.class); 19529349Speter 19612865Speter if (isSourceTypeNativeArray || isSourceTypeGeneric) { 19712865Speter final MethodHandle guard = isSourceTypeGeneric ? IS_NATIVE_ARRAY : null; 19812865Speter if(targetType.isArray()) { 19912865Speter return new GuardedInvocation(ARRAY_CONVERTERS.get(targetType), guard); 20012865Speter } else if(targetType == List.class) { 20112865Speter return new GuardedInvocation(TO_LIST, guard); 20212865Speter } else if(targetType == Deque.class) { 20312865Speter return new GuardedInvocation(TO_DEQUE, guard); 20412865Speter } else if(targetType == Queue.class) { 20512865Speter return new GuardedInvocation(TO_QUEUE, guard); 20612865Speter } else if(targetType == Collection.class) { 20725582Speter return new GuardedInvocation(TO_COLLECTION, guard); 20825582Speter } 20925582Speter } 21025582Speter return null; 21114220Speter } 21214220Speter 21329349Speter private static MethodHandle createArrayConverter(final Class<?> type) { 21424452Speter assert type.isArray(); 21524440Speter final MethodHandle converter = MH.insertArguments(JSType.TO_JAVA_ARRAY.methodHandle(), 1, type.getComponentType()); 21635938Sdyson return MH.asType(converter, converter.type().changeReturnType(type)); 21735938Sdyson } 21835938Sdyson 21935938Sdyson private static GuardedInvocation getMirrorConverter(final Class<?> sourceType, final Class<?> targetType) { 22035938Sdyson // Could've also used (targetType.isAssignableFrom(ScriptObjectMirror.class) && targetType != Object.class) but 22135938Sdyson // it's probably better to explicitly spell out the supported target types 22235938Sdyson if (targetType == Map.class || targetType == Bindings.class || targetType == JSObject.class || targetType == ScriptObjectMirror.class) { 22335938Sdyson if (ScriptObject.class.isAssignableFrom(sourceType)) { 22451138Salfred return new GuardedInvocation(CREATE_MIRROR); 22551138Salfred } else if (sourceType.isAssignableFrom(ScriptObject.class) || sourceType.isInterface()) { 22651138Salfred return new GuardedInvocation(CREATE_MIRROR, IS_SCRIPT_OBJECT); 22725537Sdfr } 22825537Sdfr } 22925537Sdfr return null; 23025537Sdfr } 23125537Sdfr 23225537Sdfr private static boolean isAutoConvertibleFromFunction(final Class<?> clazz) { 23325537Sdfr return isAbstractClass(clazz) && !ScriptObject.class.isAssignableFrom(clazz) && 23425537Sdfr JavaAdapterFactory.isAutoConvertibleFromFunction(clazz); 23525537Sdfr } 23625537Sdfr 23728400Speter /** 23856115Speter * Utility method used by few other places in the code. Tests if the class has the abstract modifier and is not an 23956115Speter * array class. For some reason, array classes have the abstract modifier set in HotSpot JVM, and we don't want to 24036034Speter * treat array classes as abstract. 24126671Sdyson * @param clazz the inspected class 24226671Sdyson * @return true if the class is abstract and is not an array type. 24326671Sdyson */ 24426671Sdyson static boolean isAbstractClass(final Class<?> clazz) { 24526671Sdyson return Modifier.isAbstract(clazz.getModifiers()) && !clazz.isArray(); 24626671Sdyson } 24726671Sdyson 24826671Sdyson 24969514Sjake @Override 25069514Sjake public Comparison compareConversion(final Class<?> sourceType, final Class<?> targetType1, final Class<?> targetType2) { 25126671Sdyson if(sourceType == NativeArray.class) { 25226671Sdyson // Prefer lists, as they're less costly to create than arrays. 25329391Sphk if(isList(targetType1)) { 25434925Sdufault if(!isList(targetType2)) { 25534925Sdufault return Comparison.TYPE_1_BETTER; 25634925Sdufault } 25734925Sdufault } else if(isList(targetType2)) { 25834925Sdufault return Comparison.TYPE_2_BETTER; 25934925Sdufault } 26034925Sdufault // Then prefer arrays 26134925Sdufault if(targetType1.isArray()) { 26235938Sdyson if(!targetType2.isArray()) { 26399856Salfred return Comparison.TYPE_1_BETTER; 26441089Speter } 26546155Sphk } else if(targetType2.isArray()) { 26651791Smarcel return Comparison.TYPE_2_BETTER; 26751791Smarcel } 268105950Speter } 26951791Smarcel if(ScriptObject.class.isAssignableFrom(sourceType)) { 270105950Speter // Prefer interfaces 27156271Srwatson if(targetType1.isInterface()) { 27256271Srwatson if(!targetType2.isInterface()) { 27356271Srwatson return Comparison.TYPE_1_BETTER; 27456271Srwatson } 27556271Srwatson } else if(targetType2.isInterface()) { 27656271Srwatson return Comparison.TYPE_2_BETTER; 27756271Srwatson } 27856271Srwatson } 27954803Srwatson return Comparison.INDETERMINATE; 28054803Srwatson } 28154803Srwatson 28254803Srwatson private static boolean isList(final Class<?> clazz) { 28355943Sjasone return clazz == List.class || clazz == Deque.class; 28456115Speter } 28556115Speter 28659288Sjlemon private static final MethodHandle IS_SCRIPT_OBJECT = Guards.isInstance(ScriptObject.class, MH.type(Boolean.TYPE, Object.class)); 28759288Sjlemon private static final MethodHandle IS_SCRIPT_FUNCTION = Guards.isInstance(ScriptFunction.class, MH.type(Boolean.TYPE, Object.class)); 28875039Srwatson private static final MethodHandle IS_NATIVE_ARRAY = Guards.isOfClass(NativeArray.class, MH.type(Boolean.TYPE, Object.class)); 28975039Srwatson 29075039Srwatson private static final MethodHandle IS_NASHORN_OR_UNDEFINED_TYPE = findOwnMH("isNashornTypeOrUndefined", Boolean.TYPE, Object.class); 29175427Srwatson private static final MethodHandle CREATE_MIRROR = findOwnMH("createMirror", Object.class, Object.class); 29283652Speter 29383796Srwatson private static final MethodHandle TO_COLLECTION; 29485891Sphk private static final MethodHandle TO_DEQUE; 29590889Sjulian private static final MethodHandle TO_LIST; 29690889Sjulian private static final MethodHandle TO_QUEUE; 297103972Sarchie static { 298103972Sarchie final MethodHandle listAdapterCreate = new Lookup(MethodHandles.lookup()).findStatic( 299103972Sarchie ListAdapter.class, "create", MethodType.methodType(ListAdapter.class, Object.class)); 300100897Srwatson TO_COLLECTION = asReturning(listAdapterCreate, Collection.class); 301100897Srwatson TO_DEQUE = asReturning(listAdapterCreate, Deque.class); 302100897Srwatson TO_LIST = asReturning(listAdapterCreate, List.class); 303100897Srwatson TO_QUEUE = asReturning(listAdapterCreate, Queue.class); 304100897Srwatson } 305100897Srwatson 30694936Smux private static MethodHandle asReturning(final MethodHandle mh, final Class<?> nrtype) { 30796084Smux return mh.asType(mh.type().changeReturnType(nrtype)); 30897372Smarcel } 30999856Salfred 310101426Srwatson @SuppressWarnings("unused") 311103575Salfred private static boolean isNashornTypeOrUndefined(final Object obj) { 312103575Salfred return obj instanceof ScriptObject || obj instanceof Undefined; 313103575Salfred } 314103575Salfred 315103575Salfred @SuppressWarnings("unused") 316103575Salfred private static Object createMirror(final Object obj) { 317103575Salfred return obj instanceof ScriptObject? ScriptUtils.wrap((ScriptObject)obj) : obj; 318103575Salfred } 319103575Salfred 320105692Srwatson private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) { 321105692Srwatson return MH.findStatic(MethodHandles.lookup(), NashornLinker.class, name, MH.type(rtype, types)); 322105692Srwatson } 323104731Srwatson} 324104731Srwatson 325104731Srwatson