Skip to content

Commit c0beb18

Browse files
Changes before error encountered
Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com>
1 parent 13816e0 commit c0beb18

File tree

10 files changed

+250
-133
lines changed

10 files changed

+250
-133
lines changed

src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/JniSignatureHelper.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,16 @@ namespace Microsoft.Android.Sdk.TrimmableTypeMap;
99
static class JniSignatureHelper
1010
{
1111
/// <summary>
12-
/// Parses the raw JNI type descriptor strings from a JNI method signature.
12+
/// Parses the JNI parameter types from a JNI method signature into a list of <see cref="JniParameterInfo"/>.
1313
/// </summary>
14-
public static List<string> ParseParameterTypeStrings (string jniSignature)
14+
public static List<JniParameterInfo> ParseParameterTypes (string jniSignature)
1515
{
16-
var result = new List<string> ();
16+
var result = new List<JniParameterInfo> ();
1717
int i = 1; // skip opening '('
1818
while (i < jniSignature.Length && jniSignature [i] != ')') {
1919
int start = i;
2020
SkipSingleType (jniSignature, ref i);
21-
result.Add (jniSignature.Substring (start, i - start));
21+
result.Add (new JniParameterInfo { JniType = jniSignature.Substring (start, i - start) });
2222
}
2323
return result;
2424
}

src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/PEAssemblyBuilder.cs

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,14 +92,29 @@ public void WritePE (string outputPath)
9292
Directory.CreateDirectory (dir);
9393
}
9494

95+
var peBlob = BuildPEBlob ();
96+
using var fs = File.Create (outputPath);
97+
peBlob.WriteContentTo (fs);
98+
}
99+
100+
/// <summary>
101+
/// Serialises the metadata + IL into a PE DLL written to <paramref name="stream"/>.
102+
/// </summary>
103+
public void WritePE (Stream stream)
104+
{
105+
var peBlob = BuildPEBlob ();
106+
peBlob.WriteContentTo (stream);
107+
}
108+
109+
BlobBuilder BuildPEBlob ()
110+
{
95111
var peBuilder = new ManagedPEBuilder (
96112
new PEHeaderBuilder (imageCharacteristics: Characteristics.Dll),
97113
new MetadataRootBuilder (Metadata),
98114
ILBuilder);
99115
var peBlob = new BlobBuilder ();
100116
peBuilder.Serialize (peBlob);
101-
using var fs = File.Create (outputPath);
102-
peBlob.WriteContentTo (fs);
117+
return peBlob;
103118
}
104119

105120
/// <summary>

src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/RootTypeMapAssemblyGenerator.cs

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,33 @@ public void Generate (IReadOnlyList<string> perAssemblyTypeMapNames, string outp
4848

4949
assemblyName ??= DefaultAssemblyName;
5050
var moduleName = Path.GetFileName (outputPath);
51+
var pe = BuildPE (perAssemblyTypeMapNames, assemblyName, moduleName);
52+
pe.WritePE (outputPath);
53+
}
5154

55+
/// <summary>
56+
/// Generates the root typemap assembly and writes it to <paramref name="stream"/>.
57+
/// </summary>
58+
/// <param name="perAssemblyTypeMapNames">Names of per-assembly typemap assemblies to reference.</param>
59+
/// <param name="stream">Stream to write the output PE to.</param>
60+
/// <param name="assemblyName">Optional assembly name (defaults to _Microsoft.Android.TypeMaps).</param>
61+
public void Generate (IReadOnlyList<string> perAssemblyTypeMapNames, Stream stream, string? assemblyName = null)
62+
{
63+
if (perAssemblyTypeMapNames is null) {
64+
throw new ArgumentNullException (nameof (perAssemblyTypeMapNames));
65+
}
66+
if (stream is null) {
67+
throw new ArgumentNullException (nameof (stream));
68+
}
69+
70+
assemblyName ??= DefaultAssemblyName;
71+
var moduleName = assemblyName + ".dll";
72+
var pe = BuildPE (perAssemblyTypeMapNames, assemblyName, moduleName);
73+
pe.WritePE (stream);
74+
}
75+
76+
PEAssemblyBuilder BuildPE (IReadOnlyList<string> perAssemblyTypeMapNames, string assemblyName, string moduleName)
77+
{
5278
var pe = new PEAssemblyBuilder (_systemRuntimeVersion);
5379
pe.EmitPreamble (assemblyName, moduleName);
5480

@@ -76,6 +102,6 @@ public void Generate (IReadOnlyList<string> perAssemblyTypeMapNames, string outp
76102
pe.Metadata.AddCustomAttribute (EntityHandle.AssemblyDefinition, ctorRef, blobHandle);
77103
}
78104

79-
pe.WritePE (outputPath);
105+
return pe;
80106
}
81107
}

