Merge branch 'objc' of github.com:mono/embeddinator-4000

This commit is contained in:
Joao Matos 2017-04-17 15:56:33 +01:00
Родитель 801e852c0c 27e5f34941
Коммит bbdb54218c
27 изменённых файлов: 1592 добавлений и 353 удалений

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

@ -11,3 +11,46 @@ The use of Automatic Reference Counting (ARC) is **required** to call the genera
### NSString support
API that expose `System.String` types are converted into `NSString`. This makes memory management easier than dealing with `char*`.
## Main differences with .NET
### Constructors v.s. Initializers
In Objective-C, you can call any of the initializer prototypes of any of the parent classes in the inheritance chain unless it is marked as unavailable (NS_UNAVAILABLE).
In C# you must explicitly declare a constructor member inside a class, this means constructors are not inherited.
In order to expose the right representation of the C# API to Objective-C, we add `NS_UNAVAILABLE` to any initializer that is not present in the child class from the parent class.
C# API:
```csharp
public class Unique {
public Unique () : this (1)
{
}
public Unique (int id)
{
}
}
public class SuperUnique : Unique {
public SuperUnique () : base (911)
{
}
}
```
Objective-C surfaced API:
```objectivec
@interface SuperUnique : Unique
- (instancetype)initWithId:(int)id NS_UNAVAILABLE;
- (instancetype)init;
@end
```
Here we can see that `initWithId:` has been marked as unavailable.

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

@ -1,4 +1,4 @@
id:{932C3F0C-D968-42D1-BB14-D97C73361983}
id:{932C3F0C-D968-42D1-BB14-D97C73361983}
title:Embeddinator-4000 errors
[//]: # (The original file resides under https://github.com/mono/Embeddinator-4000/tree/master/docs/error.md)
@ -40,21 +40,103 @@ The tool does not support the target `X`. It is possible that another version of
The tool does not support the compilation target `X`. It is possible that another version of the tool supports it or that it does not apply in this environment.
<h3><a name="EM0006"/>EM0006: Could not find the Xcode location.</h3>
The tool could not find the currently selected Xcode location using the `xcode-select -p` command. Please verify that this command succeeds, and returns the correct Xcode location.
<h3><a name="EM0008"/>EM0008: The architecture '{arch}' is not valid for {platform}. Valid architectures for {platform} are: '{architectures}'.</h3>
The architecture in the error message is not valid for the targeted platform. Please verify that the --abi option is passed a valid architecture.
<h3><a name="EM0009"/>EM0009: The feature `X` is not currently implemented by the generator</h3>
This is a known issue that we intend to fix in a future release of the generator. Contributions are welcome.
<h3><a name="EM0099"/>EM0099: Internal error *. Please file a bug report with a test case (https://github.com/mono/Embeddinator-4000/issues).</h3>
This error message is reported when an internal consistency check in the Embeddinator-4000 fails.
This indicates a bug in the Embeddinator-4000; please file a bug report at [https://github.com/mono/Embeddinator-4000/issues](https://github.com/mono/Embeddinator-4000/issues) with a test case.
# EM1xxx: code generation
<h3><a name="EM1000"/>EM1000: The feature `X` is not currently implemented by the generator</h3>
<!-- 1xxx: code processing -->
This is a known issue that we intend to fix in a future release of the generator. Contributions are welcome.
# EM1xxx: Code Processing
<h3><a name="EM1010"/>Type `T` is not generated because `X` are not supported.</h3>
This is a **warning** that the type `T` will be ignored (i.e. nothing will be generated) because it uses `X`, a feature that is not supported.
Note: Supported features will evolve with new versions of the tool.
<h3><a name="EM1011"/>Type `T` is not generated because it lacks a native counterpart.</h3>
This is a **warning** that the type `T` will be ignored (i.e. nothing will be generated) because it uses it expose something from the .NET framework that has no counterpart in the native platform.
<h3><a name="EM1011"/>Type `T` is not generated because it lacks marshaling code with a native counterpart.</h3>
This is a **warning** that the type `T` will be ignored (i.e. nothing will be generated) because it uses it expose something from the .NET framework that requires extra marshaling.
Note: This is something that is might get supported, with some limitations, in a future version of the tool.
<h3><a name="EM1020"/>Constructor `C` is not generated because of parameter type `T` is not supported.</h3>
This is a **warning** that the constructor `C` will be ignored (i.e. nothing will be generated) because a parameter of type `T` is not supported.
There should be an earlier warning giving more information why type `T` is not supported.
Note: Supported features will evolve with new versions of the tool.
<h3><a name="EM1021"/>Constructor `C` parameter `P` has a default value that is not supported.</h3>
This is a **warning** that the default parameters of constructor `C` are not generating any extra code.
Note: Supported features will evolve with new versions of the tool.
<h3><a name="EM1030"/>Method `M` is not generated because return type `T` is not supported.</h3>
This is a **warning** that the method `M` will be ignored (i.e. nothing will be generated) because it's return type `T` is not supported.
There should be an earlier warning giving more information why type `T` is not supported.
Note: Supported features will evolve with new versions of the tool.
<h3><a name="EM1031"/>Method `M` is not generated because of parameter type `T` is not supported.</h3>
This is a **warning** that the method `M` will be ignored (i.e. nothing will be generated) because a parameter of type `T` is not supported.
There should be an earlier warning giving more information why type `T` is not supported.
Note: Supported features will evolve with new versions of the tool.
<h3><a name="EM1032"/>Method `M` parameter `P` has a default value that is not supported.</h3>
This is a **warning** that the default parameters of method `M` are not generating any extra code.
Note: Supported features will evolve with new versions of the tool.
<h3><a name="EM1040"/>Property `P` is not generated because of parameter type `T` is not supported.</h3>
This is a **warning** that the property `P` will be ignored (i.e. nothing will be generated) because the exposed type `T` is not supported.
There should be an earlier warning giving more information why type `T` is not supported.
Note: Supported features will evolve with new versions of the tool.
<!-- 2xxx: code generation -->
# EM2xxx: Code Generation
<!-- 1xxx: code generation -->
<!-- 2xxx: reserved -->
<!-- 3xxx: reserved -->
<!-- 4xxx: reserved -->
<!-- 5xxx: reserved -->

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

@ -6,4 +6,4 @@ OBJCGEN_FILES := \
bin/Debug/objcgen.exe: $(OBJCGEN_FILES)
nuget restore ../generator.sln
xbuild objcgen.csproj
msbuild objcgen.csproj

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

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.IO;
using IKVM.Reflection;
using Type = IKVM.Reflection.Type;
@ -42,6 +43,7 @@ namespace Embeddinator {
public static class Driver
{
public static Embedder CurrentEmbedder { get; private set; }
static int Main (string [] args)
{
try {
@ -52,23 +54,31 @@ namespace Embeddinator {
}
}
public static int Main2 (string [] args)
public static int Main2 (params string [] args)
{
var action = Action.None;
var embedder = new Embedder ();
CurrentEmbedder = embedder;
var os = new OptionSet {
{ "c|compile", "Compiles the generated output", v => CompileCode = true },
{ "d|debug", "Build the native library with debug information.", v => Debug = true },
{ "gen=", $"Target generator (default {TargetLanguage})", v => SetTarget (v) },
{ "o|out|outdir=", "Output directory", v => OutputDirectory = v },
{ "p|platform=", $"Target platform (iOS, macOS [default], watchOS, tvOS)", v => SetPlatform (v) },
{ "dll|shared", "Compiles as a shared library (default)", v => CompilationTarget = CompilationTarget.SharedLibrary },
{ "static", "Compiles as a static library (unsupported)", v => CompilationTarget = CompilationTarget.StaticLibrary },
{ "c|compile", "Compiles the generated output", v => embedder.CompileCode = true },
{ "d|debug", "Build the native library with debug information.", v => embedder.Debug = true },
{ "gen=", $"Target generator (default {embedder.TargetLanguage})", v => embedder.SetTarget (v) },
{ "abi=", "A comma-separated list of ABIs to compile. If not specified, all ABIs applicable to the selected platform will be built. Valid values (also depends on platform): i386, x86_64, armv7, armv7s, armv7k, arm64.", (v) =>
{
embedder.ABIs.AddRange (v.Split (',').Select ((a) => a.ToLowerInvariant ()));
}
},
{ "o|out|outdir=", "Output directory", v => embedder.OutputDirectory = v },
{ "p|platform=", $"Target platform (iOS, macOS [default], watchOS, tvOS)", v => embedder.SetPlatform (v) },
{ "dll|shared", "Compiles as a shared library (default)", v => embedder.CompilationTarget = CompilationTarget.SharedLibrary },
{ "static", "Compiles as a static library (unsupported)", v => embedder.CompilationTarget = CompilationTarget.StaticLibrary },
{ "vs=", $"Visual Studio version for compilation (unsupported)", v => { throw new EmbeddinatorException (2, $"Option `--vs` is not supported"); } },
{ "h|?|help", "Displays the help", v => action = Action.Help },
{ "v|verbose", "generates diagnostic verbose output", v => ErrorHelper.Verbosity++ },
{ "version", "Display the version information.", v => action = Action.Version },
{ "target=", "The compilation target (static, shared, framework).", v => SetCompilationTarget (v) },
{ "target=", "The compilation target (static, shared, framework).", v => embedder.SetCompilationTarget (v) },
};
var assemblies = os.Parse (args);
@ -91,22 +101,25 @@ namespace Embeddinator {
return 0;
case Action.Generate:
try {
var result = Generate (assemblies);
if (CompileCode && (result == 0))
result = Compile ();
var result = embedder.Generate (assemblies);
if (embedder.CompileCode && (result == 0))
result = embedder.Compile ();
Console.WriteLine ("Done");
return result;
} catch (NotImplementedException e) {
throw new EmbeddinatorException (1000, $"The feature `{e.Message}` is not currently supported by the tool");
throw new EmbeddinatorException (9, $"The feature `{e.Message}` is not currently supported by the tool");
}
default:
throw ErrorHelper.CreateError (99, "Internal error: invalid action {0}. Please file a bug report with a test case (https://github.com/mono/Embeddinator-4000/issues)", action);
}
}
}
static string outputDirectory = ".";
public class Embedder {
public static string OutputDirectory {
string outputDirectory = ".";
public string OutputDirectory {
get { return outputDirectory; }
set {
if (!Directory.Exists (value)) {
@ -121,15 +134,17 @@ namespace Embeddinator {
}
}
static bool CompileCode { get; set; }
public bool CompileCode { get; set; }
static bool Debug { get; set; }
public bool Debug { get; set; }
static bool Shared { get { return CompilationTarget == CompilationTarget.SharedLibrary; } }
public bool Shared { get { return CompilationTarget == CompilationTarget.SharedLibrary; } }
static string LibraryName { get; set; }
public string LibraryName { get; set; }
public static void SetPlatform (string platform)
public List<string> ABIs { get; private set; } = new List<string> ();
public void SetPlatform (string platform)
{
switch (platform.ToLowerInvariant ()) {
case "osx":
@ -152,7 +167,7 @@ namespace Embeddinator {
}
}
public static void SetTarget (string value)
public void SetTarget (string value)
{
switch (value.ToLowerInvariant ()) {
case "objc":
@ -166,7 +181,7 @@ namespace Embeddinator {
}
}
public static void SetCompilationTarget (string value)
public void SetCompilationTarget (string value)
{
switch (value.ToLowerInvariant ()) {
case "library":
@ -186,11 +201,11 @@ namespace Embeddinator {
}
}
public static Platform Platform { get; set; } = Platform.macOS;
public static TargetLanguage TargetLanguage { get; private set; } = TargetLanguage.ObjectiveC;
public static CompilationTarget CompilationTarget { get; set; } = CompilationTarget.SharedLibrary;
public Platform Platform { get; set; } = Platform.macOS;
public TargetLanguage TargetLanguage { get; private set; } = TargetLanguage.ObjectiveC;
public CompilationTarget CompilationTarget { get; set; } = CompilationTarget.SharedLibrary;
static int Generate (List<string> args)
public int Generate (List<string> args)
{
Console.WriteLine ("Parsing assemblies...");
@ -228,59 +243,316 @@ namespace Embeddinator {
return 0;
}
static int Compile ()
static string xcode_app;
static string XcodeApp {
get {
if (string.IsNullOrEmpty (xcode_app)) {
using (var process = new Process ()) {
process.StartInfo.FileName = "xcode-select";
process.StartInfo.Arguments = "-p";
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
process.Start ();
var output = process.StandardOutput.ReadToEnd ();
if (process.ExitCode != 0)
throw ErrorHelper.CreateError (6, "Could not find the Xcode location.");
xcode_app = Path.GetDirectoryName (Path.GetDirectoryName (output.Trim ()));
}
}
return xcode_app;
}
}
class BuildInfo
{
public string Sdk;
public string [] Architectures;
public string SdkName; // used in -m{SdkName}-version-min
public string MinVersion;
public string XamariniOSSDK;
public string CompilerFlags;
public string LinkerFlags;
}
public int Compile ()
{
Console.WriteLine ("Compiling binding code...");
BuildInfo [] build_infos;
switch (Platform) {
case Platform.macOS:
build_infos = new BuildInfo [] {
new BuildInfo { Sdk = "MacOSX", Architectures = new string [] { "i386", "x86_64" }, SdkName = "macosx", MinVersion = "10.7" },
};
break;
case Platform.iOS:
case Platform.watchOS:
build_infos = new BuildInfo [] {
new BuildInfo { Sdk = "iPhoneOS", Architectures = new string [] { "armv7", "armv7s", "arm64" }, SdkName = "iphoneos", MinVersion = "8.0", XamariniOSSDK = "MonoTouch.iphoneos.sdk" },
new BuildInfo { Sdk = "iPhoneSimulator", Architectures = new string [] { "i386", "x86_64" }, SdkName = "ios-simulator", MinVersion = "8.0", XamariniOSSDK = "MonoTouch.iphonesimulator.sdk" },
};
break;
case Platform.tvOS:
throw new NotImplementedException ($"platform={Platform}");
build_infos = new BuildInfo [] {
new BuildInfo { Sdk = "AppleTVOS", Architectures = new string [] { "arm64" }, SdkName = "tvos", MinVersion = "9.0", XamariniOSSDK = "Xamarin.AppleTVOS.sdk", CompilerFlags = "-fembed-bitcode", LinkerFlags = "-fembed-bitcode" },
new BuildInfo { Sdk = "AppleTVSimulator", Architectures = new string [] { "x86_64" }, SdkName = "tvos-simulator", MinVersion = "9.0", XamariniOSSDK = "Xamarin.AppleTVSimulator.sdk" },
};
break;
case Platform.watchOS:
build_infos = new BuildInfo [] {
new BuildInfo { Sdk = "WatchOS", Architectures = new string [] { "armv7k" }, SdkName = "watchos", MinVersion = "2.0", XamariniOSSDK = "Xamarin.WatchOS.sdk", CompilerFlags = "-fembed-bitcode", LinkerFlags = "-fembed-bitcode" },
new BuildInfo { Sdk = "WatchSimulator", Architectures = new string [] { "i386" }, SdkName = "watchos-simulator", MinVersion = "2.0", XamariniOSSDK = "Xamarin.WatchSimulator.sdk" },
};
break;
default:
throw ErrorHelper.CreateError (99, "Internal error: invalid platform {0}. Please file a bug report with a test case (https://github.com/mono/Embeddinator-4000/issues).", Platform);
}
// filter/validate ABIs
if (ABIs.Count > 0) {
// Validate
var all_architectures = build_infos.SelectMany ((v) => v.Architectures);
var invalid_architectures = ABIs.Except (all_architectures).ToArray ();
if (invalid_architectures.Length > 0) {
var arch = invalid_architectures [0];
throw ErrorHelper.CreateError (8, $"The architecture '{arch}' is not valid for {Platform}. Valid architectures for {Platform} are: {string.Join (", ", all_architectures)}");
}
// Filter
foreach (var info in build_infos)
info.Architectures = info.Architectures.Where ((v) => ABIs.Contains (v)).ToArray ();
}
switch (CompilationTarget) {
case CompilationTarget.SharedLibrary:
case CompilationTarget.StaticLibrary:
break;
case CompilationTarget.Framework:
case CompilationTarget.StaticLibrary:
throw new NotImplementedException ($"Compilation target: {CompilationTarget}");
default:
throw ErrorHelper.CreateError (99, "Internal error: invalid compilation target {0}. Please file a bug report with a test case (https://github.com/mono/Embeddinator-4000/issues).", CompilationTarget);
}
StringBuilder options = new StringBuilder ("clang ");
if (Debug)
options.Append ("-g -O0 ");
options.Append ("-fobjc-arc ");
options.Append ("-DMONO_EMBEDDINATOR_DLL_EXPORT ");
options.Append ("-framework CoreFoundation ");
options.Append ("-framework Foundation ");
options.Append ("-I\"/Library/Frameworks/Mono.framework/Versions/Current/include/mono-2.0\" -L\"/Library/Frameworks/Mono.framework/Versions/Current/lib/\" -lmonosgen-2.0 ");
options.Append ("glib.c mono_embeddinator.c objc-support.m bindings.m ");
options.Append ("-ObjC -lobjc ");
var lipo_files = new List<string> ();
var output_file = string.Empty;
var files = new string [] {
Path.Combine (OutputDirectory, "glib.c"),
Path.Combine (OutputDirectory, "mono_embeddinator.c"),
Path.Combine (OutputDirectory, "objc-support.m"),
Path.Combine (OutputDirectory, "bindings.m"),
};
switch (CompilationTarget) {
case CompilationTarget.SharedLibrary:
options.Append ($"-dynamiclib ");
options.Append ($"-install_name @rpath/lib{LibraryName}.dylib ");
options.Append ($"-o lib{LibraryName}.dylib ");
output_file = $"lib{LibraryName}.dylib";
break;
case CompilationTarget.StaticLibrary:
throw new NotImplementedException ("compile to static library");
case CompilationTarget.Framework:
throw new NotImplementedException ("compile to framework");
output_file = $"{LibraryName}.a";
break;
default:
throw ErrorHelper.CreateError (99, "Internal error: invalid compilation target {0}. Please file a bug report with a test case (https://github.com/mono/Embeddinator-4000/issues).", CompilationTarget);
}
Console.WriteLine ($"\tInvoking: xcrun {options}");
var p = Process.Start ("xcrun", options.ToString ());
p.WaitForExit ();
return p.ExitCode;
int exitCode;
foreach (var build_info in build_infos) {
foreach (var arch in build_info.Architectures) {
var archOutputDirectory = Path.Combine (OutputDirectory, arch);
Directory.CreateDirectory (archOutputDirectory);
var common_options = new StringBuilder ("clang ");
if (Debug)
common_options.Append ("-g -O0 ");
else
common_options.Append ("-O2 -DTOKENLOOKUP ");
common_options.Append ("-fobjc-arc ");
common_options.Append ("-ObjC ");
common_options.Append ("-Wall ");
common_options.Append ($"-arch {arch} ");
common_options.Append ($"-isysroot {XcodeApp}/Contents/Developer/Platforms/{build_info.Sdk}.platform/Developer/SDKs/{build_info.Sdk}.sdk ");
common_options.Append ($"-m{build_info.SdkName}-version-min={build_info.MinVersion} ");
common_options.Append ("-I/Library/Frameworks/Mono.framework/Versions/Current/include/mono-2.0 ");
// Build each file to a .o
var object_files = new List<string> ();
foreach (var file in files) {
var compiler_options = new StringBuilder (common_options.ToString ());
compiler_options.Append ("-DMONO_EMBEDDINATOR_DLL_EXPORT ");
compiler_options.Append (build_info.CompilerFlags).Append (" ");
compiler_options.Append ("-c ");
compiler_options.Append (Quote (file)).Append (" ");
var objfile = Path.Combine (archOutputDirectory, Path.ChangeExtension (Path.GetFileName (file), "o"));
compiler_options.Append ($"-o {Quote (objfile)} ");
object_files.Add (objfile);
if (!Xcrun (compiler_options, out exitCode))
return exitCode;
}
switch (CompilationTarget) {
case CompilationTarget.SharedLibrary:
// Link all the .o files into a .dylib
var options = new StringBuilder (common_options.ToString ());
options.Append ($"-dynamiclib ");
options.Append (build_info.LinkerFlags).Append (" ");
options.Append ("-lobjc ");
options.Append ("-framework CoreFoundation ");
options.Append ("-framework Foundation ");
options.Append ($"-install_name {Quote ("@rpath/" + output_file)} ");
foreach (var objfile in object_files)
options.Append (Quote (objfile)).Append (" ");
var dynamic_ofile = Path.Combine (archOutputDirectory, output_file);
options.Append ($"-o ").Append (Quote (dynamic_ofile)).Append (" ");
lipo_files.Add (dynamic_ofile);
if (!string.IsNullOrEmpty (build_info.XamariniOSSDK)) {
options.Append ($"-L/Library/Frameworks/Xamarin.iOS.framework/Versions/Current/SDKs/{build_info.XamariniOSSDK}/usr/lib ");
} else {
options.Append ("-L/Library/Frameworks/Mono.framework/Versions/Current/lib/ ");
}
options.Append ("-lmonosgen-2.0 ");
if (!Xcrun (options, out exitCode))
return exitCode;
break;
case CompilationTarget.StaticLibrary:
// Archive all the .o files into a .a
var archive_options = new StringBuilder ("ar cru ");
var static_ofile = Path.Combine (archOutputDirectory, output_file);
archive_options.Append (static_ofile).Append (" ");
lipo_files.Add (static_ofile);
foreach (var objfile in object_files)
archive_options.Append (objfile).Append (" ");
if (!Xcrun (archive_options, out exitCode))
return exitCode;
break;
case CompilationTarget.Framework:
throw new NotImplementedException ("compile to framework");
default:
throw ErrorHelper.CreateError (99, "Internal error: invalid compilation target {0}. Please file a bug report with a test case (https://github.com/mono/Embeddinator-4000/issues).", CompilationTarget);
}
}
}
var output_path = Path.Combine (OutputDirectory, output_file);
if (!Lipo (lipo_files, output_path, out exitCode))
return exitCode;
if (!DSymUtil (output_path, out exitCode))
return exitCode;
return 0;
}
string GetTargetFramework ()
{
switch (Platform) {
case Platform.macOS:
throw new NotImplementedException ("target framework for macOS");
case Platform.iOS:
return "Xamarin.iOS,v1.0";
case Platform.tvOS:
return "Xamarin.TVOS,v1.0";
case Platform.watchOS:
return "Xamarin.WatchOS,v1.0";
default:
throw ErrorHelper.CreateError (99, "Internal error: invalid platform {0}. Please file a bug report with a test case (https://github.com/mono/Embeddinator-4000/issues).", Platform);
}
}
public static bool RunProcess (string filename, string arguments, out int exitCode, out string stdout)
{
Console.WriteLine($"\t{filename} {arguments}");
using (var p = new Process ()) {
p.StartInfo.FileName = filename;
p.StartInfo.Arguments = arguments;
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.Start();
stdout = p.StandardOutput.ReadToEnd();
exitCode = p.ExitCode;
return exitCode == 0;
}
}
public static int RunProcess (string filename, string arguments)
{
Console.WriteLine ($"\t{filename} {arguments}");
using (var p = Process.Start (filename, arguments)) {
p.WaitForExit ();
return p.ExitCode;
}
}
public static bool RunProcess (string filename, string arguments, out int exitCode)
{
exitCode = RunProcess (filename, arguments);
return exitCode == 0;
}
public static bool Xcrun (StringBuilder options, out int exitCode)
{
return RunProcess ("xcrun", options.ToString (), out exitCode);
}
bool DSymUtil (string input, out int exitCode)
{
exitCode = 0;
if (!Debug)
return true;
string output;
switch (CompilationTarget) {
case CompilationTarget.StaticLibrary:
return true;
case CompilationTarget.SharedLibrary:
output = input + ".dSYM";
break;
default:
throw ErrorHelper.CreateError (99, "Internal error: invalid compilation target {0}. Please file a bug report with a test case (https://github.com/mono/Embeddinator-4000/issues).", CompilationTarget);
}
var dsymutil_options = new StringBuilder ("dsymutil ");
dsymutil_options.Append (Quote (input)).Append (" ");
dsymutil_options.Append ($"-o {Quote (output)} ");
return Xcrun (dsymutil_options, out exitCode);
}
public static bool Lipo (List<string> inputs, string output, out int exitCode)
{
Directory.CreateDirectory (Path.GetDirectoryName (output));
if (inputs.Count == 1) {
File.Copy (inputs [0], output, true);
exitCode = 0;
return true;
} else {
var lipo_options = new StringBuilder ("lipo ");
foreach (var file in inputs)
lipo_options.Append (file).Append (" ");
lipo_options.Append ("-create -output ");
lipo_options.Append (Quote (output));
return Xcrun (lipo_options, out exitCode);
}
}
public static string Quote (string f)
{
if (f.IndexOf (' ') == -1 && f.IndexOf ('\'') == -1 && f.IndexOf (',') == -1 && f.IndexOf ('$') == -1)
return f;
var s = new StringBuilder ();
s.Append ('"');
foreach (var c in f) {
if (c == '"' || c == '\\')
s.Append ('\\');
s.Append (c);
}
s.Append ('"');
return s.ToString ();
}
}
}

24
objcgen/extensions.cs Normal file
Просмотреть файл

@ -0,0 +1,24 @@
using System;
using IKVM.Reflection;
using Type = IKVM.Reflection.Type;
namespace Embeddinator {
public static class TypeExtensions {
public static bool Is (this Type self, string @namespace, string name)
{
return (self.Namespace == @namespace) && (self.Name == name);
}
public static bool HasCustomAttribute (this Type self, string @namespace, string name)
{
foreach (var ca in self.CustomAttributes) {
if (ca.AttributeType.Is (@namespace, name))
return true;
}
return false;
}
}
}

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

@ -306,11 +306,18 @@
<Compile Include="error.cs" />
<Compile Include="ErrorHelper.cs" />
<Compile Include="Version.generated.cs" />
<Compile Include="objcgenerator-helpers.cs" />
<Compile Include="objcgenerator-processor.cs" />
<Compile Include="extensions.cs" />
</ItemGroup>
<ItemGroup>
<Folder Include="support\" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="..\support\embeddinator.h">
<Link>support\embeddinator.h</Link>
<LogicalName>embeddinator.h</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="..\support\glib.c">
<Link>support\glib.c</Link>
<LogicalName>glib.c</LogicalName>

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

@ -0,0 +1,80 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using IKVM.Reflection;
using Type = IKVM.Reflection.Type;
using Embeddinator;
namespace ObjC {
public partial class ObjCGenerator {
// get a name that is safe to use from ObjC code
public static string GetObjCName (Type t)
{
return t.FullName.Replace ('.', '_');
}
void GetSignatures (string objName, string monoName, MemberInfo info, ParameterInfo [] parameters, out string objcSignature, out string monoSignature)
{
var method = (info as MethodBase); // else it's a PropertyInfo
// special case for setter-only - the underscore looks ugly
if ((method != null) && method.IsSpecialName)
objName = objName.Replace ("_", String.Empty);
StringBuilder objc = new StringBuilder (objName);
var mono = new StringBuilder (monoName);
mono.Append ('(');
int n = 0;
foreach (var p in parameters) {
if (objc.Length > objName.Length) {
objc.Append (' ');
mono.Append (',');
}
if (method != null) {
if (n == 0) {
if (method.IsConstructor || !method.IsSpecialName)
objc.Append (PascalCase (p.Name));
} else
objc.Append (p.Name.ToLowerInvariant ());
}
var pt = p.ParameterType;
var ptname = GetTypeName (p.ParameterType);
if (types.Contains (pt))
ptname += " *";
objc.Append (":(").Append (ptname).Append (") ").Append (p.Name);
mono.Append (GetMonoName (p.ParameterType));
n++;
}
mono.Append (')');
objcSignature = objc.ToString ();
monoSignature = mono.ToString ();
}
public IEnumerable<ConstructorInfo> GetUnavailableParentCtors (Type type, List<ConstructorInfo> typeCtors)
{
var baseType = type.BaseType;
if (baseType.Namespace == "System" && baseType.Name == "Object")
return Enumerable.Empty<ConstructorInfo> ();
List<ConstructorInfo> parentCtors;
if (!ctors.TryGetValue (baseType, out parentCtors))
return Enumerable.Empty<ConstructorInfo> ();
var finalList = new List<ConstructorInfo> ();
foreach (var pctor in parentCtors) {
var pctorParams = pctor.GetParameters ();
foreach (var ctor in typeCtors) {
var ctorParams = ctor.GetParameters ();
if (pctorParams.Any (pc => !ctorParams.Any (p => p.Position == pc.Position && pc.ParameterType == p.ParameterType))) {
finalList.Add (pctor);
break;
}
}
}
return finalList;
}
}
}

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

@ -0,0 +1,211 @@
using System;
using System.Collections.Generic;
using System.Linq;
using IKVM.Reflection;
using Type = IKVM.Reflection.Type;
using Embeddinator;
namespace ObjC {
public partial class ObjCGenerator {
List<Exception> delayed = new List<Exception> ();
HashSet<Type> unsupported = new HashSet<Type> ();
bool IsSupported (Type t)
{
if (t.IsByRef)
return IsSupported (t.GetElementType ());
if (unsupported.Contains (t))
return false;
// FIXME protocols
if (t.IsInterface) {
delayed.Add (ErrorHelper.CreateWarning (1010, $"Type `{t}` is not generated because `interfaces` are not supported."));
unsupported.Add (t);
return false;
}
if (t.IsGenericParameter || t.IsGenericType) {
delayed.Add (ErrorHelper.CreateWarning (1010, $"Type `{t}` is not generated because `generics` are not supported."));
unsupported.Add (t);
return false;
}
switch (t.Namespace) {
case "System":
switch (t.Name) {
case "Object": // we cannot accept arbitrary NSObject (which we might not have bound) into mono
case "Exception":
case "IFormatProvider":
case "Type":
delayed.Add (ErrorHelper.CreateWarning (1011, $"Type `{t}` is not generated because it lacks a native counterpart."));
unsupported.Add (t);
return false;
case "DateTime": // FIXME: NSDateTime
case "Decimal": // FIXME: NSDecimal
case "TimeSpan":
delayed.Add (ErrorHelper.CreateWarning (1012, $"Type `{t}` is not generated because it lacks a marshaling code with a native counterpart."));
unsupported.Add (t);
return false;
}
break;
}
return true;
}
protected IEnumerable<Type> GetTypes (Assembly a)
{
foreach (var t in a.GetTypes ()) {
if (!t.IsPublic)
continue;
if (!IsSupported (t))
continue;
yield return t;
}
}
protected IEnumerable<ConstructorInfo> GetConstructors (Type t)
{
foreach (var ctor in t.GetConstructors ()) {
// .cctor not to be called directly by native code
if (ctor.IsStatic)
continue;
if (!ctor.IsPublic)
continue;
bool pcheck = true;
foreach (var p in ctor.GetParameters ()) {
var pt = p.ParameterType;
if (!IsSupported (pt)) {
delayed.Add (ErrorHelper.CreateWarning (1020, $"Constructor `{ctor}` is not generated because of parameter type `{pt}` is not supported."));
pcheck = false;
} else if (p.HasDefaultValue) {
delayed.Add (ErrorHelper.CreateWarning (1021, $"Constructor `{ctor}` parameter `{p.Name}` has a default value that is not supported."));
}
}
if (!pcheck)
continue;
yield return ctor;
}
}
protected IEnumerable<MethodInfo> GetMethods (Type t)
{
foreach (var mi in t.GetMethods (BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly)) {
if (!mi.IsPublic)
continue;
var rt = mi.ReturnType;
if (!IsSupported (rt)) {
delayed.Add (ErrorHelper.CreateWarning (1030, $"Method `{mi}` is not generated because return type `{rt}` is not supported."));
continue;
}
bool pcheck = true;
foreach (var p in mi.GetParameters ()) {
var pt = p.ParameterType;
if (!IsSupported (pt)) {
delayed.Add (ErrorHelper.CreateWarning (1031, $"Method `{mi}` is not generated because of parameter type `{pt}` is not supported."));
pcheck = false;
} else if (p.HasDefaultValue) {
delayed.Add (ErrorHelper.CreateWarning (1032, $"Method `{mi}` parameter `{p.Name}` has a default value that is not supported."));
}
}
if (!pcheck)
continue;
yield return mi;
}
}
protected IEnumerable<PropertyInfo> GetProperties (Type t)
{
foreach (var pi in t.GetProperties (BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly)) {
var pt = pi.PropertyType;
if (!IsSupported (pt)) {
delayed.Add (ErrorHelper.CreateWarning (1040, $"Property `{pi}` is not generated because of parameter type `{pt}` is not supported."));
continue;
}
yield return pi;
}
}
protected IEnumerable<FieldInfo> GetFields (Type t)
{
foreach (var fi in t.GetFields (BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly)) {
if (!fi.IsPublic)
continue;
var ft = fi.FieldType;
if (!IsSupported (ft)) {
delayed.Add (ErrorHelper.CreateWarning (1050, $"Field `{fi}` is not generated because of field type `{ft}` is not supported."));
continue;
}
yield return fi;
}
}
List<Type> enums = new List<Type> ();
List<Type> types = new List<Type> ();
Dictionary<Type, List<ConstructorInfo>> ctors = new Dictionary<Type, List<ConstructorInfo>> ();
Dictionary<Type, List<MethodInfo>> methods = new Dictionary<Type, List<MethodInfo>> ();
Dictionary<Type, List<PropertyInfo>> properties = new Dictionary<Type, List<PropertyInfo>> ();
Dictionary<Type, List<FieldInfo>> fields = new Dictionary<Type, List<FieldInfo>> ();
public override void Process (IEnumerable<Assembly> assemblies)
{
foreach (var a in assemblies) {
foreach (var t in GetTypes (a)) {
if (t.IsEnum)
enums.Add (t);
else
types.Add (t);
var constructors = GetConstructors (t).OrderBy ((arg) => arg.ParameterCount).ToList ();
if (constructors.Count > 0)
ctors.Add (t, constructors);
var meths = GetMethods (t).OrderBy ((arg) => arg.Name).ToList ();
if (meths.Count > 0)
methods.Add (t, meths);
var props = new List<PropertyInfo> ();
foreach (var pi in GetProperties (t)) {
var getter = pi.GetGetMethod ();
var setter = pi.GetSetMethod ();
// setter only property are valid in .NET and we need to generate a method in ObjC (there's no writeonly properties)
if (getter == null)
continue;
// indexers are implemented as methods
if ((getter.ParameterCount > 0) || ((setter != null) && setter.ParameterCount > 1))
continue;
// we can do better than methods for the more common cases (readonly and readwrite)
meths.Remove (getter);
meths.Remove (setter);
props.Add (pi);
}
props = props.OrderBy ((arg) => arg.Name).ToList ();
if (props.Count > 0)
properties.Add (t, props);
// fields will need to be wrapped within properties - except for enums
if (!t.IsEnum) {
var f = GetFields (t).OrderBy ((arg) => arg.Name).ToList ();
if (f.Count > 0)
fields.Add (t, f);
}
}
}
types = types.OrderBy ((arg) => arg.FullName).OrderBy ((arg) => types.Contains (arg.BaseType)).ToList ();
Console.WriteLine ($"\t{types.Count} types found");
ErrorHelper.Show (delayed);
}
}
}

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

@ -11,68 +11,14 @@ using Embeddinator;
namespace ObjC {
public class ObjCGenerator : Generator {
public partial class ObjCGenerator : Generator {
static TextWriter headers = new StringWriter ();
static TextWriter implementation = new StringWriter ();
static ParameterInfo [] NoParameters = new ParameterInfo [0];
List<Type> types = new List<Type> ();
Dictionary<Type, List<ConstructorInfo>> ctors = new Dictionary<Type, List<ConstructorInfo>> ();
Dictionary<Type, List<MethodInfo>> methods = new Dictionary<Type, List<MethodInfo>> ();
Dictionary<Type, List<PropertyInfo>> properties = new Dictionary<Type, List<PropertyInfo>> ();
public override void Process (IEnumerable<Assembly> assemblies)
{
foreach (var a in assemblies) {
foreach (var t in a.GetTypes ()) {
if (!t.IsPublic)
continue;
// gather types for forward declarations
types.Add (t);
var constructors = new List<ConstructorInfo> ();
foreach (var ctor in t.GetConstructors ()) {
// .cctor not to be called directly by native code
if (ctor.IsStatic)
continue;
if (!ctor.IsPublic)
continue;
constructors.Add (ctor);
}
constructors = constructors.OrderBy ((arg) => arg.ParameterCount).ToList ();
ctors.Add (t, constructors);
var meths = new List<MethodInfo> ();
foreach (var mi in t.GetMethods (BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly)) {
meths.Add (mi);
}
methods.Add (t, meths);
var props = new List<PropertyInfo> ();
foreach (var pi in t.GetProperties (BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly)) {
var getter = pi.GetGetMethod ();
var setter = pi.GetSetMethod ();
// setter only property are valid in .NET and we need to generate a method in ObjC (there's no writeonly properties)
if (getter == null)
continue;
// we can do better than methods for the more common cases (readonly and readwrite)
meths.Remove (getter);
meths.Remove (setter);
props.Add (pi);
}
props = props.OrderBy ((arg) => arg.Name).ToList ();
properties.Add (t, props);
}
}
types = types.OrderBy ((arg) => arg.FullName).OrderBy ((arg) => types.Contains (arg.BaseType)).ToList ();
Console.WriteLine ($"\t{types.Count} types found");
}
public override void Generate (IEnumerable<Assembly> assemblies)
{
headers.WriteLine ("#include \"mono_embeddinator.h\"");
headers.WriteLine ("#include \"embeddinator.h\"");
headers.WriteLine ("#import <Foundation/Foundation.h>");
headers.WriteLine ();
headers.WriteLine ();
@ -80,8 +26,6 @@ namespace ObjC {
headers.WriteLine ("#error Embeddinator code must be built with ARC.");
headers.WriteLine ("#endif");
headers.WriteLine ();
headers.WriteLine ("MONO_EMBEDDINATOR_BEGIN_DECLS");
headers.WriteLine ();
headers.WriteLine ("// forward declarations");
foreach (var t in types)
@ -91,6 +35,7 @@ namespace ObjC {
implementation.WriteLine ("#include \"bindings.h\"");
implementation.WriteLine ("#include \"glib.h\"");
implementation.WriteLine ("#include \"objc-support.h\"");
implementation.WriteLine ("#include \"mono_embeddinator.h\"");
implementation.WriteLine ("#include <mono/jit/jit.h>");
implementation.WriteLine ("#include <mono/metadata/assembly.h>");
implementation.WriteLine ("#include <mono/metadata/object.h>");
@ -118,9 +63,6 @@ namespace ObjC {
implementation.WriteLine ();
base.Generate (assemblies);
headers.WriteLine ();
headers.WriteLine ("MONO_EMBEDDINATOR_END_DECLS");
}
protected override void Generate (Assembly a)
@ -136,11 +78,49 @@ namespace ObjC {
implementation.WriteLine ("}");
implementation.WriteLine ();
foreach (var t in enums) {
GenerateEnum (t);
}
foreach (var t in types) {
Generate (t);
}
}
void GenerateEnum (Type t)
{
var managed_name = GetObjCName (t);
var underlying_type = t.GetEnumUnderlyingType ();
var base_type = GetTypeName (underlying_type);
// it's nicer to expose flags as unsigned integers - but .NET defaults to `int`
bool flags = t.HasCustomAttribute ("System", "FlagsAttribute");
if (flags) {
switch (Type.GetTypeCode (underlying_type)) {
case TypeCode.SByte:
case TypeCode.Int16:
case TypeCode.Int32:
case TypeCode.Int64:
base_type = "unsigned " + base_type;
break;
}
}
var macro = flags ? "NS_OPTIONS" : "NS_ENUM";
headers.WriteLine ($"typedef {macro}({base_type}, {managed_name}) {{");
foreach (var name in t.GetEnumNames ()) {
var value = t.GetField (name).GetRawConstantValue ();
headers.Write ($"\t{managed_name}{name} = ");
if (flags)
headers.Write ($"0x{value:x}");
else
headers.Write (value);
headers.WriteLine (',');
}
headers.WriteLine ("};");
headers.WriteLine ();
}
protected override void Generate (Type t)
{
var has_bound_base_class = types.Contains (t.BaseType);
@ -151,15 +131,17 @@ namespace ObjC {
var native_name = GetTypeName (t);
headers.WriteLine ();
headers.WriteLine ($"// {t.AssemblyQualifiedName}");
headers.WriteLine ($"@interface {native_name} : {GetTypeName (t.BaseType)}");
headers.WriteLine ($"@interface {native_name} : {GetTypeName (t.BaseType)} {{");
if (!static_type && !has_bound_base_class) {
headers.WriteLine ("\t@public MonoEmbedObject* _object;");
}
headers.WriteLine ("}");
headers.WriteLine ();
implementation.WriteLine ();
implementation.WriteLine ($"// {t.AssemblyQualifiedName}");
implementation.WriteLine ($"@implementation {native_name} {{");
// our internal field is only needed once in the type hierarchy
if (!static_type && !has_bound_base_class)
implementation.WriteLine ("\t@public MonoEmbedObject* _object;");
implementation.WriteLine ("}");
implementation.WriteLine ();
@ -169,7 +151,12 @@ namespace ObjC {
implementation.WriteLine ("\t\treturn;");
var aname = t.Assembly.GetName ().Name;
implementation.WriteLine ($"\t__lookup_assembly_{aname} ();");
implementation.WriteLine ("#if TOKENLOOKUP");
implementation.WriteLine ($"\t{managed_name}_class = mono_class_get (__{aname}_image, 0x{t.MetadataToken:X8});");
implementation.WriteLine ("#else");
implementation.WriteLine ($"\t{managed_name}_class = mono_class_from_name (__{aname}_image, \"{t.Namespace}\", \"{t.Name}\");");
implementation.WriteLine ("#endif");
implementation.WriteLine ("}");
implementation.WriteLine ();
@ -185,76 +172,67 @@ namespace ObjC {
var default_init = false;
List<ConstructorInfo> constructors;
if (ctors.TryGetValue (t, out constructors)) {
// First get the unavailable init ctor selectors in parent class
var unavailableCtors = GetUnavailableParentCtors (t, constructors);
if (unavailableCtors.Count () > 0) {
// TODO: Print a #pragma mark once we have a well defined header structure http://nshipster.com/pragma/
headers.WriteLine ("// List of unavailable initializers. See Constructors v.s. Initializers in our docs");
headers.WriteLine ("// https://github.com/mono/Embeddinator-4000/blob/master/docs/ObjC.md");
foreach (var uctor in unavailableCtors) {
var ctorparams = uctor.GetParameters ();
string name = "init";
string signature = ".ctor()";
if (ctorparams.Length > 0)
GetSignatures ("initWith", uctor.Name, uctor, ctorparams, out name, out signature);
headers.WriteLine ($"- (instancetype){name} NS_UNAVAILABLE;");
}
headers.WriteLine ();
}
foreach (var ctor in constructors) {
var pcount = ctor.ParameterCount;
default_init |= pcount == 0;
var parameters = ctor.GetParameters ();
StringBuilder name = new StringBuilder ("init");
foreach (var p in parameters) {
if (name.Length == 4)
name.Append ("With");
else
name.Append (' ');
name.Append (PascalCase (p.Name));
name.Append (":(").Append (GetTypeName (p.ParameterType)).Append (") ").Append (p.Name);
}
var signature = new StringBuilder (".ctor(");
foreach (var p in parameters) {
if (signature.Length > 6)
signature.Append (',');
signature.Append (GetMonoName (p.ParameterType));
}
signature.Append (")");
string name = "init";
string signature = ".ctor()";
if (parameters.Length > 0)
GetSignatures ("initWith", ctor.Name, ctor, parameters, out name, out signature);
headers.WriteLine ($"- (instancetype){name};");
implementation.WriteLine ($"- (instancetype){name}");
implementation.WriteLine ("{");
implementation.WriteLine ($"\tconst char __method_name [] = \"{t.FullName}:{signature}\";");
implementation.WriteLine ("\tstatic MonoMethod* __method = nil;");
implementation.WriteLine ("\tif (!__method) {");
implementation.WriteLine ("#if TOKENLOOKUP");
implementation.WriteLine ($"\t\t__method = mono_get_method (__{aname}_image, 0x{ctor.MetadataToken:X8}, {managed_name}_class);");
implementation.WriteLine ("#else");
implementation.WriteLine ($"\t\tconst char __method_name [] = \"{t.FullName}:{signature}\";");
implementation.WriteLine ($"\t\t__method = mono_embeddinator_lookup_method (__method_name, {managed_name}_class);");
implementation.WriteLine ("#endif");
implementation.WriteLine ("\t}");
// TODO: this logic will need to be update for managed NSObject types (e.g. from XI / XM) not to call [super init]
implementation.WriteLine ("\tif (!_object) {");
implementation.WriteLine ($"\t\tMonoObject* __instance = mono_object_new (__mono_context.domain, {managed_name}_class);");
implementation.WriteLine ("\t\tMonoObject* __exception = nil;");
string postInvoke = String.Empty;
var args = "nil";
if (pcount > 0) {
implementation.WriteLine ($"\t\tvoid* __args [{pcount}];");
for (int i = 0; i < pcount; i++) {
var p = parameters [i];
switch (Type.GetTypeCode (p.ParameterType)) {
case TypeCode.String:
implementation.WriteLine ($"\t\t__args [{i}] = mono_string_new (__mono_context.domain, [{p.Name} UTF8String]);");
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:
implementation.WriteLine ($"\t\t__args [{i}] = &{p.Name};");
break;
default:
throw new NotImplementedException ($"Converting type {p.ParameterType.FullName} to mono code");
}
}
Generate (parameters, out postInvoke);
args = "__args";
}
implementation.WriteLine ($"\t\tmono_runtime_invoke (__method, __instance, {args}, &__exception);");
var instance = "__instance";
if (t.IsValueType) {
implementation.WriteLine ($"\t\tvoid* __unboxed = mono_object_unbox (__instance);");
instance = "__unboxed";
}
implementation.WriteLine ($"\t\tmono_runtime_invoke (__method, {instance}, {args}, &__exception);");
implementation.WriteLine ("\t\tif (__exception)");
// TODO: Apple often do NSLog (or asserts but they are more brutal) and returning nil is allowed (and common)
implementation.WriteLine ("\t\t\treturn nil;");
//implementation.WriteLine ("\t\t\tmono_embeddinator_throw_exception (__exception);");
implementation.Write (postInvoke);
implementation.WriteLine ("\t\t_object = mono_embeddinator_create_object (__instance);");
implementation.WriteLine ("\t}");
if (types.Contains (t.BaseType))
@ -270,6 +248,7 @@ namespace ObjC {
if (static_type)
headers.WriteLine ("// a .net static type cannot be initialized");
headers.WriteLine ("- (instancetype)init NS_UNAVAILABLE;");
headers.WriteLine ("+ (instancetype)new NS_UNAVAILABLE;");
}
// TODO we should re-use the base `init` when it exists
@ -287,20 +266,25 @@ namespace ObjC {
implementation.WriteLine ();
}
headers.WriteLine ();
List<PropertyInfo> props;
if (properties.TryGetValue (t, out props)) {
headers.WriteLine ();
foreach (var pi in props)
Generate (pi);
headers.WriteLine ();
}
headers.WriteLine ();
List<FieldInfo> f;
if (fields.TryGetValue (t, out f)) {
headers.WriteLine ();
foreach (var fi in f)
Generate (fi);
}
List<MethodInfo> meths;
if (methods.TryGetValue (t, out meths)) {
headers.WriteLine ();
foreach (var mi in meths)
Generate (mi);
headers.WriteLine ();
}
headers.WriteLine ("@end");
@ -310,6 +294,58 @@ namespace ObjC {
implementation.WriteLine ();
}
void Generate (ParameterInfo [] parameters, out string postInvoke)
{
StringBuilder post = new StringBuilder ();
var pcount = parameters.Length;
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;
}
}
postInvoke = post.ToString ();
}
protected override void Generate (PropertyInfo pi)
{
var getter = pi.GetGetMethod ();
@ -327,14 +363,70 @@ namespace ObjC {
headers.Write (", readonly");
else
headers.Write (", readwrite");
var property_type = GetTypeName (pi.PropertyType);
var pt = pi.PropertyType;
var property_type = GetTypeName (pt);
if (types.Contains (pt))
property_type += " *";
headers.WriteLine ($") {property_type} {name};");
ImplementMethod (getter.IsStatic, getter.ReturnType, name, NoParameters, pi.DeclaringType, getter.Name);
ImplementMethod (getter, name, pi);
if (setter == null)
return;
ImplementMethod (setter.IsStatic, setter.ReturnType, "set" + pi.Name, setter.GetParameters (), pi.DeclaringType, setter.Name);
ImplementMethod (setter, "set" + pi.Name, pi);
}
protected void Generate (FieldInfo fi)
{
bool read_only = ((fi.Attributes & FieldAttributes.InitOnly) == FieldAttributes.InitOnly);
headers.Write ("@property (nonatomic");
if (fi.IsStatic)
headers.Write (", class");
if (read_only)
headers.Write (", readonly");
else
headers.Write (", readwrite");
var ft = fi.FieldType;
var field_type = GetTypeName (ft);
if (types.Contains (ft))
field_type += " *";
var name = CamelCase (fi.Name);
headers.WriteLine ($") {field_type} {name};");
// it's similar, but different from implementing a method
var type = fi.DeclaringType;
var managed_type_name = GetObjCName (type);
var return_type = GetReturnType (type, fi.FieldType);
implementation.Write (fi.IsStatic ? '+' : '-');
implementation.WriteLine ($" ({return_type}) {CamelCase (fi.Name)}");
implementation.WriteLine ("{");
implementation.WriteLine ("\tstatic MonoClassField* __field = nil;");
implementation.WriteLine ("\tif (!__field) {");
implementation.WriteLine ("#if TOKENLOOKUP");
var 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}");
var instance = "nil";
if (!fi.IsStatic) {
implementation.WriteLine ($"\tMonoObject* __instance = mono_gchandle_get_target (_object->_handle);");
instance = "__instance";
}
implementation.WriteLine ($"\tMonoObject* __result = mono_field_get_value_object (__mono_context.domain, __field, {instance});");
ReturnValue (fi.FieldType);
implementation.WriteLine ("}");
implementation.WriteLine ();
if (read_only)
return;
// TODO write support
}
public string GetReturnType (Type declaringType, Type returnType)
@ -349,82 +441,78 @@ namespace ObjC {
}
// TODO override with attribute ? e.g. [ObjC.Selector ("foo")]
void ImplementMethod (bool isStatic, Type returnType, string name, ParameterInfo [] parametersInfo, Type type, string managed_name)
void ImplementMethod (MethodInfo info, string name, PropertyInfo pi = null)
{
var type = info.DeclaringType;
var managed_type_name = GetObjCName (type);
var return_type = GetReturnType (type, returnType);
StringBuilder parameters = new StringBuilder ();
StringBuilder managed_parameters = new StringBuilder ();
foreach (var p in parametersInfo) {
if (parameters.Length > 0) {
parameters.Append (' ');
managed_parameters.Append (' ');
}
parameters.Append (":(").Append (GetTypeName (p.ParameterType)).Append (")").Append (p.Name);
managed_parameters.Append (GetMonoName (p.ParameterType));
var managed_name = info.Name;
var return_type = GetReturnType (type, info.ReturnType);
var parametersInfo = info.GetParameters ();
string objcsig;
string monosig;
GetSignatures (name, managed_name, (MemberInfo) pi ?? info, parametersInfo, out objcsig, out monosig);
if (pi == null) {
headers.Write (info.IsStatic ? '+' : '-');
headers.WriteLine ($" ({return_type}){objcsig};");
}
implementation.Write (isStatic ? '+' : '-');
implementation.WriteLine ($" ({return_type}) {name}{parameters}");
implementation.Write (info.IsStatic ? '+' : '-');
implementation.WriteLine ($" ({return_type}) {objcsig}");
implementation.WriteLine ("{");
implementation.WriteLine ($"\tconst char __method_name [] = \"{type.FullName}:{managed_name}({managed_parameters})\";");
implementation.WriteLine ("\tstatic MonoMethod* __method = nil;");
implementation.WriteLine ("\tif (!__method) {");
//implementation.WriteLine ($"\t\t__lookup_class_{managed_type_name} ();");
implementation.WriteLine ("#if TOKENLOOKUP");
var aname = type.Assembly.GetName ().Name;
implementation.WriteLine ($"\t\t__method = mono_get_method (__{aname}_image, 0x{info.MetadataToken:X8}, {managed_type_name}_class);");
implementation.WriteLine ("#else");
implementation.WriteLine ($"\t\tconst char __method_name [] = \"{type.FullName}:{monosig}\";");
implementation.WriteLine ($"\t\t__method = mono_embeddinator_lookup_method (__method_name, {managed_type_name}_class);");
implementation.WriteLine ("#endif");
implementation.WriteLine ("\t}");
string postInvoke = String.Empty;
var args = "nil";
if (parametersInfo.Length > 0) {
Generate (parametersInfo, out postInvoke);
args = "__args";
implementation.WriteLine ($"\tvoid* __args [{parametersInfo.Length}];");
for (int i = 0; i < parametersInfo.Length; i++) {
implementation.WriteLine ($"\t__args [{i}] = &{parametersInfo [i].Name};");
}
}
implementation.WriteLine ("\tMonoObject* __exception = nil;");
var instance = "nil";
if (!isStatic) {
implementation.WriteLine ($"\tMonoObject* instance = mono_gchandle_get_target (_object->_handle);");
instance = "instance";
if (!info.IsStatic) {
implementation.WriteLine ($"\tMonoObject* __instance = mono_gchandle_get_target (_object->_handle);");
if (type.IsValueType) {
implementation.WriteLine ($"\t\tvoid* __unboxed = mono_object_unbox (__instance);");
instance = "__unboxed";
} else {
instance = "__instance";
}
}
implementation.Write ("\t");
if (!IsVoid (returnType))
if (!info.ReturnType.Is ("System", "Void"))
implementation.Write ("MonoObject* __result = ");
implementation.WriteLine ($"mono_runtime_invoke (__method, {instance}, {args}, &__exception);");
implementation.WriteLine ("\tif (__exception)");
implementation.WriteLine ("\t\tmono_embeddinator_throw_exception (__exception);");
ReturnValue (returnType);
// ref and out parameters might need to be converted back
implementation.Write (postInvoke);
ReturnValue (info.ReturnType);
implementation.WriteLine ("}");
implementation.WriteLine ();
}
public static bool IsVoid (Type t)
{
if (t.Name != "Void")
return false;
return (t.Namespace == "System");
}
protected override void Generate (MethodInfo mi)
{
StringBuilder parameters = new StringBuilder ();
foreach (var p in mi.GetParameters ()) {
if (parameters.Length > 0)
parameters.Append (' ');
parameters.Append (":(").Append (GetTypeName (p.ParameterType)).Append (")").Append (p.Name);
}
var return_type = GetReturnType (mi.DeclaringType, mi.ReturnType);
var name = CamelCase (mi.Name);
headers.Write (mi.IsStatic ? '+' : '-');
headers.WriteLine ($" ({return_type}){name}{parameters};");
ImplementMethod (mi.IsStatic, mi.ReturnType, name, mi.GetParameters (), mi.DeclaringType, mi.Name);
string name;
if (mi.IsSpecialName && mi.IsStatic && mi.Name.StartsWith ("op_", StringComparison.Ordinal))
name = CamelCase (mi.Name.Substring (3));
else
name = CamelCase (mi.Name);
ImplementMethod (mi, name);
}
void ReturnValue (Type t)
@ -480,12 +568,19 @@ namespace ObjC {
// TODO override with attribute ? e.g. [Obj.Name ("XAMType")]
public static string GetTypeName (Type t)
{
if (t.IsByRef)
return GetTypeName (t.GetElementType ()) + "*";
if (t.IsEnum)
return GetObjCName (t);
switch (Type.GetTypeCode (t)) {
case TypeCode.Object:
switch (t.Namespace) {
case "System":
switch (t.Name) {
case "Object":
case "ValueType":
return "NSObject";
case "Void":
return "void";
@ -520,7 +615,7 @@ namespace ObjC {
case TypeCode.UInt64:
return "unsigned long long";
case TypeCode.String:
return "NSString*";
return "NSString *";
default:
throw new NotImplementedException ($"Converting type {t.Name} to a native type name");
}
@ -528,20 +623,24 @@ namespace ObjC {
public static string GetMonoName (Type t)
{
if (t.IsByRef)
return GetMonoName (t.GetElementType ()) + "&";
if (t.IsEnum)
return t.FullName;
switch (Type.GetTypeCode (t)) {
case TypeCode.Object:
switch (t.Namespace) {
case "System":
switch (t.Name) {
case "Object":
return "object";
case "Void":
return "void";
default:
throw new NotImplementedException ($"Converting type {t.Name} to a mono type name");
return "object";
}
default:
throw new NotImplementedException ($"Converting type {t.Name} to a mono type name");
return t.FullName;
}
case TypeCode.Boolean:
return "bool";
@ -591,11 +690,5 @@ namespace ObjC {
return String.Empty;
return Char.ToUpperInvariant (s [0]) + s.Substring (1, s.Length - 1);
}
// get a name that is safe to use from ObjC code
public static string GetObjCName (Type t)
{
return t.FullName.Replace ('.', '_');
}
}
}

36
support/embeddinator.h Normal file
Просмотреть файл

@ -0,0 +1,36 @@
/*
* Definitions needed for the generated header files.
*
* (C) 2017 Microsoft, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* Arrays
*/
typedef struct MonoEmbedArray MonoEmbedArray;
/**
* Objects
*/
typedef struct MonoEmbedObject MonoEmbedObject;

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

@ -1,4 +1,4 @@
/*
/*
* Mono managed-to-native support code.
*
* Author:
@ -33,16 +33,14 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#if defined(__APPLE__)
#include <errno.h>
#include <libproc.h>
#include <unistd.h>
#include <mach-o/dyld.h>
#endif
#if defined(__OBJC__)
#include <objc/runtime.h>
#include <objc/objc-runtime.h>
#endif
#ifdef _WIN32
@ -76,6 +74,10 @@ int mono_embeddinator_init(mono_embeddinator_context_t* ctx, const char* domain)
mono_embeddinator_set_context(ctx);
char cwd[PATH_MAX];
getcwd(cwd, PATH_MAX);
mono_domain_set_config(ctx->domain, cwd, "app.config");
return true;
}
@ -101,14 +103,10 @@ static GString* get_current_executable_path()
if (path_override)
return path_override;
#if defined(__APPLE__)
int ret;
pid_t pid;
char pathbuf[PROC_PIDPATHINFO_MAXSIZE];
pid = getpid();
ret = proc_pidpath (pid, pathbuf, sizeof(pathbuf));
return (ret > 0) ? g_string_new(pathbuf) : 0;
char pathbuf [1024];
uint32_t bufsize = sizeof (pathbuf);
int ret = _NSGetExecutablePath (pathbuf, &bufsize);
return ret == 0 ? g_string_new (pathbuf) : 0;
#elif defined(_WIN32)
HMODULE hModule = GetModuleHandleW(0);
CHAR pathbuf[MAX_PATH];
@ -159,8 +157,6 @@ MonoImage* mono_embeddinator_load_assembly(mono_embeddinator_context_t* ctx, con
MonoAssembly* mono_assembly = mono_domain_assembly_open(ctx->domain, path);
g_free (path);
if (!mono_assembly)
{
mono_embeddinator_error_t error;
@ -168,9 +164,13 @@ MonoImage* mono_embeddinator_load_assembly(mono_embeddinator_context_t* ctx, con
error.string = path;
mono_embeddinator_error(error);
g_free (path);
return 0;
}
g_free (path);
return mono_assembly_get_image(mono_assembly);
}
@ -191,7 +191,6 @@ MonoClass* mono_embeddinator_search_class(const char* assembly, const char* _nam
char* path = mono_embeddinator_search_assembly(assembly);
MonoAssembly* mono_assembly = mono_domain_assembly_open(ctx->domain, path);
g_free (path);
if (mono_assembly == 0)
{
@ -201,6 +200,8 @@ MonoClass* mono_embeddinator_search_class(const char* assembly, const char* _nam
mono_embeddinator_error(error);
}
g_free (path);
MonoImage* image = mono_assembly_get_image(mono_assembly);
MonoClass* klass = mono_class_from_name(image, _namespace, name);

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

@ -58,6 +58,7 @@
#define MONO_EMBEDDINATOR_API MONO_EMBEDDINATOR_API_IMPORT
#endif
#include "embeddinator.h"
typedef uint16_t gunichar2;
typedef struct _GArray GArray;
@ -202,24 +203,17 @@ typedef void (*mono_embeddinator_error_report_hook_t)(mono_embeddinator_error_t)
MONO_EMBEDDINATOR_API
void* mono_embeddinator_install_error_report_hook(mono_embeddinator_error_report_hook_t hook);
/**
* Arrays
*/
typedef struct
struct MonoEmbedArray
{
GArray* array;
} MonoEmbedArray;
};
/**
* Objects
*/
typedef struct
struct MonoEmbedObject
{
MonoClass* _class;
uint32_t _handle;
} MonoEmbedObject;
};
/**
* Creates a MonoEmbedObject support object from a Mono object instance.

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

@ -1,5 +1,5 @@
bin/Debug/managed.dll: $(wildcard *.cs) managed.csproj
/Library/Frameworks/Mono.framework/Versions/Current/Commands/msbuild $(BUILD_FLAGS) managed.csproj || /Library/Frameworks/Mono.framework/Versions/Current/Commands/xbuild $(BUILD_FLAGS) managed.csproj
/Library/Frameworks/Mono.framework/Versions/Current/Commands/msbuild $(BUILD_FLAGS) managed.csproj
all: bin/Debug/managed.dll

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

@ -1,35 +1,42 @@
#if NON_OBJC_SUPPORTED_TESTS
using System;
using System.Linq;
using System;
namespace Enums {
public enum Enum
{
Two = 2,
Three
public enum ByteEnum : byte {
Zero = 0,
Max = 255,
}
public enum EnumByte : byte
{
Two = 2,
Three
public enum IntEnum /* default */ {
Min = Int32.MinValue,
Max = Int32.MaxValue,
}
[Flags]
public enum EnumFlags
{
FlagOne = 1 << 0,
FlagTwo = 1 << 2
public enum ByteFlags {
Empty = 0x0,
Bit0 = 0x1,
Bit1 = 0x2,
Bit2 = 0x4,
Bit3 = 0x8,
Bit4 = 0x10,
Bit5 = 0x20,
Bit6 = 0x40,
Bit7 = 0x80,
}
public static class EnumTypes {
public enum ShortEnum : short {
Min = Int16.MinValue,
Max = Int16.MaxValue,
}
public static int PassEnum (Enum e) { return (int)e; }
public static byte PassEnumByte (EnumByte e) { return (byte)e; }
public static int PassEnumFlags (EnumFlags e) { return (int)e; }
public static class Enumer {
public static ByteFlags Test (ByteEnum b, ref IntEnum i, out ShortEnum s)
{
s = b == ByteEnum.Max ? ShortEnum.Max : ShortEnum.Min;
i = i == IntEnum.Min ? IntEnum.Max : IntEnum.Min;
return ByteFlags.Bit5 | ByteFlags.Bit1;
}
}
}
#endif

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

@ -31,13 +31,14 @@
</ItemGroup>
<ItemGroup>
<Compile Include="arrays.cs" />
<Compile Include="enums.cs" />
<Compile Include="properties.cs" />
<Compile Include="namespaces.cs" />
<Compile Include="types.cs" />
<Compile Include="exceptions.cs" />
<Compile Include="constructors.cs" />
<Compile Include="methods.cs" />
<Compile Include="structs.cs" />
<Compile Include="enums.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
</Project>

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

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
namespace Methods {
@ -19,4 +20,69 @@ namespace Methods {
// only getter will be generated
public int Id { get; private set; }
}
public class Parameters {
public static string Concat (string first, string second)
{
if (first == null)
return second;
if (second == null)
return first;
return first + second;
}
public static void Ref (ref bool boolean, ref string @string)
{
boolean = !boolean;
@string = @string == null ? "hello" : null;
}
public static void Out (string @string, out int length, out string upper)
{
length = @string == null ? 0 : @string.Length;
upper = @string == null ? null : @string.ToUpperInvariant ();
}
}
public class Item {
// not generated
internal Item (int id)
{
Integer = id;
}
public int Integer { get; private set; }
}
public static class Factory {
public static Item CreateItem (int id)
{
return new Item (id);
}
}
public class Collection {
List<Item> c = new List<Item> ();
public void Add (Item item)
{
c.Add (item);
}
public void Remove (Item item)
{
c.Remove (item);
}
public int Count => c.Count;
public Item this [int index] {
get { return c [index]; }
set { c [index] = value; }
}
}
}

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

@ -0,0 +1,39 @@
using System;
namespace Structs {
public struct Point {
public static readonly Point Zero;
public Point (float x, float y)
{
X = x;
Y = y;
}
public float X { get; private set; }
public float Y { get; private set; }
public static bool operator == (Point left, Point right)
{
return ((left.X == right.X) && (left.Y == right.Y));
}
public static bool operator != (Point left, Point right)
{
return !(left == right);
}
public static Point operator + (Point left, Point right)
{
return new Point (left.X + right.X, left.Y + right.Y);
}
public static Point operator - (Point left, Point right)
{
return new Point (left.X - right.X, left.Y - right.Y);
}
}
}

13
tests/objc-cli/.gitignore поставляемый
Просмотреть файл

@ -1,11 +1,5 @@
glib.c
glib.h
mono_embeddinator.c
mono_embeddinator.h
objc-support.m
objc-support.h
perf-cli
test-cli
debug
release/
# Xcode
.DS_Store
build/
@ -24,3 +18,6 @@ profile
*.moved-aside
DerivedData
.idea/
i386
x86_64

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

@ -1,4 +1,6 @@
all: run-test perf xctest test-leaks
all: run-test perf xctest test-leaks test-static test-dynamic
MONO=/Library/Frameworks/Mono.framework/Versions/Current/Commands/mono
OBJC_GEN_DIR=../../objcgen
OBJC_GEN=$(OBJC_GEN_DIR)/bin/Debug/objcgen.exe
@ -10,45 +12,71 @@ MANAGED_DLL=$(MANAGED_DLL_DIR)/bin/Debug/managed.dll
$(MANAGED_DLL): $(wildcard $(MANAGED_DLL_DIR)/*.cs)
make -C $(MANAGED_DLL_DIR)
managed.dll: $(MANAGED_DLL)
%/managed.dll: $(MANAGED_DLL)
mkdir -p $(dir $@)
cp $< $@
libmanaged.dylib: managed.dll $(OBJC_GEN)
/Library/Frameworks/Mono.framework/Versions/Current/Commands/mono --debug $(OBJC_GEN) --debug managed.dll -c
debug/libmanaged.dylib: debug/managed.dll $(OBJC_GEN)
$(MONO) --debug $(OBJC_GEN) --debug debug/managed.dll -c -o debug
test-cli: test-managed.m libmanaged.dylib
clang -g $< -lmanaged -L. -framework Foundation -o $@ -rpath @executable_path -fobjc-arc
release/libmanaged.dylib: release/managed.dll $(OBJC_GEN)
$(MONO) --debug $(OBJC_GEN) release/managed.dll -c -o release
perf-cli: perf-test.m libmanaged.dylib
clang -g $< -lmanaged -L. -framework Foundation -o $@ -rpath @executable_path -fobjc-arc
debug/test-cli: test-managed.m debug/libmanaged.dylib
clang -g -O0 $< -lmanaged -Ldebug -Idebug -framework Foundation -o $@ -rpath @executable_path -fobjc-arc
run-test: test-cli
./test-cli
release/test-cli: test-managed.m release/libmanaged.dylib
clang -O2 $< -lmanaged -Lrelease -Irelease -framework Foundation -o $@ -rpath @executable_path -fobjc-arc
perf: perf-cli
time ./perf-cli
debug/perf-cli: perf-test.m debug/libmanaged.dylib
clang -g -O0 $< -lmanaged -Ldebug -Idebug -framework Foundation -o $@ -rpath @executable_path -fobjc-arc
release/perf-cli: perf-test.m release/libmanaged.dylib
clang -O2 $< -lmanaged -Lrelease -Irelease -framework Foundation -o $@ -rpath @executable_path -fobjc-arc
run-%-cli-test: %/test-cli
@echo "Test ($*)"
./$<
run-%-perf-test: %/perf-cli
@echo "Performance ($*)"
time ./$<
run-test: run-debug-cli-test run-release-cli-test
perf: run-debug-perf-test run-release-perf-test
clean:
@rm -f *.h glib.c mono_embeddinator.c bindings.m *.dylib *.dll test-cli perf-cli
@rm -rf debug release
xctest:
xctest: debug/libmanaged.dylib
xcodebuild test -project libmanaged/*.xcodeproj -scheme Tests
test-leaks: test-cli-leaks test-perf-leaks test-xctest-leaks
../leaktest/bin/Debug/leaktest.exe: $(wildcard ../leaktest/*.cs) libLeakCheckAtExit.dylib
/Library/Frameworks/Mono.framework/Versions/Current/Commands/xbuild /verbosity:quiet /nologo ../leaktest/leaktest.csproj
/Library/Frameworks/Mono.framework/Versions/Current/Commands/msbuild /verbosity:quiet /nologo ../leaktest/leaktest.csproj
test-cli-leaks: test-cli ../leaktest/bin/Debug/leaktest.exe
test-cli-leaks: debug/test-cli ../leaktest/bin/Debug/leaktest.exe
mono --debug ../leaktest/bin/Debug/leaktest.exe $(abspath $<)
test-perf-leaks: perf-cli ../leaktest/bin/Debug/leaktest.exe
test-perf-leaks: debug/perf-cli ../leaktest/bin/Debug/leaktest.exe
mono --debug ../leaktest/bin/Debug/leaktest.exe $(abspath $<)
test-xctest-leaks: ../leaktest/bin/Debug/leaktest.exe $(MANAGED_DLL) libmanaged.dylib
test-xctest-leaks: ../leaktest/bin/Debug/leaktest.exe $(MANAGED_DLL) debug/libmanaged.dylib
rm -Rf libmanaged/Tests.xctest
xcodebuild -quiet build-for-testing -project libmanaged/*.xcodeproj -scheme Tests CONFIGURATION_BUILD_DIR=$(abspath libmanaged)
mono --debug ../leaktest/bin/Debug/leaktest.exe `xcrun -f xctest` -XCTest All $(abspath libmanaged/Tests.xctest)
libLeakCheckAtExit.dylib: leak-at-exit.c
clang -arch i386 -arch x86_64 -shared $< -o $@
test-static: test-static-macos test-static-ios test-static-tvos test-static-watchos
test-static-%: $(MANAGED_DLL) $(OBJC_GEN)
/Library/Frameworks/Mono.framework/Versions/Current/Commands/mono --debug $(OBJC_GEN) --debug $(MANAGED_DLL) -c --outdir=build/$@-temp-dir --target=staticlibrary --platform=$*
test-dynamic: test-dynamic-macos test-dynamic-ios test-dynamic-tvos test-dynamic-watchos
test-dynamic-%: $(MANAGED_DLL) $(OBJC_GEN)
/Library/Frameworks/Mono.framework/Versions/Current/Commands/mono --debug $(OBJC_GEN) --debug $(MANAGED_DLL) -c --outdir=build/$@-temp-dir --target=dylib --platform=$*

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

@ -41,7 +41,7 @@
XCTAssertFalse ([query isSecret], "instance property getter only 3");
// setter only property turned into method, so different syntax
[query set_Secret: 1];
[query setSecret: 1];
XCTAssertTrue ([query isSecret], "instance property getter only 4");
}
@ -90,30 +90,108 @@
id super_unique_default_init = [[Constructors_SuperUnique alloc] init];
XCTAssert ([super_unique_default_init id] == 411, "super id");
// FIXME - this should not be allowed, that .ctor is not available to call in .NET as it is not re-declared in SuperUnique
id super_unique_init_id = [[Constructors_SuperUnique alloc] initWithId:42];
XCTAssert ([super_unique_init_id id] == 42, "id");
Constructors_Implicit* implicit = [[Constructors_Implicit alloc] init];
XCTAssertEqualObjects (@"OK", [implicit testResult], "implicit");
Constructors_AllTypeCode* all1 = [[Constructors_AllTypeCode alloc] initWithB1:true C2:USHRT_MAX S:@"Mono"];
Constructors_AllTypeCode* all1 = [[Constructors_AllTypeCode alloc] initWithB1:true c2:USHRT_MAX s:@"Mono"];
XCTAssertTrue ([all1 testResult], "all1");
Constructors_AllTypeCode* all2 = [[Constructors_AllTypeCode alloc] initWithI8:SCHAR_MAX I16:SHRT_MAX I32:INT_MAX I64:LONG_MAX];
Constructors_AllTypeCode* all2 = [[Constructors_AllTypeCode alloc] initWithI8:SCHAR_MAX i16:SHRT_MAX i32:INT_MAX i64:LONG_MAX];
XCTAssertTrue ([all2 testResult], "all2");
Constructors_AllTypeCode* all3 = [[Constructors_AllTypeCode alloc] initWithU8:UCHAR_MAX U16:USHRT_MAX U32:UINT_MAX U64:ULONG_MAX];
Constructors_AllTypeCode* all3 = [[Constructors_AllTypeCode alloc] initWithU8:UCHAR_MAX u16:USHRT_MAX u32:UINT_MAX u64:ULONG_MAX];
XCTAssertTrue ([all3 testResult], "all3");
Constructors_AllTypeCode* all4 = [[Constructors_AllTypeCode alloc] initWithF32:FLT_MAX F64:DBL_MAX];
Constructors_AllTypeCode* all4 = [[Constructors_AllTypeCode alloc] initWithF32:FLT_MAX f64:DBL_MAX];
XCTAssertTrue ([all4 testResult], "all4");
}
- (void)testMethods {
id static_method = [Methods_Static create: 1];
id static_method = [Methods_Static createId: 1];
XCTAssert ([static_method id] == 1, "create id");
XCTAssertNil ([Methods_Parameters concatFirst:nil second:nil], "string input nil + nil]");
XCTAssertEqualObjects (@"first", [Methods_Parameters concatFirst:@"first" second:nil], "string first + nil");
XCTAssertEqualObjects (@"second", [Methods_Parameters concatFirst:nil second:@"second"], "string nil + second");
XCTAssertEqualObjects (@"firstsecond", [Methods_Parameters concatFirst:@"first" second:@"second"], "string first + second");
bool b = true;
NSString* s = nil;
[Methods_Parameters refBoolean:&b string:&s];
XCTAssertFalse (b, "ref bool 1");
XCTAssertEqualObjects (@"hello", s, "ref string 1");
[Methods_Parameters refBoolean:&b string:&s];
XCTAssertTrue (b, "ref bool 2");
XCTAssertNil (s, "ref string 2");
int l;
[Methods_Parameters outString:nil length:&l upper:&s];
XCTAssert (l == 0, "out int 1");
XCTAssertNil (s, "out string 1");
[Methods_Parameters outString:@"Xamarin" length:&l upper:&s];
XCTAssert (l == 7, "out int 2");
XCTAssertEqualObjects (@"XAMARIN", s, "ref string 2");
id item = [Methods_Factory createItemId:1];
XCTAssert ([item integer] == 1, "indirect creation 1");
Methods_Collection *collection = [[Methods_Collection alloc] init];
XCTAssert ([collection count] == 0, "count 0");
[collection addItem:item];
XCTAssert ([collection count] == 1, "count 1");
XCTAssert ([[collection getItem:0] integer] == [item integer], "get 1");
id item2 = [Methods_Factory createItemId:2];
[collection setItem:0 value:item2];
XCTAssert ([collection count] == 1, "count 2");
XCTAssert ([[collection getItem:0] integer] == [item2 integer], "get 2");
[collection removeItem:item]; // not there
XCTAssert ([collection count] == 1, "count 3");
[collection removeItem:item2];
XCTAssert ([collection count] == 0, "count 4");
}
- (void) testStructs {
id p1 = [[Structs_Point alloc] initWithX:1.0f y:-1.0f];
XCTAssert ([p1 x] == 1.0f, "x 1");
XCTAssert ([p1 y] == -1.0f, "y 1");
id p2 = [[Structs_Point alloc] initWithX:2.0f y:-2.0f];
XCTAssert ([p2 x] == 2.0f, "x 2");
XCTAssert ([p2 y] == -2.0f, "y 2");
XCTAssert ([Structs_Point equality:p1 right:p1], "p1 == p1");
XCTAssert ([Structs_Point equality:p2 right:p2], "p2 == p2");
XCTAssert ([Structs_Point inequality:p1 right:p2], "p1 != p2");
id p3 = [Structs_Point addition:p1 right:p2];
XCTAssert ([p3 x] == 3.0f, "x 3");
XCTAssert ([p3 y] == -3.0f, "y 3");
id p4 = [Structs_Point subtraction:p3 right:p2];
XCTAssert ([Structs_Point equality:p4 right:p1], "p4 == p1");
id z = [Structs_Point zero];
XCTAssert ([z x] == 0.0f, "x 4");
XCTAssert ([z y] == 0.0f, "y 4");
}
- (void) testEnums {
Enums_IntEnum i = Enums_IntEnumMin;
Enums_ShortEnum s;
Enums_ByteFlags f = [Enums_Enumer testB:Enums_ByteEnumMax i:&i s:&s];
XCTAssert (f == 0x22, "return flag 1");
XCTAssert (i == Enums_IntEnumMax, "ref enum 1");
XCTAssert (s == Enums_ShortEnumMax, "out enum 1");
f = [Enums_Enumer testB:Enums_ByteEnumZero i:&i s:&s];
XCTAssert (i == Enums_IntEnumMin, "ref enum 2");
XCTAssert (s == Enums_ShortEnumMin, "out enum 2");
}
- (void)testStaticCallPerformance {

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

@ -29,8 +29,8 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
7E3455791E92B06100FCAD74 /* libmanaged.dylib.dSYM */ = {isa = PBXFileReference; lastKnownFileType = wrapper.dsym; name = libmanaged.dylib.dSYM; path = ../libmanaged.dylib.dSYM; sourceTree = "<group>"; };
7E5081F01E928C24008125B5 /* libmanaged.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libmanaged.dylib; path = ../libmanaged.dylib; sourceTree = "<group>"; };
7E3455791E92B06100FCAD74 /* libmanaged.dylib.dSYM */ = {isa = PBXFileReference; lastKnownFileType = wrapper.dsym; name = libmanaged.dylib.dSYM; path = ../debug/libmanaged.dylib.dSYM; sourceTree = "<group>"; };
7E5081F01E928C24008125B5 /* libmanaged.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libmanaged.dylib; path = ../debug/libmanaged.dylib; sourceTree = "<group>"; };
D1B0CD801E904EA6008FF17D /* managed.dll */ = {isa = PBXFileReference; lastKnownFileType = file; name = managed.dll; path = ../../../managed/bin/Debug/managed.dll; sourceTree = "<group>"; };
D1B0CD811E904EA6008FF17D /* managed.pdb */ = {isa = PBXFileReference; lastKnownFileType = file; name = managed.pdb; path = ../../../managed/bin/Debug/managed.pdb; sourceTree = "<group>"; };
D1E2B21F1E8F4FBB00846AF8 /* Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
@ -166,7 +166,7 @@
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "make -C .. libmanaged.dylib\n";
shellScript = "make -C .. debug/libmanaged.dylib\n";
};
/* End PBXShellScriptBuildPhase section */
@ -276,11 +276,11 @@
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = YES;
COMBINE_HIDPI_IMAGES = YES;
HEADER_SEARCH_PATHS = ..;
HEADER_SEARCH_PATHS = ../debug;
INFOPLIST_FILE = Tests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
LIBRARY_SEARCH_PATHS = (
..,
../debug,
"$(PROJECT_DIR)",
);
PRODUCT_BUNDLE_IDENTIFIER = com.xamarin.Tests;
@ -294,11 +294,11 @@
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = YES;
COMBINE_HIDPI_IMAGES = YES;
HEADER_SEARCH_PATHS = ..;
HEADER_SEARCH_PATHS = ../debug;
INFOPLIST_FILE = Tests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
LIBRARY_SEARCH_PATHS = (
..,
../debug,
"$(PROJECT_DIR)",
);
PRODUCT_BUNDLE_IDENTIFIER = com.xamarin.Tests;

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

@ -0,0 +1,56 @@
using System;
using System.IO;
using System.Runtime.InteropServices;
namespace Xamarin
{
// A class that creates temporary directories next to the test assembly, and cleans the output on startup
// Advantages:
// * The temporary directories are automatically cleaned on Wrench (unlike /tmp, which isn't)
// * The temporary directories stay after a test is run (until a new test run is started),
// which makes it easier to re-run (copy-paste) commands that failed.
public static class Cache // Not really a cache (since the root directory is cleaned in the cctor), but I couldn't come up with a better name.
{
static string root;
static int last_number;
static Cache ()
{
root = Path.Combine (Path.GetDirectoryName (System.Reflection.Assembly.GetExecutingAssembly ().Location), "tmp-test-dir");
if (Directory.Exists (root))
Directory.Delete (root, true);
Directory.CreateDirectory (root);
}
[DllImport ("libc", SetLastError = true)]
static extern int mkdir (string path, ushort mode);
public static string CreateTemporaryDirectory (string name = null)
{
if (string.IsNullOrEmpty (name)) {
var calling_method = new System.Diagnostics.StackFrame (1).GetMethod ();
if (calling_method != null) {
name = calling_method.DeclaringType.FullName + "." + calling_method.Name;
} else {
name = "unknown-test";
}
}
var rv = Path.Combine (root, name);
for (int i = last_number; i < 10000 + last_number; i++) {
// There's no way to know if Directory.CreateDirectory
// created the directory or not (which would happen if the directory
// already existed). Checking if the directory exists before
// creating it would result in a race condition if multiple
// threads create temporary directories at the same time.
if (mkdir (rv, Convert.ToUInt16 ("777", 8)) == 0) {
last_number = i;
return rv;
}
rv = Path.Combine (root, name + i);
}
throw new Exception ("Could not create temporary directory");
}
}
}

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

@ -14,11 +14,12 @@ namespace DriverTest {
Random rnd = new Random ();
string valid = Path.Combine (Path.GetTempPath (), "output-" + rnd.Next ().ToString ());
try {
Driver.OutputDirectory = valid;
var embedder = new Embedder ();
embedder.OutputDirectory = valid;
Assert.That (Directory.Exists (valid), "valid");
try {
Driver.OutputDirectory = "/:output";
embedder.OutputDirectory = "/:output";
Assert.Fail ("invalid");
} catch (EmbeddinatorException ee) {
Assert.True (ee.Error, "Error");
@ -34,8 +35,9 @@ namespace DriverTest {
{
var valid = new string [] { "ObjC", "Obj-C", "ObjectiveC", "objective-c" };
foreach (var t in valid) {
Driver.SetTarget (t);
Assert.That (Driver.TargetLanguage, Is.EqualTo (TargetLanguage.ObjectiveC), t);
var embedder = new Embedder ();
embedder.SetTarget (t);
Assert.That (embedder.TargetLanguage, Is.EqualTo (TargetLanguage.ObjectiveC), t);
}
}
@ -45,7 +47,8 @@ namespace DriverTest {
var notsupported = new string [] { "C", "C++", "Java" };
foreach (var t in notsupported) {
try {
Driver.SetTarget (t);
var embedder = new Embedder ();
embedder.SetTarget (t);
}
catch (EmbeddinatorException ee) {
Assert.True (ee.Error, "Error");
@ -58,7 +61,8 @@ namespace DriverTest {
public void Target_Invalid ()
{
try {
Driver.SetTarget ("invalid");
var embedder = new Embedder ();
embedder.SetTarget ("invalid");
}
catch (EmbeddinatorException ee) {
Assert.True (ee.Error, "Error");
@ -71,23 +75,27 @@ namespace DriverTest {
{
var valid = new string [] { "OSX", "MacOSX", "MacOS", "Mac" };
foreach (var p in valid) {
Driver.SetPlatform (p);
Assert.That (Driver.Platform, Is.EqualTo (Platform.macOS), p);
var embedder = new Embedder ();
embedder.SetPlatform (p);
Assert.That (embedder.Platform, Is.EqualTo (Platform.macOS), p);
}
foreach (var p in new string [] { "ios", "iOS" }) {
Driver.SetPlatform (p);
Assert.That (Driver.Platform, Is.EqualTo (Platform.iOS), p);
var embedder = new Embedder ();
embedder.SetPlatform (p);
Assert.That (embedder.Platform, Is.EqualTo (Platform.iOS), p);
}
foreach (var p in new string [] { "tvos", "tvOS" }) {
Driver.SetPlatform (p);
Assert.That (Driver.Platform, Is.EqualTo (Platform.tvOS), p);
var embedder = new Embedder ();
embedder.SetPlatform (p);
Assert.That (embedder.Platform, Is.EqualTo (Platform.tvOS), p);
}
foreach (var p in new string [] { "watchos", "watchOS" }) {
Driver.SetPlatform (p);
Assert.That (Driver.Platform, Is.EqualTo (Platform.watchOS), p);
var embedder = new Embedder ();
embedder.SetPlatform (p);
Assert.That (embedder.Platform, Is.EqualTo (Platform.watchOS), p);
}
}
@ -97,7 +105,8 @@ namespace DriverTest {
var notsupported = new string [] { "Windows", "Android", "iOS", "tvOS", "watchOS" };
foreach (var p in notsupported) {
try {
Driver.SetPlatform (p);
var embedder = new Embedder ();
embedder.SetPlatform (p);
} catch (EmbeddinatorException ee) {
Assert.True (ee.Error, "Error");
Assert.That (ee.Code, Is.EqualTo (3), "Code");
@ -109,7 +118,8 @@ namespace DriverTest {
public void Platform_Invalid ()
{
try {
Driver.SetPlatform ("invalid");
var embedder = new Embedder ();
embedder.SetPlatform ("invalid");
} catch (EmbeddinatorException ee) {
Assert.True (ee.Error, "Error");
Assert.That (ee.Code, Is.EqualTo (3), "Code");
@ -141,21 +151,33 @@ namespace DriverTest {
[Test]
public void CompilationTarget ()
{
var embedder = new Embedder ();
Asserts.ThrowsEmbeddinatorException (5, "The compilation target `invalid` is not valid.", () => Driver.Main2 (new [] { "--target=invalid" }));
Asserts.ThrowsEmbeddinatorException (5, "The compilation target `invalid` is not valid.", () => Driver.SetCompilationTarget ("invalid"));
Asserts.ThrowsEmbeddinatorException (5, "The compilation target `invalid` is not valid.", () => embedder.SetCompilationTarget ("invalid"));
foreach (var ct in new string [] { "library", "sharedlibrary", "dylib" }) {
Driver.SetCompilationTarget (ct);
Assert.That (Driver.CompilationTarget, Is.EqualTo (global::Embeddinator.CompilationTarget.SharedLibrary), ct);
embedder.SetCompilationTarget (ct);
Assert.That (embedder.CompilationTarget, Is.EqualTo (global::Embeddinator.CompilationTarget.SharedLibrary), ct);
}
foreach (var ct in new string [] { "framework" }) {
Driver.SetCompilationTarget (ct);
Assert.That (Driver.CompilationTarget, Is.EqualTo (global::Embeddinator.CompilationTarget.Framework), ct);
embedder.SetCompilationTarget (ct);
Assert.That (embedder.CompilationTarget, Is.EqualTo (global::Embeddinator.CompilationTarget.Framework), ct);
}
foreach (var ct in new string [] { "static", "staticlibrary" }) {
Driver.SetCompilationTarget (ct);
Assert.That (Driver.CompilationTarget, Is.EqualTo (global::Embeddinator.CompilationTarget.StaticLibrary), ct);
embedder.SetCompilationTarget (ct);
Assert.That (embedder.CompilationTarget, Is.EqualTo (global::Embeddinator.CompilationTarget.StaticLibrary), ct);
}
}
[Test]
public void ABI ()
{
Driver.Main2 (new string [] { "--abi=armv7" });
CollectionAssert.AreEquivalent (new string [] { "armv7" }, Driver.CurrentEmbedder.ABIs, "armv7");
// We don't validate when setting the option
Driver.Main2 (new string [] { "--abi=any" });
CollectionAssert.AreEquivalent (new string [] { "any" }, Driver.CurrentEmbedder.ABIs, "any");
}
}
}

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

@ -0,0 +1,73 @@
using System;
using System.IO;
using NUnit.Framework;
using Embeddinator;
namespace DriverTest
{
[TestFixture]
public class EmbedderTest
{
[Test]
[TestCase (Platform.iOS, "armv7k")]
[TestCase (Platform.macOS, "armv7")]
[TestCase (Platform.tvOS, "armv7")]
[TestCase (Platform.watchOS, "arm64")]
[TestCase (Platform.iOS, "invalid")]
public void Invalid_ABI (Platform platform, string abi)
{
var dll = CompileLibrary (platform);
string valid;
switch (platform) {
case Platform.iOS:
valid = "armv7, armv7s, arm64, i386, x86_64";
break;
case Platform.macOS:
valid = "i386, x86_64";
break;
case Platform.tvOS:
valid = "arm64, x86_64";
break;
case Platform.watchOS:
valid = "armv7k, i386";
break;
default:
throw new NotImplementedException ();
}
Asserts.ThrowsEmbeddinatorException (8, $"The architecture '{abi}' is not valid for {platform}. Valid architectures for {platform} are: {valid}", () => Driver.Main2 ("--platform", platform.ToString (), "--abi", abi, "-c", dll, "-o", Xamarin.Cache.CreateTemporaryDirectory ()));
}
[Test]
[TestCase (Platform.macOS, "x86_64")]
public void ABI (Platform platform, string abi)
{
var dll = CompileLibrary (platform);
var tmpdir = Xamarin.Cache.CreateTemporaryDirectory ();
Driver.Main2 ("--platform", platform.ToString (), "--abi", abi, "-c", dll, "-o", tmpdir);
string output;
int exitCode;
Assert.IsTrue (Embedder.RunProcess ("xcrun", $"lipo -info {tmpdir}/libLibrary.dylib", out exitCode, out output), "lipo");
StringAssert.IsMatch ($"Non-fat file: .* is architecture: {abi}", output, "architecture");
}
string CompileLibrary (Platform platform, string code = null)
{
int exitCode;
var tmpdir = Xamarin.Cache.CreateTemporaryDirectory ();
var cs_path = Path.Combine (tmpdir, "library.cs");
var dll_path = Path.Combine (tmpdir, "library.dll");
if (code == null)
code = "public class Test { public static void X () {} }";
File.WriteAllText (cs_path, code);
if (!Embedder.RunProcess ("/Library/Frameworks/Mono.framework/Versions/Current/bin/csc", $"/target:library {Embedder.Quote (cs_path)} /out:{Embedder.Quote (dll_path)}", out exitCode))
Assert.Fail ("Failed to compile test code");
return dll_path;
}
}
}

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

@ -0,0 +1,33 @@
using NUnit.Framework;
using System;
using ObjC;
using Embeddinator;
using IKVM.Reflection;
using Type = IKVM.Reflection.Type;
namespace ObjCGeneratorTest {
[TestFixture]
public class ExtensionsTest {
public static Universe Universe { get; } = new Universe (UniverseOptions.None);
public static Assembly mscorlib { get; } = Universe.Load ("mscorlib.dll");
[Test]
public void Is ()
{
Assert.True (mscorlib.GetType ("System.Void").Is ("System", "Void"), "void");
Assert.False (mscorlib.GetType ("System.Object").Is ("System", "Void"), "object");
}
[Test]
public void HasAttribute ()
{
var fa = mscorlib.GetType ("System.IO.FileAccess");
Assert.True (fa.HasCustomAttribute ("System", "FlagsAttribute"), "Has");
Assert.False (fa.HasCustomAttribute ("System", "Flags"), "Has Not");
}
}
}

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

@ -47,7 +47,7 @@ namespace ObjCGeneratorTest {
Assert.That (ObjCGenerator.GetTypeName (mscorlib.GetType ("System.UInt64")), Is.EqualTo ("unsigned long long"), "ulong");
Assert.That (ObjCGenerator.GetTypeName (mscorlib.GetType ("System.Single")), Is.EqualTo ("float"), "float");
Assert.That (ObjCGenerator.GetTypeName (mscorlib.GetType ("System.Double")), Is.EqualTo ("double"), "double");
Assert.That (ObjCGenerator.GetTypeName (mscorlib.GetType ("System.String")), Is.EqualTo ("NSString*"), "string");
Assert.That (ObjCGenerator.GetTypeName (mscorlib.GetType ("System.String")), Is.EqualTo ("NSString *"), "string");
Assert.That (ObjCGenerator.GetTypeName (mscorlib.GetType ("System.Object")), Is.EqualTo ("NSObject"), "object");
Assert.That (ObjCGenerator.GetTypeName (mscorlib.GetType ("System.Void")), Is.EqualTo ("void"), "void");
}
@ -78,13 +78,6 @@ namespace ObjCGeneratorTest {
Assert.That (ObjCGenerator.GetMonoName (mscorlib.GetType ("System.Object")), Is.EqualTo ("object"), "object");
}
[Test]
public void IsVoid ()
{
Assert.True (ObjCGenerator.IsVoid (mscorlib.GetType ("System.Void")), "void");
Assert.False (ObjCGenerator.IsVoid (mscorlib.GetType ("System.Object")), "object");
}
[Test]
public void GetObjCName ()
{

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

@ -34,6 +34,9 @@
<Compile Include="ObjCGeneratorTest.cs" />
<Compile Include="DriverTest.cs" />
<Compile Include="Asserts.cs" />
<Compile Include="EmbedderTest.cs" />
<Compile Include="Cache.cs" />
<Compile Include="ExtensionsTest.cs" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />