aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBenedikt Ritter <britter@apache.org>2016-09-06 06:38:18 +0000
committerBenedikt Ritter <britter@apache.org>2016-09-06 06:38:18 +0000
commitc96a3e5a40040ef43e0a51935a368458763d3424 (patch)
treee698d0ceebc8d38e34f9402ffef7af735c1c2de6 /src
parent7ceb5a6b8236517d708d7965f4873344c5630969 (diff)
downloadapache-commons-bcel-c96a3e5a40040ef43e0a51935a368458763d3424.tar.gz
BCEL-276: LocalVariableTypeTable is not updated. Thanks to Sam Yoon. This also fixes #10 from GitHub.
git-svn-id: https://svn.apache.org/repos/asf/commons/proper/bcel/trunk@1759364 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'src')
-rw-r--r--src/changes/changes.xml15
-rw-r--r--src/main/java/org/apache/bcel/generic/MethodGen.java74
-rw-r--r--src/test/java/org/apache/bcel/LocalVariableTypeTableTestCase.java142
-rw-r--r--src/test/java/org/apache/bcel/data/SimpleClassHasMethodIncludeGenericArgument.java34
4 files changed, 237 insertions, 28 deletions
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index a24921d6..0d5bc08b 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -21,7 +21,7 @@
This file is also used by the maven-changes-plugin to generate the release notes.
Useful ways of finding items to add to this file are:
-1. Add items when you fix a bug or add a feature (this makes the
+1. Add items when you fix a bug or add a feature (this makes the
release process easy :-).
2. Do a JIRA search for tickets closed since the previous release.
@@ -33,7 +33,7 @@ To generate the release notes from this file:
mvn changes:announcement-generate -Prelease-notes [-Dchanges.version=nnn]
-then tweak the formatting if necessary
+then tweak the formatting if necessary
and commit
The <action> type attribute can be add,update,fix,remove.
@@ -45,16 +45,16 @@ The <action> type attribute can be add,update,fix,remove.
<author email="dev@commons.apache.org">Apache Commons developers</author>
</properties>
- <!-- NOTE:
+ <!-- NOTE:
The description below is specially formatted so as to improve the layout of the generated release notes:
The parsing process removes all line feeds, replacing them with a single space.
The Velocity template in resources/templates has been enhanced to replace pairs of adjacent spaces
with a new-line in the release notes. (These spaces are ignored when displaying HTML).
If the output is not quite correct, check for invisible trailing spaces!
-
+
N.B. The release notes template groups actions by type, and only extracts data for the current release.
The changes report outputs actions in the order they appear in this file.
-
+
To regenerate the release notes:
mvn changes:announcement-generate -Prelease-notes [-Dchanges.version=nnn]
@@ -63,6 +63,7 @@ The <action> type attribute can be add,update,fix,remove.
<body>
<release version="6.1" date="tba" description="tba">
+ <action issue="BCEL-276" type="fix" dev="britter" due-to="Sam Yoon">LocalVariableTypeTable is not updated.</action>
<action issue="BCEL-277" type="fix" dev="britter" due-to="Sam Yoon">Resolving the String representation of a constant throws NoSuchElementException in case of CONSTANT_NameAndType constant.</action>
</release>
@@ -72,7 +73,7 @@ The <action> type attribute can be add,update,fix,remove.
It requires Java 7 or higher to run.
-
+
COMPATIBILITY with 5.2
======================
@@ -111,7 +112,7 @@ Source compatible - Yes, sort of;
For full information about API changes please see the extended Clirr report:
http://commons.apache.org/bcel/clirr-report.html">
- <action issue="BCEL-262" type="update" dev="britter" due-to="Mark Roberts">InvokeInstruction.getClassName(ConstantPoolGen)
+ <action issue="BCEL-262" type="update" dev="britter" due-to="Mark Roberts">InvokeInstruction.getClassName(ConstantPoolGen)
should not throw an exception when it detects an array.</action>
<action issue="BCEL-237" type="fix" dev="sebb">non-empty final arrays should be private as they are mutable.</action>
<action issue="BCEL-230" type="update" dev="britter">Document the Java platform requirement clearly and early.</action>
diff --git a/src/main/java/org/apache/bcel/generic/MethodGen.java b/src/main/java/org/apache/bcel/generic/MethodGen.java
index 58b94ae7..178ddc44 100644
--- a/src/main/java/org/apache/bcel/generic/MethodGen.java
+++ b/src/main/java/org/apache/bcel/generic/MethodGen.java
@@ -35,6 +35,7 @@ import org.apache.bcel.classfile.LineNumber;
import org.apache.bcel.classfile.LineNumberTable;
import org.apache.bcel.classfile.LocalVariable;
import org.apache.bcel.classfile.LocalVariableTable;
+import org.apache.bcel.classfile.LocalVariableTypeTable;
import org.apache.bcel.classfile.Method;
import org.apache.bcel.classfile.ParameterAnnotationEntry;
import org.apache.bcel.classfile.ParameterAnnotations;
@@ -42,7 +43,7 @@ import org.apache.bcel.classfile.RuntimeVisibleParameterAnnotations;
import org.apache.bcel.classfile.Utility;
import org.apache.bcel.util.BCELComparator;
-/**
+/**
* Template class for building up a method. This is done by defining exception
* handlers, adding thrown exceptions, local variables and attributes, whereas
* the `LocalVariableTable' and `LineNumberTable' attributes will be set
@@ -65,6 +66,8 @@ public class MethodGen extends FieldGenOrMethodGen {
private int max_stack;
private InstructionList il;
private boolean strip_attributes;
+ private LocalVariableTable local_variable_table = null;
+ private LocalVariableTypeTable local_variable_type_table = null;
private final List<LocalVariableGen> variable_vec = new ArrayList<>();
private final List<LineNumberGen> line_number_vec = new ArrayList<>();
private final List<CodeExceptionGen> exception_vec = new ArrayList<>();
@@ -222,21 +225,10 @@ public class MethodGen extends FieldGenOrMethodGen {
}
}
} else if (a instanceof LocalVariableTable) {
- final LocalVariable[] lv = ((LocalVariableTable) a).getLocalVariableTable();
- removeLocalVariables();
- for (final LocalVariable l : lv) {
- InstructionHandle start = il.findHandle(l.getStartPC());
- InstructionHandle end = il.findHandle(l.getStartPC() + l.getLength());
- // Repair malformed handles
- if (null == start) {
- start = il.getStart();
- }
- if (null == end) {
- end = il.getEnd();
- }
- addLocalVariable(l.getName(), Type.getType(l.getSignature()), l
- .getIndex(), start, end);
- }
+ this.local_variable_table = (LocalVariableTable) a;
+ updateLocalVariableTable(this.local_variable_table);
+ } else if (a instanceof LocalVariableTypeTable) {
+ this.local_variable_type_table = (LocalVariableTypeTable) a;
} else {
addCodeAttribute(a);
}
@@ -258,7 +250,6 @@ public class MethodGen extends FieldGenOrMethodGen {
}
}
-
/**
* Adds a local variable to this method.
*
@@ -292,7 +283,6 @@ public class MethodGen extends FieldGenOrMethodGen {
+ " as type for local variable");
}
-
/**
* Adds a local variable to this method and assigns an index automatically.
*
@@ -629,8 +619,18 @@ public class MethodGen extends FieldGenOrMethodGen {
/* Create LocalVariableTable and LineNumberTable attributes (for debuggers, e.g.)
*/
if ((variable_vec.size() > 0) && !strip_attributes) {
+ if (local_variable_table != null) {
+ updateLocalVariableTable(local_variable_table);
+ }
addCodeAttribute(lvt = getLocalVariableTable(_cp));
}
+ if (local_variable_type_table != null) {
+ // LocalVariable length in LocalVariableTypeTable is not updated automatically. It's a difference with LocalVariableTable.
+ if (lvt != null) {
+ adjustLocalVariableLength(lvt);
+ }
+ addCodeAttribute(local_variable_type_table);
+ }
if ((line_number_vec.size() > 0) && !strip_attributes) {
addCodeAttribute(lnt = getLineNumberTable(_cp));
}
@@ -683,6 +683,38 @@ public class MethodGen extends FieldGenOrMethodGen {
return m;
}
+ private void updateLocalVariableTable(LocalVariableTable a) {
+ final LocalVariable[] lv = a.getLocalVariableTable();
+ removeLocalVariables();
+ for (final LocalVariable l : lv) {
+ InstructionHandle start = il.findHandle(l.getStartPC());
+ InstructionHandle end = il.findHandle(l.getStartPC() + l.getLength());
+ // Repair malformed handles
+ if (null == start) {
+ start = il.getStart();
+ }
+ if (null == end) {
+ end = il.getEnd();
+ }
+ addLocalVariable(l.getName(), Type.getType(l.getSignature()), l
+ .getIndex(), start, end);
+ }
+ }
+
+ private void adjustLocalVariableLength(LocalVariableTable lvt) {
+ LocalVariable[] lv = lvt.getLocalVariableTable();
+ LocalVariable[] lvg = local_variable_type_table.getLocalVariableTypeTable();
+
+ for (int i = 0, length = lvg.length; i < length; i++) {
+ for (LocalVariable l : lv) {
+ if (lvg[i].getName().equals(l.getName()) && lvg[i].getIndex() == l.getIndex()) {
+ lvg[i].setLength(l.getLength());
+ break;
+ }
+ }
+ }
+ }
+
/**
* Remove all NOPs from the instruction list (if possible) and update every
@@ -1167,7 +1199,7 @@ public class MethodGen extends FieldGenOrMethodGen {
l.add(annotation);
param_annotations[parameterIndex] = l;
}
- }
+ }
@@ -1192,7 +1224,7 @@ public class MethodGen extends FieldGenOrMethodGen {
* Return value as defined by given BCELComparator strategy.
* By default two MethodGen objects are said to be equal when
* their names and signatures are equal.
- *
+ *
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
@@ -1204,7 +1236,7 @@ public class MethodGen extends FieldGenOrMethodGen {
/**
* Return value as defined by given BCELComparator strategy.
* By default return the hashcode of the method's name XOR signature.
- *
+ *
* @see java.lang.Object#hashCode()
*/
@Override
diff --git a/src/test/java/org/apache/bcel/LocalVariableTypeTableTestCase.java b/src/test/java/org/apache/bcel/LocalVariableTypeTableTestCase.java
new file mode 100644
index 00000000..ca7e2da1
--- /dev/null
+++ b/src/test/java/org/apache/bcel/LocalVariableTypeTableTestCase.java
@@ -0,0 +1,142 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.bcel;
+
+import org.apache.bcel.classfile.JavaClass;
+import org.apache.bcel.classfile.Method;
+import org.apache.bcel.generic.ACONST_NULL;
+import org.apache.bcel.generic.ALOAD;
+import org.apache.bcel.generic.ConstantPoolGen;
+import org.apache.bcel.generic.GETSTATIC;
+import org.apache.bcel.generic.INVOKEVIRTUAL;
+import org.apache.bcel.generic.Instruction;
+import org.apache.bcel.generic.InstructionList;
+import org.apache.bcel.generic.LocalVariableGen;
+import org.apache.bcel.generic.MethodGen;
+import org.apache.bcel.generic.Type;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.util.LinkedList;
+import java.util.List;
+
+public class LocalVariableTypeTableTestCase extends AbstractTestCase {
+ public class TestClassLoader extends ClassLoader {
+ public TestClassLoader(ClassLoader parent) {
+ super(parent);
+ }
+
+ public Class<?> findClass(String name, byte[] bytes) {
+ return defineClass(name, bytes, 0, bytes.length);
+ }
+ }
+
+ @Test
+ public void testWithGenericArguement() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, IOException {
+ String targetClass = PACKAGE_BASE_NAME + ".data.SimpleClassHasMethodIncludeGenericArgument";
+ TestClassLoader loader = new TestClassLoader(getClass().getClassLoader());
+ Class cls = loader.findClass(targetClass, getBytesFromClass(targetClass));
+
+ java.lang.reflect.Method method = cls.getDeclaredMethod("a", String.class, List.class);
+ method.invoke(null, "a1", new LinkedList<String>());
+ method = cls.getDeclaredMethod("b", String.class, List.class);
+ method.invoke(null, "b1", new LinkedList<String>());
+ method = cls.getDeclaredMethod("c", String.class, String.class);
+ method.invoke(null, "c1", "c2");
+ method = cls.getDeclaredMethod("d", List.class, String.class);
+ method.invoke(null, new LinkedList<String>(), "d2");
+ }
+
+ private byte[] getBytesFromClass(String className) throws ClassNotFoundException, IOException {
+ JavaClass clazz = getTestClass(className);
+ ConstantPoolGen cp = new ConstantPoolGen(clazz.getConstantPool());
+
+ Method[] methods = clazz.getMethods();
+
+ for (int i = 0; i < methods.length; i++) {
+ Method method = methods[i];
+ if (!method.isNative() && !method.isAbstract())
+ methods[i] = injection(clazz, method, cp, findFirstStringLocalVariableOffset(method));
+ }
+
+ clazz.setConstantPool(cp.getFinalConstantPool());
+
+ return clazz.getBytes();
+ }
+
+ public Method injection(JavaClass clazz, Method method, ConstantPoolGen cp, int firstStringOffset) {
+ MethodGen methodGen = new MethodGen(method, clazz.getClassName(), cp);
+
+ InstructionList instructionList = methodGen.getInstructionList();
+ instructionList.insert(instructionList.getStart(), makeWillBeAddedInstructionList(methodGen, firstStringOffset));
+
+ methodGen.setMaxStack();
+ methodGen.setMaxLocals();
+
+ method = methodGen.getMethod();
+ instructionList.dispose();
+
+ return method;
+ }
+
+ public InstructionList makeWillBeAddedInstructionList(MethodGen methodGen, int firstStringOffset) {
+ if (firstStringOffset == -1)
+ return new InstructionList();
+
+ LocalVariableGen localVariableGen = methodGen.getLocalVariables()[firstStringOffset];
+ Instruction instruction;
+
+ if (localVariableGen != null)
+ instruction = new ALOAD(localVariableGen.getIndex());
+ else
+ instruction = new ACONST_NULL();
+
+ return createPrintln(methodGen.getConstantPool(), instruction);
+ }
+
+ public InstructionList createPrintln(ConstantPoolGen cp, Instruction instruction) {
+ final InstructionList il = new InstructionList();
+
+ final int out = cp.addFieldref("java.lang.System", "out", "Ljava/io/PrintStream;");
+ final int println = cp.addMethodref("java.io.PrintStream", "println", "(Ljava/lang/String;)V");
+ il.append(new GETSTATIC(out));
+ il.append(instruction);
+ il.append(new INVOKEVIRTUAL(println));
+
+ return il;
+ }
+
+ public int findFirstStringLocalVariableOffset(Method method) {
+ Type[] argumentTypes = method.getArgumentTypes();
+ int offset = -1;
+
+ for (int i = 0, count = argumentTypes.length; i < count; i++) {
+ if (Type.STRING.getSignature().equals(argumentTypes[i].getSignature())) {
+ if (method.isStatic())
+ offset = i;
+ else
+ offset = i + 1;
+
+ break;
+ }
+ }
+
+ return offset;
+ }
+}
diff --git a/src/test/java/org/apache/bcel/data/SimpleClassHasMethodIncludeGenericArgument.java b/src/test/java/org/apache/bcel/data/SimpleClassHasMethodIncludeGenericArgument.java
new file mode 100644
index 00000000..c9acb259
--- /dev/null
+++ b/src/test/java/org/apache/bcel/data/SimpleClassHasMethodIncludeGenericArgument.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.bcel.data;
+
+import java.util.List;
+
+public class SimpleClassHasMethodIncludeGenericArgument {
+ public static void a(String a1, List<String> a2) {
+ }
+
+ public static void b(String b1, List b2) {
+ }
+
+ public static void c(String c1, String c2) {
+ }
+
+ public static void d(List<String> d1, String d2) {
+ }
+}