src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/TypeMapAssemblyEmitter.cs

Lines changed: 73 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,7 @@ namespace Microsoft.Android.Sdk.TrimmableTypeMap;
4242
/// </remarks>
4343
sealed class TypeMapAssemblyEmitter
4444
{
45-
readonly Version _systemRuntimeVersion;
46-
47-
PEAssemblyBuilder _pe = null!;
45+
readonly PEAssemblyBuilder _pe;
4846

4947
AssemblyReferenceHandle _javaInteropRef;
5048

@@ -73,7 +71,10 @@ sealed class TypeMapAssemblyEmitter
7371
/// </param>
7472
public TypeMapAssemblyEmitter (Version systemRuntimeVersion)
7573
{
76-
_systemRuntimeVersion = systemRuntimeVersion ?? throw new ArgumentNullException (nameof (systemRuntimeVersion));
74+
if (systemRuntimeVersion is null) {
75+
throw new ArgumentNullException (nameof (systemRuntimeVersion));
76+
}
77+
_pe = new PEAssemblyBuilder (systemRuntimeVersion);
7778
}
7879

7980
/// <summary>
@@ -88,7 +89,6 @@ public void Emit (TypeMapAssemblyData model, string outputPath)
8889
throw new ArgumentNullException (nameof (outputPath));
8990
}
9091

91-
_pe = new PEAssemblyBuilder (_systemRuntimeVersion);
9292
_pe.EmitPreamble (model.AssemblyName, model.ModuleName);
9393

9494
_javaInteropRef = _pe.AddAssemblyRef ("Java.Interop", new Version (0, 0, 0, 0));
@@ -242,34 +242,19 @@ void EmitProxyType (JavaPeerProxyData proxy)
242242
void EmitCreateInstance (JavaPeerProxyData proxy)
243243
{
244244
if (!proxy.HasActivation) {
245-
EmitCreateInstanceBody (encoder => {
246-
encoder.OpCode (ILOpCode.Ldnull);
247-
encoder.OpCode (ILOpCode.Ret);
248-
});
245+
EmitCreateInstanceNoActivation ();
249246
return;
250247
}
251248

252249
// Generic type definitions cannot be instantiated
253250
if (proxy.IsGenericDefinition) {
254-
EmitCreateInstanceBody (encoder => {
255-
encoder.LoadString (_pe.Metadata.GetOrAddUserString ("Cannot create instance of open generic type."));
256-
encoder.OpCode (ILOpCode.Newobj);
257-
encoder.Token (_notSupportedExceptionCtorRef);
258-
encoder.OpCode (ILOpCode.Throw);
259-
});
251+
EmitCreateInstanceOpenGeneric ();
260252
return;
261253
}
262254

263255
// Interface with invoker: new TInvoker(IntPtr, JniHandleOwnership)
264256
if (proxy.InvokerType != null) {
265-
var invokerCtorRef = AddActivationCtorRef (_pe.ResolveTypeRef (proxy.InvokerType));
266-
EmitCreateInstanceBody (encoder => {
267-
encoder.OpCode (ILOpCode.Ldarg_1);
268-
encoder.OpCode (ILOpCode.Ldarg_2);
269-
encoder.OpCode (ILOpCode.Newobj);
270-
encoder.Token (invokerCtorRef);
271-
encoder.OpCode (ILOpCode.Ret);
272-
});
257+
EmitCreateInstanceWithInvoker (proxy.InvokerType);
273258
return;
274259
}
275260

@@ -278,34 +263,74 @@ void EmitCreateInstance (JavaPeerProxyData proxy)
278263
var targetTypeRef = _pe.ResolveTypeRef (proxy.TargetType);
279264

280265
if (activationCtor.IsOnLeafType) {
281-
// Leaf type has its own ctor: new T(IntPtr, JniHandleOwnership)
282-
var ctorRef = AddActivationCtorRef (targetTypeRef);
283-
EmitCreateInstanceBody (encoder => {
284-
encoder.OpCode (ILOpCode.Ldarg_1);
285-
encoder.OpCode (ILOpCode.Ldarg_2);
286-
encoder.OpCode (ILOpCode.Newobj);
287-
encoder.Token (ctorRef);
288-
encoder.OpCode (ILOpCode.Ret);
289-
});
266+
EmitCreateInstanceLeafType (targetTypeRef);
290267
} else {
291-
// Inherited ctor: GetUninitializedObject(typeof(T)) + call Base::.ctor(IntPtr, JniHandleOwnership)
292-
var baseActivationCtorRef = AddActivationCtorRef (_pe.ResolveTypeRef (activationCtor.DeclaringType));
293-
EmitCreateInstanceBody (encoder => {
294-
encoder.OpCode (ILOpCode.Ldtoken);
295-
encoder.Token (targetTypeRef);
296-
encoder.Call (_getTypeFromHandleRef);
297-
encoder.Call (_getUninitializedObjectRef);
298-
encoder.OpCode (ILOpCode.Castclass);
299-
encoder.Token (targetTypeRef);
268+
EmitCreateInstanceInheritedCtor (targetTypeRef, activationCtor);
269+
}
270+
}
300271

301-
encoder.OpCode (ILOpCode.Dup);
302-
encoder.OpCode (ILOpCode.Ldarg_1);
303-
encoder.OpCode (ILOpCode.Ldarg_2);
304-
encoder.Call (baseActivationCtorRef);
272+
void EmitCreateInstanceNoActivation ()
273+
{
274+
EmitCreateInstanceBody (encoder => {
275+
encoder.OpCode (ILOpCode.Ldnull);
276+
encoder.OpCode (ILOpCode.Ret);
277+
});
278+
}
305279

306-
encoder.OpCode (ILOpCode.Ret);
307-
});
308-
}
280+
void EmitCreateInstanceOpenGeneric ()
281+
{
282+
EmitCreateInstanceBody (encoder => {
283+
encoder.LoadString (_pe.Metadata.GetOrAddUserString ("Cannot create instance of open generic type."));
284+
encoder.OpCode (ILOpCode.Newobj);
285+
encoder.Token (_notSupportedExceptionCtorRef);
286+
encoder.OpCode (ILOpCode.Throw);
287+
});
288+
}
289+
290+
void EmitCreateInstanceWithInvoker (TypeRefData invokerType)
291+
{
292+
var invokerCtorRef = AddActivationCtorRef (_pe.ResolveTypeRef (invokerType));
293+
EmitCreateInstanceBody (encoder => {
294+
encoder.OpCode (ILOpCode.Ldarg_1);
295+
encoder.OpCode (ILOpCode.Ldarg_2);
296+
encoder.OpCode (ILOpCode.Newobj);
297+
encoder.Token (invokerCtorRef);
298+
encoder.OpCode (ILOpCode.Ret);
299+
});
300+
}
301+
302+
void EmitCreateInstanceLeafType (EntityHandle targetTypeRef)
303+
{
304+
// Leaf type has its own ctor: new T(IntPtr, JniHandleOwnership)
305+
var ctorRef = AddActivationCtorRef (targetTypeRef);
306+
EmitCreateInstanceBody (encoder => {
307+
encoder.OpCode (ILOpCode.Ldarg_1);
308+
encoder.OpCode (ILOpCode.Ldarg_2);
309+
encoder.OpCode (ILOpCode.Newobj);
310+
encoder.Token (ctorRef);
311+
encoder.OpCode (ILOpCode.Ret);
312+
});
313+
}
314+
315+
void EmitCreateInstanceInheritedCtor (EntityHandle targetTypeRef, ActivationCtorData activationCtor)
316+
{
317+
// Inherited ctor: GetUninitializedObject(typeof(T)) + call Base::.ctor(IntPtr, JniHandleOwnership)
318+
var baseActivationCtorRef = AddActivationCtorRef (_pe.ResolveTypeRef (activationCtor.DeclaringType));
319+
EmitCreateInstanceBody (encoder => {
320+
encoder.OpCode (ILOpCode.Ldtoken);
321+
encoder.Token (targetTypeRef);
322+
encoder.Call (_getTypeFromHandleRef);
323+
encoder.Call (_getUninitializedObjectRef);
324+
encoder.OpCode (ILOpCode.Castclass);
325+
encoder.Token (targetTypeRef);
326+
327+
encoder.OpCode (ILOpCode.Dup);
328+
encoder.OpCode (ILOpCode.Ldarg_1);
329+
encoder.OpCode (ILOpCode.Ldarg_2);
330+
encoder.Call (baseActivationCtorRef);
331+
332+
encoder.OpCode (ILOpCode.Ret);
333+
});
309334
}
310335

311336
void EmitCreateInstanceBody (Action<InstructionEncoder> emitIL)

src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/JavaPeerInfo.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,17 @@ sealed record JavaPeerInfo
7979
/// </summary>
8080
public string? InvokerTypeName { get; init; }
8181

82+
/// <summary>
83+
/// JNI name of the direct base class (if it is a Java peer type), or null if the base is not a peer.
84+
/// For example, <c>java/lang/Object</c> for <c>Activity</c>.
85+
/// </summary>
86+
public string? BaseJavaName { get; init; }
87+
88+
/// <summary>
89+
/// JNI names of directly implemented interface types that are Java peer types.
90+
/// </summary>
91+
public IReadOnlyList<string> ImplementedInterfaceJavaNames { get; init; } = Array.Empty<string> ();
92+
8293
/// <summary>
8394
/// True if this is an open generic type definition.
8495
/// Generic types get TypeMap entries but CreateInstance throws NotSupportedException.

