Browse Source

[objc] Added initial function call and tests

Cauê Waneck 10 years ago
parent
commit
d33d14657b

+ 2 - 1
ast.ml

@@ -130,6 +130,7 @@ module Meta = struct
 		| NotNull
 		| NotNull
 		| NoUsing
 		| NoUsing
 		| Ns
 		| Ns
+		| Objc
 		| Op
 		| Op
 		| Optional
 		| Optional
 		| Overload
 		| Overload
@@ -910,4 +911,4 @@ let full_dot_path mpath tpath =
 	if mpath = tpath then
 	if mpath = tpath then
 		(fst tpath) @ [snd tpath]
 		(fst tpath) @ [snd tpath]
 	else
 	else
-		(fst mpath) @ [snd mpath;snd tpath]
+		(fst mpath) @ [snd mpath;snd tpath]

+ 1 - 0
common.ml

@@ -453,6 +453,7 @@ module MetaInfo = struct
 		| NotNull -> ":notNull",("Declares an abstract type as not accepting null values",[UsedOn TAbstract])
 		| NotNull -> ":notNull",("Declares an abstract type as not accepting null values",[UsedOn TAbstract])
 		| NoUsing -> ":noUsing",("Prevents a field from being used with 'using'",[UsedOn TClassField])
 		| NoUsing -> ":noUsing",("Prevents a field from being used with 'using'",[UsedOn TClassField])
 		| Ns -> ":ns",("Internally used by the Swf generator to handle namespaces",[Platform Flash])
 		| Ns -> ":ns",("Internally used by the Swf generator to handle namespaces",[Platform Flash])
+		| Objc -> ":objc",("Declares a class or interface that is used to interoperate with Objective-C code",[Platform Cpp;UsedOn TClass])
 		| Op -> ":op",("Declares an abstract field as being an operator overload",[HasParam "The operation";UsedOn TAbstractField])
 		| Op -> ":op",("Declares an abstract field as being an operator overload",[HasParam "The operation";UsedOn TAbstractField])
 		| Optional -> ":optional",("Marks the field of a structure as optional",[UsedOn TClassField])
 		| Optional -> ":optional",("Marks the field of a structure as optional",[UsedOn TClassField])
 		| Overload -> ":overload",("Allows the field to be called with different argument types",[HasParam "Function specification (no expression)";UsedOn TClassField])
 		| Overload -> ":overload",("Allows the field to be called with different argument types",[HasParam "Function specification (no expression)";UsedOn TClassField])

+ 47 - 0
gencpp.ml

@@ -562,6 +562,13 @@ let is_fromStaticFunction_call func =
    | _ -> false
    | _ -> false
 ;;
 ;;
 
 
+let is_objc_call field =
+  match field with
+  | FStatic(cl,_) | FInstance(cl,_,_) ->
+      cl.cl_extern && Meta.has Meta.Objc cl.cl_meta
+  | _ -> false
+;;
+
 let is_addressOf_call func =
 let is_addressOf_call func =
    match (remove_parens func).eexpr with
    match (remove_parens func).eexpr with
    | TField (_,FStatic ({cl_path=["cpp"],"Pointer"},{cf_name="addressOf"} ) ) -> true
    | TField (_,FStatic ({cl_path=["cpp"],"Pointer"},{cf_name="addressOf"} ) ) -> true
@@ -645,6 +652,13 @@ let rec class_string klass suffix params remap =
             | t when type_has_meta_key t Meta.NotNull -> "Dynamic"
             | t when type_has_meta_key t Meta.NotNull -> "Dynamic"
             | _ -> "/*NULL*/" ^ (type_string t) )
             | _ -> "/*NULL*/" ^ (type_string t) )
          | _ -> assert false);
          | _ -> assert false);
+   (* Objective-C class *)
+   | path when klass.cl_extern && Meta.has Meta.Objc klass.cl_meta ->
+     let str = join_class_path_remap klass.cl_path "::" in
+     if suffix = "_obj" then
+       str
+     else
+       str ^ " *"
    (* Normal class *)
    (* Normal class *)
    | path when klass.cl_extern && (not (is_internal_class path) )->
    | path when klass.cl_extern && (not (is_internal_class path) )->
             (join_class_path_remap klass.cl_path "::") ^ suffix
             (join_class_path_remap klass.cl_path "::") ^ suffix
@@ -2030,6 +2044,39 @@ and gen_expression ctx retval expression =
             output (" )");
             output (" )");
          | _ -> error "fromStaticFunction must take a static function" expression.epos;
          | _ -> error "fromStaticFunction must take a static function" expression.epos;
       )
       )
