author | hannesw |
Wed, 21 Mar 2018 16:55:34 +0100 | |
changeset 49275 | c639a6b33c5c |
parent 47216 | 71c04702a3d5 |
permissions | -rw-r--r-- |
24719 | 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; |
|
27 |
||
28 |
import static jdk.nashorn.internal.lookup.Lookup.MH; |
|
29 |
import static jdk.nashorn.internal.runtime.JSType.getAccessorTypeIndex; |
|
30 |
import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.isValid; |
|
31 |
||
32 |
import java.lang.invoke.MethodHandle; |
|
33 |
import java.lang.invoke.MethodHandles; |
|
34 |
import java.lang.invoke.MethodType; |
|
34447
ec4c069f9436
8141338: Move jdk.internal.dynalink package to jdk.dynalink
attila
parents:
33337
diff
changeset
|
35 |
import jdk.dynalink.CallSiteDescriptor; |
ec4c069f9436
8141338: Move jdk.internal.dynalink package to jdk.dynalink
attila
parents:
33337
diff
changeset
|
36 |
import jdk.dynalink.linker.GuardedInvocation; |
ec4c069f9436
8141338: Move jdk.internal.dynalink package to jdk.dynalink
attila
parents:
33337
diff
changeset
|
37 |
import jdk.dynalink.linker.support.TypeUtilities; |
24719 | 38 |
import jdk.nashorn.internal.codegen.types.Type; |
39 |
import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor; |
|
40 |
||
41 |
/** |
|
42 |
* Optimistic return value filters |
|
43 |
*/ |
|
44 |
public final class OptimisticReturnFilters { |
|
45 |
private static final MethodHandle[] ENSURE_INT; |
|
46 |
private static final MethodHandle[] ENSURE_NUMBER; |
|
47 |
||
34732 | 48 |
// These extend the type index constants in JSType |
49 |
private static final int VOID_TYPE_INDEX; |
|
24719 | 50 |
private static final int BOOLEAN_TYPE_INDEX; |
51 |
private static final int CHAR_TYPE_INDEX; |
|
34732 | 52 |
private static final int LONG_TYPE_INDEX; |
24719 | 53 |
private static final int FLOAT_TYPE_INDEX; |
54 |
||
55 |
static { |
|
56 |
final MethodHandle INT_DOUBLE = findOwnMH("ensureInt", int.class, double.class, int.class); |
|
57 |
ENSURE_INT = new MethodHandle[] { |
|
58 |
null, |
|
59 |
INT_DOUBLE, |
|
60 |
findOwnMH("ensureInt", int.class, Object.class, int.class), |
|
24751 | 61 |
findOwnMH("ensureInt", int.class, int.class), |
24719 | 62 |
findOwnMH("ensureInt", int.class, boolean.class, int.class), |
63 |
findOwnMH("ensureInt", int.class, char.class, int.class), |
|
34732 | 64 |
findOwnMH("ensureInt", int.class, long.class, int.class), |
24719 | 65 |
INT_DOUBLE.asType(INT_DOUBLE.type().changeParameterType(0, float.class)), |
66 |
}; |
|
67 |
||
34732 | 68 |
VOID_TYPE_INDEX = ENSURE_INT.length - 5; |
69 |
BOOLEAN_TYPE_INDEX = ENSURE_INT.length - 4; |
|
70 |
CHAR_TYPE_INDEX = ENSURE_INT.length - 3; |
|
71 |
LONG_TYPE_INDEX = ENSURE_INT.length - 2; |
|
24719 | 72 |
FLOAT_TYPE_INDEX = ENSURE_INT.length - 1; |
73 |
||
74 |
ENSURE_NUMBER = new MethodHandle[] { |
|
75 |
null, |
|
76 |
null, |
|
77 |
findOwnMH("ensureNumber", double.class, Object.class, int.class), |
|
24751 | 78 |
ENSURE_INT[VOID_TYPE_INDEX].asType(ENSURE_INT[VOID_TYPE_INDEX].type().changeReturnType(double.class)), |
24719 | 79 |
ENSURE_INT[BOOLEAN_TYPE_INDEX].asType(ENSURE_INT[BOOLEAN_TYPE_INDEX].type().changeReturnType(double.class)), |
80 |
ENSURE_INT[CHAR_TYPE_INDEX].asType(ENSURE_INT[CHAR_TYPE_INDEX].type().changeReturnType(double.class)), |
|
34732 | 81 |
findOwnMH("ensureNumber", double.class, long.class, int.class), |
24719 | 82 |
null |
83 |
}; |
|
84 |
} |
|
85 |
||
86 |
/** |
|
87 |
* Given a method handle and an expected return type, perform return value filtering |
|
88 |
* according to the optimistic type coercion rules |
|
89 |
* @param mh method handle |
|
90 |
* @param expectedReturnType expected return type |
|
91 |
* @param programPoint program point |
|
92 |
* @return filtered method |
|
93 |
*/ |
|
94 |
public static MethodHandle filterOptimisticReturnValue(final MethodHandle mh, final Class<?> expectedReturnType, final int programPoint) { |
|
95 |
if(!isValid(programPoint)) { |
|
96 |
return mh; |
|
97 |
} |
|
98 |
||
99 |
final MethodType type = mh.type(); |
|
100 |
final Class<?> actualReturnType = type.returnType(); |
|
101 |
if(TypeUtilities.isConvertibleWithoutLoss(actualReturnType, expectedReturnType)) { |
|
102 |
return mh; |
|
103 |
} |
|
104 |
||
105 |
final MethodHandle guard = getOptimisticTypeGuard(expectedReturnType, actualReturnType); |
|
24751 | 106 |
return guard == null ? mh : MH.filterReturnValue(mh, MH.insertArguments(guard, guard.type().parameterCount() - 1, programPoint)); |
24719 | 107 |
} |
108 |
||
109 |
/** |
|
110 |
* Given a guarded invocation and a callsite descriptor, perform return value filtering |
|
24751 | 111 |
* according to the optimistic type coercion rules, using the return value from the descriptor |
24719 | 112 |
* @param inv the invocation |
113 |
* @param desc the descriptor |
|
114 |
* @return filtered invocation |
|
115 |
*/ |
|
116 |
public static GuardedInvocation filterOptimisticReturnValue(final GuardedInvocation inv, final CallSiteDescriptor desc) { |
|
117 |
if(!NashornCallSiteDescriptor.isOptimistic(desc)) { |
|
118 |
return inv; |
|
119 |
} |
|
120 |
return inv.replaceMethods(filterOptimisticReturnValue(inv.getInvocation(), desc.getMethodType().returnType(), |
|
121 |
NashornCallSiteDescriptor.getProgramPoint(desc)), inv.getGuard()); |
|
122 |
} |
|
123 |
||
124 |
private static MethodHandle getOptimisticTypeGuard(final Class<?> actual, final Class<?> provable) { |
|
125 |
final MethodHandle guard; |
|
126 |
final int provableTypeIndex = getProvableTypeIndex(provable); |
|
127 |
if (actual == int.class) { |
|
128 |
guard = ENSURE_INT[provableTypeIndex]; |
|
129 |
} else if (actual == double.class) { |
|
130 |
guard = ENSURE_NUMBER[provableTypeIndex]; |
|
131 |
} else { |
|
132 |
guard = null; |
|
133 |
assert !actual.isPrimitive() : actual + ", " + provable; |
|
134 |
} |
|
135 |
if(guard != null && !(provable.isPrimitive())) { |
|
136 |
// Make sure filtering a MethodHandle(...)String works with a filter MethodHandle(Object, int)... Note that |
|
137 |
// if the return type of the method is incompatible with Number, then the guard will always throw an |
|
138 |
// UnwarrantedOperationException when invoked, but we must link it anyway as we need the guarded function to |
|
139 |
// successfully execute and return the non-convertible return value that it'll put into the thrown |
|
140 |
// UnwarrantedOptimismException. |
|
141 |
return guard.asType(guard.type().changeParameterType(0, provable)); |
|
142 |
} |
|
143 |
return guard; |
|
144 |
} |
|
145 |
||
24778
2ff5d7041566
8044638: Tidy up Nashorn codebase for code standards
attila
parents:
24751
diff
changeset
|
146 |
private static int getProvableTypeIndex(final Class<?> provable) { |
24719 | 147 |
final int accTypeIndex = getAccessorTypeIndex(provable); |
148 |
if(accTypeIndex != -1) { |
|
149 |
return accTypeIndex; |
|
150 |
} else if(provable == boolean.class) { |
|
151 |
return BOOLEAN_TYPE_INDEX; |
|
24751 | 152 |
} else if(provable == void.class) { |
153 |
return VOID_TYPE_INDEX; |
|
24719 | 154 |
} else if(provable == byte.class || provable == short.class) { |
155 |
return 0; // never needs a guard, as it's assignable to int |
|
156 |
} else if(provable == char.class) { |
|
157 |
return CHAR_TYPE_INDEX; |
|
34732 | 158 |
} else if(provable == long.class) { |
159 |
return LONG_TYPE_INDEX; |
|
24719 | 160 |
} else if(provable == float.class) { |
161 |
return FLOAT_TYPE_INDEX; |
|
162 |
} |
|
163 |
throw new AssertionError(provable.getName()); |
|
164 |
} |
|
165 |
||
166 |
//maps staticallyProvableCallSiteType to actualCallSiteType, throws exception if impossible |
|
167 |
@SuppressWarnings("unused") |
|
168 |
private static int ensureInt(final long arg, final int programPoint) { |
|
169 |
if (JSType.isRepresentableAsInt(arg)) { |
|
170 |
return (int)arg; |
|
171 |
} |
|
34732 | 172 |
throw UnwarrantedOptimismException.createNarrowest(arg, programPoint); |
24719 | 173 |
} |
174 |
||
175 |
@SuppressWarnings("unused") |
|
176 |
private static int ensureInt(final double arg, final int programPoint) { |
|
31490 | 177 |
if (JSType.isStrictlyRepresentableAsInt(arg)) { |
24719 | 178 |
return (int)arg; |
179 |
} |
|
34732 | 180 |
throw new UnwarrantedOptimismException(arg, programPoint, Type.NUMBER); |
24719 | 181 |
} |
182 |
||
183 |
/** |
|
26378
ca7093bd5fd1
8056129: AtomicInteger is treated as primitive number with optimistic compilation
attila
parents:
25865
diff
changeset
|
184 |
* Returns the argument value as an int. If the argument is not a wrapper for a primitive numeric type |
ca7093bd5fd1
8056129: AtomicInteger is treated as primitive number with optimistic compilation
attila
parents:
25865
diff
changeset
|
185 |
* with a value that can be exactly represented as an int, throw an {@link UnwarrantedOptimismException}. |
ca7093bd5fd1
8056129: AtomicInteger is treated as primitive number with optimistic compilation
attila
parents:
25865
diff
changeset
|
186 |
* This method is only public so that generated script code can use it. See {code CodeGenerator.ENSURE_INT}. |
24719 | 187 |
* @param arg the original argument. |
188 |
* @param programPoint the program point used in the exception |
|
189 |
* @return the value of the argument as an int. |
|
26378
ca7093bd5fd1
8056129: AtomicInteger is treated as primitive number with optimistic compilation
attila
parents:
25865
diff
changeset
|
190 |
* @throws UnwarrantedOptimismException if the argument is not a wrapper for a primitive numeric type with |
ca7093bd5fd1
8056129: AtomicInteger is treated as primitive number with optimistic compilation
attila
parents:
25865
diff
changeset
|
191 |
* a value that can be exactly represented as an int. |
24719 | 192 |
*/ |
193 |
public static int ensureInt(final Object arg, final int programPoint) { |
|
194 |
// NOTE: this doesn't delegate to ensureInt(double, int) as in that case if arg were a Long, it would throw a |
|
195 |
// (potentially imprecise) Double in the UnwarrantedOptimismException. This way, it will put the correct valued |
|
196 |
// Long into the exception. |
|
26378
ca7093bd5fd1
8056129: AtomicInteger is treated as primitive number with optimistic compilation
attila
parents:
25865
diff
changeset
|
197 |
if (isPrimitiveNumberWrapper(arg)) { |
24719 | 198 |
final double d = ((Number)arg).doubleValue(); |
31490 | 199 |
if (JSType.isStrictlyRepresentableAsInt(d)) { |
24719 | 200 |
return (int)d; |
201 |
} |
|
202 |
} |
|
34732 | 203 |
throw UnwarrantedOptimismException.createNarrowest(arg, programPoint); |
24719 | 204 |
} |
205 |
||
26378
ca7093bd5fd1
8056129: AtomicInteger is treated as primitive number with optimistic compilation
attila
parents:
25865
diff
changeset
|
206 |
private static boolean isPrimitiveNumberWrapper(final Object obj) { |
ca7093bd5fd1
8056129: AtomicInteger is treated as primitive number with optimistic compilation
attila
parents:
25865
diff
changeset
|
207 |
if (obj == null) { |
ca7093bd5fd1
8056129: AtomicInteger is treated as primitive number with optimistic compilation
attila
parents:
25865
diff
changeset
|
208 |
return false; |
ca7093bd5fd1
8056129: AtomicInteger is treated as primitive number with optimistic compilation
attila
parents:
25865
diff
changeset
|
209 |
} |
ca7093bd5fd1
8056129: AtomicInteger is treated as primitive number with optimistic compilation
attila
parents:
25865
diff
changeset
|
210 |
final Class<?> c = obj.getClass(); |
ca7093bd5fd1
8056129: AtomicInteger is treated as primitive number with optimistic compilation
attila
parents:
25865
diff
changeset
|
211 |
return c == Integer.class || c == Double.class || c == Long.class || |
ca7093bd5fd1
8056129: AtomicInteger is treated as primitive number with optimistic compilation
attila
parents:
25865
diff
changeset
|
212 |
c == Float.class || c == Short.class || c == Byte.class; |
ca7093bd5fd1
8056129: AtomicInteger is treated as primitive number with optimistic compilation
attila
parents:
25865
diff
changeset
|
213 |
} |
ca7093bd5fd1
8056129: AtomicInteger is treated as primitive number with optimistic compilation
attila
parents:
25865
diff
changeset
|
214 |
|
24719 | 215 |
@SuppressWarnings("unused") |
216 |
private static int ensureInt(final boolean arg, final int programPoint) { |
|
217 |
throw new UnwarrantedOptimismException(arg, programPoint, Type.OBJECT); |
|
218 |
} |
|
219 |
||
220 |
@SuppressWarnings("unused") |
|
221 |
private static int ensureInt(final char arg, final int programPoint) { |
|
222 |
throw new UnwarrantedOptimismException(arg, programPoint, Type.OBJECT); |
|
223 |
} |
|
224 |
||
24751 | 225 |
@SuppressWarnings("unused") |
226 |
private static int ensureInt(final int programPoint) { |
|
227 |
// Turns a void into UNDEFINED |
|
228 |
throw new UnwarrantedOptimismException(ScriptRuntime.UNDEFINED, programPoint, Type.OBJECT); |
|
229 |
} |
|
230 |
||
34732 | 231 |
|
232 |
@SuppressWarnings("unused") |
|
233 |
private static double ensureNumber(final long arg, final int programPoint) { |
|
234 |
if (JSType.isRepresentableAsDouble(arg)) { |
|
235 |
return (double) arg; |
|
24719 | 236 |
} |
34732 | 237 |
throw new UnwarrantedOptimismException(arg, programPoint, Type.OBJECT); |
24719 | 238 |
} |
239 |
||
240 |
/** |
|
34732 | 241 |
* Returns the argument value as a double. If the argument is not a wrapper for a primitive numeric type |
242 |
* that can be represented as double throw an {@link UnwarrantedOptimismException}. |
|
243 |
* This method is only public so that generated script code can use it. See {code CodeGenerator.ENSURE_NUMBER}. |
|
24719 | 244 |
* @param arg the original argument. |
245 |
* @param programPoint the program point used in the exception |
|
246 |
* @return the value of the argument as a double. |
|
26378
ca7093bd5fd1
8056129: AtomicInteger is treated as primitive number with optimistic compilation
attila
parents:
25865
diff
changeset
|
247 |
* @throws UnwarrantedOptimismException if the argument is not a wrapper for a primitive numeric type. |
24719 | 248 |
*/ |
249 |
public static double ensureNumber(final Object arg, final int programPoint) { |
|
34732 | 250 |
if (isPrimitiveNumberWrapper(arg) |
251 |
&& (arg.getClass() != Long.class || JSType.isRepresentableAsDouble((Long) arg))) { |
|
252 |
return ((Number) arg).doubleValue(); |
|
24719 | 253 |
} |
34732 | 254 |
throw new UnwarrantedOptimismException(arg, programPoint, Type.OBJECT); |
24719 | 255 |
} |
256 |
||
257 |
private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) { |
|
258 |
return MH.findStatic(MethodHandles.lookup(), OptimisticReturnFilters.class, name, MH.type(rtype, types)); |
|
259 |
} |
|
260 |
} |