Skip to content

Commit c0cc770

Browse files
jpobstjonpryor
authored andcommitted
[generator] Support Kotlin's unsigned types (#539) (#553)
Fixes: #525 Context: dotnet/android#4054 Context: https://github.com/Kotlin/KEEP/blob/13b67668ccc5b4741ecc37d0dd050fd77227c035/proposals/unsigned-types.md Context: https://kotlinlang.org/docs/reference/basic-types.html#unsigned-integers Another place where Kotlin makes use of "name mangling" -- see also commit f3553f4 -- is in the use of unsigned types such as `UInt`. At the JVM ABI level, Kotlin treats unsigned types as their signed counterparts, e.g. `kotlin.UInt` is an `int` and `kotlin.UIntArray` is an `int[]`: // Kotlin public class Example { public fun value(value: UInt) : UInt { return value } public fun array(value: UIntArray) : UIntArray { return value } } // `javap` output: public final class Example { public final int value-WZ4Q5Ns(int); public final int[] array--ajY-9A(int[]); } Kotlin uses Java Annotations to determine whether a parameter or return type is actually an unsigned type instead of a signed type. Update `Xamarin.Android.Tools.Bytecode` and `generator` to bind e.g.: * `kotlin.UInt` as `System.UInt32` * `kotlin.UIntArray` as a `System.UInt32[]` and likewise for the other unsigned types `ushort`, `ulong`, `ubyte`. In order to do this, we pretend that they are native Java types and just translate a few places where we need to tell Java the real type. ~~ Xamarin.Android.Tools.Bytecode / class-parse ~~ When we read the Kotlin metadata in the Java bytecode, if we come across one of these types we store it within an additional `FieldInfo.KotlinType` property that we can access later. When we are generating the XML we check this additional flag and if it's one of our types we emit it instead of the native Java type. For example: <method abstract="false" deprecated="not deprecated" final="false" name="unsignedAbstractMethod-WZ4Q5Ns" native="false" return="uint" jni-return="I" static="false" synchronized="false" visibility="public" bridge="false" synthetic="false" jni-signature="(I)I"> <parameter name="value" type="uint" jni-type="I" /> </method> Here we see that even though `@jni-return` is `I` -- meaning `int` -- the `@return` property is `uint`. Likewise `parameter/@jni-type` and `parameter/@type`. The JNI ABI is `int`, but we bind in C# as `uint`. ~~ ApiXmlAdjuster ~~ Update `JavaTypeReference` to contain unsigned types: UInt = new JavaTypeReference ("uint"); UShort = new JavaTypeReference ("ushort"); ULong = new JavaTypeReference ("ulong"); UByte = new JavaTypeReference ("ubyte"); ~~ generator ~~ `generator` has the 4 new types added to the `SymbolTable` as `SimpleSymbols`: AddType (new SimpleSymbol ("0", "uint", "uint", "I", returnCast: "(uint)")); AddType (new SimpleSymbol ("0", "ushort", "ushort", "S", returnCast: "(ushort)")); AddType (new SimpleSymbol ("0", "ulong", "ulong", "J", returnCast: "(ulong)")); AddType (new SimpleSymbol ("0", "ubyte", "byte", "B", returnCast: "(byte)")); There are 2 fixups we have to make because we use `GetIntValue(...)`, etc. instead of having unsigned versions: * Override name of which method to call, e.g.: `GetIntValue()` instead of `GetUintValue()`. * Cast the `int` value returned to `uint`. This is accomplished via the new `ISymbol.ReturnCast` property. ~~ A Note On API Compatibility ~~ Bindings which use Kotlin Unsigned Types will *only* work on Xamarin.Android 10.2.0 or later ("Visual Studio 16.5"). The problem is that while we *can* emit C# source code which will *compile* against older versions of Xamarin.Android, if they use arrays they will not *run* under older versions of Xamarin.Android. For example, imagine this binding code for the above Kotlin `Example.array()` method: // C# Binding of Example.array() partial class Example { public unsafe uint[] Array(uint[] value) { const string __id = "array--ajY-9A.([I)[I"; IntPtr native_value = JNIEnv.NewArray ((int[]) (object) value); // Works!...ish? JniArgumentValue* __args = stackalloc JniArgumentValue [1]; __args [0] = new JniArgumentValue (native_value); JniObjectReference r = _members.InstanceMethods.InvokeVirtualIntMethod (__id, this, __args); return (uint[]) JNIEnv.GetArray (r.Handle, JniHandleOwnership.DoNotTransfer, typeof (uint)); } } That could conceivably *compile* against older Xamarin.Android versions. However, that cannot *run* against older Xamarin.Android versions, as *eventually* `JNIEnv.GetArray()` will hit some dictionaries to determine how to marshal `IntPtr` to a `uint[]`, at which point things will fail because there is no such mapping *until* Xamarin.Android 10.2.0. We feel that a "hard" ABI requirement will have more "graceful" failure conditions than a solution which doesn't add ABI requirements. In this case, if you create a Kotlin library binding which exposes unsigned types, attempting to build an app in Release configuration against older Xamarin.Android versions will result in a linker error, as the required `JNIEnv` methods will not be resolvable. (cherry picked from commit 71afce5)
1 parent 5009f32 commit c0cc770

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+888
-56
lines changed

