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 }