1

I've got compiled .jar plugin with X.class file. X.class file contains a method Y with parameters Y(string s1, string s2....). I need to pass to one more string - so i launched reJ and dirtyJoe, edited a descriptor of my Y method, changed maximum local variables count from 8 to 9, added new local variable, set it same as previous variables, just gave it another index, edited code and saved the method. I packed it back into .jar file and tried to compile in Unity with new version of my plugin. Unfortunately - it gave me error saying my new variable is invalid -

EXCEPTION FROM SIMULATION:
local 0008: invalid

...at bytecode offset 00000036
locals[0000]: Ljava/lang/String;
locals[0001]: Ljava/lang/String;
locals[0002]: Ljava/lang/String;
locals[0003]: Ljava/lang/String;
locals[0004]: Ljava/lang/String;
locals[0005]: [B
locals[0006]: Landroid/net/Uri;
locals[0007]: Landroid/content/Intent;
locals[0008]: <invalid>
stack[0001]: Landroid/content/Intent;
stack[top0]: string{"android.intent.extra.TEXT"}
...while working on block 0036
...while working on method StartShareIntentMedia:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
...while processing StartShareIntentMedia (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
...while processing com/androidnative/features/social/common/SocialGate.class

That's my very first time with Java bytecode, hope i'll get some help. Thanks!

  • I'm somewhat confused why you are attempting to a parameter at the bytecode level - could you explain a little why using the function as-provided doesn't meet your needs? – romeara Aug 17 '15 at 01:36
  • because I need to put one more string to android Intent called in this function – Shapes Workshop Aug 17 '15 at 09:29

1 Answers1

3

Editing bytecode is not for the faint of heart. I'd recommend reading the JVM specification before you start.

That being said, there are a couple things you could be doing wrong, based on your description.

At the beginning of the method, the method parameters are passed in in local variable slots 0..n-1. You can't just choose any index for your new local variable, you have to use the index after the last current parameter. If the bytecode is already using that slot for something else, you'll have to adjust all usages of it to something else. Alternatively, you can add a move sequence (i.e. aload n astore y) at the beginning of the method. This won't affect any usages of that slot as a local variable later in the method.

If you have debugging information, like a LocalVariableTable, you'll have to adjust all the references in that. If the bytecode has a StackMapTable you'll have to adjust that. Assuming the code isn't using invokedynamic, it's probably easier to just change the bytecode version back to 50.0 and remove the stack map table entirely.

Next up, you'll have to change the method descriptor. The descriptor essentially gives the signature of the method, and in bytecode, methods are always identified by the (class, name, descriptor) triple. You'll need to add a string in as a parameter at the end of the descriptor before the final closing parenthesis and return type. You'll have to add the new descriptor to the constant pool if it's not there already.

Then you'll have to adjust the definition of the method to reference the new descriptor, and also adjust every single place where that method is called (better hope that inheritance and reflection aren't involved!).

And of course, you'll also have to adjust every callsite to actually pass in the new parameter.

Antimony
  • 37,781
  • 10
  • 100
  • 107
  • Thanks for a great answer! I no longer need the feature i was working on, but i'm still gonna try to achieve this in free time ;) – Shapes Workshop Aug 17 '15 at 09:27
  • `StackMapTable` was added in v50, so you will have to go to a version *before 50* when removing `StackMapTable`s. Besides `invokedynamic`, you’ll then loose all J8 features, `static` and `default` methods in `interface`s, type annotations and reflective method parameters as well… – Holger Aug 18 '15 at 14:43
  • @Holger Legacy verification fallback is optional in version 50. So if you want to be pedantic and guarantee support, you'll need to go back to 49, but any JVM you care about will fall back to legacy verification in version 50 when a stackmap isn't present. – Antimony Aug 19 '15 at 02:35
  • But why should anyone removing stackmaps go to v50 when the only difference between v49 and v50 *is* the introduction of stackmaps? – Holger Aug 19 '15 at 09:57