Description
In the current implementation, the indices of the variables in the local variable table are used without considering the StackMapTable
code attribute. For larger methods this leads to wrong names and types of the variables.
For example in BasicTikaFSConsumerBuilder.java the following code
if( tikaConfigPath == null) {
Node tikaConfigNode = node.getAttributes().getNamedItem("tikaConfig");
if (tikaConfigNode != null) {
tikaConfigPath = PropsUtil.getString(tikaConfigNode.getNodeValue(), null);
}
}
if (tikaConfigPath != null) {
try (InputStream is = Files.newInputStream(Paths.get(tikaConfigPath))) {
config = new TikaConfig(is);
} catch (Exception e) {
throw new RuntimeException(e);
}
} else {
config = TikaConfig.getDefaultConfig();
}
List<FileResourceConsumer> consumers = new LinkedList<FileResourceConsumer>();
int numConsumers = BatchProcessBuilder.getNumConsumers(runtimeAttributes);
results in the following bytecode
167: ifnonnull 203
170: aload_1
171: invokeinterface #8, 1 // InterfaceMethod org/w3c/dom/Node.getAttributes:()Lorg/w3c/dom/NamedNodeMap;
176: ldc #14 // String tikaConfig
178: invokeinterface #9, 2 // InterfaceMethod org/w3c/dom/NamedNodeMap.getNamedItem:(Ljava/lang/String;)Lorg/w3c/dom/Node;
183: astore 10
185: aload 10
187: ifnull 203
190: aload 10
192: invokeinterface #10, 1 // InterfaceMethod org/w3c/dom/Node.getNodeValue:()Ljava/lang/String;
197: aconst_null
198: invokestatic #15 // Method org/apache/tika/util/PropsUtil.getString:(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
201: astore 9
203: aload 9
205: ifnull 342
208: aload 9
210: iconst_0
211: anewarray #4 // class java/lang/String
214: invokestatic #16 // Method java/nio/file/Paths.get:(Ljava/lang/String;[Ljava/lang/String;)Ljava/nio/file/Path;
217: iconst_0
218: anewarray #17 // class java/nio/file/OpenOption
221: invokestatic #18 // Method java/nio/file/Files.newInputStream:(Ljava/nio/file/Path;[Ljava/nio/file/OpenOption;)Ljava/io/InputStream;
224: astore 10
...
342: invokestatic #27 // Method org/apache/tika/config/TikaConfig.getDefaultConfig:()Lorg/apache/tika/config/TikaConfig;
345: astore 8
347: new #28 // class java/util/LinkedList
350: dup
351: invokespecial #29 // Method java/util/LinkedList."<init>":()V
354: astore 10
with the following local variable table (irrelevant parts omitted):
LocalVariableTable:
Start Length Slot Name Signature
...
185 18 10 tikaConfigNode Lorg/w3c/dom/Node;
260 7 12 x2 Ljava/lang/Throwable;
309 7 14 x2 Ljava/lang/Throwable;
226 101 10 is Ljava/io/InputStream;
...
356 293 10 consumers Ljava/util/List;
...
in the goto-program, it is the variable consumers
which gets assigned the values intended for tikaConfigNode
// 410 file BasicTikaFSConsumersBuilder.java line 87
return_tmp18 = NONDET(struct org.w3c.dom.Node *);
// 411 file BasicTikaFSConsumersBuilder.java line 87
consumers = (struct java.util.List *)return_tmp18;
// 412 file BasicTikaFSConsumersBuilder.java line 88
IF (void *)(void *)consumers == null THEN GOTO 5
// 413 file BasicTikaFSConsumersBuilder.java line 89
ASSERT false // block 31
// 414 file BasicTikaFSConsumersBuilder.java line 89
(void *)consumers . org.w3c.dom.Node.getNodeValue:()Ljava/lang/String;();
and is
// 437 file BasicTikaFSConsumersBuilder.java line 93
return_tmp24 = NONDET(struct java.io.InputStream *);
// 438 file BasicTikaFSConsumersBuilder.java line 93
consumers = (struct java.util.List *)return_tmp24;
// 439 file BasicTikaFSConsumersBuilder.java line 93
numConsumers = (int)null;
// 440 file BasicTikaFSConsumersBuilder.java line 94
new_tmp25 = MALLOC(struct org.apache.tika.config.TikaConfig, 4);
// 441 file BasicTikaFSConsumersBuilder.java line 94
*new_tmp25 = { .@class_identifier="java::org.apache.tika.config.TikaConfig" };
// 442 file BasicTikaFSConsumersBuilder.java line 94
new_tmp25 . org.apache.tika.config.TikaConfig.<init>:(Ljava/io/InputStream;)V((void *)consumers);
// 443 file BasicTikaFSConsumersBuilder.java line 94
ASSERT false // block 38
// 444 file BasicTikaFSConsumersBuilder.java line 94
config = (struct org.apache.tika.config.TikaConfig *)new_tmp25;
// 445 file BasicTikaFSConsumersBuilder.java line 95
IF (void *)(void *)consumers == null THEN GOTO 7
// 446 file BasicTikaFSConsumersBuilder.java line 95
ASSERT false // block 39
// 447 file BasicTikaFSConsumersBuilder.java line 95
IF (void *)(void *)numConsumers == null THEN GOTO 6
// 448 file BasicTikaFSConsumersBuilder.java line 95
ASSERT false // block 40
// 449 file BasicTikaFSConsumersBuilder.java line 95
(void *)consumers . java.io.InputStream.close:()V();
although consumers
is of type List<FileResourceConsumer>
, which is also the cast in the goto-program in the above example.
The reason for this is that the index of all three variables is 10
and consumers
is the last using this index.
To correct this, one has to take into account the relevant StackMapTable
of the code (if such a table does exists). The relevant frame for the correct usage of consumers
is this:
frame_type = 255 /* full_frame */
offset_delta = 34
locals = [ class org/apache/tika/batch/fs/builders/BasicTikaFSConsumersBuilder, class org/w3c/dom/Node, class java/util/Map, class java/util/concurrent/ArrayBlockingQueue, int, class java/lang/String, class java/lang/Long, class java/lang/String, class org/apache/tika/config/TikaConfig, class java/lang/String, class java/util/List, int, class org/w3c/dom/NodeList, class org/w3c/dom/Node, class org/w3c/dom/Node, class org/w3c/dom/Node, int ]
stack = []
while the correct ones for the preceeding variables with index 10 have to be constructed with the stack frame entries before.