Skip to content
Commits on Source (5)
......@@ -124,7 +124,7 @@ public class Analyzer<V extends Value> implements Opcodes {
for (int j = startIndex; j < endIndex; ++j) {
List<TryCatchBlockNode> insnHandlers = handlers[j];
if (insnHandlers == null) {
insnHandlers = new ArrayList<TryCatchBlockNode>();
insnHandlers = new ArrayList<>();
handlers[j] = insnHandlers;
}
insnHandlers.add(tryCatchBlock);
......@@ -134,11 +134,11 @@ public class Analyzer<V extends Value> implements Opcodes {
// For each instruction, compute the subroutine to which it belongs.
// Follow the main 'subroutine', and collect the jsr instructions to nested subroutines.
Subroutine main = new Subroutine(null, method.maxLocals, null);
List<AbstractInsnNode> jsrInsns = new ArrayList<AbstractInsnNode>();
List<AbstractInsnNode> jsrInsns = new ArrayList<>();
findSubroutine(0, main, jsrInsns);
// Follow the nested subroutines, and collect their own nested subroutines, until all
// subroutines are found.
Map<LabelNode, Subroutine> jsrSubroutines = new HashMap<LabelNode, Subroutine>();
Map<LabelNode, Subroutine> jsrSubroutines = new HashMap<>();
while (!jsrInsns.isEmpty()) {
JumpInsnNode jsrInsn = (JumpInsnNode) jsrInsns.remove(0);
Subroutine subroutine = jsrSubroutines.get(jsrInsn.label);
......@@ -233,7 +233,7 @@ public class Analyzer<V extends Value> implements Opcodes {
}
} else if (insnOpcode == RET) {
if (subroutine == null) {
throw new AnalyzerException(insnNode, "RET instruction outside of a sub routine");
throw new AnalyzerException(insnNode, "RET instruction outside of a subroutine");
}
for (int i = 0; i < subroutine.callers.size(); ++i) {
JumpInsnNode caller = subroutine.callers.get(i);
......@@ -313,7 +313,7 @@ public class Analyzer<V extends Value> implements Opcodes {
private void findSubroutine(
final int insnIndex, final Subroutine subroutine, final List<AbstractInsnNode> jsrInsns)
throws AnalyzerException {
ArrayList<Integer> instructionIndicesToProcess = new ArrayList<Integer>();
ArrayList<Integer> instructionIndicesToProcess = new ArrayList<>();
instructionIndicesToProcess.add(insnIndex);
while (!instructionIndicesToProcess.isEmpty()) {
int currentInsnIndex =
......@@ -459,7 +459,7 @@ public class Analyzer<V extends Value> implements Opcodes {
* @return the created frame.
*/
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 {
* @return the created 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> {
* this frame corresponds to the successor of the jump instruction (i.e. the next instruction
* 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.
......@@ -159,7 +161,7 @@ public class Frame<V extends Value> {
*/
public V getLocal(final int index) {
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];
}
......@@ -173,7 +175,7 @@ public class Frame<V extends Value> {
*/
public void setLocal(final int index, final V value) {
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;
}
......@@ -206,7 +208,7 @@ public class Frame<V extends Value> {
* @param value the new value of the stack slot.
* @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;
}
......@@ -598,36 +600,11 @@ public class Frame<V extends Value> {
case Opcodes.INVOKESPECIAL:
case Opcodes.INVOKESTATIC:
case Opcodes.INVOKEINTERFACE:
{
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;
}
executeInvokeInsn(insn, ((MethodInsnNode) insn).desc, interpreter);
break;
case Opcodes.INVOKEDYNAMIC:
{
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;
}
executeInvokeInsn(insn, ((InvokeDynamicInsnNode) insn).desc, interpreter);
break;
case Opcodes.NEW:
push(interpreter.newOperation(insn));
break;
......@@ -648,7 +625,7 @@ public class Frame<V extends Value> {
interpreter.unaryOperation(insn, pop());
break;
case Opcodes.MULTIANEWARRAY:
List<V> valueList = new ArrayList<V>();
List<V> valueList = new ArrayList<>();
for (int i = ((MultiANewArrayInsnNode) insn).dims; i > 0; --i) {
valueList.add(0, pop());
}
......@@ -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.
*
......
......@@ -87,12 +87,18 @@ final class SmallSet<T> extends AbstractSet<T> {
@Override
public Iterator<T> iterator() {
return new IteratorImpl<T>(element1, element2);
return new IteratorImpl<>(element1, element2);
}
@Override
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> {
if (otherSet.element2 == null) {
// If this set also contains exactly one element, we have two distinct elements.
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.element1 == element1 || otherSet.element1 == element2) {
......@@ -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
// to store the result.
HashSet<T> result = new HashSet<T>(4);
HashSet<T> result = new HashSet<>(4);
result.add(element1);
if (element2 != null) {
result.add(element2);
......
......@@ -204,7 +204,7 @@ public class SourceInterpreter extends Interpreter<SourceValue> implements Opcod
}
}
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(value2.insns);
return new SourceValue(Math.min(value1.size, value2.size), setUnion);
......
......@@ -77,7 +77,7 @@ public class SourceValue implements Value {
*/
public SourceValue(final int size, final AbstractInsnNode insnNode) {
this.size = size;
this.insns = new SmallSet<AbstractInsnNode>(insnNode);
this.insns = new SmallSet<>(insnNode);
}
/**
......
......@@ -61,7 +61,7 @@ final class Subroutine {
Subroutine(final LabelNode start, final int maxLocals, final JumpInsnNode caller) {
this.start = start;
this.localsUsed = new boolean[maxLocals];
this.callers = new ArrayList<JumpInsnNode>();
this.callers = new ArrayList<>();
callers.add(caller);
}
......@@ -73,7 +73,7 @@ final class Subroutine {
Subroutine(final Subroutine subroutine) {
this.start = subroutine.start;
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);
}
......
......@@ -28,6 +28,7 @@
package org.objectweb.asm.tree.analysis;
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.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTimeoutPreemptively;
......@@ -36,7 +37,9 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
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;
......@@ -46,34 +49,67 @@ import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.MethodNode;
/**
* BasicInterpreter tests.
* Unit tests for {@link Analyzer}, when used with a {@link BasicInterpreter}.
*
* @author Eric Bruneton
*/
public class BasicInterpreterTest extends AsmTest {
public class AnalyzerWithBasicInterpreterTest extends AsmTest {
private static final String CLASS_NAME = "C";
@Test
public void testConstructor() {
assertDoesNotThrow(() -> 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
* different control flow paths, with different local variable types (#316204).
* Tests that the precompiled classes can be successfully analyzed with a BasicInterpreter, and
* 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.
*/
@Test
public void testMergeWithJsrReachableFromTwoDifferentPaths()
throws IOException, AnalyzerException {
ClassReader classReader =
new ClassReader(Files.newInputStream(Paths.get("src/test/resources/Issue316204.class")));
@ParameterizedTest
@MethodSource(ALL_CLASSES_AND_LATEST_API)
public void testAnalyze_basicInterpreter(
final PrecompiledClass classParameter, final Api apiParameter) throws AnalyzerException {
ClassNode classNode = new ClassNode();
classReader.accept(classNode, 0);
Analyzer<BasicValue> analyzer = new Analyzer<>(new BasicInterpreter());
analyzer.analyze(classNode.name, getMethod(classNode, "basicStopBundles"));
assertEquals("RIR..... ", analyzer.getFrames()[104].toString());
new ClassReader(classParameter.getBytes()).accept(classNode, 0);
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
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,52 +120,47 @@ public class BasicInterpreterTest extends AsmTest {
* @throws AnalyzerException if the test class can't be analyzed.
*/
@Test
public void testAnalyzeWithBadInterpreter() throws AnalyzerException {
public void testAnalyze_badInterpreter() {
ClassNode classNode = new ClassNode();
new ClassReader(PrecompiledClass.JDK8_ALL_FRAMES.getBytes()).accept(classNode, 0);
Analyzer<BasicValue> analyzer =
new Analyzer<BasicValue>(
new BasicInterpreter(Opcodes.ASM7) {
@Override
public BasicValue merge(final BasicValue value1, final BasicValue value2) {
return new BasicValue(super.merge(value1, value2).getType());
}
});
ArrayList<Executable> analyses = new ArrayList<>();
for (MethodNode methodNode : classNode.methods) {
Analyzer<BasicValue> analyzer =
new Analyzer<BasicValue>(
new BasicInterpreter(Opcodes.ASM7) {
@Override
public BasicValue merge(final BasicValue value1, final BasicValue value2) {
return new BasicValue(super.merge(value1, value2).getType());
}
});
assertTimeoutPreemptively(ofSeconds(1), () -> analyzer.analyze("Test", methodNode));
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
* that Analyzer can be subclassed to use custom frames.
* Tests that stack map frames are correctly merged when a JSR instruction can be reached from two
* 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.
*/
@ParameterizedTest
@MethodSource(ALL_CLASSES_AND_LATEST_API)
public void testAnalyze(final PrecompiledClass classParameter, final Api apiParameter)
throws AnalyzerException {
@Test
public void testAnalyze_mergeWithJsrReachableFromTwoDifferentPaths()
throws IOException, AnalyzerException {
ClassReader classReader =
new ClassReader(Files.newInputStream(Paths.get("src/test/resources/Issue316204.class")));
ClassNode classNode = new ClassNode();
new ClassReader(classParameter.getBytes()).accept(classNode, 0);
for (MethodNode methodNode : classNode.methods) {
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
protected Frame<BasicValue> newFrame(final Frame<? extends BasicValue> src) {
return new CustomFrame(src);
}
};
analyzer.analyze(classNode.name, methodNode);
for (Frame<? extends BasicValue> frame : analyzer.getFrames()) {
assertTrue(frame == null || frame instanceof CustomFrame);
}
}
classReader.accept(classNode, 0);
Analyzer<BasicValue> analyzer = new Analyzer<>(new BasicInterpreter());
analyzer.analyze(classNode.name, getMethod(classNode, "basicStopBundles"));
assertEquals("RIR..... ", analyzer.getFrames()[104].toString());
}
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 @@
// 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.assertThrows;
import org.junit.jupiter.api.Test;
......@@ -38,14 +39,15 @@ import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.MethodNode;
/**
* SourceInterpreter tests.
* Unit tests for {@link Analyzer}, when used with a {@link SourceInterpreter}.
*
* @author Eric Bruneton
*/
public class SourceInterpreterTest extends AsmTest {
public class AnalyzerWithSourceInterpreterTest extends AsmTest {
@Test
public void testConstructor() {
assertDoesNotThrow(() -> new SourceInterpreter());
assertThrows(IllegalStateException.class, () -> new SourceInterpreter() {});
}
......@@ -56,13 +58,14 @@ public class SourceInterpreterTest extends AsmTest {
*/
@ParameterizedTest
@MethodSource(ALL_CLASSES_AND_LATEST_API)
public void testAnalyze(final PrecompiledClass classParameter, final Api apiParameter)
throws AnalyzerException {
public void testAnalyze_sourceInterpreter(
final PrecompiledClass classParameter, final Api apiParameter) {
ClassNode classNode = new ClassNode();
new ClassReader(classParameter.getBytes()).accept(classNode, 0);
Analyzer<SourceValue> analyzer = new Analyzer<>(new SourceInterpreter());
for (MethodNode methodNode : classNode.methods) {
Analyzer<SourceValue> analyzer = new Analyzer<SourceValue>(new SourceInterpreter());
analyzer.analyze(classNode.name, methodNode);
assertDoesNotThrow(() -> analyzer.analyze(classNode.name, methodNode));
}
}
}
......@@ -33,48 +33,42 @@ import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.Test;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.InsnNode;
/**
* BasicValue and SourceValue tests.
* Unit tests for {@link BasicValue}.
*
* @author Eric Bruneton
*/
public class ValueTest {
public class BasicValueTest {
@Test
public void testBasicValue() {
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);
public void testIsReference() {
assertTrue(BasicValue.REFERENCE_VALUE.isReference());
assertTrue(new BasicValue(Type.getObjectType("[I")).isReference());
assertFalse(BasicValue.UNINITIALIZED_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());
assertNotEquals(0, BasicValue.INT_VALUE.hashCode());
}
@Test
public void testToString() {
assertEquals(".", BasicValue.UNINITIALIZED_VALUE.toString());
assertEquals("A", BasicValue.RETURNADDRESS_VALUE.toString());
assertEquals("R", BasicValue.REFERENCE_VALUE.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 @@
// 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.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
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.params.ParameterizedTest;
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.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
*/
public class SimpleVerifierTest extends AsmTest implements Opcodes {
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));
}
public class SimpleVerifierTest {
@Test
public void testConstructor() {
assertDoesNotThrow(() -> 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
@CsvSource({
"java/lang/String, java/lang/Number, java/lang/Object",
......@@ -468,125 +61,69 @@ public class SimpleVerifierTest extends AsmTest implements Opcodes {
"java/lang/Long, 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) {
BasicValue value1 = new BasicValue(Type.getObjectType(internalName1));
BasicValue value2 = new BasicValue(Type.getObjectType(internalName2));
BasicValue expectedValue = new BasicValue(Type.getObjectType(expectedInternalName));
SimpleVerifier verifier = new SimpleVerifier();
assertEquals(expectedValue, verifier.merge(value1, value2));
assertEquals(expectedValue, verifier.merge(value2, value1));
}
@Test
public void testClassNotFound() {
Label loopLabel = new Label();
methodNode.visitVarInsn(ALOAD, 0);
methodNode.visitVarInsn(ASTORE, 1);
methodNode.visitLabel(loopLabel);
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));
BasicValue merge1 = verifier.merge(value1, value2);
BasicValue merge2 = verifier.merge(value2, value1);
BasicValue expectedValue = new BasicValue(Type.getObjectType(expectedInternalName));
assertEquals(expectedValue, merge1);
assertEquals(expectedValue, merge2);
}
@Test
public void testIsAssignableFrom() {
public void testIsAssignableFrom_subclassWithInterfaces() {
Type baseType = Type.getObjectType("C");
Type superType = Type.getObjectType("D");
Type interfaceType = Type.getObjectType("I");
new SimpleVerifier(ASM7, baseType, superType, Arrays.asList(interfaceType), false) {
void test() {
assertTrue(isAssignableFrom(baseType, baseType));
assertTrue(isAssignableFrom(superType, baseType));
assertTrue(isAssignableFrom(interfaceType, baseType));
}
@Override
protected Class<?> getClass(final Type type) {
// Return dummy classes, to make sure isAssignable in test() does not rely on them.
if (type == baseType) {
return int.class;
}
if (type == superType) {
return float.class;
}
if (type == interfaceType) {
return double.class;
}
return super.getClass(type);
}
}.test();
new SimpleVerifier(ASM7, interfaceType, null, null, true) {
void test() {
assertTrue(isAssignableFrom(interfaceType, baseType));
assertTrue(isAssignableFrom(interfaceType, Type.getObjectType("[I")));
assertFalse(isAssignableFrom(interfaceType, Type.INT_TYPE));
}
@Override
protected Type getSuperClass(final Type type) {
return superType;
}
}.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();
}
/**
* 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(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);
}
SimpleVerifier simpleVerifier =
new SimpleVerifier(Opcodes.ASM7, baseType, superType, Arrays.asList(interfaceType), false) {
@Override
public boolean isAssignableFrom(final Type type1, final Type type2) {
return super.isAssignableFrom(type1, type2);
}
@Override
protected Class<?> getClass(final Type type) {
// Return dummy classes, to make sure isAssignable in test() does not rely on them.
if (type == baseType) {
return int.class;
}
if (type == superType) {
return float.class;
}
if (type == interfaceType) {
return double.class;
}
return super.getClass(type);
}
};
assertTrue(simpleVerifier.isAssignableFrom(baseType, baseType));
assertTrue(simpleVerifier.isAssignableFrom(superType, baseType));
assertTrue(simpleVerifier.isAssignableFrom(interfaceType, baseType));
}
@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
protected Type getSuperClass(final Type type) {
return Type.getObjectType("java/lang/Object");
}
};
assertTrue(simpleVerifier.isAssignableFrom(interfaceType, baseType));
assertTrue(simpleVerifier.isAssignableFrom(interfaceType, Type.getObjectType("[I")));
assertFalse(simpleVerifier.isAssignableFrom(interfaceType, Type.INT_TYPE));
}
}
......@@ -38,9 +38,10 @@ import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Set;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.function.Executable;
/**
* SmallSet tests.
* Unit tests for {@link SmallSet}.
*
* @author Eric Bruneton
*/
......@@ -52,69 +53,119 @@ public class SmallSetTest {
private static final Object ELEMENT4 = new Object();
@Test
public void testUnion1() {
SmallSet<Object> set1 = new SmallSet<Object>(ELEMENT1);
SmallSet<Object> set2 = new SmallSet<Object>(ELEMENT1);
public void testUnion_oneElement_emptySet() {
SmallSet<Object> set1 = new SmallSet<>(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> union2 = set2.union(set1);
assertEquals(union1, union2);
assertEquals(union1, new HashSet<Object>(Arrays.asList(ELEMENT1)));
}
@Test
public void testUnion2() {
public void testUnion_oneElement_twoElements_superSet() {
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> union2 = set2.union(set1);
assertEquals(union1, union2);
assertEquals(union1, new HashSet<Object>(Arrays.asList(ELEMENT1, ELEMENT2)));
}
@Test
public void testUnion2EqualSets() {
public void testUnion_twoElements_twoElements_equalSets() {
SmallSet<Object> set1 = newSmallSet(ELEMENT1, ELEMENT2);
SmallSet<Object> set2 = newSmallSet(ELEMENT2, ELEMENT1);
Set<Object> union1 = set1.union(set2);
Set<Object> union2 = set2.union(set1);
assertEquals(union1, union2);
assertEquals(union1, new HashSet<Object>(Arrays.asList(ELEMENT1, ELEMENT2)));
}
@Test
public void testUnion3() {
public void testUnion_twoElements_oneElement_distinctSets() {
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> union2 = set2.union(set1);
assertEquals(union1, union2);
assertEquals(union1, new HashSet<Object>(Arrays.asList(ELEMENT1, ELEMENT2, ELEMENT3)));
}
@Test
public void testUnion4() {
public void testUnion_twoElements_twoElements_distincSets() {
SmallSet<Object> set1 = newSmallSet(ELEMENT1, ELEMENT2);
SmallSet<Object> set2 = newSmallSet(ELEMENT3, ELEMENT4);
Set<Object> union1 = set1.union(set2);
Set<Object> union2 = set2.union(set1);
assertEquals(union1, union2);
assertEquals(
union1, new HashSet<Object>(Arrays.asList(ELEMENT1, ELEMENT2, ELEMENT3, ELEMENT4)));
}
@Test
public void testIterator() {
public void testIterator_next_firstElement() {
Iterator<Object> iterator = newSmallSet(ELEMENT1, ELEMENT2).iterator();
Object element = iterator.next();
assertEquals(ELEMENT1, element);
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());
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());
}
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));
}
}
......@@ -25,50 +25,38 @@
// 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;
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.objectweb.asm.FieldVisitor;
import org.junit.jupiter.api.Test;
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 Eugene Kuleshov
* @author Eric Bruneton
*/
@Deprecated
public class RemappingFieldAdapter extends FieldVisitor {
public class SourceValueTest {
private final Remapper remapper;
public RemappingFieldAdapter(final FieldVisitor fieldVisitor, final Remapper remapper) {
this(Opcodes.ASM6, fieldVisitor, remapper);
}
protected RemappingFieldAdapter(
final int api, final FieldVisitor fieldVisitor, final Remapper remapper) {
super(api, fieldVisitor);
this.remapper = remapper;
@Test
public void testGetSize() {
assertEquals(2, new SourceValue(2).getSize());
}
@Override
public AnnotationVisitor visitAnnotation(final String descriptor, final boolean visible) {
AnnotationVisitor annotationVisitor = fv.visitAnnotation(remapper.mapDesc(descriptor), visible);
return annotationVisitor == null
? null
: new RemappingAnnotationAdapter(annotationVisitor, remapper);
@Test
public void testEquals() {
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);
}
@Override
public AnnotationVisitor visitTypeAnnotation(
final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) {
AnnotationVisitor annotationVisitor =
super.visitTypeAnnotation(typeRef, typePath, remapper.mapDesc(descriptor), visible);
return annotationVisitor == null
? null
: new RemappingAnnotationAdapter(annotationVisitor, remapper);
@Test
public void testHashcode() {
assertEquals(0, new SourceValue(1).hashCode());
assertNotEquals(0, new SourceValue(1, new InsnNode(Opcodes.NOP)).hashCode());
}
}
This diff is collapsed.
......@@ -121,8 +121,8 @@ public abstract class AdviceAdapter extends GeneratorAdapter implements Opcodes
public void visitCode() {
super.visitCode();
if (isConstructor) {
stackFrame = new ArrayList<Object>();
forwardJumpStackFrames = new HashMap<Label, List<Object>>();
stackFrame = new ArrayList<>();
forwardJumpStackFrames = new HashMap<>();
} else {
onMethodEnter();
}
......@@ -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
public void visitMethodInsn(
final int opcode,
final int opcodeAndSource,
final String owner,
final String name,
final String descriptor,
final boolean isInterface) {
if (api < Opcodes.ASM5) {
super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
if (api < Opcodes.ASM5 && (opcodeAndSource & Opcodes.SOURCE_DEPRECATED) == 0) {
// Redirect the call to the deprecated version of this method.
super.visitMethodInsn(opcodeAndSource, owner, name, descriptor, isInterface);
return;
}
mv.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
super.visitMethodInsn(opcodeAndSource, owner, name, descriptor, isInterface);
int opcode = opcodeAndSource & ~Opcodes.SOURCE_MASK;
doVisitMethodInsn(opcode, descriptor);
}
......@@ -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
// 'onMethodEnter').
if (isConstructor && !forwardJumpStackFrames.containsKey(handler)) {
List<Object> handlerStackFrame = new ArrayList<Object>();
List<Object> handlerStackFrame = new ArrayList<>();
handlerStackFrame.add(OTHER);
forwardJumpStackFrames.put(handler, handlerStackFrame);
}
......@@ -597,7 +583,7 @@ public abstract class AdviceAdapter extends GeneratorAdapter implements Opcodes
if (forwardJumpStackFrames.containsKey(label)) {
return;
}
forwardJumpStackFrames.put(label, new ArrayList<Object>(stackFrame));
forwardJumpStackFrames.put(label, new ArrayList<>(stackFrame));
}
private Object popValue() {
......
......@@ -143,9 +143,9 @@ public class AnalyzerAdapter extends MethodVisitor {
final MethodVisitor methodVisitor) {
super(api, methodVisitor);
this.owner = owner;
locals = new ArrayList<Object>();
stack = new ArrayList<Object>();
uninitializedTypes = new HashMap<Object, Object>();
locals = new ArrayList<>();
stack = new ArrayList<>();
uninitializedTypes = new HashMap<>();
if ((access & Opcodes.ACC_STATIC) == 0) {
if ("<init>".equals(name)) {
......@@ -205,8 +205,8 @@ public class AnalyzerAdapter extends MethodVisitor {
this.locals.clear();
this.stack.clear();
} else {
this.locals = new ArrayList<Object>();
this.stack = new ArrayList<Object>();
this.locals = new ArrayList<>();
this.stack = new ArrayList<>();
}
visitFrameTypes(numLocal, local, this.locals);
visitFrameTypes(numStack, stack, this.stack);
......@@ -258,7 +258,7 @@ public class AnalyzerAdapter extends MethodVisitor {
if (opcode == Opcodes.NEW) {
if (labels == null) {
Label label = new Label();
labels = new ArrayList<Label>(3);
labels = new ArrayList<>(3);
labels.add(label);
if (mv != null) {
mv.visitLabel(label);
......@@ -279,45 +279,21 @@ public class AnalyzerAdapter extends MethodVisitor {
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
public void visitMethodInsn(
final int opcode,
final int opcodeAndSource,
final String owner,
final String name,
final String descriptor,
final boolean isInterface) {
if (api < Opcodes.ASM5) {
super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
if (api < Opcodes.ASM5 && (opcodeAndSource & Opcodes.SOURCE_DEPRECATED) == 0) {
// Redirect the call to the deprecated version of this method.
super.visitMethodInsn(opcodeAndSource, owner, name, descriptor, isInterface);
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) {
labels = null;
return;
......@@ -378,7 +354,7 @@ public class AnalyzerAdapter extends MethodVisitor {
public void visitLabel(final Label label) {
super.visitLabel(label);
if (labels == null) {
labels = new ArrayList<Label>(3);
labels = new ArrayList<>(3);
}
labels.add(label);
}
......@@ -495,9 +471,12 @@ public class AnalyzerAdapter extends MethodVisitor {
maxStack = Math.max(maxStack, stack.size());
}
private void pushDescriptor(final String descriptor) {
int index = descriptor.charAt(0) == '(' ? descriptor.indexOf(')') + 1 : 0;
switch (descriptor.charAt(index)) {
private void pushDescriptor(final String fieldOrMethodDescriptor) {
String descriptor =
fieldOrMethodDescriptor.charAt(0) == '('
? Type.getReturnType(fieldOrMethodDescriptor).getDescriptor()
: fieldOrMethodDescriptor;
switch (descriptor.charAt(0)) {
case 'V':
return;
case 'Z':
......@@ -519,18 +498,10 @@ public class AnalyzerAdapter extends MethodVisitor {
push(Opcodes.TOP);
return;
case '[':
if (index == 0) {
push(descriptor);
} else {
push(descriptor.substring(index, descriptor.length()));
}
push(descriptor);
break;
case 'L':
if (index == 0) {
push(descriptor.substring(1, descriptor.length() - 1));
} else {
push(descriptor.substring(index + 1, descriptor.length() - 1));
}
push(descriptor.substring(1, descriptor.length() - 1));
break;
default:
throw new AssertionError();
......@@ -566,6 +537,9 @@ public class AnalyzerAdapter extends MethodVisitor {
}
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) {
labels = null;
return;
......@@ -866,9 +840,6 @@ public class AnalyzerAdapter extends MethodVisitor {
pop(4);
push(Opcodes.INTEGER);
break;
case Opcodes.JSR:
case Opcodes.RET:
throw new IllegalArgumentException("JSR/RET are not supported");
case Opcodes.GETSTATIC:
pushDescriptor(stringArg);
break;
......
......@@ -111,42 +111,20 @@ public class CodeSizeEvaluator extends MethodVisitor implements Opcodes {
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
public void visitMethodInsn(
final int opcode,
final int opcodeAndSource,
final String owner,
final String name,
final String descriptor,
final boolean isInterface) {
if (api < Opcodes.ASM5) {
super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
if (api < Opcodes.ASM5 && (opcodeAndSource & Opcodes.SOURCE_DEPRECATED) == 0) {
// Redirect the call to the deprecated version of this method.
super.visitMethodInsn(opcodeAndSource, owner, name, descriptor, isInterface);
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) {
minSize += 5;
maxSize += 5;
......@@ -154,9 +132,7 @@ public class CodeSizeEvaluator extends MethodVisitor implements Opcodes {
minSize += 3;
maxSize += 3;
}
if (mv != null) {
mv.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
}
super.visitMethodInsn(opcodeAndSource, owner, name, descriptor, isInterface);
}
@Override
......