Skip to content

Commit cb26eab

Browse files
[TrimmableTypeMap] Split array-proxy rank: reference-1, primitive-3
The trimmable typemap generates array-proxy types (JavaObjectArray<T>, T[], nested closures) up to a configurable rank so that array types can be resolved without Array.CreateInstance under NativeAOT. Emitting these uniformly at rank 3 caused a NativeAOT ILC type explosion: on an AppCompat app ILC compiled/preinitialized ~129,320 types and IlcCompile took 333s, which pushed the heaviest CI apps past the 30-min DefaultBuildTimeOut. Reference-type array proxies (scanned Java peers, System.String, boxed Nullable<T>) are now capped at a separate, lower rank (_AndroidTrimmableTypeMapMaxReferenceArrayRank, default 1 for NativeAOT) while primitive arrays keep the higher rank (default 3). Jagged reference arrays (Foo[][]) are rare, and each nesting level multiplies the JavaObjectArray<T> closure ILC must process, so this trims the bulk of the cost while preserving the common Foo[] and int[][][] cases. The anchor matrix (model.MaxArrayRank) stays uniform at max(primitive, reference) across all typemap assemblies so RootTypeMapAssemblyGenerator's rectangular [assembly][rank] sentinel layout is unchanged; only the emitted entries per rank differ. Measured on the same AppCompat NativeAOT app: ILC types 129,320 -> 22,673 (5.7x fewer) IlcCompile 333s -> 91s (3.65x faster) TypeMap DLLs 19.7MB -> 9.7MB (~2x smaller) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 5d4c2e2 commit cb26eab

7 files changed

Lines changed: 102 additions & 22 deletions

