diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/BufferedMethodBuilder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/BufferedMethodBuilder.java index 0819831466e..eaa2e2b710e 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/BufferedMethodBuilder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/BufferedMethodBuilder.java @@ -25,6 +25,7 @@ package jdk.internal.classfile.impl; import java.lang.constant.MethodTypeDesc; +import java.lang.reflect.AccessFlag; import java.util.ArrayList; import java.util.List; import java.util.Optional; @@ -44,7 +45,7 @@ import java.lang.classfile.MethodModel; import java.lang.classfile.constantpool.Utf8Entry; public final class BufferedMethodBuilder - implements TerminalMethodBuilder, MethodInfo { + implements TerminalMethodBuilder { private final List elements; private final SplitConstantPool constantPool; private final ClassFileImpl context; @@ -59,23 +60,32 @@ public final class BufferedMethodBuilder ClassFileImpl context, Utf8Entry nameInfo, Utf8Entry typeInfo, + int flags, MethodModel original) { this.elements = new ArrayList<>(); this.constantPool = constantPool; this.context = context; this.name = nameInfo; this.desc = typeInfo; - this.flags = AccessFlags.ofMethod(); + this.flags = AccessFlags.ofMethod(flags); this.original = original; } @Override public MethodBuilder with(MethodElement element) { elements.add(element); - if (element instanceof AccessFlags f) this.flags = f; + if (element instanceof AccessFlags f) this.flags = checkFlags(f); return this; } + private AccessFlags checkFlags(AccessFlags updated) { + boolean wasStatic = updated.has(AccessFlag.STATIC); + boolean isStatic = flags.has(AccessFlag.STATIC); + if (wasStatic != isStatic) + throw new IllegalArgumentException("Cannot change ACC_STATIC flag of method"); + return updated; + } + @Override public ConstantPoolBuilder constantPool() { return constantPool; diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/ChainedClassBuilder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/ChainedClassBuilder.java index b33d192f9b3..04565278217 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/ChainedClassBuilder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/ChainedClassBuilder.java @@ -79,7 +79,7 @@ public final class ChainedClassBuilder public ClassBuilder withMethod(Utf8Entry name, Utf8Entry descriptor, int flags, Consumer handler) { consumer.accept(new BufferedMethodBuilder(terminal.constantPool, terminal.context, - name, descriptor, null) + name, descriptor, flags, null) .run(handler) .toModel()); return this; @@ -88,7 +88,7 @@ public final class ChainedClassBuilder @Override public ClassBuilder transformMethod(MethodModel method, MethodTransform transform) { BufferedMethodBuilder builder = new BufferedMethodBuilder(terminal.constantPool, terminal.context, - method.methodName(), method.methodType(), method); + method.methodName(), method.methodType(), method.flags().flagsMask(), method); builder.transform(method, transform); consumer.accept(builder.toModel()); return this; diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/DirectMethodBuilder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/DirectMethodBuilder.java index 50daf243056..a4b3bbe1b84 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/DirectMethodBuilder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/DirectMethodBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2024, 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 @@ -42,7 +42,7 @@ import java.lang.classfile.constantpool.Utf8Entry; public final class DirectMethodBuilder extends AbstractDirectBuilder - implements TerminalMethodBuilder, WritableElement, MethodInfo { + implements TerminalMethodBuilder, WritableElement { final Utf8Entry name; final Utf8Entry desc; diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/MethodInfo.java b/src/java.base/share/classes/jdk/internal/classfile/impl/MethodInfo.java index 0dd81776f67..ce511447706 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/MethodInfo.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/MethodInfo.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2024, 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 @@ -29,7 +29,8 @@ import java.lang.classfile.constantpool.Utf8Entry; import static java.lang.classfile.ClassFile.ACC_STATIC; -public interface MethodInfo { +public sealed interface MethodInfo + permits MethodImpl, TerminalMethodBuilder, BufferedMethodBuilder.Model { Utf8Entry methodName(); Utf8Entry methodType(); MethodTypeDesc methodTypeSymbol(); diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/TerminalMethodBuilder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/TerminalMethodBuilder.java index 636b12a9acf..e1c66bec503 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/TerminalMethodBuilder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/TerminalMethodBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2024, 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 @@ -28,7 +28,7 @@ import java.lang.classfile.CodeModel; import java.lang.classfile.MethodBuilder; public sealed interface TerminalMethodBuilder - extends MethodBuilder + extends MethodBuilder, MethodInfo permits BufferedMethodBuilder, DirectMethodBuilder { BufferedCodeBuilder bufferedCodeBuilder(CodeModel original); } diff --git a/test/jdk/jdk/classfile/MethodBuilderStaticFlagTest.java b/test/jdk/jdk/classfile/MethodBuilderStaticFlagTest.java new file mode 100644 index 00000000000..d5a24a144ea --- /dev/null +++ b/test/jdk/jdk/classfile/MethodBuilderStaticFlagTest.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2024, 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. + */ + +import jdk.internal.classfile.impl.ChainedClassBuilder; +import org.junit.jupiter.api.Test; + +import java.lang.classfile.ClassBuilder; +import java.lang.classfile.ClassElement; +import java.lang.classfile.ClassFile; +import java.lang.classfile.ClassTransform; +import java.lang.constant.ClassDesc; + +import static java.lang.classfile.ClassFile.ACC_PUBLIC; +import static java.lang.classfile.ClassFile.ACC_STATIC; +import static java.lang.constant.ConstantDescs.MTD_void; +import static org.junit.jupiter.api.Assertions.*; + +/* + * @test + * @bug 8336777 + * @summary Testing MethodBuilder correctly rejecting resetting the static + * access flag. + * @run junit MethodBuilderStaticFlagTest + */ +class MethodBuilderStaticFlagTest { + + void testClassBuilder(ClassBuilder clb) { + clb.withMethod("staticToStatic", MTD_void, ACC_STATIC, mb -> mb.withFlags(ACC_PUBLIC | ACC_STATIC)); + assertThrows(IllegalArgumentException.class, () -> + clb.withMethod("staticToInstance", MTD_void, ACC_STATIC, mb -> mb.withFlags(ACC_PUBLIC))); + assertThrows(IllegalArgumentException.class, () -> + clb.withMethod("instanceToStatic", MTD_void, 0, mb -> mb.withFlags(ACC_PUBLIC | ACC_STATIC))); + clb.withMethod("instanceToInstance", MTD_void, 0, mb -> mb.withFlags(ACC_PUBLIC)); + } + + @Test + void testDirectBuilder() { + ClassFile.of().build(ClassDesc.of("C1"), this::testClassBuilder); + } + + @Test + void testBufferedBuilder() { + var cf = ClassFile.of(); + var bytes = cf.build(ClassDesc.of("C2"), _ -> {}); + var cm = cf.parse(bytes); + + cf.transformClass(cm, new ClassTransform() { + @Override + public void accept(ClassBuilder builder, ClassElement element) { + builder.with(element); + } + + @Override + public void atEnd(ClassBuilder clb) { + assertInstanceOf(ChainedClassBuilder.class, clb); + testClassBuilder(clb); + } + }.andThen(ClassBuilder::with)); + } +}