Skip to content

Commit 0b119d6

Browse files
simonrozsivalCopilot
authored andcommitted
Reduce typemap signature encoder allocations
Cache fixed activation and CreateInstance signatures so repeated PE emission avoids nested signature encoder delegates while preserving emitted bytes. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
1 parent 51e0fdf commit 0b119d6

3 files changed

Lines changed: 101 additions & 22 deletions

File tree

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

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -162,9 +162,12 @@ public MemberReferenceHandle AddMemberRef (EntityHandle parent, string name, Act
162162
{
163163
_sigBlob.Clear ();
164164
encodeSig (new BlobEncoder (_sigBlob));
165-
return Metadata.AddMemberReference (parent, Metadata.GetOrAddString (name), Metadata.GetOrAddBlob (_sigBlob));
165+
return AddMemberRef (parent, name, Metadata.GetOrAddBlob (_sigBlob));
166166
}
167167

168+
public MemberReferenceHandle AddMemberRef (EntityHandle parent, string name, BlobHandle signature)
169+
=> Metadata.AddMemberReference (parent, Metadata.GetOrAddString (name), signature);
170+
168171
/// <summary>
169172
/// Resolves a <see cref="TypeRefData"/> to a TypeReference/TypeSpecification handle, with caching.
170173
/// </summary>
@@ -382,6 +385,15 @@ public MethodDefinitionHandle EmitBody (string name, MethodAttributes attrs,
382385
Action<BlobEncoder> encodeSig, Action<TrackedInstructionEncoder> emitIL)
383386
=> EmitBody (name, attrs, encodeSig, emitIL, encodeLocals: null, useBranches: false);
384387

388+
public MethodDefinitionHandle EmitBody (string name, MethodAttributes attrs,
389+
BlobHandle signature, Action<TrackedInstructionEncoder> emitIL)
390+
=> EmitBody (name, attrs, signature, emitIL, encodeLocals: null, useBranches: false);
391+
392+
public MethodDefinitionHandle EmitBody (string name, MethodAttributes attrs,
393+
BlobHandle signature, Action<TrackedInstructionEncoder> emitIL,
394+
Action<BlobBuilder>? encodeLocals)
395+
=> EmitBody (name, attrs, signature, emitIL, encodeLocals, useBranches: false);
396+
385397
/// <summary>
386398
/// Emits a method body and definition with optional local variable declarations.
387399
/// </summary>
@@ -408,7 +420,13 @@ public MethodDefinitionHandle EmitBody (string name, MethodAttributes attrs,
408420
encodeSig (new BlobEncoder (_sigBlob));
409421
// Capture the sig blob handle before emitIL, because emitIL callbacks
410422
// may call AddMemberRef which clears and repopulates _sigBlob.
411-
var sigBlobHandle = Metadata.GetOrAddBlob (_sigBlob);
423+
return EmitBody (name, attrs, Metadata.GetOrAddBlob (_sigBlob), emitIL, encodeLocals, useBranches);
424+
}
425+
426+
MethodDefinitionHandle EmitBody (string name, MethodAttributes attrs,
427+
BlobHandle signature, Action<TrackedInstructionEncoder> emitIL,
428+
Action<BlobBuilder>? encodeLocals, bool useBranches)
429+
{
412430

413431
StandaloneSignatureHandle localSigHandle = default;
414432
if (encodeLocals != null) {
@@ -433,7 +451,7 @@ public MethodDefinitionHandle EmitBody (string name, MethodAttributes attrs,
433451
return Metadata.AddMethodDefinition (
434452
attrs, MethodImplAttributes.IL,
435453
Metadata.GetOrAddString (name),
436-
sigBlobHandle,
454+
signature,
437455
bodyOffset, MetadataTokens.ParameterHandle (Metadata.GetRowCount (TableIndex.Param) + 1));
438456
}
439457

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

Lines changed: 37 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,9 @@ sealed class TypeMapAssemblyEmitter
137137
MemberReferenceHandle _jniEnvTypesRegisterNativesRef;
138138
MemberReferenceHandle _readOnlySpanOfJniNativeMethodCtorRef;
139139

140+
BlobHandle _activationCtorSignature;
141+
BlobHandle _createInstanceSignature;
142+
140143
EntityHandle _anchorTypeHandle;
141144

142145
ExportMethodDispatchEmitter? _exportMethodDispatchEmitter;
@@ -950,38 +953,53 @@ void EmitCreateInstanceBody (Action<TrackedInstructionEncoder> emitIL)
950953
{
951954
_pe.EmitBody ("CreateInstance",
952955
MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig,
953-
sig => sig.MethodSignature (isInstanceMethod: true).Parameters (2,
954-
rt => rt.Type ().Type (_iJavaPeerableRef, false),
955-
p => {
956-
p.AddParameter ().Type ().IntPtr ();
957-
p.AddParameter ().Type ().Type (_jniHandleOwnershipRef, true);
958-
}),
956+
GetCreateInstanceSignature (),
959957
emitIL);
960958
}
961959

962960
void EmitCreateInstanceBodyWithLocals (Action<BlobBuilder> encodeLocals, Action<TrackedInstructionEncoder> emitIL)
963961
{
964962
_pe.EmitBody ("CreateInstance",
965963
MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig,
966-
sig => sig.MethodSignature (isInstanceMethod: true).Parameters (2,
967-
rt => rt.Type ().Type (_iJavaPeerableRef, false),
968-
p => {
969-
p.AddParameter ().Type ().IntPtr ();
970-
p.AddParameter ().Type ().Type (_jniHandleOwnershipRef, true);
971-
}),
964+
GetCreateInstanceSignature (),
972965
emitIL,
973966
encodeLocals);
974967
}
975968

976969
MemberReferenceHandle AddActivationCtorRef (EntityHandle declaringTypeRef)
977970
{
978-
return _pe.AddMemberRef (declaringTypeRef, ".ctor",
979-
sig => sig.MethodSignature (isInstanceMethod: true).Parameters (2,
980-
rt => rt.Void (),
981-
p => {
982-
p.AddParameter ().Type ().IntPtr ();
983-
p.AddParameter ().Type ().Type (_jniHandleOwnershipRef, true);
984-
}));
971+
return _pe.AddMemberRef (declaringTypeRef, ".ctor", GetActivationCtorSignature ());
972+
}
973+
974+
BlobHandle GetActivationCtorSignature ()
975+
{
976+
if (_activationCtorSignature.IsNil) {
977+
var blob = new BlobBuilder (8);
978+
blob.WriteByte ((byte) SignatureAttributes.Instance);
979+
blob.WriteCompressedInteger (2);
980+
blob.WriteByte ((byte) SignatureTypeCode.Void);
981+
blob.WriteByte ((byte) SignatureTypeCode.IntPtr);
982+
blob.WriteByte ((byte) SignatureTypeKind.ValueType);
983+
blob.WriteCompressedInteger (CodedIndex.TypeDefOrRefOrSpec (_jniHandleOwnershipRef));
984+
_activationCtorSignature = _pe.Metadata.GetOrAddBlob (blob);
985+
}
986+
return _activationCtorSignature;
987+
}
988+
989+
BlobHandle GetCreateInstanceSignature ()
990+
{
991+
if (_createInstanceSignature.IsNil) {
992+
var blob = new BlobBuilder (8);
993+
blob.WriteByte ((byte) SignatureAttributes.Instance);
994+
blob.WriteCompressedInteger (2);
995+
blob.WriteByte ((byte) SignatureTypeKind.Class);
996+
blob.WriteCompressedInteger (CodedIndex.TypeDefOrRefOrSpec (_iJavaPeerableRef));
997+
blob.WriteByte ((byte) SignatureTypeCode.IntPtr);
998+
blob.WriteByte ((byte) SignatureTypeKind.ValueType);
999+
blob.WriteCompressedInteger (CodedIndex.TypeDefOrRefOrSpec (_jniHandleOwnershipRef));
1000+
_createInstanceSignature = _pe.Metadata.GetOrAddBlob (blob);
1001+
}
1002+
return _createInstanceSignature;
9851003
}
9861004

9871005
MemberReferenceHandle AddManagedCtorRef (EntityHandle declaringTypeRef, IReadOnlyList<TypeRefData> parameterTypes)

tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/TypeMapAssemblyGeneratorTests.cs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,49 @@ public void EmitBody_ILCallbackCallsAddMemberRef_SignatureNotCorrupted ()
393393
Assert.Equal ("System.Int32", paramType);
394394
}
395395

396+
[Fact]
397+
public void EmitBody_PreencodedSignature_PreservesMethodSignature ()
398+
{
399+
var pe = new PEAssemblyBuilder (new Version (11, 0, 0, 0));
400+
pe.EmitPreamble ("PreencodedSigTest", "PreencodedSigTest.dll");
401+
var objectRef = pe.Metadata.AddTypeReference (pe.SystemRuntimeRef,
402+
pe.Metadata.GetOrAddString ("System"), pe.Metadata.GetOrAddString ("Object"));
403+
pe.Metadata.AddTypeDefinition (
404+
TypeAttributes.Public | TypeAttributes.Class,
405+
pe.Metadata.GetOrAddString ("Test"),
406+
pe.Metadata.GetOrAddString ("MyType"),
407+
objectRef,
408+
MetadataTokens.FieldDefinitionHandle (pe.Metadata.GetRowCount (TableIndex.Field) + 1),
409+
MetadataTokens.MethodDefinitionHandle (pe.Metadata.GetRowCount (TableIndex.MethodDef) + 1));
410+
var signature = new BlobBuilder ();
411+
signature.WriteByte ((byte) SignatureAttributes.Instance);
412+
signature.WriteCompressedInteger (1);
413+
signature.WriteByte ((byte) SignatureTypeCode.String);
414+
signature.WriteByte ((byte) SignatureTypeCode.Int32);
415+
416+
pe.EmitBody (
417+
"PreencodedMethod",
418+
MethodAttributes.Public,
419+
pe.Metadata.GetOrAddBlob (signature),
420+
encoder => {
421+
encoder.OpCode (ILOpCode.Ldnull);
422+
encoder.Return (returnsValue: true);
423+
});
424+
using var stream = new MemoryStream ();
425+
pe.WritePE (stream);
426+
stream.Position = 0;
427+
using var peReader = new PEReader (stream);
428+
var reader = peReader.GetMetadataReader ();
429+
var method = reader.TypeDefinitions
430+
.SelectMany (handle => reader.GetTypeDefinition (handle).GetMethods ())
431+
.Select (handle => reader.GetMethodDefinition (handle))
432+
.Single (method => reader.GetString (method.Name) == "PreencodedMethod");
433+
var decoded = method.DecodeSignature (SignatureTypeProvider.Instance, null);
434+
435+
Assert.Equal ("System.String", decoded.ReturnType);
436+
Assert.Equal ("System.Int32", Assert.Single (decoded.ParameterTypes));
437+
}
438+
396439
[Fact]
397440
public void Generate_JiStyleInvoker_FirstParamIsByRef ()
398441
{

0 commit comments

Comments
 (0)