src/Java.Interop.Tools.TypeNameMappings/Java.Interop.Tools.TypeNameMappings/JavaNativeTypeManager.cs

+6
Original file line numberDiff line numberDiff line change
@@ -232,10 +232,16 @@ static string GetPrimitiveClass (Type type)
232232
return "F";
233233
if (type == typeof (int))
234234
return "I";
235+
if (type == typeof (uint))
236+
return "I";
235237
if (type == typeof (long))
236238
return "J";
239+
if (type == typeof (ulong))
240+
return "J";
237241
if (type == typeof (short))
238242
return "S";
243+
if (type == typeof (ushort))
244+
return "S";
239245
if (type == typeof (bool))
240246
return "Z";
241247
return null;

src/Java.Interop/Java.Interop/JniArgumentValue.cs

+8
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ public JniArgumentValue (sbyte value)
3434
b = value;
3535
}
3636

37+
public JniArgumentValue (byte value) : this ((sbyte)value) { }
38+
3739
public JniArgumentValue (char value)
3840
{
3941
this = new JniArgumentValue ();
@@ -46,18 +48,24 @@ public JniArgumentValue (short value)
4648
s = value;
4749
}
4850

51+
public JniArgumentValue (ushort value) : this ((short)value) { }
52+
4953
public JniArgumentValue (int value)
5054
{
5155
this = new JniArgumentValue ();
5256
i = value;
5357
}
5458

59+
public JniArgumentValue (uint value) : this ((int)value) { }
60+
5561
public JniArgumentValue (long value)
5662
{
5763
this = new JniArgumentValue ();
5864
j = value;
5965
}
6066

