[objc] Add support for writing to exposed, managed fields (#136)
This commit is contained in:
Родитель
7d8d1e955d
Коммит
cfcce4be3d
|
@ -301,51 +301,55 @@ namespace ObjC {
|
|||
implementation.WriteLine ($"\t\tvoid* __args [{pcount}];");
|
||||
for (int i = 0; i < pcount; i++) {
|
||||
var p = parameters [i];
|
||||
|
||||
var pt = p.ParameterType;
|
||||
var is_by_ref = pt.IsByRef;
|
||||
if (is_by_ref)
|
||||
pt = pt.GetElementType ();
|
||||
|
||||
switch (Type.GetTypeCode (pt)) {
|
||||
case TypeCode.String:
|
||||
if (is_by_ref) {
|
||||
implementation.WriteLine ($"\t\tMonoString* __string = *{p.Name} ? mono_string_new (__mono_context.domain, [*{p.Name} UTF8String]) : nil;");
|
||||
implementation.WriteLine ($"\t\t__args [{i}] = &__string;");
|
||||
post.AppendLine ($"\t\t*{p.Name} = mono_embeddinator_get_nsstring (__string);");
|
||||
} else
|
||||
implementation.WriteLine ($"\t\t__args [{i}] = {p.Name} ? mono_string_new (__mono_context.domain, [{p.Name} UTF8String]) : nil;");
|
||||
break;
|
||||
case TypeCode.Boolean:
|
||||
case TypeCode.Char:
|
||||
case TypeCode.SByte:
|
||||
case TypeCode.Int16:
|
||||
case TypeCode.Int32:
|
||||
case TypeCode.Int64:
|
||||
case TypeCode.Byte:
|
||||
case TypeCode.UInt16:
|
||||
case TypeCode.UInt32:
|
||||
case TypeCode.UInt64:
|
||||
case TypeCode.Single:
|
||||
case TypeCode.Double:
|
||||
if (is_by_ref)
|
||||
implementation.WriteLine ($"\t\t__args [{i}] = {p.Name};");
|
||||
else
|
||||
implementation.WriteLine ($"\t\t__args [{i}] = &{p.Name};");
|
||||
break;
|
||||
default:
|
||||
if (pt.IsValueType)
|
||||
implementation.WriteLine ($"\t\t__args [{i}] = mono_object_unbox (mono_gchandle_get_target ({p.Name}->_object->_handle));");
|
||||
else if (types.Contains (pt))
|
||||
implementation.WriteLine ($"\t\t__args [{i}] = mono_gchandle_get_target ({p.Name}->_object->_handle);");
|
||||
else
|
||||
throw new NotImplementedException ($"Converting type {pt.FullName} to mono code");
|
||||
break;
|
||||
}
|
||||
GenerateArgument (p.Name, $"__args[{i}]", p.ParameterType, ref post);
|
||||
}
|
||||
postInvoke = post.ToString ();
|
||||
}
|
||||
|
||||
void GenerateArgument (string paramaterName, string argumentName, Type t, ref StringBuilder post)
|
||||
{
|
||||
var is_by_ref = t.IsByRef;
|
||||
if (is_by_ref)
|
||||
t = t.GetElementType ();
|
||||
|
||||
switch (Type.GetTypeCode (t)) {
|
||||
case TypeCode.String:
|
||||
if (is_by_ref) {
|
||||
implementation.WriteLine ($"\t\tMonoString* __string = *{paramaterName} ? mono_string_new (__mono_context.domain, [*{paramaterName} UTF8String]) : nil;");
|
||||
implementation.WriteLine ($"\t\t{argumentName} = &__string;");
|
||||
post.AppendLine ($"\t\t*{paramaterName} = mono_embeddinator_get_nsstring (__string);");
|
||||
} else
|
||||
implementation.WriteLine ($"\t\t{argumentName} = {paramaterName} ? mono_string_new (__mono_context.domain, [{paramaterName} UTF8String]) : nil;");
|
||||
break;
|
||||
case TypeCode.Boolean:
|
||||
case TypeCode.Char:
|
||||
case TypeCode.SByte:
|
||||
case TypeCode.Int16:
|
||||
case TypeCode.Int32:
|
||||
case TypeCode.Int64:
|
||||
case TypeCode.Byte:
|
||||
case TypeCode.UInt16:
|
||||
case TypeCode.UInt32:
|
||||
case TypeCode.UInt64:
|
||||
case TypeCode.Single:
|
||||
case TypeCode.Double:
|
||||
if (is_by_ref)
|
||||
implementation.WriteLine ($"\t\t{argumentName} = {paramaterName};");
|
||||
else
|
||||
implementation.WriteLine ($"\t\t{argumentName} = &{paramaterName};");
|
||||
break;
|
||||
default:
|
||||
if (t.IsValueType)
|
||||
implementation.WriteLine ($"\t\t{argumentName} = mono_object_unbox (mono_gchandle_get_target ({paramaterName}->_object->_handle));");
|
||||
else
|
||||
if (types.Contains (t))
|
||||
implementation.WriteLine ($"\t\t{argumentName} = {paramaterName} ? mono_gchandle_get_target ({paramaterName}->_object->_handle): nil;");
|
||||
else
|
||||
throw new NotImplementedException ($"Converting type {t.FullName} to mono code");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Generate (PropertyInfo pi)
|
||||
{
|
||||
var getter = pi.GetGetMethod ();
|
||||
|
@ -376,7 +380,7 @@ namespace ObjC {
|
|||
|
||||
protected void Generate (FieldInfo fi)
|
||||
{
|
||||
bool read_only = ((fi.Attributes & FieldAttributes.InitOnly) == FieldAttributes.InitOnly);
|
||||
bool read_only = fi.IsInitOnly || fi.IsLiteral;
|
||||
|
||||
headers.Write ("@property (nonatomic");
|
||||
if (fi.IsStatic)
|
||||
|
@ -384,8 +388,12 @@ namespace ObjC {
|
|||
if (read_only)
|
||||
headers.Write (", readonly");
|
||||
var ft = fi.FieldType;
|
||||
var bound = types.Contains (ft);
|
||||
if (bound && ft.IsValueType)
|
||||
headers.Write (", nonnull");
|
||||
|
||||
var field_type = GetTypeName (ft);
|
||||
if (types.Contains (ft))
|
||||
if (bound)
|
||||
field_type += " *";
|
||||
|
||||
var name = CamelCase (fi.Name);
|
||||
|
@ -416,13 +424,41 @@ namespace ObjC {
|
|||
instance = "__instance";
|
||||
}
|
||||
implementation.WriteLine ($"\tMonoObject* __result = mono_field_get_value_object (__mono_context.domain, __field, {instance});");
|
||||
if (types.Contains (ft)) {
|
||||
implementation.WriteLine ("\tif (!__result)");
|
||||
implementation.WriteLine ("\t\treturn nil;");
|
||||
}
|
||||
ReturnValue (fi.FieldType);
|
||||
implementation.WriteLine ("}");
|
||||
implementation.WriteLine ();
|
||||
|
||||
if (read_only)
|
||||
return;
|
||||
// TODO write support
|
||||
implementation.Write (fi.IsStatic ? '+' : '-');
|
||||
implementation.WriteLine ($" (void) set{fi.Name}:({field_type})value");
|
||||
implementation.WriteLine ("{");
|
||||
implementation.WriteLine ("\tstatic MonoClassField* __field = nil;");
|
||||
implementation.WriteLine ("\tif (!__field) {");
|
||||
implementation.WriteLine ("#if TOKENLOOKUP");
|
||||
aname = type.Assembly.GetName ().Name;
|
||||
implementation.WriteLine ($"\t\t__field = mono_class_get_field ({managed_type_name}_class, 0x{fi.MetadataToken:X8});");
|
||||
implementation.WriteLine ("#else");
|
||||
implementation.WriteLine ($"\t\tconst char __field_name [] = \"{fi.Name}\";");
|
||||
implementation.WriteLine ($"\t\t__field = mono_class_get_field_from_name ({managed_type_name}_class, __field_name);");
|
||||
implementation.WriteLine ("#endif");
|
||||
implementation.WriteLine ("\t}");
|
||||
StringBuilder sb = null;
|
||||
implementation.WriteLine ($"\t\tvoid* __value;");
|
||||
GenerateArgument ("value", "__value", fi.FieldType, ref sb);
|
||||
if (fi.IsStatic) {
|
||||
implementation.WriteLine ($"\tMonoVTable *__vtable = mono_class_vtable (__mono_context.domain, {managed_type_name}_class);");
|
||||
implementation.WriteLine ("\tmono_field_static_set_value (__vtable, __field, __value);");
|
||||
} else {
|
||||
implementation.WriteLine ($"\tMonoObject* __instance = mono_gchandle_get_target (_object->_handle);");
|
||||
implementation.WriteLine ("\tmono_field_set_value (__instance, __field, __value);");
|
||||
}
|
||||
implementation.WriteLine ("}");
|
||||
implementation.WriteLine ();
|
||||
}
|
||||
|
||||
public string GetReturnType (Type declaringType, Type returnType)
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
using System;
|
||||
|
||||
namespace Fields {
|
||||
|
||||
public class Class {
|
||||
|
||||
// read only
|
||||
public const long MaxLong = Int64.MaxValue;
|
||||
|
||||
public static Class Scratch = new Class (true);
|
||||
|
||||
// read/write
|
||||
public static int Integer;
|
||||
|
||||
public bool Boolean;
|
||||
|
||||
public Struct Structure;
|
||||
|
||||
public Class (bool enabled)
|
||||
{
|
||||
Boolean = enabled;
|
||||
}
|
||||
}
|
||||
|
||||
public struct Struct {
|
||||
|
||||
// read only
|
||||
public static readonly Struct Empty;
|
||||
|
||||
// read/write
|
||||
public static Struct Scratch = new Struct ();
|
||||
|
||||
public static int Integer;
|
||||
|
||||
public bool Boolean;
|
||||
|
||||
public Class Class;
|
||||
|
||||
public Struct (bool enabled)
|
||||
{
|
||||
Boolean = enabled;
|
||||
Class = new Class (false);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -38,6 +38,7 @@
|
|||
<Compile Include="methods.cs" />
|
||||
<Compile Include="structs.cs" />
|
||||
<Compile Include="enums.cs" />
|
||||
<Compile Include="fields.cs" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
|
@ -194,6 +194,64 @@
|
|||
XCTAssert (s == Enums_ShortEnumMin, "out enum 2");
|
||||
}
|
||||
|
||||
- (void) testFieldsInReference {
|
||||
XCTAssert ([Fields_Class maxLong] == LONG_MAX, "class const");
|
||||
|
||||
XCTAssert (Fields_Class.integer == 0, "static field unset");
|
||||
Fields_Class.integer = 1;
|
||||
XCTAssert (Fields_Class.integer == 1, "static field set");
|
||||
|
||||
XCTAssertTrue (Fields_Class.scratch.boolean, "scratch default");
|
||||
|
||||
Fields_Class.scratch = [[Fields_Class alloc] initWithEnabled:false];
|
||||
XCTAssertFalse (Fields_Class.scratch.boolean, "scratch re-assign");
|
||||
|
||||
Fields_Class *ref1 = [[Fields_Class alloc] initWithEnabled:true];
|
||||
XCTAssertTrue (ref1.boolean, "init / boolean / true");
|
||||
ref1.boolean = false;
|
||||
XCTAssertFalse (ref1.boolean, "init / boolean / set 1");
|
||||
|
||||
XCTAssertNotNil (ref1.structure, "init / class initialized 1");
|
||||
XCTAssertFalse (ref1.structure.boolean, "init / class / boolean / default");
|
||||
ref1.structure = [[Fields_Struct alloc] initWithEnabled:true];
|
||||
XCTAssertTrue (ref1.structure.boolean, "init / class / boolean / true");
|
||||
|
||||
Fields_Class *ref2 = [[Fields_Class alloc] initWithEnabled:false];
|
||||
XCTAssertNotNil ([ref2 structure], "init / class initialized 2");
|
||||
XCTAssertFalse ([ref2 boolean], "init / boolean / false");
|
||||
}
|
||||
|
||||
- (void) testFieldsInValueType {
|
||||
XCTAssert (Fields_Struct.integer == 0, "static valuetype field unset");
|
||||
Fields_Struct.integer = 1;
|
||||
XCTAssert (Fields_Struct.integer == 1, "static valuetype field set");
|
||||
|
||||
XCTAssertFalse (Fields_Struct.scratch.boolean, "scratch default");
|
||||
|
||||
Fields_Struct.scratch = [[Fields_Struct alloc] initWithEnabled:true];
|
||||
XCTAssertTrue (Fields_Struct.scratch.boolean, "scratch re-assign");
|
||||
|
||||
Fields_Struct *empty = [Fields_Struct empty];
|
||||
XCTAssertNotNil (empty, "empty / struct static readonly");
|
||||
XCTAssertNil ([empty class], "empty / class uninitialized");
|
||||
|
||||
Fields_Struct *struct1 = [[Fields_Struct alloc] initWithEnabled:true];
|
||||
XCTAssertTrue (struct1.boolean, "init / boolean / true");
|
||||
struct1.boolean = false;
|
||||
XCTAssertFalse (struct1.boolean, "init / boolean / set 1");
|
||||
|
||||
XCTAssertNotNil (struct1.class, "init / class initialized 1");
|
||||
XCTAssertFalse (struct1.class.boolean, "init / class / boolean / default");
|
||||
struct1.class = nil;
|
||||
XCTAssertNil (struct1.class, "init / class set 1");
|
||||
struct1.class = [[Fields_Class alloc] initWithEnabled:true];
|
||||
XCTAssertTrue (struct1.class.boolean, "init / class / boolean / true");
|
||||
|
||||
Fields_Struct *struct2 = [[Fields_Struct alloc] initWithEnabled:false];
|
||||
XCTAssertNotNil ([struct2 class], "init / class initialized 2");
|
||||
XCTAssertFalse ([struct2 boolean], "init / boolean / false");
|
||||
}
|
||||
|
||||
- (void)testStaticCallPerformance {
|
||||
const int iterations = 1000000;
|
||||
[self measureBlock:^{
|
||||
|
|
Загрузка…
Ссылка в новой задаче