summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--asm-analysis/src/main/java/org/objectweb/asm/tree/analysis/Analyzer.java3
-rw-r--r--asm-analysis/src/test/java/org/objectweb/asm/tree/analysis/AnalyzerWithBasicInterpreterTest.java22
-rw-r--r--asm-test/src/main/java/org/objectweb/asm/test/AsmTest.java1
-rw-r--r--asm-test/src/main/resources/jdk3/SubOptimalMaxStackAndLocals.classbin0 -> 203 bytes
-rw-r--r--asm-test/src/resources/java/jdk11/AllStructures.jasm2
-rw-r--r--asm-test/src/resources/java/jdk3/SubOptimalMaxStackAndLocals.jasm50
-rw-r--r--asm-util/src/main/java/org/objectweb/asm/util/CheckMethodAdapter.java4
-rw-r--r--asm-util/src/test/java/org/objectweb/asm/util/CheckClassAdapterTest.java29
-rw-r--r--asm-util/src/test/resources/jdk3.SubOptimalMaxStackAndLocals.txt14
-rw-r--r--asm/src/test/java/org/objectweb/asm/ClassWriterTest.java16
10 files changed, 133 insertions, 8 deletions
diff --git a/asm-analysis/src/main/java/org/objectweb/asm/tree/analysis/Analyzer.java b/asm-analysis/src/main/java/org/objectweb/asm/tree/analysis/Analyzer.java
index f22e9b0f..7aad8db5 100644
--- a/asm-analysis/src/main/java/org/objectweb/asm/tree/analysis/Analyzer.java
+++ b/asm-analysis/src/main/java/org/objectweb/asm/tree/analysis/Analyzer.java
@@ -305,6 +305,9 @@ public class Analyzer<V extends Value> implements Opcodes {
*/
private static int computeMaxLocals(final MethodNode method) {
int maxLocals = Type.getArgumentsAndReturnSizes(method.desc) >> 2;
+ if ((method.access & Opcodes.ACC_STATIC) != 0) {
+ maxLocals -= 1;
+ }
for (AbstractInsnNode insnNode : method.instructions) {
if (insnNode instanceof VarInsnNode) {
int local = ((VarInsnNode) insnNode).var;
diff --git a/asm-analysis/src/test/java/org/objectweb/asm/tree/analysis/AnalyzerWithBasicInterpreterTest.java b/asm-analysis/src/test/java/org/objectweb/asm/tree/analysis/AnalyzerWithBasicInterpreterTest.java
index 6cefb4d4..fc199934 100644
--- a/asm-analysis/src/test/java/org/objectweb/asm/tree/analysis/AnalyzerWithBasicInterpreterTest.java
+++ b/asm-analysis/src/test/java/org/objectweb/asm/tree/analysis/AnalyzerWithBasicInterpreterTest.java
@@ -33,6 +33,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTimeoutPreemptively;
import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assumptions.assumeTrue;
import java.io.IOException;
import java.nio.file.Files;
@@ -45,6 +46,7 @@ import org.junit.jupiter.params.provider.MethodSource;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.test.AsmTest;
+import org.objectweb.asm.test.AsmTest.PrecompiledClass;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.MethodNode;
@@ -138,6 +140,8 @@ class AnalyzerWithBasicInterpreterTest extends AsmTest {
analyzedMethodMaxs.add(new MethodMaxs(methodNode.maxStack, methodNode.maxLocals));
}
+ // jdk3.SubOptimalMaxStackAndLocals has non optimal max stack and max local values on purpose.
+ assumeTrue(classParameter != PrecompiledClass.JDK3_SUB_OPTIMAL_MAX_STACK_AND_LOCALS);
for (int i = 0; i < analyzedMethodMaxs.size(); ++i) {
assertTrue(analyzedMethodMaxs.get(i).maxLocals >= methodMaxs.get(i).maxLocals);
assertTrue(analyzedMethodMaxs.get(i).maxStack >= methodMaxs.get(i).maxStack);
@@ -145,6 +149,24 @@ class AnalyzerWithBasicInterpreterTest extends AsmTest {
}
/**
+ * Tests that analyzeAndComputeMaxs computes the smallest possible maxLocals for static methods.
+ *
+ * @throws AnalyzerException if the test class can't be analyzed.
+ */
+ @Test
+ void testAnalyzeAndComputeMaxs_staticMethod() throws AnalyzerException {
+ MethodNode methodNode =
+ new MethodNodeBuilder("(I)V", /* maxStack = */ 0, /* maxLocals = */ 0).vreturn().build();
+ methodNode.access |= Opcodes.ACC_STATIC;
+ Analyzer<BasicValue> analyzer = new Analyzer<BasicValue>(new BasicInterpreter());
+
+ analyzer.analyzeAndComputeMaxs("C", methodNode);
+
+ assertEquals(1, methodNode.maxLocals);
+ assertEquals(0, methodNode.maxStack);
+ }
+
+ /**
* Tests that the analyzer does not loop infinitely, even if the {@link Interpreter#merge} method
* does not follow its required contract (namely that if the merge result is equal to the first
* argument, the first argument should be returned - see #316326).
diff --git a/asm-test/src/main/java/org/objectweb/asm/test/AsmTest.java b/asm-test/src/main/java/org/objectweb/asm/test/AsmTest.java
index 48eb1863..ee0ad099 100644
--- a/asm-test/src/main/java/org/objectweb/asm/test/AsmTest.java
+++ b/asm-test/src/main/java/org/objectweb/asm/test/AsmTest.java
@@ -143,6 +143,7 @@ public abstract class AsmTest {
JDK3_ARTIFICIAL_STRUCTURES("jdk3.ArtificialStructures"),
JDK3_INNER_CLASS("jdk3.AllStructures$InnerClass"),
JDK3_LARGE_METHOD("jdk3.LargeMethod"),
+ JDK3_SUB_OPTIMAL_MAX_STACK_AND_LOCALS("jdk3.SubOptimalMaxStackAndLocals"),
JDK5_ALL_INSTRUCTIONS("jdk5.AllInstructions"),
JDK5_ALL_STRUCTURES("jdk5.AllStructures"),
JDK5_ANNOTATION("jdk5.AllStructures$InvisibleAnnotation"),
diff --git a/asm-test/src/main/resources/jdk3/SubOptimalMaxStackAndLocals.class b/asm-test/src/main/resources/jdk3/SubOptimalMaxStackAndLocals.class
new file mode 100644
index 00000000..b494edc3
--- /dev/null
+++ b/asm-test/src/main/resources/jdk3/SubOptimalMaxStackAndLocals.class
Binary files differ
diff --git a/asm-test/src/resources/java/jdk11/AllStructures.jasm b/asm-test/src/resources/java/jdk11/AllStructures.jasm
index ecc72c7b..61fcb028 100644
--- a/asm-test/src/resources/java/jdk11/AllStructures.jasm
+++ b/asm-test/src/resources/java/jdk11/AllStructures.jasm
@@ -29,7 +29,7 @@ package jdk11;
/**
* A class with JDK11 specific class file feature. A corresponding class file can be generated
- with the OpenJDK asmtools (https://wiki.openjdk.java.net/display/CodeTools/asmtools),
+ * with the OpenJDK asmtools (https://wiki.openjdk.java.net/display/CodeTools/asmtools),
* version 7 or more. Usage:
*
* java -jar asmtools.jar jasm AllStructures.jasm
diff --git a/asm-test/src/resources/java/jdk3/SubOptimalMaxStackAndLocals.jasm b/asm-test/src/resources/java/jdk3/SubOptimalMaxStackAndLocals.jasm
new file mode 100644
index 00000000..7e21bb5f
--- /dev/null
+++ b/asm-test/src/resources/java/jdk3/SubOptimalMaxStackAndLocals.jasm
@@ -0,0 +1,50 @@
+// ASM: a very small and fast Java bytecode manipulation framework
+// Copyright (c) 2000-2011 INRIA, France Telecom
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+// 1. Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// 2. Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// 3. Neither the name of the copyright holders nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+// THE POSSIBILITY OF SUCH DAMAGE.
+package jdk3;
+
+/**
+ * A class with valid but non optimal max_stack and max_locals, on purpose (to test cases like
+ * this). A corresponding class file can be generated with the OpenJDK asmtools
+ * (https://wiki.openjdk.java.net/display/CodeTools/asmtools), version 7 or more. Usage:
+ *
+ * java -jar asmtools.jar jasm SubOptimalMaxStackAndLocals.jasm
+ *
+ * @author Eric Bruneton
+ */
+super public class SubOptimalMaxStackAndLocals
+ version 47:0
+{
+
+public Method "<init>":"()V"
+ stack 2 locals 3
+{
+ aload_0;
+ invokespecial Method java/lang/Object."<init>":"()V";
+ return;
+}
+} // end Class SubOptimalMaxStackAndLocals
diff --git a/asm-util/src/main/java/org/objectweb/asm/util/CheckMethodAdapter.java b/asm-util/src/main/java/org/objectweb/asm/util/CheckMethodAdapter.java
index 8b4ba7a9..6dc2e583 100644
--- a/asm-util/src/main/java/org/objectweb/asm/util/CheckMethodAdapter.java
+++ b/asm-util/src/main/java/org/objectweb/asm/util/CheckMethodAdapter.java
@@ -446,6 +446,8 @@ public class CheckMethodAdapter extends MethodVisitor {
new MethodNode(api, access, name, descriptor, null, null) {
@Override
public void visitEnd() {
+ int originalMaxLocals = maxLocals;
+ int originalMaxStack = maxStack;
boolean checkMaxStackAndLocals = false;
boolean checkFrames = false;
if (methodVisitor instanceof MethodWriterWrapper) {
@@ -473,6 +475,8 @@ public class CheckMethodAdapter extends MethodVisitor {
throwError(analyzer, e);
}
if (methodVisitor != null) {
+ maxLocals = originalMaxLocals;
+ maxStack = originalMaxStack;
accept(methodVisitor);
}
}
diff --git a/asm-util/src/test/java/org/objectweb/asm/util/CheckClassAdapterTest.java b/asm-util/src/test/java/org/objectweb/asm/util/CheckClassAdapterTest.java
index 4206bbcd..280ec628 100644
--- a/asm-util/src/test/java/org/objectweb/asm/util/CheckClassAdapterTest.java
+++ b/asm-util/src/test/java/org/objectweb/asm/util/CheckClassAdapterTest.java
@@ -468,7 +468,7 @@ class CheckClassAdapterTest extends AsmTest implements Opcodes {
*/
@ParameterizedTest
@MethodSource(ALL_CLASSES_AND_ALL_APIS)
- void testVisitMethods_precompiledClass(
+ void testVisitMethods_classWriterDelegate_precompiledClass(
final PrecompiledClass classParameter, final Api apiParameter) {
byte[] classFile = classParameter.getBytes();
ClassReader classReader = new ClassReader(classFile);
@@ -481,7 +481,32 @@ class CheckClassAdapterTest extends AsmTest implements Opcodes {
Exception exception = assertThrows(UnsupportedOperationException.class, accept);
assertTrue(exception.getMessage().matches(UNSUPPORTED_OPERATION_MESSAGE_PATTERN));
} else {
- classReader.accept(classVisitor, attributes(), 0);
+ assertDoesNotThrow(accept);
+ assertEquals(new ClassFile(classFile), new ClassFile(classWriter.toByteArray()));
+ }
+ }
+
+ /**
+ * Tests that classes are unchanged with a ClassReader->CheckClassAdapter->ClassVisitor transform.
+ */
+ @ParameterizedTest
+ @MethodSource(ALL_CLASSES_AND_ALL_APIS)
+ void testVisitMethods_nonClassWriterDelegate_precompiledClass(
+ final PrecompiledClass classParameter, final Api apiParameter) {
+ byte[] classFile = classParameter.getBytes();
+ ClassReader classReader = new ClassReader(classFile);
+ ClassWriter classWriter = new ClassWriter(0);
+ ClassVisitor noOpClassVisitor =
+ new ClassVisitor(/* latest */ Opcodes.ASM10_EXPERIMENTAL, classWriter) {};
+ ClassVisitor classVisitor = new CheckClassAdapter(apiParameter.value(), noOpClassVisitor, true);
+
+ Executable accept = () -> classReader.accept(classVisitor, attributes(), 0);
+
+ if (classParameter.isMoreRecentThan(apiParameter)) {
+ Exception exception = assertThrows(UnsupportedOperationException.class, accept);
+ assertTrue(exception.getMessage().matches(UNSUPPORTED_OPERATION_MESSAGE_PATTERN));
+ } else {
+ assertDoesNotThrow(accept);
assertEquals(new ClassFile(classFile), new ClassFile(classWriter.toByteArray()));
}
}
diff --git a/asm-util/src/test/resources/jdk3.SubOptimalMaxStackAndLocals.txt b/asm-util/src/test/resources/jdk3.SubOptimalMaxStackAndLocals.txt
new file mode 100644
index 00000000..c349b069
--- /dev/null
+++ b/asm-util/src/test/resources/jdk3.SubOptimalMaxStackAndLocals.txt
@@ -0,0 +1,14 @@
+// class version 47.0 (47)
+// access flags 0x21
+public class jdk3/SubOptimalMaxStackAndLocals {
+
+ // compiled from: SubOptimalMaxStackAndLocals.jasm
+
+ // access flags 0x1
+ public <init>()V
+ ALOAD 0
+ INVOKESPECIAL java/lang/Object.<init> ()V
+ RETURN
+ MAXSTACK = 2
+ MAXLOCALS = 3
+}
diff --git a/asm/src/test/java/org/objectweb/asm/ClassWriterTest.java b/asm/src/test/java/org/objectweb/asm/ClassWriterTest.java
index 22afbd4b..0871e90c 100644
--- a/asm/src/test/java/org/objectweb/asm/ClassWriterTest.java
+++ b/asm/src/test/java/org/objectweb/asm/ClassWriterTest.java
@@ -556,11 +556,13 @@ class ClassWriterTest extends AsmTest {
/**
* Tests that a ClassReader -> ClassWriter transform with the COMPUTE_MAXS option leaves classes
* unchanged. This is not true in general (the valid max stack and max locals for a given method
- * are not unique), but this should be the case with our precompiled classes.
+ * are not unique), but this should be the case with our precompiled classes (except
+ * jdk3.SubOptimalMaxStackAndLocals, which has non optimal max values on purpose).
*/
@ParameterizedTest
@MethodSource(ALL_CLASSES_AND_ALL_APIS)
void testReadAndWrite_computeMaxs(final PrecompiledClass classParameter, final Api apiParameter) {
+ assumeTrue(classParameter != PrecompiledClass.JDK3_SUB_OPTIMAL_MAX_STACK_AND_LOCALS);
byte[] classFile = classParameter.getBytes();
ClassReader classReader = new ClassReader(classFile);
ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS);
@@ -614,8 +616,10 @@ class ClassWriterTest extends AsmTest {
assertTrue(classWriter.hasFlags(ClassWriter.COMPUTE_FRAMES));
// The computed stack map frames should be equal to the original ones, if any (classes before
// JDK8 don't have ones). This is not true in general (the valid frames for a given method are
- // not unique), but this should be the case with our precompiled classes.
- if (classParameter.isMoreRecentThan(Api.ASM4)) {
+ // not unique), but this should be the case with our precompiled classes (except
+ // jdk3.SubOptimalMaxStackAndLocals, which has non optimal max values on purpose).
+ if (classParameter.isMoreRecentThan(Api.ASM4)
+ && classParameter != PrecompiledClass.JDK3_SUB_OPTIMAL_MAX_STACK_AND_LOCALS) {
assertEquals(new ClassFile(classFile), new ClassFile(newClassFile));
}
Executable newInstance = () -> new ClassFile(newClassFile).newInstance();
@@ -663,8 +667,10 @@ class ClassWriterTest extends AsmTest {
// The computed stack map frames should be equal to the original ones, if any (classes before
// JDK8 don't have ones). This is not true in general (the valid frames for a given method are
- // not unique), but this should be the case with our precompiled classes.
- if (classParameter.isMoreRecentThan(Api.ASM4)) {
+ // not unique), but this should be the case with our precompiled classes (except
+ // jdk3.SubOptimalMaxStackAndLocals, which has non optimal max values on purpose).
+ if (classParameter.isMoreRecentThan(Api.ASM4)
+ && classParameter != PrecompiledClass.JDK3_SUB_OPTIMAL_MAX_STACK_AND_LOCALS) {
assertEquals(new ClassFile(classFile), new ClassFile(newClassFile));
}
Executable newInstance = () -> new ClassFile(newClassFile).newInstance();