Bug 470658 - Corrupted Local Variable Table
Summary: Corrupted Local Variable Table
Status: RESOLVED FIXED
Alias: None
Product: AspectJ
Classification: Tools
Component: Compiler (show other bugs)
Version: 1.8.6   Edit
Hardware: PC Windows 8
: P3 normal (vote)
Target Milestone: 1.8.8   Edit
Assignee: aspectj inbox CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2015-06-21 12:14 EDT by Ariel Cattan CLA
Modified: 2016-01-07 15:54 EST (History)
2 users (show)

See Also:


Attachments
Zip containing all files to reproduce the issue (14.98 KB, application/x-zip-compressed)
2015-06-21 12:14 EDT, Ariel Cattan CLA
no flags Details
The class with the issue after weaving (48.78 KB, application/octet-stream)
2015-06-21 12:23 EDT, Ariel Cattan CLA
no flags Details
Zip containing all files and also Android Dexer to reproduce the issue (823.96 KB, application/x-zip-compressed)
2015-06-23 13:04 EDT, Ariel Cattan CLA
no flags Details
Zip to reproduce problem with modification of existing local variable table (25.45 MB, application/x-zip-compressed)
2015-08-27 07:49 EDT, Ariel Cattan CLA
no flags Details

Note You need to log in before you can comment on or make changes to this bug.
Description Ariel Cattan CLA 2015-06-21 12:14:25 EDT
Created attachment 254594 [details]
Zip containing all files to reproduce the issue

Hi :-)
I have found an issue during weaving, which can be reproduced by the attached files. The weaved class after running ajc has some corrupted Local Variable Tables, where some slots are missing.
When opening the weaved class - com/crashlytics/android/v.class - and looking at it using Sublime for example, I could see the following:

 LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0     154     0  arg0   Lcom/crashlytics/android/internal/aq;
            0     154     1  arg1   I
            0     154     2  arg2   J
            0     154     4  arg3   Ljava/lang/String;

As seen above, Slot 3 is missing...
This issue causes some problems with Android's build framework.

To reproduce the problem please do the following:
- Unzip the attached
- Define a ASPECTJ_HOME env variable, or simply edit the run.cmd file
- Execute run.cmd

The result would be ajc weaving the class v.class, using FilesApsect.aj found under the src dir. And the weaved jar will contain a v.class with the problem.

Thanks!
Ariel
Comment 1 Ariel Cattan CLA 2015-06-21 12:23:13 EDT
Created attachment 254595 [details]
The class with the issue after weaving

Adding also the outcome of the weaving where the issue can be seen.
Comment 2 Andrew Clement CLA 2015-06-22 12:33:03 EDT
What precisely is telling you that the local variable table is corrupted? What is the exact error?

The type of the third argument in your method is 'J' which is a 'long' which means it is a double size value, hence it takes up both slots 2 and 3. 

If I take this:

public class Code {
  public static void main(String []argv) {

  }
  public void m(int i, long j, boolean z) {
    System.out.println(i);
    System.out.println(j);
    System.out.println(z);
  }
}

and 'javac -g Code.java' then 'javap -verbose Code' then I see:

     LocalVariableTable:
        Start  Length  Slot  Name   Signature
               0      23     0  this   LCode;
               0      23     1     i   I
               0      23     2     j   J
               0      23     4     z   Z

The 'J' is taking up two slots ('D' for double would act in the same way)

What *is* different between your inpath and outjar is that your inpath code does not have local variable tables for these methods, where AspectJ is trying to 'reconstruct them' during weaving.

Maybe from the error message I can understand what it is complaining about.
Comment 3 Ariel Cattan CLA 2015-06-23 13:01:38 EDT
Thank you for looking at this issue.
I probably misinterpreted the issue. The problem arises while trying to convert the out.jar into a dex file by the Android Dexer. The error is as follows:

EXCEPTION FROM SIMULATION:
local variable type mismatch: attempt to set or access a value of type java.lang.Object using a local variable of type int. This is symptomatic of .class transformation tools that ignore local variable information.

...at bytecode offset 0000006a
locals[0000]: Lcom/crashlytics/android/internal/aq;
locals[0001]: I
locals[0002]: J
locals[0003]: <invalid>
locals[0004]: Ljava/lang/String;
stack[top0]: [B
...while working on block 006a
...while working on method a:(Lcom/crashlytics/android/internal/aq;IJLjava/lang/String;)V
...while processing a (Lcom/crashlytics/android/internal/aq;IJLjava/lang/String;)V
...while processing com/crashlytics/android/v.class

It is actually complaining about different variable types, and I misconcluded that it was related to the slot numbers.
For your convenience I will attach an updated zip file, together with the Android Dexer and a script that will run ajc and immediately after run dx for easy simulation of the issue.

Thanks!
Ariel
Comment 4 Ariel Cattan CLA 2015-06-23 13:04:06 EDT
Created attachment 254650 [details]
Zip containing all files and also Android Dexer to reproduce the issue
Comment 5 Andrew Clement CLA 2015-08-08 11:37:46 EDT
Sorry I'm slow to look at this.

So it seems the input code that is being passed in is a bit 'naughty'.

it is a static method declared like this:

 static void a(com.crashlytics.android.internal.aq, int, long, java.lang.String);

which should mean there are 4 local variables and they'd take up slots 0,1,2,4 (this is a static method so we don't store 'this' in 0, there is no slot 3 because the third variable is a double size one).  That means anytime the code in the method does an 'aload 1' it would be grabbing an int (for code generated by a regular compiler)

