I am writing an gradle plugin for compiling android application with scala
I ran into a problem that scalac
inlined numeric values of android resources (R.java
) to .class-files
At the time of assembly of the android modules, the R
values are unknown. Therefore, you need to access them using getstatic
.
How can I disable inline public static int
which defined in R.class
in R.jar
?
I don’t really understand. They are static final
but they are unknown? When or how are they filled in?
And doesn’t javac also inline static final
values? Why on earth do they encode them as compile time constants if they only intend to fill in those values through some runtime reflection magic?
for compile module R.java
is
public final class R {
private R() { }
public static final class string {
private string() { }
public static int app_name;
}
}
This class is located in module/R.jar
when compile android project R.java
public final class R {
private R() { }
public static final class string {
private string() { }
public static int app_name = 0x7f0b001b;
}
}
This class is located in app/R.jar
Thus, when a module is compiled, it refers to the module/R.jar
, and when building a project, it must reference app/R.jar
I don’t see a static final int
field though. I’m not sure but if scalac treats app_name
as a static final constant that might actually be a bug.
I assume you don’t have the optimizer enabled?
Yes, optimizer is disable.
compile options:
0 -> -unchecked
1 -> -encoding
2 -> UTF-8
compiler version:
0 -> 2.11.12
compile order:
0 -> Mixed
name hashing:
0 -> true
If app_name
is indeed not final
I find it unlikely that it is intended that scalac inlines it. However it’s also rather unlikely that (if it’s actually a bug) this will get fixed in 2.11.
Perhaps you could try setting compile order to JavaThenScala
. If that doesn’t work I’m not sure there’s anything you can do. I don’t think you can disable inlining (what the compiler thinks are) compile time constants.
Actually I can’t reproduce this issue.
I see a getstatic
in the bytecode:
0: getstatic #16 // Field R$string.app_name:I
3: ireturn
Your thoughts gave me an idea. Since a really similar class in the project is compiled into getstatic
The idea is that if scalac public static int
compiled to getstatic
and public static final int
to iconst_0
Then you should check whether the libraries dependencies have a variant of the type:
public final class R {
private R() { }
public static final class string {
private string() { }
public static final int app_name = 0;
}
}
To my regret, the assumption turned out to be false.
I created a small project contains two dependencies R.jar
and RM.jar
R.jar
is generated from android project
RM.jar
contain copy of R.java
and compiled as artifact
object Hello extends App {
println("My compile "+com.kos.sacamodule.R.anim.abc_grow_fade_in_from_bottom+" ok.")
println("From android "+com.kos.javamodule.R.anim.abc_grow_fade_in_from_bottom+" hm.")
}
it compiled to:
0: getstatic #61 // Field scala/Predef$.MODULE$:Lscala/Predef$;
3: new #63 // class scala/collection/mutable/StringBuilder
6: dup
7: invokespecial #64 // Method scala/collection/mutable/StringBuilder."<init>":()V
10: ldc #66 // String My compile
12: invokevirtual #70 // Method scala/collection/mutable/StringBuilder.append:(Ljava/lang/Object;)Lscala/collection/mutable/StringBuilder;
15: getstatic #76 // Field com/kos/sacamodule/R$anim.abc_grow_fade_in_from_bottom:I
18: invokestatic #82 // Method scala/runtime/BoxesRunTime.boxToInteger:(I)Ljava/lang/Integer;
21: invokevirtual #70 // Method scala/collection/mutable/StringBuilder.append:(Ljava/lang/Object;)Lscala/collection/mutable/StringBuilder;
24: ldc #84 // String ok.
26: invokevirtual #70 // Method scala/collection/mutable/StringBuilder.append:(Ljava/lang/Object;)Lscala/collection/mutable/StringBuilder;
29: invokevirtual #88 // Method scala/collection/mutable/StringBuilder.toString:()Ljava/lang/String;
32: invokevirtual #92 // Method scala/Predef$.println:(Ljava/lang/Object;)V
35: getstatic #61 // Field scala/Predef$.MODULE$:Lscala/Predef$;
38: new #63 // class scala/collection/mutable/StringBuilder
41: dup
42: invokespecial #64 // Method scala/collection/mutable/StringBuilder."<init>":()V
45: ldc #94 // String From android
47: invokevirtual #70 // Method scala/collection/mutable/StringBuilder.append:(Ljava/lang/Object;)Lscala/collection/mutable/StringBuilder;
50: iconst_0
51: invokestatic #82 // Method scala/runtime/BoxesRunTime.boxToInteger:(I)Ljava/lang/Integer;
54: invokevirtual #70 // Method scala/collection/mutable/StringBuilder.append:(Ljava/lang/Object;)Lscala/collection/mutable/StringBuilder;
57: ldc #96 // String hm.
59: invokevirtual #70 // Method scala/collection/mutable/StringBuilder.append:(Ljava/lang/Object;)Lscala/collection/mutable/StringBuilder;
62: invokevirtual #88 // Method scala/collection/mutable/StringBuilder.toString:()Ljava/lang/String;
65: invokevirtual #92 // Method scala/Predef$.println:(Ljava/lang/Object;)V
68: return
This see
15: getstatic
- it from my compiled RM.jar
50: iconst_0
- if from android R.jar