src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/JavaPeerScanner.cs

Lines changed: 55 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,9 @@ public List<JavaPeerInfo> Scan (IReadOnlyList<string> assemblyPaths)
9797
// Phase 3: Force unconditional on types referenced by [Application] attributes
9898
ForceUnconditionalCrossReferences (resultsByManagedName, assemblyCache);
9999

100+
// Phase 4: Resolve BaseJavaName and ImplementedInterfaceJavaNames from the type hierarchy
101+
ResolveJavaHierarchyNames (resultsByManagedName);
102+
100103
return new List<JavaPeerInfo> (resultsByManagedName.Values);
101104
}
102105

@@ -117,6 +120,56 @@ static void ForceUnconditionalCrossReferences (Dictionary<string, JavaPeerInfo>
117120
}
118121
}
119122

123+
/// <summary>
124+
/// Resolves <see cref="JavaPeerInfo.BaseJavaName"/> and
125+
/// <see cref="JavaPeerInfo.ImplementedInterfaceJavaNames"/> for all scanned peers
126+
/// by looking up their base class and directly-implemented interfaces in the scan results.
127+
/// </summary>
128+
void ResolveJavaHierarchyNames (Dictionary<string, JavaPeerInfo> results)
129+
{
130+
// Build a fast lookup: managed full type name → JNI name
131+
var jniByManagedName = new Dictionary<string, string> (StringComparer.Ordinal);
132+
foreach (var peer in results.Values) {
133+
jniByManagedName [peer.ManagedTypeName] = peer.JavaName;
134+
}
135+
136+
var keys = new List<string> (results.Keys);
137+
foreach (var managedName in keys) {
138+
var peer = results [managedName];
139+
if (!TryResolveType (managedName, peer.AssemblyName, out var handle, out var index)) {
140+
continue;
141+
}
142+
143+
var typeDef = index.Reader.GetTypeDefinition (handle);
144+
145+
// BaseJavaName: look up the direct base class's JNI name
146+
string? baseJniName = null;
147+
var baseInfo = GetBaseTypeInfo (typeDef, index);
148+
if (baseInfo is not null && jniByManagedName.TryGetValue (baseInfo.Value.typeName, out var bn)) {
149+
baseJniName = bn;
150+
}
151+
152+
// ImplementedInterfaceJavaNames: JNI names of directly implemented interfaces
153+
var ifaceJniNames = new List<string> ();
154+
foreach (var ifaceHandle in typeDef.GetInterfaceImplementations ()) {
155+
var iface = index.Reader.GetInterfaceImplementation (ifaceHandle);
156+
var ifaceInfo = ResolveEntityHandle (iface.Interface, index);
157+
if (ifaceInfo is not null && jniByManagedName.TryGetValue (ifaceInfo.Value.typeName, out var ifaceJni)) {
158+
ifaceJniNames.Add (ifaceJni);
159+
}
160+
}
161+
162+
if (baseJniName != null || ifaceJniNames.Count > 0) {
163+
results [managedName] = peer with {
164+
BaseJavaName = baseJniName,
165+
ImplementedInterfaceJavaNames = ifaceJniNames.Count > 0
166+
? (IReadOnlyList<string>) ifaceJniNames
167+
: Array.Empty<string> (),
168+
};
169+
}
170+
}
171+
}
172+
120173
static void ForceUnconditionalIfPresent (Dictionary<string, JavaPeerInfo> resultsByManagedName, string? managedTypeName)
121174
{
122175
if (managedTypeName is null) {
@@ -273,9 +326,8 @@ static void AddMarshalMethod (List<MarshalMethodInfo> methods, RegisterInfo regi
273326
if (isConstructor) {
274327
int ctorIndex = 0;
275328
foreach (var method in methods) {
276-
if (method.IsConstructor) {
329+
if (method.IsConstructor)
277330
ctorIndex++;
278-
}
279331
}
280332
nativeCallbackName = ctorIndex == 0 ? "n_ctor" : $"n_ctor_{ctorIndex}";
281333
}
@@ -713,11 +765,6 @@ static string ExtractShortName (string fullName)
713765

714766
static List<JniParameterInfo> ParseJniParameters (string jniSignature)
715767
{
716-
var typeStrings = JniSignatureHelper.ParseParameterTypeStrings (jniSignature);
717-
var result = new List<JniParameterInfo> (typeStrings.Count);
718-
foreach (var t in typeStrings) {
719-
result.Add (new JniParameterInfo { JniType = t });
720-
}
721-
return result;
768+
return JniSignatureHelper.ParseParameterTypes (jniSignature);
722769
}
723770
}

0 commit comments

Comments
 (0)