Skip to content

StackMapTable attribute is ignored in class files, leading to wrong names and types in goto program #177

Closed
@mgudemann

Description

@mgudemann

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.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions