[objc] Add support for writing to exposed, managed fields (#136)

This commit is contained in:
Sebastien Pouliot 2017-04-18 11:57:21 -04:00 коммит произвёл GitHub
Родитель 7d8d1e955d
Коммит cfcce4be3d
4 изменённых файлов: 184 добавлений и 44 удалений

Просмотреть файл

@ -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)

45
tests/managed/fields.cs Normal file
Просмотреть файл

@ -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:^{