Thu, 24 May 2018 16:39:31 +0800
Merge
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 */
26 package jdk.nashorn.internal.runtime.linker;
28 import static jdk.nashorn.internal.lookup.Lookup.MH;
30 import java.lang.invoke.MethodHandle;
31 import java.lang.invoke.MethodHandles;
32 import jdk.internal.dynalink.linker.ConversionComparator;
33 import jdk.internal.dynalink.linker.GuardedInvocation;
34 import jdk.internal.dynalink.linker.GuardedTypeConversion;
35 import jdk.internal.dynalink.linker.GuardingTypeConverterFactory;
36 import jdk.internal.dynalink.linker.LinkRequest;
37 import jdk.internal.dynalink.linker.LinkerServices;
38 import jdk.internal.dynalink.linker.TypeBasedGuardingDynamicLinker;
39 import jdk.internal.dynalink.support.TypeUtilities;
40 import jdk.nashorn.internal.objects.Global;
41 import jdk.nashorn.internal.runtime.ConsString;
42 import jdk.nashorn.internal.runtime.JSType;
43 import jdk.nashorn.internal.runtime.ScriptRuntime;
45 /**
46 * Internal linker for String, Boolean, and Number objects, only ever used by Nashorn engine and not exposed to other
47 * engines. It is used for treatment of strings, boolean, and numbers as JavaScript primitives. Also provides ECMAScript
48 * primitive type conversions for these types when linking to Java methods.
49 */
50 final class NashornPrimitiveLinker implements TypeBasedGuardingDynamicLinker, GuardingTypeConverterFactory, ConversionComparator {
51 private static final GuardedTypeConversion VOID_TO_OBJECT = new GuardedTypeConversion(
52 new GuardedInvocation(MethodHandles.constant(Object.class, ScriptRuntime.UNDEFINED)), true);
54 @Override
55 public boolean canLinkType(final Class<?> type) {
56 return canLinkTypeStatic(type);
57 }
59 private static boolean canLinkTypeStatic(final Class<?> type) {
60 return type == String.class || type == Boolean.class || type == ConsString.class || type == Integer.class
61 || type == Double.class || type == Float.class || type == Short.class || type == Byte.class;
62 }
64 @Override
65 public GuardedInvocation getGuardedInvocation(final LinkRequest origRequest, final LinkerServices linkerServices)
66 throws Exception {
67 final LinkRequest request = origRequest.withoutRuntimeContext(); // Nashorn has no runtime context
69 final Object self = request.getReceiver();
70 final NashornCallSiteDescriptor desc = (NashornCallSiteDescriptor) request.getCallSiteDescriptor();
72 return Bootstrap.asTypeSafeReturn(Global.primitiveLookup(request, self), linkerServices, desc);
73 }
75 /**
76 * This implementation of type converter factory will pretty much allow implicit conversions of anything to anything
77 * else that's allowed among JavaScript primitive types (string to number, boolean to string, etc.)
78 * @param sourceType the type to convert from
79 * @param targetType the type to convert to
80 * @return a conditional converter from source to target type
81 */
82 @Override
83 public GuardedTypeConversion convertToType(final Class<?> sourceType, final Class<?> targetType) {
84 final MethodHandle mh = JavaArgumentConverters.getConverter(targetType);
85 if (mh == null) {
86 if(targetType == Object.class && sourceType == void.class) {
87 return VOID_TO_OBJECT;
88 }
89 return null;
90 }
92 return new GuardedTypeConversion(new GuardedInvocation(mh, canLinkTypeStatic(sourceType) ? null : GUARD_PRIMITIVE).asType(mh.type().changeParameterType(0, sourceType)), true);
93 }
95 /**
96 * Implements the somewhat involved prioritization of JavaScript primitive types conversions. Instead of explaining
97 * it here in prose, just follow the source code comments.
98 * @param sourceType the source type to convert from
99 * @param targetType1 one candidate target type
100 * @param targetType2 another candidate target type
101 * @return one of {@link jdk.internal.dynalink.linker.ConversionComparator.Comparison} values signifying which
102 * target type should be favored for conversion.
103 */
104 @Override
105 public Comparison compareConversion(final Class<?> sourceType, final Class<?> targetType1, final Class<?> targetType2) {
106 final Class<?> wrapper1 = getWrapperTypeOrSelf(targetType1);
107 if (sourceType == wrapper1) {
108 // Source type exactly matches target 1
109 return Comparison.TYPE_1_BETTER;
110 }
111 final Class<?> wrapper2 = getWrapperTypeOrSelf(targetType2);
112 if (sourceType == wrapper2) {
113 // Source type exactly matches target 2
114 return Comparison.TYPE_2_BETTER;
115 }
117 if (Number.class.isAssignableFrom(sourceType)) {
118 // If exactly one of the targets is a number, pick it.
119 if (Number.class.isAssignableFrom(wrapper1)) {
120 if (!Number.class.isAssignableFrom(wrapper2)) {
121 return Comparison.TYPE_1_BETTER;
122 }
123 } else if (Number.class.isAssignableFrom(wrapper2)) {
124 return Comparison.TYPE_2_BETTER;
125 }
127 // If exactly one of the targets is a character, pick it. Numbers can be reasonably converted to chars using
128 // the UTF-16 values.
129 if (Character.class == wrapper1) {
130 return Comparison.TYPE_1_BETTER;
131 } else if (Character.class == wrapper2) {
132 return Comparison.TYPE_2_BETTER;
133 }
135 // For all other cases, we fall through to the next if statement - not that we repeat the condition in it
136 // too so if we entered this branch, we'll enter the below if statement too.
137 }
139 if (sourceType == String.class || sourceType == Boolean.class || Number.class.isAssignableFrom(sourceType)) {
140 // Treat wrappers as primitives.
141 final Class<?> primitiveType1 = getPrimitiveTypeOrSelf(targetType1);
142 final Class<?> primitiveType2 = getPrimitiveTypeOrSelf(targetType2);
143 // Basically, choose the widest possible primitive type. (First "if" returning TYPE_2_BETTER is correct;
144 // when faced with a choice between double and int, choose double).
145 if (TypeUtilities.isMethodInvocationConvertible(primitiveType1, primitiveType2)) {
146 return Comparison.TYPE_2_BETTER;
147 } else if (TypeUtilities.isMethodInvocationConvertible(primitiveType2, primitiveType1)) {
148 return Comparison.TYPE_1_BETTER;
149 }
150 // Ok, at this point we're out of possible number conversions, so try strings. A String can represent any
151 // value without loss, so if one of the potential targets is string, go for it.
152 if (targetType1 == String.class) {
153 return Comparison.TYPE_1_BETTER;
154 }
155 if (targetType2 == String.class) {
156 return Comparison.TYPE_2_BETTER;
157 }
158 }
160 return Comparison.INDETERMINATE;
161 }
163 private static Class<?> getPrimitiveTypeOrSelf(final Class<?> type) {
164 final Class<?> primitive = TypeUtilities.getPrimitiveType(type);
165 return primitive == null ? type : primitive;
166 }
168 private static Class<?> getWrapperTypeOrSelf(final Class<?> type) {
169 final Class<?> wrapper = TypeUtilities.getWrapperType(type);
170 return wrapper == null ? type : wrapper;
171 }
173 @SuppressWarnings("unused")
174 private static boolean isJavaScriptPrimitive(final Object o) {
175 return JSType.isString(o) || o instanceof Boolean || JSType.isNumber(o) || o == null;
176 }
178 private static final MethodHandle GUARD_PRIMITIVE = findOwnMH("isJavaScriptPrimitive", boolean.class, Object.class);
180 private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
181 return MH.findStatic(MethodHandles.lookup(), NashornPrimitiveLinker.class, name, MH.type(rtype, types));
182 }
183 }