Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,10 @@ sealed class PEAssemblyBuilder
// Avoids creating duplicate __utf8_N types when multiple fields share the same size.
readonly Dictionary<int, TypeDefinitionHandle> _sizedTypeCache = new ();

// Deduplication cache for UTF-8 string RVA fields. Strings like "()V" that repeat across
// many proxy types are stored once and shared via the same FieldDefinitionHandle.
readonly Dictionary<string, FieldDefinitionHandle> _utf8FieldCache = new (StringComparer.Ordinal);
// JNI signatures are owner-independent and can safely share one RVA field. JNI method names
// are owner-specific after R8 rewriting, so each registration receives its own field.
readonly Dictionary<string, FieldDefinitionHandle> _sharedUtf8FieldCache = new (StringComparer.Ordinal);
readonly Dictionary<string, Queue<FieldDefinitionHandle>> _uniqueUtf8FieldCache = new (StringComparer.Ordinal);
TypeDefinitionHandle _privateImplDetailsType;
int _utf8FieldCounter;

Expand Down Expand Up @@ -273,31 +274,57 @@ TypeReferenceHandle MakeTypeRefForManagedName (EntityHandle scope, string manage
}

/// <summary>
/// Emits deduplicated RVA fields containing the supplied null-terminated UTF-8 strings.
/// Emits RVA fields containing the supplied null-terminated UTF-8 strings.
/// <paramref name="sharedValues"/> are deduplicated, while every occurrence in
/// <paramref name="uniqueValues"/> receives a separate field.
/// Fields are grouped by size so each group is emitted contiguously on its sized helper
/// type before any consuming types are emitted.
/// </summary>
public void PrepareUtf8Fields (IEnumerable<string> values)
public void PrepareUtf8Fields (IEnumerable<string> sharedValues, IEnumerable<string> uniqueValues)
{
var valuesBySize = new SortedDictionary<int, SortedSet<string>> ();
foreach (string value in values) {
var sharedValuesBySize = new SortedDictionary<int, SortedSet<string>> ();
foreach (string value in sharedValues) {
int size = System.Text.Encoding.UTF8.GetByteCount (value) + 1;
if (!valuesBySize.TryGetValue (size, out var valuesForSize)) {
if (!sharedValuesBySize.TryGetValue (size, out var valuesForSize)) {
valuesForSize = new SortedSet<string> (StringComparer.Ordinal);
valuesBySize.Add (size, valuesForSize);
sharedValuesBySize.Add (size, valuesForSize);
}
valuesForSize.Add (value);
}

foreach (var group in valuesBySize) {
var sizedType = GetOrCreateSizedType (group.Key);
foreach (string value in group.Value) {
AddUtf8Field (value, sizedType);
var uniqueValuesBySize = new SortedDictionary<int, SortedDictionary<string, int>> ();
foreach (string value in uniqueValues) {
int size = System.Text.Encoding.UTF8.GetByteCount (value) + 1;
if (!uniqueValuesBySize.TryGetValue (size, out var valuesForSize)) {
valuesForSize = new SortedDictionary<string, int> (StringComparer.Ordinal);
uniqueValuesBySize.Add (size, valuesForSize);
}
valuesForSize.TryGetValue (value, out int count);
valuesForSize [value] = count + 1;
}

var sizes = new SortedSet<int> (sharedValuesBySize.Keys);
sizes.UnionWith (uniqueValuesBySize.Keys);
foreach (int size in sizes) {
var sizedType = GetOrCreateSizedType (size);
if (sharedValuesBySize.TryGetValue (size, out var sharedForSize)) {
foreach (string value in sharedForSize) {
_sharedUtf8FieldCache.Add (value, AddUtf8Field (value, sizedType));
}
}
if (uniqueValuesBySize.TryGetValue (size, out var uniqueForSize)) {
foreach (var pair in uniqueForSize) {
var fields = new Queue<FieldDefinitionHandle> (pair.Value);
for (int i = 0; i < pair.Value; i++) {
fields.Enqueue (AddUtf8Field (pair.Key, sizedType));
}
_uniqueUtf8FieldCache.Add (pair.Key, fields);
}
}
}
}

void AddUtf8Field (string value, TypeDefinitionHandle sizedType)
FieldDefinitionHandle AddUtf8Field (string value, TypeDefinitionHandle sizedType)
{
// Encode to null-terminated UTF-8 (all JNI names/signatures are ASCII).
_sigBlob.Clear ();
Expand All @@ -313,22 +340,33 @@ void AddUtf8Field (string value, TypeDefinitionHandle sizedType)
Metadata.GetOrAddBlob (_sigBlob));

Metadata.AddFieldRelativeVirtualAddress (fieldHandle, rva);

_utf8FieldCache [value] = fieldHandle;
return fieldHandle;
}

/// <summary>
/// Returns a previously prepared UTF-8 RVA field.
/// </summary>
public FieldDefinitionHandle GetUtf8Field (string value)
{
if (_utf8FieldCache.TryGetValue (value, out var existing)) {
if (_sharedUtf8FieldCache.TryGetValue (value, out var existing)) {
return existing;
}

throw new InvalidOperationException ($"UTF-8 field '{value}' was not prepared before type emission.");
}

/// <summary>
/// Returns and consumes one previously prepared unique UTF-8 RVA field.
/// </summary>
public FieldDefinitionHandle GetUniqueUtf8Field (string value)
{
if (_uniqueUtf8FieldCache.TryGetValue (value, out var fields) && fields.Count > 0) {
return fields.Dequeue ();
}

throw new InvalidOperationException ($"Unique UTF-8 field '{value}' was not prepared before type emission.");
}

void EnsurePrivateImplDetailsType ()
{
if (!_privateImplDetailsType.IsNil) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
Expand Down Expand Up @@ -196,7 +197,10 @@ void EmitCore (TypeMapAssemblyData model, bool useSharedTypemapUniverse)
}
EmitMemberReferences ();

_pe.PrepareUtf8Fields (EnumerateNativeRegistrationStrings (model.ProxyTypes));
var validRegistrations = EnumerateValidNativeRegistrations (model.ProxyTypes);
_pe.PrepareUtf8Fields (
validRegistrations.Select (registration => registration.JniSignature),
validRegistrations.Select (registration => registration.JniMethodName));

// Track wrapper targets → handles for RegisterNatives.
var wrapperHandles = new Dictionary<UcoWrapperTargetData, MethodDefinitionHandle> ();
Expand All @@ -220,17 +224,30 @@ void EmitCore (TypeMapAssemblyData model, bool useSharedTypemapUniverse)
_pe.EmitIgnoresAccessChecksToAttribute (model.IgnoresAccessChecksTo);
}

static IEnumerable<string> EnumerateNativeRegistrationStrings (IReadOnlyList<JavaPeerProxyData> proxies)
static List<NativeRegistrationData> EnumerateValidNativeRegistrations (IReadOnlyList<JavaPeerProxyData> proxies)
{
var wrapperTargets = new HashSet<UcoWrapperTargetData> ();
foreach (var proxy in proxies) {
foreach (var method in proxy.UcoMethods) {
wrapperTargets.Add (UcoWrapperTargetData.From (proxy, method.WrapperName));
}
foreach (var constructor in proxy.UcoConstructors) {
wrapperTargets.Add (UcoWrapperTargetData.From (proxy, constructor.WrapperName));
}
}

var registrations = new List<NativeRegistrationData> ();
foreach (var proxy in proxies) {
if (!proxy.IsAcw) {
continue;
}
foreach (var registration in proxy.NativeRegistrations) {
yield return registration.JniMethodName;
yield return registration.JniSignature;
if (wrapperTargets.Contains (registration.WrapperTarget)) {
registrations.Add (registration);
}
}
}
return registrations;
}

static List<JavaPeerProxyData> OrderProxiesForWrapperTargets (IReadOnlyList<JavaPeerProxyData> proxies)
Expand Down Expand Up @@ -1656,11 +1673,12 @@ void EmitRegisterNatives (JavaPeerProxyData proxy,
return;
}

// Get the prepared, deduplicated RVA fields for each unique name/signature string.
// Method names are unique per registration because R8 member mappings are owner-specific.
// Signatures remain safely deduplicated because descriptor class mappings are owner-independent.
var nameFields = new FieldDefinitionHandle [validRegs.Count];
var sigFields = new FieldDefinitionHandle [validRegs.Count];
for (int i = 0; i < validRegs.Count; i++) {
nameFields [i] = _pe.GetUtf8Field (validRegs [i].Reg.JniMethodName);
nameFields [i] = _pe.GetUniqueUtf8Field (validRegs [i].Reg.JniMethodName);
sigFields [i] = _pe.GetUtf8Field (validRegs [i].Reg.JniSignature);
}

Expand Down
Loading
Loading