Skip to content

[TrimmableTypeMap] TypeMap assembly + JCW Java source generators #10799

Closed

Description

@simonrozsival

Part of #10789
Depends on: Scanner sub-issue (PR1)

Overview

Implement the IL assembly generator and JCW Java source generator that transform JavaPeerInfo scan results into:

  1. Per-assembly typemap assemblies + root _Microsoft.Android.TypeMaps.dll — using SRM Ecma335 APIs
  2. JCW Java source files — with registerNatives in static {} blocks

TypeMap Assembly Generator (TypeMapAssemblyGenerator)

Uses SRM types: MetadataBuilder, BlobBuilder, InstructionEncoder, ControlFlowBuilder, MethodBodyStreamEncoder, PEHeaderBuilder.

Generated assembly contents

Assembly-level attributes:

  • [assembly: IgnoresAccessChecksTo("Mono.Android")] etc. for all referenced assemblies
  • [assembly: TypeMap<Java.Lang.Object>("jni/name", typeof(Proxy), typeof(TrimTarget))] — one per type
  • [assembly: TypeMapAssociation<AliasesUniverse>(typeof(Source), typeof(AliasHolder))] — for aliases

Per-type proxy classes — JavaPeerProxyAttribute subclasses:

  • Self-application: [Proxy] class Proxy : JavaPeerProxyAttribute
  • CreateInstance(IntPtr, JniHandleOwnership):
    • MCW with own ctor: new T(handle, transfer)
    • User type without own ctor: GetUninitializedObject(T) + direct call to base ctor (via IgnoresAccessChecksTo)
    • Interface: new TInvoker(handle, transfer)
    • Generic: throw new NotSupportedException()
  • GetContainerFactory(): return JavaPeerContainerFactory.Create<T>()
  • For ACW types: IAndroidCallableWrapper.RegisterNatives(JniType) + [UnmanagedCallersOnly] UCO methods (delegation or inlined)

Per-assembly output model (for incremental builds):

  • One typemap assembly per input assembly with Java peer types
  • Root assembly uses [assembly: TypeMapAssemblyTarget<JLO>(assemblyName)] to reference per-assembly typemaps
  • Alias detection and cross-ref forcing handled in root assembly

JCW Java Source Generator (JcwJavaSourceGenerator)

Generates .java files for ACW types (DoNotGenerateAcw=false):

package example;
public class MainActivity extends android.app.Activity
    implements mono.android.IGCUserPeer
{
    static { mono.android.Runtime.registerNatives(MainActivity.class); }

    public MainActivity() { super(); if (getClass() == MainActivity.class) nctor_0(); }
    private native void nctor_0();

    @Override
    public void onCreate(android.os.Bundle p0) { n_onCreate(p0); }
    public native void n_onCreate(android.os.Bundle p0);
}

Key: generate Java class and IL proxy from the same ordered method list to ensure RegisterNatives connects the right native methods to the right UCO methods.

Tests

  • Produces valid PE assembly loadable by MetadataReader
  • Generated assembly has correct TypeMapAttribute entries (2-arg and 3-arg)
  • Proxy types have correct self-application pattern
  • CreateInstance IL is valid for each ctor style
  • RegisterNatives method has correct JNI registrations
  • UCO wrappers have [UnmanagedCallersOnly] attribute
  • Non-blittable params converted correctly (byte for bool)
  • Java source has registerNatives in static {} block
  • Correct nctor_N declarations and @Override annotations
  • Package names from [Register], not CRC-hashed

Activity

  1. added this to the .NET 11 milestone on Feb 11, 2026
  2. locked and limited conversation to collaborators on Apr 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Area: CoreCLRIssues that only occur when using CoreCLR.Area: NativeAOTIssues that only occur when using NativeAOT.needs-triageIssues that need to be assigned.trimmable-type-map

Type

No type

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions