Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Dump local experimental code: FlatArrays, Native, React (yup, none of…
… that compiles yet, but still committing)
- Loading branch information
Showing
30 changed files
with
1,546 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
scalaVersion := "2.11.6" | ||
|
||
libraryDependencies <+= scalaVersion("org.scala-lang" % "scala-compiler" % _) | ||
|
||
libraryDependencies <+= scalaVersion("org.scala-lang" % "scala-reflect" % _) | ||
|
||
libraryDependencies += "junit" % "junit" % "4.11" % "test" | ||
|
||
libraryDependencies += "com.novocode" % "junit-interface" % "0.11" % "test" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
resolvers ++= Seq( | ||
Classpaths.sbtPluginReleases, | ||
Opts.resolver.sonatypeReleases | ||
) | ||
|
||
addSbtPlugin("org.ensime" % "ensime-sbt-cmd" % "0.1.2") | ||
|
||
addSbtPlugin("com.typesafe.sbt" % "sbt-pgp" % "0.8.2") | ||
|
||
addSbtPlugin("com.typesafe.sbt" % "sbt-scalariform" % "1.2.0") | ||
|
||
addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "2.5.0") | ||
|
||
addSbtPlugin("com.github.mpeltonen" % "sbt-idea" % "1.6.0") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,267 @@ | ||
package scalaxy | ||
|
||
import scala.language.dynamics | ||
import scala.language.experimental.macros | ||
import scala.reflect.ClassTag | ||
import scala.reflect.api.Universe | ||
import scala.reflect.macros.blackbox | ||
import scala.reflect.macros.whitebox | ||
import scala.collection.breakOut | ||
|
||
package object flatarray { | ||
type Fibers = Array[Array[_]] | ||
|
||
implicit lazy val IntFlatIO = ScalarFlatIO[Int]() | ||
implicit lazy val ShortFlatIO = ScalarFlatIO[Short]() | ||
implicit lazy val LongFlatIO = ScalarFlatIO[Long]() | ||
implicit lazy val ByteFlatIO = ScalarFlatIO[Byte]() | ||
implicit lazy val BooleanFlatIO = ScalarFlatIO[Boolean]() | ||
implicit lazy val FloatFlatIO = ScalarFlatIO[Float]() | ||
implicit lazy val DoubleFlatIO = ScalarFlatIO[Double]() | ||
implicit lazy val CharFlatIO = ScalarFlatIO[Char]() | ||
|
||
def getMembers(u: Universe)(tpe: u.Type): List[(u.TermName, u.Type)] = { | ||
import u._ | ||
|
||
val members = tpe.members.find(_.isConstructor).get.asTerm.asMethod.paramLists.flatten | ||
// val members = tpe.members.filter(m => m.isTerm && m.asTerm.isCaseAccessor && m.asTerm.isGetter) | ||
|
||
(for (m <- members) yield { | ||
m.name.toTermName -> m.typeSignature | ||
})(breakOut) | ||
} | ||
} | ||
|
||
package flatarray { | ||
|
||
trait FlatIO[A] { | ||
def classTag: ClassTag[A] | ||
|
||
def buildFibers(length: Int): Fibers | ||
def set(fibers: Fibers, index: Int, value: A): Unit | ||
def get(fibers: Fibers, index: Int): A | ||
} | ||
|
||
object FlatIO { | ||
def of[A <: AnyRef]: FlatIO[A] = | ||
macro FlatIO.ofImpl[A] | ||
|
||
def ofImpl[A <: AnyRef : c.WeakTypeTag](c: blackbox.Context): c.Expr[FlatIO[A]] = { | ||
import c.universe._ | ||
|
||
val a = weakTypeOf[A] | ||
val members = getMembers(c.universe)(a) | ||
|
||
c.Expr[FlatIO[A]](q""" | ||
new FlatIO[${a}] { | ||
override def classTag = implicitly[scala.reflect.ClassTag[${a}]] | ||
override def buildFibers(length: Int): Fibers = | ||
Array[Array[_]](..${ | ||
members.map(_._2).map(tpe => q"new Array[${tpe}](length).asInstanceOf[Array[_]]") | ||
}) | ||
override def set(fibers: Fibers, index: Int, value: ${a}): Unit = { | ||
require(fibers.length == ${members.size}) | ||
..${members.zipWithIndex.map({ | ||
case ((name, tpe), i) => | ||
q"fibers($i).asInstanceOf[Array[${tpe}]](index) = value.$name" | ||
})} | ||
} | ||
override def get(fibers: Fibers, index: Int): ${a} = { | ||
require(fibers.length == ${members.size}) | ||
${a.typeSymbol.companion}( | ||
..${members.zipWithIndex.map({ | ||
case ((name, tpe), i) => | ||
q"fibers($i).asInstanceOf[Array[${tpe}]](index)" | ||
})} | ||
) | ||
} | ||
} | ||
""") | ||
} | ||
} | ||
|
||
case class ScalarFlatIO[A <: AnyVal : ClassTag]() extends FlatIO[A] { | ||
override def classTag = implicitly[ClassTag[A]] | ||
|
||
override def buildFibers(length: Int): Fibers = | ||
Array[Array[_]](new Array[A](length)) | ||
|
||
override def set(fibers: Fibers, index: Int, value: A): Unit = { | ||
var Array(fiber) = fibers | ||
fibers.asInstanceOf[Array[A]](index) = value | ||
} | ||
|
||
override def get(fibers: Fibers, index: Int): A = { | ||
var Array(fiber) = fibers | ||
fibers.asInstanceOf[Array[A]](index) | ||
} | ||
} | ||
|
||
class Ghost[A](val flatArray: FlatArray[A], val index: Int) extends Dynamic { | ||
// TODO: lazy too costly? might be preferable to create same instance concurrently in some cases. | ||
lazy val materialized: A = | ||
flatArray.materialize(index) | ||
|
||
def selectDynamic(name: String): Any = | ||
macro Ghost.selectDynamicImpl[A] | ||
|
||
def applyDynamic(name: String)(args: Any*): Any = | ||
macro Ghost.applyDynamicImpl[A] | ||
|
||
override def toString = s"Ghost { $materialized }" | ||
// override def toString = ??? | ||
|
||
override def hashCode = ??? | ||
|
||
override def equals(o: AnyRef) = ??? | ||
} | ||
|
||
object Ghost { | ||
implicit def materialize[A](ghost: Ghost[A]): A = | ||
macro Ghost.materializeImpl[A] | ||
|
||
def materializeImpl[A : c.WeakTypeTag](c: blackbox.Context)(ghost: c.Expr[Ghost[A]]): c.Expr[A] = { | ||
import c.universe._ | ||
|
||
c.Expr[A](ghost.tree match { | ||
case Apply(Select(array, name), List(index)) if name.toString == "apply" => | ||
// case q"$array.apply($index)" => | ||
q"$array.materialize($index)" | ||
case _ => | ||
c.error(ghost.tree.pos, s"Ghost should not be materialized: $ghost (application: ${c.macroApplication}, prefix = ${c.prefix})") | ||
// q"$ghost.materialized" | ||
q"null" | ||
}) | ||
} | ||
|
||
def applyDynamicImpl[A : c.WeakTypeTag](c: whitebox.Context)(name: c.Expr[String])(args: c.Expr[Any]*): c.Tree = { | ||
import c.universe._ | ||
|
||
val Seq() = args.toSeq | ||
val Literal(Constant(nameStr: String)) = name.tree | ||
q""" | ||
${c.prefix}.${TermName(nameStr)} | ||
""" | ||
} | ||
def selectDynamicImpl[A : c.WeakTypeTag](c: whitebox.Context)(name: c.Expr[String]): c.Tree = { | ||
import c.universe._ | ||
|
||
val a = weakTypeOf[A] | ||
val members = getMembers(c.universe)(a) | ||
|
||
val Literal(Constant(nameStr: String)) = name.tree | ||
|
||
members.zipWithIndex.find(_._1._1.toString == nameStr).map({ | ||
case ((_, tpe), fiberIndex) => | ||
c.prefix.tree match { | ||
case Apply(Select(array, name), List(index)) if name.toString == "apply" => | ||
// case q"$array.apply($index)" => | ||
q"$array.fibers($fiberIndex).asInstanceOf[Array[${tpe}]]($index)" | ||
case ghost => | ||
val ghostName = TermName(c.freshName("ghost")) | ||
q""" | ||
val $ghostName = $ghost | ||
$ghostName.flatArray.fibers($fiberIndex).asInstanceOf[Array[${tpe}]]($ghostName.index) | ||
""" | ||
} | ||
}).getOrElse { | ||
c.error(name.tree.pos, "No such method on $a: $name") | ||
q"null" | ||
} | ||
} | ||
} | ||
|
||
class FlatArray[A : FlatIO](val fibers: Fibers, val length: Int) | ||
{ | ||
def this(length: Int) = | ||
this(fibers = implicitly[FlatIO[A]].buildFibers(length), length = length) | ||
|
||
// private[this] def writeArray(a: Array[A]): Unit = | ||
// macro FlatArray.writeArrayImpl[A] | ||
|
||
def materialize(index: Int): A = | ||
implicitly[FlatIO[A]].get(fibers, index) | ||
|
||
def apply(index: Int): Ghost[A] = | ||
new Ghost[A](this, index) | ||
|
||
// def update(index: Int, value: A): Unit = | ||
// macro FlatArray.updateImpl[A] | ||
|
||
// def toArray: Array[A] = | ||
// macro FlatArray.toArrayImpl[A] | ||
} | ||
|
||
object FlatArray { | ||
def writeArrayImpl[A : c.WeakTypeTag](c: blackbox.Context)(a: c.Expr[Array[A]]): c.Expr[Unit] = { | ||
import c.universe._ | ||
|
||
val a = weakTypeOf[A] | ||
val members = getMembers(c.universe)(a).map({ | ||
case (name, tpe) => | ||
(name, tpe, TermName(c.freshName(name.toString))) | ||
}) | ||
var flatArrayName = c.freshName("flatArray") | ||
var arrayName = c.freshName("array") | ||
var lengthName = c.freshName("length") | ||
var indexName = c.freshName("index") | ||
|
||
c.Expr[Unit](q""" | ||
val $flatArrayName = ${c.prefix} | ||
val $arrayName = $a | ||
val $lengthName = $arrayName.length | ||
require($lengthName == $flatArrayName.length) | ||
..${members.zipWithIndex.map({ | ||
case ((name, tpe, fiberName), i) => | ||
q"val $fiberName = fibers($i).asInstanceOf[Array[${tpe}]]" | ||
})} | ||
for ($indexName <- 0 until $lengthName) { | ||
..${members.zipWithIndex.map({ | ||
case ((name, tpe, fiberName), i) => | ||
q"$fiberName(index) = value" | ||
})} | ||
} | ||
""") | ||
} | ||
// def updateImpl[A : c.WeakTypeTag](c: blackbox.Context)(index: c.Expr[Int], value: c.Expr[A]): c.Expr[Unit] = { | ||
// import c.universe._ | ||
// | ||
// ??? | ||
// } | ||
def toArrayImpl[A : c.WeakTypeTag](c: blackbox.Context): c.Expr[Array[A]] = { | ||
import c.universe._ | ||
|
||
|
||
val a = weakTypeOf[A] | ||
val members = getMembers(c.universe)(a).map({ | ||
case (name, tpe) => | ||
(name, tpe, TermName(c.freshName(name.toString))) | ||
}) | ||
var flatArrayName = c.freshName("flatArray") | ||
var arrayName = c.freshName("array") | ||
var lengthName = c.freshName("length") | ||
var indexName = c.freshName("index") | ||
|
||
c.Expr[Array[A]](q""" | ||
val $flatArrayName = ${c.prefix} | ||
val $lengthName = $flatArrayName.length | ||
val $arrayName = new Array[${a}]($lengthName) | ||
..${members.zipWithIndex.map({ | ||
case ((name, tpe, fiberName), i) => | ||
q"val $fiberName = fibers($i).asInstanceOf[Array[${tpe}]]" | ||
})} | ||
for ($indexName <- 0 until $lengthName) { | ||
$arrayName($indexName) = ${weakTypeOf[A].typeSymbol.companion}( | ||
..${members.zipWithIndex.map({ | ||
case ((name, tpe, fiberName), i) => | ||
q"$fiberName(index)" | ||
})} | ||
) | ||
} | ||
""") | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
package scalaxy.flatarray | ||
package test | ||
|
||
import org.junit._ | ||
import scala.language.dynamics | ||
|
||
import scala.collection.JavaConversions._ | ||
|
||
class FlatArrayTest | ||
{ | ||
case class Foo( | ||
a: Int, | ||
b: Float, | ||
c: Double, | ||
pt: (Double, Double)) | ||
{ | ||
/// This can't be optimized away: it causes materialization of the class | ||
def sum = a + b | ||
|
||
|
||
/// Ensures this is not instantiated! | ||
??? | ||
} | ||
|
||
|
||
implicit val FooIO: FlatIO[Foo] = FlatIO.of[Foo] | ||
|
||
def sumOptimized(foo: Ghost[Foo]) = foo.a + foo.b | ||
|
||
@Test | ||
def test { | ||
val n = 100 | ||
val in: FlatArray[Foo] = new FlatArray[Foo](n) | ||
|
||
val out = new Array[Float](n) | ||
for (i <- 0 until n) { | ||
val item = in(i) | ||
|
||
out(i) = item.a + item.b | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
#ifndef _SCALAXY_ERROR_HPP | ||
#define _SCALAXY_ERROR_HPP | ||
|
||
namespace scalaxy { | ||
|
||
class error { | ||
const char *m_message; | ||
public: | ||
error(const char *message) : m_message(message) { } | ||
void throw_if_needed(JNIEnv *env) const { | ||
if (env->ExceptionCheck()) { | ||
return; | ||
} | ||
jclass clazz = env->FindClass("java/lang/RuntimeException"); | ||
env->ThrowNew(clazz, m_message); | ||
} | ||
static void caught_unexpected(JNIEnv *env) { | ||
error("Unexpected native error!").throw_if_needed(env); | ||
} | ||
}; | ||
|
||
} // namespace scalaxy | ||
|
||
#endif // _SCALAXY_ERROR_HPP |
Oops, something went wrong.