diff --git a/src/changes/changes.xml b/src/changes/changes.xml index a13b8681e0..efbcb90972 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -61,6 +61,7 @@ The type attribute can be add,update,fix,remove. + BCELifier now emits boolean literals for boolean ConstantValue fields so generated creator classes run. Use modern Mockito on Java 11+ (#528). MethodGen.setMaxLocals() should account for LocalVariableTable entries (#529). Fix constant pool module access test on Java 27 (#534). diff --git a/src/main/java/org/apache/bcel/util/BCELifier.java b/src/main/java/org/apache/bcel/util/BCELifier.java index 392f60bf7f..53ef89bbe5 100644 --- a/src/main/java/org/apache/bcel/util/BCELifier.java +++ b/src/main/java/org/apache/bcel/util/BCELifier.java @@ -281,7 +281,12 @@ public void visitField(final Field field) { if (field.getType() == Type.BYTE) { printWriter.print("(byte)"); } - printWriter.print(cv); + if (field.getType() == Type.BOOLEAN) { + // A boolean ConstantValue is stored as a CONSTANT_Integer, but FieldGen.setInitValue(int) rejects boolean fields. + printWriter.print(field.getConstantPool().getConstantInteger(cv.getConstantValueIndex()).getBytes() != 0); + } else { + printWriter.print(cv); + } if (field.getType() == Type.LONG) { printWriter.print("L"); } diff --git a/src/test/java/BooleanConstants.java b/src/test/java/BooleanConstants.java new file mode 100644 index 0000000000..29566e7ef1 --- /dev/null +++ b/src/test/java/BooleanConstants.java @@ -0,0 +1,23 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +public class BooleanConstants { + + public static final boolean ON = true; + public static final boolean OFF = false; +} diff --git a/src/test/java/org/apache/bcel/util/BCELifierTest.java b/src/test/java/org/apache/bcel/util/BCELifierTest.java index 715b40162a..345b2a570c 100644 --- a/src/test/java/org/apache/bcel/util/BCELifierTest.java +++ b/src/test/java/org/apache/bcel/util/BCELifierTest.java @@ -43,6 +43,7 @@ import org.apache.bcel.generic.BinaryOpCreator; import org.apache.bcel.generic.ClassGen; import org.apache.bcel.generic.ConstantPoolGen; +import org.apache.bcel.generic.FieldGen; import org.apache.bcel.generic.InstructionConst; import org.apache.bcel.generic.InstructionFactory; import org.apache.bcel.generic.InstructionList; @@ -177,6 +178,31 @@ void testBinaryOp(final String exp) throws Exception { } } + @Test + void testBooleanConstantFieldInitValue() throws Exception { + // javac stores "static final boolean ON = true;" as a CONSTANT_Integer ConstantValue; the generated creator + // must call FieldGen.setInitValue(boolean), since setInitValue(int) throws for boolean fields. + final ClassGen cg = new ClassGen("Example", "java.lang.Object", "Example.java", Const.ACC_PUBLIC | Const.ACC_SUPER, new String[] {}); + final ConstantPoolGen cp = cg.getConstantPool(); + final FieldGen fg = new FieldGen(Const.ACC_PUBLIC | Const.ACC_STATIC | Const.ACC_FINAL, Type.BOOLEAN, "ON", cp); + fg.setInitValue(true); + cg.addField(fg.getField()); + + final ByteArrayOutputStream os = new ByteArrayOutputStream(); + new BCELifier(cg.getJavaClass(), os).start(); + final String source = new String(os.toByteArray(), StandardCharsets.UTF_8); + + assertTrue(source.contains("field.setInitValue(true);"), source); + assertFalse(source.contains("field.setInitValue(1);"), source); + + final ByteArrayOutputStream compiledClassOutput = new ByteArrayOutputStream(); + new BCELifier(BCELifier.getJavaClass("BooleanConstants"), compiledClassOutput).start(); + final String compiledClassSource = new String(compiledClassOutput.toByteArray(), StandardCharsets.UTF_8); + assertTrue(compiledClassSource.contains("field.setInitValue(true);"), compiledClassSource); + assertTrue(compiledClassSource.contains("field.setInitValue(false);"), compiledClassSource); + assertFalse(compiledClassSource.contains("field.setInitValue(0);"), compiledClassSource); + } + @Test void testClassNameRejectedWhenNotJavaIdentifier() { // Class file names may contain characters the Java language forbids (JVMS 4.2.2 bans only . ; [ /). @@ -297,7 +323,8 @@ void testHelloWorld() throws Exception { "org.apache.commons.lang.builder.ToStringBuilder.class", "org.apache.commons.lang.SerializationUtils.class", "org.apache.commons.lang.ArrayUtils.class", - "target/test-classes/Java4Example.class" + "target/test-classes/Java4Example.class", + "target/test-classes/BooleanConstants.class" // @formatter:on }) void testJavapCompare(final String pathToClass) throws Exception {