67+
public JniArgumentValue (ulong value) : this ((long) value) { }
68+
6169
public JniArgumentValue (float value)
6270
{
6371
this = new JniArgumentValue ();

src/Xamarin.Android.Tools.ApiXmlAdjuster/JavaTypeReference.cs

+14-2
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,11 @@ public class JavaTypeReference
1616
public static readonly JavaTypeReference Float;
1717
public static readonly JavaTypeReference Double;
1818
public static readonly JavaTypeReference GenericWildcard;
19-
19+
public static readonly JavaTypeReference UInt;
20+
public static readonly JavaTypeReference UShort;
21+
public static readonly JavaTypeReference ULong;
22+
public static readonly JavaTypeReference UByte;
23+
2024
internal static JavaTypeReference GetSpecialType (string name)
2125
{
2226
switch (name) {
@@ -29,6 +33,10 @@ internal static JavaTypeReference GetSpecialType (string name)
2933
case "long": return Long;
3034
case "float": return Float;
3135
case "double": return Double;
36+
case "uint": return UInt;
37+
case "ushort": return UShort;
38+
case "ulong": return ULong;
39+
case "ubyte": return UByte;
3240
case "?": return GenericWildcard;
3341
}
3442
return null;
@@ -46,8 +54,12 @@ static JavaTypeReference ()
4654
Float = new JavaTypeReference ("float");
4755
Double = new JavaTypeReference ("double");
4856
GenericWildcard = new JavaTypeReference ("?");
57+
UInt = new JavaTypeReference ("uint");
58+
UShort = new JavaTypeReference ("ushort");
59+
ULong = new JavaTypeReference ("ulong");
60+
UByte = new JavaTypeReference ("ubyte");
4961
}
50-
62+
5163
JavaTypeReference (string specialName)
5264
{
5365
SpecialName = specialName;

src/Xamarin.Android.Tools.Bytecode/Fields.cs

+1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ public sealed class FieldInfo {
3333
public ConstantPool ConstantPool {get; private set;}
3434
public FieldAccessFlags AccessFlags {get; private set;}
3535
public AttributeCollection Attributes {get; private set;}
36+
public string KotlinType { get; set; }
3637

3738
public FieldInfo (ConstantPool constantPool, Stream stream)
3839
{

src/Xamarin.Android.Tools.Bytecode/Kotlin/KotlinClassMetadata.cs

+9
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.Diagnostics;
34
using System.Linq;
45
using System.Text;
56
using org.jetbrains.kotlin.metadata.jvm;
7+
using ProtoBuf;
68
using Type = org.jetbrains.kotlin.metadata.jvm.Type;
79

810
namespace Xamarin.Android.Tools.Bytecode
911
{
12+
// https://github.com/JetBrains/kotlin/blob/master/core/metadata.jvm/src/jvm_metadata.proto
1013
public class KotlinFile
1114
{
1215
public List<KotlinFunction> Functions { get; set; }
@@ -220,6 +223,8 @@ internal static KotlinExpression FromProtobuf (Expression exp, JvmNameResolver r
220223
public class KotlinFunction : KotlinMethodBase
221224
{
222225
public string Name { get; set; }
226+
public string JvmName { get; set; }
227+
public string JvmSignature { get; set; }
223228
public KotlinFunctionFlags Flags { get; set; }
224229
public KotlinType ReturnType { get; set; }
225230
public int ReturnTypeId { get; set; }
@@ -235,9 +240,13 @@ internal static KotlinFunction FromProtobuf (Function f, JvmNameResolver resolve
235240
if (f is null)
236241
return null;
237242

243+
var sig = Extensible.GetValue<JvmMethodSignature> (f, 100);
244+
238245
return new KotlinFunction {
239246
Flags = (KotlinFunctionFlags)f.Flags,
240247
Name = resolver.GetString (f.Name),
248+
JvmName = resolver.GetString ((sig?.Name ?? 0) > 0 ? sig.Name : f.Name),
249+
JvmSignature = sig is null ? null : resolver.GetString (sig.Desc),
241250
ReturnType = KotlinType.FromProtobuf (f.ReturnType, resolver),
242251
ReturnTypeId = f.ReturnTypeId,
243252
ReceiverType = KotlinType.FromProtobuf (f.ReceiverType, resolver),

src/Xamarin.Android.Tools.Bytecode/Kotlin/KotlinFixups.cs

+71-17
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,15 @@ public static void Fixup (IList<ClassFile> classes)
4545
FixupJavaMethods (c.Methods);
4646

4747
foreach (var met in metadata.Functions)
48-
FixupFunction (FindJavaMethod (class_metadata, met, c), met, class_metadata);
48+
FixupFunction (FindJavaMethod (metadata, met, c), met, class_metadata);
4949

5050
foreach (var prop in metadata.Properties) {
51-
var getter = FindJavaPropertyGetter (class_metadata, prop, c);
52-
var setter = FindJavaPropertySetter (class_metadata, prop, c);
51+
var getter = FindJavaPropertyGetter (metadata, prop, c);
52+
var setter = FindJavaPropertySetter (metadata, prop, c);
5353

5454
FixupProperty (getter, setter, prop);
55+
56+
FixupField (FindJavaFieldProperty (metadata, prop, c), prop);
5557
}
5658

5759
} catch (Exception ex) {
@@ -96,7 +98,6 @@ static void FixupConstructor (MethodInfo method, KotlinConstructor metadata)
9698
Log.Debug ($"Kotlin: Hiding internal constructor {method.DeclaringType?.ThisClass.Name.Value} - {metadata.GetSignature ()}");
9799
method.AccessFlags = MethodAccessFlags.Private;
98100
}
99-
100101
}
101102

102103
static void FixupFunction (MethodInfo method, KotlinFunction metadata, KotlinClass kotlinClass)
@@ -111,18 +112,24 @@ static void FixupFunction (MethodInfo method, KotlinFunction metadata, KotlinCla
111112
return;
112113
}
113114

114-
// Kotlin provides actual parameter names
115115
var java_parameters = method.GetFilteredParameters ();
116116

117117
for (var i = 0; i < java_parameters.Length; i++) {
118118
var java_p = java_parameters [i];
119119
var kotlin_p = metadata.ValueParameters [i];
120120

121+
// Kotlin provides actual parameter names
121122
if (TypesMatch (java_p.Type, kotlin_p.Type, kotlinClass) && java_p.IsUnnamedParameter () && !kotlin_p.IsUnnamedParameter ()) {
122123
Log.Debug ($"Kotlin: Renaming parameter {method.DeclaringType?.ThisClass.Name.Value} - {method.Name} - {java_p.Name} -> {kotlin_p.Name}");
123124
java_p.Name = kotlin_p.Name;
124125
}
126+
127+
// Handle erasure of Kotlin unsigned types
128+
java_p.KotlinType = GetKotlinType (java_p.Type.TypeSignature, kotlin_p.Type.ClassName);
125129
}
130+
131+
// Handle erasure of Kotlin unsigned types
132+
method.KotlinReturnType = GetKotlinType (method.ReturnType.TypeSignature, metadata.ReturnType.ClassName);
126133
}
127134

128135
static void FixupExtensionMethod (MethodInfo method)
@@ -158,16 +165,32 @@ static void FixupProperty (MethodInfo getter, MethodInfo setter, KotlinProperty
158165
return;
159166
}
160167

168+
// Handle erasure of Kotlin unsigned types
169+
if (getter != null)
170+
getter.KotlinReturnType = GetKotlinType (getter.ReturnType.TypeSignature, metadata.ReturnType.ClassName);
171+
161172
if (setter != null) {
162173
var setter_parameter = setter.GetParameters ().First ();
163174

164-
if (setter_parameter.IsUnnamedParameter ()) {
175+
if (setter_parameter.IsUnnamedParameter () || setter_parameter.Name == "<set-?>") {
165176
Log.Debug ($"Kotlin: Renaming setter parameter {setter.DeclaringType?.ThisClass.Name.Value} - {setter.Name} - {setter_parameter.Name} -> value");
166177
setter_parameter.Name = "value";
167178
}
179+
180+
// Handle erasure of Kotlin unsigned types
181+
setter_parameter.KotlinType = GetKotlinType (setter_parameter.Type.TypeSignature, metadata.ReturnType.ClassName);
168182
}
169183
}
170184

185+
static void FixupField (FieldInfo field, KotlinProperty metadata)
186+
{
187+
if (field is null)
188+
return;
189+
190+
// Handle erasure of Kotlin unsigned types
191+
field.KotlinType = GetKotlinType (field.Descriptor, metadata.ReturnType.ClassName);
192+
}
193+
171194
static MethodInfo FindJavaConstructor (KotlinClass kotlinClass, KotlinConstructor constructor, ClassFile klass)
172195
{
173196
var all_constructors = klass.Methods.Where (method => method.Name == "<init>" || method.Name == "<clinit>");
@@ -181,16 +204,16 @@ static MethodInfo FindJavaConstructor (KotlinClass kotlinClass, KotlinConstructo
181204
return null;
182205
}
183206

184-
static MethodInfo FindJavaMethod (KotlinClass kotlinClass, KotlinFunction function, ClassFile klass)
207+
static MethodInfo FindJavaMethod (KotlinFile kotlinFile, KotlinFunction function, ClassFile klass)
185208
{
186-
var possible_methods = klass.Methods.Where (method => method.GetMethodNameWithoutSuffix () == function.Name &&
209+
var possible_methods = klass.Methods.Where (method => method.Name == function.JvmName &&
187210
method.GetFilteredParameters ().Length == function.ValueParameters.Count);
188211

189212
foreach (var method in possible_methods) {
190-
if (!TypesMatch (method.ReturnType, function.ReturnType, kotlinClass))
213+
if (!TypesMatch (method.ReturnType, function.ReturnType, kotlinFile))
191214
continue;
192215

193-
if (!ParametersMatch (kotlinClass, method, function.ValueParameters))
216+
if (!ParametersMatch (kotlinFile, method, function.ValueParameters))
194217
continue;
195218

196219
return method;
@@ -199,7 +222,15 @@ static MethodInfo FindJavaMethod (KotlinClass kotlinClass, KotlinFunction functi
199222
return null;
200223
}
201224

202-
static MethodInfo FindJavaPropertyGetter (KotlinClass kotlinClass, KotlinProperty property, ClassFile klass)
225+
static FieldInfo FindJavaFieldProperty (KotlinFile kotlinClass, KotlinProperty property, ClassFile klass)
226+
{
227+
var possible_methods = klass.Fields.Where (field => field.Name == property.Name &&
228+
TypesMatch (new TypeInfo (field.Descriptor, field.Descriptor), property.ReturnType, kotlinClass));
229+
230+
return possible_methods.FirstOrDefault ();
231+
}
232+
233+
static MethodInfo FindJavaPropertyGetter (KotlinFile kotlinClass, KotlinProperty property, ClassFile klass)
203234
{
204235
var possible_methods = klass.Methods.Where (method => (string.Compare (method.GetMethodNameWithoutSuffix (), $"get{property.Name}", true) == 0 ||
205236
string.Compare (method.GetMethodNameWithoutSuffix (), property.Name, true) == 0) &&
@@ -209,7 +240,7 @@ static MethodInfo FindJavaPropertyGetter (KotlinClass kotlinClass, KotlinPropert
209240
return possible_methods.FirstOrDefault ();
210241
}
211242

212-
static MethodInfo FindJavaPropertySetter (KotlinClass kotlinClass, KotlinProperty property, ClassFile klass)
243+
static MethodInfo FindJavaPropertySetter (KotlinFile kotlinClass, KotlinProperty property, ClassFile klass)
213244
{
214245
var possible_methods = klass.Methods.Where (method => string.Compare (method.GetMethodNameWithoutSuffix (), $"set{property.Name}", true) == 0 &&
215246
property.ReturnType != null &&
@@ -219,7 +250,7 @@ static MethodInfo FindJavaPropertySetter (KotlinClass kotlinClass, KotlinPropert
219250
return possible_methods.FirstOrDefault ();
220251
}
221252

222-
static bool ParametersMatch (KotlinClass kotlinClass, MethodInfo method, List<KotlinValueParameter> kotlinParameters)
253+
static bool ParametersMatch (KotlinFile kotlinClass, MethodInfo method, List<KotlinValueParameter> kotlinParameters)
223254
{
224255
var java_parameters = method.GetFilteredParameters ();
225256

@@ -237,13 +268,13 @@ static bool ParametersMatch (KotlinClass kotlinClass, MethodInfo method, List<Ko
237268
return true;
238269
}
239270

240-
static bool TypesMatch (TypeInfo javaType, KotlinType kotlinType, KotlinClass kotlinClass)
271+
static bool TypesMatch (TypeInfo javaType, KotlinType kotlinType, KotlinFile kotlinFile)
241272
{
242273
// Generic type
243274
if (!string.IsNullOrWhiteSpace (kotlinType.TypeParameterName) && $"T{kotlinType.TypeParameterName};" == javaType.TypeSignature)
244275
return true;
245276

246-
if (javaType.BinaryName == KotlinUtilities.ConvertKotlinTypeSignature (kotlinType, kotlinClass))
277+
if (javaType.BinaryName == KotlinUtilities.ConvertKotlinTypeSignature (kotlinType, kotlinFile))
247278
return true;
248279

249280
// Could be a generic type erasure
@@ -253,17 +284,40 @@ static bool TypesMatch (TypeInfo javaType, KotlinType kotlinType, KotlinClass ko
253284
// Sometimes Kotlin keeps its native types rather than converting them to Java native types
254285
// ie: "Lkotlin/UShort;" instead of "S"
255286
if (javaType.BinaryName.StartsWith ("L", StringComparison.Ordinal) && javaType.BinaryName.EndsWith (";", StringComparison.Ordinal)) {
256-
if (KotlinUtilities.ConvertKotlinClassToJava (javaType.BinaryName.Substring (1, javaType.BinaryName.Length - 2)) == KotlinUtilities.ConvertKotlinTypeSignature (kotlinType, kotlinClass))
287+
if (KotlinUtilities.ConvertKotlinClassToJava (javaType.BinaryName.Substring (1, javaType.BinaryName.Length - 2)) == KotlinUtilities.ConvertKotlinTypeSignature (kotlinType, kotlinFile))
257288
return true;
258289
}
259290

260291
// Same for some arrays
261292
if (javaType.BinaryName.StartsWith ("[L", StringComparison.Ordinal) && javaType.BinaryName.EndsWith (";", StringComparison.Ordinal)) {
262-
if ("[" + KotlinUtilities.ConvertKotlinClassToJava (javaType.BinaryName.Substring (2, javaType.BinaryName.Length - 3)) == KotlinUtilities.ConvertKotlinTypeSignature (kotlinType, kotlinClass))
293+
if ("[" + KotlinUtilities.ConvertKotlinClassToJava (javaType.BinaryName.Substring (2, javaType.BinaryName.Length - 3)) == KotlinUtilities.ConvertKotlinTypeSignature (kotlinType, kotlinFile))
263294
return true;
264295
}
265296

266297
return false;
267298
}
299+
300+
static string GetKotlinType (string jvmType, string kotlinClass)
301+
{
302+
// Handle erasure of Kotlin unsigned types
303+
if (jvmType == "I" && kotlinClass == "kotlin/UInt;")
304+
return "uint";
305+
if (jvmType == "[I" && kotlinClass == "kotlin/UIntArray;")
306+
return "uint[]";
307+
if (jvmType == "S" && kotlinClass == "kotlin/UShort;")
308+
return "ushort";
309+
if (jvmType == "[S" && kotlinClass == "kotlin/UShortArray;")
310+
return "ushort[]";
311+
if (jvmType == "J" && kotlinClass == "kotlin/ULong;")
312+
return "ulong";
313+
if (jvmType == "[J" && kotlinClass == "kotlin/ULongArray;")
314+
return "ulong[]";
315+
if (jvmType == "B" && kotlinClass == "kotlin/UByte;")
316+
return "ubyte";
317+
if (jvmType == "[B" && kotlinClass == "kotlin/UByteArray;")
318+
return "ubyte[]";
319+
320+
return null;
321+
}
268322
}
269323
}

src/Xamarin.Android.Tools.Bytecode/Kotlin/KotlinUtilities.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,15 @@ namespace Xamarin.Android.Tools.Bytecode
88
{
99
public static class KotlinUtilities
1010
{
11-
public static string ConvertKotlinTypeSignature (KotlinType type, KotlinClass klass = null)
11+
public static string ConvertKotlinTypeSignature (KotlinType type, KotlinFile metadata = null)
1212
{
1313
if (type is null)
1414
return string.Empty;
1515

1616
var class_name = type.ClassName;
1717

1818
if (string.IsNullOrWhiteSpace (class_name)) {
19-
if (klass is object) {
19+
if (metadata is KotlinClass klass) {
2020

2121
var tp = klass.TypeParameters.FirstOrDefault (t => t.Id == type.TypeParameter);
2222

src/Xamarin.Android.Tools.Bytecode/Methods.cs

+2
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ public sealed class MethodInfo {
3131
public ClassFile DeclaringType {get; private set;}
3232
public MethodAccessFlags AccessFlags {get; set;}
3333
public AttributeCollection Attributes {get; private set;}
34+
public string KotlinReturnType {get; set;}
3435

3536
public MethodInfo (ConstantPool constantPool, ClassFile declaringType, Stream stream)
3637
{
@@ -290,6 +291,7 @@ public sealed class ParameterInfo : IEquatable<ParameterInfo> {
290291
public string Name;
291292
public int Position;
292293
public TypeInfo Type = new TypeInfo ();
294+
public string KotlinType;
293295

294296
public MethodParameterAccessFlags AccessFlags;
295297

0 commit comments

Comments
 (0)