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