diff --git a/src/jdk.incubator.code/share/classes/jdk/incubator/code/dialect/java/JavaOp.java b/src/jdk.incubator.code/share/classes/jdk/incubator/code/dialect/java/JavaOp.java index d2102bf8111..ef67004f80a 100644 --- a/src/jdk.incubator.code/share/classes/jdk/incubator/code/dialect/java/JavaOp.java +++ b/src/jdk.incubator.code/share/classes/jdk/incubator/code/dialect/java/JavaOp.java @@ -667,7 +667,7 @@ public InvokeOp transform(CopyContext cc, OpTransformer ot) { static void validateArgCount(InvokeKind invokeKind, boolean isVarArgs, MethodRef invokeDescriptor, List operands) { int paramCount = invokeDescriptor.type().parameterTypes().size(); - int argCount = operands.size() - (invokeKind == InvokeKind.STATIC ? 0 : 1); + int argCount = operands.size() - (invokeKind == InvokeKind.STATIC || invokeDescriptor.isConstructor() ? 0 : 1); if ((!isVarArgs && argCount != paramCount) || argCount < paramCount - 1) { throw new IllegalArgumentException(invokeKind + " " + isVarArgs + " " + invokeDescriptor); @@ -4950,6 +4950,70 @@ public TypeElement resultType() { } } + @OpDeclaration(ClassDecOp.NAME) + public static final class ClassDecOp extends JavaOp + implements Op.Nested { + + static final String NAME = "class.dec"; + static final String ATTRIBUTE_CLASS_TYPE = NAME + ".type"; + + private final ClassType classType; + private final Body fieldsAndMethods; + + ClassDecOp(ExternalizedOp def) { + // Required attribute + ClassType classType = def.extractAttributeValue(ATTRIBUTE_CLASS_TYPE, + true, v -> switch (v) { + case ClassType ct -> ct; + case null, default -> + throw new UnsupportedOperationException("Unsupported class type value:" + v); + }); + + Body.Builder fieldsAndMethods = def.bodyDefinitions().get(0); + + this(classType, fieldsAndMethods); + } + + ClassDecOp(ClassType classType, Body.Builder fieldsAndMethods) { + super(List.of()); + + this.classType = classType; + this.fieldsAndMethods = fieldsAndMethods.build(this); + } + + ClassDecOp(ClassDecOp that, CopyContext cc, OpTransformer ot) { + super(that, cc); + + this.classType = that.classType; + this.fieldsAndMethods = that.fieldsAndMethods.transform(cc, ot).build(this); + } + + @Override + public Op transform(CopyContext cc, OpTransformer ot) { + return new ClassDecOp(this, cc, ot); + } + + @Override + public TypeElement resultType() { + // @@@ for now + return VOID; + } + + @Override + public List bodies() { + return List.of(fieldsAndMethods); + } + + @Override + public Map externalize() { + return Map.of("", classType); + } + } + + public static ClassDecOp classDecOp(ClassType classType, Body.Builder fieldsAndMethods) { + return new ClassDecOp(classType, fieldsAndMethods); + } + static Op createOp(ExternalizedOp def) { Op op = switch (def.name()) { case "add" -> new AddOp(def); @@ -5012,6 +5076,11 @@ static Op createOp(ExternalizedOp def) { case "sub" -> new SubOp(def); case "throw" -> new ThrowOp(def); case "xor" -> new XorOp(def); + case "class.dec" -> new ClassDecOp(def); + // @@@ better error reporting + // instead of: + //Caused by: java.lang.NullPointerException: Cannot invoke "jdk.incubator.code.Op.result()" because "op" is null + //at jdk.incubator.code/jdk.incubator.code.Block$Builder.op(Block.java:688) default -> null; }; if (op != null) { diff --git a/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/ReflectMethods.java b/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/ReflectMethods.java index 2f75f6fdfb8..c6e490f4b8e 100644 --- a/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/ReflectMethods.java +++ b/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/ReflectMethods.java @@ -438,12 +438,21 @@ class BodyScanner extends TreeScannerPrev { private Type pt = Type.noType; private final boolean isQuoted; private Type bodyTarget; - private final Map> localCaptures = new HashMap<>(); + private Map> localCaptures = new HashMap<>(); BodyScanner(JCMethodDecl tree) { + this(tree, null); + } + + BodyScanner(JCMethodDecl md, BodyStack stack) { + this(md, new LinkedHashMap<>(), stack); + } + + BodyScanner(JCMethodDecl tree, Map> localCaptures, BodyStack parent) { this.tree = tree; this.name = tree.name; this.isQuoted = false; + this.localCaptures = localCaptures; List parameters = new ArrayList<>(); int blockArgOffset = 0; @@ -460,7 +469,7 @@ class BodyScanner extends TreeScannerPrev { FunctionType bodyType = CoreType.functionType( typeToTypeElement(tree.sym.type.getReturnType()), parameters); - this.stack = this.top = new BodyStack(null, tree.body, bodyType); + this.stack = this.top = new BodyStack(parent, tree.body, bodyType); // @@@ this as local variable? (it can never be stored to) for (int i = 0 ; i < tree.params.size() ; i++) { @@ -1009,7 +1018,15 @@ public void visitIdent(JCIdent tree) { if (sym.isStatic()) { result = append(JavaOp.fieldLoad(resultType, fr)); } else { - result = append(JavaOp.fieldLoad(resultType, fr, thisValue())); + // field owner sym is mapped to a value that represents the receiver + // by default, the receiver is the @CodeReflection method class instance + Value receiver; + try { + receiver = loadVar(tree.sym.owner); + } catch (NoSuchElementException e) { + receiver = thisValue(); + } + result = append(JavaOp.fieldLoad(resultType, fr, receiver)); // instead look for a symbol of the same type as field ref, in stacks } } } @@ -1061,7 +1078,13 @@ public void visitSelect(JCFieldAccess tree) { switch (sym.getKind()) { case FIELD, ENUM_CONSTANT -> { if (sym.name.equals(names._this) || sym.name.equals(names._super)) { - result = thisValue(); + try { + // e.g. Foo.this + // we will load the value mapped to Foo symbol + result = loadVar(tree.selected.type.tsym); + } catch (NoSuchElementException e) { + result = thisValue(); + } } else { FieldRef fr = symbolToFieldRef(sym, qualifierTarget.hasTag(NONE) ? tree.selected.type : qualifierTarget); @@ -1109,7 +1132,9 @@ public void visitApply(JCTree.JCMethodInvocation tree) { Symbol sym = access.sym; List args = new ArrayList<>(); JavaOp.InvokeOp.InvokeKind ik; - if (!sym.isStatic()) { + if (sym.isConstructor()) { + ik = JavaOp.InvokeOp.InvokeKind.SUPER; + } else if (!sym.isStatic()) { ik = JavaOp.InvokeOp.InvokeKind.INSTANCE; args.add(thisValue()); } else { @@ -1119,6 +1144,10 @@ public void visitApply(JCTree.JCMethodInvocation tree) { args.addAll(scanMethodArguments(tree.args, tree.meth.type, tree.varargsElement)); MethodRef mr = symbolToMethodRef(sym, symbolSiteType(sym)); + if (sym.isConstructor()) { + // the above mr will have return type void + mr = MethodRef.constructor(mr.refType(), mr.type().parameterTypes()); + } Value res = append(JavaOp.invoke(ik, tree.varargsElement != null, typeToTypeElement(meth.type.getReturnType()), mr, args)); if (sym.type.getReturnType().getTag() != TypeTag.VOID) { @@ -1356,7 +1385,8 @@ public void visitNewClass(JCTree.JCNewClass tree) { Type type = tree.type; Type outer = type.getEnclosingType(); List args = new ArrayList<>(); - if (!outer.hasTag(TypeTag.NONE)) { + // do this for inner class but not for local class + if (!tree.type.tsym.isDirectlyOrIndirectlyLocal() && !outer.hasTag(TypeTag.NONE)) { // Obtain outer value for inner class, and add as first argument JCTree.JCExpression encl = tree.encl; Value outerInstance; @@ -1369,6 +1399,19 @@ public void visitNewClass(JCTree.JCNewClass tree) { argtypes.add(outerInstance.type()); } if (tree.type.tsym.isDirectlyOrIndirectlyLocal()) { + // for local class, add outer instance as arg if applicable + Symbol ownerMethod = tree.type.tsym.owner; // the method where the local class is defined + if (!ownerMethod.isStatic()) { + Symbol outerClass = ownerMethod.owner; + Value outerInstance; + try { + outerInstance = loadVar(outerClass); + } catch (NoSuchElementException e) { + outerInstance = thisValue(); + } + args.add(outerInstance); + argtypes.add(outerInstance.type()); + } for (Symbol c : localCaptures.get(tree.type.tsym)) { args.add(loadVar(c)); argtypes.add(symbolToErasedDesc(c)); @@ -2436,6 +2479,68 @@ protected void addFreeVars(ClassSymbol c) { } FreeVarScanner fvs = new FreeVarScanner(); localCaptures.put(tree.sym, List.copyOf(fvs.analyzeCaptures())); + + ListBuffer capturesSymbols = new ListBuffer<>(); + Symbol ownerMethod = tree.sym.owner; + if (!ownerMethod.isStatic()) { + capturesSymbols.add(ownerMethod.owner); + } + capturesSymbols.addAll(localCaptures.get(tree.sym)); + + pushBody(tree, FunctionType.FUNCTION_TYPE_VOID); + + // fields declarations + List fieldsDec = tree.defs.stream().filter(d -> d instanceof JCVariableDecl).toList(); + // fields for the local class captures + List fieldsForCaptures = new ArrayList<>(); + for (Symbol capturesSymbol : capturesSymbols) { + Op.Result r = append(CoreOp.var(typeToTypeElement(capturesSymbol.type))); + fieldsForCaptures.add(r); + stack.localToOp.put(capturesSymbol, r); + } + // fields of the local class + for (JCTree fd : fieldsDec) { + scan(fd); + } + + // methods declarations + List methodsDec = tree.defs.stream().filter(d -> d instanceof JCMethodDecl).toList(); + for (JCTree md : methodsDec) { + // We pass the stack to have the mapping of captures to fields available + CoreOp.FuncOp funcOp = new BodyScanner((JCMethodDecl) md, localCaptures, stack).scanMethod(); + if (!((JCMethodDecl) md).sym.isConstructor()) { + append(funcOp); + continue; + } + // for a constructor, we modify the created funcOp by adding params that represent captures, + // plus operations that assign these params to the right fields + List newParamsTypes = new ArrayList<>(); + // add params that represent captures + newParamsTypes.addAll(capturesSymbols.stream().map(s -> typeToTypeElement(s.type)).toList()); + // add original params expect the first one that represent the local class instance + newParamsTypes.addAll(funcOp.invokableType().parameterTypes().stream().skip(1).toList()); + // @@@ simple way of doing this kind of transformation ? + Body.Builder nfbody = Body.Builder.of(stack.body, CoreType.functionType(funcOp.resultType(), newParamsTypes)); + Block.Builder nfblock = nfbody.entryBlock(); + nfblock.body(funcOp.body(), nfblock.parameters().subList(capturesSymbols.length(), nfblock.parameters().size()), + (block, op) -> { + if (op instanceof Op.Terminating) { + List paramsForCaptures = nfblock.parameters().subList(0, capturesSymbols.length()); + for (int i = 0; i < paramsForCaptures.size(); i++) { + block.op(CoreOp.varStore(fieldsForCaptures.get(i), paramsForCaptures.get(i))); + } + } + block.op(op); + return block; + }); + funcOp = CoreOp.func(funcOp.funcName(), nfbody); + append(funcOp); + } + append(CoreOp.core_yield()); + Body.Builder body = stack.body; + popBody(); + + result = append(JavaOp.classDecOp((ClassType) symbolToErasedDesc(tree.sym), body)); } } diff --git a/src/jdk.incubator.code/share/classes/jdk/incubator/code/interpreter/Interpreter.java b/src/jdk.incubator.code/share/classes/jdk/incubator/code/interpreter/Interpreter.java index 5159e43b04a..d1b14c0c004 100644 --- a/src/jdk.incubator.code/share/classes/jdk/incubator/code/interpreter/Interpreter.java +++ b/src/jdk.incubator.code/share/classes/jdk/incubator/code/interpreter/Interpreter.java @@ -630,6 +630,8 @@ private Quoted __internal_quoted() { } lock.unlock(); return null; + } if (o instanceof JavaOp.ClassDecOp) { + return null; } else { throw interpreterException( new UnsupportedOperationException("Unsupported operation: " + o)); diff --git a/test/langtools/tools/javac/reflect/LocalClassTest.java b/test/langtools/tools/javac/reflect/LocalClassTest.java index 356e7e32c5a..93b06a24186 100644 --- a/test/langtools/tools/javac/reflect/LocalClassTest.java +++ b/test/langtools/tools/javac/reflect/LocalClassTest.java @@ -27,7 +27,7 @@ * @test * @summary Smoke test for code reflection with local class creation expressions. * @modules jdk.incubator.code - * @build LocalClassTest + * @compile LocalClassTest.java * @build CodeReflectionTester * @run main CodeReflectionTester LocalClassTest */ @@ -42,11 +42,26 @@ public class LocalClassTest { @CodeReflection @IR(""" func @"testLocalNoCapture" (%0 : java.type:"LocalClassTest")java.type:"void" -> { - %1 : java.type:"LocalClassTest::$1Foo" = new %0 @java.ref:"LocalClassTest::$1Foo::(LocalClassTest)"; - invoke %1 @java.ref:"LocalClassTest::$1Foo::m():void"; - return; + class.dec @java.type:"LocalClassTest::$1Foo" ()java.type:"void" -> { + %1 : Var = var; + func @"" (%3 : java.type:"LocalClassTest")java.type:"void" -> { + invoke @java.ref:"java.lang.Object::()" @invoke.kind="SUPER"; + var.store %1 %3; + return; + }; + func @"m" (%2 : java.type:"LocalClassTest::$1Foo")java.type:"void" -> { + return; + }; + yield; + }; + %4 : java.type:"LocalClassTest::$1Foo" = new %0 @java.ref:"LocalClassTest::$1Foo::(LocalClassTest)"; + invoke %4 @java.ref:"LocalClassTest::$1Foo::m():void"; + return; }; """) + // curr there is no link between newOp and classDecOp + // classDescOp should have an attribute that matches the refType of the MethodRef used with NewOp + // how the interpreter works ?? does it work in the first place void testLocalNoCapture() { class Foo { void m() { } @@ -57,9 +72,21 @@ void m() { } @CodeReflection @IR(""" func @"testAnonNoCapture" (%0 : java.type:"LocalClassTest")java.type:"void" -> { - %1 : java.type:"LocalClassTest::$1" = new %0 @java.ref:"LocalClassTest::$1::(LocalClassTest)"; - invoke %1 @java.ref:"LocalClassTest::$1::m():void"; - return; + class.dec @java.type:"LocalClassTest::$1" ()java.type:"void" -> { + %1 : Var = var; + func @"" (%2 : java.type:"LocalClassTest")java.type:"void" -> { + invoke @java.ref:"java.lang.Object::()" @invoke.kind="SUPER"; + var.store %1 %2; + return; + }; + func @"m" (%3 : java.type:"LocalClassTest::$1")java.type:"void" -> { + return; + }; + yield; + }; + %4 : java.type:"LocalClassTest::$1" = new %0 @java.ref:"LocalClassTest::$1::(LocalClassTest)"; + invoke %4 @java.ref:"LocalClassTest::$1::m():void"; + return; }; """) void testAnonNoCapture() { @@ -71,11 +98,26 @@ void m() { } @CodeReflection @IR(""" func @"testLocalCaptureParam" (%0 : java.type:"LocalClassTest", %1 : java.type:"java.lang.String")java.type:"java.lang.String" -> { - %2 : Var = var %1 @"s"; - %3 : java.type:"java.lang.String" = var.load %2; - %4 : java.type:"LocalClassTest::$2Foo" = new %0 %3 @java.ref:"LocalClassTest::$2Foo::(LocalClassTest, java.lang.String)"; - %5 : java.type:"java.lang.String" = invoke %4 @java.ref:"LocalClassTest::$2Foo::m():java.lang.String"; - return %5; + %2 : Var = var %1 @"s"; + class.dec @java.type:"LocalClassTest::$2Foo" ()java.type:"void" -> { + %3 : Var = var; + %4 : Var = var; + func @"" (%5 : java.type:"LocalClassTest", %6 : java.type:"java.lang.String")java.type:"void" -> { + invoke @java.ref:"java.lang.Object::()" @invoke.kind="SUPER"; + var.store %3 %5; + var.store %4 %6; + return; + }; + func @"m" (%7 : java.type:"LocalClassTest::$2Foo")java.type:"java.lang.String" -> { + %8 : java.type:"java.lang.String" = var.load %4; + return %8; + }; + yield; + }; + %9 : java.type:"java.lang.String" = var.load %2; + %10 : java.type:"LocalClassTest::$2Foo" = new %0 %9 @java.ref:"LocalClassTest::$2Foo::(LocalClassTest, java.lang.String)"; + %11 : java.type:"java.lang.String" = invoke %10 @java.ref:"LocalClassTest::$2Foo::m():java.lang.String"; + return %11; }; """) String testLocalCaptureParam(String s) { @@ -88,12 +130,27 @@ class Foo { @CodeReflection @IR(""" func @"testAnonCaptureParam" (%0 : java.type:"LocalClassTest", %1 : java.type:"java.lang.String")java.type:"java.lang.String" -> { - %2 : Var = var %1 @"s"; - %3 : java.type:"java.lang.String" = var.load %2; - %4 : java.type:"LocalClassTest::$2" = new %0 %3 @java.ref:"LocalClassTest::$2::(LocalClassTest, java.lang.String)"; - %5 : java.type:"java.lang.String" = invoke %4 @java.ref:"LocalClassTest::$2::m():java.lang.String"; - return %5; - }; + %2 : Var = var %1 @"s"; + class.dec @java.type:"LocalClassTest::$2" ()java.type:"void" -> { + %3 : Var = var; + %4 : Var = var; + func @"" (%5 : java.type:"LocalClassTest", %6 : java.type:"java.lang.String")java.type:"void" -> { + invoke @java.ref:"java.lang.Object::()" @invoke.kind="SUPER"; + var.store %3 %5; + var.store %4 %6; + return; + }; + func @"m" (%7 : java.type:"LocalClassTest::$2")java.type:"java.lang.String" -> { + %8 : java.type:"java.lang.String" = var.load %4; + return %8; + }; + yield; + }; + %9 : java.type:"java.lang.String" = var.load %2; + %10 : java.type:"LocalClassTest::$2" = new %0 %9 @java.ref:"LocalClassTest::$2::(LocalClassTest, java.lang.String)"; + %11 : java.type:"java.lang.String" = invoke %10 @java.ref:"LocalClassTest::$2::m():java.lang.String"; + return %11; + }; """) String testAnonCaptureParam(String s) { return new Object() { @@ -104,13 +161,35 @@ String testAnonCaptureParam(String s) { @CodeReflection @IR(""" func @"testLocalCaptureParamAndField" (%0 : java.type:"LocalClassTest", %1 : java.type:"java.lang.String")java.type:"java.lang.String" -> { - %2 : Var = var %1 @"s"; - %3 : java.type:"java.lang.String" = constant @"Hello!"; - %4 : Var = var %3 @"localConst"; - %5 : java.type:"java.lang.String" = var.load %2; - %6 : java.type:"LocalClassTest::$3Foo" = new %0 %5 @java.ref:"LocalClassTest::$3Foo::(LocalClassTest, java.lang.String)"; - %7 : java.type:"java.lang.String" = invoke %6 @java.ref:"LocalClassTest::$3Foo::m():java.lang.String"; - return %7; + %2 : Var = var %1 @"s"; + %3 : java.type:"java.lang.String" = constant @"Hello!"; + %4 : Var = var %3 @"localConst"; + class.dec @java.type:"LocalClassTest::$3Foo" ()java.type:"void" -> { + %5 : Var = var; + %6 : Var = var; + func @"" (%7 : java.type:"LocalClassTest", %8 : java.type:"java.lang.String")java.type:"void" -> { + invoke @java.ref:"java.lang.Object::()" @invoke.kind="SUPER"; + var.store %5 %7; + var.store %6 %8; + return; + }; + func @"m" (%9 : java.type:"LocalClassTest::$3Foo")java.type:"java.lang.String" -> { + %10 : java.type:"java.lang.String" = var.load %4; + %11 : java.type:"java.lang.String" = var.load %6; + %12 : java.type:"java.lang.String" = concat %10 %11; + %13 : java.type:"LocalClassTest" = var.load %5; + %14 : java.type:"java.lang.String" = field.load %13 @java.ref:"LocalClassTest::nonConstString:java.lang.String"; + %15 : java.type:"java.lang.String" = concat %12 %14; + %16 : java.type:"java.lang.String" = field.load @java.ref:"LocalClassTest::CONST_STRING:java.lang.String"; + %17 : java.type:"java.lang.String" = concat %15 %16; + return %17; + }; + yield; + }; + %18 : java.type:"java.lang.String" = var.load %2; + %19 : java.type:"LocalClassTest::$3Foo" = new %0 %18 @java.ref:"LocalClassTest::$3Foo::(LocalClassTest, java.lang.String)"; + %20 : java.type:"java.lang.String" = invoke %19 @java.ref:"LocalClassTest::$3Foo::m():java.lang.String"; + return %20; }; """) String testLocalCaptureParamAndField(String s) { @@ -124,13 +203,35 @@ class Foo { @CodeReflection @IR(""" func @"testAnonCaptureParamAndField" (%0 : java.type:"LocalClassTest", %1 : java.type:"java.lang.String")java.type:"java.lang.String" -> { - %2 : Var = var %1 @"s"; - %3 : java.type:"java.lang.String" = constant @"Hello!"; - %4 : Var = var %3 @"localConst"; - %5 : java.type:"java.lang.String" = var.load %2; - %6 : java.type:"LocalClassTest::$3" = new %0 %5 @java.ref:"LocalClassTest::$3::(LocalClassTest, java.lang.String)"; - %7 : java.type:"java.lang.String" = invoke %6 @java.ref:"LocalClassTest::$3::m():java.lang.String"; - return %7; + %2 : Var = var %1 @"s"; + %3 : java.type:"java.lang.String" = constant @"Hello!"; + %4 : Var = var %3 @"localConst"; + class.dec @java.type:"LocalClassTest::$3" ()java.type:"void" -> { + %5 : Var = var; + %6 : Var = var; + func @"" (%7 : java.type:"LocalClassTest", %8 : java.type:"java.lang.String")java.type:"void" -> { + invoke @java.ref:"java.lang.Object::()" @invoke.kind="SUPER"; + var.store %5 %7; + var.store %6 %8; + return; + }; + func @"m" (%9 : java.type:"LocalClassTest::$3")java.type:"java.lang.String" -> { + %10 : java.type:"java.lang.String" = var.load %4; + %11 : java.type:"java.lang.String" = var.load %6; + %12 : java.type:"java.lang.String" = concat %10 %11; + %13 : java.type:"LocalClassTest" = var.load %5; + %14 : java.type:"java.lang.String" = field.load %13 @java.ref:"LocalClassTest::nonConstString:java.lang.String"; + %15 : java.type:"java.lang.String" = concat %12 %14; + %16 : java.type:"java.lang.String" = field.load @java.ref:"LocalClassTest::CONST_STRING:java.lang.String"; + %17 : java.type:"java.lang.String" = concat %15 %16; + return %17; + }; + yield; + }; + %18 : java.type:"java.lang.String" = var.load %2; + %19 : java.type:"LocalClassTest::$3" = new %0 %18 @java.ref:"LocalClassTest::$3::(LocalClassTest, java.lang.String)"; + %20 : java.type:"java.lang.String" = invoke %19 @java.ref:"LocalClassTest::$3::m():java.lang.String"; + return %20; }; """) String testAnonCaptureParamAndField(String s) { @@ -143,15 +244,53 @@ String testAnonCaptureParamAndField(String s) { @CodeReflection @IR(""" func @"testLocalDependency" (%0 : java.type:"LocalClassTest", %1 : java.type:"int", %2 : java.type:"int")java.type:"void" -> { - %3 : Var = var %1 @"s"; - %4 : Var = var %2 @"i"; - %5 : java.type:"int" = var.load %3; - %6 : java.type:"int" = var.load %4; - %7 : java.type:"LocalClassTest::$1Bar" = new %0 %5 %6 @java.ref:"LocalClassTest::$1Bar::(LocalClassTest, int, int)"; - return; - }; + %3 : Var = var %1 @"s"; + %4 : Var = var %2 @"i"; + class.dec @java.type:"LocalClassTest::$4Foo" ()java.type:"void" -> { + %5 : Var = var; + %6 : Var = var; + func @"" (%7 : java.type:"LocalClassTest", %8 : java.type:"int")java.type:"void" -> { + invoke @java.ref:"java.lang.Object::()" @invoke.kind="SUPER"; + var.store %5 %7; + var.store %6 %8; + return; + }; + func @"i" (%9 : java.type:"LocalClassTest::$4Foo")java.type:"int" -> { + %10 : java.type:"int" = var.load %6; + return %10; + }; + yield; + }; + class.dec @java.type:"LocalClassTest::$1Bar" ()java.type:"void" -> { + %11 : Var = var; + %12 : Var = var; + %13 : Var = var; + func @"" (%14 : java.type:"LocalClassTest", %15 : java.type:"int", %16 : java.type:"int")java.type:"void" -> { + invoke @java.ref:"java.lang.Object::()" @invoke.kind="SUPER"; + var.store %11 %14; + var.store %12 %15; + var.store %13 %16; + return; + }; + func @"s" (%17 : java.type:"LocalClassTest::$1Bar")java.type:"int" -> { + %18 : java.type:"int" = var.load %12; + return %18; + }; + func @"foo" (%19 : java.type:"LocalClassTest::$1Bar")java.type:"LocalClassTest::$4Foo" -> { + %20 : java.type:"LocalClassTest" = var.load %11; + %21 : java.type:"int" = var.load %13; + %22 : java.type:"LocalClassTest::$4Foo" = new %20 %21 @java.ref:"LocalClassTest::$4Foo::(LocalClassTest, int)"; + return %22; + }; + yield; + }; + %23 : java.type:"int" = var.load %3; + %24 : java.type:"int" = var.load %4; + %25 : java.type:"LocalClassTest::$1Bar" = new %0 %23 %24 @java.ref:"LocalClassTest::$1Bar::(LocalClassTest, int, int)"; + return; + }; """) - void testLocalDependency(int s, int i) { + void testLocalDependency(int s, int i) { // static is like defined in a top level context, record sweet spot, worth keep pushing to model top level classes class Foo { int i() { return i; } } @@ -165,13 +304,51 @@ class Bar { @CodeReflection @IR(""" func @"testAnonDependency" (%0 : java.type:"LocalClassTest", %1 : java.type:"int", %2 : java.type:"int")java.type:"void" -> { - %3 : Var = var %1 @"s"; - %4 : Var = var %2 @"i"; - %5 : java.type:"int" = var.load %3; - %6 : java.type:"int" = var.load %4; - %7 : java.type:"LocalClassTest::$4" = new %0 %5 %6 @java.ref:"LocalClassTest::$4::(LocalClassTest, int, int)"; - return; - }; + %3 : Var = var %1 @"s"; + %4 : Var = var %2 @"i"; + class.dec @java.type:"LocalClassTest::$5Foo" ()java.type:"void" -> { + %5 : Var = var; + %6 : Var = var; + func @"" (%7 : java.type:"LocalClassTest", %8 : java.type:"int")java.type:"void" -> { + invoke @java.ref:"java.lang.Object::()" @invoke.kind="SUPER"; + var.store %5 %7; + var.store %6 %8; + return; + }; + func @"i" (%9 : java.type:"LocalClassTest::$5Foo")java.type:"int" -> { + %10 : java.type:"int" = var.load %6; + return %10; + }; + yield; + }; + class.dec @java.type:"LocalClassTest::$4" ()java.type:"void" -> { + %11 : Var = var; + %12 : Var = var; + %13 : Var = var; + func @"" (%14 : java.type:"LocalClassTest", %15 : java.type:"int", %16 : java.type:"int")java.type:"void" -> { + invoke @java.ref:"java.lang.Object::()" @invoke.kind="SUPER"; + var.store %11 %14; + var.store %12 %15; + var.store %13 %16; + return; + }; + func @"s" (%17 : java.type:"LocalClassTest::$4")java.type:"int" -> { + %18 : java.type:"int" = var.load %12; + return %18; + }; + func @"foo" (%19 : java.type:"LocalClassTest::$4")java.type:"LocalClassTest::$5Foo" -> { + %20 : java.type:"LocalClassTest" = var.load %11; + %21 : java.type:"int" = var.load %13; + %22 : java.type:"LocalClassTest::$5Foo" = new %20 %21 @java.ref:"LocalClassTest::$5Foo::(LocalClassTest, int)"; + return %22; + }; + yield; + }; + %23 : java.type:"int" = var.load %3; + %24 : java.type:"int" = var.load %4; + %25 : java.type:"LocalClassTest::$4" = new %0 %23 %24 @java.ref:"LocalClassTest::$4::(LocalClassTest, int, int)"; + return; + }; """) void testAnonDependency(int s, int i) { class Foo {