Skip to content
Commits on Source (5)
...@@ -124,7 +124,7 @@ public class Analyzer<V extends Value> implements Opcodes { ...@@ -124,7 +124,7 @@ public class Analyzer<V extends Value> implements Opcodes {
for (int j = startIndex; j < endIndex; ++j) { for (int j = startIndex; j < endIndex; ++j) {
List<TryCatchBlockNode> insnHandlers = handlers[j]; List<TryCatchBlockNode> insnHandlers = handlers[j];
if (insnHandlers == null) { if (insnHandlers == null) {
insnHandlers = new ArrayList<TryCatchBlockNode>(); insnHandlers = new ArrayList<>();
handlers[j] = insnHandlers; handlers[j] = insnHandlers;
} }
insnHandlers.add(tryCatchBlock); insnHandlers.add(tryCatchBlock);
...@@ -134,11 +134,11 @@ public class Analyzer<V extends Value> implements Opcodes { ...@@ -134,11 +134,11 @@ public class Analyzer<V extends Value> implements Opcodes {
// For each instruction, compute the subroutine to which it belongs. // For each instruction, compute the subroutine to which it belongs.
// Follow the main 'subroutine', and collect the jsr instructions to nested subroutines. // Follow the main 'subroutine', and collect the jsr instructions to nested subroutines.
Subroutine main = new Subroutine(null, method.maxLocals, null); Subroutine main = new Subroutine(null, method.maxLocals, null);
List<AbstractInsnNode> jsrInsns = new ArrayList<AbstractInsnNode>(); List<AbstractInsnNode> jsrInsns = new ArrayList<>();
findSubroutine(0, main, jsrInsns); findSubroutine(0, main, jsrInsns);
// Follow the nested subroutines, and collect their own nested subroutines, until all // Follow the nested subroutines, and collect their own nested subroutines, until all
// subroutines are found. // subroutines are found.
Map<LabelNode, Subroutine> jsrSubroutines = new HashMap<LabelNode, Subroutine>(); Map<LabelNode, Subroutine> jsrSubroutines = new HashMap<>();
while (!jsrInsns.isEmpty()) { while (!jsrInsns.isEmpty()) {
JumpInsnNode jsrInsn = (JumpInsnNode) jsrInsns.remove(0); JumpInsnNode jsrInsn = (JumpInsnNode) jsrInsns.remove(0);
Subroutine subroutine = jsrSubroutines.get(jsrInsn.label); Subroutine subroutine = jsrSubroutines.get(jsrInsn.label);
...@@ -313,7 +313,7 @@ public class Analyzer<V extends Value> implements Opcodes { ...@@ -313,7 +313,7 @@ public class Analyzer<V extends Value> implements Opcodes {
private void findSubroutine( private void findSubroutine(
final int insnIndex, final Subroutine subroutine, final List<AbstractInsnNode> jsrInsns) final int insnIndex, final Subroutine subroutine, final List<AbstractInsnNode> jsrInsns)
throws AnalyzerException { throws AnalyzerException {
ArrayList<Integer> instructionIndicesToProcess = new ArrayList<Integer>(); ArrayList<Integer> instructionIndicesToProcess = new ArrayList<>();
instructionIndicesToProcess.add(insnIndex); instructionIndicesToProcess.add(insnIndex);
while (!instructionIndicesToProcess.isEmpty()) { while (!instructionIndicesToProcess.isEmpty()) {
int currentInsnIndex = int currentInsnIndex =
...@@ -459,7 +459,7 @@ public class Analyzer<V extends Value> implements Opcodes { ...@@ -459,7 +459,7 @@ public class Analyzer<V extends Value> implements Opcodes {
* @return the created frame. * @return the created frame.
*/ */
protected Frame<V> newFrame(final int numLocals, final int numStack) { protected Frame<V> newFrame(final int numLocals, final int numStack) {
return new Frame<V>(numLocals, numStack); return new Frame<>(numLocals, numStack);
} }
/** /**
...@@ -469,7 +469,7 @@ public class Analyzer<V extends Value> implements Opcodes { ...@@ -469,7 +469,7 @@ public class Analyzer<V extends Value> implements Opcodes {
* @return the created frame. * @return the created frame.
*/ */
protected Frame<V> newFrame(final Frame<? extends V> frame) { protected Frame<V> newFrame(final Frame<? extends V> frame) {
return new Frame<V>(frame); return new Frame<>(frame);
} }
/** /**
......
...@@ -120,7 +120,9 @@ public class Frame<V extends Value> { ...@@ -120,7 +120,9 @@ public class Frame<V extends Value> {
* this frame corresponds to the successor of the jump instruction (i.e. the next instruction * this frame corresponds to the successor of the jump instruction (i.e. the next instruction
* in the instructions sequence). * in the instructions sequence).
*/ */
public void initJumpTarget(final int opcode, final LabelNode target) {} public void initJumpTarget(final int opcode, final LabelNode target) {
// Does nothing by default.
}
/** /**
* Sets the expected return type of the analyzed method. * Sets the expected return type of the analyzed method.
...@@ -159,7 +161,7 @@ public class Frame<V extends Value> { ...@@ -159,7 +161,7 @@ public class Frame<V extends Value> {
*/ */
public V getLocal(final int index) { public V getLocal(final int index) {
if (index >= numLocals) { if (index >= numLocals) {
throw new IndexOutOfBoundsException("Trying to access an inexistant local variable"); throw new IndexOutOfBoundsException("Trying to get an inexistant local variable " + index);
} }
return values[index]; return values[index];
} }
...@@ -173,7 +175,7 @@ public class Frame<V extends Value> { ...@@ -173,7 +175,7 @@ public class Frame<V extends Value> {
*/ */
public void setLocal(final int index, final V value) { public void setLocal(final int index, final V value) {
if (index >= numLocals) { if (index >= numLocals) {
throw new IndexOutOfBoundsException("Trying to access an inexistant local variable " + index); throw new IndexOutOfBoundsException("Trying to set an inexistant local variable " + index);
} }
values[index] = value; values[index] = value;
} }
...@@ -206,7 +208,7 @@ public class Frame<V extends Value> { ...@@ -206,7 +208,7 @@ public class Frame<V extends Value> {
* @param value the new value of the stack slot. * @param value the new value of the stack slot.
* @throws IndexOutOfBoundsException if the stack slot does not exist. * @throws IndexOutOfBoundsException if the stack slot does not exist.
*/ */
public void setStack(final int index, final V value) throws IndexOutOfBoundsException { public void setStack(final int index, final V value) {
values[numLocals + index] = value; values[numLocals + index] = value;
} }
...@@ -598,36 +600,11 @@ public class Frame<V extends Value> { ...@@ -598,36 +600,11 @@ public class Frame<V extends Value> {
case Opcodes.INVOKESPECIAL: case Opcodes.INVOKESPECIAL:
case Opcodes.INVOKESTATIC: case Opcodes.INVOKESTATIC:
case Opcodes.INVOKEINTERFACE: case Opcodes.INVOKEINTERFACE:
{ executeInvokeInsn(insn, ((MethodInsnNode) insn).desc, interpreter);
List<V> valueList = new ArrayList<V>();
String methodDescriptor = ((MethodInsnNode) insn).desc;
for (int i = Type.getArgumentTypes(methodDescriptor).length; i > 0; --i) {
valueList.add(0, pop());
}
if (insn.getOpcode() != Opcodes.INVOKESTATIC) {
valueList.add(0, pop());
}
if (Type.getReturnType(methodDescriptor) == Type.VOID_TYPE) {
interpreter.naryOperation(insn, valueList);
} else {
push(interpreter.naryOperation(insn, valueList));
}
break; break;
}
case Opcodes.INVOKEDYNAMIC: case Opcodes.INVOKEDYNAMIC:
{ executeInvokeInsn(insn, ((InvokeDynamicInsnNode) insn).desc, interpreter);
List<V> valueList = new ArrayList<V>();
String methodDesccriptor = ((InvokeDynamicInsnNode) insn).desc;
for (int i = Type.getArgumentTypes(methodDesccriptor).length; i > 0; --i) {
valueList.add(0, pop());
}
if (Type.getReturnType(methodDesccriptor) == Type.VOID_TYPE) {
interpreter.naryOperation(insn, valueList);
} else {
push(interpreter.naryOperation(insn, valueList));
}
break; break;
}
case Opcodes.NEW: case Opcodes.NEW:
push(interpreter.newOperation(insn)); push(interpreter.newOperation(insn));
break; break;
...@@ -648,7 +625,7 @@ public class Frame<V extends Value> { ...@@ -648,7 +625,7 @@ public class Frame<V extends Value> {
interpreter.unaryOperation(insn, pop()); interpreter.unaryOperation(insn, pop());
break; break;
case Opcodes.MULTIANEWARRAY: case Opcodes.MULTIANEWARRAY:
List<V> valueList = new ArrayList<V>(); List<V> valueList = new ArrayList<>();
for (int i = ((MultiANewArrayInsnNode) insn).dims; i > 0; --i) { for (int i = ((MultiANewArrayInsnNode) insn).dims; i > 0; --i) {
valueList.add(0, pop()); valueList.add(0, pop());
} }
...@@ -663,6 +640,23 @@ public class Frame<V extends Value> { ...@@ -663,6 +640,23 @@ public class Frame<V extends Value> {
} }
} }
private void executeInvokeInsn(
final AbstractInsnNode insn, final String methodDescriptor, final Interpreter<V> interpreter)
throws AnalyzerException {
ArrayList<V> valueList = new ArrayList<>();
for (int i = Type.getArgumentTypes(methodDescriptor).length; i > 0; --i) {
valueList.add(0, pop());
}
if (insn.getOpcode() != Opcodes.INVOKESTATIC && insn.getOpcode() != Opcodes.INVOKEDYNAMIC) {
valueList.add(0, pop());
}
if (Type.getReturnType(methodDescriptor) == Type.VOID_TYPE) {
interpreter.naryOperation(insn, valueList);
} else {
push(interpreter.naryOperation(insn, valueList));
}
}
/** /**
* Merges the given frame into this frame. * Merges the given frame into this frame.
* *
......
...@@ -87,12 +87,18 @@ final class SmallSet<T> extends AbstractSet<T> { ...@@ -87,12 +87,18 @@ final class SmallSet<T> extends AbstractSet<T> {
@Override @Override
public Iterator<T> iterator() { public Iterator<T> iterator() {
return new IteratorImpl<T>(element1, element2); return new IteratorImpl<>(element1, element2);
} }
@Override @Override
public int size() { public int size() {
return element1 == null ? 0 : (element2 == null ? 1 : 2); if (element1 == null) {
return 0;
} else if (element2 == null) {
return 1;
} else {
return 2;
}
} }
// ----------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------
...@@ -124,7 +130,7 @@ final class SmallSet<T> extends AbstractSet<T> { ...@@ -124,7 +130,7 @@ final class SmallSet<T> extends AbstractSet<T> {
if (otherSet.element2 == null) { if (otherSet.element2 == null) {
// If this set also contains exactly one element, we have two distinct elements. // If this set also contains exactly one element, we have two distinct elements.
if (element2 == null) { if (element2 == null) {
return new SmallSet<T>(element1, otherSet.element1); return new SmallSet<>(element1, otherSet.element1);
} }
// If otherSet is included in this set, return this set. // If otherSet is included in this set, return this set.
if (otherSet.element1 == element1 || otherSet.element1 == element2) { if (otherSet.element1 == element1 || otherSet.element1 == element2) {
...@@ -139,7 +145,7 @@ final class SmallSet<T> extends AbstractSet<T> { ...@@ -139,7 +145,7 @@ final class SmallSet<T> extends AbstractSet<T> {
// At this point we know that there are at least 3 distinct elements, so we need a generic set // At this point we know that there are at least 3 distinct elements, so we need a generic set
// to store the result. // to store the result.
HashSet<T> result = new HashSet<T>(4); HashSet<T> result = new HashSet<>(4);
result.add(element1); result.add(element1);
if (element2 != null) { if (element2 != null) {
result.add(element2); result.add(element2);
......
...@@ -204,7 +204,7 @@ public class SourceInterpreter extends Interpreter<SourceValue> implements Opcod ...@@ -204,7 +204,7 @@ public class SourceInterpreter extends Interpreter<SourceValue> implements Opcod
} }
} }
if (value1.size != value2.size || !containsAll(value1.insns, value2.insns)) { if (value1.size != value2.size || !containsAll(value1.insns, value2.insns)) {
HashSet<AbstractInsnNode> setUnion = new HashSet<AbstractInsnNode>(); HashSet<AbstractInsnNode> setUnion = new HashSet<>();
setUnion.addAll(value1.insns); setUnion.addAll(value1.insns);
setUnion.addAll(value2.insns); setUnion.addAll(value2.insns);
return new SourceValue(Math.min(value1.size, value2.size), setUnion); return new SourceValue(Math.min(value1.size, value2.size), setUnion);
......
...@@ -77,7 +77,7 @@ public class SourceValue implements Value { ...@@ -77,7 +77,7 @@ public class SourceValue implements Value {
*/ */
public SourceValue(final int size, final AbstractInsnNode insnNode) { public SourceValue(final int size, final AbstractInsnNode insnNode) {
this.size = size; this.size = size;
this.insns = new SmallSet<AbstractInsnNode>(insnNode); this.insns = new SmallSet<>(insnNode);
} }
/** /**
......
...@@ -61,7 +61,7 @@ final class Subroutine { ...@@ -61,7 +61,7 @@ final class Subroutine {
Subroutine(final LabelNode start, final int maxLocals, final JumpInsnNode caller) { Subroutine(final LabelNode start, final int maxLocals, final JumpInsnNode caller) {
this.start = start; this.start = start;
this.localsUsed = new boolean[maxLocals]; this.localsUsed = new boolean[maxLocals];
this.callers = new ArrayList<JumpInsnNode>(); this.callers = new ArrayList<>();
callers.add(caller); callers.add(caller);
} }
...@@ -73,7 +73,7 @@ final class Subroutine { ...@@ -73,7 +73,7 @@ final class Subroutine {
Subroutine(final Subroutine subroutine) { Subroutine(final Subroutine subroutine) {
this.start = subroutine.start; this.start = subroutine.start;
this.localsUsed = new boolean[subroutine.localsUsed.length]; this.localsUsed = new boolean[subroutine.localsUsed.length];
this.callers = new ArrayList<JumpInsnNode>(subroutine.callers); this.callers = new ArrayList<>(subroutine.callers);
System.arraycopy(subroutine.localsUsed, 0, this.localsUsed, 0, subroutine.localsUsed.length); System.arraycopy(subroutine.localsUsed, 0, this.localsUsed, 0, subroutine.localsUsed.length);
} }
......
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
package org.objectweb.asm.tree.analysis; package org.objectweb.asm.tree.analysis;
import static java.time.Duration.ofSeconds; import static java.time.Duration.ofSeconds;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTimeoutPreemptively; import static org.junit.jupiter.api.Assertions.assertTimeoutPreemptively;
...@@ -36,7 +37,9 @@ import static org.junit.jupiter.api.Assertions.assertTrue; ...@@ -36,7 +37,9 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.ArrayList;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.function.Executable;
import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.provider.MethodSource;
import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassReader;
...@@ -46,34 +49,67 @@ import org.objectweb.asm.tree.ClassNode; ...@@ -46,34 +49,67 @@ import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.MethodNode; import org.objectweb.asm.tree.MethodNode;
/** /**
* BasicInterpreter tests. * Unit tests for {@link Analyzer}, when used with a {@link BasicInterpreter}.
* *
* @author Eric Bruneton * @author Eric Bruneton
*/ */
public class BasicInterpreterTest extends AsmTest { public class AnalyzerWithBasicInterpreterTest extends AsmTest {
private static final String CLASS_NAME = "C";
@Test @Test
public void testConstructor() { public void testConstructor() {
assertDoesNotThrow(() -> new BasicInterpreter());
assertThrows(IllegalStateException.class, () -> new BasicInterpreter() {}); assertThrows(IllegalStateException.class, () -> new BasicInterpreter() {});
} }
@Test
public void testAnalyze_invalidNewArray() {
MethodNode methodNode =
new MethodNodeBuilder().iconst_0().intInsn(Opcodes.NEWARRAY, -1).vreturn().build();
Executable analyze =
() -> new Analyzer<BasicValue>(new BasicInterpreter()).analyze(CLASS_NAME, methodNode);
String message = assertThrows(AnalyzerException.class, analyze).getMessage();
assertTrue(message.contains("Invalid array type"));
}
/** /**
* Tests that stack map frames are correctly merged when a JSR instruction can be reached from two * Tests that the precompiled classes can be successfully analyzed with a BasicInterpreter, and
* different control flow paths, with different local variable types (#316204). * that Analyzer can be subclassed to use custom frames.
* *
* @throws IOException if the test class can't be loaded.
* @throws AnalyzerException if the test class can't be analyzed. * @throws AnalyzerException if the test class can't be analyzed.
*/ */
@Test @ParameterizedTest
public void testMergeWithJsrReachableFromTwoDifferentPaths() @MethodSource(ALL_CLASSES_AND_LATEST_API)
throws IOException, AnalyzerException { public void testAnalyze_basicInterpreter(
ClassReader classReader = final PrecompiledClass classParameter, final Api apiParameter) throws AnalyzerException {
new ClassReader(Files.newInputStream(Paths.get("src/test/resources/Issue316204.class")));
ClassNode classNode = new ClassNode(); ClassNode classNode = new ClassNode();
classReader.accept(classNode, 0); new ClassReader(classParameter.getBytes()).accept(classNode, 0);
Analyzer<BasicValue> analyzer = new Analyzer<>(new BasicInterpreter()); Analyzer<BasicValue> analyzer =
analyzer.analyze(classNode.name, getMethod(classNode, "basicStopBundles")); new Analyzer<BasicValue>(new BasicInterpreter()) {
assertEquals("RIR..... ", analyzer.getFrames()[104].toString()); @Override
protected Frame<BasicValue> newFrame(final int numLocals, final int numStack) {
return new CustomFrame(numLocals, numStack);
}
@Override
protected Frame<BasicValue> newFrame(final Frame<? extends BasicValue> src) {
return new CustomFrame(src);
}
};
ArrayList<Frame<? extends BasicValue>[]> methodFrames = new ArrayList<>();
for (MethodNode methodNode : classNode.methods) {
methodFrames.add(analyzer.analyze(classNode.name, methodNode));
}
for (Frame<? extends BasicValue>[] frames : methodFrames) {
for (Frame<? extends BasicValue> frame : frames) {
assertTrue(frame == null || frame instanceof CustomFrame);
}
}
} }
/** /**
...@@ -84,10 +120,9 @@ public class BasicInterpreterTest extends AsmTest { ...@@ -84,10 +120,9 @@ public class BasicInterpreterTest extends AsmTest {
* @throws AnalyzerException if the test class can't be analyzed. * @throws AnalyzerException if the test class can't be analyzed.
*/ */
@Test @Test
public void testAnalyzeWithBadInterpreter() throws AnalyzerException { public void testAnalyze_badInterpreter() {
ClassNode classNode = new ClassNode(); ClassNode classNode = new ClassNode();
new ClassReader(PrecompiledClass.JDK8_ALL_FRAMES.getBytes()).accept(classNode, 0); new ClassReader(PrecompiledClass.JDK8_ALL_FRAMES.getBytes()).accept(classNode, 0);
for (MethodNode methodNode : classNode.methods) {
Analyzer<BasicValue> analyzer = Analyzer<BasicValue> analyzer =
new Analyzer<BasicValue>( new Analyzer<BasicValue>(
new BasicInterpreter(Opcodes.ASM7) { new BasicInterpreter(Opcodes.ASM7) {
...@@ -96,40 +131,36 @@ public class BasicInterpreterTest extends AsmTest { ...@@ -96,40 +131,36 @@ public class BasicInterpreterTest extends AsmTest {
return new BasicValue(super.merge(value1, value2).getType()); return new BasicValue(super.merge(value1, value2).getType());
} }
}); });
assertTimeoutPreemptively(ofSeconds(1), () -> analyzer.analyze("Test", methodNode));
ArrayList<Executable> analyses = new ArrayList<>();
for (MethodNode methodNode : classNode.methods) {
analyses.add(() -> analyzer.analyze(CLASS_NAME, methodNode));
}
for (Executable analysis : analyses) {
assertTimeoutPreemptively(ofSeconds(1), analysis);
} }
} }
/** /**
* Tests that the precompiled classes can be successfully analyzed with a BasicInterpreter, and * Tests that stack map frames are correctly merged when a JSR instruction can be reached from two
* that Analyzer can be subclassed to use custom frames. * different control flow paths, with different local variable types (#316204).
* *
* @throws IOException if the test class can't be loaded.
* @throws AnalyzerException if the test class can't be analyzed. * @throws AnalyzerException if the test class can't be analyzed.
*/ */
@ParameterizedTest @Test
@MethodSource(ALL_CLASSES_AND_LATEST_API) public void testAnalyze_mergeWithJsrReachableFromTwoDifferentPaths()
public void testAnalyze(final PrecompiledClass classParameter, final Api apiParameter) throws IOException, AnalyzerException {
throws AnalyzerException { ClassReader classReader =
new ClassReader(Files.newInputStream(Paths.get("src/test/resources/Issue316204.class")));
ClassNode classNode = new ClassNode(); ClassNode classNode = new ClassNode();
new ClassReader(classParameter.getBytes()).accept(classNode, 0); classReader.accept(classNode, 0);
for (MethodNode methodNode : classNode.methods) { Analyzer<BasicValue> analyzer = new Analyzer<>(new BasicInterpreter());
Analyzer<BasicValue> analyzer =
new Analyzer<BasicValue>(new BasicInterpreter()) {
@Override
protected Frame<BasicValue> newFrame(final int numLocals, final int numStack) {
return new CustomFrame(numLocals, numStack);
}
@Override analyzer.analyze(classNode.name, getMethod(classNode, "basicStopBundles"));
protected Frame<BasicValue> newFrame(final Frame<? extends BasicValue> src) {
return new CustomFrame(src); assertEquals("RIR..... ", analyzer.getFrames()[104].toString());
}
};
analyzer.analyze(classNode.name, methodNode);
for (Frame<? extends BasicValue> frame : analyzer.getFrames()) {
assertTrue(frame == null || frame instanceof CustomFrame);
}
}
} }
private static MethodNode getMethod(final ClassNode classNode, final String name) { private static MethodNode getMethod(final ClassNode classNode, final String name) {
......
// 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 org.objectweb.asm.tree.analysis;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.function.Executable;
import org.junit.jupiter.params.ParameterizedTest;
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.tree.ClassNode;
import org.objectweb.asm.tree.MethodNode;
/**
* Unit tests for {@link Analyzer}, when used with a {@link BasicVerifier}.
*
* @author Eric Bruneton
*/
public class AnalyzerWithBasicVerifierTest extends AsmTest {
private static final String CLASS_NAME = "C";
@Test
public void testConstructor() {
assertDoesNotThrow(() -> new BasicVerifier());
assertThrows(IllegalStateException.class, () -> new BasicVerifier() {});
}
@Test
public void testAnalyze_invalidAload() {
MethodNode methodNode = new MethodNodeBuilder().iconst_0().istore(1).aload(1).vreturn().build();
Executable analyze = () -> newAnalyzer().analyze(CLASS_NAME, methodNode);
String message = assertThrows(AnalyzerException.class, analyze).getMessage();
assertTrue(message.contains("Expected an object reference, but found I"));
}
@Test
public void testAnalyze_invalidAstore() {
MethodNode methodNode = new MethodNodeBuilder().iconst_0().astore(1).vreturn().build();
Executable analyze = () -> newAnalyzer().analyze(CLASS_NAME, methodNode);
String message = assertThrows(AnalyzerException.class, analyze).getMessage();
assertTrue(message.contains("Expected an object reference or a return address, but found I"));
}
@Test
public void testAnalyze_invalidIstore() {
MethodNode methodNode = new MethodNodeBuilder().aconst_null().istore(1).vreturn().build();
Executable analyze = () -> newAnalyzer().analyze(CLASS_NAME, methodNode);
String message = assertThrows(AnalyzerException.class, analyze).getMessage();
assertTrue(message.contains(" Expected I, but found R"));
}
@Test
public void testAnalyze_invalidCheckcast() {
MethodNode methodNode =
new MethodNodeBuilder()
.iconst_0()
.typeInsn(Opcodes.CHECKCAST, "java/lang/String")
.vreturn()
.build();
Executable analyze = () -> newAnalyzer().analyze(CLASS_NAME, methodNode);
String message = assertThrows(AnalyzerException.class, analyze).getMessage();
assertTrue(message.contains("Expected an object reference, but found I"));
}
@Test
public void testAnalyze_invalidArraylength() {
MethodNode methodNode =
new MethodNodeBuilder().iconst_0().insn(Opcodes.ARRAYLENGTH).vreturn().build();
Executable analyze = () -> newAnalyzer().analyze(CLASS_NAME, methodNode);
String message = assertThrows(AnalyzerException.class, analyze).getMessage();
assertTrue(message.contains("Expected an array reference, but found I"));
}
@Test
public void testAnalyze_invalidAthrow() {
MethodNode methodNode =
new MethodNodeBuilder().iconst_0().insn(Opcodes.ATHROW).vreturn().build();
Executable analyze = () -> newAnalyzer().analyze(CLASS_NAME, methodNode);
String message = assertThrows(AnalyzerException.class, analyze).getMessage();
assertTrue(message.contains("Expected an object reference, but found I"));
}
@Test
public void testAnalyze_invalidIneg() {
MethodNode methodNode =
new MethodNodeBuilder().insn(Opcodes.FCONST_0).insn(Opcodes.INEG).vreturn().build();
Executable analyze = () -> newAnalyzer().analyze(CLASS_NAME, methodNode);
String message = assertThrows(AnalyzerException.class, analyze).getMessage();
assertTrue(message.contains("Expected I, but found F"));
}
@Test
public void testAnalyze_invalidIadd() {
MethodNode methodNode =
new MethodNodeBuilder()
.insn(Opcodes.FCONST_0)
.insn(Opcodes.ICONST_0)
.insn(Opcodes.IADD)
.vreturn()
.build();
Executable analyze = () -> newAnalyzer().analyze(CLASS_NAME, methodNode);
String message = assertThrows(AnalyzerException.class, analyze).getMessage();
assertTrue(message.contains("First argument: expected I, but found F"));
}
@Test
public void testAnalyze_invalidIastore() {
MethodNode methodNode =
new MethodNodeBuilder()
.insn(Opcodes.ICONST_1)
.intInsn(Opcodes.NEWARRAY, Opcodes.T_INT)
.insn(Opcodes.FCONST_0)
.insn(Opcodes.ICONST_0)
.insn(Opcodes.IASTORE)
.vreturn()
.build();
Executable analyze = () -> newAnalyzer().analyze(CLASS_NAME, methodNode);
String message = assertThrows(AnalyzerException.class, analyze).getMessage();
assertTrue(message.contains("Second argument: expected I, but found F"));
}
@Test
public void testAnalyze_invalidFastore() {
MethodNode methodNode =
new MethodNodeBuilder()
.insn(Opcodes.ICONST_1)
.intInsn(Opcodes.NEWARRAY, Opcodes.T_FLOAT)
.insn(Opcodes.ICONST_0)
.insn(Opcodes.ICONST_0)
.insn(Opcodes.FASTORE)
.vreturn()
.build();
Executable analyze = () -> newAnalyzer().analyze(CLASS_NAME, methodNode);
String message = assertThrows(AnalyzerException.class, analyze).getMessage();
assertTrue(message.contains("Third argument: expected F, but found I"));
}
@Test
public void testAnalyze_invalidLastore() {
MethodNode methodNode =
new MethodNodeBuilder()
.insn(Opcodes.ICONST_1)
.insn(Opcodes.ICONST_0)
.insn(Opcodes.LCONST_0)
.insn(Opcodes.LASTORE)
.vreturn()
.build();
Executable analyze = () -> newAnalyzer().analyze(CLASS_NAME, methodNode);
String message = assertThrows(AnalyzerException.class, analyze).getMessage();
assertTrue(message.contains("First argument: expected a R array reference, but found I"));
}
@Test
public void testAnalyze_invalidMultianewarray() {
MethodNode methodNode =
new MethodNodeBuilder()
.insn(Opcodes.FCONST_1)
.insn(Opcodes.ICONST_2)
.multiANewArrayInsn("[[I", 2)
.vreturn()
.build();
Executable analyze = () -> newAnalyzer().analyze(CLASS_NAME, methodNode);
String message = assertThrows(AnalyzerException.class, analyze).getMessage();
assertEquals("Error at instruction 2: Expected I, but found F", message);
}
/**
* Tests that the precompiled classes can be successfully analyzed with a BasicVerifier.
*
* @throws AnalyzerException if the test class can't be analyzed.
*/
@ParameterizedTest
@MethodSource(ALL_CLASSES_AND_LATEST_API)
public void testAnalyze_basicVerifier(
final PrecompiledClass classParameter, final Api apiParameter) {
ClassNode classNode = new ClassNode();
new ClassReader(classParameter.getBytes()).accept(classNode, 0);
Analyzer<BasicValue> analyzer = newAnalyzer();
for (MethodNode methodNode : classNode.methods) {
assertDoesNotThrow(() -> analyzer.analyze(classNode.name, methodNode));
}
}
private static Analyzer<BasicValue> newAnalyzer() {
return new Analyzer<>(new BasicVerifier());
}
}
// 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 org.objectweb.asm.tree.analysis;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assumptions.assumeFalse;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.function.Executable;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.Label;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.test.AsmTest;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.MethodNode;
/**
* Unit tests for {@link Analyzer}, when used with a {@link SimpleVerifier}.
*
* @author Eric Bruneton
*/
public class AnalyzerWithSimpleVerifierTest extends AsmTest {
private static final String CLASS_NAME = "C";
@Test
public void testAnalyze_invalidInvokevirtual() {
MethodNode methodNode =
new MethodNodeBuilder()
.insn(Opcodes.ACONST_NULL)
.typeInsn(Opcodes.CHECKCAST, "java/lang/Object")
.methodInsn(Opcodes.INVOKEVIRTUAL, "java/util/ArrayList", "size", "()I", false)
.vreturn()
.build();
Executable analyze = () -> newAnalyzer().analyze(CLASS_NAME, methodNode);
String message = assertThrows(AnalyzerException.class, analyze).getMessage();
assertTrue(
message.contains(
"Method owner: expected Ljava/util/ArrayList;, but found Ljava/lang/Object;"));
}
@Test
public void testAnalyze_invalidInvokeinterface() {
MethodNode methodNode =
new MethodNodeBuilder()
.insn(Opcodes.ACONST_NULL)
.typeInsn(Opcodes.CHECKCAST, "java/util/List")
.insn(Opcodes.FCONST_0)
.methodInsn(
Opcodes.INVOKEINTERFACE, "java/util/List", "get", "(I)Ljava/lang/Object;", true)
.vreturn()
.build();
Executable analyze = () -> newAnalyzer().analyze(CLASS_NAME, methodNode);
String message = assertThrows(AnalyzerException.class, analyze).getMessage();
assertEquals("Error at instruction 3: Argument 1: expected I, but found F", message);
}
@Test
public void testAnalyze_classNotFound() {
Label loopLabel = new Label();
MethodNode methodNode =
new MethodNodeBuilder()
.aload(0)
.astore(1)
.label(loopLabel)
.aconst_null()
.typeInsn(Opcodes.CHECKCAST, "D")
.astore(1)
.go(loopLabel)
.build();
Executable analyze = () -> newAnalyzer().analyze(CLASS_NAME, methodNode);
String message = assertThrows(AnalyzerException.class, analyze).getMessage();
assertTrue(message.contains("Type java.lang.ClassNotFoundException: D not present"));
}
@Test
public void testAnalyze_mergeStackFrames() throws AnalyzerException {
Label loopLabel = new Label();
MethodNode methodNode =
new MethodNodeBuilder(1, 4)
.aload(0)
.astore(1)
.aconst_null()
.typeInsn(Opcodes.CHECKCAST, "java/lang/Number")
.astore(2)
.aload(0)
.astore(3)
.label(loopLabel)
.aconst_null()
.typeInsn(Opcodes.CHECKCAST, "java/lang/Number")
.astore(1)
.aload(0)
.astore(2)
.aconst_null()
.typeInsn(Opcodes.CHECKCAST, "java/lang/Integer")
.astore(3)
.go(loopLabel)
.build();
Executable analyze = () -> newAnalyzer().analyze(CLASS_NAME, methodNode);
assertDoesNotThrow(analyze);
assertDoesNotThrow(() -> MethodNodeBuilder.buildClassWithMethod(methodNode).newInstance());
}
/**
* Tests that the precompiled classes can be successfully analyzed with a SimpleVerifier.
*
* @throws AnalyzerException if the test class can't be analyzed.
*/
@ParameterizedTest
@MethodSource(ALL_CLASSES_AND_LATEST_API)
public void testAnalyze_simpleVerifier(
final PrecompiledClass classParameter, final Api apiParameter) {
ClassNode classNode = new ClassNode();
new ClassReader(classParameter.getBytes()).accept(classNode, 0);
assumeFalse(classNode.methods.isEmpty());
Analyzer<BasicValue> analyzer =
new Analyzer<BasicValue>(
new SimpleVerifier(
Type.getObjectType(classNode.name),
Type.getObjectType(classNode.superName),
(classNode.access & Opcodes.ACC_INTERFACE) != 0));
for (MethodNode methodNode : classNode.methods) {
assertDoesNotThrow(() -> analyzer.analyze(classNode.name, methodNode));
}
}
/**
* Checks that the merge of an ArrayList and an SQLException can be returned as an Iterable. The
* merged type is recomputed by SimpleVerifier as Object (because of limitations of the merging
* algorithm, due to multiple interface inheritance), but the subtyping check is relaxed if the
* super type is an interface type.
*
* @throws AnalyzerException if the test class can't be analyzed.
*/
@Test
public void testIsAssignableFrom_interface() throws AnalyzerException {
Label elseLabel = new Label();
Label endIfLabel = new Label();
MethodNode methodNode =
new MethodNodeBuilder(
"(Ljava/util/ArrayList;Ljava/sql/SQLException;)Ljava/lang/Iterable;", 1, 3)
.aload(1)
.ifnonnull(elseLabel)
.aload(1)
.go(endIfLabel)
.label(elseLabel)
.aload(2)
.label(endIfLabel)
.areturn()
.build();
Executable analyze = () -> newAnalyzer().analyze(CLASS_NAME, methodNode);
assertDoesNotThrow(analyze);
assertDoesNotThrow(() -> MethodNodeBuilder.buildClassWithMethod(methodNode).newInstance());
}
private static Analyzer<BasicValue> newAnalyzer() {
return new Analyzer<>(
new SimpleVerifier(Type.getType("LC;"), Type.getType("Ljava/lang/Number;"), false));
}
}
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
// THE POSSIBILITY OF SUCH DAMAGE. // THE POSSIBILITY OF SUCH DAMAGE.
package org.objectweb.asm.tree.analysis; package org.objectweb.asm.tree.analysis;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertThrows;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
...@@ -38,14 +39,15 @@ import org.objectweb.asm.tree.ClassNode; ...@@ -38,14 +39,15 @@ import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.MethodNode; import org.objectweb.asm.tree.MethodNode;
/** /**
* SourceInterpreter tests. * Unit tests for {@link Analyzer}, when used with a {@link SourceInterpreter}.
* *
* @author Eric Bruneton * @author Eric Bruneton
*/ */
public class SourceInterpreterTest extends AsmTest { public class AnalyzerWithSourceInterpreterTest extends AsmTest {
@Test @Test
public void testConstructor() { public void testConstructor() {
assertDoesNotThrow(() -> new SourceInterpreter());
assertThrows(IllegalStateException.class, () -> new SourceInterpreter() {}); assertThrows(IllegalStateException.class, () -> new SourceInterpreter() {});
} }
...@@ -56,13 +58,14 @@ public class SourceInterpreterTest extends AsmTest { ...@@ -56,13 +58,14 @@ public class SourceInterpreterTest extends AsmTest {
*/ */
@ParameterizedTest @ParameterizedTest
@MethodSource(ALL_CLASSES_AND_LATEST_API) @MethodSource(ALL_CLASSES_AND_LATEST_API)
public void testAnalyze(final PrecompiledClass classParameter, final Api apiParameter) public void testAnalyze_sourceInterpreter(
throws AnalyzerException { final PrecompiledClass classParameter, final Api apiParameter) {
ClassNode classNode = new ClassNode(); ClassNode classNode = new ClassNode();
new ClassReader(classParameter.getBytes()).accept(classNode, 0); new ClassReader(classParameter.getBytes()).accept(classNode, 0);
Analyzer<SourceValue> analyzer = new Analyzer<>(new SourceInterpreter());
for (MethodNode methodNode : classNode.methods) { for (MethodNode methodNode : classNode.methods) {
Analyzer<SourceValue> analyzer = new Analyzer<SourceValue>(new SourceInterpreter()); assertDoesNotThrow(() -> analyzer.analyze(classNode.name, methodNode));
analyzer.analyze(classNode.name, methodNode);
} }
} }
} }
...@@ -33,48 +33,42 @@ import static org.junit.jupiter.api.Assertions.assertNotEquals; ...@@ -33,48 +33,42 @@ import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type; import org.objectweb.asm.Type;
import org.objectweb.asm.tree.InsnNode;
/** /**
* BasicValue and SourceValue tests. * Unit tests for {@link BasicValue}.
* *
* @author Eric Bruneton * @author Eric Bruneton
*/ */
public class ValueTest { public class BasicValueTest {
@Test @Test
public void testBasicValue() { public void testIsReference() {
assertEquals(new BasicValue(null), BasicValue.UNINITIALIZED_VALUE);
assertEquals(new BasicValue(Type.INT_TYPE), BasicValue.INT_VALUE);
assertEquals(BasicValue.INT_VALUE, BasicValue.INT_VALUE);
assertNotEquals(new Object(), BasicValue.INT_VALUE);
assertTrue(BasicValue.REFERENCE_VALUE.isReference()); assertTrue(BasicValue.REFERENCE_VALUE.isReference());
assertTrue(new BasicValue(Type.getObjectType("[I")).isReference()); assertTrue(new BasicValue(Type.getObjectType("[I")).isReference());
assertFalse(BasicValue.UNINITIALIZED_VALUE.isReference()); assertFalse(BasicValue.UNINITIALIZED_VALUE.isReference());
assertFalse(BasicValue.INT_VALUE.isReference()); assertFalse(BasicValue.INT_VALUE.isReference());
}
@Test
public void testEquals() {
assertEquals(new BasicValue(null), BasicValue.UNINITIALIZED_VALUE);
assertEquals(new BasicValue(Type.INT_TYPE), BasicValue.INT_VALUE);
assertEquals(BasicValue.INT_VALUE, BasicValue.INT_VALUE);
assertNotEquals(new Object(), BasicValue.INT_VALUE);
}
@Test
public void testHashCode() {
assertEquals(0, BasicValue.UNINITIALIZED_VALUE.hashCode()); assertEquals(0, BasicValue.UNINITIALIZED_VALUE.hashCode());
assertNotEquals(0, BasicValue.INT_VALUE.hashCode()); assertNotEquals(0, BasicValue.INT_VALUE.hashCode());
}
@Test
public void testToString() {
assertEquals(".", BasicValue.UNINITIALIZED_VALUE.toString()); assertEquals(".", BasicValue.UNINITIALIZED_VALUE.toString());
assertEquals("A", BasicValue.RETURNADDRESS_VALUE.toString()); assertEquals("A", BasicValue.RETURNADDRESS_VALUE.toString());
assertEquals("R", BasicValue.REFERENCE_VALUE.toString()); assertEquals("R", BasicValue.REFERENCE_VALUE.toString());
assertEquals("LI;", new BasicValue(Type.getObjectType("I")).toString()); assertEquals("LI;", new BasicValue(Type.getObjectType("I")).toString());
} }
@Test
public void testSourceValue() {
assertEquals(2, new SourceValue(2).getSize());
assertEquals(new SourceValue(1), new SourceValue(1));
assertNotEquals(new SourceValue(1), new SourceValue(1, new InsnNode(Opcodes.NOP)));
assertNotEquals(new SourceValue(1), new SourceValue(2));
assertNotEquals(new SourceValue(1), null);
assertEquals(0, new SourceValue(1).hashCode());
assertNotEquals(0, new SourceValue(1, new InsnNode(Opcodes.NOP)).hashCode());
}
} }
// 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 org.objectweb.asm.tree.analysis;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.test.ClassFile;
import org.objectweb.asm.tree.MethodNode;
/**
* A builder of {@link MethodNode}, to construct test cases for unit tests.
*
* @author Eric Bruneton
*/
final class MethodNodeBuilder {
private final MethodNode methodNode;
MethodNodeBuilder() {
this(10, 10);
}
MethodNodeBuilder(final int maxStack, final int maxLocals) {
this("()V", maxStack, maxLocals);
}
MethodNodeBuilder(final String descriptor, final int maxStack, final int maxLocals) {
methodNode = new MethodNode(Opcodes.ACC_PUBLIC, "m", descriptor, null, null);
methodNode.maxStack = maxStack;
methodNode.maxLocals = maxLocals;
methodNode.visitCode();
}
MethodNodeBuilder insn(final int opcode) {
methodNode.visitInsn(opcode);
return this;
}
MethodNodeBuilder intInsn(final int opcode, final int operand) {
methodNode.visitIntInsn(opcode, operand);
return this;
}
MethodNodeBuilder typeInsn(final int opcode, final String operand) {
methodNode.visitTypeInsn(opcode, operand);
return this;
}
MethodNodeBuilder methodInsn(
final int opcode,
final String owner,
final String name,
final String descriptor,
final boolean isInterface) {
methodNode.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
return this;
}
MethodNodeBuilder multiANewArrayInsn(final String descriptor, final int numDimensions) {
methodNode.visitMultiANewArrayInsn(descriptor, numDimensions);
return this;
}
MethodNodeBuilder nop() {
methodNode.visitInsn(Opcodes.NOP);
return this;
}
MethodNodeBuilder push() {
methodNode.visitInsn(Opcodes.ICONST_0);
return this;
}
MethodNodeBuilder pop() {
methodNode.visitInsn(Opcodes.POP);
return this;
}
MethodNodeBuilder iconst_0() {
methodNode.visitInsn(Opcodes.ICONST_0);
return this;
}
MethodNodeBuilder istore(final int var) {
methodNode.visitVarInsn(Opcodes.ISTORE, var);
return this;
}
MethodNodeBuilder aload(final int var) {
methodNode.visitVarInsn(Opcodes.ALOAD, var);
return this;
}
MethodNodeBuilder iload(final int var) {
methodNode.visitVarInsn(Opcodes.ILOAD, var);
return this;
}
MethodNodeBuilder astore(final int var) {
methodNode.visitVarInsn(Opcodes.ASTORE, var);
return this;
}
MethodNodeBuilder ret(final int var) {
methodNode.visitVarInsn(Opcodes.RET, var);
return this;
}
MethodNodeBuilder athrow() {
methodNode.visitInsn(Opcodes.ATHROW);
return this;
}
MethodNodeBuilder aconst_null() {
methodNode.visitInsn(Opcodes.ACONST_NULL);
return this;
}
MethodNodeBuilder areturn() {
methodNode.visitInsn(Opcodes.ARETURN);
return this;
}
MethodNodeBuilder vreturn() {
methodNode.visitInsn(Opcodes.RETURN);
return this;
}
MethodNodeBuilder label(final Label label) {
methodNode.visitLabel(label);
return this;
}
MethodNodeBuilder iinc(final int var, final int increment) {
methodNode.visitIincInsn(var, increment);
return this;
}
MethodNodeBuilder go(final Label label) {
methodNode.visitJumpInsn(Opcodes.GOTO, label);
return this;
}
MethodNodeBuilder jsr(final Label label) {
methodNode.visitJumpInsn(Opcodes.JSR, label);
return this;
}
MethodNodeBuilder ifnonnull(final Label label) {
methodNode.visitJumpInsn(Opcodes.IFNONNULL, label);
return this;
}
MethodNodeBuilder ifne(final Label label) {
methodNode.visitJumpInsn(Opcodes.IFNE, label);
return this;
}
MethodNodeBuilder trycatch(final Label start, final Label end, final Label handler) {
return trycatch(start, end, handler, null);
}
MethodNodeBuilder trycatch(
final Label start, final Label end, final Label handler, final String type) {
methodNode.visitTryCatchBlock(start, end, handler, type);
return this;
}
MethodNodeBuilder localVariable(
final String name,
final String descriptor,
final String signature,
final Label start,
final Label end,
final int index) {
methodNode.visitLocalVariable(name, descriptor, signature, start, end, index);
return this;
}
MethodNode build() {
methodNode.visitEnd();
return methodNode;
}
static ClassFile buildClassWithMethod(final MethodNode methodNode) {
ClassWriter classWriter = new ClassWriter(0);
classWriter.visit(Opcodes.V1_5, Opcodes.ACC_PUBLIC, "C", null, "java/lang/Object", null);
MethodVisitor methodVisitor =
classWriter.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "()V", null, null);
methodVisitor.visitCode();
methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
methodVisitor.visitMethodInsn(
Opcodes.INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
methodVisitor.visitInsn(Opcodes.RETURN);
methodVisitor.visitMaxs(1, 1);
methodVisitor.visitEnd();
methodNode.accept(classWriter);
return new ClassFile(classWriter.toByteArray());
}
}
...@@ -27,439 +27,32 @@ ...@@ -27,439 +27,32 @@
// THE POSSIBILITY OF SUCH DAMAGE. // THE POSSIBILITY OF SUCH DAMAGE.
package org.objectweb.asm.tree.analysis; package org.objectweb.asm.tree.analysis;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.Arrays; import java.util.Arrays;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource; import org.junit.jupiter.params.provider.CsvSource;
import org.junit.jupiter.params.provider.MethodSource;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.Label;
import org.objectweb.asm.Opcodes; import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type; import org.objectweb.asm.Type;
import org.objectweb.asm.test.AsmTest;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.MethodNode;
/** /**
* SimpleVerifier tests. * Unit tests for {@link SimpleVerifier}.
* *
* @author Eric Bruneton * @author Eric Bruneton
*/ */
public class SimpleVerifierTest extends AsmTest implements Opcodes { public class SimpleVerifierTest {
private Analyzer<?> anaylzer;
private MethodNode methodNode;
@BeforeEach
public void setUp() {
Type baseType = Type.getType("LC;");
Type superType = Type.getType("Ljava/lang/Number;");
anaylzer = new Analyzer<BasicValue>(new SimpleVerifier(baseType, superType, false));
methodNode = new MethodNode(ACC_PUBLIC, "m", "()V", null, null);
}
private void assertValid() throws AnalyzerException {
methodNode.visitInsn(RETURN);
methodNode.visitMaxs(10, 10);
anaylzer.analyze("C", methodNode);
Frame<?>[] frames = anaylzer.getFrames();
for (Frame<?> frame : frames) {
if (frame != null) {
frame.toString();
}
}
anaylzer.getHandlers(0);
}
private void assertInvalid() {
methodNode.visitInsn(RETURN);
methodNode.visitMaxs(10, 10);
assertThrows(AnalyzerException.class, () -> anaylzer.analyze("C", methodNode));
}
@Test @Test
public void testConstructor() { public void testConstructor() {
assertDoesNotThrow(() -> new SimpleVerifier());
assertThrows(IllegalStateException.class, () -> new SimpleVerifier() {}); assertThrows(IllegalStateException.class, () -> new SimpleVerifier() {});
} }
@Test
public void testInvalidOpcode() {
methodNode.visitInsn(-1);
assertInvalid();
}
@Test
public void testInvalidPop() {
methodNode.visitInsn(LCONST_0);
methodNode.visitInsn(POP);
assertInvalid();
}
@Test
public void testInvalidPop2() {
methodNode.visitInsn(LCONST_0);
methodNode.visitInsn(ICONST_0);
methodNode.visitInsn(POP2);
assertInvalid();
}
@Test
public void testInvalidDup() {
methodNode.visitInsn(LCONST_0);
methodNode.visitInsn(DUP);
assertInvalid();
}
@Test
public void testInvalidDupx1() {
methodNode.visitInsn(LCONST_0);
methodNode.visitInsn(ICONST_0);
methodNode.visitInsn(DUP_X1);
assertInvalid();
}
@Test
public void testInvalidDupx2() {
methodNode.visitInsn(LCONST_0);
methodNode.visitInsn(ICONST_0);
methodNode.visitInsn(ICONST_0);
methodNode.visitInsn(DUP_X2);
assertInvalid();
}
@Test
public void testInvalidDup2() {
methodNode.visitInsn(LCONST_0);
methodNode.visitInsn(ICONST_0);
methodNode.visitInsn(DUP2);
assertInvalid();
}
@Test
public void testInvalidDup2x1() {
methodNode.visitInsn(LCONST_0);
methodNode.visitInsn(ICONST_0);
methodNode.visitInsn(ICONST_0);
methodNode.visitInsn(DUP2_X1);
assertInvalid();
}
@Test
public void testInvalidDup2x2() {
methodNode.visitInsn(LCONST_0);
methodNode.visitInsn(ICONST_0);
methodNode.visitInsn(ICONST_0);
methodNode.visitInsn(ICONST_0);
methodNode.visitInsn(DUP2_X2);
assertInvalid();
}
@Test
public void testInvalidSwap() {
methodNode.visitInsn(LCONST_0);
methodNode.visitInsn(ICONST_0);
methodNode.visitInsn(SWAP);
assertInvalid();
}
@Test
public void testInvalidGetLocal() {
methodNode.visitVarInsn(ALOAD, 10);
assertInvalid();
}
@Test
public void testInvalidSetLocal() {
methodNode.visitInsn(ACONST_NULL);
methodNode.visitVarInsn(ASTORE, 10);
assertInvalid();
}
@Test
public void testInvalidEmptyStack() {
methodNode.visitInsn(POP);
assertInvalid();
}
@Test
public void testInvalidFullStack() {
methodNode.visitInsn(ICONST_0);
methodNode.visitInsn(ICONST_0);
methodNode.visitInsn(ICONST_0);
methodNode.visitInsn(ICONST_0);
methodNode.visitInsn(ICONST_0);
methodNode.visitInsn(ICONST_0);
methodNode.visitInsn(ICONST_0);
methodNode.visitInsn(ICONST_0);
methodNode.visitInsn(ICONST_0);
methodNode.visitInsn(ICONST_0);
methodNode.visitInsn(ICONST_0);
assertInvalid();
}
@Test
public void testInconsistentStackHeights() {
Label ifLabel = new Label();
methodNode.visitInsn(ICONST_0);
methodNode.visitJumpInsn(IFEQ, ifLabel);
methodNode.visitInsn(ICONST_0);
methodNode.visitLabel(ifLabel);
assertInvalid();
}
@Test
public void testInvalidNewArray() {
methodNode.visitInsn(ICONST_1);
methodNode.visitIntInsn(NEWARRAY, -1);
assertInvalid();
}
@Test
public void testInvalidAload() {
methodNode.visitInsn(ICONST_0);
methodNode.visitVarInsn(ISTORE, 1);
methodNode.visitVarInsn(ALOAD, 1);
assertInvalid();
}
@Test
public void testInvalidAstore() {
methodNode.visitInsn(ICONST_0);
methodNode.visitVarInsn(ASTORE, 1);
assertInvalid();
}
@Test
public void testInvalidIstore() {
methodNode.visitInsn(ACONST_NULL);
methodNode.visitVarInsn(ISTORE, 1);
assertInvalid();
}
@Test
public void testInvalidCheckcast() {
methodNode.visitInsn(ICONST_0);
methodNode.visitTypeInsn(CHECKCAST, "java/lang/String");
assertInvalid();
}
@Test
public void testInvalidArraylength() {
methodNode.visitInsn(ICONST_0);
methodNode.visitInsn(ARRAYLENGTH);
assertInvalid();
}
@Test
public void testInvalidAthrow() {
methodNode.visitInsn(ICONST_0);
methodNode.visitInsn(ATHROW);
assertInvalid();
}
@Test
public void testInvalidIneg() {
methodNode.visitInsn(FCONST_0);
methodNode.visitInsn(INEG);
assertInvalid();
}
@Test
public void testInvalidIadd() {
methodNode.visitInsn(FCONST_0);
methodNode.visitInsn(ICONST_0);
methodNode.visitInsn(IADD);
assertInvalid();
}
@Test
public void testInvalidIsub() {
methodNode.visitInsn(ICONST_0);
methodNode.visitInsn(FCONST_0);
methodNode.visitInsn(ISUB);
assertInvalid();
}
@Test
public void testInvalidIastore() {
methodNode.visitInsn(ICONST_1);
methodNode.visitIntInsn(NEWARRAY, T_INT);
methodNode.visitInsn(FCONST_0);
methodNode.visitInsn(ICONST_0);
methodNode.visitInsn(IASTORE);
assertInvalid();
}
@Test
public void testInvalidFastore() {
methodNode.visitInsn(ICONST_1);
methodNode.visitIntInsn(NEWARRAY, T_FLOAT);
methodNode.visitInsn(ICONST_0);
methodNode.visitInsn(ICONST_0);
methodNode.visitInsn(FASTORE);
assertInvalid();
}
@Test
public void testInvalidLastore() {
methodNode.visitInsn(ICONST_1);
methodNode.visitInsn(ICONST_0);
methodNode.visitInsn(LCONST_0);
methodNode.visitInsn(LASTORE);
assertInvalid();
}
@Test
public void testInvalidMultianewarray() {
methodNode.visitInsn(FCONST_1);
methodNode.visitInsn(ICONST_2);
methodNode.visitMultiANewArrayInsn("[[I", 2);
assertInvalid();
}
@Test
public void testInvalidInvokevirtual() {
methodNode.visitInsn(ACONST_NULL);
methodNode.visitTypeInsn(CHECKCAST, "java/lang/Object");
methodNode.visitMethodInsn(INVOKEVIRTUAL, "java/util/ArrayList", "size", "()I", false);
assertInvalid();
}
@Test
public void testInvalidInvokeinterface() {
methodNode.visitInsn(ACONST_NULL);
methodNode.visitTypeInsn(CHECKCAST, "java/util/List");
methodNode.visitInsn(FCONST_0);
methodNode.visitMethodInsn(
INVOKEINTERFACE, "java/util/List", "get", "(I)Ljava/lang/Object;", true);
assertInvalid();
}
@Test
public void testInvalidRet() {
methodNode.visitVarInsn(RET, 1);
assertInvalid();
}
@Test
public void testInvalidFalloff() {
methodNode.visitMaxs(10, 10);
assertThrows(AnalyzerException.class, () -> anaylzer.analyze("C", methodNode));
}
@Test
public void testInvalidSubroutineFalloff() {
Label gotoLabel = new Label();
Label jsrLabel = new Label();
methodNode.visitJumpInsn(GOTO, gotoLabel);
methodNode.visitLabel(jsrLabel);
methodNode.visitVarInsn(ASTORE, 1);
methodNode.visitVarInsn(RET, 1);
methodNode.visitLabel(gotoLabel);
methodNode.visitJumpInsn(JSR, jsrLabel);
methodNode.visitMaxs(10, 10);
assertThrows(AnalyzerException.class, () -> anaylzer.analyze("C", methodNode));
}
@Test
public void testNestedSubroutines() throws AnalyzerException {
Label subroutine1Label = new Label();
Label subroutine2Label = new Label();
methodNode.visitJumpInsn(JSR, subroutine1Label);
methodNode.visitInsn(RETURN);
methodNode.visitLabel(subroutine1Label);
methodNode.visitVarInsn(ASTORE, 1);
methodNode.visitJumpInsn(JSR, subroutine2Label);
methodNode.visitJumpInsn(JSR, subroutine2Label);
methodNode.visitVarInsn(RET, 1);
methodNode.visitLabel(subroutine2Label);
methodNode.visitVarInsn(ASTORE, 2);
methodNode.visitVarInsn(RET, 2);
assertValid();
}
@Test
public void testSubroutineLocalsAccess() throws AnalyzerException {
methodNode.visitCode();
Label startLabel = new Label();
Label exceptionHandler1Label = new Label();
Label exceptionHandler2Label = new Label();
Label subroutineLabel = new Label();
methodNode.visitTryCatchBlock(startLabel, startLabel, exceptionHandler1Label, null);
methodNode.visitTryCatchBlock(
startLabel, exceptionHandler2Label, exceptionHandler2Label, "java/lang/RuntimeException");
methodNode.visitLabel(startLabel);
methodNode.visitJumpInsn(JSR, subroutineLabel);
methodNode.visitInsn(RETURN);
methodNode.visitLabel(exceptionHandler1Label);
methodNode.visitVarInsn(ASTORE, 1);
methodNode.visitJumpInsn(JSR, subroutineLabel);
methodNode.visitVarInsn(ALOAD, 1);
methodNode.visitInsn(ATHROW);
methodNode.visitLabel(subroutineLabel);
methodNode.visitVarInsn(ASTORE, 2);
methodNode.visitInsn(ACONST_NULL);
methodNode.visitVarInsn(ASTORE, 3);
methodNode.visitVarInsn(RET, 2);
methodNode.visitLabel(exceptionHandler2Label);
methodNode.visitVarInsn(ASTORE, 4);
methodNode.visitVarInsn(ALOAD, 4);
methodNode.visitInsn(ATHROW);
assertValid();
}
@Disabled("TODO currently Analyzer can not detect this situation")
@Test
public void testOverlappingSubroutines() {
// The problem is that other overlapping subroutine situations are valid, such as
// when a nested subroutine implicitly returns to its parent subroutine, without a RET.
Label subroutine1Label = new Label();
Label subroutine2Label = new Label();
Label endSubroutineLabel = new Label();
methodNode.visitJumpInsn(JSR, subroutine1Label);
methodNode.visitJumpInsn(JSR, subroutine2Label);
methodNode.visitInsn(RETURN);
methodNode.visitLabel(subroutine1Label);
methodNode.visitVarInsn(ASTORE, 1);
methodNode.visitJumpInsn(GOTO, endSubroutineLabel);
methodNode.visitLabel(subroutine2Label);
methodNode.visitVarInsn(ASTORE, 1);
methodNode.visitLabel(endSubroutineLabel);
methodNode.visitVarInsn(RET, 1);
assertInvalid();
}
@Test
public void testMerge() throws AnalyzerException {
Label loopLabel = new Label();
methodNode.visitVarInsn(ALOAD, 0);
methodNode.visitVarInsn(ASTORE, 1);
methodNode.visitInsn(ACONST_NULL);
methodNode.visitTypeInsn(CHECKCAST, "java/lang/Number");
methodNode.visitVarInsn(ASTORE, 2);
methodNode.visitVarInsn(ALOAD, 0);
methodNode.visitVarInsn(ASTORE, 3);
methodNode.visitLabel(loopLabel);
methodNode.visitInsn(ACONST_NULL);
methodNode.visitTypeInsn(CHECKCAST, "java/lang/Number");
methodNode.visitVarInsn(ASTORE, 1);
methodNode.visitVarInsn(ALOAD, 0);
methodNode.visitVarInsn(ASTORE, 2);
methodNode.visitInsn(ACONST_NULL);
methodNode.visitTypeInsn(CHECKCAST, "java/lang/Integer");
methodNode.visitVarInsn(ASTORE, 3);
methodNode.visitJumpInsn(GOTO, loopLabel);
assertValid();
}
@ParameterizedTest @ParameterizedTest
@CsvSource({ @CsvSource({
"java/lang/String, java/lang/Number, java/lang/Object", "java/lang/String, java/lang/Number, java/lang/Object",
...@@ -468,41 +61,31 @@ public class SimpleVerifierTest extends AsmTest implements Opcodes { ...@@ -468,41 +61,31 @@ public class SimpleVerifierTest extends AsmTest implements Opcodes {
"java/lang/Long, java/util/List, java/lang/Object", "java/lang/Long, java/util/List, java/lang/Object",
"java/util/Map, java/util/List, java/lang/Object" "java/util/Map, java/util/List, java/lang/Object"
}) })
public void testMergeObjectTypes( public void testMerge_objectTypes(
final String internalName1, final String internalName2, final String expectedInternalName) { final String internalName1, final String internalName2, final String expectedInternalName) {
BasicValue value1 = new BasicValue(Type.getObjectType(internalName1)); BasicValue value1 = new BasicValue(Type.getObjectType(internalName1));
BasicValue value2 = new BasicValue(Type.getObjectType(internalName2)); BasicValue value2 = new BasicValue(Type.getObjectType(internalName2));
BasicValue expectedValue = new BasicValue(Type.getObjectType(expectedInternalName));
SimpleVerifier verifier = new SimpleVerifier(); SimpleVerifier verifier = new SimpleVerifier();
assertEquals(expectedValue, verifier.merge(value1, value2));
assertEquals(expectedValue, verifier.merge(value2, value1));
}
@Test BasicValue merge1 = verifier.merge(value1, value2);
public void testClassNotFound() { BasicValue merge2 = verifier.merge(value2, value1);
Label loopLabel = new Label();
methodNode.visitVarInsn(ALOAD, 0); BasicValue expectedValue = new BasicValue(Type.getObjectType(expectedInternalName));
methodNode.visitVarInsn(ASTORE, 1); assertEquals(expectedValue, merge1);
methodNode.visitLabel(loopLabel); assertEquals(expectedValue, merge2);
methodNode.visitInsn(ACONST_NULL);
methodNode.visitTypeInsn(CHECKCAST, "D");
methodNode.visitVarInsn(ASTORE, 1);
methodNode.visitJumpInsn(GOTO, loopLabel);
methodNode.visitMaxs(10, 10);
assertThrows(Exception.class, () -> anaylzer.analyze("C", methodNode));
} }
@Test @Test
public void testIsAssignableFrom() { public void testIsAssignableFrom_subclassWithInterfaces() {
Type baseType = Type.getObjectType("C"); Type baseType = Type.getObjectType("C");
Type superType = Type.getObjectType("D"); Type superType = Type.getObjectType("D");
Type interfaceType = Type.getObjectType("I"); Type interfaceType = Type.getObjectType("I");
new SimpleVerifier(ASM7, baseType, superType, Arrays.asList(interfaceType), false) { SimpleVerifier simpleVerifier =
new SimpleVerifier(Opcodes.ASM7, baseType, superType, Arrays.asList(interfaceType), false) {
void test() { @Override
assertTrue(isAssignableFrom(baseType, baseType)); public boolean isAssignableFrom(final Type type1, final Type type2) {
assertTrue(isAssignableFrom(superType, baseType)); return super.isAssignableFrom(type1, type2);
assertTrue(isAssignableFrom(interfaceType, baseType));
} }
@Override @Override
...@@ -519,74 +102,28 @@ public class SimpleVerifierTest extends AsmTest implements Opcodes { ...@@ -519,74 +102,28 @@ public class SimpleVerifierTest extends AsmTest implements Opcodes {
} }
return super.getClass(type); return super.getClass(type);
} }
}.test(); };
new SimpleVerifier(ASM7, interfaceType, null, null, true) {
void test() { assertTrue(simpleVerifier.isAssignableFrom(baseType, baseType));
assertTrue(isAssignableFrom(interfaceType, baseType)); assertTrue(simpleVerifier.isAssignableFrom(superType, baseType));
assertTrue(isAssignableFrom(interfaceType, Type.getObjectType("[I"))); assertTrue(simpleVerifier.isAssignableFrom(interfaceType, baseType));
assertFalse(isAssignableFrom(interfaceType, Type.INT_TYPE));
} }
@Test
public void testIsAssignableFrom_interface() {
Type baseType = Type.getObjectType("C");
Type interfaceType = Type.getObjectType("I");
SimpleVerifier simpleVerifier =
new SimpleVerifier(Opcodes.ASM7, interfaceType, null, null, true) {
@Override @Override
protected Type getSuperClass(final Type type) { protected Type getSuperClass(final Type type) {
return superType; return Type.getObjectType("java/lang/Object");
}
}.test();
}
/**
* Checks that the merge of an ArrayList and an SQLException can be returned as an Iterable. The
* merged type is recomputed by SimpleVerifier as Object (because of limitations of the merging
* algorithm, due to multiple interface inheritance), but the subtyping check is relaxed if the
* super type is an interface type.
*
* @throws AnalyzerException if the test class can't be analyzed.
*/
@Test
public void testIsAssignableFromInterface() throws AnalyzerException {
methodNode =
new MethodNode(
ACC_PUBLIC | ACC_STATIC,
"m",
"(Ljava/util/ArrayList;Ljava/sql/SQLException;)Ljava/lang/Iterable;",
null,
null);
methodNode.visitCode();
methodNode.visitVarInsn(ALOAD, 0);
Label elseLabel = new Label();
methodNode.visitJumpInsn(IFNULL, elseLabel);
methodNode.visitVarInsn(ALOAD, 0);
Label endIfLabel = new Label();
methodNode.visitJumpInsn(GOTO, endIfLabel);
methodNode.visitLabel(elseLabel);
methodNode.visitVarInsn(ALOAD, 1);
methodNode.visitLabel(endIfLabel);
methodNode.visitInsn(ARETURN);
methodNode.visitMaxs(1, 2);
assertValid();
} }
};
/** assertTrue(simpleVerifier.isAssignableFrom(interfaceType, baseType));
* Tests that the precompiled classes can be successfully analyzed with a SimpleVerifier. assertTrue(simpleVerifier.isAssignableFrom(interfaceType, Type.getObjectType("[I")));
* assertFalse(simpleVerifier.isAssignableFrom(interfaceType, Type.INT_TYPE));
* @throws AnalyzerException if the test class can't be analyzed.
*/
@ParameterizedTest
@MethodSource(ALL_CLASSES_AND_LATEST_API)
public void testAnalyze(final PrecompiledClass classParameter, final Api apiParameter)
throws AnalyzerException {
ClassNode classNode = new ClassNode();
new ClassReader(classParameter.getBytes()).accept(classNode, 0);
for (MethodNode methodNode : classNode.methods) {
Analyzer<BasicValue> analyzer =
new Analyzer<BasicValue>(
new SimpleVerifier(
Type.getObjectType(classNode.name),
Type.getObjectType(classNode.superName),
(classNode.access & Opcodes.ACC_INTERFACE) != 0));
analyzer.analyze(classNode.name, methodNode);
}
} }
} }
...@@ -38,9 +38,10 @@ import java.util.Iterator; ...@@ -38,9 +38,10 @@ import java.util.Iterator;
import java.util.NoSuchElementException; import java.util.NoSuchElementException;
import java.util.Set; import java.util.Set;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.function.Executable;
/** /**
* SmallSet tests. * Unit tests for {@link SmallSet}.
* *
* @author Eric Bruneton * @author Eric Bruneton
*/ */
...@@ -52,69 +53,119 @@ public class SmallSetTest { ...@@ -52,69 +53,119 @@ public class SmallSetTest {
private static final Object ELEMENT4 = new Object(); private static final Object ELEMENT4 = new Object();
@Test @Test
public void testUnion1() { public void testUnion_oneElement_emptySet() {
SmallSet<Object> set1 = new SmallSet<Object>(ELEMENT1); SmallSet<Object> set1 = new SmallSet<>(ELEMENT1);
SmallSet<Object> set2 = new SmallSet<Object>(ELEMENT1); SmallSet<Object> set2 = new SmallSet<>();
Set<Object> union1 = set1.union(set2);
Set<Object> union2 = set2.union(set1);
assertEquals(set1, union1);
assertEquals(set1, union2);
}
@Test
public void testUnion_oneElement_oneElement() {
SmallSet<Object> set1 = new SmallSet<>(ELEMENT1);
SmallSet<Object> set2 = new SmallSet<>(ELEMENT1);
Set<Object> union1 = set1.union(set2); Set<Object> union1 = set1.union(set2);
Set<Object> union2 = set2.union(set1); Set<Object> union2 = set2.union(set1);
assertEquals(union1, union2); assertEquals(union1, union2);
assertEquals(union1, new HashSet<Object>(Arrays.asList(ELEMENT1))); assertEquals(union1, new HashSet<Object>(Arrays.asList(ELEMENT1)));
} }
@Test @Test
public void testUnion2() { public void testUnion_oneElement_twoElements_superSet() {
SmallSet<Object> set1 = newSmallSet(ELEMENT1, ELEMENT2); SmallSet<Object> set1 = newSmallSet(ELEMENT1, ELEMENT2);
SmallSet<Object> set2 = new SmallSet<Object>(ELEMENT1); SmallSet<Object> set2 = new SmallSet<>(ELEMENT1);
Set<Object> union1 = set1.union(set2); Set<Object> union1 = set1.union(set2);
Set<Object> union2 = set2.union(set1); Set<Object> union2 = set2.union(set1);
assertEquals(union1, union2); assertEquals(union1, union2);
assertEquals(union1, new HashSet<Object>(Arrays.asList(ELEMENT1, ELEMENT2))); assertEquals(union1, new HashSet<Object>(Arrays.asList(ELEMENT1, ELEMENT2)));
} }
@Test @Test
public void testUnion2EqualSets() { public void testUnion_twoElements_twoElements_equalSets() {
SmallSet<Object> set1 = newSmallSet(ELEMENT1, ELEMENT2); SmallSet<Object> set1 = newSmallSet(ELEMENT1, ELEMENT2);
SmallSet<Object> set2 = newSmallSet(ELEMENT2, ELEMENT1); SmallSet<Object> set2 = newSmallSet(ELEMENT2, ELEMENT1);
Set<Object> union1 = set1.union(set2); Set<Object> union1 = set1.union(set2);
Set<Object> union2 = set2.union(set1); Set<Object> union2 = set2.union(set1);
assertEquals(union1, union2); assertEquals(union1, union2);
assertEquals(union1, new HashSet<Object>(Arrays.asList(ELEMENT1, ELEMENT2))); assertEquals(union1, new HashSet<Object>(Arrays.asList(ELEMENT1, ELEMENT2)));
} }
@Test @Test
public void testUnion3() { public void testUnion_twoElements_oneElement_distinctSets() {
SmallSet<Object> set1 = newSmallSet(ELEMENT1, ELEMENT2); SmallSet<Object> set1 = newSmallSet(ELEMENT1, ELEMENT2);
SmallSet<Object> set2 = new SmallSet<Object>(ELEMENT3); SmallSet<Object> set2 = new SmallSet<>(ELEMENT3);
Set<Object> union1 = set1.union(set2); Set<Object> union1 = set1.union(set2);
Set<Object> union2 = set2.union(set1); Set<Object> union2 = set2.union(set1);
assertEquals(union1, union2); assertEquals(union1, union2);
assertEquals(union1, new HashSet<Object>(Arrays.asList(ELEMENT1, ELEMENT2, ELEMENT3))); assertEquals(union1, new HashSet<Object>(Arrays.asList(ELEMENT1, ELEMENT2, ELEMENT3)));
} }
@Test @Test
public void testUnion4() { public void testUnion_twoElements_twoElements_distincSets() {
SmallSet<Object> set1 = newSmallSet(ELEMENT1, ELEMENT2); SmallSet<Object> set1 = newSmallSet(ELEMENT1, ELEMENT2);
SmallSet<Object> set2 = newSmallSet(ELEMENT3, ELEMENT4); SmallSet<Object> set2 = newSmallSet(ELEMENT3, ELEMENT4);
Set<Object> union1 = set1.union(set2); Set<Object> union1 = set1.union(set2);
Set<Object> union2 = set2.union(set1); Set<Object> union2 = set2.union(set1);
assertEquals(union1, union2); assertEquals(union1, union2);
assertEquals( assertEquals(
union1, new HashSet<Object>(Arrays.asList(ELEMENT1, ELEMENT2, ELEMENT3, ELEMENT4))); union1, new HashSet<Object>(Arrays.asList(ELEMENT1, ELEMENT2, ELEMENT3, ELEMENT4)));
} }
@Test @Test
public void testIterator() { public void testIterator_next_firstElement() {
Iterator<Object> iterator = newSmallSet(ELEMENT1, ELEMENT2).iterator(); Iterator<Object> iterator = newSmallSet(ELEMENT1, ELEMENT2).iterator();
Object element = iterator.next();
assertEquals(ELEMENT1, element);
assertTrue(iterator.hasNext()); assertTrue(iterator.hasNext());
assertEquals(ELEMENT1, iterator.next()); }
assertTrue(iterator.hasNext());
assertEquals(ELEMENT2, iterator.next()); @Test
public void testIterator_next_secondElement() {
Iterator<Object> iterator = newSmallSet(ELEMENT1, ELEMENT2).iterator();
iterator.next();
Object element = iterator.next();
assertEquals(ELEMENT2, element);
assertFalse(iterator.hasNext()); assertFalse(iterator.hasNext());
assertThrows(NoSuchElementException.class, () -> iterator.next()); }
@Test
public void testIterator_next_noSuchElement() {
Iterator<Object> iterator = newSmallSet(ELEMENT1, ELEMENT2).iterator();
iterator.next();
iterator.next();
Executable next = () -> iterator.next();
assertThrows(NoSuchElementException.class, next);
}
@Test
public void testIterator_remove() {
Iterator<Object> iterator = newSmallSet(ELEMENT1, ELEMENT2).iterator();
iterator.next();
assertThrows(UnsupportedOperationException.class, () -> iterator.remove()); assertThrows(UnsupportedOperationException.class, () -> iterator.remove());
} }
private SmallSet<Object> newSmallSet(final Object element1, final Object element2) { private static SmallSet<Object> newSmallSet(final Object element1, final Object element2) {
return (SmallSet<Object>) new SmallSet<Object>(element1).union(new SmallSet<Object>(element2)); return (SmallSet<Object>) new SmallSet<Object>(element1).union(new SmallSet<Object>(element2));
} }
} }
...@@ -25,50 +25,38 @@ ...@@ -25,50 +25,38 @@
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
// THE POSSIBILITY OF SUCH DAMAGE. // THE POSSIBILITY OF SUCH DAMAGE.
package org.objectweb.asm.tree.analysis;
package org.objectweb.asm.commons; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import org.objectweb.asm.AnnotationVisitor; import org.junit.jupiter.api.Test;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.Opcodes; import org.objectweb.asm.Opcodes;
import org.objectweb.asm.TypePath; import org.objectweb.asm.tree.InsnNode;
/** /**
* A {@link FieldVisitor} adapter for type remapping. * Unit tests for {@link SourceValue} tests.
* *
* @deprecated use {@link FieldRemapper} instead. * @author Eric Bruneton
* @author Eugene Kuleshov
*/ */
@Deprecated public class SourceValueTest {
public class RemappingFieldAdapter extends FieldVisitor {
private final Remapper remapper; @Test
public void testGetSize() {
public RemappingFieldAdapter(final FieldVisitor fieldVisitor, final Remapper remapper) { assertEquals(2, new SourceValue(2).getSize());
this(Opcodes.ASM6, fieldVisitor, remapper);
}
protected RemappingFieldAdapter(
final int api, final FieldVisitor fieldVisitor, final Remapper remapper) {
super(api, fieldVisitor);
this.remapper = remapper;
} }
@Override @Test
public AnnotationVisitor visitAnnotation(final String descriptor, final boolean visible) { public void testEquals() {
AnnotationVisitor annotationVisitor = fv.visitAnnotation(remapper.mapDesc(descriptor), visible); assertEquals(new SourceValue(1), new SourceValue(1));
return annotationVisitor == null assertNotEquals(new SourceValue(1), new SourceValue(1, new InsnNode(Opcodes.NOP)));
? null assertNotEquals(new SourceValue(1), new SourceValue(2));
: new RemappingAnnotationAdapter(annotationVisitor, remapper); assertNotEquals(new SourceValue(1), null);
} }
@Override @Test
public AnnotationVisitor visitTypeAnnotation( public void testHashcode() {
final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) { assertEquals(0, new SourceValue(1).hashCode());
AnnotationVisitor annotationVisitor = assertNotEquals(0, new SourceValue(1, new InsnNode(Opcodes.NOP)).hashCode());
super.visitTypeAnnotation(typeRef, typePath, remapper.mapDesc(descriptor), visible);
return annotationVisitor == null
? null
: new RemappingAnnotationAdapter(annotationVisitor, remapper);
} }
} }
This diff is collapsed.
...@@ -121,8 +121,8 @@ public abstract class AdviceAdapter extends GeneratorAdapter implements Opcodes ...@@ -121,8 +121,8 @@ public abstract class AdviceAdapter extends GeneratorAdapter implements Opcodes
public void visitCode() { public void visitCode() {
super.visitCode(); super.visitCode();
if (isConstructor) { if (isConstructor) {
stackFrame = new ArrayList<Object>(); stackFrame = new ArrayList<>();
forwardJumpStackFrames = new HashMap<Label, List<Object>>(); forwardJumpStackFrames = new HashMap<>();
} else { } else {
onMethodEnter(); onMethodEnter();
} }
...@@ -436,35 +436,21 @@ public abstract class AdviceAdapter extends GeneratorAdapter implements Opcodes ...@@ -436,35 +436,21 @@ public abstract class AdviceAdapter extends GeneratorAdapter implements Opcodes
} }
} }
/**
* Deprecated.
*
* @deprecated use {@link #visitMethodInsn(int, String, String, String, boolean)} instead.
*/
@Deprecated
@Override
public void visitMethodInsn(
final int opcode, final String owner, final String name, final String descriptor) {
if (api >= Opcodes.ASM5) {
super.visitMethodInsn(opcode, owner, name, descriptor);
return;
}
mv.visitMethodInsn(opcode, owner, name, descriptor, opcode == Opcodes.INVOKEINTERFACE);
doVisitMethodInsn(opcode, descriptor);
}
@Override @Override
public void visitMethodInsn( public void visitMethodInsn(
final int opcode, final int opcodeAndSource,
final String owner, final String owner,
final String name, final String name,
final String descriptor, final String descriptor,
final boolean isInterface) { final boolean isInterface) {
if (api < Opcodes.ASM5) { if (api < Opcodes.ASM5 && (opcodeAndSource & Opcodes.SOURCE_DEPRECATED) == 0) {
super.visitMethodInsn(opcode, owner, name, descriptor, isInterface); // Redirect the call to the deprecated version of this method.
super.visitMethodInsn(opcodeAndSource, owner, name, descriptor, isInterface);
return; return;
} }
mv.visitMethodInsn(opcode, owner, name, descriptor, isInterface); super.visitMethodInsn(opcodeAndSource, owner, name, descriptor, isInterface);
int opcode = opcodeAndSource & ~Opcodes.SOURCE_MASK;
doVisitMethodInsn(opcode, descriptor); doVisitMethodInsn(opcode, descriptor);
} }
...@@ -580,7 +566,7 @@ public abstract class AdviceAdapter extends GeneratorAdapter implements Opcodes ...@@ -580,7 +566,7 @@ public abstract class AdviceAdapter extends GeneratorAdapter implements Opcodes
// initialized twice), so this is not issue (in the sense that there is no risk to emit a wrong // initialized twice), so this is not issue (in the sense that there is no risk to emit a wrong
// 'onMethodEnter'). // 'onMethodEnter').
if (isConstructor && !forwardJumpStackFrames.containsKey(handler)) { if (isConstructor && !forwardJumpStackFrames.containsKey(handler)) {
List<Object> handlerStackFrame = new ArrayList<Object>(); List<Object> handlerStackFrame = new ArrayList<>();
handlerStackFrame.add(OTHER); handlerStackFrame.add(OTHER);
forwardJumpStackFrames.put(handler, handlerStackFrame); forwardJumpStackFrames.put(handler, handlerStackFrame);
} }
...@@ -597,7 +583,7 @@ public abstract class AdviceAdapter extends GeneratorAdapter implements Opcodes ...@@ -597,7 +583,7 @@ public abstract class AdviceAdapter extends GeneratorAdapter implements Opcodes
if (forwardJumpStackFrames.containsKey(label)) { if (forwardJumpStackFrames.containsKey(label)) {
return; return;
} }
forwardJumpStackFrames.put(label, new ArrayList<Object>(stackFrame)); forwardJumpStackFrames.put(label, new ArrayList<>(stackFrame));
} }
private Object popValue() { private Object popValue() {
......
...@@ -143,9 +143,9 @@ public class AnalyzerAdapter extends MethodVisitor { ...@@ -143,9 +143,9 @@ public class AnalyzerAdapter extends MethodVisitor {
final MethodVisitor methodVisitor) { final MethodVisitor methodVisitor) {
super(api, methodVisitor); super(api, methodVisitor);
this.owner = owner; this.owner = owner;
locals = new ArrayList<Object>(); locals = new ArrayList<>();
stack = new ArrayList<Object>(); stack = new ArrayList<>();
uninitializedTypes = new HashMap<Object, Object>(); uninitializedTypes = new HashMap<>();
if ((access & Opcodes.ACC_STATIC) == 0) { if ((access & Opcodes.ACC_STATIC) == 0) {
if ("<init>".equals(name)) { if ("<init>".equals(name)) {
...@@ -205,8 +205,8 @@ public class AnalyzerAdapter extends MethodVisitor { ...@@ -205,8 +205,8 @@ public class AnalyzerAdapter extends MethodVisitor {
this.locals.clear(); this.locals.clear();
this.stack.clear(); this.stack.clear();
} else { } else {
this.locals = new ArrayList<Object>(); this.locals = new ArrayList<>();
this.stack = new ArrayList<Object>(); this.stack = new ArrayList<>();
} }
visitFrameTypes(numLocal, local, this.locals); visitFrameTypes(numLocal, local, this.locals);
visitFrameTypes(numStack, stack, this.stack); visitFrameTypes(numStack, stack, this.stack);
...@@ -258,7 +258,7 @@ public class AnalyzerAdapter extends MethodVisitor { ...@@ -258,7 +258,7 @@ public class AnalyzerAdapter extends MethodVisitor {
if (opcode == Opcodes.NEW) { if (opcode == Opcodes.NEW) {
if (labels == null) { if (labels == null) {
Label label = new Label(); Label label = new Label();
labels = new ArrayList<Label>(3); labels = new ArrayList<>(3);
labels.add(label); labels.add(label);
if (mv != null) { if (mv != null) {
mv.visitLabel(label); mv.visitLabel(label);
...@@ -279,45 +279,21 @@ public class AnalyzerAdapter extends MethodVisitor { ...@@ -279,45 +279,21 @@ public class AnalyzerAdapter extends MethodVisitor {
execute(opcode, 0, descriptor); execute(opcode, 0, descriptor);
} }
/**
* Deprecated.
*
* @deprecated use {@link #visitMethodInsn(int, String, String, String, boolean)} instead.
*/
@Deprecated
@Override
public void visitMethodInsn(
final int opcode, final String owner, final String name, final String descriptor) {
if (api >= Opcodes.ASM5) {
super.visitMethodInsn(opcode, owner, name, descriptor);
return;
}
doVisitMethodInsn(opcode, owner, name, descriptor, opcode == Opcodes.INVOKEINTERFACE);
}
@Override @Override
public void visitMethodInsn( public void visitMethodInsn(
final int opcode, final int opcodeAndSource,
final String owner, final String owner,
final String name, final String name,
final String descriptor, final String descriptor,
final boolean isInterface) { final boolean isInterface) {
if (api < Opcodes.ASM5) { if (api < Opcodes.ASM5 && (opcodeAndSource & Opcodes.SOURCE_DEPRECATED) == 0) {
super.visitMethodInsn(opcode, owner, name, descriptor, isInterface); // Redirect the call to the deprecated version of this method.
super.visitMethodInsn(opcodeAndSource, owner, name, descriptor, isInterface);
return; return;
} }
doVisitMethodInsn(opcode, owner, name, descriptor, isInterface); super.visitMethodInsn(opcodeAndSource, owner, name, descriptor, isInterface);
} int opcode = opcodeAndSource & ~Opcodes.SOURCE_MASK;
private void doVisitMethodInsn(
final int opcode,
final String owner,
final String name,
final String descriptor,
final boolean isInterface) {
if (mv != null) {
mv.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
}
if (this.locals == null) { if (this.locals == null) {
labels = null; labels = null;
return; return;
...@@ -378,7 +354,7 @@ public class AnalyzerAdapter extends MethodVisitor { ...@@ -378,7 +354,7 @@ public class AnalyzerAdapter extends MethodVisitor {
public void visitLabel(final Label label) { public void visitLabel(final Label label) {
super.visitLabel(label); super.visitLabel(label);
if (labels == null) { if (labels == null) {
labels = new ArrayList<Label>(3); labels = new ArrayList<>(3);
} }
labels.add(label); labels.add(label);
} }
...@@ -495,9 +471,12 @@ public class AnalyzerAdapter extends MethodVisitor { ...@@ -495,9 +471,12 @@ public class AnalyzerAdapter extends MethodVisitor {
maxStack = Math.max(maxStack, stack.size()); maxStack = Math.max(maxStack, stack.size());
} }
private void pushDescriptor(final String descriptor) { private void pushDescriptor(final String fieldOrMethodDescriptor) {
int index = descriptor.charAt(0) == '(' ? descriptor.indexOf(')') + 1 : 0; String descriptor =
switch (descriptor.charAt(index)) { fieldOrMethodDescriptor.charAt(0) == '('
? Type.getReturnType(fieldOrMethodDescriptor).getDescriptor()
: fieldOrMethodDescriptor;
switch (descriptor.charAt(0)) {
case 'V': case 'V':
return; return;
case 'Z': case 'Z':
...@@ -519,18 +498,10 @@ public class AnalyzerAdapter extends MethodVisitor { ...@@ -519,18 +498,10 @@ public class AnalyzerAdapter extends MethodVisitor {
push(Opcodes.TOP); push(Opcodes.TOP);
return; return;
case '[': case '[':
if (index == 0) {
push(descriptor); push(descriptor);
} else {
push(descriptor.substring(index, descriptor.length()));
}
break; break;
case 'L': case 'L':
if (index == 0) {
push(descriptor.substring(1, descriptor.length() - 1)); push(descriptor.substring(1, descriptor.length() - 1));
} else {
push(descriptor.substring(index + 1, descriptor.length() - 1));
}
break; break;
default: default:
throw new AssertionError(); throw new AssertionError();
...@@ -566,6 +537,9 @@ public class AnalyzerAdapter extends MethodVisitor { ...@@ -566,6 +537,9 @@ public class AnalyzerAdapter extends MethodVisitor {
} }
private void execute(final int opcode, final int intArg, final String stringArg) { private void execute(final int opcode, final int intArg, final String stringArg) {
if (opcode == Opcodes.JSR || opcode == Opcodes.RET) {
throw new IllegalArgumentException("JSR/RET are not supported");
}
if (this.locals == null) { if (this.locals == null) {
labels = null; labels = null;
return; return;
...@@ -866,9 +840,6 @@ public class AnalyzerAdapter extends MethodVisitor { ...@@ -866,9 +840,6 @@ public class AnalyzerAdapter extends MethodVisitor {
pop(4); pop(4);
push(Opcodes.INTEGER); push(Opcodes.INTEGER);
break; break;
case Opcodes.JSR:
case Opcodes.RET:
throw new IllegalArgumentException("JSR/RET are not supported");
case Opcodes.GETSTATIC: case Opcodes.GETSTATIC:
pushDescriptor(stringArg); pushDescriptor(stringArg);
break; break;
......
...@@ -111,42 +111,20 @@ public class CodeSizeEvaluator extends MethodVisitor implements Opcodes { ...@@ -111,42 +111,20 @@ public class CodeSizeEvaluator extends MethodVisitor implements Opcodes {
super.visitFieldInsn(opcode, owner, name, descriptor); super.visitFieldInsn(opcode, owner, name, descriptor);
} }
/**
* Deprecated.
*
* @deprecated use {@link #visitMethodInsn(int, String, String, String, boolean)} instead.
*/
@Deprecated
@Override
public void visitMethodInsn(
final int opcode, final String owner, final String name, final String descriptor) {
if (api >= Opcodes.ASM5) {
super.visitMethodInsn(opcode, owner, name, descriptor);
return;
}
doVisitMethodInsn(opcode, owner, name, descriptor, opcode == Opcodes.INVOKEINTERFACE);
}
@Override @Override
public void visitMethodInsn( public void visitMethodInsn(
final int opcode, final int opcodeAndSource,
final String owner, final String owner,
final String name, final String name,
final String descriptor, final String descriptor,
final boolean isInterface) { final boolean isInterface) {
if (api < Opcodes.ASM5) { if (api < Opcodes.ASM5 && (opcodeAndSource & Opcodes.SOURCE_DEPRECATED) == 0) {
super.visitMethodInsn(opcode, owner, name, descriptor, isInterface); // Redirect the call to the deprecated version of this method.
super.visitMethodInsn(opcodeAndSource, owner, name, descriptor, isInterface);
return; return;
} }
doVisitMethodInsn(opcode, owner, name, descriptor, isInterface); int opcode = opcodeAndSource & ~Opcodes.SOURCE_MASK;
}
private void doVisitMethodInsn(
final int opcode,
final String owner,
final String name,
final String descriptor,
final boolean isInterface) {
if (opcode == INVOKEINTERFACE) { if (opcode == INVOKEINTERFACE) {
minSize += 5; minSize += 5;
maxSize += 5; maxSize += 5;
...@@ -154,9 +132,7 @@ public class CodeSizeEvaluator extends MethodVisitor implements Opcodes { ...@@ -154,9 +132,7 @@ public class CodeSizeEvaluator extends MethodVisitor implements Opcodes {
minSize += 3; minSize += 3;
maxSize += 3; maxSize += 3;
} }
if (mv != null) { super.visitMethodInsn(opcodeAndSource, owner, name, descriptor, isInterface);
mv.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
}
} }
@Override @Override
......