+    | TCall ({ eexpr = TField(fexpr,field) }, arg_list) when is_objc_call field ->
+      output "[ ";
+      (match field with
+      | FStatic(cl,_) ->
+          output (join_class_path_remap cl.cl_path "::")
+      | FInstance _ ->
+          gen_expression ctx true fexpr
+      | _ -> assert false);
+      let names = ExtString.String.nsplit (field_name field) ":" in
+      let field_name, arg_names = match names with
+        | name :: args -> name, args
+        | _ -> assert false (* per nsplit specs, this should never happen *)
+      in
+      output (" " ^ field_name);
+      (try match arg_list, arg_names with
+      | [], _ -> ()
+      | [single_arg], _ -> output ": "; gen_expression ctx true single_arg
+      | first_arg :: args, _ :: arg_names ->
+          gen_expression ctx true first_arg;
+          ctx.ctx_calling <- true;
+          List.iter2 (fun arg arg_name ->
+            output (", " ^ arg_name ^ ": ");
+            gen_expression ctx true arg) args arg_names
+      | _ -> raise Exit
+      with | Invalid_argument _ | Exit -> (* not all arguments names are known *)
+        error (
+          "The function called here with name " ^ (String.concat ":" names) ^
+          " does not contain the right amount of arguments' names as required" ^
+          " by the objective-c calling / naming convention:" ^
+          " expected " ^ (string_of_int (List.length arg_list)) ^
+          " and found " ^ (string_of_int (List.length arg_names)))
+        expression.epos);
+      output " ]"
 
 
    | TCall (func, [arg]) when is_addressOf_call func && not (is_lvalue arg) ->
    | TCall (func, [arg]) when is_addressOf_call func && not (is_lvalue arg) ->
       error "addressOf must take a local or member variable" expression.epos;
       error "addressOf must take a local or member variable" expression.epos;

+ 7 - 0
tests/RunCi.hx

