diff --git a/.gitignore b/.gitignore index 136d405..cb677c6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ target/ .idea/ _site/ +.bsp/ diff --git a/build.sbt b/build.sbt index 65748b3..e9080ad 100644 --- a/build.sbt +++ b/build.sbt @@ -1,23 +1,25 @@ -organization in ThisBuild := "de.surfice" +ThisBuild / organization := "de.surfice" -version in ThisBuild := "0.0.2-SNAPSHOT" +ThisBuild / version := "0.0.2-SNAPSHOT" -scalaVersion in ThisBuild := "2.11.12" +ThisBuild / scalaVersion := "2.13.8" val Version = new { val slogging = "0.5.3" - val objc = "0.0.5-SNAPSHOT" + val objc = "0.0.6" val utest = "0.6.3" } lazy val commonSettings = Seq( scalacOptions ++= Seq("-deprecation","-unchecked","-feature","-language:implicitConversions","-Xlint"), - addCompilerPlugin("org.scalamacros" % "paradise" % "2.1.0" cross CrossVersion.full), + //addCompilerPlugin("org.scalamacros" % "paradise" % "2.1.0" cross CrossVersion.full), + scalacOptions += "-Ymacro-annotations", libraryDependencies ++= Seq( - "de.surfice" %%% "scalanative-interop-objc" % Version.objc, - "com.lihaoyi" %%% "utest" % Version.utest % "test" - ), + "org.scala-lang" % "scala-reflect" % scalaVersion.value + //"de.surfice" %%% "scalanative-interop-objc" % Version.objc, + //"com.lihaoyi" %%% "utest" % Version.utest % "test" + ), testFrameworks += new TestFramework("utest.runner.Framework") ) @@ -31,10 +33,20 @@ lazy val cocoa = project.in(file(".")) .settings(dontPublish:_*) .settings( name := "sncocoa" + ) + +lazy val objc = project + .enablePlugins(ScalaNativePlugin) + .settings(commonSettings ++ nativeSettings ++ publishingSettings:_*) + .settings( + libraryDependencies ++= Seq( + "de.surfice" %% "smacrotools" % "0.0.9-SNAPSHOT" ) + ) lazy val foundation = project .enablePlugins(ScalaNativePlugin) + .dependsOn(objc) .settings(commonSettings ++ nativeSettings ++ publishingSettings:_*) .settings( name := "scalanative-cocoa-foundation" @@ -78,8 +90,8 @@ lazy val test = project lazy val dontPublish = Seq( publish := {}, publishLocal := {}, - com.typesafe.sbt.pgp.PgpKeys.publishSigned := {}, - com.typesafe.sbt.pgp.PgpKeys.publishLocalSigned := {}, + com.jsuereth.sbtpgp.PgpKeys.publishSigned := {}, + com.jsuereth.sbtpgp.PgpKeys.publishLocalSigned := {}, publishArtifact := false, publishTo := Some(Resolver.file("Unused transient repository",file("target/unusedrepo"))) ) @@ -114,4 +126,3 @@ lazy val publishingSettings = Seq( ) ) - diff --git a/foundation/src/main/scala/cocoa/foundation/Foundation.scala b/foundation/src/main/scala/cocoa/foundation/Foundation.scala index 08c6b26..6864a2d 100644 --- a/foundation/src/main/scala/cocoa/foundation/Foundation.scala +++ b/foundation/src/main/scala/cocoa/foundation/Foundation.scala @@ -4,6 +4,7 @@ package cocoa.foundation import scala.scalanative.native._ +import scala.scalanative.unsafe._ import objc.runtime._ import cocoa.foundation._ @@ -29,6 +30,6 @@ object Foundation { // def CFBridgingRetain_(X: id): CFTypeRef = extern // def CFBridgingRelease_(X: CFTypeRef): id = extern - def NSLog(format: NSString, args: CVararg*): Unit = extern + def NSLog(format: NSString, args: CVarArg*): Unit = extern def NXReadNSObjectFromCoder(decoder: NSCoder): NSObject = extern } diff --git a/foundation/src/main/scala/cocoa/foundation/Macros.scala b/foundation/src/main/scala/cocoa/foundation/Macros.scala index 080f3dc..0645795 100644 --- a/foundation/src/main/scala/cocoa/foundation/Macros.scala +++ b/foundation/src/main/scala/cocoa/foundation/Macros.scala @@ -18,7 +18,7 @@ private[this] class Macros(val c: blackbox.Context) extends BlackboxMacroTools w def nsquoteImpl() = { val expr = c.prefix match { - case Expr(Apply(x,args)) => q"scalanative.native.CQuote(..$args).c()" + case Expr(Apply(x,args)) => q"scalanative.unsafe.CQuote(..$args).c()" } c.Expr(q"cocoa.foundation.NSString($expr)") } @@ -35,7 +35,7 @@ private[this] class Macros(val c: blackbox.Context) extends BlackboxMacroTools w q"""import objc.runtime._ import objc.helper._ val sel = sel_registerName(${cstring(genSelectorString(method))}) - $callSuper.cast[${method.returnType}] + $callSuper.asInstanceOf[${method.returnType}] """ } t diff --git a/foundation/src/main/scala/cocoa/foundation/NSArray.scala b/foundation/src/main/scala/cocoa/foundation/NSArray.scala index 1268652..5272e66 100644 --- a/foundation/src/main/scala/cocoa/foundation/NSArray.scala +++ b/foundation/src/main/scala/cocoa/foundation/NSArray.scala @@ -5,6 +5,8 @@ import de.surfice.smacrotools.debug import scala.language.experimental.macros import scalanative.native._ +import scalanative.unsafe._ +import scalanative.unsigned._ import objc._ @@ -80,7 +82,7 @@ object NSArray extends NSArrayClass { // TODO: use Iterable instead of Seq def arrayWithObjects[T<:NSObject](objects: Seq[T]): NSArray[T] = Zone { implicit z => val count = objects.size - val array = stackalloc[id]( sizeof[id] * count) + val array = stackalloc[id]( sizeof[id] * count.toUInt) for(i<-0 until count) !(array + i) = objects(i).toPtr arrayWithObjects_count_(array,count.toULong) @@ -93,4 +95,4 @@ object NSArray extends NSArrayClass { def apply(idx: Int): T = ns.objectAtIndex_(idx.toUInt) } -} \ No newline at end of file +} diff --git a/foundation/src/main/scala/cocoa/foundation/NSCharacterSet.scala b/foundation/src/main/scala/cocoa/foundation/NSCharacterSet.scala index eb45e0a..cdf72ab 100644 --- a/foundation/src/main/scala/cocoa/foundation/NSCharacterSet.scala +++ b/foundation/src/main/scala/cocoa/foundation/NSCharacterSet.scala @@ -3,6 +3,7 @@ package cocoa.foundation import scala.language.experimental.macros import scalanative.native._ +import scalanative.unsafe._ import objc._ import scala.scalanative.posix.inttypes.uint8_t @@ -50,4 +51,4 @@ abstract class NSCharacterSetClass extends NSObjectClass { object NSCharacterSet extends NSCharacterSetClass { override type InstanceType = NSCharacterSet -} \ No newline at end of file +} diff --git a/foundation/src/main/scala/cocoa/foundation/NSCoder.scala b/foundation/src/main/scala/cocoa/foundation/NSCoder.scala index 4c3f73a..7869d91 100644 --- a/foundation/src/main/scala/cocoa/foundation/NSCoder.scala +++ b/foundation/src/main/scala/cocoa/foundation/NSCoder.scala @@ -3,6 +3,7 @@ package cocoa.foundation import scala.language.experimental.macros import scalanative.native._ +import scalanative.unsafe._ import objc._ import scala.scalanative.native.objc.runtime.{int32_t, int64_t} import scala.scalanative.posix.inttypes.uint8_t diff --git a/foundation/src/main/scala/cocoa/foundation/NSCoding.scala b/foundation/src/main/scala/cocoa/foundation/NSCoding.scala index 40e4368..fd445c6 100644 --- a/foundation/src/main/scala/cocoa/foundation/NSCoding.scala +++ b/foundation/src/main/scala/cocoa/foundation/NSCoding.scala @@ -5,10 +5,9 @@ package cocoa.foundation import scalanative.native._ +import scalanative.unsafe._ import objc._ -import scala.scalanative.native.extern - @ObjC trait NSCoding { @inline def encodeWithCoder_(aCoder: NSCoder): Unit = extern diff --git a/foundation/src/main/scala/cocoa/foundation/NSCopying.scala b/foundation/src/main/scala/cocoa/foundation/NSCopying.scala index e2bdd0d..530fadc 100644 --- a/foundation/src/main/scala/cocoa/foundation/NSCopying.scala +++ b/foundation/src/main/scala/cocoa/foundation/NSCopying.scala @@ -5,6 +5,7 @@ package cocoa.foundation import scalanative.native._ +import scalanative.unsafe._ import objc._ @ObjC diff --git a/foundation/src/main/scala/cocoa/foundation/NSData.scala b/foundation/src/main/scala/cocoa/foundation/NSData.scala index d79c2a3..cb0e7e5 100644 --- a/foundation/src/main/scala/cocoa/foundation/NSData.scala +++ b/foundation/src/main/scala/cocoa/foundation/NSData.scala @@ -5,6 +5,7 @@ package cocoa.foundation import scalanative.native._ +import scalanative.unsafe._ import objc._ import scala.language.experimental.macros @@ -58,4 +59,4 @@ abstract class NSDataClass extends NSObjectClass { object NSData extends NSDataClass { override type InstanceType = NSData -} \ No newline at end of file +} diff --git a/foundation/src/main/scala/cocoa/foundation/NSDate.scala_ b/foundation/src/main/scala/cocoa/foundation/NSDate.scala_ index b645a2c..5ce6a1c 100644 --- a/foundation/src/main/scala/cocoa/foundation/NSDate.scala_ +++ b/foundation/src/main/scala/cocoa/foundation/NSDate.scala_ @@ -1,6 +1,7 @@ package cocoa.foundation import scalanative.native._ +import scalanative.unsafe._ import scala.language.experimental.macros @@ -40,4 +41,4 @@ abstract class NSDateClass extends NSObjectClass { object NSDate extends NSDateClass { override type InstanceType = NSDate -} \ No newline at end of file +} diff --git a/foundation/src/main/scala/cocoa/foundation/NSDictionary.scala b/foundation/src/main/scala/cocoa/foundation/NSDictionary.scala index ea56cda..eeb6da7 100644 --- a/foundation/src/main/scala/cocoa/foundation/NSDictionary.scala +++ b/foundation/src/main/scala/cocoa/foundation/NSDictionary.scala @@ -2,6 +2,8 @@ package cocoa.foundation import scala.scalanative.native._ +import scala.scalanative.unsafe._ +import scala.scalanative.unsigned._ import objc._ @@ -63,8 +65,8 @@ object NSDictionary extends NSDictionaryClass { // TODO: use Iterable instead of Seq? def dictionaryWithObjects[K<:NSObject, V<:NSObject](objects: Seq[(K,V)]): NSDictionary[K,V] = Zone { implicit z => val count = objects.size - val objArray = stackalloc[id]( sizeof[id] * count) - val keyArray = stackalloc[id]( sizeof[id] * count ) + val objArray = stackalloc[id]( sizeof[id] * count.toUInt) + val keyArray = stackalloc[id]( sizeof[id] * count.toUInt ) for(i<-0 until count) { !(keyArray + i) = objects(i)._1.toPtr !(objArray + i) = objects(i)._2.toPtr @@ -74,4 +76,4 @@ object NSDictionary extends NSDictionaryClass { } -} \ No newline at end of file +} diff --git a/foundation/src/main/scala/cocoa/foundation/NSDiscardableContent.scala b/foundation/src/main/scala/cocoa/foundation/NSDiscardableContent.scala index a468879..c91cc9c 100644 --- a/foundation/src/main/scala/cocoa/foundation/NSDiscardableContent.scala +++ b/foundation/src/main/scala/cocoa/foundation/NSDiscardableContent.scala @@ -2,6 +2,7 @@ package cocoa.foundation import scalanative.native._ +import scalanative.unsafe._ import objc._ @ObjC diff --git a/foundation/src/main/scala/cocoa/foundation/NSEnumerator.scala b/foundation/src/main/scala/cocoa/foundation/NSEnumerator.scala index 3818dd9..a17c01a 100644 --- a/foundation/src/main/scala/cocoa/foundation/NSEnumerator.scala +++ b/foundation/src/main/scala/cocoa/foundation/NSEnumerator.scala @@ -5,6 +5,7 @@ import cocoa.foundation.NSFastEnumeration.NSFastEnumerationState import scala.language.experimental.macros import scalanative.native._ +import scalanative.unsafe._ import objc._ @ObjC diff --git a/foundation/src/main/scala/cocoa/foundation/NSError.scala b/foundation/src/main/scala/cocoa/foundation/NSError.scala index 31f8787..8539103 100644 --- a/foundation/src/main/scala/cocoa/foundation/NSError.scala +++ b/foundation/src/main/scala/cocoa/foundation/NSError.scala @@ -2,6 +2,7 @@ package cocoa.foundation import scalanative.native._ +import scalanative.unsafe._ import objc._ @ObjC @@ -28,4 +29,4 @@ abstract class NSErrorClass extends NSObjectClass { object NSError extends NSErrorClass { override type InstanceType = NSError -} \ No newline at end of file +} diff --git a/foundation/src/main/scala/cocoa/foundation/NSIndexSet.scala b/foundation/src/main/scala/cocoa/foundation/NSIndexSet.scala index 193b089..67cc17b 100644 --- a/foundation/src/main/scala/cocoa/foundation/NSIndexSet.scala +++ b/foundation/src/main/scala/cocoa/foundation/NSIndexSet.scala @@ -3,6 +3,7 @@ package cocoa.foundation import scala.language.experimental.macros import scalanative.native._ +import scalanative.unsafe._ import objc._ @ObjC @@ -47,4 +48,4 @@ abstract class NSIndexSetClass extends NSObjectClass { object NSIndexSet extends NSIndexSetClass { override type InstanceType = NSIndexSet -} \ No newline at end of file +} diff --git a/foundation/src/main/scala/cocoa/foundation/NSInvocation.scala_ b/foundation/src/main/scala/cocoa/foundation/NSInvocation.scala_ index 5f60638..49b0e64 100644 --- a/foundation/src/main/scala/cocoa/foundation/NSInvocation.scala_ +++ b/foundation/src/main/scala/cocoa/foundation/NSInvocation.scala_ @@ -1,6 +1,7 @@ package cocoa.foundation import scala.scalanative.native._ +import scala.scalanative.unsafe._ import scala.language.experimental.macros @@ -28,4 +29,4 @@ abstract class NSInvocationClass { } object NSInvocation extends NSInvocationClass { -} \ No newline at end of file +} diff --git a/foundation/src/main/scala/cocoa/foundation/NSLocale.scala b/foundation/src/main/scala/cocoa/foundation/NSLocale.scala index 5bd0e88..72bbb96 100644 --- a/foundation/src/main/scala/cocoa/foundation/NSLocale.scala +++ b/foundation/src/main/scala/cocoa/foundation/NSLocale.scala @@ -2,6 +2,7 @@ package cocoa.foundation import scalanative.native._ +import scalanative.unsafe._ import objc._ import scala.language.experimental.macros @@ -41,4 +42,4 @@ abstract class NSLocaleClass extends NSObjectClass { object NSLocale extends NSLocaleClass { override type InstanceType = NSLocale -} \ No newline at end of file +} diff --git a/foundation/src/main/scala/cocoa/foundation/NSMutableArray.scala b/foundation/src/main/scala/cocoa/foundation/NSMutableArray.scala index 9161301..3b27f8b 100644 --- a/foundation/src/main/scala/cocoa/foundation/NSMutableArray.scala +++ b/foundation/src/main/scala/cocoa/foundation/NSMutableArray.scala @@ -3,6 +3,8 @@ package cocoa.foundation import scala.language.experimental.macros import scalanative.native._ +import scalanative.unsafe._ +import scalanative.unsigned._ import objc._ @ObjC @@ -59,7 +61,7 @@ object NSMutableArray extends NSMutableArrayClass { // TODO: use Iterable instead of Seq def arrayWithObjects[T<:NSObject](objects: Seq[T]): NSMutableArray[T] = Zone { implicit z => val count = objects.size - val array = stackalloc[id]( sizeof[id] * count) + val array = stackalloc[id]( sizeof[id] * count.toUInt) for(i<-0 until count) !(array + i) = objects(i).toPtr arrayWithObjects_count_(array,count.toULong) @@ -72,4 +74,4 @@ object NSMutableArray extends NSMutableArrayClass { } def empty[V<:NSObject]: NSMutableArray[V] = alloc().init().asInstanceOf[NSMutableArray[V]] -} \ No newline at end of file +} diff --git a/foundation/src/main/scala/cocoa/foundation/NSMutableArray.scala_ b/foundation/src/main/scala/cocoa/foundation/NSMutableArray.scala_ index e1d834b..fa31904 100644 --- a/foundation/src/main/scala/cocoa/foundation/NSMutableArray.scala_ +++ b/foundation/src/main/scala/cocoa/foundation/NSMutableArray.scala_ @@ -6,6 +6,7 @@ package cocoa.foundation import scalanative.native._ +import scalanative.unsafe._ import scala.language.experimental.macros @@ -56,4 +57,4 @@ abstract class NSMutableArrayClass extends NSArrayClass { object NSMutableArray extends NSMutableArrayClass { override type InstanceType = NSMutableArray[_] -} \ No newline at end of file +} diff --git a/foundation/src/main/scala/cocoa/foundation/NSMutableCharacterSet.scala b/foundation/src/main/scala/cocoa/foundation/NSMutableCharacterSet.scala index fc80f42..85a3904 100644 --- a/foundation/src/main/scala/cocoa/foundation/NSMutableCharacterSet.scala +++ b/foundation/src/main/scala/cocoa/foundation/NSMutableCharacterSet.scala @@ -3,6 +3,7 @@ package cocoa.foundation import scala.language.experimental.macros import scalanative.native._ +import scalanative.unsafe._ import objc._ @@ -42,4 +43,4 @@ abstract class NSMutableCharacterSetClass extends NSCharacterSetClass { object NSMutableCharacterSet extends NSMutableCharacterSetClass { override type InstanceType = NSMutableCharacterSet -} \ No newline at end of file +} diff --git a/foundation/src/main/scala/cocoa/foundation/NSMutableCopying.scala b/foundation/src/main/scala/cocoa/foundation/NSMutableCopying.scala index fd19b7e..c562fe5 100644 --- a/foundation/src/main/scala/cocoa/foundation/NSMutableCopying.scala +++ b/foundation/src/main/scala/cocoa/foundation/NSMutableCopying.scala @@ -2,6 +2,7 @@ package cocoa.foundation import scalanative.native._ +import scalanative.unsafe._ import objc._ @ObjC diff --git a/foundation/src/main/scala/cocoa/foundation/NSMutableData.scala b/foundation/src/main/scala/cocoa/foundation/NSMutableData.scala index 90e9563..158bebb 100644 --- a/foundation/src/main/scala/cocoa/foundation/NSMutableData.scala +++ b/foundation/src/main/scala/cocoa/foundation/NSMutableData.scala @@ -3,6 +3,7 @@ package cocoa.foundation import scala.language.experimental.macros import scalanative.native._ +import scalanative.unsafe._ import objc._ @ObjC @@ -28,4 +29,4 @@ abstract class NSMutableDataClass extends NSDataClass { object NSMutableData extends NSMutableDataClass { override type InstanceType = NSMutableData -} \ No newline at end of file +} diff --git a/foundation/src/main/scala/cocoa/foundation/NSMutableDictionary.scala b/foundation/src/main/scala/cocoa/foundation/NSMutableDictionary.scala index 44b69f7..c6e8b81 100644 --- a/foundation/src/main/scala/cocoa/foundation/NSMutableDictionary.scala +++ b/foundation/src/main/scala/cocoa/foundation/NSMutableDictionary.scala @@ -2,6 +2,8 @@ package cocoa.foundation import scalanative.native._ +import scalanative.unsafe._ +import scalanative.unsigned._ import objc._ @ObjC @@ -41,8 +43,8 @@ object NSMutableDictionary extends NSMutableDictionaryClass { // TODO: use Iterable instead of Seq? def dictionaryWithObjects[K<:NSObject, V<:NSObject](objects: Seq[(K,V)]): NSMutableDictionary[K,V] = Zone { implicit z => val count = objects.size - val objArray = stackalloc[id]( sizeof[id] * count) - val keyArray = stackalloc[id]( sizeof[id] * count ) + val objArray = stackalloc[id]( sizeof[id] * count.toUInt) + val keyArray = stackalloc[id]( sizeof[id] * count.toUInt ) for(i<-0 until count) { !(keyArray + i) = objects(i)._1.toPtr !(objArray + i) = objects(i)._2.toPtr @@ -52,4 +54,4 @@ object NSMutableDictionary extends NSMutableDictionaryClass { } def empty[K<:NSObject,V<:NSObject]: NSMutableDictionary[K,V] = alloc().init().asInstanceOf[NSMutableDictionary[K,V]] -} \ No newline at end of file +} diff --git a/foundation/src/main/scala/cocoa/foundation/NSMutableIndexSet.scala b/foundation/src/main/scala/cocoa/foundation/NSMutableIndexSet.scala index a805076..dd3b45a 100644 --- a/foundation/src/main/scala/cocoa/foundation/NSMutableIndexSet.scala +++ b/foundation/src/main/scala/cocoa/foundation/NSMutableIndexSet.scala @@ -3,6 +3,7 @@ package cocoa.foundation import scala.language.experimental.macros import scalanative.native._ +import scalanative.unsafe._ import objc._ @ObjC @@ -23,4 +24,4 @@ abstract class NSMutableIndexSetClass extends NSIndexSetClass { object NSMutableIndexSet extends NSMutableIndexSetClass { override type InstanceType = NSMutableIndexSet -} \ No newline at end of file +} diff --git a/foundation/src/main/scala/cocoa/foundation/NSMutableString.scala b/foundation/src/main/scala/cocoa/foundation/NSMutableString.scala index e9760b6..27149d7 100644 --- a/foundation/src/main/scala/cocoa/foundation/NSMutableString.scala +++ b/foundation/src/main/scala/cocoa/foundation/NSMutableString.scala @@ -2,6 +2,8 @@ package cocoa.foundation import scalanative.native._ +import scalanative.unsigned._ +import scalanative.unsafe._ import objc._ diff --git a/foundation/src/main/scala/cocoa/foundation/NSNotification.scala b/foundation/src/main/scala/cocoa/foundation/NSNotification.scala index 0f89b23..c764e67 100644 --- a/foundation/src/main/scala/cocoa/foundation/NSNotification.scala +++ b/foundation/src/main/scala/cocoa/foundation/NSNotification.scala @@ -2,6 +2,8 @@ package cocoa.foundation import scalanative.native._ +import scalanative.unsigned._ +import scalanative.unsafe._ import objc._ @ObjC @@ -21,4 +23,4 @@ abstract class NSNotificationClass extends NSObjectClass { object NSNotification extends NSNotificationClass { override type InstanceType = NSNotification -} \ No newline at end of file +} diff --git a/foundation/src/main/scala/cocoa/foundation/NSNotificationCenter.scala b/foundation/src/main/scala/cocoa/foundation/NSNotificationCenter.scala index dbe493d..55a0f2a 100644 --- a/foundation/src/main/scala/cocoa/foundation/NSNotificationCenter.scala +++ b/foundation/src/main/scala/cocoa/foundation/NSNotificationCenter.scala @@ -2,6 +2,7 @@ package cocoa.foundation import scalanative.native._ +import scalanative.unsafe._ import objc._ @@ -23,4 +24,4 @@ abstract class NSNotificationCenterClass extends NSObjectClass { object NSNotificationCenter extends NSNotificationCenterClass { override type InstanceType = NSNotificationCenter -} \ No newline at end of file +} diff --git a/foundation/src/main/scala/cocoa/foundation/NSNumber.scala b/foundation/src/main/scala/cocoa/foundation/NSNumber.scala index 5149eb5..063cb45 100644 --- a/foundation/src/main/scala/cocoa/foundation/NSNumber.scala +++ b/foundation/src/main/scala/cocoa/foundation/NSNumber.scala @@ -2,6 +2,8 @@ package cocoa.foundation import scala.scalanative.native._ +import scala.scalanative.unsafe._ +import scala.scalanative.unsigned._ import objc._ diff --git a/foundation/src/main/scala/cocoa/foundation/NSObject.scala b/foundation/src/main/scala/cocoa/foundation/NSObject.scala index 92d0020..327b47a 100644 --- a/foundation/src/main/scala/cocoa/foundation/NSObject.scala +++ b/foundation/src/main/scala/cocoa/foundation/NSObject.scala @@ -3,6 +3,7 @@ package cocoa.foundation import scala.language.experimental.macros import scalanative.native._ +import scalanative.unsafe._ import objc._ import scala.scalanative.native.objc.runtime.{IMP, ObjCObject} @@ -80,4 +81,4 @@ abstract class NSObjectClass extends ObjCObject { object NSObject extends NSObjectClass { override type InstanceType = NSObject -} \ No newline at end of file +} diff --git a/foundation/src/main/scala/cocoa/foundation/NSSecureCoding.scala b/foundation/src/main/scala/cocoa/foundation/NSSecureCoding.scala index d089e3c..49f9e5c 100644 --- a/foundation/src/main/scala/cocoa/foundation/NSSecureCoding.scala +++ b/foundation/src/main/scala/cocoa/foundation/NSSecureCoding.scala @@ -2,6 +2,7 @@ package cocoa.foundation import scalanative.native._ +import scalanative.unsafe._ import objc._ @ObjC diff --git a/foundation/src/main/scala/cocoa/foundation/NSString.scala b/foundation/src/main/scala/cocoa/foundation/NSString.scala index a0a8631..c13b939 100644 --- a/foundation/src/main/scala/cocoa/foundation/NSString.scala +++ b/foundation/src/main/scala/cocoa/foundation/NSString.scala @@ -3,6 +3,7 @@ package cocoa.foundation import scala.language.experimental.macros import scalanative.native._ +import scalanative.unsafe._ import objc._ @@ -172,4 +173,4 @@ object NSString extends NSStringClass { */ @inline def string: String = fromCString(ns.UTF8String()) } -} \ No newline at end of file +} diff --git a/foundation/src/main/scala/cocoa/foundation/NSURL.scala b/foundation/src/main/scala/cocoa/foundation/NSURL.scala index be755e5..92633b4 100644 --- a/foundation/src/main/scala/cocoa/foundation/NSURL.scala +++ b/foundation/src/main/scala/cocoa/foundation/NSURL.scala @@ -3,6 +3,7 @@ package cocoa.foundation import scala.language.experimental.macros import scalanative.native._ +import scalanative.unsafe._ import objc._ @ObjC @@ -96,4 +97,4 @@ abstract class NSURLClass extends NSObjectClass { object NSURL extends NSURLClass { override type InstanceType = NSURL -} \ No newline at end of file +} diff --git a/foundation/src/main/scala/cocoa/foundation/NSURLComponents.scala b/foundation/src/main/scala/cocoa/foundation/NSURLComponents.scala index b83ff97..64aeac2 100644 --- a/foundation/src/main/scala/cocoa/foundation/NSURLComponents.scala +++ b/foundation/src/main/scala/cocoa/foundation/NSURLComponents.scala @@ -3,6 +3,7 @@ package cocoa.foundation import scala.language.experimental.macros import scalanative.native._ +import scalanative.unsafe._ import objc._ @ObjC @@ -60,4 +61,4 @@ abstract class NSURLComponentsClass extends NSObjectClass { object NSURLComponents extends NSURLComponentsClass { override type InstanceType = NSURLComponents -} \ No newline at end of file +} diff --git a/foundation/src/main/scala/cocoa/foundation/NSURLQueryItem.scala b/foundation/src/main/scala/cocoa/foundation/NSURLQueryItem.scala index a1d25a9..7928fdc 100644 --- a/foundation/src/main/scala/cocoa/foundation/NSURLQueryItem.scala +++ b/foundation/src/main/scala/cocoa/foundation/NSURLQueryItem.scala @@ -3,6 +3,7 @@ package cocoa.foundation import scala.language.experimental.macros import scalanative.native._ +import scalanative.unsafe._ import objc._ @ObjC @@ -20,4 +21,4 @@ abstract class NSURLQueryItemClass extends NSObjectClass { object NSURLQueryItem extends NSURLQueryItemClass { override type InstanceType = NSURLQueryItem -} \ No newline at end of file +} diff --git a/foundation/src/main/scala/cocoa/foundation/NSValue.scala b/foundation/src/main/scala/cocoa/foundation/NSValue.scala index 3df087f..64ea3cf 100644 --- a/foundation/src/main/scala/cocoa/foundation/NSValue.scala +++ b/foundation/src/main/scala/cocoa/foundation/NSValue.scala @@ -2,6 +2,7 @@ package cocoa.foundation import scala.scalanative.native._ +import scala.scalanative.unsafe._ import objc._ @ObjC diff --git a/foundation/src/main/scala/cocoa/foundation/convert/Wrappers.scala b/foundation/src/main/scala/cocoa/foundation/convert/Wrappers.scala index a547be3..e517fc2 100644 --- a/foundation/src/main/scala/cocoa/foundation/convert/Wrappers.scala +++ b/foundation/src/main/scala/cocoa/foundation/convert/Wrappers.scala @@ -5,6 +5,8 @@ package cocoa.foundation.convert import scala.scalanative.native._ +import scala.scalanative.unsigned._ +import scala.scalanative.unsafe._ import cocoa.foundation.{NSArray, NSDictionary, NSEnumerator, NSObject, Foundation, NSQuote} import Foundation.NSLog import objc.runtime._ @@ -32,7 +34,9 @@ object Wrappers { // TODO: more efficient implementation override def iterator: Iterator[(K, V)] = KeyValueIterator(keyIterator,ns) - override def -(key: K): Map[K, V] = ??? + //override def -(key: K): Map[K, V] = ??? + override def removed(key: K): Map[K, V] = ??? + override def updated[V1 >: V](key: K, value: V1): Map[K, V] = ??? override def size: CInt = ns.count().toInt } @@ -66,7 +70,7 @@ object Wrappers { override def next(): T = { loadNext() _consumed = true - _next.cast[NSObject].asInstanceOf[T] + _next.asInstanceOf[NSObject].asInstanceOf[T] } } } diff --git a/foundation/src/main/scala/cocoa/foundation/package.scala b/foundation/src/main/scala/cocoa/foundation/package.scala index 1565c9a..b7eda52 100644 --- a/foundation/src/main/scala/cocoa/foundation/package.scala +++ b/foundation/src/main/scala/cocoa/foundation/package.scala @@ -5,6 +5,8 @@ package cocoa import scala.language.experimental.macros import scala.scalanative.native._ +import scala.scalanative.unsafe._ +import scala.scalanative.unsigned._ import objc.runtime._ package object foundation { @@ -31,7 +33,7 @@ package object foundation { //implicit def objectToId(o: ObjCObject): id = o.cast[id] implicit final class RichObjCObject(val o: ObjCObject) { - def toPtr: id = o.cast[id] + def toPtr: id = o.asInstanceOf[id] } /** NSString literal. */ diff --git a/foundation/src/main/scala/wrapDictionary.scala b/foundation/src/main/scala/wrapDictionary.scala index 8c78a2d..045a7e4 100644 --- a/foundation/src/main/scala/wrapDictionary.scala +++ b/foundation/src/main/scala/wrapDictionary.scala @@ -5,8 +5,6 @@ import de.surfice.smacrotools.MacroAnnotationHandler import scala.annotation.{StaticAnnotation, compileTimeOnly} import scala.language.experimental.macros import scala.reflect.macros.whitebox -import scala.scalanative.native.objc.ObjCMacroTools -import scala.scalanative.native.objc.runtime.ObjCObject @compileTimeOnly("enable macro paradise to expand macro annotations") class wrapDictionary extends StaticAnnotation { diff --git a/objc/src/main/scala/scalanative/native/objc/Block.scala b/objc/src/main/scala/scalanative/native/objc/Block.scala new file mode 100644 index 0000000..906c06a --- /dev/null +++ b/objc/src/main/scala/scalanative/native/objc/Block.scala @@ -0,0 +1,183 @@ +package scala.scalanative.native.objc + +import de.surfice.smacrotools.BlackboxMacroTools + +import scalanative.native._ +import scalanative.unsafe._ +import scala.reflect.macros.blackbox +import scala.language.experimental.macros + + +sealed trait Block +sealed trait Block0[R] extends Block +sealed trait Block1[T1,R] extends Block +sealed trait Block2[T1,T2,R] extends Block +sealed trait Block3[T1,T2,T3,R] extends Block +sealed trait Block4[T1,T2,T3,T4,R] extends Block +sealed trait Block5[T1,T2,T3,T4,T5,R] extends Block +sealed trait Block6[T1,T2,T3,T4,T5,T6,R] extends Block +sealed trait Block7[T1,T2,T3,T4,T5,T6,T7,R] extends Block +sealed trait Block8[T1,T2,T3,T4,T5,T6,T7,T8,R] extends Block +sealed trait Block9[T1,T2,T3,T4,T5,T6,T7,T8,T9,R] extends Block + +object Block { + + protected[objc] type BlockStruct = CStruct6[Ptr[Byte],CInt,CInt,Ptr[Byte],Ptr[Byte],Ptr[Byte]] + + @inline def apply[R](f: CFuncPtr1[Block,R])(implicit ba: BlockAlloc): Block0[R] = + makeBlock(CFuncPtr.toPtr(f)).asInstanceOf[Block0[R]] + + @inline def apply[T1,R](f: CFuncPtr2[Block,T1,R])(implicit ba: BlockAlloc): Block1[T1,R] = + makeBlock(CFuncPtr.toPtr(f)).asInstanceOf[Block1[T1,R]] + + @inline def apply[T1,T2,R](f: CFuncPtr3[Block,T1,T2,R])(implicit ba: BlockAlloc): Block2[T1,T2,R] = + makeBlock(CFuncPtr.toPtr(f)).asInstanceOf[Block2[T1,T2,R]] + + @inline def apply[T1,T2,T3,R](f: CFuncPtr4[Block,T1,T2,T3,R])(implicit ba: BlockAlloc): Block3[T1,T2,T3,R] = + makeBlock(CFuncPtr.toPtr(f)).asInstanceOf[Block3[T1,T2,T3,R]] + + @inline def apply[T1,T2,T3,T4,R](f: CFuncPtr5[Block,T1,T2,T3,T4,R])(implicit ba: BlockAlloc): Block4[T1,T2,T3,T4,R] = + makeBlock(CFuncPtr.toPtr(f)).asInstanceOf[Block4[T1,T2,T3,T4,R]] + + @inline def apply[T1,T2,T3,T4,T5,R](f: CFuncPtr6[Block,T1,T2,T3,T4,T5,R])(implicit ba: BlockAlloc): Block5[T1,T2,T3,T4,T5,R] = + makeBlock(CFuncPtr.toPtr(f)).asInstanceOf[Block5[T1,T2,T3,T4,T5,R]] + + @inline def apply[T1,T2,T3,T4,T5,T6,R](f: CFuncPtr7[Block,T1,T2,T3,T4,T5,T6,R])(implicit ba: BlockAlloc): Block6[T1,T2,T3,T4,T5,T6,R] = + makeBlock(CFuncPtr.toPtr(f)).asInstanceOf[Block6[T1,T2,T3,T4,T5,T6,R]] + + @inline def apply[T1,T2,T3,T4,T5,T6,T7,R](f: CFuncPtr8[Block,T1,T2,T3,T4,T5,T6,T7,R])(implicit ba: BlockAlloc): Block7[T1,T2,T3,T4,T5,T6,T7,R] = + makeBlock(CFuncPtr.toPtr(f)).asInstanceOf[Block7[T1,T2,T3,T4,T5,T6,T7,R]] + + @inline def apply[T1,T2,T3,T4,T5,T6,T7,T8,R](f: CFuncPtr9[Block,T1,T2,T3,T4,T5,T6,T7,T8,R])(implicit ba: BlockAlloc): Block8[T1,T2,T3,T4,T5,T6,T7,T8,R] = + makeBlock(CFuncPtr.toPtr(f)).asInstanceOf[Block8[T1,T2,T3,T4,T5,T6,T7,T8,R]] + + @inline def apply[T1,T2,T3,T4,T5,T6,T7,T8,T9,R](f: CFuncPtr10[Block,T1,T2,T3,T4,T5,T6,T7,T8,T9,R])(implicit ba: BlockAlloc): Block9[T1,T2,T3,T4,T5,T6,T7,T8,T9,R] = + makeBlock(CFuncPtr.toPtr(f)).asInstanceOf[Block9[T1,T2,T3,T4,T5,T6,T7,T8,T9,R]] +/* + @inline def apply[T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,R](f: CFuncPtr11[Block,T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,R])(implicit ba: BlockAlloc): Block = + makeBlock(f.asInstanceOf[Ptr[Byte]]) + + @inline def apply[T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,R](f: CFuncPtr12[Block,T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,R])(implicit ba: BlockAlloc): Block = + makeBlock(f.asInstanceOf[Ptr[Byte]]) + + @inline def apply[T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,R](f: CFuncPtr13[Block,T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,R])(implicit ba: BlockAlloc): Block = + makeBlock(f.asInstanceOf[Ptr[Byte]]) + + @inline def apply[T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,R](f: CFuncPtr14[Block,T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,R])(implicit ba: BlockAlloc): Block = + makeBlock(f.asInstanceOf[Ptr[Byte]]) + + @inline def apply[T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14,R](f: CFuncPtr15[Block,T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14,R])(implicit ba: BlockAlloc): Block = + makeBlock(f.asInstanceOf[Ptr[Byte]]) + + @inline def apply[T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14,T15,R](f: CFuncPtr16[Block,T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14,T15,R])(implicit ba: BlockAlloc): Block = + makeBlock(f.asInstanceOf[Ptr[Byte]]) + + @inline def apply[T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14,T15,T16,R](f: CFuncPtr17[Block,T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14,T15,T16,R])(implicit ba: BlockAlloc): Block = + makeBlock(f.asInstanceOf[Ptr[Byte]]) + + @inline def apply[T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14,T15,T16,T17,R](f: CFuncPtr18[Block,T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14,T15,T16,T17,R])(implicit ba: BlockAlloc): Block = + makeBlock(f.asInstanceOf[Ptr[Byte]]) + + @inline def apply[T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14,T15,T16,T17,T18,R](f: CFuncPtr19[Block,T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14,T15,T16,T17,T18,R])(implicit ba: BlockAlloc): Block = + makeBlock(f.asInstanceOf[Ptr[Byte]]) + + @inline def apply[T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14,T15,T16,T17,T18,T19,R](f: CFuncPtr20[Block,T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14,T15,T16,T17,T18,T19,R])(implicit ba: BlockAlloc): Block = + makeBlock(f.asInstanceOf[Ptr[Byte]]) +*/ + private def makeBlock(f: Ptr[Byte])(implicit ba: BlockAlloc) : Block = { + val block = ba.alloc() + block._1 = null + block._2 = 0 + block._3 = 0 + block._4 = f + block._5 = null + block._6 = ba.asInstanceOf[Ptr[Byte]] // store the reference to the allocator so that we can use it to release the block + block.asInstanceOf[Block] + } + + def apply[R](f: Block=>R): Block = macro Macros.blockImpl + def apply[T1,R](f: (Block,T1)=>R): Block = macro Macros.blockImpl + def apply[T1,T2,R](f: (Block,T1,T2)=>R): Block2[T1,T2,R] = macro Macros.blockImpl + def apply[T1,T2,T3,R](f: (Block,T1,T2,T3)=>R): Block = macro Macros.blockImpl + def apply[T1,T2,T3,T4,R](f: (Block,T1,T2,T3,T4)=>R): Block = macro Macros.blockImpl + def apply[T1,T2,T3,T4,T5,R](f: (Block,T1,T2,T3,T4,T5)=>R): Block = macro Macros.blockImpl + def apply[T1,T2,T3,T4,T5,T6,R](f: (Block,T1,T2,T3,T4,T5,T6)=>R): Block = macro Macros.blockImpl + def apply[T1,T2,T3,T4,T5,T6,T7,R](f: (Block,T1,T2,T3,T4,T5,T6,T7)=>R): Block = macro Macros.blockImpl + def apply[T1,T2,T3,T4,T5,T6,T7,T8,R](f: (Block,T1,T2,T3,T4,T5,T6,T7,T8)=>R): Block = macro Macros.blockImpl + def apply[T1,T2,T3,T4,T5,T6,T7,T8,T9,R](f: (Block,T1,T2,T3,T4,T5,T6,T7,T8,T9)=>R): Block = macro Macros.blockImpl + /* + def apply[T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,R](f: (Block,T1,T2,T3,T4,T5,T6,T7,T8,T9,T10)=>R): Block = macro Macros.blockImpl + def apply[T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,R](f: (Block,T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11)=>R): Block = macro Macros.blockImpl + def apply[T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,R](f: (Block,T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12)=>R): Block = macro Macros.blockImpl + def apply[T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,R](f: (Block,T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13)=>R): Block = macro Macros.blockImpl + def apply[T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14,R](f: (Block,T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14)=>R): Block = macro Macros.blockImpl + def apply[T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14,T15,R](f: (Block,T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14,T15)=>R): Block = macro Macros.blockImpl + def apply[T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14,T15,T16,R](f: (Block,T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14,T15,T16)=>R): Block = macro Macros.blockImpl + def apply[T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14,T15,T16,T17,R](f: (Block,T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14,T15,T16,T17)=>R): Block = macro Macros.blockImpl + def apply[T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14,T15,T16,T17,T18,R](f: (Block,T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14,T15,T16,T17,T18)=>R): Block = macro Macros.blockImpl + def apply[T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14,T15,T16,T17,T18,T19,R](f: (Block,T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14,T15,T16,T17,T18,T19)=>R): Block = macro Macros.blockImpl +*/ + + implicit final class Wrapper(val block: Block) { + def release(): Unit = { + val ba = block.asInstanceOf[Ptr[BlockStruct]]._6.asInstanceOf[BlockAlloc] + ba.free(block) + } + } + + protected[objc] class Macros(val c: blackbox.Context) extends BlackboxMacroTools with ObjCMacroTools { + import c.universe._ + + def blockImpl(f: Tree): Tree = + (f match { + case func: Function => func.vparams.size + case x => println(x.getClass); 0 + }) match { + case 1 => + q"scalanative.native.objc.Block(scalanative.native.CFuncPtr.fromFunction1($f))" + case 2 => + q"scalanative.native.objc.Block(scalanative.native.CFuncPtr.fromFunction2($f))" + case 3 => + q"scalanative.native.objc.Block(scalanative.native.CFuncPtr.fromFunction3($f))" + case 4 => + q"scalanative.native.objc.Block(scalanative.native.CFuncPtr.fromFunction4($f))" + case 5 => + q"scalanative.native.objc.Block(scalanative.native.CFuncPtr.fromFunction5($f))" + case 6 => + q"scalanative.native.objc.Block(scalanative.native.CFuncPtr.fromFunction6($f))" + case 7 => + q"scalanative.native.objc.Block(scalanative.native.CFuncPtr.fromFunction7($f))" + case 8 => + q"scalanative.native.objc.Block(scalanative.native.CFuncPtr.fromFunction8($f))" + case 9 => + q"scalanative.native.objc.Block(scalanative.native.CFuncPtr.fromFunction9($f))" + case 10 => + q"scalanative.native.objc.Block(scalanative.native.CFuncPtr.fromFunction10($f))" + case 11 => + q"scalanative.native.objc.Block(scalanative.native.CFuncPtr.fromFunction11($f))" + case 12 => + q"scalanative.native.objc.Block(scalanative.native.CFuncPtr.fromFunction12($f))" + case 13 => + q"scalanative.native.objc.Block(scalanative.native.CFuncPtr.fromFunction13($f))" + case 14 => + q"scalanative.native.objc.Block(scalanative.native.CFuncPtr.fromFunction14($f))" + case 15 => + q"scalanative.native.objc.Block(scalanative.native.CFuncPtr.fromFunction15($f))" + case 16 => + q"scalanative.native.objc.Block(scalanative.native.CFuncPtr.fromFunction16($f))" + case 17 => + q"scalanative.native.objc.Block(scalanative.native.CFuncPtr.fromFunction17($f))" + case 18 => + q"scalanative.native.objc.Block(scalanative.native.CFuncPtr.fromFunction18($f))" + case 19 => + q"scalanative.native.objc.Block(scalanative.native.CFuncPtr.fromFunction19($f))" + case 20 => + q"scalanative.native.objc.Block(scalanative.native.CFuncPtr.fromFunction20($f))" + case 21 => + q"scalanative.native.objc.Block(scalanative.native.CFuncPtr.fromFunction21($f))" + case 22 => + q"scalanative.native.objc.Block(scalanative.native.CFuncPtr.fromFunction22($f))" + } + + } +} diff --git a/objc/src/main/scala/scalanative/native/objc/BlockAlloc.scala b/objc/src/main/scala/scalanative/native/objc/BlockAlloc.scala new file mode 100644 index 0000000..a969e86 --- /dev/null +++ b/objc/src/main/scala/scalanative/native/objc/BlockAlloc.scala @@ -0,0 +1,58 @@ +package scala.scalanative.native.objc + +import scala.scalanative.native.objc.Block.BlockStruct +import scalanative.native._ +import scala.scalanative.unsafe._ + +trait BlockAlloc { + protected[objc] def alloc(): Ptr[BlockStruct] + def free(block: Block): Unit = {} +} + +object BlockAlloc { + + object Implicits { + implicit val global: BlockAlloc = BlockAlloc.global + } + + def zone(implicit z: Zone): BlockAlloc = new ZoneAlloc + + private class ZoneAlloc(implicit val z: Zone) extends BlockAlloc { + protected[objc] override def alloc(): Ptr[BlockStruct] = scalanative.unsafe.alloc[BlockStruct]() + } + + // doesn't currently work -- maybe if we could get the pointer to _NSConcreteStackBlock... + /* + object stack extends BlockAlloc { + override protected[objc] def alloc(): Ptr[BlockStruct] = { + val ptr = stackalloc[BlockStruct] + // currently we have no way to get a pointer to a static struct + !ptr._1 = ext._NSConcreteStackBlock + ptr + } + } + */ + + /** + * This allocator uses malloc/free to allocate/release blocks. + */ + object global extends BlockAlloc { + protected[objc] override def alloc(): Ptr[BlockStruct] = scalanative.libc.stdlib.malloc(sizeof[BlockStruct]).asInstanceOf[Ptr[BlockStruct]] + + override def free(block: Block): Unit = scalanative.libc.stdlib.free(block.asInstanceOf[Ptr[Byte]]) + } + + /* + @extern + object ext { + + val _NSConcreteStackBlock: CStruct1[Byte] = extern + } + */ +} + +object BlockZone { + def apply[R](f: BlockAlloc =>R): R = Zone{ implicit z => + f(BlockAlloc.zone) + } +} diff --git a/objc/src/main/scala/scalanative/native/objc/ObjC.scala b/objc/src/main/scala/scalanative/native/objc/ObjC.scala new file mode 100644 index 0000000..38e5558 --- /dev/null +++ b/objc/src/main/scala/scalanative/native/objc/ObjC.scala @@ -0,0 +1,179 @@ +package scala.scalanative.native.objc + + +import de.surfice.smacrotools.MacroAnnotationHandler + +import scala.language.experimental.macros +import scala.annotation.{StaticAnnotation, compileTimeOnly} +import scala.reflect.macros.{TypecheckException, whitebox} + +@compileTimeOnly("enable macro paradise to expand macro annotations") +class ObjC() extends StaticAnnotation { + def macroTransform(annottees: Any*): Any = macro ObjC.ObjCMacro.impl +} + +@compileTimeOnly("enable macro paradise to expand macro annotations") +class ObjCClass() extends StaticAnnotation { + def macroTransform(annottees: Any*): Any = macro ObjC.ObjCClassMacro.impl +} + +class selector(name: String) extends StaticAnnotation + +object ObjC { + + private[native] class ObjCMacro(val c: whitebox.Context) extends BaseMacro { + override def isObjCClass: Boolean = false + } + private[native] class ObjCClassMacro(val c: whitebox.Context) extends BaseMacro { + override def isObjCClass: Boolean = true + } + + private[native] abstract class BaseMacro + extends MacroAnnotationHandler + with ObjCMacroTools { + + import c.universe._ + + def isObjCClass: Boolean + + override val annotationName: String = "scala.scalanative.native.ObjC" + override val supportsClasses: Boolean = true + override val supportsTraits: Boolean = true + override val supportsObjects: Boolean = true + override val createCompanion: Boolean = true + + + override def analyze: Analysis = super.analyze andThen { + case (cls: TypeParts, data) => + // collect selectors to be emitted into companion object body + val selectors = cls.body.collect { + case DefDef(mods, name, types, args, rettype, rhs) if isExtern(rhs) => + genSelector(name, args) + } + (cls, data.selectors = data.selectors ++ selectors) + case default => default + } + + override def transform: Transformation = super.transform andThen { + /* transform class */ + case cls: ClassTransformData => + val transformedBody = + (if(cls.data.replaceClassBody.isDefined) + cls.data.replaceClassBody.get + else + cls.modParts.body) + .map { + case t@DefDef(mods, name, types, args, rettype, rhs) if isExtern(rhs) => + val selectorTerm = q"${cls.modParts.name.toTermName}.${genSelector(name, args)._2}" + val call = + if(isObjCClass) + genCall(q"__cls", selectorTerm, args, rettype) + else + genCall(q"this", selectorTerm, args, rettype) + DefDef(mods, name, types, args, rettype, call) + case x => x + } + + cls + .updBody(transformedBody) + .updCtorMods(Modifiers(Flag.PROTECTED)) // ensure that the class can't be instatiated using new + + /* transform companion object */ + case obj: ObjectTransformData => + // val containing the class reference + val objcCls = + q"""lazy val __cls = { + import scalanative.native._ + ..${obj.data.objcClassInits} + objc.runtime.objc_getClass(scalanative.unsafe.CQuote(StringContext(${obj.modParts.name.toString})).c() ) + }""" + // collect selector definitions from class + val clsSelectors = obj.data.selectors + // collect selector definitions and statements (transformed ObjC-calls and other statements) + // from companion + val (objSelectors, objStmts) = obj.modParts.body + .map { + case t@DefDef(mods, name, types, args, rettype, Ident(TermName("extern"))) => + val sel = genSelector(name, args) + val call = genCall(clsTarget, sel._2, args, rettype) //q"objc.objc_msgSend(_cls,$selectorVal,${paramNames(t)})" + (Some(sel), DefDef(mods, name, types, args, rettype, call)) + case stmt => (None, stmt) + }.unzip + // create selector definitions + val selectorDefs = (clsSelectors ++ objSelectors.collect { case Some(sel) => sel }) + .toMap + .map(p => genSelectorDef(p._1, p._2)) + // new body = (transformed) statements ++ selector definitions + val transformedBody = (selectorDefs.toSeq :+ objcCls) ++ objStmts ++ obj.data.additionalCompanionStmts + obj.updBody(transformedBody) + case default => default + } + + + private def isExtern(rhs: Tree): Boolean = rhs match { + case Ident(TermName(id)) => + id == "extern" + case Select(_,name) => + name.toString == "extern" + case x => + false + } + + + private def genCall(target: TermName, selectorVal: TermName, argsList: List[List[ValDef]], rettype: Tree): Tree = + genCall(q"$target", q"$selectorVal", argsList, rettype) + + + private def genCall(target: Tree, selectorVal: Tree, argsList: List[List[ValDef]], rettype: Tree): Tree = { + val argnames = argsList match { + case Nil => Nil + case List(args) => args map { + case t@ValDef(_, name, tpe, _) => + // TODO: do we really need this casting? without, the NIR compiler complains about a missing type tag + castMode(tpe) match { + case CastMode.TypeArg => + q"$name.asInstanceOf[AnyRef]" + case _ => q"$name" + } + } + case x => + c.error(c.enclosingPosition, "multiple parameter lists not supported for ObjC classes") + ??? + } + // TODO: check if intermediate casting is still required + castMode(rettype) match { + case CastMode.Direct => + q"scalanative.native.objc.runtime.objc_msgSend($target,$selectorVal,..$argnames).asInstanceOf[$rettype]" + case CastMode.Object => + q"scalanative.native.objc.runtime.objc_msgSend($target,$selectorVal,..$argnames).asInstanceOf[Object].asInstanceOf[$rettype]" + case CastMode.InstanceOf | CastMode.TypeArg => + q"scalanative.native.objc.runtime.objc_msgSend($target,$selectorVal,..$argnames).asInstanceOf[Object].asInstanceOf[$rettype]" + } + } + + // As of scala-native 0.3.2, casting from unsigned (UInt, ULong, ...) to signed (CInt, CLong, ...) + // is not supported. Hence we need to add an additional cast to Object in these cases. + private def castMode(rettype: Tree): CastMode.Value = try{ + getQualifiedTypeName(rettype, withMacrosDisabled = true, dealias = true) match { + case "Boolean" | "Int" | "Long" | "Short" | + "scala.scalanative.native.UShort" => + CastMode.Object + case "Float" | "Double" => + CastMode.InstanceOf + case x => + CastMode.Direct + } + // TODO: we shouldn't need this catch - can we avoid this Excpetion? + } catch { + case _: Throwable => CastMode.TypeArg + } + + object CastMode extends Enumeration { + val Direct = Value + val Object = Value + val InstanceOf = Value + val TypeArg = Value + } + + } +} diff --git a/objc/src/main/scala/scalanative/native/objc/ObjCMacroTools.scala b/objc/src/main/scala/scalanative/native/objc/ObjCMacroTools.scala new file mode 100644 index 0000000..f2ec76e --- /dev/null +++ b/objc/src/main/scala/scalanative/native/objc/ObjCMacroTools.scala @@ -0,0 +1,95 @@ +package scala.scalanative.native.objc + +import de.surfice.smacrotools.{CommonMacroTools, WhiteboxMacroTools} +import scala.language.reflectiveCalls + +trait ObjCMacroTools extends CommonMacroTools { + import c.universe._ + + implicit class MacroData(var data: Map[String, Any]) { + type Data = Map[String, Any] + type Selectors = Seq[(String, TermName)] + type Statements = Seq[Tree] + + // selectors to be defined in the companion object + def selectors: Selectors = data.getOrElse("selectors", Nil).asInstanceOf[Selectors] + + def selectors_=(selectors: Selectors): Data = { + data += "selectors" -> selectors + data + } + + // statements to be executed during ObjC class intialization for @ScalaDefined classes + // (i.e. the code required to define the ObjC class when the first call to a class method is issued) + def objcClassInits: Statements = data.getOrElse("objcClassInits", Nil).asInstanceOf[Statements] + + def objcClassInits_=(stmts: Statements): Data = { + data += "objcClassInits" -> stmts + data + } + + def additionalCompanionStmts: Statements = data.getOrElse("compStmts", Nil).asInstanceOf[Statements] + def additionalCompanionStmts_=(stmts: Statements): Data = { + data += "compStmts" -> stmts + data + } + + def replaceClassBody: Option[Statements] = data.getOrElse("replaceClsBody", None).asInstanceOf[Option[Statements]] + def replaceClassBody_=(stmts: Option[Statements]): Data = { + data += "replaceClsBody" -> stmts + data + } + } + + protected[this] val clsTarget = TermName("__cls") + + protected[this] def cstring(s: String) = q"scalanative.unsafe.CQuote(StringContext($s)).c()" + + protected[this] def genSelector(name: TermName, args: List[List[ValDef]]): (String, TermName) = { + val s = genSelectorString(name, args) + (s, genSelectorTerm(s)) + } + + protected[this] def genSelectorTerm(name: TermName, args: List[List[ValDef]]): TermName = + genSelectorTerm(genSelectorString(name,args)) + + protected[this] def genSelectorTermString(selectorString: String): String = + "__sel_"+selectorString.replaceAll(":","_") + + protected[this] def genSelectorTerm(selectorString: String): TermName = { + TermName(genSelectorTermString(selectorString)) + } + + // TODO: handle arguments! + protected[this] def genSelectorString(method: MethodSymbol): String = method.name.toString + + protected[this] def genSelectorString(name: TermName, args: List[List[ValDef]]): String = + name.toString.replaceAll("_",":") + /* + if(name.toString.contains("_")) + name.toString.replaceAll("_",":") + else args match { + case Nil | List(Nil) => selectorMethodName(name) + case List(args) => selectorMethodName(name) +: (args.tail map { + case ValDef(_, name, _, _) => name.toString + }) mkString("", ":", ":") + case x => + c.error(c.enclosingPosition, "multiple parameter lists not supported for ObjC classes") + ??? + } + */ + + + private def selectorMethodName(name: TermName): String = { + val s = name.toString + if(s.endsWith("$eq")) + "set" + s.head.toUpper + s.tail.stripSuffix("_$eq") + else + s + } + + + protected[this] def genSelectorDef(selector: String, selectorTerm: TermName) = + q"protected lazy val $selectorTerm = scalanative.native.objc.runtime.sel_registerName(scalanative.unsafe.CQuote(StringContext($selector)).c())" + +} diff --git a/objc/src/main/scala/scalanative/native/objc/ObjCParent.scala b/objc/src/main/scala/scalanative/native/objc/ObjCParent.scala new file mode 100644 index 0000000..44659b8 --- /dev/null +++ b/objc/src/main/scala/scalanative/native/objc/ObjCParent.scala @@ -0,0 +1,45 @@ +// Copyright (c) 2017. Distributed under the MIT License (see included LICENSE file). +package scala.scalanative.native.objc + +import scala.scalanative.unsafe._ +import scala.scalanative.native._ +import scala.scalanative.unsigned._ +import runtime._ +import helper._ + +object ObjCParent { + private lazy val sel_initWithRef = sel_registerName(c"initWithRef:") + private lazy val sel_methodSignatureForSelector = sel_registerName(c"methodSignatureForSelector:") + private lazy val sel_forwardInvocation = sel_registerName(c"forwardInvocation:") + + private lazy val cls = { + def init(self: id, sel: SEL, ref: id): id = { + val proxy = objc_msgSend(self,sel_alloc) + setScalaInstanceIVar(proxy,ref) + println("Initializing Proxy!") + proxy + } + def methodSignature(self: id, sel: SEL, selForSignature: SEL): id = { + val ref = getScalaInstanceIVar[Object](self).asInstanceOf[id] + objc_msgSend(ref,sel_methodSignatureForSelector,selForSignature) + } + def forwardInvocation(self: id, sel: SEL, invocation: id): id = { + val ref = getScalaInstanceIVar[Object](self).asInstanceOf[id] + println("forwarding invocation") + self + } + val newCls = objc_allocateClassPair(objc_getClass(c"NSProxy"),c"ScalaParentProxy",0.toULong) + val metaCls = object_getClass(newCls) + addScalaInstanceIVar(newCls) + class_addMethod(metaCls,sel_initWithRef,CFuncPtr3.fromScalaFunction(init),c"@:@") + class_addMethod(newCls,sel_methodSignatureForSelector,CFuncPtr3.fromScalaFunction(methodSignature _),c"@:@") + class_addMethod(newCls,sel_forwardInvocation,CFuncPtr3.fromScalaFunction(forwardInvocation _),c"@:@") + objc_registerClassPair(newCls) + newCls + } + + def apply(ref: id): id = { + val proxy = objc_msgSend(cls,sel_initWithRef,ref) + proxy + } +} diff --git a/objc/src/main/scala/scalanative/native/objc/ObjCProxy.scala b/objc/src/main/scala/scalanative/native/objc/ObjCProxy.scala new file mode 100644 index 0000000..feb4af5 --- /dev/null +++ b/objc/src/main/scala/scalanative/native/objc/ObjCProxy.scala @@ -0,0 +1,14 @@ +// Project: scalanative-cocoa +// Module: objc +// Description: Provides a proxy class to encapsulate scala instances within ObjC objects +// Copyright (c) 2017. Distributed under the MIT License (see included LICENSE file). package objc +package scala.scalanative.native.objc + +import scala.scalanative.native._ +import scala.scalanative.native.objc.runtime.{ObjCObject, id} + +final class ObjCProxy[T] private() extends ObjCObject { + @inline def get: T = helper.getScalaInstanceIVar[T](this.asInstanceOf[id]) +} + + diff --git a/objc/src/main/scala/scalanative/native/objc/ScalaObjC.scala b/objc/src/main/scala/scalanative/native/objc/ScalaObjC.scala new file mode 100644 index 0000000..3c28e2d --- /dev/null +++ b/objc/src/main/scala/scalanative/native/objc/ScalaObjC.scala @@ -0,0 +1,324 @@ +package scala.scalanative.native.objc + +import de.surfice.smacrotools.MacroAnnotationHandler + +import scala.annotation.{StaticAnnotation, compileTimeOnly} +import scala.language.experimental.macros +import scala.reflect.macros.whitebox +import scala.scalanative.native.objc.runtime.ObjCObject + +@compileTimeOnly("enable macro paradise to expand macro annotations") +class ScalaObjC() extends StaticAnnotation { + def macroTransform(annottees: Any*): Any = macro ScalaObjC.Macro.impl +} + +object ScalaObjC { + + private[native] class Macro(val c: whitebox.Context) + extends MacroAnnotationHandler + with ObjCMacroTools { + import c.universe._ + + override val annotationName: String = "SNDefined" + override val supportsClasses: Boolean = true + override val supportsTraits: Boolean = false + override val supportsObjects: Boolean = true + override val createCompanion: Boolean = true + + val tpeRetain = typeOf[retain] + + override def analyze: Analysis = super.analyze andThen { + case (cls: ClassParts, data: Data) => + // all public defs, vals, and vars are exposed to ObjC + val exposedMembers = getExposedMembers(cls.body) + + // collect all required ObjC selectors (every selector is stored as a lazy val on the companion object) + val selectors = exposedMembers.map( _.selector ) ++ + // add setter selectors for public vars + exposedMembers.filter(_.provideSetter).map{m => + val name = genSetterSelectorName(m.name) + (name,genSelectorTerm(name)) //TermName("__sel_"+name)) + } + + val updData = MacroData(data) + updData.selectors = data.selectors ++ selectors + updData.objcClassInits = allocDef(cls) ++ + objcClassDef(cls,exposedMembers) ++ + (exposedMembers map genExposedMethodProxy(cls)) ++ + (exposedMembers filter(_.provideSetter) map genExposedVarSetterProxy(cls)) + (cls,updData.data) + case x => x + } + + + override def transform: Transformation = super.transform andThen { + /* transform companion object */ + case obj: ObjectTransformData => + val objcCls = + q"""lazy val __cls = { + import scalanative.native.objc + ..${obj.data.objcClassInits} + objc.runtime.objc_getClass(${cstring(obj.modParts.name.toString)}) + }""" + // collect selector definitions from class and companion + val clsSelectors = obj.data.selectors + // collect selector definitions and statements (transformed ObjC-calls and other statements) + // from companion + val (objSelectors, objStmts) = obj.modParts.body + .map { + case t@DefDef(mods, name, types, args, rettype, Ident(TermName("extern"))) => + val sel = genSelector(name, args) + val call = genCall(clsTarget, sel._2, args, rettype) //q"objc.objc_msgSend(_cls,$selectorVal,${paramNames(t)})" + (Some(sel), DefDef(mods, name, types, args, rettype, call)) + case stmt => (None, stmt) + }.unzip + val selectorDefs = (clsSelectors ++ objSelectors.collect { case Some(sel) => sel }) + .toMap + .map(p => genSelectorDef(p._1, p._2)) + val transformedBody = Seq(objcCls) ++ selectorDefs ++ objStmts ++ obj.data.additionalCompanionStmts + obj.updBody(transformedBody) + case default => default + } + + // generate code to define the ObjC proxy class + private def objcClassDef(cls: ClassParts, exposedMembers: Seq[ExposedMember]) = Seq[Tree] { + val clsName = cstring(cls.name.toString) + val parent = c.typecheck(cls.parents.head,c.TYPEmode).tpe match { + case p if p =:= weakTypeOf[Object] => q"scalanative.native.objc.defaultRootObject" + case p if p <:< weakTypeOf[ObjCObject] => q"${p.typeSymbol.companion}.__cls" + case _ => + error("@ScalaObjC class must be a descendant of ObjCObject") + ??? + } + + val exposedVarSetters = exposedMembers + .filter(_.provideSetter) + .map(m => + registerExposedMember( + m.copy(name = TermName(genSetterSelectorName(m.name)), params = List(ValDef(null,m.name,null,null)), isSetter = true)) ) + + q"""import scalanative.native._ + import scalanative.unsigned._ + import scalanative.unsafe._ + import objc.runtime._ + import objc.helper._ + + val newCls = objc_allocateClassPair($parent,$clsName,0.toULong) + objc.helper.addScalaInstanceIVar(newCls) + val metaCls = object_getClass(newCls) + class_addMethod(metaCls,sel_allocWithZone,CFuncPtr3.fromScalaFunction(__allocWithZone),c"@:@") + ..${exposedMembers map registerExposedMember} + ..$exposedVarSetters + objc_registerClassPair(newCls) + """ + } + //class_addMethod(metaCls,sel_alloc,CFuncPtr.fromFunction1(__alloc),c"@:") + + private def allocDef(cls: ClassParts) = Seq[Tree] { + val clsName = cls.name + val retType = cls.name.toTypeName // q"$clsName" + val proxyType = cls.params match { + case List(ValDef(_,_,tpt,_)) => tpt + case _ => + c.error(c.enclosingPosition,s"ScalaObjC classes must have exactly one constructor argument (self)") + ??? + } + q"""def __allocWithZone(clsObj: objc.runtime.id, sel: objc.runtime.SEL, zone: objc.runtime.id): $proxyType = { + import scalanative.native.objc + import objc.helper._ + val ref = objc.helper.msgSendSuper1(clsObj,sel_allocWithZone,zone) + val proxy = ref.asInstanceOf[$proxyType] + val instance = new $clsName(proxy) + setScalaInstanceIVar(ref,instance) + proxy + } + """ + } + + private def registerExposedMember(m: ExposedMember): Tree = { + val typeEncoding = cstring( genTypeEncoding(m) ) + q"""class_addMethod(newCls,${m.selector._2},${exposedMethodCast(m)},$typeEncoding)""" + } + + private def exposedMethodCast(m: ExposedMember): Tree = m.params.size match { + case 0 => q"""CFuncPtr2.fromScalaFunction(${methodProxyName(m)})""" + case 1 => q"""CFuncPtr3.fromScalaFunction(${methodProxyName(m)})""" + case 2 => q"""CFuncPtr4.fromScalaFunction(${methodProxyName(m)})""" + case 3 => q"""CFuncPtr5.fromScalaFunction(${methodProxyName(m)})""" + case 4 => q"""CFuncPtr6.fromScalaFunction(${methodProxyName(m)})""" + case 5 => q"""CFuncPtr7.fromScalaFunction(${methodProxyName(m)})""" + case 6 => q"""CFuncPtr8.fromScalaFunction(${methodProxyName(m)})""" + case 7 => q"""CFuncPtr9.fromScalaFunction(${methodProxyName(m)})""" + case 8 => q"""CFuncPtr10.fromScalaFunction(${methodProxyName(m)})""" + case 9 => q"""CFuncPtr11.fromScalaFunction(${methodProxyName(m)})""" + case 10 => q"""CFuncPtr12.fromScalaFunction(${methodProxyName(m)})""" + case 11 => q"""CFuncPtr13.fromScalaFunction(${methodProxyName(m)})""" + case 12 => q"""CFuncPtr14.fromScalaFunction(${methodProxyName(m)})""" + case 13 => q"""CFuncPtr15.fromScalaFunction(${methodProxyName(m)})""" + case 14 => q"""CFuncPtr16.fromScalaFunction(${methodProxyName(m)})""" + case 15 => q"""CFuncPtr17.fromScalaFunction(${methodProxyName(m)})""" + case 16 => q"""CFuncPtr18.fromScalaFunction(${methodProxyName(m)})""" + case 17 => q"""CFuncPtr19.fromScalaFunction(${methodProxyName(m)})""" + case 18 => q"""CFuncPtr20.fromScalaFunction(${methodProxyName(m)})""" + case 19 => q"""CFuncPtr21.fromScalaFunction(${methodProxyName(m)})""" + case 20 => q"""CFuncPtr22.fromScalaFunction(${methodProxyName(m)})""" + case x => + c.error(c.enclosingPosition,s"Scala-defined ObjC methods with more than 20 parameters are not supported") + ??? + } + + + private def getExposedMembers(body: Seq[Tree]): Seq[ExposedMember] = body collect { + case m: DefDef if isPublic(m.mods) => + val returnType = getType(m.tpt) + ExposedMember(m.name,m.vparamss.headOption.getOrElse(Nil),m.vparamss.nonEmpty, tpe = Some(returnType), customSelector = findCustomSelector(m.mods.annotations)) + case p: ValDef if isPublic(p.mods) => + val tpe = + if(p.tpt.nonEmpty) + c.typecheck(p.tpt,c.TYPEmode,withMacrosDisabled = true).tpe + else + c.typecheck(p.rhs,c.TERMmode,withMacrosDisabled = true).tpe + ExposedMember(p.name,Nil,hasParamList = false, provideSetter = p.mods.hasFlag(Flag.MUTABLE), tpe = Some(tpe), retain = hasAnnotation(p.mods.annotations,tpeRetain) ) + } + + + private def methodProxyName(m: ExposedMember): TermName = + if(m.provideSetter) methodProxyName(m.name.toString) + else { + val name = m.params.map(_.name).mkString(m.name.toString+"_","_","") + methodProxyName(name) + } + private def methodProxyName(name: String): TermName = TermName("__m_"+name) + + private def genExposedMethodProxy(cls: ClassParts)(m: ExposedMember) = { + import m._ + val call = if(hasParamList) q"o.$name(..${paramNames(params)})" else q"o.$name" + q"""def ${methodProxyName(m)}(self: scalanative.native.objc.runtime.id, sel: scalanative.native.objc.runtime.SEL, ..$params) = { + val o = scalanative.native.objc.helper.getScalaInstanceIVar[${cls.name}](self) + $call + } + """ + } + + private def genExposedVarSetterProxy(cls: ClassParts)(m: ExposedMember) = { + import m._ + def setValue = + if(m.retain) + q"""if(o.$name != null) + o.$name.release() + o.$name = value.retain() + """ + else + q"""o.$name = value""" + + q"""def ${methodProxyName(genSetterSelectorName(m.name))}(self: scalanative.native.objc.runtime.id, sel: scalanative.native.objc.runtime.SEL, value: ${m.tpe.get}) = { + val o = scalanative.native.objc.helper.getScalaInstanceIVar[${cls.name}](self) + $setValue + } + """ + } + + private def genTypeEncoding(m: ExposedMember): String = { + val returnType = + if(m.isSetter) "v" + else genTypeCode(m.tpe) + + returnType + "@:" + m.params.map(genTypeCode).mkString + } + + private def genTypeCode(p: ValDef): String = genTypeCode(p.tpe) + + private def genTypeCode(tpe: Option[Type]): String = + if(tpe.isEmpty) "v" + else genTypeCode(tpe.get) + + private def genTypeCode(tpe: Type): String = "@" + + private val _extern = q"scalanative.unsafe.extern" + + private def isPublic(mods: Modifiers): Boolean = + !( mods.hasFlag(Flag.PROTECTED) || mods.hasFlag(Flag.PRIVATE) ) + + + private def genCall(target: TermName, selectorVal: TermName, argsList: List[List[ValDef]], rettype: Tree): Tree = + genCall(q"$target", q"$selectorVal", argsList, rettype) + + + private def genCall(target: Tree, selectorVal: Tree, argsList: List[List[ValDef]], rettype: Tree): Tree = { + val argnames = argsList match { + case Nil => Nil + case List(args) => args map { + case t@ValDef(_, name, tpe, _) => + // TODO: do we really need this casting? without, the NIR compiler complains about a missing type tag + castMode(tpe) match { + case CastMode.TypeArg => + q"$name.asInstanceOf[AnyRef]" + case _ => q"$name" + } + } + case x => + c.error(c.enclosingPosition, "multiple parameter lists not supported for ObjC classes") + ??? + } + // TODO: check if intermediate casting is still required + castMode(rettype) match { + case CastMode.Direct => + q"scalanative.native.objc.runtime.objc_msgSend($target,$selectorVal,..$argnames).asInstanceOf[$rettype]" + case CastMode.Object => + q"scalanative.native.objc.runtime.objc_msgSend($target,$selectorVal,..$argnames).asInstanceOf[Object].asInstanceOf[$rettype]" + case CastMode.InstanceOf | CastMode.TypeArg => + q"scalanative.native.objc.runtime.objc_msgSend($target,$selectorVal,..$argnames).asInstanceOf[Object].asInstanceOf[$rettype]" + } + } + + // As of scala-native 0.3.2, casting from unsigned (UInt, ULong, ...) to signed (CInt, CLong, ...) + // is not supported. Hence we need to add an additional cast to Object in these cases. + private def castMode(rettype: Tree): CastMode.Value = try{ + getQualifiedTypeName(rettype, withMacrosDisabled = true, dealias = true) match { + case "Boolean" | "Int" | "Long" | "Short" | + "scala.scalanative.native.UShort" => + CastMode.Object + case "Float" | "Double" => + CastMode.InstanceOf + case x => + CastMode.Direct + } + // TODO: we shouldn't need this catch - can we avoid this Excpetion? + } catch { + case _: Throwable => CastMode.TypeArg + } + + private def genSetterSelectorName(name: TermName): String = + "set" + name.toString.head.toUpper + name.toString.tail + ":" + + object CastMode extends Enumeration { + val Direct = Value + val Object = Value + val InstanceOf = Value + val TypeArg = Value + } + + def findCustomSelector(annots: Seq[Tree]): Option[String] = { + findAnnotation(annots,"selector") + .map(t => extractAnnotationParameters(t,Seq("name")) ) + .map(_.apply("name")) + .map(t => extractStringConstant(t.get).get) + } + + case class ExposedMember(name: TermName, + params: List[ValDef], + hasParamList: Boolean, + tpe: Option[Type] = None, + provideSetter: Boolean = false, + customSelector: Option[String] = None, + retain: Boolean = true, + isSetter: Boolean = false) { + lazy val selector: (String,TermName) = customSelector match { + case Some(sel) => (sel,genSelectorTerm(sel)) + case _ => genSelector(name,List(params)) + } + } + + } + +} diff --git a/objc/src/main/scala/scalanative/native/objc/helper.scala b/objc/src/main/scala/scalanative/native/objc/helper.scala new file mode 100644 index 0000000..68e17da --- /dev/null +++ b/objc/src/main/scala/scalanative/native/objc/helper.scala @@ -0,0 +1,67 @@ +// Project: scalanative-cocoa +// Module: objc +// Description: Helper functions for interaction with the ObjC runtime + +// Copyright (c) 2017. Distributed under the MIT License (see included LICENSE file). +package scala.scalanative.native.objc + +import scala.scalanative.native._ +import scala.scalanative.unsafe._ +import scala.scalanative.unsigned._ +import runtime._ + +object helper { + +// implicit def objectToId(o: ObjCObject): id = o.cast[id] + + private val scalaInstanceIVar = c"s" + val sel_alloc: SEL = sel_registerName(c"alloc") + val sel_allocWithZone: SEL = sel_registerName(c"allocWithZone:") + + def msgSendSuper0(self: id, op: SEL): id = { + val objc_super = stackalloc[objc_super]( sizeof[objc_super] ) + val super_class = class_getSuperclass(object_getClass(self)) + //TODO: removed dereference here + objc_super._1 = self + objc_super._2 = super_class + runtime.objc_msgSendSuper(objc_super,op) + } + + def msgSendSuper1(self: id, op: SEL, arg1: Ptr[Byte]): id = { + val objc_super = stackalloc[objc_super]( sizeof[objc_super] ) + val super_class = class_getSuperclass(object_getClass(self)) + //TODO: removed dereference here + objc_super._1 = self + objc_super._2 = super_class + runtime.objc_msgSendSuper(objc_super,op, arg1) + } + + @inline def addScalaInstanceIVar(cls: ClassPtr): Boolean = runtime.class_addIvar(cls,scalaInstanceIVar,8.toULong,3.toUByte,null) + + @inline def setScalaInstanceIVar(obj: id, instance: Object): runtime.IVar = + runtime.object_setInstanceVariable(obj,scalaInstanceIVar,instance.asInstanceOf[Ptr[Byte]]) + + // TODO: can we use IVar instead of getInstanceVariable? + @inline def getScalaInstanceIVar[T](obj: id): T = { + val out = stackalloc[Ptr[Byte]]( sizeof[Ptr[Byte]] ) + runtime.object_getInstanceVariable(obj,scalaInstanceIVar,out) + (!out).asInstanceOf[Object].asInstanceOf[T] + } + + def allocProxy(clsObj: id, instance: id=>Object): id = { + val obj = msgSendSuper0(clsObj,sel_alloc) + setScalaInstanceIVar(obj,instance(obj)) + obj + } + +// implicit final class RichObjCObject(val o: ObjCObject) extends AnyVal { + // due to https://github.com/scala-native/scala-native/issues/547 we currently can't pass on CVararg* + // so we explicitly provide methods for multiple arg counts +// def msgSend(selector: CString, arg1: CVararg): id = objc_msgSend(o,sel_registerName(selector),arg1) +// def msgSend(selector: CString, arg1: CVararg, arg2: CVararg): id = objc_msgSend(o,sel_registerName(selector),arg1,arg2) +// } + +} + + + diff --git a/objc/src/main/scala/scalanative/native/objc/package.scala b/objc/src/main/scala/scalanative/native/objc/package.scala new file mode 100644 index 0000000..34b4bff --- /dev/null +++ b/objc/src/main/scala/scalanative/native/objc/package.scala @@ -0,0 +1,18 @@ +package scala.scalanative.native + +import scala.scalanative.unsafe._ +import scala.annotation.StaticAnnotation +import scala.scalanative.native.objc.runtime.{ClassPtr, id} +import scala.language.experimental.macros + +package object objc { + + val objc_nil: id = null + + val objc_Nil: ClassPtr = null + + var defaultRootObject: ClassPtr = runtime.objc_getClass(c"NSObject") + + implicit def function2Block1[T1,R](f: (Block,T1) => R): Block1[T1,R] = macro Block.Macros.blockImpl + implicit def function2Block2[T1,T2,R](f: (Block,T1,T2) => R): Block2[T1,T2,R] = macro Block.Macros.blockImpl +} diff --git a/objc/src/main/scala/scalanative/native/objc/reflect.scala b/objc/src/main/scala/scalanative/native/objc/reflect.scala new file mode 100644 index 0000000..eb4312a --- /dev/null +++ b/objc/src/main/scala/scalanative/native/objc/reflect.scala @@ -0,0 +1,44 @@ +package scala.scalanative.native.objc + +import scala.scalanative.native._ +import scala.scalanative.unsafe._ +import objc.runtime._ +import scalanative.unsigned.UInt + +object reflect { + + def findClasses(): Iterable[ClassDescriptor] = { + Nil + } + + /** + * Provides methods for introspection of Objective-C classes. + * + * @param cls ObjC class to be introspected. + */ + final class ClassDescriptor(val cls: ClassPtr) extends AnyVal { + /** + * Returns the name of this class + */ + def name: String = fromCString(class_getName(cls)) + + /** + * Returns all methods defined on this ObjC class. + */ + def methods(): Iterable[Method] = { + val outCount = stackalloc[UInt]() + val methodsPtr = class_copyMethodList(cls,outCount) + val count = (!outCount).toInt + val methods = Array.ofDim[Method](count) + for(i <- 0 until (!outCount).toInt) { + methods(i) = methodsPtr(i) + } + scalanative.libc.stdlib.free(methodsPtr.asInstanceOf[Ptr[Byte]]) + methods + } + } + object ClassDescriptor { + def apply(cls: ClassPtr): ClassDescriptor = new ClassDescriptor(cls) + } + +} diff --git a/objc/src/main/scala/scalanative/native/objc/retain.scala b/objc/src/main/scala/scalanative/native/objc/retain.scala new file mode 100644 index 0000000..6d33e66 --- /dev/null +++ b/objc/src/main/scala/scalanative/native/objc/retain.scala @@ -0,0 +1,5 @@ +package scala.scalanative.native.objc + +import scala.annotation.StaticAnnotation + +class retain extends StaticAnnotation diff --git a/objc/src/main/scala/scalanative/native/objc/runtime.scala b/objc/src/main/scala/scalanative/native/objc/runtime.scala new file mode 100644 index 0000000..e34f819 --- /dev/null +++ b/objc/src/main/scala/scalanative/native/objc/runtime.scala @@ -0,0 +1,182 @@ +// Project: scalanative-cocoa +// Module: objc +// Description: Bindings to the ObjC runtime defined in objc/*.h +package scala.scalanative.native.objc + +import scala.scalanative.native +import scala.scalanative.native._ +import scala.scalanative.unsafe._ +import scalanative.unsigned.{UInt, UByte} + +/** + * Raw bindings to the Objective-C runtime defined in objc/\*.h + */ +@extern +object runtime { + + type int32_t = CInt + type int64_t = CLong + + trait ObjCObject + + type BOOL = Boolean + +// type id = Ptr[Byte] //CUnsignedLong + type id = Ptr[Byte] //CUnsignedLong + + /** + * An opaque type that represents an Objective-C class. + */ + type ClassPtr = Ptr[Byte] + + /** + * Defines an opaque type that represents an Objective-C method selector + */ + type SEL = Ptr[Byte] + + /** + * A pointer to an Objective-C method implementation. + */ + type IMP = CFuncPtr + + type objc_super = CStruct2[id,ClassPtr] + + /** + * An opaque type that represents an instance variable. + */ + type IVar = Ptr[Byte] + + /** + * An opaque type that represents a method in an Objective-C class definition + */ + type Method = Ptr[Byte] + + /** + * An opaque type that represents an Objective-C category + */ + type Category = Ptr[Byte] + + /** + * Returns the name of a class. + * + * @param cls A class object. + * + * @return The name of the class, or the empty string if `cls` is null. + */ + def class_getName(cls: ClassPtr): CString = extern + + /** + * Creates an instance of a class, allocating memory for the class in the default malloc zone. + * + * @param cls The class to be instantiated + * @param extraBytes number of extra bytes to allocate. + * @return an instance of the class. + */ + def class_createInstance(cls: ClassPtr, extraBytes: CSSize): id = extern + + /** + * Adds a new method to a class with a given anem and implementation. + * + * @see [[https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html#//apple_ref/doc/uid/TP40008048-CH100]] + * + * @param cls The class to which to add a method + * @param name A selector that specifies the name of the method being added + * @param imp A function which is the implementation of the new method. + * The function must take at least two arguments: `self` and `_cmd` + * @param types An array of characters that describe the type of the arguments to the method. + * @return true if the method was added successfully, otherwise false (for example, the class + * already contains a method implementation with that name. + */ + def class_addMethod(cls: ClassPtr, name: SEL, imp: IMP, types: CString): CBool = extern + + /** + * Adds a new instance variable to a class. + * + * @param cls + * @param name + * @param size + * @param alignment + * @param types + * @return true if the instance variable was added successfully, otherwise false + * (for example, the class already contains an instance variable with that name) + */ + def class_addIvar(cls: ClassPtr, name: CString, size: CSize, alignment: UByte, types: CString): CBool = extern + + def class_getSuperclass(cls: ClassPtr): ClassPtr = extern + + /** + * Describes the instance methods implemented by a class. + * + * @note You must free the returned array with `free()`. + * @param cls The class to be inspected. + * @param outCount On return, contains the length of the returned array. + * If outCount is `null`, the length is not returned. + * @return an array with all [[Method]]s implemented by the specified class. + * The array contains `outCount` elements, followed by a `null` terminator. + * You must free the array with `free()` + */ + def class_copyMethodList(cls: ClassPtr, outCount: Ptr[UInt]): Ptr[Method] = extern + + /** + * Describes the instance variables declared by a class. + * + * @param cls + * @param outCount + * @return + */ + def class_copyIvarList(cls: ClassPtr, outCount: Ptr[UInt]): Ptr[IVar] = extern + + def object_isClass(obj: id): CBool = extern + + def object_getClassName(obj: id): CString = extern + + def object_setInstanceVariable(obj: id, name: CString, value: Ptr[Byte]): IVar = extern + + def object_getInstanceVariable(obj: id, name: CString, outValue: Ptr[Ptr[Byte]]): IVar = extern + + /** + * Returns the class of an object. + * + * @param obj + * @return class of the object, or + */ + def object_getClass(obj: id): ClassPtr = extern + + /** + * Returns the class definition of the specified class. + * + * @param name the name of the class to look up. + * + * @return The class object for the named class, or null if the class is not registered with the + * Objective-C runtime. + */ + def objc_getClass(name: CString): ClassPtr = extern + + /** + * Creates a new class and metaclass. + * + * @see [[https://developer.apple.com/documentation/objectivec/1418559-objc_allocateclasspair?language=objc]] + * + * @param superclass The class to use as the new class's superclass, or Nil to create a new root class. + * @param name The string to use as the new class's name. The string will be copied. + * @param extraBytes the number of bytes to allocate for indexed ivars at the end of the class and metaclass objects. This should usually be 0. + * @return The new class, or Nil if the class could not be created (for example, the desired name is already in use). + */ + def objc_allocateClassPair(superclass: ClassPtr, name: CString, extraBytes: CSize): ClassPtr = extern + + def objc_registerClassPair(newClass: ClassPtr): Unit = extern + + + def objc_msgSend(self: Any, op: SEL, args: CVarArg*): id = extern + + def objc_msgSendSuper(objc_super: Ptr[objc_super], op: SEL, args: CVarArg*): id = extern + + /** + * Registers a method with the ObjC runtime system, maps the method name to a selector, and returns the selector value. + * + * @param str name of the method you wish to register. + */ + def sel_registerName(str: CString): SEL = extern +} + + diff --git a/project/build.properties b/project/build.properties index c091b86..22af262 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=0.13.16 +sbt.version=1.7.1 diff --git a/project/plugins.sbt b/project/plugins.sbt index d9b75db..4401b6a 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,5 +1,5 @@ -addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.3.8") +addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.4.8-SNAPSHOT") -addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "1.1") +addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "3.9.13") -addSbtPlugin("com.jsuereth" % "sbt-pgp" % "1.0.0") +addSbtPlugin("com.github.sbt" % "sbt-pgp" % "2.1.2") diff --git a/uikit/src/main/scala/cocoa/uikit/UIApplication.scala b/uikit/src/main/scala/cocoa/uikit/UIApplication.scala index 5f1cb65..0304b36 100644 --- a/uikit/src/main/scala/cocoa/uikit/UIApplication.scala +++ b/uikit/src/main/scala/cocoa/uikit/UIApplication.scala @@ -2,7 +2,9 @@ package cocoa.uikit import cocoa.foundation.{BOOL, NSArray, NSInteger, NSObject, NSObjectClass, NSString, NSTimeInterval, NSURL, id} +import scalanative.native.objc.{ObjC, ObjCClass} import scalanative.native._ +import scalanative.unsafe._ @ObjC class UIApplication extends NSObject { //extends UIResponder { diff --git a/uikit/src/main/scala/cocoa/uikit/UIApplicationDelegate.scala b/uikit/src/main/scala/cocoa/uikit/UIApplicationDelegate.scala index 4eaac2b..43fde0a 100644 --- a/uikit/src/main/scala/cocoa/uikit/UIApplicationDelegate.scala +++ b/uikit/src/main/scala/cocoa/uikit/UIApplicationDelegate.scala @@ -4,7 +4,9 @@ package cocoa.uikit import cocoa.foundation.{BOOL, NSArray, NSCoder, NSData, NSDictionary, NSError, NSObject, NSObjectClass, NSString, NSTimeInterval, NSURL, id} import scala.language.experimental.macros +import scalanative.native.objc.{ObjC, ObjCClass} import scalanative.native._ +import scalanative.unsafe._ @ObjC trait UIApplicationDelegate extends NSObject { @@ -61,4 +63,4 @@ abstract class UIApplicationDelegateClass extends NSObjectClass { object UIApplicationDelegate extends UIApplicationDelegateClass { override type InstanceType = UIApplicationDelegate -} \ No newline at end of file +} diff --git a/uikit/src/main/scala/cocoa/uikit/UIKit.scala b/uikit/src/main/scala/cocoa/uikit/UIKit.scala index 9f38dc8..0c2e3d4 100644 --- a/uikit/src/main/scala/cocoa/uikit/UIKit.scala +++ b/uikit/src/main/scala/cocoa/uikit/UIKit.scala @@ -2,6 +2,7 @@ package cocoa.uikit import scalanative.native._ +import scalanative.unsafe._ import cocoa.foundation.{BOOL, NSString} @extern diff --git a/uikit/src/main/scala/cocoa/uikit/UIViewController.scala b/uikit/src/main/scala/cocoa/uikit/UIViewController.scala index 79bc166..e0a6b7b 100644 --- a/uikit/src/main/scala/cocoa/uikit/UIViewController.scala +++ b/uikit/src/main/scala/cocoa/uikit/UIViewController.scala @@ -5,7 +5,9 @@ import cocoa.foundation.{BOOL, NSArray, NSCoder, NSCoding, NSObject, NSObjectCla import de.surfice.smacrotools.debug import scala.language.experimental.macros +import scalanative.native.objc.{ObjC, ObjCClass} import scalanative.native._ +import scalanative.unsafe._ @ObjC @debug @@ -146,4 +148,4 @@ abstract class UIViewControllerClass extends NSObjectClass { //extends UIRespond object UIViewController extends UIViewControllerClass { override type InstanceType = UIViewController -} \ No newline at end of file +}