Author: britter Date: Tue Sep 6 06:38:18 2016 New Revision: 1759364 URL: http://svn.apache.org/viewvc?rev=1759364&view=rev Log: BCEL-276: LocalVariableTypeTable is not updated. Thanks to Sam Yoon. This also fixes #10 from GitHub.
Added: commons/proper/bcel/trunk/src/test/java/org/apache/bcel/LocalVariableTypeTableTestCase.java commons/proper/bcel/trunk/src/test/java/org/apache/bcel/data/SimpleClassHasMethodIncludeGenericArgument.java Modified: commons/proper/bcel/trunk/src/changes/changes.xml commons/proper/bcel/trunk/src/main/java/org/apache/bcel/generic/MethodGen.java Modified: commons/proper/bcel/trunk/src/changes/changes.xml URL: http://svn.apache.org/viewvc/commons/proper/bcel/trunk/src/changes/changes.xml?rev=1759364&r1=1759363&r2=1759364&view=diff ============================================================================== --- commons/proper/bcel/trunk/src/changes/changes.xml [utf-8] (original) +++ commons/proper/bcel/trunk/src/changes/changes.xml [utf-8] Tue Sep 6 06:38:18 2016 @@ -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 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,u <author email="d...@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,u <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,u 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> Modified: commons/proper/bcel/trunk/src/main/java/org/apache/bcel/generic/MethodGen.java URL: http://svn.apache.org/viewvc/commons/proper/bcel/trunk/src/main/java/org/apache/bcel/generic/MethodGen.java?rev=1759364&r1=1759363&r2=1759364&view=diff ============================================================================== --- commons/proper/bcel/trunk/src/main/java/org/apache/bcel/generic/MethodGen.java (original) +++ commons/proper/bcel/trunk/src/main/java/org/apache/bcel/generic/MethodGen.java Tue Sep 6 06:38:18 2016 @@ -35,6 +35,7 @@ import org.apache.bcel.classfile.LineNum 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.Runtime 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 FieldGenO 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 FieldGenO } } } 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 FieldGenO } } - /** * Adds a local variable to this method. * @@ -292,7 +283,6 @@ public class MethodGen extends FieldGenO + " 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 FieldGenO /* 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 FieldGenO 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 FieldGenO l.add(annotation); param_annotations[parameterIndex] = l; } - } + } @@ -1192,7 +1224,7 @@ public class MethodGen extends FieldGenO * 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 FieldGenO /** * 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 Added: commons/proper/bcel/trunk/src/test/java/org/apache/bcel/LocalVariableTypeTableTestCase.java URL: http://svn.apache.org/viewvc/commons/proper/bcel/trunk/src/test/java/org/apache/bcel/LocalVariableTypeTableTestCase.java?rev=1759364&view=auto ============================================================================== --- commons/proper/bcel/trunk/src/test/java/org/apache/bcel/LocalVariableTypeTableTestCase.java (added) +++ commons/proper/bcel/trunk/src/test/java/org/apache/bcel/LocalVariableTypeTableTestCase.java Tue Sep 6 06:38:18 2016 @@ -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; + } +} Added: commons/proper/bcel/trunk/src/test/java/org/apache/bcel/data/SimpleClassHasMethodIncludeGenericArgument.java URL: http://svn.apache.org/viewvc/commons/proper/bcel/trunk/src/test/java/org/apache/bcel/data/SimpleClassHasMethodIncludeGenericArgument.java?rev=1759364&view=auto ============================================================================== --- commons/proper/bcel/trunk/src/test/java/org/apache/bcel/data/SimpleClassHasMethodIncludeGenericArgument.java (added) +++ commons/proper/bcel/trunk/src/test/java/org/apache/bcel/data/SimpleClassHasMethodIncludeGenericArgument.java Tue Sep 6 06:38:18 2016 @@ -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) { + } +}