1 /*
  2  * Copyright (c) 2024, 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 java.lang.reflect.code.interpreter;
 27 
 28 import java.lang.invoke.*;
 29 import java.lang.reflect.Array;
 30 import java.lang.reflect.Proxy;
 31 import java.lang.reflect.code.*;
 32 import java.lang.reflect.code.op.CoreOp;
 33 import java.lang.reflect.code.type.ArrayType;
 34 import java.lang.reflect.code.type.FieldRef;
 35 import java.lang.reflect.code.type.MethodRef;
 36 import java.lang.reflect.code.type.FunctionType;
 37 import java.lang.reflect.code.type.JavaType;
 38 import java.lang.reflect.code.TypeElement;
 39 import java.lang.reflect.code.type.VarType;
 40 import java.util.*;
 41 import java.util.stream.Collectors;
 42 import java.util.stream.Stream;
 43 
 44 import static java.util.stream.Collectors.toMap;
 45 
 46 public final class Interpreter {
 47     private Interpreter() {
 48     }
 49 
 50     @SuppressWarnings("serial")
 51     public static class InterpreterException extends RuntimeException {
 52         public InterpreterException(Throwable cause) {
 53             super(cause);
 54         }
 55     }
 56 
 57     static InterpreterException interpreterException(Throwable cause) {
 58         return new InterpreterException(cause);
 59     }
 60 
 61     record BlockContext(Block b, Map<Value, Object> values) {
 62     }
 63 
 64     static final class OpContext {
 65         final Deque<BlockContext> stack = new ArrayDeque<>();
 66         final Deque<ExceptionRegionRecord> erStack = new ArrayDeque<>();
 67 
 68         Object getValue(Value v) {
 69             // @@@ Only dominating values are accessible
 70             BlockContext bc = findContext(v);
 71             if (bc != null) {
 72                 return bc.values.get(v);
 73             } else {
 74                 throw interpreterException(new IllegalArgumentException("Undefined value: " + v));
 75             }
 76         }
 77 
 78         Object setValue(Value v, Object o) {
 79             BlockContext bc = findContext(v);
 80             if (bc != null) {
 81                 throw interpreterException(new IllegalArgumentException("Value already defined: " + v));
 82             }
 83             stack.peek().values.put(v, o);
 84             return o;
 85         }
 86 
 87         BlockContext findContext(Value v) {
 88             Optional<BlockContext> ob = stack.stream().filter(b -> b.values.containsKey(v)).findFirst();
 89             return ob.orElse(null);
 90         }
 91 
 92         boolean contains(Block.Reference s) {
 93             Block sb = s.targetBlock();
 94             return stack.stream().anyMatch(bc -> bc.b.equals(sb));
 95         }
 96 
 97         void successor(Block.Reference sb) {
 98             List<Object> sbValues = sb.arguments().stream().map(this::getValue).toList();
 99 
100             Block b = sb.targetBlock();
101             Map<Value, Object> bValues = new HashMap<>();
102             for (int i = 0; i < sbValues.size(); i++) {
103                 bValues.put(b.parameters().get(i), sbValues.get(i));
104             }
105 
106             if (contains(sb)) {
107                 // if block is already dominating pop back up from the back branch to the block
108                 // before the successor block
109                 while (!stack.peek().b.equals(sb.targetBlock())) {
110                     stack.pop();
111                 }
112                 stack.pop();
113             }
114             stack.push(new BlockContext(b, bValues));
115         }
116 
117         void successor(Block b, Map<Value, Object> bValues) {
118             stack.push(new BlockContext(b, bValues));
119         }
120 
121         void popTo(BlockContext bc) {
122             while (!stack.peek().equals(bc)) {
123                 stack.pop();
124             }
125         }
126 
127         void pushExceptionRegion(ExceptionRegionRecord erb) {
128             erStack.push(erb);
129         }
130 
131         void popExceptionRegion(CoreOp.ExceptionRegionEnter ers) {
132             if (erStack.peek().ers != ers) {
133                 // @@@ Use internal exception type
134                 throw interpreterException(new IllegalStateException("Mismatched exception regions"));
135             }
136             erStack.pop();
137         }
138 
139         Block exception(MethodHandles.Lookup l, Throwable e) {
140             // Find the first matching exception region
141             // with a catch block whose argument type is assignable-compatible to the throwable
142             ExceptionRegionRecord er;
143             Block cb = null;
144             while ((er = erStack.poll()) != null &&
145                     (cb = er.match(l, e)) == null) {
146             }
147 
148             if (er == null) {
149                 return null;
150             }
151 
152             // Pop the block context to the block defining the start of the exception region
153             popTo(er.mark);
154             return cb;
155         }
156     }
157 
158     static final class VarBox
159             implements CoreOp.Var<Object> {
160         Object value;
161 
162         public Object value() {
163             return value;
164         }
165 
166         VarBox(Object value) {
167             this.value = value;
168         }
169     }
170 
171     record ClosureRecord(CoreOp.ClosureOp op,
172                          Map<Value, Object> capturedValues) {
173     }
174 
175     record TupleRecord(List<Object> components) {
176         Object getComponent(int index) {
177             return components.get(index);
178         }
179 
180         TupleRecord with(int index, Object value) {
181             List<Object> copy = new ArrayList<>(components);
182             copy.set(index, value);
183             return new TupleRecord(copy);
184         }
185     }
186 
187     record ExceptionRegionRecord(BlockContext mark, CoreOp.ExceptionRegionEnter ers)
188             implements CoreOp.ExceptionRegion {
189         Block match(MethodHandles.Lookup l, Throwable e) {
190             for (Block.Reference catchBlock : ers.catchBlocks()) {
191                 Block target = catchBlock.targetBlock();
192                 List<Block.Parameter> args = target.parameters();
193                 if (args.size() != 1) {
194                     throw interpreterException(new IllegalStateException("Catch block must have one argument"));
195                 }
196                 TypeElement et = args.get(0).type();
197                 if (et instanceof VarType vt) {
198                     et = vt.valueType();
199                 }
200                 if (resolveToClass(l, et).isInstance(e)) {
201                     return target;
202                 }
203             }
204             return null;
205         }
206     }
207 
208     public static <T extends Op & Op.Invokable>
209     Object invoke(T op,
210                   Object... args) {
211         return invoke(MethodHandles.publicLookup(), op, args);
212     }
213 
214     public static <T extends Op & Op.Invokable>
215     Object invoke(MethodHandles.Lookup l, T op,
216                   Object... args) {
217         return invoke(l, op, new ArrayList<>(Arrays.asList(args)));
218     }
219 
220     public static <T extends Op & Op.Invokable>
221     Object invoke(MethodHandles.Lookup l, T op,
222                   Map<Value, Object> capturedValues,
223                   Object... args) {
224         return invoke(l, op, capturedValues, new ArrayList<>(Arrays.asList(args)));
225     }
226 
227     public static <T extends Op & Op.Invokable>
228     Object invoke(T op,
229                   List<Object> args) {
230         return invoke(MethodHandles.publicLookup(), op, args);
231     }
232 
233     public static <T extends Op & Op.Invokable>
234     Object invoke(T op,
235                   Map<Value, Object> capturedValues,
236                   List<Object> args) {
237         return invoke(MethodHandles.publicLookup(), op, capturedValues, args);
238     }
239 
240     public static <T extends Op & Op.Invokable>
241     Object invoke(MethodHandles.Lookup l, T op,
242                   List<Object> args) {
243         return invoke(l, op, Map.of(), args);
244     }
245 
246     public static <T extends Op & Op.Invokable>
247     Object invoke(MethodHandles.Lookup l, T invokableOp,
248                   Map<Value, Object> capturedValues,
249                   List<Object> args) {
250         Body r = invokableOp.bodies().get(0);
251         return invoke(l, r, capturedValues, new OpContext(), args);
252 
253     }
254     static Object invoke(MethodHandles.Lookup l, Body r,
255                                  Map<Value, Object> capturedValues, OpContext oc,
256                                  List<Object> args) {
257         return invoke(l,r.entryBlock(), capturedValues, oc, args);
258     }
259 
260     private static Object invoke(MethodHandles.Lookup l, Block first,
261                   Map<Value, Object> capturedValues, OpContext oc,
262                   List<Object> args) {
263 
264         if (args.size() != first.parameters().size()) {
265             throw interpreterException(new IllegalArgumentException("Incorrect number of arguments"));
266         }
267         Map<Value, Object> values = new HashMap<>();
268         for (int i = 0; i < first.parameters().size(); i++) {
269             values.put(first.parameters().get(i), args.get(i));
270         }
271 
272         // Note that first block cannot have any successors so the queue will have at least one entry
273         oc.stack.push(new BlockContext(first, values));
274         capturedValues.forEach(oc::setValue);
275         while (true) {
276             BlockContext bc = oc.stack.peek();
277 
278             // Execute all but the terminating operation
279             int nops = bc.b.ops().size();
280             try {
281                 for (int i = 0; i < nops - 1; i++) {
282                     Op op = bc.b.ops().get(i);
283                     assert !(op instanceof Op.Terminating) : op.opName();
284 
285                     Object result = exec(l, oc, op);
286                     oc.setValue(op.result(), result);
287                 }
288             } catch (InterpreterException e) {
289                 throw e;
290             } catch (Throwable t) {
291                 processThrowable(oc, l, t);
292                 continue;
293             }
294 
295             // Execute the terminating operation
296             Op to = bc.b.terminatingOp();
297             if (to instanceof CoreOp.ConditionalBranchOp cb) {
298                 boolean p;
299                 Object bop = oc.getValue(cb.predicate());
300                 if (bop instanceof Boolean bp) {
301                     p = bp;
302                 } else if (bop instanceof Integer ip) {
303                     // @@@ This is required when lifting up from bytecode, since boolean values
304                     // are erased to int values, abd the bytecode lifting implementation is not currently
305                     // sophisticated enough to recover the type information
306                     p = ip != 0;
307                 } else {
308                     throw interpreterException(
309                             new UnsupportedOperationException("Unsupported type input to operation: " + cb));
310                 }
311                 Block.Reference sb = p ? cb.trueBranch() : cb.falseBranch();
312                 oc.successor(sb);
313             } else if (to instanceof CoreOp.BranchOp b) {
314                 Block.Reference sb = b.branch();
315 
316                 oc.successor(sb);
317             } else if (to instanceof CoreOp.ThrowOp _throw) {
318                 Throwable t = (Throwable) oc.getValue(_throw.argument());
319                 processThrowable(oc, l, t);
320             } else if (to instanceof CoreOp.ReturnOp ret) {
321                 Value rv = ret.returnValue();
322                 return rv == null ? null : oc.getValue(rv);
323             } else if (to instanceof CoreOp.YieldOp yop) {
324                 Value yv = yop.yieldValue();
325                 return yv == null ? null : oc.getValue(yv);
326             } else if (to instanceof CoreOp.ExceptionRegionEnter ers) {
327                 var er = new ExceptionRegionRecord(oc.stack.peek(), ers);
328                 oc.setValue(ers.result(), er);
329 
330                 oc.pushExceptionRegion(er);
331 
332                 oc.successor(ers.start());
333             } else if (to instanceof CoreOp.ExceptionRegionExit ere) {
334                 oc.popExceptionRegion(ere.regionStart());
335 
336                 oc.successor(ere.end());
337             } else {
338                 throw interpreterException(
339                         new UnsupportedOperationException("Unsupported terminating operation: " + to.opName()));
340             }
341         }
342     }
343 
344     static <T extends Op & Op.Invokable>
345     Object interpretBody(MethodHandles.Lookup l, Body r,
346                          OpContext oc) {
347         return invoke(l, r, Map.of(), oc, List.of());
348     }
349 
350     static void processThrowable(OpContext oc, MethodHandles.Lookup l, Throwable t) {
351         // Find a matching catch block
352         Block cb = oc.exception(l, t);
353         if (cb == null) {
354             // If there is no matching catch bock then rethrow back to the caller
355             eraseAndThrow(t);
356             throw new InternalError("should not reach here");
357         }
358 
359         // Add a new block context to the catch block with the exception as the argument
360         Map<Value, Object> bValues = new HashMap<>();
361         Block.Parameter eArg = cb.parameters().get(0);
362         if (eArg.type() instanceof VarType) {
363             bValues.put(eArg, new VarBox(t));
364         } else {
365             bValues.put(eArg, t);
366         }
367         oc.successor(cb, bValues);
368     }
369 
370 
371 
372     @SuppressWarnings("unchecked")
373     public static <E extends Throwable> void eraseAndThrow(Throwable e) throws E {
374         throw (E) e;
375     }
376 
377     static Object exec(MethodHandles.Lookup l, OpContext oc, Op o) {
378         if (o instanceof CoreOp.ConstantOp co) {
379             if (co.resultType().equals(JavaType.J_L_CLASS)) {
380                 return resolveToClass(l, (JavaType) co.value());
381             } else {
382                 return co.value();
383             }
384         } else if (o instanceof CoreOp.FuncCallOp fco) {
385             String name = fco.funcName();
386 
387             // Find top-level op
388             Op top = fco;
389             while (top.ancestorBody() != null) {
390                 top = top.ancestorBody().parentOp();
391             }
392 
393             // Ensure top-level op is a module and function name
394             // is in the module's function table
395             if (top instanceof CoreOp.ModuleOp mop) {
396                 CoreOp.FuncOp funcOp = mop.functionTable().get(name);
397                 if (funcOp == null) {
398                     throw interpreterException(
399                             new IllegalStateException
400                                     ("Function " + name + " cannot be resolved: not in module's function table"));
401                 }
402 
403                 Object[] values = o.operands().stream().map(oc::getValue).toArray();
404                 return Interpreter.invoke(funcOp, values);
405             } else {
406                 throw interpreterException(
407                         new IllegalStateException(
408                                 "Function " + name + " cannot be resolved: top level op is not a module"));
409             }
410         } else if (o instanceof CoreOp.InvokeOp co) {
411             MethodHandle mh;
412             if (co.hasReceiver()) {
413                 mh = methodHandle(l, co.invokeDescriptor());
414             } else {
415                 mh = methodStaticHandle(l, co.invokeDescriptor());
416             }
417             MethodType target = resolveToMethodType(l, o.opType());
418             mh = mh.asType(target).asFixedArity();
419             Object[] values = o.operands().stream().map(oc::getValue).toArray();
420             return invoke(mh, values);
421         } else if (o instanceof CoreOp.NewOp no) {
422             Object[] values = o.operands().stream().map(oc::getValue).toArray();
423             JavaType nType = (JavaType) no.constructorType().returnType();
424             if (nType instanceof ArrayType at) {
425                 if (values.length > at.dimensions()) {
426                     throw interpreterException(new IllegalArgumentException("Bad constructor NewOp: " + no));
427                 }
428                 int[] lengths = Stream.of(values).mapToInt(v -> (int) v).toArray();
429                 for (int length : lengths) {
430                     nType = ((ArrayType)nType).componentType();
431                 }
432                 return Array.newInstance(resolveToClass(l, nType), lengths);
433             } else {
434                 MethodHandle mh = constructorHandle(l, no.constructorType());
435                 return invoke(mh, values);
436             }
437         } else if (o instanceof CoreOp.QuotedOp qo) {
438             Map<Value, Object> capturedValues = qo.capturedValues().stream()
439                     .collect(toMap(v -> v, oc::getValue, (v, _) -> v, LinkedHashMap::new));
440             return new Quoted(qo.quotedOp(), capturedValues);
441         } else if (o instanceof CoreOp.LambdaOp lo) {
442             Map<Value, Object> capturedValues = lo.capturedValues().stream()
443                     .collect(toMap(v -> v, oc::getValue, (v, _) -> v, LinkedHashMap::new));
444             Class<?> fi = resolveToClass(l, lo.functionalInterface());
445 
446             MethodHandle fProxy = INVOKE_LAMBDA_MH.bindTo(l).bindTo(lo).bindTo(capturedValues)
447                     .asCollector(Object[].class, lo.body().entryBlock().parameters().size());
448             Object fiInstance = MethodHandleProxies.asInterfaceInstance(fi, fProxy);
449 
450             // If a quotable lambda proxy again to implement Quotable
451             if (Quotable.class.isAssignableFrom(fi)) {
452                 return Proxy.newProxyInstance(l.lookupClass().getClassLoader(), new Class<?>[]{fi},
453                         (_, method, args) -> {
454                             if (method.getDeclaringClass() == Quotable.class) {
455                                 // Implement Quotable::quoted
456                                 return new Quoted(lo, capturedValues);
457                             } else {
458                                 // Delegate to FI instance
459                                 return method.invoke(fiInstance, args);
460                             }
461                         });
462             } else {
463                 return fiInstance;
464             }
465         } else if (o instanceof CoreOp.ClosureOp co) {
466             Map<Value, Object> capturedValues = co.capturedValues().stream()
467                     .collect(toMap(v -> v, oc::getValue));
468             return new ClosureRecord(co, capturedValues);
469         } else if (o instanceof CoreOp.ClosureCallOp cco) {
470             List<Object> values = o.operands().stream().map(oc::getValue).toList();
471             ClosureRecord cr = (ClosureRecord) values.get(0);
472 
473             return Interpreter.invoke(l, cr.op(), cr.capturedValues, values.subList(1, values.size()));
474         } else if (o instanceof CoreOp.VarOp vo) {
475             return new VarBox(oc.getValue(o.operands().get(0)));
476         } else if (o instanceof CoreOp.VarAccessOp.VarLoadOp vlo) {
477             // Cast to CoreOp.Var, since the instance may have originated as an external instance
478             // via a captured value map
479             CoreOp.Var<?> vb = (CoreOp.Var<?>) oc.getValue(o.operands().get(0));
480             return vb.value();
481         } else if (o instanceof CoreOp.VarAccessOp.VarStoreOp vso) {
482             VarBox vb = (VarBox) oc.getValue(o.operands().get(0));
483             vb.value = oc.getValue(o.operands().get(1));
484             return null;
485         } else if (o instanceof CoreOp.TupleOp to) {
486             List<Object> values = o.operands().stream().map(oc::getValue).toList();
487             return new TupleRecord(values);
488         } else if (o instanceof CoreOp.TupleLoadOp tlo) {
489             TupleRecord tb = (TupleRecord) oc.getValue(o.operands().get(0));
490             return tb.getComponent(tlo.index());
491         } else if (o instanceof CoreOp.TupleWithOp two) {
492             TupleRecord tb = (TupleRecord) oc.getValue(o.operands().get(0));
493             return tb.with(two.index(), oc.getValue(o.operands().get(1)));
494         } else if (o instanceof CoreOp.FieldAccessOp.FieldLoadOp fo) {
495             if (fo.operands().isEmpty()) {
496                 VarHandle vh = fieldStaticHandle(l, fo.fieldDescriptor());
497                 return vh.get();
498             } else {
499                 Object v = oc.getValue(o.operands().get(0));
500                 VarHandle vh = fieldHandle(l, fo.fieldDescriptor());
501                 return vh.get(v);
502             }
503         } else if (o instanceof CoreOp.FieldAccessOp.FieldStoreOp fo) {
504             if (fo.operands().size() == 1) {
505                 Object v = oc.getValue(o.operands().get(0));
506                 VarHandle vh = fieldStaticHandle(l, fo.fieldDescriptor());
507                 vh.set(v);
508             } else {
509                 Object r = oc.getValue(o.operands().get(0));
510                 Object v = oc.getValue(o.operands().get(1));
511                 VarHandle vh = fieldHandle(l, fo.fieldDescriptor());
512                 vh.set(r, v);
513             }
514             return null;
515         } else if (o instanceof CoreOp.InstanceOfOp io) {
516             Object v = oc.getValue(o.operands().get(0));
517             return isInstance(l, io.type(), v);
518         } else if (o instanceof CoreOp.CastOp co) {
519             Object v = oc.getValue(o.operands().get(0));
520             return cast(l, co.type(), v);
521         } else if (o instanceof CoreOp.ArrayLengthOp) {
522             Object a = oc.getValue(o.operands().get(0));
523             return Array.getLength(a);
524         } else if (o instanceof CoreOp.ArrayAccessOp.ArrayLoadOp) {
525             Object a = oc.getValue(o.operands().get(0));
526             Object index = oc.getValue(o.operands().get(1));
527             return Array.get(a, (int) index);
528         } else if (o instanceof CoreOp.ArrayAccessOp.ArrayStoreOp) {
529             Object a = oc.getValue(o.operands().get(0));
530             Object index = oc.getValue(o.operands().get(1));
531             Object v = oc.getValue(o.operands().get(2));
532             Array.set(a, (int) index, v);
533             return null;
534         } else if (o instanceof CoreOp.ArithmeticOperation || o instanceof CoreOp.TestOperation) {
535             MethodHandle mh = opHandle(o.opName(), o.opType());
536             Object[] values = o.operands().stream().map(oc::getValue).toArray();
537             return invoke(mh, values);
538         } else if (o instanceof CoreOp.ConvOp) {
539             MethodHandle mh = opHandle(o.opName() + "_" + o.opType().returnType(), o.opType());
540             Object[] values = o.operands().stream().map(oc::getValue).toArray();
541             return invoke(mh, values);
542         } else if (o instanceof CoreOp.AssertOp _assert) {
543             //Note: The nature of asserts and munged bodies may require a re-visiting.
544             //This code seems to work without poisoning contexts. See TestAssert.java in tests for relevant test coverage.
545             Body testBody = _assert.bodies.get(0);
546             boolean testResult = (boolean) interpretBody(l, testBody, oc);
547             if (!testResult) {
548                 if (_assert.bodies.size() > 1) {
549                     Body messageBody = _assert.bodies.get(1);
550                     String message = String.valueOf(interpretBody(l, messageBody, oc));
551                     throw new AssertionError(message);
552                 } else {
553                     throw new AssertionError();
554                 }
555             }
556             return null;
557         } else if (o instanceof CoreOp.ConcatOp) {
558             return o.operands().stream()
559                     .map(oc::getValue)
560                     .map(String::valueOf)
561                     .collect(Collectors.joining());
562         } else {
563             throw interpreterException(
564                     new UnsupportedOperationException("Unsupported operation: " + o.opName()));
565         }
566     }
567 
568     static final MethodHandle INVOKE_LAMBDA_MH;
569     static {
570         try {
571             INVOKE_LAMBDA_MH = MethodHandles.lookup().findStatic(Interpreter.class, "invokeLambda",
572                     MethodType.methodType(Object.class, MethodHandles.Lookup.class,
573                             CoreOp.LambdaOp.class, Map.class, Object[].class));
574         } catch (Throwable t) {
575             throw new InternalError(t);
576         }
577     }
578 
579     static Object invokeLambda(MethodHandles.Lookup l, CoreOp.LambdaOp op, Map<Value, Object> capturedValues, Object[] args) {
580         return invoke(l, op, capturedValues, args);
581     }
582 
583     static MethodHandle opHandle(String opName, FunctionType ft) {
584         MethodType mt = resolveToMethodType(MethodHandles.lookup(), ft).erase();
585         try {
586             return MethodHandles.lookup().findStatic(InvokableLeafOps.class, opName, mt);
587         } catch (NoSuchMethodException | IllegalAccessException e) {
588             throw interpreterException(e);
589         }
590     }
591 
592     static MethodHandle methodStaticHandle(MethodHandles.Lookup l, MethodRef d) {
593         return resolveToMethodHandle(l, d);
594     }
595 
596     static MethodHandle methodHandle(MethodHandles.Lookup l, MethodRef d) {
597         return resolveToMethodHandle(l, d);
598     }
599 
600     static MethodHandle constructorHandle(MethodHandles.Lookup l, FunctionType ft) {
601         MethodType mt = resolveToMethodType(l, ft);
602 
603         if (mt.returnType().isArray()) {
604             if (mt.parameterCount() != 1 || mt.parameterType(0) != int.class) {
605                 throw interpreterException(new IllegalArgumentException("Bad constructor descriptor: " + ft));
606             }
607             return MethodHandles.arrayConstructor(mt.returnType());
608         } else {
609             try {
610                 return l.findConstructor(mt.returnType(), mt.changeReturnType(void.class));
611             } catch (NoSuchMethodException | IllegalAccessException e) {
612                 throw interpreterException(e);
613             }
614         }
615     }
616 
617     static VarHandle fieldStaticHandle(MethodHandles.Lookup l, FieldRef d) {
618         return resolveToVarHandle(l, d);
619     }
620 
621     static VarHandle fieldHandle(MethodHandles.Lookup l, FieldRef d) {
622         return resolveToVarHandle(l, d);
623     }
624 
625     static Object isInstance(MethodHandles.Lookup l, TypeElement d, Object v) {
626         Class<?> c = resolveToClass(l, d);
627         return c.isInstance(v);
628     }
629 
630     static Object cast(MethodHandles.Lookup l, TypeElement d, Object v) {
631         Class<?> c = resolveToClass(l, d);
632         return c.cast(v);
633     }
634 
635     static MethodHandle resolveToMethodHandle(MethodHandles.Lookup l, MethodRef d) {
636         try {
637             return d.resolveToHandle(l);
638         } catch (ReflectiveOperationException e) {
639             throw interpreterException(e);
640         }
641     }
642 
643     static VarHandle resolveToVarHandle(MethodHandles.Lookup l, FieldRef d) {
644         try {
645             return d.resolveToHandle(l);
646         } catch (ReflectiveOperationException e) {
647             throw interpreterException(e);
648         }
649     }
650 
651     public static MethodType resolveToMethodType(MethodHandles.Lookup l, FunctionType ft) {
652         try {
653             return MethodRef.toNominalDescriptor(ft).resolveConstantDesc(l);
654         } catch (ReflectiveOperationException e) {
655             throw interpreterException(e);
656         }
657     }
658 
659     public static Class<?> resolveToClass(MethodHandles.Lookup l, TypeElement d) {
660         try {
661             if (d instanceof JavaType jt) {
662                 return (Class<?>)jt.erasure().resolve(l);
663             } else {
664                 throw new ReflectiveOperationException();
665             }
666         } catch (ReflectiveOperationException e) {
667             throw interpreterException(e);
668         }
669     }
670 
671     static Object invoke(MethodHandle m, Object... args) {
672         try {
673             return m.invokeWithArguments(args);
674         } catch (RuntimeException | Error e) {
675             throw e;
676         } catch (Throwable e) {
677             eraseAndThrow(e);
678             throw new InternalError("should not reach here");
679         }
680     }
681 }