@@ -616,6 +616,13 @@ class RunCi {
 					changeDirectory(sysDir);
 					changeDirectory(sysDir);
 					runCommand("haxe", ["compile-cpp.hxml"]);
 					runCommand("haxe", ["compile-cpp.hxml"]);
 					runCpp("bin/cpp/Main-debug", []);
 					runCpp("bin/cpp/Main-debug", []);
+
+					if (Sys.systemName() == "Mac")
+					{
+						changeDirectory(miscDir + "cppObjc");
+						runCommand("haxe", ["build.hxml"]);
+						runCpp("bin/TestObjc-debug");
+					}
 				case Js:
 				case Js:
 					getJSDependencies();
 					getJSDependencies();
 
 

+ 1 - 0
tests/misc/cppObjc/.gitignore

@@ -0,0 +1 @@
+bin

+ 84 - 0
tests/misc/cppObjc/TestObjc.hx

@@ -0,0 +1,84 @@
+class TestObjc extends haxe.unit.TestCase
+{
+	static function main()
+	{
+		var runner = new haxe.unit.TestRunner();
+		runner.add(new TestObjc());
+		var code = runner.run() ? 0 : 1;
+		Sys.exit(code);
+	}
+
+	public function testCall()
+	{
+		var cls = TestClass.alloc().init();
+		assertEquals(cls.getOtherThing(), 0);
+		cls.setOtherThing(42);
+		assertEquals(cls.getOtherThing(), 42);
+		assertEquals(cls.isBiggerThan10(2), false);
+		assertEquals(cls.isBiggerThan10(12), true);
+		assertEquals(cls.isBiggerThan10Int(3), false);
+		assertEquals(cls.isBiggerThan10Int(14), true);
+		assertEquals(cls.addHello("World"), "Hello, World");
+		cls.something = " test";
+		assertEquals(cls.something, " test");
+		assertEquals(cls.addSomething("Hey,"), "Hey, test");
+	}
+}
+
+@:include("./native/include/test.h")
+@:sourceFile("./native/test.m")
+@:objc extern class TestClass
+{
+	static function aStatic():Int;
+
+	static function alloc():TestClass;
+	function init():TestClass;
+
+	var something(get,set):NSString;
+
+	@:native("something") private function get_something():NSString;
+	@:native("setSomething") private function set_something(value:NSString):NSString;
+
+	function setOtherThing(value:Int):Void;
+	function getOtherThing():Int;
+	function getOtherThingChar():cpp.Int8;
+	function addHello(str:NSString):NSString;
+	function addSomething(str:NSString):NSString;
+	function isBiggerThan10(value:NSNumber):Bool;
+	function isBiggerThan10Num(value:NSNumber):NSNumber;
+	function isBiggerThan10Int(integer:Int):Bool;
+
+	@:plain static function some_c_call(t:TestClass):Int;
+	@:plain static function is_bigger_than_10(t:TestClass, val:Int):Bool;
+}
+
+@:forward abstract NSString(_NSString) from _NSString to _NSString
+{
+	@:from @:extern inline public static function fromString(str:String):NSString
+		return _NSString.stringWithUTF8String(str);
+
+	@:to @:extern inline public function toString():String
+		return this.UTF8String();
+}
+
+@:native("NSString") @:objc extern class _NSString
+{
+	static function stringWithUTF8String(str:cpp.CastCharStar):NSString;
+
+	function UTF8String():cpp.ConstCharStar;
+}
+
+@:forward abstract NSNumber(_NSNumber) from _NSNumber to _NSNumber
+{
+	@:from @:extern inline public static function fromInt(i:Int):NSNumber
+		return _NSNumber.numberWithInt(i);
+
+	@:to @:extern inline public function toInt():Int
+		return this.intValue();
+}
+
+@:native("NSNumber") @:objc extern class _NSNumber
+{
+	static function numberWithInt(i:Int):NSNumber;
+	function intValue():Int;
+}

+ 4 - 0
tests/misc/cppObjc/build.hxml

@@ -0,0 +1,4 @@
+-main TestObjc
+-cpp bin
+-D file_extension=mm
+-debug

+ 2 - 0
tests/misc/cppObjc/native/.gitignore

@@ -0,0 +1,2 @@
+*.o
+*.a

+ 20 - 0
tests/misc/cppObjc/native/Makefile

@@ -0,0 +1,20 @@
+CC=clang
+
+SOURCES=$(wildcard *.m)
+OBJECTS=$(SOURCES:.m=.o)
+
+libtest.a : $(OBJECTS)
+	$(AR) rcs libtest.a $(OBJECTS)
+	ranlib libtest.a
+
+%.c.o : %.c $(C_DEPS)
+	$(VERBOSE)$(CC) $(CFLAGS) -o $@ -c $<
+	 
+%.cpp.o : %.cpp $(C_DEPS)
+	$(VERBOSE)$(CXX) $(CXXFLAGS) -o $@ -c $<
+	 
+%.m.o : %.m $(C_DEPS)
+	$(VERBOSE)$(CXX) $(CFLAGS) -o $@ -c $<
+	 
+%.mm.o : %.mm $(C_DEPS)
+	$(VERBOSE)$(CXX) $(CXXFLAGS) -o $@ -c $< 

+ 31 - 0
tests/misc/cppObjc/native/include/test.h

@@ -0,0 +1,31 @@
+#import <Foundation/Foundation.h>
+
+@interface TestClass : NSObject {
+	@public int otherThing;
+}
+
+@property (retain) NSString *something;
+
++ (int)aStatic;
+
+- (void)setOtherThing:(int) value;
+
+- (int)getOtherThing;
+
+- (char)getOtherThingChar;
+
+- (NSString *)addHello:(NSString *)str;
+
+- (NSString *)addSomething:(NSString *)str;
+
+- (BOOL)isBiggerThan10:(NSNumber *)value;
+
+- (NSNumber *)isBiggerThan10Num:(NSNumber *)value;
+
+- (BOOL)isBiggerThan10Int:(int)integer;
+
+@end
+
+int some_c_call(TestClass *t);
+
+BOOL is_bigger_than_10(TestClass *t, int val);

+ 62 - 0
tests/misc/cppObjc/native/test.m

@@ -0,0 +1,62 @@
+#import "include/test.h"
+
+@implementation TestClass
+
+@synthesize something = _something;
+
++ (int)aStatic
+{
+	return 42;
+}
+
+- (void)setOtherThing:(int) value
+{
+	self->otherThing = value;
+}
+
+- (int)getOtherThing
+{
+	return self->otherThing;
+}
+
+- (char)getOtherThingChar
+{
+	return (char) self->otherThing;
+}
+
+- (NSString *)addHello:(NSString *)str
+{
+	return [@"Hello, " stringByAppendingString: str];
+}
+
+- (NSString *)addSomething:(NSString *)str
+{
+	return [str stringByAppendingString: self->_something];
+}
+
+- (BOOL)isBiggerThan10:(NSNumber *)value
+{
+	return [value doubleValue] > 10;
+}
+
+- (NSNumber *)isBiggerThan10Num:(NSNumber *)value
+{
+	return [NSNumber numberWithBool:[value doubleValue] > 10];
+}
+
+- (BOOL)isBiggerThan10Int:(int)integer
+{
+	return integer > 10;
+}
+
+@end
+
+int some_c_call(TestClass *t)
+{
+	return [t getOtherThing] + 10;
+}
+
+BOOL is_bigger_than_10(TestClass *t, int val)
+{
+	return [t isBiggerThan10Int: val];
+}