You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Fixes: #4415
Context: ce2bc68
Commit ce2bc68 optimized type mappings between managed types and Java
types in large part by removing strings from the managed -> JNI
mapping: instead of using an assembly-qualified *string* as a key,
the assembly MVID & type metadata token were used as keys.
This setup works reliably in Release apps, in which the assemblies
don't change. In a commercial Debug with Fast Deployment situation,
it falls down badly because every change to any source code that's
built into a mapped assembly may cause the assembly to change its
MVID, and renaming of any type -- removing or adding a type -- will
rearrange the type definition table in the resulting assembly, thus
changing the type token ids (which are basically offsets into the
type definition table in the PE executable). This is what may cause
an app to crash on the runtime with an exception similar to:
android.runtime.JavaProxyThrowable: System.NotSupportedException: Cannot create instance of type 'com.glmsoftware.OBDNowProto.SettingsFragmentCompat': no Java peer type found.
at Java.Interop.JniPeerMembers+JniInstanceMethods..ctor (System.Type declaringType) [0x0004b] in <e3e4dfa992a7411b85acfe193481be3e>:0
at Java.Interop.JniPeerMembers+JniInstanceMethods.GetConstructorsForType (System.Type declaringType) [0x00031] in <e3e4dfa992a7411b85acfe193481be3e>:0
at Java.Interop.JniPeerMembers+JniInstanceMethods.StartCreateInstance (System.String constructorSignature, System.Type declaringType, Java.Interop.JniArgumentValue* parameters) [0x00038] in <e3e4dfa992a7411b85acfe193481be3e>:0
at AndroidX.Preference.PreferenceFragmentCompat..ctor () [0x00034] in <005e3ae6340747e1aea6d08b095cf286>:0
at com.glmsoftware.OBDNowProto.SettingsFragmentCompat..ctor () [0x00026] in <a8dbee4be1674aa08cce57b50f21e347>:0
at com.glmsoftware.OBDNowProto.SettingsActivity.OnCreate (Android.OS.Bundle bundle) [0x00083] in <a8dbee4be1674aa08cce57b50f21e347>:0
at Android.App.Activity.n_OnCreate_Landroid_os_Bundle_ (System.IntPtr jnienv, System.IntPtr native__this, System.IntPtr native_savedInstanceState) [0x00011] in <c56099afccf04721853684f376a89527>:0
at (wrapper dynamic-method) Android.Runtime.DynamicMethodNameCounter.3(intptr,intptr,intptr)
at crc64596a13587a898911.SettingsActivity.n_onCreate(Native Method)
at crc64596a13587a898911.SettingsActivity.onCreate(SettingsActivity.java:40)
at android.app.Activity.performCreate(Activity.java:7825)
at android.app.Activity.performCreate(Activity.java:7814)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1306)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3245)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3409)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:83)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2016)
at android.os.Handler.dispatchMessage(Handler.java:107)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7356)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)
A "workaround" would be fully rebuild the application, which negates
the point to incremental builds and the inner-dev-loop cycle.
The fix is to partially revert ce2bc68 in the sense that it restores
the use of string-based type names for Java-to-Managed and
Managed-to-Java type lookups for ***Debug*** builds only. Unlike the
pre-ce2bc689 world, only *one* copy of the set of string names is
present within the data structures, at a tiny (sub millisecond) expense
at the run time to fix up pointers between the two tables.
~~ File Formats ~~
All data in all file formats remains little-endian.
In Debug configuration builds, each assembly will have a corresponding
`*.typemap` file which will be loaded at runtime.
The file format in pseudo-C++:
struct DebugTypemapFileHeader {
byte magic [4]; // "XATS"
uint32_t format_version; // 2
uint32_t entry_count;
uint32_t java_type_name_width;
uint32_t managed_type_name_width;
uint32_t assembly_name_size;
byte assembly_name [assembly_name_size];
DebugTypemapFileJavaToManagedEntry java_to_managed [entry_count];
DebugTypemapFileManagedToJavaEntry managed_to_java [entry_count];
}
struct DebugTypemapFileJavaToManagedEntry {
byte jni_name [DebugTypemapFileHeader::java_type_name_width];
uint32_t managed_index; // Index into DebugTypemapFileHeader::managed_to_java
};
struct DebugTypemapFileManagedToJavaEntry {
byte managed_name [DebugTypemapFileHeader::java_type_name_width];
uint32_t jni_index; // Index into DebugTypemapFileHeader::java_to_managed
};
`DebugTypemapFileHeader::java_type_name_width` and
`DebugTypemapFileHeader::managed_type_name_width` are the maximum
length + 1 (terminating NUL) for JNI names and assembly-qualified
managed names.
`DebugTypemapFileJavaToManagedEntry::jni_name` and
`DebugTypemapFileManagedToJavaEntry::managed_name` are NUL-padded.
0 commit comments