diff --git a/src/jdk.incubator.code/share/classes/jdk/incubator/code/dialect/core/CoreOp.java b/src/jdk.incubator.code/share/classes/jdk/incubator/code/dialect/core/CoreOp.java index e03234d28c9..24c507673f1 100644 --- a/src/jdk.incubator.code/share/classes/jdk/incubator/code/dialect/core/CoreOp.java +++ b/src/jdk.incubator.code/share/classes/jdk/incubator/code/dialect/core/CoreOp.java @@ -83,18 +83,32 @@ public String externalizeOpName() { public static final class FuncOp extends CoreOp implements Op.Invokable, Op.Isolated, Op.Lowerable { + public enum MethodKind {STATIC, INSTANCE} + /** * A builder for constructing a function operation. */ public static class Builder { final Body.Builder ancestorBody; + final CodeType ownerType; final String funcName; final FunctionType signature; + final MethodKind methodKind; + + Builder(Body.Builder ancestorBody, CodeType ownerType, String funcName, FunctionType signature, MethodKind mk) { + this.ancestorBody = ancestorBody; + this.ownerType = ownerType; + this.funcName = funcName; + this.signature = signature; + this.methodKind = mk; + } Builder(Body.Builder ancestorBody, String funcName, FunctionType signature) { this.ancestorBody = ancestorBody; + this.ownerType = null; this.funcName = funcName; this.signature = signature; + this.methodKind = null; } /** @@ -106,7 +120,7 @@ public static class Builder { public FuncOp body(Consumer c) { Body.Builder body = Body.Builder.of(ancestorBody, signature); c.accept(body.entryBlock()); - return new FuncOp(funcName, body); + return new FuncOp(ownerType, funcName, body, methodKind); } } @@ -116,9 +130,14 @@ public FuncOp body(Consumer c) { * The externalized attribute modeling the function name */ static final String ATTRIBUTE_FUNC_NAME = NAME + ".name"; + static final String ATTRIBUTE_FUNC_OWNER_TYPE = NAME + ".owner.type"; + static final String ATTRIBUTE_FUNC_KIND = NAME + ".kind"; + final CodeType ownerType; final String funcName; final Body body; + final MethodKind methodKind; + FuncOp(ExternalizedOp def) { if (!def.operands().isEmpty()) { @@ -126,26 +145,47 @@ public FuncOp body(Consumer c) { } String funcName = def.extractAttributeValue(ATTRIBUTE_FUNC_NAME, true, - v -> switch (v) { + u -> switch (u) { case String s -> s; - case null, default -> throw new UnsupportedOperationException("Unsupported func name value:" + v); + case null, default -> + throw new UnsupportedOperationException("Unsupported func name value:" + u); + }); + + + CodeType ownerType = def.extractAttributeValue(ATTRIBUTE_FUNC_OWNER_TYPE, false, + v -> switch (v) { + case CodeType ct -> ct; + case null -> null; + default -> throw new UnsupportedOperationException("Unsupported func owner type:" + v); + }); + + MethodKind methodKind = def.extractAttributeValue(ATTRIBUTE_FUNC_KIND, false, + v -> switch (v) { + case String s -> MethodKind.valueOf(s); + case MethodKind mk -> mk; + case null -> null; + default -> throw new UnsupportedOperationException("Unsupported func kind:" + v); }); - this(funcName, def.bodyDefinitions().get(0)); + this(ownerType, funcName, def.bodyDefinitions().get(0), methodKind); } FuncOp(FuncOp that, CodeContext cc, CodeTransformer ct) { super(that, cc); + this.ownerType = that.ownerType; this.funcName = that.funcName; this.body = that.body.transform(cc, ct).build(this); + this.methodKind = that.methodKind; } FuncOp(FuncOp that, String funcName, CodeContext cc, CodeTransformer ct) { super(that, cc); + this.ownerType = that.ownerType; this.funcName = funcName; this.body = that.body.transform(cc, ct).build(this); + this.methodKind = that.methodKind; } @Override @@ -177,8 +217,19 @@ public FuncOp transform(String funcName, CodeTransformer ct) { FuncOp(String funcName, Body.Builder bodyBuilder) { super(List.of()); + this.ownerType = null; + this.funcName = funcName; + this.body = bodyBuilder.build(this); + this.methodKind = null; + } + + FuncOp(CodeType ownerType, String funcName, Body.Builder bodyBuilder, MethodKind mk) { + super(List.of()); + + this.ownerType = ownerType; this.funcName = funcName; this.body = bodyBuilder.build(this); + this.methodKind = mk; } @Override @@ -188,7 +239,15 @@ public List bodies() { @Override public Map externalize() { - return Map.of("", funcName); + Map m = new HashMap<>(); + m.put("", funcName); + if (ownerType != null) { + m.put(ATTRIBUTE_FUNC_OWNER_TYPE, ownerType); + } + if (methodKind != null) { + m.put(ATTRIBUTE_FUNC_KIND, methodKind); + } + return m; } /** @@ -214,6 +273,18 @@ public Block.Builder lower(Block.Builder b, CodeTransformer _ignore) { public CodeType resultType() { return JavaType.VOID; } + + public Optional mref() { + return ownerType == null || methodKind == null ? + Optional.empty() : + Optional.of(MethodRef.method(ownerType, funcName, body.bodySignature().returnType(), paramTypes())); + } + + private List paramTypes() { + int n = body.bodySignature().parameterTypes().size(); + return methodKind == MethodKind.INSTANCE ? body.bodySignature().parameterTypes().subList(1, n) : + body.bodySignature().parameterTypes(); + } } /** @@ -1527,6 +1598,19 @@ public static FuncOp.Builder func(String funcName, FunctionType signature) { return new FuncOp.Builder(null, funcName, signature); } + /** + * Creates a function operation builder. + * + * @param ownerType the function owner type + * @param funcName the function name + * @param signature the function's signature, represented as a function type + * @param mk the function kind + * @return the function operation builder + */ + public static FuncOp.Builder func(CodeType ownerType, String funcName, FunctionType signature, FuncOp.MethodKind mk) { + return new FuncOp.Builder(null, ownerType, funcName, signature, mk); + } + /** * Creates a function operation. * @@ -1538,6 +1622,18 @@ public static FuncOp func(String funcName, Body.Builder body) { return new FuncOp(funcName, body); } + /** + * Creates a function operation. + * + * @param ownerType the function owner type + * @param body the body builder defining the function body + * @param mk the function kind + * @return the function operation + */ + public static FuncOp func(CodeType ownerType, String funcName, Body.Builder body, FuncOp.MethodKind mk) { + return new FuncOp(ownerType, funcName, body, mk); + } + /** * Creates a function call operation. * diff --git a/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/OpBuilder.java b/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/OpBuilder.java index ff256bd66cf..69a25e5416a 100644 --- a/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/OpBuilder.java +++ b/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/OpBuilder.java @@ -700,6 +700,10 @@ Value buildAttributeValue(Object value) { FieldRef enumValueRef = FieldRef.field(InvokeOp.InvokeKind.class, ik.name(), InvokeOp.InvokeKind.class); yield builder.op(fieldLoad(enumValueRef)); } + case FuncOp.MethodKind mk -> { + FieldRef enumValueRef = FieldRef.field(FuncOp.MethodKind.class, mk.name(), FuncOp.MethodKind.class); + yield builder.op(fieldLoad(enumValueRef)); + } case Object o when value == ExternalizedOp.NULL_ATTRIBUTE_VALUE -> builder.op(fieldLoad(FieldRef.field(ExternalizedOp.class, "NULL_ATTRIBUTE_VALUE", Object.class))); 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 2e9d26126e2..7b127019a4b 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 @@ -2490,7 +2490,10 @@ UnsupportedASTException unsupported(JCTree tree) { CoreOp.FuncOp scanMethod(JCBlock body) { scan(body, ReflectMethods.this.currentNode()); appendReturnOrUnreachable(body); - CoreOp.FuncOp func = CoreOp.func(name.toString(), stack.body); + CodeType ownerType = typeToCodeType(((JCMethodDecl) tree).sym.owner.type); + CoreOp.FuncOp.MethodKind mk = ((JCMethodDecl) tree).sym.isStatic() ? CoreOp.FuncOp.MethodKind.STATIC : + CoreOp.FuncOp.MethodKind.INSTANCE; + CoreOp.FuncOp func = CoreOp.func(ownerType, name.toString(), stack.body, mk); func.setLocation(generateLocation(tree, true)); return func; } diff --git a/test/jdk/jdk/incubator/code/TestFuncOpMethodRef.java b/test/jdk/jdk/incubator/code/TestFuncOpMethodRef.java new file mode 100644 index 00000000000..f9131740de1 --- /dev/null +++ b/test/jdk/jdk/incubator/code/TestFuncOpMethodRef.java @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @modules jdk.incubator.code + * @modules jdk.incubator.code/jdk.incubator.code.internal + * @library lib + * @run junit TestFuncOpMethodRef + */ + +import jdk.incubator.code.Body; +import jdk.incubator.code.CodeTransformer; +import jdk.incubator.code.dialect.core.CoreOp; +import jdk.incubator.code.dialect.core.CoreType; +import jdk.incubator.code.dialect.core.FunctionType; +import jdk.incubator.code.dialect.java.FieldRef; +import jdk.incubator.code.dialect.java.JavaOp; +import jdk.incubator.code.dialect.java.JavaType; +import jdk.incubator.code.dialect.java.MethodRef; +import jdk.incubator.code.extern.DialectFactory; +import jdk.incubator.code.internal.OpBuilder; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.lang.invoke.MethodHandles; +import java.util.LinkedHashMap; +import java.util.Map; + +import static jdk.incubator.code.dialect.core.CoreOp.*; + +public class TestFuncOpMethodRef { + private final JavaType thisType = JavaType.type(this.getClass().describeConstable().get()); + private final MethodRef MR = MethodRef.method(thisType, "f", CoreType.FUNCTION_TYPE_VOID); + + @Test + void test() { + FuncOp f = func(MR.name(), CoreType.FUNCTION_TYPE_VOID).body(b -> { + b.op(return_()); + }); + Assertions.assertTrue(f.mref().isEmpty()); + + CoreOp.FuncOp cf = externalizeAndCreate(f); + Assertions.assertTrue(cf.mref().isEmpty()); + } + + @Test + void test2() { + FuncOp f = func(MR.refType(), MR.name(), MR.signature(), FuncOp.MethodKind.STATIC).body(b -> { + b.op(return_()); + }); + Assertions.assertTrue(f.mref().isPresent()); + Assertions.assertEquals(MR, f.mref().get()); + + CoreOp.FuncOp cf = externalizeAndCreate(f); + Assertions.assertTrue(cf.mref().isPresent()); + + Assertions.assertEquals(f.mref().get(), cf.mref().get()); + } + + @Test + void test5() { + Body.Builder bb = Body.Builder.of(null, CoreType.FUNCTION_TYPE_VOID); + bb.entryBlock().op(return_()); + FuncOp f = func(MR.refType(), MR.name(), bb, FuncOp.MethodKind.STATIC); + Assertions.assertTrue(f.mref().isPresent()); + Assertions.assertEquals(MR, f.mref().get()); + + FuncOp cf = externalizeAndCreate(f); + Assertions.assertTrue(cf.mref().isPresent()); + Assertions.assertEquals(f.mref().get(), cf.mref().get()); + } + + @Test + void test6() { + Body.Builder bb = Body.Builder.of(null, CoreType.FUNCTION_TYPE_VOID); + bb.entryBlock().op(return_()); + FuncOp f = func(MR.name(), bb); + Assertions.assertTrue(f.mref().isEmpty()); + + FuncOp cf = externalizeAndCreate(f); + Assertions.assertTrue(cf.mref().isEmpty()); + } + + private static FuncOp externalizeAndCreate(FuncOp f) { + ModuleOp mop = OpBuilder.createBuilderFunctions(new LinkedHashMap<>(Map.of(f.funcName(), f)), + b -> b.op(JavaOp.fieldLoad( + FieldRef.field(JavaOp.class, "JAVA_DIALECT_FACTORY", DialectFactory.class)))); + CoreOp.FuncOp cf = (CoreOp.FuncOp) Interpreter.invoke(MethodHandles.lookup(), + mop.transform(CodeTransformer.LOWERING_TRANSFORMER).functionTable().get(f.funcName())); + return cf; + } +} \ No newline at end of file