88 import java.lang.invoke.MethodType; |
88 import java.lang.invoke.MethodType; |
89 import java.lang.invoke.SwitchPoint; |
89 import java.lang.invoke.SwitchPoint; |
90 import java.lang.invoke.WrongMethodTypeException; |
90 import java.lang.invoke.WrongMethodTypeException; |
91 import java.util.List; |
91 import java.util.List; |
92 import jdk.internal.dynalink.CallSiteDescriptor; |
92 import jdk.internal.dynalink.CallSiteDescriptor; |
|
93 import jdk.internal.dynalink.support.CatchExceptionCombinator; |
93 import jdk.internal.dynalink.support.Guards; |
94 import jdk.internal.dynalink.support.Guards; |
|
95 import jdk.nashorn.internal.runtime.options.Options; |
94 |
96 |
95 /** |
97 /** |
96 * Represents a conditionally valid method handle. It is an immutable triple of an invocation method handle, a guard |
98 * Represents a conditionally valid method handle. It is an immutable triple of an invocation method handle, a guard |
97 * method handle that defines the applicability of the invocation handle, and a switch point that can be used for |
99 * method handle that defines the applicability of the invocation handle, and a switch point that can be used for |
98 * external invalidation of the invocation handle. The invocation handle is suitable for invocation if the guard |
100 * external invalidation of the invocation handle. The invocation handle is suitable for invocation if the guard |
100 * switch point are optional; neither, one, or both can be present. |
102 * switch point are optional; neither, one, or both can be present. |
101 * |
103 * |
102 * @author Attila Szegedi |
104 * @author Attila Szegedi |
103 */ |
105 */ |
104 public class GuardedInvocation { |
106 public class GuardedInvocation { |
|
107 private static final boolean USE_FAST_REWRITE = Options.getBooleanProperty("nashorn.fastrewrite"); |
|
108 |
105 private final MethodHandle invocation; |
109 private final MethodHandle invocation; |
106 private final MethodHandle guard; |
110 private final MethodHandle guard; |
|
111 private final Class<? extends Throwable> exception; |
107 private final SwitchPoint switchPoint; |
112 private final SwitchPoint switchPoint; |
|
113 |
|
114 /** |
|
115 * Creates a new guarded invocation. This invocation is unconditional as it has no invalidations. |
|
116 * |
|
117 * @param invocation the method handle representing the invocation. Must not be null. |
|
118 * @throws NullPointerException if invocation is null. |
|
119 */ |
|
120 public GuardedInvocation(MethodHandle invocation) { |
|
121 this(invocation, null, null, null); |
|
122 } |
108 |
123 |
109 /** |
124 /** |
110 * Creates a new guarded invocation. |
125 * Creates a new guarded invocation. |
111 * |
126 * |
112 * @param invocation the method handle representing the invocation. Must not be null. |
127 * @param invocation the method handle representing the invocation. Must not be null. |
114 * it must return boolean. For some useful guards, check out the {@link Guards} class. It can be null to represent |
129 * it must return boolean. For some useful guards, check out the {@link Guards} class. It can be null to represent |
115 * an unconditional invocation, although that is unusual. |
130 * an unconditional invocation, although that is unusual. |
116 * @throws NullPointerException if invocation is null. |
131 * @throws NullPointerException if invocation is null. |
117 */ |
132 */ |
118 public GuardedInvocation(MethodHandle invocation, MethodHandle guard) { |
133 public GuardedInvocation(MethodHandle invocation, MethodHandle guard) { |
119 this(invocation, guard, null); |
134 this(invocation, guard, null, null); |
|
135 } |
|
136 |
|
137 /** |
|
138 * Creates a new guarded invocation. |
|
139 * |
|
140 * @param invocation the method handle representing the invocation. Must not be null. |
|
141 * @param switchPoint the optional switch point that can be used to invalidate this linkage. |
|
142 * @throws NullPointerException if invocation is null. |
|
143 */ |
|
144 public GuardedInvocation(MethodHandle invocation, SwitchPoint switchPoint) { |
|
145 this(invocation, null, switchPoint, null); |
120 } |
146 } |
121 |
147 |
122 /** |
148 /** |
123 * Creates a new guarded invocation. |
149 * Creates a new guarded invocation. |
124 * |
150 * |
128 * and the switch point are null, this represents an unconditional invocation, which is legal but unusual. |
154 * and the switch point are null, this represents an unconditional invocation, which is legal but unusual. |
129 * @param switchPoint the optional switch point that can be used to invalidate this linkage. |
155 * @param switchPoint the optional switch point that can be used to invalidate this linkage. |
130 * @throws NullPointerException if invocation is null. |
156 * @throws NullPointerException if invocation is null. |
131 */ |
157 */ |
132 public GuardedInvocation(MethodHandle invocation, MethodHandle guard, SwitchPoint switchPoint) { |
158 public GuardedInvocation(MethodHandle invocation, MethodHandle guard, SwitchPoint switchPoint) { |
|
159 this(invocation, guard, switchPoint, null); |
|
160 } |
|
161 |
|
162 /** |
|
163 * Creates a new guarded invocation. |
|
164 * |
|
165 * @param invocation the method handle representing the invocation. Must not be null. |
|
166 * @param guard the method handle representing the guard. Must have the same method type as the invocation, except |
|
167 * it must return boolean. For some useful guards, check out the {@link Guards} class. It can be null. If both it |
|
168 * and the switch point are null, this represents an unconditional invocation, which is legal but unusual. |
|
169 * @param switchPoint the optional switch point that can be used to invalidate this linkage. |
|
170 * @param exception the optional exception type that is expected to be thrown by the invocation and that also |
|
171 * invalidates the linkage. |
|
172 * @throws NullPointerException if invocation is null. |
|
173 */ |
|
174 public GuardedInvocation(MethodHandle invocation, MethodHandle guard, SwitchPoint switchPoint, Class<? extends Throwable> exception) { |
133 invocation.getClass(); // NPE check |
175 invocation.getClass(); // NPE check |
134 this.invocation = invocation; |
176 this.invocation = invocation; |
135 this.guard = guard; |
177 this.guard = guard; |
136 this.switchPoint = switchPoint; |
178 this.switchPoint = switchPoint; |
137 } |
179 this.exception = exception; |
138 |
180 } |
139 /** |
181 |
140 * Creates a new guarded invocation. |
|
141 * |
|
142 * @param invocation the method handle representing the invocation. Must not be null. |
|
143 * @param switchPoint the optional switch point that can be used to invalidate this linkage. |
|
144 * @param guard the method handle representing the guard. Must have the same method type as the invocation, except |
|
145 * it must return boolean. For some useful guards, check out the {@link Guards} class. It can be null. If both it |
|
146 * and the switch point are null, this represents an unconditional invocation, which is legal but unusual. |
|
147 * @throws NullPointerException if invocation is null. |
|
148 */ |
|
149 public GuardedInvocation(MethodHandle invocation, SwitchPoint switchPoint, MethodHandle guard) { |
|
150 this(invocation, guard, switchPoint); |
|
151 } |
|
152 /** |
182 /** |
153 * Returns the invocation method handle. |
183 * Returns the invocation method handle. |
154 * |
184 * |
155 * @return the invocation method handle. It will never be null. |
185 * @return the invocation method handle. It will never be null. |
156 */ |
186 */ |
172 * |
202 * |
173 * @return the switch point that can be used to invalidate the invocation handle. Can be null. |
203 * @return the switch point that can be used to invalidate the invocation handle. Can be null. |
174 */ |
204 */ |
175 public SwitchPoint getSwitchPoint() { |
205 public SwitchPoint getSwitchPoint() { |
176 return switchPoint; |
206 return switchPoint; |
|
207 } |
|
208 |
|
209 /** |
|
210 * Returns the exception type that if thrown should be used to invalidate the linkage. |
|
211 * |
|
212 * @return the exception type that if thrown should be used to invalidate the linkage. Can be null. |
|
213 */ |
|
214 public Class<? extends Throwable> getException() { |
|
215 return exception; |
177 } |
216 } |
178 |
217 |
179 /** |
218 /** |
180 * Returns true if and only if this guarded invocation has a switchpoint, and that switchpoint has been invalidated. |
219 * Returns true if and only if this guarded invocation has a switchpoint, and that switchpoint has been invalidated. |
181 * @return true if and only if this guarded invocation has a switchpoint, and that switchpoint has been invalidated. |
220 * @return true if and only if this guarded invocation has a switchpoint, and that switchpoint has been invalidated. |
204 * @param newInvocation the new invocation |
243 * @param newInvocation the new invocation |
205 * @param newGuard the new guard |
244 * @param newGuard the new guard |
206 * @return a new guarded invocation with the replaced methods and the same switch point as this invocation. |
245 * @return a new guarded invocation with the replaced methods and the same switch point as this invocation. |
207 */ |
246 */ |
208 public GuardedInvocation replaceMethods(MethodHandle newInvocation, MethodHandle newGuard) { |
247 public GuardedInvocation replaceMethods(MethodHandle newInvocation, MethodHandle newGuard) { |
209 return new GuardedInvocation(newInvocation, newGuard, switchPoint); |
248 return new GuardedInvocation(newInvocation, newGuard, switchPoint, exception); |
210 } |
249 } |
211 |
250 |
212 private GuardedInvocation replaceMethodsOrThis(MethodHandle newInvocation, MethodHandle newGuard) { |
251 private GuardedInvocation replaceMethodsOrThis(MethodHandle newInvocation, MethodHandle newGuard) { |
213 if(newInvocation == invocation && newGuard == guard) { |
252 if(newInvocation == invocation && newGuard == guard) { |
214 return this; |
253 return this; |
239 return replaceMethodsOrThis(linkerServices.asType(invocation, newType), guard == null ? null : |
278 return replaceMethodsOrThis(linkerServices.asType(invocation, newType), guard == null ? null : |
240 Guards.asType(linkerServices, guard, newType)); |
279 Guards.asType(linkerServices, guard, newType)); |
241 } |
280 } |
242 |
281 |
243 /** |
282 /** |
|
283 * Changes the type of the invocation, as if {@link LinkerServices#asTypeLosslessReturn(MethodHandle, MethodType)} was |
|
284 * applied to its invocation and {@link LinkerServices#asType(MethodHandle, MethodType)} applied to its guard, if it |
|
285 * has one (with return type changed to boolean, and parameter count potentially truncated for the guard). If the |
|
286 * invocation doesn't change its type, returns this object. |
|
287 * @param linkerServices the linker services to use for the conversion |
|
288 * @param newType the new type of the invocation. |
|
289 * @return a guarded invocation with the new type applied to it. |
|
290 */ |
|
291 public GuardedInvocation asTypeSafeReturn(LinkerServices linkerServices, MethodType newType) { |
|
292 return replaceMethodsOrThis(linkerServices.asTypeLosslessReturn(invocation, newType), guard == null ? null : |
|
293 Guards.asType(linkerServices, guard, newType)); |
|
294 } |
|
295 |
|
296 /** |
244 * Changes the type of the invocation, as if {@link MethodHandle#asType(MethodType)} was applied to its invocation |
297 * Changes the type of the invocation, as if {@link MethodHandle#asType(MethodType)} was applied to its invocation |
245 * and its guard, if it has one (with return type changed to boolean for guard). If the invocation already is of the |
298 * and its guard, if it has one (with return type changed to boolean for guard). If the invocation already is of the |
246 * required type, returns this object. |
299 * required type, returns this object. |
247 * @param desc a call descriptor whose method type is adapted. |
300 * @param desc a call descriptor whose method type is adapted. |
248 * @return a guarded invocation with the new type applied to it. |
301 * @return a guarded invocation with the new type applied to it. |
301 * @return a composite method handle. |
354 * @return a composite method handle. |
302 */ |
355 */ |
303 public MethodHandle compose(MethodHandle switchpointFallback, MethodHandle guardFallback) { |
356 public MethodHandle compose(MethodHandle switchpointFallback, MethodHandle guardFallback) { |
304 final MethodHandle guarded = |
357 final MethodHandle guarded = |
305 guard == null ? invocation : MethodHandles.guardWithTest(guard, invocation, guardFallback); |
358 guard == null ? invocation : MethodHandles.guardWithTest(guard, invocation, guardFallback); |
306 return switchPoint == null ? guarded : switchPoint.guardWithTest(guarded, switchpointFallback); |
359 final MethodHandle catchGuarded = exception == null ? guarded : catchException(guarded, exception, |
307 } |
360 MethodHandles.dropArguments(guardFallback, 0, exception)); |
308 |
361 return switchPoint == null ? catchGuarded : switchPoint.guardWithTest(catchGuarded, switchpointFallback); |
|
362 } |
|
363 |
|
364 private static MethodHandle catchException(final MethodHandle target, final Class<? extends Throwable> exType, final MethodHandle handler) { |
|
365 if(USE_FAST_REWRITE) { |
|
366 return CatchExceptionCombinator.catchException(target, exType, handler); |
|
367 } |
|
368 return MethodHandles.catchException(target, exType, handler); |
|
369 } |
309 private static void assertType(MethodHandle mh, MethodType type) { |
370 private static void assertType(MethodHandle mh, MethodType type) { |
310 if(!mh.type().equals(type)) { |
371 if(!mh.type().equals(type)) { |
311 throw new WrongMethodTypeException("Expected type: " + type + " actual type: " + mh.type()); |
372 throw new WrongMethodTypeException("Expected type: " + type + " actual type: " + mh.type()); |
312 } |
373 } |
313 } |
374 } |