File tree

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

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ static readonly (string Name, string ManagedTypeName) [] NullableArrayProxies =
7070
/// Emit per-rank array <c>TypeMap</c> entries + <c>__ArrayMapRank{N}</c> sentinels
7171
/// for ranks 1..<paramref name="maxArrayRank"/>. 0 disables array entry emission.
7272
/// </param>
73-
public static TypeMapAssemblyData Build (IReadOnlyList<JavaPeerInfo> peers, string outputPath, string? assemblyName = null, int maxArrayRank = 0)
73+
public static TypeMapAssemblyData Build (IReadOnlyList<JavaPeerInfo> peers, string outputPath, string? assemblyName = null, int maxArrayRank = 0, int maxReferenceArrayRank = 0)
7474
{
7575
if (peers is null) {
7676
throw new ArgumentNullException (nameof (peers));
@@ -81,13 +81,20 @@ public static TypeMapAssemblyData Build (IReadOnlyList<JavaPeerInfo> peers, stri
8181
if (maxArrayRank < 0) {
8282
throw new ArgumentOutOfRangeException (nameof (maxArrayRank), maxArrayRank, "Must be >= 0.");
8383
}
84+
if (maxReferenceArrayRank < 0) {
85+
throw new ArgumentOutOfRangeException (nameof (maxReferenceArrayRank), maxReferenceArrayRank, "Must be >= 0.");
86+
}
8487

8588
assemblyName ??= Path.GetFileNameWithoutExtension (outputPath);
8689

8790
var model = new TypeMapAssemblyData {
8891
AssemblyName = assemblyName,
8992
ModuleName = Path.GetFileName (outputPath),
90-
MaxArrayRank = maxArrayRank,
93+
// The per-assembly __ArrayMapRank{N} anchor count must be uniform across every typemap
94+
// assembly (the root generator builds a rectangular [assembly][rank] matrix), so the model
95+
// always carries the overall maximum rank. Primitive and reference element types may
96+
// populate different rank ranges within that shared anchor set.
97+
MaxArrayRank = Math.Max (maxArrayRank, maxReferenceArrayRank),
9198
};
9299

93100
// Invoker types are NOT emitted as separate proxies or TypeMap entries.
@@ -137,13 +144,14 @@ public static TypeMapAssemblyData Build (IReadOnlyList<JavaPeerInfo> peers, stri
137144

138145
EmitPeers (model, jniName, peersForName, assemblyName, usedProxyNames);
139146

140-
if (maxArrayRank > 0) {
141-
EmitArrayEntries (model, jniName, peersForName, maxArrayRank);
147+
// Java peer types are reference types: emit array proxies only up to the reference rank.
148+
if (maxReferenceArrayRank > 0) {
149+
EmitArrayEntries (model, jniName, peersForName, maxReferenceArrayRank);
142150
}
143151
}
144152

145-
if (maxArrayRank > 0 && string.Equals (assemblyName, "_Java.Interop.TypeMap", StringComparison.Ordinal)) {
146-
EmitPrimitiveArrayEntries (model, maxArrayRank);
153+
if (string.Equals (assemblyName, "_Java.Interop.TypeMap", StringComparison.Ordinal)) {
154+
EmitPrimitiveArrayEntries (model, maxArrayRank, maxReferenceArrayRank);
147155
}
148156

149157
BuildNativeRegistrations (model);
@@ -693,7 +701,12 @@ static void EmitArrayEntriesForPeer (TypeMapAssemblyData model, JavaPeerInfo pee
693701
}
694702
}
695703

696-
static void EmitPrimitiveArrayEntries (TypeMapAssemblyData model, int maxArrayRank)
704+
// Emits array proxies for the built-in element types that are not scanned Java peers:
705+
// * the keyword primitives (int/bool/...) up to maxArrayRank (jagged/multidim primitive arrays
706+
// like int[][][] are cheap — a small fixed set of element types),
707+
// * System.String and the boxed Nullable<T> value types up to maxReferenceArrayRank (these map to
708+
// java/lang/String and java/lang/<Boxed>, i.e. Java reference arrays).
709+
static void EmitPrimitiveArrayEntries (TypeMapAssemblyData model, int maxArrayRank, int maxReferenceArrayRank)
697710
{
698711
foreach (var primitive in PrimitiveArrayProxies) {
699712
for (int rank = 1; rank <= maxArrayRank; rank++) {
@@ -728,7 +741,8 @@ static void EmitPrimitiveArrayEntries (TypeMapAssemblyData model, int maxArrayRa
728741
// Java peer (EmitArrayEntriesForPeer) nor a primitive. Emit its array proxies here (Primitive is
729742
// null, so it gets the reference-array family) so GetTypes ("[Ljava/lang/String;") yields
730743
// System.String[] / JavaObjectArray<string> / JavaArray<string> on NativeAOT, matching CoreCLR.
731-
for (int rank = 1; rank <= maxArrayRank; rank++) {
744+
// It is a reference type, so it only goes up to maxReferenceArrayRank.
745+
for (int rank = 1; rank <= maxReferenceArrayRank; rank++) {
732746
var proxy = new ArrayProxyData {
733747
TypeName = ManagedTypeNameToArrayProxyTypeName ("System.String", rank),
734748
ElementType = new TypeRefData {
@@ -753,10 +767,11 @@ static void EmitPrimitiveArrayEntries (TypeMapAssemblyData model, int maxArrayRa
753767
// primitive proxy). Emit reference-array proxies (Primitive is null) so GetTypes
754768
// ("[Ljava/lang/Boolean;") yields bool?[] / JavaObjectArray<bool?> on NativeAOT. The element
755769
// key uses the normalized generic form (simple assembly names) that
756-
// TrimmableTypeMap.BuildManagedTypeKey produces at runtime for Nullable<T>.
770+
// TrimmableTypeMap.BuildManagedTypeKey produces at runtime for Nullable<T>. The boxed values are
771+
// Java reference types, so they only go up to maxReferenceArrayRank.
757772
foreach (var nullablePrimitive in NullableArrayProxies) {
758773
var elementTypeName = $"System.Nullable`1[[{nullablePrimitive.ManagedTypeName}, System.Runtime]]";
759-
for (int rank = 1; rank <= maxArrayRank; rank++) {
774+
for (int rank = 1; rank <= maxReferenceArrayRank; rank++) {
760775
var proxy = new ArrayProxyData {
761776
TypeName = $"Nullable_{nullablePrimitive.Name}_ArrayProxy{rank}",
762777
ElementType = new TypeRefData {

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,11 @@ public TypeMapAssemblyGenerator (Version systemRuntimeVersion)
2727
/// <param name="useSharedTypemapUniverse">
2828
/// When true, uses <c>Java.Lang.Object</c> as the shared anchor type. When false, emits a per-assembly anchor.
2929
/// </param>
30-
/// <param name="maxArrayRank">Max rank for per-rank array <c>TypeMap</c> entries. 0 disables.</param>
31-
public void Generate (IReadOnlyList<JavaPeerInfo> peers, Stream stream, string assemblyName, bool useSharedTypemapUniverse = false, int maxArrayRank = 0)
30+
/// <param name="maxArrayRank">Max rank for per-rank primitive array <c>TypeMap</c> entries. 0 disables.</param>
31+
/// <param name="maxReferenceArrayRank">Max rank for per-rank reference-type (Java peer, String, boxed Nullable) array <c>TypeMap</c> entries. 0 disables.</param>
32+
public void Generate (IReadOnlyList<JavaPeerInfo> peers, Stream stream, string assemblyName, bool useSharedTypemapUniverse = false, int maxArrayRank = 0, int maxReferenceArrayRank = 0)
3233
{
33-
var model = ModelBuilder.Build (peers, assemblyName + ".dll", assemblyName, maxArrayRank);
34+
var model = ModelBuilder.Build (peers, assemblyName + ".dll", assemblyName, maxArrayRank, maxReferenceArrayRank);
3435
var emitter = new TypeMapAssemblyEmitter (_systemRuntimeVersion);
3536
emitter.Emit (model, stream, useSharedTypemapUniverse);
3637
}

src/Microsoft.Android.Sdk.TrimmableTypeMap/TrimmableTypeMapGenerator.cs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ public TrimmableTypeMapResult Execute (
3535
XDocument? manifestTemplate = null,
3636
string? packageNamingPolicy = null,
3737
int maxArrayRank = 0,
38+
int maxReferenceArrayRank = 0,
3839
bool generateTypeMapAssemblies = true)
3940
{
4041
_ = assemblies ?? throw new ArgumentNullException (nameof (assemblies));
@@ -43,6 +44,9 @@ public TrimmableTypeMapResult Execute (
4344
if (maxArrayRank < 0) {
4445
throw new ArgumentOutOfRangeException (nameof (maxArrayRank), maxArrayRank, "Must be >= 0.");
4546
}
47+
if (maxReferenceArrayRank < 0) {
48+
throw new ArgumentOutOfRangeException (nameof (maxReferenceArrayRank), maxReferenceArrayRank, "Must be >= 0.");
49+
}
4650

4751
var (allPeers, assemblyManifestInfo) = ScanAssemblies (assemblies, packageNamingPolicy, frameworkAssemblyNames);
4852
if (allPeers.Count == 0) {
@@ -56,7 +60,7 @@ public TrimmableTypeMapResult Execute (
5660
PropagateCannotRegisterToDescendants (allPeers);
5761

5862
var generatedAssemblies = generateTypeMapAssemblies
59-
? GenerateTypeMapAssemblies (allPeers, systemRuntimeVersion, useSharedTypemapUniverse, maxArrayRank)
63+
? GenerateTypeMapAssemblies (allPeers, systemRuntimeVersion, useSharedTypemapUniverse, maxArrayRank, maxReferenceArrayRank)
6064
: [];
6165
var jcwPeers = allPeers.Where (ShouldGenerateJcw).ToList ();
6266
logger.LogGeneratingJcwFilesInfo (jcwPeers.Count, allPeers.Count);
@@ -174,7 +178,8 @@ List<GeneratedAssembly> GenerateTypeMapAssemblies (
174178
List<JavaPeerInfo> allPeers,
175179
Version systemRuntimeVersion,
176180
bool useSharedTypemapUniverse,
177-
int maxArrayRank)
181+
int maxArrayRank,
182+
int maxReferenceArrayRank)
178183
{
179184
List<(string AssemblyName, List<JavaPeerInfo> Peers)> peersByAssembly;
180185

@@ -203,14 +208,16 @@ List<GeneratedAssembly> GenerateTypeMapAssemblies (
203208
string typeMapAssemblyName = $"_{assemblyName}.TypeMap";
204209
perAssemblyNames.Add (typeMapAssemblyName);
205210
var stream = new MemoryStream ();
206-
generator.Generate (peers, stream, typeMapAssemblyName, useSharedTypemapUniverse, maxArrayRank);
211+
generator.Generate (peers, stream, typeMapAssemblyName, useSharedTypemapUniverse, maxArrayRank, maxReferenceArrayRank);
207212
stream.Position = 0;
208213
generatedAssemblies.Add (new GeneratedAssembly (typeMapAssemblyName, stream));
209214
logger.LogGeneratedTypeMapAssemblyInfo (typeMapAssemblyName, peers.Count);
210215
}
211216
var rootStream = new MemoryStream ();
212217
var rootGenerator = new RootTypeMapAssemblyGenerator (systemRuntimeVersion);
213-
rootGenerator.Generate (perAssemblyNames, useSharedTypemapUniverse, rootStream, maxArrayRank: maxArrayRank);
218+
// The root generator builds the [assembly][rank] anchor matrix, so it needs the overall maximum
219+
// rank across primitive and reference element types.
220+
rootGenerator.Generate (perAssemblyNames, useSharedTypemapUniverse, rootStream, maxArrayRank: Math.Max (maxArrayRank, maxReferenceArrayRank));
214221
rootStream.Position = 0;
215222
generatedAssemblies.Add (new GeneratedAssembly ("_Microsoft.Android.TypeMaps", rootStream));
216223
logger.LogGeneratedRootTypeMapInfo (perAssemblyNames.Count);

src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.Trimmable.CoreCLR.targets

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@
8080
TargetFrameworkVersion="$(TargetFrameworkVersion)"
8181
PackageNamingPolicy="$(_TrimmableTypeMapPackageNamingPolicy)"
8282
MaxArrayRank="$(_AndroidTrimmableTypeMapMaxArrayRank)"
83+
MaxReferenceArrayRank="$(_AndroidTrimmableTypeMapMaxReferenceArrayRank)"
8384
GenerateTypeMapAssemblies="false"
8485
CleanJavaSourceOutputDirectory="true"
8586
AcwMapOutputFile="$(IntermediateOutputPath)acw-map.txt"

src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.Trimmable.targets

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,12 @@
5555
defaults to 0 otherwise, where dynamic code can use Array.CreateInstance directly. -->
5656
<_AndroidTrimmableTypeMapMaxArrayRank Condition=" '$(_AndroidTrimmableTypeMapMaxArrayRank)' == '' and ('$(PublishAot)' == 'true' or '$(DynamicCodeSupport)' == 'false') ">3</_AndroidTrimmableTypeMapMaxArrayRank>
5757
<_AndroidTrimmableTypeMapMaxArrayRank Condition=" '$(_AndroidTrimmableTypeMapMaxArrayRank)' == '' ">0</_AndroidTrimmableTypeMapMaxArrayRank>
58+
<!-- Reference-type array rank (scanned Java peers, System.String, boxed Nullable<T>). Kept at 1
59+
because jagged reference arrays are rare and each nesting level multiplies the generated
60+
JavaObjectArray<T> closure that ILC must compile/preinitialize (the dominant NativeAOT
61+
build-time and binary-size cost). Primitive arrays keep the higher _AndroidTrimmableTypeMapMaxArrayRank. -->
62+
<_AndroidTrimmableTypeMapMaxReferenceArrayRank Condition=" '$(_AndroidTrimmableTypeMapMaxReferenceArrayRank)' == '' and ('$(PublishAot)' == 'true' or '$(DynamicCodeSupport)' == 'false') ">1</_AndroidTrimmableTypeMapMaxReferenceArrayRank>
63+
<_AndroidTrimmableTypeMapMaxReferenceArrayRank Condition=" '$(_AndroidTrimmableTypeMapMaxReferenceArrayRank)' == '' ">0</_AndroidTrimmableTypeMapMaxReferenceArrayRank>
5864
<IncrementalCleanDependsOn>
5965
_RecordTrimmableTypeMapFileWrites;
6066
$(IncrementalCleanDependsOn);
@@ -153,6 +159,7 @@
153159
EmbedAssemblies="$(EmbedAssembliesIntoApk)"
154160
PackageNamingPolicy="$(_TrimmableTypeMapPackageNamingPolicy)"
155161
MaxArrayRank="$(_AndroidTrimmableTypeMapMaxArrayRank)"
162+
MaxReferenceArrayRank="$(_AndroidTrimmableTypeMapMaxReferenceArrayRank)"
156163
ManifestPlaceholders="$(AndroidManifestPlaceholders)"
157164
CheckedBuild="$(_AndroidCheckedBuild)"
158165
ApplicationJavaClass="$(AndroidApplicationJavaClass)"

src/Xamarin.Android.Build.Tasks/Tasks/GenerateTrimmableTypeMap.cs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,11 +104,20 @@ public void LogJniAddNativeMethodRegistrationAttributeError (string managedTypeN
104104

105105
/// <summary>
106106
/// Maximum array rank for which the generator emits per-rank <c>__ArrayMapRank{N}</c>
107-
/// sentinels and <c>TypeMap</c> entries. 0 disables. Set via
108-
/// <c>$(_AndroidTrimmableTypeMapMaxArrayRank)</c>.
107+
/// sentinels and primitive-element <c>TypeMap</c> entries (e.g. <c>int[][][]</c>). 0 disables.
108+
/// Set via <c>$(_AndroidTrimmableTypeMapMaxArrayRank)</c>.
109109
/// </summary>
110110
public int MaxArrayRank { get; set; }
111111

112+
/// <summary>
113+
/// Maximum array rank for reference-type element <c>TypeMap</c> entries — scanned Java peers,
114+
/// <c>System.String</c>, and boxed <c>Nullable&lt;T&gt;</c> (which map to Java reference arrays).
115+
/// Kept lower than <see cref="MaxArrayRank"/> because jagged reference arrays are rare and each
116+
/// nesting level multiplies the generated <c>JavaObjectArray&lt;T&gt;</c> closure. 0 disables. Set
117+
/// via <c>$(_AndroidTrimmableTypeMapMaxReferenceArrayRank)</c>.
118+
/// </summary>
119+
public int MaxReferenceArrayRank { get; set; }
120+
112121
public string? ManifestPlaceholders { get; set; }
113122
public string? CheckedBuild { get; set; }
114123
public string? ApplicationJavaClass { get; set; }
@@ -215,6 +224,7 @@ public override bool RunTask ()
215224
manifestTemplate: manifestTemplate,
216225
packageNamingPolicy: PackageNamingPolicy,
217226
maxArrayRank: MaxArrayRank,
227+
maxReferenceArrayRank: MaxReferenceArrayRank,
218228
generateTypeMapAssemblies: GenerateTypeMapAssemblies);
219229

220230
if (GenerateTypeMapAssemblies) {

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

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,10 @@ static TypeMapAssemblyData BuildModel (IReadOnlyList<JavaPeerInfo> peers, string
1616
return ModelBuilder.Build (peers, outputPath, assemblyName);
1717
}
1818

19-
static TypeMapAssemblyData BuildModelWithArrays (IReadOnlyList<JavaPeerInfo> peers, string? assemblyName = null, int maxArrayRank = 3)
19+
static TypeMapAssemblyData BuildModelWithArrays (IReadOnlyList<JavaPeerInfo> peers, string? assemblyName = null, int maxArrayRank = 3, int? maxReferenceArrayRank = null)
2020
{
2121
var outputPath = Path.Combine (Path.GetTempPath (), (assemblyName ?? "TestTypeMap") + ".dll");
22-
return ModelBuilder.Build (peers, outputPath, assemblyName, maxArrayRank);
22+
return ModelBuilder.Build (peers, outputPath, assemblyName, maxArrayRank, maxReferenceArrayRank ?? maxArrayRank);
2323
}
2424

2525
public class BasicStructure
@@ -1274,6 +1274,45 @@ public void Build_EmitArrayEntries_PrimitiveEntries_SynthesizedForJavaInteropAss
12741274
a.AliasProxyTypeReference == nullableBoolRank1.ProxyTypeReference);
12751275
}
12761276

1277+
[Fact]
1278+
public void Build_ReferenceRankLowerThanPrimitiveRank_PeersGetReferenceRankOnly ()
1279+
{
1280+
// Split ranks: reference types (Java peers) capped at rank 1, primitives at rank 3.
1281+
var peer = MakeMcwPeer ("com/example/Foo", "Com.Example.Foo", "App");
1282+
var model = BuildModelWithArrays (new [] { peer }, maxArrayRank: 3, maxReferenceArrayRank: 1);
1283+
1284+
// The peer (a reference type) gets exactly one array entry, at rank 1.
1285+
var peerEntries = model.Entries.Where (e => e.MapKey == "Com.Example.Foo, App" && e.AnchorRank is not null).ToList ();
1286+
Assert.Single (peerEntries);
1287+
Assert.Equal (1, peerEntries [0].AnchorRank);
1288+
1289+
// The anchor set is still rank 3 (uniform across assemblies for the root matrix).
1290+
Assert.Equal (3, model.MaxArrayRank);
1291+
}
1292+
1293+
[Fact]
1294+
public void Build_ReferenceRankLowerThanPrimitiveRank_PrimitivesRank3ReferencesRank1 ()
1295+
{
1296+
var peer = MakeMcwPeer ("java/lang/Object", "Java.Lang.Object", "Java.Interop");
1297+
var model = BuildModelWithArrays (new [] { peer }, assemblyName: "_Java.Interop.TypeMap", maxArrayRank: 3, maxReferenceArrayRank: 1);
1298+
1299+
// Primitives keep ranks 1..3: 12 primitives × 3.
1300+
var primitiveInt = model.Entries.Where (e => e.MapKey == "System.Int32, System.Runtime" && e.AnchorRank is not null).Select (e => e.AnchorRank).OrderBy (r => r).ToList ();
1301+
Assert.Equal (new int? [] { 1, 2, 3 }, primitiveInt);
1302+
1303+
// System.String (reference) only rank 1.
1304+
var stringRanks = model.Entries.Where (e => e.MapKey == "System.String, System.Runtime" && e.AnchorRank is not null).Select (e => e.AnchorRank).ToList ();
1305+
Assert.Equal (new int? [] { 1 }, stringRanks);
1306+
1307+
// Nullable<bool> (boxed reference) only rank 1.
1308+
var nullableRanks = model.Entries.Where (e => e.MapKey == "System.Nullable`1[[System.Boolean, System.Runtime]], System.Runtime" && e.AnchorRank is not null).Select (e => e.AnchorRank).ToList ();
1309+
Assert.Equal (new int? [] { 1 }, nullableRanks);
1310+
1311+
// Totals: 12 primitives × 3 + (String + 8 nullables) × 1 = 36 + 9 = 45.
1312+
var builtinEntries = model.Entries.Count (e => e.MapKey.StartsWith ("System.", StringComparison.Ordinal) && e.AnchorRank is not null);
1313+
Assert.Equal (45, builtinEntries);
1314+
}
1315+
12771316
[Fact]
12781317
public void Build_EmitArrayEntries_PrimitiveEntries_NotDuplicatedInOtherAssemblies ()
12791318
{
@@ -1397,7 +1436,7 @@ public void FullPipeline_ArrayEntries_AttributeBlobsRoundTrip ()
13971436
{
13981437
var peer = MakeMcwPeer ("foo/Bar", "Foo.Bar", "App");
13991438
var outputPath = Path.Combine (Path.GetTempPath (), "ArrBlobs.dll");
1400-
var model = ModelBuilder.Build (new [] { peer }, outputPath, "ArrBlobs", maxArrayRank: 3);
1439+
var model = ModelBuilder.Build (new [] { peer }, outputPath, "ArrBlobs", maxArrayRank: 3, maxReferenceArrayRank: 3);
14011440

14021441
EmitAndVerify (model, "ArrBlobs", (pe, reader) => {
14031442
var arrayAttrs = ReadAllTypeMapAttributeBlobs (reader)

0 commit comments

Comments
 (0)