However, the byte code at offset 106 in the method does this:

       103: invokevirtual #428                // Method java/lang/String.getBytes:(Ljava/lang/String;)[B
       106: astore_1

The invokevirtual leaves a byte[] on the stack and that is stored into 1 - where we'd probably expect to find an 'int' if you strictly obey method specification. Now you can get away with this kind of thing if generating byte code but you couldn't write the source to do it:

  static void a(com.crashlytics.android.internal.aq arg0, int arg1, long arg2, java.lang.String arg3) {
   arg1 = "abc".getBytes(); // NOT ALLOWED
  }

You can get away with it in generated code if you know no other code is going to try and find an int in there. And that seems to be what is happening here. No one is accessing 1 so it is used as a free variable to hold the byte array.

Because the method is not marked synthetic AspectJ is adding a local variable table (we have had other situations where byte code transformers complain if there *isnt* a local variable table for non synthetic methods). The very simplest thing I can do is a flag to say this kind of thing is happening and not to fix up a local variable table if the incoming methods don't have one.
Comment 6 Andrew Clement CLA 2015-08-10 14:44:01 EDT
New option added: -Xset:generateNewLocalVariableTables=false for these kinds of scenario. I can't make it the default because other scenarios do want local variable tables.  Analysing the byte code to see if it is doing strange things like this would be too costly to do in all situations, given that this is the less likely case.

I've tried the option and it works here, the dexer runs OK.
Comment 7 Ariel Cattan CLA 2015-08-11 02:41:41 EDT
Thank you!
Is the change expected to come out in AspectJ 1.8.7? Is there already a schedule for this release?
Thanks again,
Ariel
Comment 8 Andrew Clement CLA 2015-08-12 11:24:52 EDT
I am loosely planning 1.8.7 for the next few weeks (up to 4). If you want to try it out in the interim I just uploaded a dev build for you, see the AspectJ download page.
Comment 9 Ariel Cattan CLA 2015-08-13 06:18:05 EDT
Hi Andrew,
Thank you so much for handling this problem.
I downloaded the development version and tried it, and it indeed solves the problem! :-)

However:

1. There was an issue with aspectjrt - Android's Dex generates the following warning:

Unknown source file:
warning: Ignoring InnerClasses attribute for an anonymous inner class (org.aspectj.runtime.internal.cflowstack.ThreadStackFactoryImpl$1) that doesn't come with an associated EnclosingMethod attribute. This class was probably produced by a compiler that did not target the modern .class file format. The recommended solution is to recompile the class from source, using an up-to-date compiler and without specifying any "-target" type options. The consequence of ignoring this warning is that reflective operations on this class will incorrectly indicate that it is *not* an inner class.

I therefore grabbed aspectjrt from version 1.8.6, and everything worked fine.

2. I tried looking for the new option by running ajc with -X, but the new option wasn't listed there. You probably didn't yet add it to the command line help.

If you generate another development build - I'll be more than happy to re-run the tests with it.

Thank you!
Ariel
Comment 10 Ariel Cattan CLA 2015-08-27 07:42:41 EDT
Hi again :-)

I ran into an issue which resembles the above, but a bit different. This time it seems that ajc modifies the existing local variable table, and that causes an exception in Android's dexer.
From what I see, it seems ajc changes the length of the local variables, and I think that due to this change the dexer again falls onto an illegal astore_1 which tries to store an object into a long.
I'm attaching a zip with a run command to simulate the problem. Also, in the folder "Weaved versus Original" you can find the original class and the one after weaving and see the difference.
Am I correct in my analysis, and if yes - do you have an idea how this can be solved?

Thanks!
Ariel
Comment 11 Ariel Cattan CLA 2015-08-27 07:49:33 EDT
Created attachment 256177 [details]
Zip to reproduce problem with modification of existing local variable table
Comment 12 Andrew Clement CLA 2015-08-28 11:35:17 EDT
Yes, AspectJ can modify local variable tables. I can imagine that it would extend the range over one of these rogue instructions that is sticking something into an object that is considered to be a local variable slot. Supporting this is a bit trickier, thanks for attaching the scenario. I'll try to get to it and get something into 1.8.7 (due before SpringOne which is in September).
Comment 13 Ariel Cattan CLA 2015-10-12 09:02:48 EDT
Hi Andrew,

Any news about this issue? As far as I could see this didn't get into 1.8.7, or am I wrong?

Thanks,
Ariel
Comment 14 Andrew Clement CLA 2015-11-18 18:06:05 EST
Sorry for my slow reply. It didn't make it into 1.8.7 and still hasn't been addressed.
Comment 15 Andrew Clement CLA 2016-01-07 15:54:27 EST
I extended the meaning of the flag we are using here to also avoid expanding the range of local variables that represent method parameters. Without expanding the range the run of dx.jar runs fine for me on the woven output.