From 9ff84c60da5f7d8b1ba321be1addee1cc6da031e Mon Sep 17 00:00:00 2001 From: Aggelos Biboudis Date: Mon, 19 Nov 2018 14:51:00 +0100 Subject: [PATCH 01/45] Add basic structure --- .../run-with-compiler/tasty-interpreter.check | 0 .../tasty-interpreter/Foo.scala | 13 +++ .../tasty-interpreter/Test.scala | 96 +++++++++++++++++++ 3 files changed, 109 insertions(+) create mode 100644 tests/run-with-compiler/tasty-interpreter.check create mode 100644 tests/run-with-compiler/tasty-interpreter/Foo.scala create mode 100644 tests/run-with-compiler/tasty-interpreter/Test.scala diff --git a/tests/run-with-compiler/tasty-interpreter.check b/tests/run-with-compiler/tasty-interpreter.check new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/run-with-compiler/tasty-interpreter/Foo.scala b/tests/run-with-compiler/tasty-interpreter/Foo.scala new file mode 100644 index 000000000000..91482616c329 --- /dev/null +++ b/tests/run-with-compiler/tasty-interpreter/Foo.scala @@ -0,0 +1,13 @@ +object Foo { + def main(args: Array[String]): Unit = { + val x1 = 42 + println(x1) + + lazy val x2 = println("Hello") + x2 + x2 + + def x3 = 42 + println(x3) + } +} diff --git a/tests/run-with-compiler/tasty-interpreter/Test.scala b/tests/run-with-compiler/tasty-interpreter/Test.scala new file mode 100644 index 000000000000..3777714f1634 --- /dev/null +++ b/tests/run-with-compiler/tasty-interpreter/Test.scala @@ -0,0 +1,96 @@ +import scala.tasty.Reflection +import scala.tasty.file._ + +object Test { + def main(args: Array[String]): Unit = { + ConsumeTasty("", List("Foo"), new TastyInterpreter) + } +} + +trait Ref { + def value: Any +} +class Eager(val value: Any) extends Ref +class Lazy(thunk: => Any) extends Ref { + lazy val value: Any = thunk +} + +class Interpreter[R <: Reflection & Singleton](val reflect: R)(implicit ctx: reflect.Context) { + import reflect._ + + type Env = Map[Symbol, Ref] + + def eval(tree: Statement)(implicit env: Env): Any = { + // Our debug println + // println(tree.show) + + tree match { + case Term.Apply(Term.Ident("println"), List(arg)) => + println(eval(arg)) + + case Term.Apply(Term.Select(a, "+", Some(sig)), List(b)) => + eval(a).asInstanceOf[Int] + eval(b).asInstanceOf[Int] + + case Term.Ident(_) => + + tree.symbol match { + case IsDefSymbol(sym) => + eval(sym.tree.rhs.get) + case _ => + env(tree.symbol).value + } + + case Term.Block(stats, expr) => + + val newEnv = stats.foldLeft(env)((accEnv, stat) => stat match { + case ValDef(name, tpt, Some(rhs)) => + + val evalRef: Ref = + if (stat.symbol.flags.isLazy) + new Lazy(eval(rhs)(accEnv)) + else + new Eager(eval(rhs)(accEnv)) + + accEnv.updated(stat.symbol, evalRef) + case DefDef(_, _, _, _, _) => + // TODO: record the environment for closure purposes + accEnv + case stat => + eval(stat)(accEnv) + accEnv + }) + + eval(expr)(newEnv) + + + case Term.Literal(const) => + const.value + + case Term.Typed(expr, _) => eval(expr) + case _ => throw new MatchError(tree.show) + } + } +} + +class TastyInterpreter extends TastyConsumer { + + final def apply(reflect: Reflection)(root: reflect.Tree): Unit = { + import reflect._ + object Traverser extends TreeTraverser { + + override def traverseTree(tree: Tree)(implicit ctx: Context): Unit = tree match { + // TODO: check the correct sig and object enclosement for main + case DefDef("main", _, _, _, Some(rhs)) => + val interpreter = new Interpreter(reflect) + + println("Found main") + + interpreter.eval(rhs)(Map.empty) + // TODO: recurse only for PackageDef, ClassDef + case tree => + super.traverseTree(tree) + } + } + Traverser.traverseTree(root)(reflect.rootContext) + } +} From 55dd7fc6050873ddb29b557dc8ab95e03de77811 Mon Sep 17 00:00:00 2001 From: Aggelos Biboudis Date: Tue, 20 Nov 2018 10:58:54 +0100 Subject: [PATCH 02/45] Update tasty-interpreter.check --- tests/run-with-compiler/tasty-interpreter.check | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/run-with-compiler/tasty-interpreter.check b/tests/run-with-compiler/tasty-interpreter.check index e69de29bb2d1..d8e49b786317 100644 --- a/tests/run-with-compiler/tasty-interpreter.check +++ b/tests/run-with-compiler/tasty-interpreter.check @@ -0,0 +1,4 @@ +Found main +42 +Hello +42 From fe86dd60d8db55b3b5dbc5daaf8c5e8368328d64 Mon Sep 17 00:00:00 2001 From: Aggelos Biboudis Date: Tue, 27 Nov 2018 16:27:37 +0100 Subject: [PATCH 03/45] LF endings --- tests/run-with-compiler/tasty-interpreter.check | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/run-with-compiler/tasty-interpreter.check b/tests/run-with-compiler/tasty-interpreter.check index d8e49b786317..f93e30951915 100644 --- a/tests/run-with-compiler/tasty-interpreter.check +++ b/tests/run-with-compiler/tasty-interpreter.check @@ -1,4 +1,4 @@ -Found main -42 -Hello -42 +Found main +42 +Hello +42 From 7e75c4301b471079c973e7da7543b672603189b6 Mon Sep 17 00:00:00 2001 From: Aggelos Biboudis Date: Tue, 27 Nov 2018 17:28:04 +0100 Subject: [PATCH 04/45] Handle simple method calls --- .../run-with-compiler/tasty-interpreter.check | 17 ++++- .../tasty-interpreter/Foo.scala | 42 +++++++++-- .../tasty-interpreter/Test.scala | 71 ++++++++++++++++--- 3 files changed, 111 insertions(+), 19 deletions(-) diff --git a/tests/run-with-compiler/tasty-interpreter.check b/tests/run-with-compiler/tasty-interpreter.check index f93e30951915..3f7d10d1140e 100644 --- a/tests/run-with-compiler/tasty-interpreter.check +++ b/tests/run-with-compiler/tasty-interpreter.check @@ -1,4 +1,19 @@ -Found main 42 + Hello + 42 + +43 + +if + +5 +4 +3 +2 +1 + +42 + +55 diff --git a/tests/run-with-compiler/tasty-interpreter/Foo.scala b/tests/run-with-compiler/tasty-interpreter/Foo.scala index 91482616c329..8189de2f9017 100644 --- a/tests/run-with-compiler/tasty-interpreter/Foo.scala +++ b/tests/run-with-compiler/tasty-interpreter/Foo.scala @@ -1,13 +1,41 @@ object Foo { def main(args: Array[String]): Unit = { - val x1 = 42 - println(x1) + val x1 = 42 + println(x1) + println() - lazy val x2 = println("Hello") - x2 - x2 + lazy val x2 = println("Hello") + x2 + x2 + println() - def x3 = 42 - println(x3) + def x3 = 42 + println(x3) + println() + + var x4: Int = 42 + x4 = 43 + println(x4) + println() + + if(x1 == 42) + println("if") + else + println("else") + println() + + var x5 = 5 + while(x5 > 0){ + println(x5) + x5 = x5 - 1 + } + println() + + def meth() = 42 + println(meth()) + println() + + def methP(i: Int) = i + println(methP(55)) } } diff --git a/tests/run-with-compiler/tasty-interpreter/Test.scala b/tests/run-with-compiler/tasty-interpreter/Test.scala index 3777714f1634..cf746bd0976d 100644 --- a/tests/run-with-compiler/tasty-interpreter/Test.scala +++ b/tests/run-with-compiler/tasty-interpreter/Test.scala @@ -8,16 +8,33 @@ object Test { } trait Ref { - def value: Any + def get: Any } -class Eager(val value: Any) extends Ref +class Eager(val get: Any) extends Ref class Lazy(thunk: => Any) extends Ref { - lazy val value: Any = thunk + lazy val get: Any = thunk } +class Var(private var value: Any) extends Ref { + def get = value + + def update(rhs: Any): Unit = { + value = rhs + } +} + class Interpreter[R <: Reflection & Singleton](val reflect: R)(implicit ctx: reflect.Context) { import reflect._ + object Call { + def unapply(arg: Tree): Option[(Tree, List[Tree])] = arg match { + case fn@ Term.Select(_, _, _) => Some((fn, Nil)) + case Term.Apply(Call(fn, args1), args2) => Some((fn, args1 ::: args2)) + case Term.TypeApply(Call(fn, args), _) => Some((fn, args)) + case _ => None + } + } + type Env = Map[Symbol, Ref] def eval(tree: Statement)(implicit env: Env): Any = { @@ -28,26 +45,60 @@ class Interpreter[R <: Reflection & Singleton](val reflect: R)(implicit ctx: ref case Term.Apply(Term.Ident("println"), List(arg)) => println(eval(arg)) - case Term.Apply(Term.Select(a, "+", Some(sig)), List(b)) => - eval(a).asInstanceOf[Int] + eval(b).asInstanceOf[Int] + case Term.Apply(Term.Ident("println"), List()) => + println() - case Term.Ident(_) => + case Term.Apply(Term.Select(a, op, Some(sig)), List(b)) => + op match { + case "+" => eval(a).asInstanceOf[Int] + eval(b).asInstanceOf[Int] + case "-" => eval(a).asInstanceOf[Int] - eval(b).asInstanceOf[Int] + case ">" => eval(a).asInstanceOf[Int] > eval(b).asInstanceOf[Int] + case "==" => eval(a).asInstanceOf[Int] == eval(b).asInstanceOf[Int] + } + + // TODO: handle multiple parameters + case Term.Apply(meth@Term.Ident(_), args) => + meth.symbol match { + case IsDefSymbol(sym) => + val evaluatedArgs = args.map(arg => new Eager(eval(arg))) + val env1 = env ++ sym.tree.paramss.head.map(_.symbol).zip(evaluatedArgs) + eval(sym.tree.rhs.get)(env1) + } + + case Term.Ident(_) => tree.symbol match { case IsDefSymbol(sym) => eval(sym.tree.rhs.get) case _ => - env(tree.symbol).value + env(tree.symbol).get } - case Term.Block(stats, expr) => + case Term.Assign(lhs, rhs) => + env(lhs.symbol) match { + case varf: Var => varf.update(eval(rhs)) + } + case Term.If(cond, thenp, elsep) => + if(eval(cond).asInstanceOf[Boolean]) + eval(thenp) + else + eval(elsep) + + case Term.While(cond, expr) => + while(eval(cond).asInstanceOf[Boolean]){ + eval(expr) + } + + case Term.Block(stats, expr) => val newEnv = stats.foldLeft(env)((accEnv, stat) => stat match { case ValDef(name, tpt, Some(rhs)) => val evalRef: Ref = if (stat.symbol.flags.isLazy) - new Lazy(eval(rhs)(accEnv)) + new Lazy(eval(rhs)(accEnv)) // do not factor out rhs from here (laziness) + else if (stat.symbol.flags.isMutable) + new Var(eval(rhs)(accEnv)) else new Eager(eval(rhs)(accEnv)) @@ -83,8 +134,6 @@ class TastyInterpreter extends TastyConsumer { case DefDef("main", _, _, _, Some(rhs)) => val interpreter = new Interpreter(reflect) - println("Found main") - interpreter.eval(rhs)(Map.empty) // TODO: recurse only for PackageDef, ClassDef case tree => From b65b1b09fafab1d925d1dc981ec0b9a74197ae8f Mon Sep 17 00:00:00 2001 From: Aggelos Biboudis Date: Tue, 27 Nov 2018 17:34:32 +0100 Subject: [PATCH 05/45] Update extractor --- tests/run-with-compiler/tasty-interpreter/Test.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/run-with-compiler/tasty-interpreter/Test.scala b/tests/run-with-compiler/tasty-interpreter/Test.scala index cf746bd0976d..aa4302bf8835 100644 --- a/tests/run-with-compiler/tasty-interpreter/Test.scala +++ b/tests/run-with-compiler/tasty-interpreter/Test.scala @@ -27,9 +27,9 @@ class Interpreter[R <: Reflection & Singleton](val reflect: R)(implicit ctx: ref import reflect._ object Call { - def unapply(arg: Tree): Option[(Tree, List[Tree])] = arg match { + def unapply(arg: Tree): Option[(Tree, List[List[Tree]])] = arg match { case fn@ Term.Select(_, _, _) => Some((fn, Nil)) - case Term.Apply(Call(fn, args1), args2) => Some((fn, args1 ::: args2)) + case Term.Apply(Call(fn, args1), args2) => Some((fn, args1 :+ args2)) case Term.TypeApply(Call(fn, args), _) => Some((fn, args)) case _ => None } From 6c11c9d5bb5b3c7029675357bfb0f8ba40adb4fb Mon Sep 17 00:00:00 2001 From: Aggelos Biboudis Date: Fri, 7 Dec 2018 16:46:36 +0100 Subject: [PATCH 06/45] Move interpreter --- .../tasty-interpreter/Interpreter.scala | 101 ++++++++++++++++++ .../tasty-interpreter/Test.scala | 101 ------------------ 2 files changed, 101 insertions(+), 101 deletions(-) create mode 100644 tests/run-with-compiler/tasty-interpreter/Interpreter.scala diff --git a/tests/run-with-compiler/tasty-interpreter/Interpreter.scala b/tests/run-with-compiler/tasty-interpreter/Interpreter.scala new file mode 100644 index 000000000000..0b27688b63e9 --- /dev/null +++ b/tests/run-with-compiler/tasty-interpreter/Interpreter.scala @@ -0,0 +1,101 @@ +import scala.tasty.Reflection + +class Interpreter[R <: Reflection & Singleton](val reflect: R)(implicit ctx: reflect.Context) { + import reflect._ + + type Env = Map[Symbol, Ref] + + // object Call { + // def unapply(arg: Tree): Option[(Tree, List[List[Tree]])] = arg match { + // case fn@ Term.Select(_, _, _) => Some((fn, Nil)) + // case Term.Apply(Call(fn, args1), args2) => Some((fn, args1 :+ args2)) + // case Term.TypeApply(Call(fn, args), _) => Some((fn, args)) + // case _ => None + // } + // } + + def eval(tree: Statement)(implicit env: Env): Any = { + // Our debug println + // println(tree.show) + + tree match { + case Term.Apply(Term.Ident("println"), List(arg)) => + println(eval(arg)) + + case Term.Apply(Term.Ident("println"), List()) => + println() + + case Term.Apply(Term.Select(a, op), List(b)) => + op match { + case "+" => eval(a).asInstanceOf[Int] + eval(b).asInstanceOf[Int] + case "-" => eval(a).asInstanceOf[Int] - eval(b).asInstanceOf[Int] + case ">" => eval(a).asInstanceOf[Int] > eval(b).asInstanceOf[Int] + case "==" => eval(a).asInstanceOf[Int] == eval(b).asInstanceOf[Int] + } + + // TODO: handle multiple parameters + case Term.Apply(meth@Term.Ident(_), args) => + meth.symbol match { + case IsDefSymbol(sym) => + val evaluatedArgs = args.map(arg => new Eager(eval(arg))) + val env1 = env ++ sym.tree.paramss.head.map(_.symbol).zip(evaluatedArgs) + + eval(sym.tree.rhs.get)(env1) + } + + case Term.Ident(_) => + tree.symbol match { + case IsDefSymbol(sym) => + eval(sym.tree.rhs.get) + case _ => + env(tree.symbol).get + } + + case Term.Assign(lhs, rhs) => + env(lhs.symbol) match { + case varf: Var => varf.update(eval(rhs)) + } + + case Term.If(cond, thenp, elsep) => + if(eval(cond).asInstanceOf[Boolean]) + eval(thenp) + else + eval(elsep) + + case Term.While(cond, expr) => + while(eval(cond).asInstanceOf[Boolean]){ + eval(expr) + } + + case Term.Block(stats, expr) => + val newEnv = stats.foldLeft(env)((accEnv, stat) => stat match { + case ValDef(name, tpt, Some(rhs)) => + + val evalRef: Ref = + if (stat.symbol.flags.isLazy) + new Lazy(eval(rhs)(accEnv)) // do not factor out rhs from here (laziness) + else if (stat.symbol.flags.isMutable) + new Var(eval(rhs)(accEnv)) + else + new Eager(eval(rhs)(accEnv)) + + accEnv.updated(stat.symbol, evalRef) + case DefDef(_, _, _, _, _) => + // TODO: record the environment for closure purposes + accEnv + case stat => + eval(stat)(accEnv) + accEnv + }) + eval(expr)(newEnv) + + + case Term.Literal(const) => + const.value + + case Term.Typed(expr, _) => eval(expr) + + case _ => throw new MatchError(tree.show) + } + } +} \ No newline at end of file diff --git a/tests/run-with-compiler/tasty-interpreter/Test.scala b/tests/run-with-compiler/tasty-interpreter/Test.scala index aa4302bf8835..88e7bf492ec9 100644 --- a/tests/run-with-compiler/tasty-interpreter/Test.scala +++ b/tests/run-with-compiler/tasty-interpreter/Test.scala @@ -22,107 +22,6 @@ class Var(private var value: Any) extends Ref { } } - -class Interpreter[R <: Reflection & Singleton](val reflect: R)(implicit ctx: reflect.Context) { - import reflect._ - - object Call { - def unapply(arg: Tree): Option[(Tree, List[List[Tree]])] = arg match { - case fn@ Term.Select(_, _, _) => Some((fn, Nil)) - case Term.Apply(Call(fn, args1), args2) => Some((fn, args1 :+ args2)) - case Term.TypeApply(Call(fn, args), _) => Some((fn, args)) - case _ => None - } - } - - type Env = Map[Symbol, Ref] - - def eval(tree: Statement)(implicit env: Env): Any = { - // Our debug println - // println(tree.show) - - tree match { - case Term.Apply(Term.Ident("println"), List(arg)) => - println(eval(arg)) - - case Term.Apply(Term.Ident("println"), List()) => - println() - - case Term.Apply(Term.Select(a, op, Some(sig)), List(b)) => - op match { - case "+" => eval(a).asInstanceOf[Int] + eval(b).asInstanceOf[Int] - case "-" => eval(a).asInstanceOf[Int] - eval(b).asInstanceOf[Int] - case ">" => eval(a).asInstanceOf[Int] > eval(b).asInstanceOf[Int] - case "==" => eval(a).asInstanceOf[Int] == eval(b).asInstanceOf[Int] - } - - // TODO: handle multiple parameters - case Term.Apply(meth@Term.Ident(_), args) => - meth.symbol match { - case IsDefSymbol(sym) => - val evaluatedArgs = args.map(arg => new Eager(eval(arg))) - val env1 = env ++ sym.tree.paramss.head.map(_.symbol).zip(evaluatedArgs) - - eval(sym.tree.rhs.get)(env1) - } - - case Term.Ident(_) => - tree.symbol match { - case IsDefSymbol(sym) => - eval(sym.tree.rhs.get) - case _ => - env(tree.symbol).get - } - - case Term.Assign(lhs, rhs) => - env(lhs.symbol) match { - case varf: Var => varf.update(eval(rhs)) - } - - case Term.If(cond, thenp, elsep) => - if(eval(cond).asInstanceOf[Boolean]) - eval(thenp) - else - eval(elsep) - - case Term.While(cond, expr) => - while(eval(cond).asInstanceOf[Boolean]){ - eval(expr) - } - - case Term.Block(stats, expr) => - val newEnv = stats.foldLeft(env)((accEnv, stat) => stat match { - case ValDef(name, tpt, Some(rhs)) => - - val evalRef: Ref = - if (stat.symbol.flags.isLazy) - new Lazy(eval(rhs)(accEnv)) // do not factor out rhs from here (laziness) - else if (stat.symbol.flags.isMutable) - new Var(eval(rhs)(accEnv)) - else - new Eager(eval(rhs)(accEnv)) - - accEnv.updated(stat.symbol, evalRef) - case DefDef(_, _, _, _, _) => - // TODO: record the environment for closure purposes - accEnv - case stat => - eval(stat)(accEnv) - accEnv - }) - - eval(expr)(newEnv) - - - case Term.Literal(const) => - const.value - - case Term.Typed(expr, _) => eval(expr) - case _ => throw new MatchError(tree.show) - } - } -} - class TastyInterpreter extends TastyConsumer { final def apply(reflect: Reflection)(root: reflect.Tree): Unit = { From a5fd508640a4797a59924185e4614b9030546a46 Mon Sep 17 00:00:00 2001 From: Aggelos Biboudis Date: Fri, 7 Dec 2018 17:42:13 +0100 Subject: [PATCH 07/45] Add JVM reflect --- .../tools/dotc/tastyreflect/FlagSet.scala | 70 +++++++++++++++ .../dotc/tastyreflect/SymbolOpsImpl.scala | 8 +- library/src/scala/tasty/reflect/FlagSet.scala | 88 ++++++++++++++++++ .../src/scala/tasty/reflect/SymbolOps.scala | 3 +- .../run-with-compiler/tasty-interpreter.check | 1 + .../{Foo.scala => InterpretedMain.scala} | 4 +- .../tasty-interpreter/Interpreter.scala | 89 ++++++++++++++----- .../tasty-interpreter/Precompiled.scala | 8 ++ .../tasty-interpreter/Test.scala | 2 +- 9 files changed, 244 insertions(+), 29 deletions(-) create mode 100644 compiler/src/dotty/tools/dotc/tastyreflect/FlagSet.scala create mode 100644 library/src/scala/tasty/reflect/FlagSet.scala rename tests/run-with-compiler/tasty-interpreter/{Foo.scala => InterpretedMain.scala} (91%) create mode 100644 tests/run-with-compiler/tasty-interpreter/Precompiled.scala diff --git a/compiler/src/dotty/tools/dotc/tastyreflect/FlagSet.scala b/compiler/src/dotty/tools/dotc/tastyreflect/FlagSet.scala new file mode 100644 index 000000000000..5aabdfc97b30 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/tastyreflect/FlagSet.scala @@ -0,0 +1,70 @@ +package dotty.tools.dotc.tastyreflect + +import dotty.tools.dotc.core.Flags +import dotty.tools.dotc.core.Flags._ + +class FlagSet(flags: Flags.FlagSet) extends scala.tasty.reflect.FlagSet { + + def isProtected: Boolean = flags.is(Protected) + def isAbstract: Boolean = flags.is(Abstract) + def isFinal: Boolean = flags.is(Final) + def isSealed: Boolean = flags.is(Sealed) + def isCase: Boolean = flags.is(Case) + def isImplicit: Boolean = flags.is(Implicit) + def isErased: Boolean = flags.is(Erased) + def isLazy: Boolean = flags.is(Lazy) + def isOverride: Boolean = flags.is(Override) + def isInline: Boolean = flags.is(Inline) + def isMacro: Boolean = flags.is(Macro) + def isStatic: Boolean = flags.is(JavaStatic) + def isObject: Boolean = flags.is(Module) + def isTrait: Boolean = flags.is(Trait) + def isLocal: Boolean = flags.is(Local) + def isSynthetic: Boolean = flags.is(Synthetic) + def isArtifact: Boolean = flags.is(Artifact) + def isMutable: Boolean = flags.is(Mutable) + def isFieldAccessor: Boolean = flags.is(Accessor) + def isCaseAcessor: Boolean = flags.is(CaseAccessor) + def isCovariant: Boolean = flags.is(Covariant) + def isContravariant: Boolean = flags.is(Contravariant) + def isScala2X: Boolean = flags.is(Scala2x) + def isDefaultParameterized: Boolean = flags.is(DefaultParameterized) + def isStable: Boolean = flags.is(Stable) + def isParam: Boolean = flags.is(Param) + def isParamAccessor: Boolean = flags.is(ParamAccessor) + def isPackage: Boolean = flags.is(Package) + + override def toString: String = { + val flags = List.newBuilder[String] + if (isProtected) flags += "protected " + if (isAbstract) flags += "abstract" + if (isFinal) flags += "final" + if (isSealed) flags += "sealed" + if (isCase) flags += "case" + if (isImplicit) flags += "implicit" + if (isErased) flags += "erased" + if (isLazy) flags += "lazy" + if (isOverride) flags += "override" + if (isInline) flags += "inline" + if (isMacro) flags += "macro" + if (isStatic) flags += "javaStatic" + if (isObject) flags += "object" + if (isTrait) flags += "trait" + if (isLocal) flags += "local" + if (isSynthetic) flags += "synthetic" + if (isArtifact) flags += "artifact" + if (isMutable) flags += "mutable" + if (isFieldAccessor) flags += "accessor" + if (isCaseAcessor) flags += "caseAccessor" + if (isCovariant) flags += "covariant" + if (isContravariant) flags += "contravariant" + if (isScala2X) flags += "scala2x" + if (isDefaultParameterized) flags += "defaultParameterized" + if (isStable) flags += "stable" + if (isParam) flags += "param" + if (isParamAccessor) flags += "paramAccessor" + if (isPackage) flags += "package" + flags.result().mkString("<", ",", ">") + } + +} diff --git a/compiler/src/dotty/tools/dotc/tastyreflect/SymbolOpsImpl.scala b/compiler/src/dotty/tools/dotc/tastyreflect/SymbolOpsImpl.scala index 9f57f543effe..7117280c56ca 100644 --- a/compiler/src/dotty/tools/dotc/tastyreflect/SymbolOpsImpl.scala +++ b/compiler/src/dotty/tools/dotc/tastyreflect/SymbolOpsImpl.scala @@ -76,6 +76,10 @@ trait SymbolOpsImpl extends scala.tasty.reflect.SymbolOps with CoreImpl { } } + def isDefinedInCurrentRun(implicit ctx: Context): Boolean = { + symbol.topLevelClass.asClass.isDefinedInCurrentRun + } + } object IsPackageSymbol extends IsPackageSymbolModule { @@ -181,8 +185,8 @@ trait SymbolOpsImpl extends scala.tasty.reflect.SymbolOps with CoreImpl { def ValSymbolDeco(symbol: ValSymbol): ValSymbolAPI = new ValSymbolAPI { def tree(implicit ctx: Context): ValDef = FromSymbol.valDefFromSym(symbol) - def companionClass(implicit ctx: Context): Option[ClassSymbol] = { - val sym = symbol.companionClass + def moduleClass(implicit ctx: Context): Option[ClassSymbol] = { + val sym = symbol.moduleClass if (sym.exists) Some(sym.asClass) else None } } diff --git a/library/src/scala/tasty/reflect/FlagSet.scala b/library/src/scala/tasty/reflect/FlagSet.scala new file mode 100644 index 000000000000..67b11a7c35eb --- /dev/null +++ b/library/src/scala/tasty/reflect/FlagSet.scala @@ -0,0 +1,88 @@ +package scala.tasty.reflect + +trait FlagSet { + + /** Is this symbol `protected` */ + def isProtected: Boolean + + /** Is this symbol `abstract` */ + def isAbstract: Boolean + + /** Is this symbol `final` */ + def isFinal: Boolean + + /** Is this symbol `sealed` */ + def isSealed: Boolean + + /** Is this symbol `case` */ + def isCase: Boolean + + /** Is this symbol `implicit` */ + def isImplicit: Boolean + + /** Is this symbol `erased` */ + def isErased: Boolean + + /** Is this symbol `lazy` */ + def isLazy: Boolean + + /** Is this symbol `override` */ + def isOverride: Boolean + + /** Is this symbol `inline` */ + def isInline: Boolean + + /** Is this symbol markes as a macro. An inline method containing toplevel splices */ + def isMacro: Boolean + + /** Is this symbol marked as static. Mapped to static Java member */ + def isStatic: Boolean + + /** Is this symbol an object or its class (used for a ValDef or a ClassDef extends Modifier respectively) */ + def isObject: Boolean + + /** Is this symbol a trait */ + def isTrait: Boolean + + /** Is this symbol local? Used in conjunction with Private/private[Type] to mean private[this] extends Modifier proctected[this] */ + def isLocal: Boolean + + /** Was this symbol generated by Scala compiler */ + def isSynthetic: Boolean + + /** Is this symbol to be tagged Java Synthetic */ + def isArtifact: Boolean + + /** Is this symbol a `var` (when used on a ValDef) */ + def isMutable: Boolean + + /** Is this symbol a getter or a setter */ + def isFieldAccessor: Boolean + + /** Is this symbol a getter for case class parameter */ + def isCaseAcessor: Boolean + + /** Is this symbol a type parameter marked as covariant `+` */ + def isCovariant: Boolean + + /** Is this symbol a type parameter marked as contravariant `-` */ + def isContravariant: Boolean + + /** Was this symbol imported from Scala2.x */ + def isScala2X: Boolean + + /** Is this symbol a method with default parameters */ + def isDefaultParameterized: Boolean + + /** Is this symbol member that is assumed to be stable */ + def isStable: Boolean + + /** Is this symbol a parameter */ + def isParam: Boolean + + /** Is this symbol a parameter accessor */ + def isParamAccessor: Boolean + + /** Is this symbol a package */ + def isPackage: Boolean +} diff --git a/library/src/scala/tasty/reflect/SymbolOps.scala b/library/src/scala/tasty/reflect/SymbolOps.scala index e1915c9da180..fa6ed8e72407 100644 --- a/library/src/scala/tasty/reflect/SymbolOps.scala +++ b/library/src/scala/tasty/reflect/SymbolOps.scala @@ -51,6 +51,7 @@ trait SymbolOps extends Core { /** Annotations attached to this symbol */ def annots(implicit ctx: Context): List[Term] + def isDefinedInCurrentRun(implicit ctx: Context): Boolean } implicit def SymbolDeco(symbol: Symbol): SymbolAPI @@ -151,7 +152,7 @@ trait SymbolOps extends Core { def tree(implicit ctx: Context): ValDef /** The class symbol of the companion module class */ - def companionClass(implicit ctx: Context): Option[ClassSymbol] + def moduleClass(implicit ctx: Context): Option[ClassSymbol] } implicit def ValSymbolDeco(symbol: ValSymbol): ValSymbolAPI diff --git a/tests/run-with-compiler/tasty-interpreter.check b/tests/run-with-compiler/tasty-interpreter.check index 3f7d10d1140e..92c44d0b0d18 100644 --- a/tests/run-with-compiler/tasty-interpreter.check +++ b/tests/run-with-compiler/tasty-interpreter.check @@ -17,3 +17,4 @@ if 42 55 +precompiledModule \ No newline at end of file diff --git a/tests/run-with-compiler/tasty-interpreter/Foo.scala b/tests/run-with-compiler/tasty-interpreter/InterpretedMain.scala similarity index 91% rename from tests/run-with-compiler/tasty-interpreter/Foo.scala rename to tests/run-with-compiler/tasty-interpreter/InterpretedMain.scala index 8189de2f9017..1ca428c2e092 100644 --- a/tests/run-with-compiler/tasty-interpreter/Foo.scala +++ b/tests/run-with-compiler/tasty-interpreter/InterpretedMain.scala @@ -1,4 +1,4 @@ -object Foo { +object IntepretedMain { def main(args: Array[String]): Unit = { val x1 = 42 println(x1) @@ -37,5 +37,7 @@ object Foo { def methP(i: Int) = i println(methP(55)) + + println(Precompiled) } } diff --git a/tests/run-with-compiler/tasty-interpreter/Interpreter.scala b/tests/run-with-compiler/tasty-interpreter/Interpreter.scala index 0b27688b63e9..39447574ac66 100644 --- a/tests/run-with-compiler/tasty-interpreter/Interpreter.scala +++ b/tests/run-with-compiler/tasty-interpreter/Interpreter.scala @@ -1,18 +1,53 @@ import scala.tasty.Reflection +class JVMReflection[R <: Reflection & Singleton](val reflect: R) { + import reflect._ + + private val classLoader: ClassLoader = getClass.getClassLoader + + // taken from StdNames + final val MODULE_INSTANCE_FIELD = "MODULE$" + + def loadModule(sym: Symbol): Object = { + if (sym.owner.flags.isPackage) { + // is top level object + val moduleClass = loadClass(sym.fullName) + moduleClass.getField(MODULE_INSTANCE_FIELD).get(null) + } + else { + // nested object in an object + // val clazz = loadClass(sym.fullNameSeparated(FlatName)) + // clazz.getConstructor().newInstance().asInstanceOf[Object] + ??? + } + } + + def loadClass(name: String): Class[_] = { + try classLoader.loadClass(name) + catch { + case _: ClassNotFoundException => + val msg = s"Could not find class $name in classpath" + throw new Exception(msg) + } + } +} + class Interpreter[R <: Reflection & Singleton](val reflect: R)(implicit ctx: reflect.Context) { import reflect._ + val jvmReflection = new JVMReflection(reflect) + type Env = Map[Symbol, Ref] - // object Call { - // def unapply(arg: Tree): Option[(Tree, List[List[Tree]])] = arg match { - // case fn@ Term.Select(_, _, _) => Some((fn, Nil)) - // case Term.Apply(Call(fn, args1), args2) => Some((fn, args1 :+ args2)) - // case Term.TypeApply(Call(fn, args), _) => Some((fn, args)) - // case _ => None - // } - // } + object Call { + def unapply(arg: Tree): Option[(Tree, List[List[Term]])] = arg match { + case fn@ Term.Ident(_) => Some((fn, Nil)) + case fn@ Term.Select(_, _) => Some((fn, Nil)) + case Term.Apply(Call(fn, args1), args2) => Some((fn, args1 :+ args2)) + case Term.TypeApply(Call(fn, args), _) => Some((fn, args)) + case _ => None + } + } def eval(tree: Statement)(implicit env: Env): Any = { // Our debug println @@ -33,22 +68,28 @@ class Interpreter[R <: Reflection & Singleton](val reflect: R)(implicit ctx: ref case "==" => eval(a).asInstanceOf[Int] == eval(b).asInstanceOf[Int] } - // TODO: handle multiple parameters - case Term.Apply(meth@Term.Ident(_), args) => - meth.symbol match { - case IsDefSymbol(sym) => - val evaluatedArgs = args.map(arg => new Eager(eval(arg))) - val env1 = env ++ sym.tree.paramss.head.map(_.symbol).zip(evaluatedArgs) - - eval(sym.tree.rhs.get)(env1) - } - - case Term.Ident(_) => - tree.symbol match { - case IsDefSymbol(sym) => - eval(sym.tree.rhs.get) - case _ => - env(tree.symbol).get + case Call(fn, argss) => + argss match { + case Nil => + fn.symbol match { + case IsDefSymbol(sym) => + eval(sym.tree.rhs.get) + case _ => + if (fn.symbol.isDefinedInCurrentRun) { + env(tree.symbol).get + } + else { + jvmReflection.loadModule(fn.symbol.asVal.moduleClass.get) + } + } + case x :: Nil => + fn.symbol match { + case IsDefSymbol(sym) => + val evaluatedArgs = x.map(arg => new Eager(eval(arg))) + val env1 = env ++ sym.tree.paramss.head.map(_.symbol).zip(evaluatedArgs) + + eval(sym.tree.rhs.get)(env1) + } } case Term.Assign(lhs, rhs) => diff --git a/tests/run-with-compiler/tasty-interpreter/Precompiled.scala b/tests/run-with-compiler/tasty-interpreter/Precompiled.scala new file mode 100644 index 000000000000..601a78b79af6 --- /dev/null +++ b/tests/run-with-compiler/tasty-interpreter/Precompiled.scala @@ -0,0 +1,8 @@ + + +object Precompiled { + val staticVal = 42 + def staticMeth = 42 + + override def toString() = "precompiledModule" +} \ No newline at end of file diff --git a/tests/run-with-compiler/tasty-interpreter/Test.scala b/tests/run-with-compiler/tasty-interpreter/Test.scala index 88e7bf492ec9..02ccc6a1d55b 100644 --- a/tests/run-with-compiler/tasty-interpreter/Test.scala +++ b/tests/run-with-compiler/tasty-interpreter/Test.scala @@ -3,7 +3,7 @@ import scala.tasty.file._ object Test { def main(args: Array[String]): Unit = { - ConsumeTasty("", List("Foo"), new TastyInterpreter) + ConsumeTasty("", List("IntepretedMain"), new TastyInterpreter) } } From 5fe64468d70f345a25604a4f5c44e6906d471b6d Mon Sep 17 00:00:00 2001 From: Aggelos Biboudis Date: Fri, 7 Dec 2018 18:17:24 +0100 Subject: [PATCH 08/45] Add JVM reflect for static methods without params --- .../tastyreflect/TypeOrBoundsOpsImpl.scala | 2 + .../scala/tasty/reflect/TypeOrBoundsOps.scala | 3 +- .../run-with-compiler/tasty-interpreter.check | 4 +- .../tasty-interpreter/InterpretedMain.scala | 2 + .../tasty-interpreter/Interpreter.scala | 98 ++++++++++++++++++- .../tasty-interpreter/Precompiled.scala | 11 ++- 6 files changed, 112 insertions(+), 8 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/tastyreflect/TypeOrBoundsOpsImpl.scala b/compiler/src/dotty/tools/dotc/tastyreflect/TypeOrBoundsOpsImpl.scala index 5050997b036b..780040bbfae1 100644 --- a/compiler/src/dotty/tools/dotc/tastyreflect/TypeOrBoundsOpsImpl.scala +++ b/compiler/src/dotty/tools/dotc/tastyreflect/TypeOrBoundsOpsImpl.scala @@ -19,6 +19,8 @@ trait TypeOrBoundsOpsImpl extends scala.tasty.reflect.TypeOrBoundsOps with CoreI */ def widen(implicit ctx: Context): Type = tpe.widen + def classSymbol(implicit ctx: Context): Option[ClassSymbol] = + if (tpe.classSymbol.exists) Some(tpe.classSymbol.asClass) else None } def ConstantTypeDeco(x: ConstantType): Type.ConstantTypeAPI = new Type.ConstantTypeAPI { diff --git a/library/src/scala/tasty/reflect/TypeOrBoundsOps.scala b/library/src/scala/tasty/reflect/TypeOrBoundsOps.scala index f6f3ed7b3182..2e5a90ddf619 100644 --- a/library/src/scala/tasty/reflect/TypeOrBoundsOps.scala +++ b/library/src/scala/tasty/reflect/TypeOrBoundsOps.scala @@ -44,7 +44,7 @@ trait TypeOrBoundsOps extends Core { implicit def TypeLambdaDeco(x: TypeLambda): Type.TypeLambdaAPI implicit def TypeBoundsDeco(bounds: TypeBounds): TypeBoundsAPI - + // ----- Types ---------------------------------------------------- def typeOf[T: scala.quoted.Type]: Type @@ -53,6 +53,7 @@ trait TypeOrBoundsOps extends Core { def =:=(other: Type)(implicit ctx: Context): Boolean def <:<(other: Type)(implicit ctx: Context): Boolean def widen(implicit ctx: Context): Type + def classSymbol(implicit ctx: Context): Option[ClassSymbol] } val IsType: IsTypeModule diff --git a/tests/run-with-compiler/tasty-interpreter.check b/tests/run-with-compiler/tasty-interpreter.check index 92c44d0b0d18..3f86e63b4405 100644 --- a/tests/run-with-compiler/tasty-interpreter.check +++ b/tests/run-with-compiler/tasty-interpreter.check @@ -17,4 +17,6 @@ if 42 55 -precompiledModule \ No newline at end of file +precompiledModule +66 +55 \ No newline at end of file diff --git a/tests/run-with-compiler/tasty-interpreter/InterpretedMain.scala b/tests/run-with-compiler/tasty-interpreter/InterpretedMain.scala index 1ca428c2e092..6de4e9362c49 100644 --- a/tests/run-with-compiler/tasty-interpreter/InterpretedMain.scala +++ b/tests/run-with-compiler/tasty-interpreter/InterpretedMain.scala @@ -39,5 +39,7 @@ object IntepretedMain { println(methP(55)) println(Precompiled) + println(Precompiled.staticMeth) + println(Precompiled.staticVal) } } diff --git a/tests/run-with-compiler/tasty-interpreter/Interpreter.scala b/tests/run-with-compiler/tasty-interpreter/Interpreter.scala index 39447574ac66..45aa22f66b31 100644 --- a/tests/run-with-compiler/tasty-interpreter/Interpreter.scala +++ b/tests/run-with-compiler/tasty-interpreter/Interpreter.scala @@ -2,7 +2,7 @@ import scala.tasty.Reflection class JVMReflection[R <: Reflection & Singleton](val reflect: R) { import reflect._ - + import java.lang.reflect.{InvocationTargetException, Method} private val classLoader: ClassLoader = getClass.getClassLoader // taken from StdNames @@ -26,10 +26,87 @@ class JVMReflection[R <: Reflection & Singleton](val reflect: R) { try classLoader.loadClass(name) catch { case _: ClassNotFoundException => - val msg = s"Could not find class $name in classpath" + val msg = s"Could not find class $name in classpath$extraMsg" throw new Exception(msg) } } + + def interpretStaticMethodCall(moduleClass: Symbol, fn: Symbol, args: => List[Object]): Object = { + val instance = loadModule(moduleClass) + val name = fn.name + + val method = getMethod(instance.getClass, name, paramsSig(fn)) + method.invoke(instance, args: _*) + } + + def getMethod(clazz: Class[_], name: String, paramClasses: List[Class[_]]): Method = { + try clazz.getMethod(name, paramClasses: _*) + catch { + case _: NoSuchMethodException => + val msg = s"Could not find method ${clazz.getCanonicalName}.$name with parameters ($paramClasses%, %)$extraMsg" + throw new Exception(msg) + } + } + + /** List of classes of the parameters of the signature of `sym` */ + private def paramsSig(sym: Symbol): List[Class[_]] = { + def paramClass(param: Type): Class[_] = { + // def arrayDepth(tpe: Type, depth: Int): (Type, Int) = tpe match { + // case JavaArrayType(elemType) => arrayDepth(elemType, depth + 1) + // case _ => (tpe, depth) + // } + // def javaArraySig(tpe: Type): String = { + // val (elemType, depth) = arrayDepth(tpe, 0) + // val sym = elemType.classSymbol + // val suffix = + // if (sym == defn.BooleanClass) "Z" + // else if (sym == defn.ByteClass) "B" + // else if (sym == defn.ShortClass) "S" + // else if (sym == defn.IntClass) "I" + // else if (sym == defn.LongClass) "J" + // else if (sym == defn.FloatClass) "F" + // else if (sym == defn.DoubleClass) "D" + // else if (sym == defn.CharClass) "C" + // else "L" + javaSig(elemType) + ";" + // ("[" * depth) + suffix + // } + def javaSig(tpe: Type): String = tpe match { + // case tpe: JavaArrayType => javaArraySig(tpe) + case _ => + // Take the flatten name of the class and the full package name + // val pack = tpe.classSymbol.get.topLevelClass.owner + val packageName = "" //if (pack == defn.EmptyPackageClass) "" else pack.fullName + "." + packageName + tpe.classSymbol.get.fullName //.fullNameSeparated(FlatName).toString + } + + val sym = param.classSymbol + if (sym == definitions.BooleanClass) classOf[Boolean] + else if (sym == definitions.ByteClass) classOf[Byte] + else if (sym == definitions.CharClass) classOf[Char] + else if (sym == definitions.ShortClass) classOf[Short] + else if (sym == definitions.IntClass) classOf[Int] + else if (sym == definitions.LongClass) classOf[Long] + else if (sym == definitions.FloatClass) classOf[Float] + else if (sym == definitions.DoubleClass) classOf[Double] + else java.lang.Class.forName(javaSig(param), false, classLoader) + } + // def getExtraParams(tp: Type): List[Type] = tp.widenDealias match { + // case tp: AppliedType if defn.isImplicitFunctionType(tp) => + // // Call implicit function type direct method + // tp.args.init.map(arg => TypeErasure.erasure(arg)) ::: getExtraParams(tp.args.last) + // case _ => Nil + // } + // val extraParams = getExtraParams(sym.info.finalResultType) + // val allParams = TypeErasure.erasure(sym.info) match { + // case meth: MethodType => meth.paramInfos ::: extraParams + // case _ => extraParams + // } + // allParams.map(paramClass) + + Nil + } + + private def extraMsg = ". The most common reason for that is that you apply macros in the compilation run that defines them" } class Interpreter[R <: Reflection & Singleton](val reflect: R)(implicit ctx: reflect.Context) { @@ -69,18 +146,31 @@ class Interpreter[R <: Reflection & Singleton](val reflect: R)(implicit ctx: ref } case Call(fn, argss) => + // https://github.com/lampepfl/dotty/blob/4ff8dacd9ca9a1ade6ddf3d6b7a0c78d0c204ec4/compiler/src/dotty/tools/dotc/transform/Splicer.scala#L314 + // todo: add dispatcher argss match { case Nil => fn.symbol match { case IsDefSymbol(sym) => - eval(sym.tree.rhs.get) + if (fn.symbol.isDefinedInCurrentRun) { + eval(sym.tree.rhs.get) + } + // call to a static def, no parameters + else { + jvmReflection.interpretStaticMethodCall(fn.symbol.owner, fn.symbol, Nil) + } case _ => if (fn.symbol.isDefinedInCurrentRun) { env(tree.symbol).get } - else { + // call to a module + else if (fn.symbol.flags.isObject) { jvmReflection.loadModule(fn.symbol.asVal.moduleClass.get) } + // call to a static val + else { + jvmReflection.interpretStaticMethodCall(fn.symbol.owner, fn.symbol, Nil) + } } case x :: Nil => fn.symbol match { diff --git a/tests/run-with-compiler/tasty-interpreter/Precompiled.scala b/tests/run-with-compiler/tasty-interpreter/Precompiled.scala index 601a78b79af6..1c9f001cc1d9 100644 --- a/tests/run-with-compiler/tasty-interpreter/Precompiled.scala +++ b/tests/run-with-compiler/tasty-interpreter/Precompiled.scala @@ -1,8 +1,15 @@ +trait Bar { + def meth(): Int +} object Precompiled { - val staticVal = 42 - def staticMeth = 42 + val staticVal = 55 + def staticMeth = 66 + // Todo + // def staticMeth1() = 66 + // def staticMeth2(arg: Int) = arg + // def staticMeth3(arg: Bar): Int = arg.meth() override def toString() = "precompiledModule" } \ No newline at end of file From 13ff8669f7ba0583ef34a66f80fe8dd35151bdcc Mon Sep 17 00:00:00 2001 From: Aggelos Biboudis Date: Mon, 10 Dec 2018 11:26:43 +0100 Subject: [PATCH 09/45] Decouple static val from static meth reflection --- .../dotc/tastyreflect/SymbolOpsImpl.scala | 4 + .../src/scala/tasty/reflect/SymbolOps.scala | 5 +- .../run-with-compiler/tasty-interpreter.check | 4 +- .../tasty-interpreter/InterpretedMain.scala | 1 + .../tasty-interpreter/Interpreter.scala | 111 +----------------- .../tasty-interpreter/JVMReflection.scala | 89 ++++++++++++++ .../tasty-interpreter/Precompiled.scala | 7 +- 7 files changed, 104 insertions(+), 117 deletions(-) create mode 100644 tests/run-with-compiler/tasty-interpreter/JVMReflection.scala diff --git a/compiler/src/dotty/tools/dotc/tastyreflect/SymbolOpsImpl.scala b/compiler/src/dotty/tools/dotc/tastyreflect/SymbolOpsImpl.scala index 7117280c56ca..c4359beacb1e 100644 --- a/compiler/src/dotty/tools/dotc/tastyreflect/SymbolOpsImpl.scala +++ b/compiler/src/dotty/tools/dotc/tastyreflect/SymbolOpsImpl.scala @@ -175,6 +175,10 @@ trait SymbolOpsImpl extends scala.tasty.reflect.SymbolOps with CoreImpl { def DefSymbolDeco(symbol: DefSymbol): DefSymbolAPI = new DefSymbolAPI { def tree(implicit ctx: Context): DefDef = FromSymbol.defDefFromSym(symbol) + + def signature(implicit ctx: Context): Signature = { + symbol.signature + } } object IsValSymbol extends IsValSymbolModule { diff --git a/library/src/scala/tasty/reflect/SymbolOps.scala b/library/src/scala/tasty/reflect/SymbolOps.scala index fa6ed8e72407..b16307278b3e 100644 --- a/library/src/scala/tasty/reflect/SymbolOps.scala +++ b/library/src/scala/tasty/reflect/SymbolOps.scala @@ -122,7 +122,7 @@ trait SymbolOps extends Core { } trait TypeSymbolAPI { - /** TypeDef tree of this defintion. */ + /** TypeDef tree of this definition. */ def tree(implicit ctx: Context): TypeDef } implicit def TypeSymbolDeco(symbol: TypeSymbol): TypeSymbolAPI @@ -137,6 +137,8 @@ trait SymbolOps extends Core { trait DefSymbolAPI { /** DefDef tree of this defintion. */ def tree(implicit ctx: Context): DefDef + + def signature(implicit ctx: Context): Signature } implicit def DefSymbolDeco(symbol: DefSymbol): DefSymbolAPI @@ -153,7 +155,6 @@ trait SymbolOps extends Core { /** The class symbol of the companion module class */ def moduleClass(implicit ctx: Context): Option[ClassSymbol] - } implicit def ValSymbolDeco(symbol: ValSymbol): ValSymbolAPI diff --git a/tests/run-with-compiler/tasty-interpreter.check b/tests/run-with-compiler/tasty-interpreter.check index 3f86e63b4405..372fc7a8fa86 100644 --- a/tests/run-with-compiler/tasty-interpreter.check +++ b/tests/run-with-compiler/tasty-interpreter.check @@ -18,5 +18,5 @@ if 55 precompiledModule -66 -55 \ No newline at end of file +55 +56 \ No newline at end of file diff --git a/tests/run-with-compiler/tasty-interpreter/InterpretedMain.scala b/tests/run-with-compiler/tasty-interpreter/InterpretedMain.scala index 6de4e9362c49..570806b75b91 100644 --- a/tests/run-with-compiler/tasty-interpreter/InterpretedMain.scala +++ b/tests/run-with-compiler/tasty-interpreter/InterpretedMain.scala @@ -41,5 +41,6 @@ object IntepretedMain { println(Precompiled) println(Precompiled.staticMeth) println(Precompiled.staticVal) + // println(Precompiled.staticMeth1()) } } diff --git a/tests/run-with-compiler/tasty-interpreter/Interpreter.scala b/tests/run-with-compiler/tasty-interpreter/Interpreter.scala index 45aa22f66b31..58855bc0f56b 100644 --- a/tests/run-with-compiler/tasty-interpreter/Interpreter.scala +++ b/tests/run-with-compiler/tasty-interpreter/Interpreter.scala @@ -1,114 +1,5 @@ import scala.tasty.Reflection -class JVMReflection[R <: Reflection & Singleton](val reflect: R) { - import reflect._ - import java.lang.reflect.{InvocationTargetException, Method} - private val classLoader: ClassLoader = getClass.getClassLoader - - // taken from StdNames - final val MODULE_INSTANCE_FIELD = "MODULE$" - - def loadModule(sym: Symbol): Object = { - if (sym.owner.flags.isPackage) { - // is top level object - val moduleClass = loadClass(sym.fullName) - moduleClass.getField(MODULE_INSTANCE_FIELD).get(null) - } - else { - // nested object in an object - // val clazz = loadClass(sym.fullNameSeparated(FlatName)) - // clazz.getConstructor().newInstance().asInstanceOf[Object] - ??? - } - } - - def loadClass(name: String): Class[_] = { - try classLoader.loadClass(name) - catch { - case _: ClassNotFoundException => - val msg = s"Could not find class $name in classpath$extraMsg" - throw new Exception(msg) - } - } - - def interpretStaticMethodCall(moduleClass: Symbol, fn: Symbol, args: => List[Object]): Object = { - val instance = loadModule(moduleClass) - val name = fn.name - - val method = getMethod(instance.getClass, name, paramsSig(fn)) - method.invoke(instance, args: _*) - } - - def getMethod(clazz: Class[_], name: String, paramClasses: List[Class[_]]): Method = { - try clazz.getMethod(name, paramClasses: _*) - catch { - case _: NoSuchMethodException => - val msg = s"Could not find method ${clazz.getCanonicalName}.$name with parameters ($paramClasses%, %)$extraMsg" - throw new Exception(msg) - } - } - - /** List of classes of the parameters of the signature of `sym` */ - private def paramsSig(sym: Symbol): List[Class[_]] = { - def paramClass(param: Type): Class[_] = { - // def arrayDepth(tpe: Type, depth: Int): (Type, Int) = tpe match { - // case JavaArrayType(elemType) => arrayDepth(elemType, depth + 1) - // case _ => (tpe, depth) - // } - // def javaArraySig(tpe: Type): String = { - // val (elemType, depth) = arrayDepth(tpe, 0) - // val sym = elemType.classSymbol - // val suffix = - // if (sym == defn.BooleanClass) "Z" - // else if (sym == defn.ByteClass) "B" - // else if (sym == defn.ShortClass) "S" - // else if (sym == defn.IntClass) "I" - // else if (sym == defn.LongClass) "J" - // else if (sym == defn.FloatClass) "F" - // else if (sym == defn.DoubleClass) "D" - // else if (sym == defn.CharClass) "C" - // else "L" + javaSig(elemType) + ";" - // ("[" * depth) + suffix - // } - def javaSig(tpe: Type): String = tpe match { - // case tpe: JavaArrayType => javaArraySig(tpe) - case _ => - // Take the flatten name of the class and the full package name - // val pack = tpe.classSymbol.get.topLevelClass.owner - val packageName = "" //if (pack == defn.EmptyPackageClass) "" else pack.fullName + "." - packageName + tpe.classSymbol.get.fullName //.fullNameSeparated(FlatName).toString - } - - val sym = param.classSymbol - if (sym == definitions.BooleanClass) classOf[Boolean] - else if (sym == definitions.ByteClass) classOf[Byte] - else if (sym == definitions.CharClass) classOf[Char] - else if (sym == definitions.ShortClass) classOf[Short] - else if (sym == definitions.IntClass) classOf[Int] - else if (sym == definitions.LongClass) classOf[Long] - else if (sym == definitions.FloatClass) classOf[Float] - else if (sym == definitions.DoubleClass) classOf[Double] - else java.lang.Class.forName(javaSig(param), false, classLoader) - } - // def getExtraParams(tp: Type): List[Type] = tp.widenDealias match { - // case tp: AppliedType if defn.isImplicitFunctionType(tp) => - // // Call implicit function type direct method - // tp.args.init.map(arg => TypeErasure.erasure(arg)) ::: getExtraParams(tp.args.last) - // case _ => Nil - // } - // val extraParams = getExtraParams(sym.info.finalResultType) - // val allParams = TypeErasure.erasure(sym.info) match { - // case meth: MethodType => meth.paramInfos ::: extraParams - // case _ => extraParams - // } - // allParams.map(paramClass) - - Nil - } - - private def extraMsg = ". The most common reason for that is that you apply macros in the compilation run that defines them" -} - class Interpreter[R <: Reflection & Singleton](val reflect: R)(implicit ctx: reflect.Context) { import reflect._ @@ -169,7 +60,7 @@ class Interpreter[R <: Reflection & Singleton](val reflect: R)(implicit ctx: ref } // call to a static val else { - jvmReflection.interpretStaticMethodCall(fn.symbol.owner, fn.symbol, Nil) + jvmReflection.interpretStaticVal(fn.symbol.owner, fn.symbol) } } case x :: Nil => diff --git a/tests/run-with-compiler/tasty-interpreter/JVMReflection.scala b/tests/run-with-compiler/tasty-interpreter/JVMReflection.scala new file mode 100644 index 000000000000..0661f0716d62 --- /dev/null +++ b/tests/run-with-compiler/tasty-interpreter/JVMReflection.scala @@ -0,0 +1,89 @@ +import scala.tasty.Reflection + +class JVMReflection[R <: Reflection & Singleton](val reflect: R) { + import reflect._ + import java.lang.reflect.{InvocationTargetException, Method} + private val classLoader: ClassLoader = getClass.getClassLoader + + // taken from StdNames + final val MODULE_INSTANCE_FIELD = "MODULE$" + + def loadModule(sym: Symbol): Object = { + if (sym.owner.flags.isPackage) { + // is top level object + val moduleClass = loadClass(sym.fullName) + moduleClass.getField(MODULE_INSTANCE_FIELD).get(null) + } + else { + // nested object in an object + // val clazz = loadClass(sym.fullNameSeparated(FlatName)) + // clazz.getConstructor().newInstance().asInstanceOf[Object] + ??? + } + } + + def loadClass(name: String): Class[_] = { + try classLoader.loadClass(name) + catch { + case _: ClassNotFoundException => + val msg = s"Could not find class $name in classpath$extraMsg" + throw new Exception(msg) + } + } + + def interpretStaticVal(moduleClass: Symbol, fn: Symbol): Object = { + val instance = loadModule(moduleClass) + val name = fn.name + + val method = getMethod(instance.getClass, name, Nil) + method.invoke(instance) + } + + def interpretStaticMethodCall(moduleClass: Symbol, fn: Symbol, args: => List[Object]): Object = { + val instance = loadModule(moduleClass) + val name = fn.name + + val method = getMethod(instance.getClass, name, paramsSig(fn)) + method.invoke(instance, args: _*) + } + + def getMethod(clazz: Class[_], name: String, paramClasses: List[Class[_]]): Method = { + try clazz.getMethod(name, paramClasses: _*) + catch { + case _: NoSuchMethodException => + val msg = s"Could not find method ${clazz.getCanonicalName}.$name with parameters ($paramClasses%, %)$extraMsg" + throw new Exception(msg) + } + } + + private def paramsSig(sym: Symbol): List[Class[_]] = { + + sym.asDef.signature.paramSigs.map { param => + println(param) + ??? + // defn.valueTypeNameToJavaType(param) match { + // case Some(clazz) => clazz + // case None => + // def javaArraySig(name: String): String = { + // if (name.endsWith("[]")) "[" + javaArraySig(name.dropRight(2)) + // else name match { + // case "scala.Boolean" => "Z" + // case "scala.Byte" => "B" + // case "scala.Short" => "S" + // case "scala.Int" => "I" + // case "scala.Long" => "J" + // case "scala.Float" => "F" + // case "scala.Double" => "D" + // case "scala.Char" => "C" + // case paramName => "L" + paramName + ";" + // } + // } + // def javaSig(name: String): String = + // if (name.endsWith("[]")) javaArraySig(name) else name + // java.lang.Class.forName(javaSig(param.toString), false, classLoader) + //} + } + } + + private def extraMsg = ". The most common reason for that is that you apply macros in the compilation run that defines them" +} \ No newline at end of file diff --git a/tests/run-with-compiler/tasty-interpreter/Precompiled.scala b/tests/run-with-compiler/tasty-interpreter/Precompiled.scala index 1c9f001cc1d9..5e6807961823 100644 --- a/tests/run-with-compiler/tasty-interpreter/Precompiled.scala +++ b/tests/run-with-compiler/tasty-interpreter/Precompiled.scala @@ -4,10 +4,11 @@ trait Bar { } object Precompiled { - val staticVal = 55 - def staticMeth = 66 + def staticMeth = 55 + val staticVal = 56 + // Todo - // def staticMeth1() = 66 + // def staticMeth1() = 57 // def staticMeth2(arg: Int) = arg // def staticMeth3(arg: Bar): Int = arg.meth() From 6ffa3b06a10e900a36009cd89469db92fdd558a8 Mon Sep 17 00:00:00 2001 From: Aggelos Biboudis Date: Wed, 12 Dec 2018 16:38:32 +0100 Subject: [PATCH 10/45] wip --- .../run-with-compiler/tasty-interpreter.check | 4 +- .../tasty-interpreter/InterpretedMain.scala | 3 +- .../tasty-interpreter/Interpreter.scala | 145 +++++++++++------- .../tasty-interpreter/JVMReflection.scala | 1 - .../tasty-interpreter/Precompiled.scala | 4 +- .../tasty-interpreter/Test.scala | 15 -- 6 files changed, 94 insertions(+), 78 deletions(-) diff --git a/tests/run-with-compiler/tasty-interpreter.check b/tests/run-with-compiler/tasty-interpreter.check index 372fc7a8fa86..e9b395013332 100644 --- a/tests/run-with-compiler/tasty-interpreter.check +++ b/tests/run-with-compiler/tasty-interpreter.check @@ -19,4 +19,6 @@ if 55 precompiledModule 55 -56 \ No newline at end of file +56 +57 +58 \ No newline at end of file diff --git a/tests/run-with-compiler/tasty-interpreter/InterpretedMain.scala b/tests/run-with-compiler/tasty-interpreter/InterpretedMain.scala index 570806b75b91..5a32d67e5a72 100644 --- a/tests/run-with-compiler/tasty-interpreter/InterpretedMain.scala +++ b/tests/run-with-compiler/tasty-interpreter/InterpretedMain.scala @@ -41,6 +41,7 @@ object IntepretedMain { println(Precompiled) println(Precompiled.staticMeth) println(Precompiled.staticVal) - // println(Precompiled.staticMeth1()) + println(Precompiled.staticMeth1()) + // println(Precompiled.staticMeth2(58)) } } diff --git a/tests/run-with-compiler/tasty-interpreter/Interpreter.scala b/tests/run-with-compiler/tasty-interpreter/Interpreter.scala index 58855bc0f56b..d4d1194848db 100644 --- a/tests/run-with-compiler/tasty-interpreter/Interpreter.scala +++ b/tests/run-with-compiler/tasty-interpreter/Interpreter.scala @@ -1,5 +1,23 @@ import scala.tasty.Reflection +trait Ref { + def get: Any +} + +class Eager(val get: Any) extends Ref + +class Lazy(thunk: => Any) extends Ref { + lazy val get: Any = thunk +} + +class Var(private var value: Any) extends Ref { + def get = value + + def update(rhs: Any): Unit = { + value = rhs + } +} + class Interpreter[R <: Reflection & Singleton](val reflect: R)(implicit ctx: reflect.Context) { import reflect._ @@ -7,70 +25,82 @@ class Interpreter[R <: Reflection & Singleton](val reflect: R)(implicit ctx: ref type Env = Map[Symbol, Ref] + enum CallMode { + case Interpret, EvalOp, ReflectPrecompiled, Predef, Default + } + object Call { - def unapply(arg: Tree): Option[(Tree, List[List[Term]])] = arg match { - case fn@ Term.Ident(_) => Some((fn, Nil)) - case fn@ Term.Select(_, _) => Some((fn, Nil)) - case Term.Apply(Call(fn, args1), args2) => Some((fn, args1 :+ args2)) - case Term.TypeApply(Call(fn, args), _) => Some((fn, args)) + /* + fn + args + mode + */ + def unapply(arg: Tree): Option[(AnyRef, Tree, List[List[Term]], CallMode)] = arg match { + case fn@ Term.Apply(Term.Ident("println"), arg) => Some((AnyRef, fn, List(arg), CallMode.Predef)) + case Term.Apply(fn@ Term.Select(a, op), args) => + op match { + case "+" | "-" | ">" | "==" => Some((a, fn, List(args), CallMode.EvalOp)) + } + case fn@ Term.Select(_, _) => Some((AnyRef, fn, Nil, CallMode.Default)) + case fn@ Term.Ident(_) => Some((AnyRef, fn, Nil, CallMode.Default)) + case Term.Apply(Call(_, fn, args1, _), args2) => Some((AnyRef, fn, args1 :+ args2, CallMode.Default)) + case Term.TypeApply(Call(_, fn, args, _), _) => Some((AnyRef, fn, args, CallMode.Default)) + case _ => None } } def eval(tree: Statement)(implicit env: Env): Any = { - // Our debug println - // println(tree.show) - tree match { - case Term.Apply(Term.Ident("println"), List(arg)) => - println(eval(arg)) - - case Term.Apply(Term.Ident("println"), List()) => - println() - - case Term.Apply(Term.Select(a, op), List(b)) => - op match { - case "+" => eval(a).asInstanceOf[Int] + eval(b).asInstanceOf[Int] - case "-" => eval(a).asInstanceOf[Int] - eval(b).asInstanceOf[Int] - case ">" => eval(a).asInstanceOf[Int] > eval(b).asInstanceOf[Int] - case "==" => eval(a).asInstanceOf[Int] == eval(b).asInstanceOf[Int] - } - - case Call(fn, argss) => - // https://github.com/lampepfl/dotty/blob/4ff8dacd9ca9a1ade6ddf3d6b7a0c78d0c204ec4/compiler/src/dotty/tools/dotc/transform/Splicer.scala#L314 - // todo: add dispatcher - argss match { - case Nil => - fn.symbol match { - case IsDefSymbol(sym) => - if (fn.symbol.isDefinedInCurrentRun) { - eval(sym.tree.rhs.get) - } - // call to a static def, no parameters - else { - jvmReflection.interpretStaticMethodCall(fn.symbol.owner, fn.symbol, Nil) - } - case _ => - if (fn.symbol.isDefinedInCurrentRun) { - env(tree.symbol).get - } - // call to a module - else if (fn.symbol.flags.isObject) { - jvmReflection.loadModule(fn.symbol.asVal.moduleClass.get) - } - // call to a static val - else { - jvmReflection.interpretStaticVal(fn.symbol.owner, fn.symbol) - } - } - case x :: Nil => - fn.symbol match { - case IsDefSymbol(sym) => - val evaluatedArgs = x.map(arg => new Eager(eval(arg))) - val env1 = env ++ sym.tree.paramss.head.map(_.symbol).zip(evaluatedArgs) - - eval(sym.tree.rhs.get)(env1) - } + case Call(prefix, fn, argss, mode) => + mode match { + case CallMode.Predef => fn match { + case Term.Apply(Term.Ident("println"), List(arg)) => println(eval(arg)) + case Term.Apply(Term.Ident("println"), List()) => println() + } + case CallMode.EvalOp => prefix match { + case Term.Apply(Term.Select(a, op), List(b)) => + val b = argss.head.head + op match { + case "+" => eval(a).asInstanceOf[Int] + eval(b).asInstanceOf[Int] + case "-" => eval(a).asInstanceOf[Int] - eval(b).asInstanceOf[Int] + case ">" => eval(a).asInstanceOf[Int] > eval(b).asInstanceOf[Int] + case "==" => eval(a).asInstanceOf[Int] == eval(b).asInstanceOf[Int] + } + case _ => println("Blah: " + prefix) + } + case CallMode.Default => argss match { + case Nil | List(List()) => + fn.symbol match { + case IsDefSymbol(sym) => + if (fn.symbol.isDefinedInCurrentRun) { + eval(sym.tree.rhs.get) + } + // call to a static def, no parameters + else { + jvmReflection.interpretStaticMethodCall(fn.symbol.owner, fn.symbol, Nil) + } + case _ => + if (fn.symbol.isDefinedInCurrentRun) { + env(tree.symbol).get + } + // call to a module + else if (fn.symbol.flags.isObject) { + jvmReflection.loadModule(fn.symbol.asVal.moduleClass.get) + } + // call to a static val + else { + jvmReflection.interpretStaticVal(fn.symbol.owner, fn.symbol) + } + } + case x :: Nil => + fn.symbol match { + case IsDefSymbol(sym) => + val evaluatedArgs = x.map(arg => new Eager(eval(arg))) + val env1 = env ++ sym.tree.paramss.head.map(_.symbol).zip(evaluatedArgs) + eval(sym.tree.rhs.get)(env1) + } + } } case Term.Assign(lhs, rhs) => @@ -92,7 +122,6 @@ class Interpreter[R <: Reflection & Singleton](val reflect: R)(implicit ctx: ref case Term.Block(stats, expr) => val newEnv = stats.foldLeft(env)((accEnv, stat) => stat match { case ValDef(name, tpt, Some(rhs)) => - val evalRef: Ref = if (stat.symbol.flags.isLazy) new Lazy(eval(rhs)(accEnv)) // do not factor out rhs from here (laziness) diff --git a/tests/run-with-compiler/tasty-interpreter/JVMReflection.scala b/tests/run-with-compiler/tasty-interpreter/JVMReflection.scala index 0661f0716d62..88de801269a6 100644 --- a/tests/run-with-compiler/tasty-interpreter/JVMReflection.scala +++ b/tests/run-with-compiler/tasty-interpreter/JVMReflection.scala @@ -57,7 +57,6 @@ class JVMReflection[R <: Reflection & Singleton](val reflect: R) { } private def paramsSig(sym: Symbol): List[Class[_]] = { - sym.asDef.signature.paramSigs.map { param => println(param) ??? diff --git a/tests/run-with-compiler/tasty-interpreter/Precompiled.scala b/tests/run-with-compiler/tasty-interpreter/Precompiled.scala index 5e6807961823..f99ac8ba2b7c 100644 --- a/tests/run-with-compiler/tasty-interpreter/Precompiled.scala +++ b/tests/run-with-compiler/tasty-interpreter/Precompiled.scala @@ -8,8 +8,8 @@ object Precompiled { val staticVal = 56 // Todo - // def staticMeth1() = 57 - // def staticMeth2(arg: Int) = arg + def staticMeth1() = 57 + def staticMeth2(arg: Int) = arg // def staticMeth3(arg: Bar): Int = arg.meth() override def toString() = "precompiledModule" diff --git a/tests/run-with-compiler/tasty-interpreter/Test.scala b/tests/run-with-compiler/tasty-interpreter/Test.scala index 02ccc6a1d55b..646bf90410ce 100644 --- a/tests/run-with-compiler/tasty-interpreter/Test.scala +++ b/tests/run-with-compiler/tasty-interpreter/Test.scala @@ -7,21 +7,6 @@ object Test { } } -trait Ref { - def get: Any -} -class Eager(val get: Any) extends Ref -class Lazy(thunk: => Any) extends Ref { - lazy val get: Any = thunk -} -class Var(private var value: Any) extends Ref { - def get = value - - def update(rhs: Any): Unit = { - value = rhs - } -} - class TastyInterpreter extends TastyConsumer { final def apply(reflect: Reflection)(root: reflect.Tree): Unit = { From 663adbaaa5359adb8482fdd836995272e801c7d7 Mon Sep 17 00:00:00 2001 From: Aggelos Biboudis Date: Thu, 13 Dec 2018 17:40:04 +0100 Subject: [PATCH 11/45] Handle static methods with parameter --- .../tasty-interpreter/InterpretedMain.scala | 2 +- .../tasty-interpreter/Interpreter.scala | 127 ++++++++---------- .../tasty-interpreter/JVMReflection.scala | 57 ++++---- 3 files changed, 89 insertions(+), 97 deletions(-) diff --git a/tests/run-with-compiler/tasty-interpreter/InterpretedMain.scala b/tests/run-with-compiler/tasty-interpreter/InterpretedMain.scala index 5a32d67e5a72..169d5b23dafe 100644 --- a/tests/run-with-compiler/tasty-interpreter/InterpretedMain.scala +++ b/tests/run-with-compiler/tasty-interpreter/InterpretedMain.scala @@ -42,6 +42,6 @@ object IntepretedMain { println(Precompiled.staticMeth) println(Precompiled.staticVal) println(Precompiled.staticMeth1()) - // println(Precompiled.staticMeth2(58)) + println(Precompiled.staticMeth2(58)) } } diff --git a/tests/run-with-compiler/tasty-interpreter/Interpreter.scala b/tests/run-with-compiler/tasty-interpreter/Interpreter.scala index d4d1194848db..bccfc2ef0023 100644 --- a/tests/run-with-compiler/tasty-interpreter/Interpreter.scala +++ b/tests/run-with-compiler/tasty-interpreter/Interpreter.scala @@ -25,82 +25,72 @@ class Interpreter[R <: Reflection & Singleton](val reflect: R)(implicit ctx: ref type Env = Map[Symbol, Ref] - enum CallMode { - case Interpret, EvalOp, ReflectPrecompiled, Predef, Default + object Call { + def unapply(arg: Tree): Option[(Tree, List[List[Term]])] = arg match { + case fn@ Term.Select(_, _) => Some((fn, Nil)) + case fn@ Term.Ident(_) => Some((fn, Nil)) + case Term.Apply(Call(fn, args1), args2) => Some((fn, args1 :+ args2)) + case Term.TypeApply(Call(fn, args), _) => Some((fn, args)) + case _ => None + } } - object Call { - /* - fn - args - mode - */ - def unapply(arg: Tree): Option[(AnyRef, Tree, List[List[Term]], CallMode)] = arg match { - case fn@ Term.Apply(Term.Ident("println"), arg) => Some((AnyRef, fn, List(arg), CallMode.Predef)) - case Term.Apply(fn@ Term.Select(a, op), args) => - op match { - case "+" | "-" | ">" | "==" => Some((a, fn, List(args), CallMode.EvalOp)) + def interpretCall(fn: Tree, argss: List[List[Term]])(implicit env: Env): Any = { + (fn, argss) match { + case (Term.Ident("println"), List(List(arg))) => println(eval(arg)) + case (Term.Ident("println"), List(List())) => println() + case (_, Nil) | (_, List(List())) => fn.symbol match { + case IsDefSymbol(sym) => eval(sym.tree.rhs.get) + case _ => env(fn.symbol).get + } + case (_, x :: Nil) => + fn.symbol match { + // TODO: obviously + case IsDefSymbol(sym) => + val evaluatedArgs = x.map(arg => new Eager(eval(arg))) + val env1 = env ++ sym.tree.paramss.head.map(_.symbol).zip(evaluatedArgs) + eval(sym.tree.rhs.get)(env1) + } - case fn@ Term.Select(_, _) => Some((AnyRef, fn, Nil, CallMode.Default)) - case fn@ Term.Ident(_) => Some((AnyRef, fn, Nil, CallMode.Default)) - case Term.Apply(Call(_, fn, args1, _), args2) => Some((AnyRef, fn, args1 :+ args2, CallMode.Default)) - case Term.TypeApply(Call(_, fn, args, _), _) => Some((AnyRef, fn, args, CallMode.Default)) + } + } - case _ => None + def reflectCall(fn: Tree, argss: List[List[Term]])(implicit env: Env): Any = { + (fn, argss) match { + case (_, Nil) | (_, List(List())) => fn.symbol match { + case IsDefSymbol(sym) => jvmReflection.interpretStaticMethodCall(fn.symbol.owner, fn.symbol, Nil) + case _ => + if (fn.symbol.flags.isObject) { + jvmReflection.loadModule(fn.symbol.asVal.moduleClass.get) + } + // call to a static val + else { + jvmReflection.interpretStaticVal(fn.symbol.owner, fn.symbol) + } + } + case (_, x :: Nil) => + import Term._ + fn.symbol match { + // TODO: obviously + case IsDefSymbol(sym) => + if(sym.name == "==") eval(Term.IsSelect.unapply(fn).get.qualifier).asInstanceOf[Int] == eval(x.head).asInstanceOf[Int] + else if(sym.name == ">") eval(Term.IsSelect.unapply(fn).get.qualifier).asInstanceOf[Int] > eval(x.head).asInstanceOf[Int] + else if(sym.name == "-") eval(Term.IsSelect.unapply(fn).get.qualifier).asInstanceOf[Int] - eval(x.head).asInstanceOf[Int] + else if(sym.name == "+") eval(Term.IsSelect.unapply(fn).get.qualifier).asInstanceOf[Int] + eval(x.head).asInstanceOf[Int] + else { + def args: List[Object] = argss.flatMap((a: List[Term]) => a.map(b => eval(b).asInstanceOf[Object])) + jvmReflection.interpretStaticMethodCall(fn.symbol.owner, fn.symbol, args) + } + } } } def eval(tree: Statement)(implicit env: Env): Any = { tree match { - case Call(prefix, fn, argss, mode) => - mode match { - case CallMode.Predef => fn match { - case Term.Apply(Term.Ident("println"), List(arg)) => println(eval(arg)) - case Term.Apply(Term.Ident("println"), List()) => println() - } - case CallMode.EvalOp => prefix match { - case Term.Apply(Term.Select(a, op), List(b)) => - val b = argss.head.head - op match { - case "+" => eval(a).asInstanceOf[Int] + eval(b).asInstanceOf[Int] - case "-" => eval(a).asInstanceOf[Int] - eval(b).asInstanceOf[Int] - case ">" => eval(a).asInstanceOf[Int] > eval(b).asInstanceOf[Int] - case "==" => eval(a).asInstanceOf[Int] == eval(b).asInstanceOf[Int] - } - case _ => println("Blah: " + prefix) - } - case CallMode.Default => argss match { - case Nil | List(List()) => - fn.symbol match { - case IsDefSymbol(sym) => - if (fn.symbol.isDefinedInCurrentRun) { - eval(sym.tree.rhs.get) - } - // call to a static def, no parameters - else { - jvmReflection.interpretStaticMethodCall(fn.symbol.owner, fn.symbol, Nil) - } - case _ => - if (fn.symbol.isDefinedInCurrentRun) { - env(tree.symbol).get - } - // call to a module - else if (fn.symbol.flags.isObject) { - jvmReflection.loadModule(fn.symbol.asVal.moduleClass.get) - } - // call to a static val - else { - jvmReflection.interpretStaticVal(fn.symbol.owner, fn.symbol) - } - } - case x :: Nil => - fn.symbol match { - case IsDefSymbol(sym) => - val evaluatedArgs = x.map(arg => new Eager(eval(arg))) - val env1 = env ++ sym.tree.paramss.head.map(_.symbol).zip(evaluatedArgs) - eval(sym.tree.rhs.get)(env1) - } - } + case Call(fn, argss) => + fn match { + case Term.Ident("println") => interpretCall(fn, argss) + case _ => if (fn.symbol.isDefinedInCurrentRun) interpretCall(fn, argss) else reflectCall(fn, argss) } case Term.Assign(lhs, rhs) => @@ -124,7 +114,8 @@ class Interpreter[R <: Reflection & Singleton](val reflect: R)(implicit ctx: ref case ValDef(name, tpt, Some(rhs)) => val evalRef: Ref = if (stat.symbol.flags.isLazy) - new Lazy(eval(rhs)(accEnv)) // do not factor out rhs from here (laziness) + // do not factor out rhs from here (laziness) + new Lazy(eval(rhs)(accEnv)) else if (stat.symbol.flags.isMutable) new Var(eval(rhs)(accEnv)) else diff --git a/tests/run-with-compiler/tasty-interpreter/JVMReflection.scala b/tests/run-with-compiler/tasty-interpreter/JVMReflection.scala index 88de801269a6..ea2529666c7c 100644 --- a/tests/run-with-compiler/tasty-interpreter/JVMReflection.scala +++ b/tests/run-with-compiler/tasty-interpreter/JVMReflection.scala @@ -34,16 +34,13 @@ class JVMReflection[R <: Reflection & Singleton](val reflect: R) { def interpretStaticVal(moduleClass: Symbol, fn: Symbol): Object = { val instance = loadModule(moduleClass) val name = fn.name - val method = getMethod(instance.getClass, name, Nil) method.invoke(instance) } - def interpretStaticMethodCall(moduleClass: Symbol, fn: Symbol, args: => List[Object]): Object = { + def interpretStaticMethodCall(moduleClass: Symbol, fn: Symbol, args: List[Object]): Object = { val instance = loadModule(moduleClass) - val name = fn.name - - val method = getMethod(instance.getClass, name, paramsSig(fn)) + val method = getMethod(instance.getClass, fn.name, paramsSig(fn)) method.invoke(instance, args: _*) } @@ -58,29 +55,33 @@ class JVMReflection[R <: Reflection & Singleton](val reflect: R) { private def paramsSig(sym: Symbol): List[Class[_]] = { sym.asDef.signature.paramSigs.map { param => - println(param) - ??? - // defn.valueTypeNameToJavaType(param) match { - // case Some(clazz) => clazz - // case None => - // def javaArraySig(name: String): String = { - // if (name.endsWith("[]")) "[" + javaArraySig(name.dropRight(2)) - // else name match { - // case "scala.Boolean" => "Z" - // case "scala.Byte" => "B" - // case "scala.Short" => "S" - // case "scala.Int" => "I" - // case "scala.Long" => "J" - // case "scala.Float" => "F" - // case "scala.Double" => "D" - // case "scala.Char" => "C" - // case paramName => "L" + paramName + ";" - // } - // } - // def javaSig(name: String): String = - // if (name.endsWith("[]")) javaArraySig(name) else name - // java.lang.Class.forName(javaSig(param.toString), false, classLoader) - //} + def javaArraySig(name: String): String = { + if (name.endsWith("[]")) "[" + javaArraySig(name.dropRight(2)) + else name match { + case "scala.Boolean" => "Z" + case "scala.Byte" => "B" + case "scala.Short" => "S" + case "scala.Int" => "I" + case "scala.Long" => "J" + case "scala.Float" => "F" + case "scala.Double" => "D" + case "scala.Char" => "C" + case paramName => "L" + paramName + ";" + } + } + + def javaSig(name: String): String = + if (name.endsWith("[]")) javaArraySig(name) else name + + if (param == "scala.Boolean") classOf[Boolean] + else if (param == "scala.Byte") classOf[Byte] + else if (param == "scala.Char") classOf[Char] + else if (param == "scala.Short") classOf[Short] + else if (param == "scala.Int") classOf[Int] + else if (param == "scala.Long") classOf[Long] + else if (param == "scala.Float") classOf[Float] + else if (param == "scala.Double") classOf[Double] + else java.lang.Class.forName(javaSig(param), false, classLoader) } } From 32c08058dc34b50009a6e90113415c1af9a6db32 Mon Sep 17 00:00:00 2001 From: Aggelos Biboudis Date: Thu, 13 Dec 2018 18:10:07 +0100 Subject: [PATCH 12/45] Handles multiple args --- .../run-with-compiler/tasty-interpreter.check | 3 +- .../tasty-interpreter/InterpretedMain.scala | 2 + .../tasty-interpreter/Interpreter.scala | 76 ++++++++----------- .../tasty-interpreter/JVMReflection.scala | 6 ++ .../tasty-interpreter/Precompiled.scala | 3 +- 5 files changed, 43 insertions(+), 47 deletions(-) diff --git a/tests/run-with-compiler/tasty-interpreter.check b/tests/run-with-compiler/tasty-interpreter.check index e9b395013332..96e1e1ddc414 100644 --- a/tests/run-with-compiler/tasty-interpreter.check +++ b/tests/run-with-compiler/tasty-interpreter.check @@ -21,4 +21,5 @@ precompiledModule 55 56 57 -58 \ No newline at end of file +58 +59 \ No newline at end of file diff --git a/tests/run-with-compiler/tasty-interpreter/InterpretedMain.scala b/tests/run-with-compiler/tasty-interpreter/InterpretedMain.scala index 169d5b23dafe..54cc7b5fd26a 100644 --- a/tests/run-with-compiler/tasty-interpreter/InterpretedMain.scala +++ b/tests/run-with-compiler/tasty-interpreter/InterpretedMain.scala @@ -43,5 +43,7 @@ object IntepretedMain { println(Precompiled.staticVal) println(Precompiled.staticMeth1()) println(Precompiled.staticMeth2(58)) + println(Precompiled.staticMeth3(new Object)) + // println(Precompiled.staticMeth5(new Bar)) } } diff --git a/tests/run-with-compiler/tasty-interpreter/Interpreter.scala b/tests/run-with-compiler/tasty-interpreter/Interpreter.scala index bccfc2ef0023..dd10c9e15708 100644 --- a/tests/run-with-compiler/tasty-interpreter/Interpreter.scala +++ b/tests/run-with-compiler/tasty-interpreter/Interpreter.scala @@ -36,62 +36,48 @@ class Interpreter[R <: Reflection & Singleton](val reflect: R)(implicit ctx: ref } def interpretCall(fn: Tree, argss: List[List[Term]])(implicit env: Env): Any = { - (fn, argss) match { - case (Term.Ident("println"), List(List(arg))) => println(eval(arg)) - case (Term.Ident("println"), List(List())) => println() - case (_, Nil) | (_, List(List())) => fn.symbol match { - case IsDefSymbol(sym) => eval(sym.tree.rhs.get) - case _ => env(fn.symbol).get - } - case (_, x :: Nil) => - fn.symbol match { - // TODO: obviously - case IsDefSymbol(sym) => - val evaluatedArgs = x.map(arg => new Eager(eval(arg))) - val env1 = env ++ sym.tree.paramss.head.map(_.symbol).zip(evaluatedArgs) - eval(sym.tree.rhs.get)(env1) - - } + fn.symbol match { + // TODO: obviously + case IsDefSymbol(sym) => + val evaluatedArgs = argss.flatten.map(arg => new Eager(eval(arg))) + val env1 = env ++ sym.tree.paramss.headOption.getOrElse(Nil).map(_.symbol).zip(evaluatedArgs) + eval(sym.tree.rhs.get)(env1) + case _ => env(fn.symbol).get } } def reflectCall(fn: Tree, argss: List[List[Term]])(implicit env: Env): Any = { - (fn, argss) match { - case (_, Nil) | (_, List(List())) => fn.symbol match { - case IsDefSymbol(sym) => jvmReflection.interpretStaticMethodCall(fn.symbol.owner, fn.symbol, Nil) - case _ => - if (fn.symbol.flags.isObject) { - jvmReflection.loadModule(fn.symbol.asVal.moduleClass.get) - } - // call to a static val - else { - jvmReflection.interpretStaticVal(fn.symbol.owner, fn.symbol) - } - } - case (_, x :: Nil) => - import Term._ - fn.symbol match { - // TODO: obviously - case IsDefSymbol(sym) => - if(sym.name == "==") eval(Term.IsSelect.unapply(fn).get.qualifier).asInstanceOf[Int] == eval(x.head).asInstanceOf[Int] - else if(sym.name == ">") eval(Term.IsSelect.unapply(fn).get.qualifier).asInstanceOf[Int] > eval(x.head).asInstanceOf[Int] - else if(sym.name == "-") eval(Term.IsSelect.unapply(fn).get.qualifier).asInstanceOf[Int] - eval(x.head).asInstanceOf[Int] - else if(sym.name == "+") eval(Term.IsSelect.unapply(fn).get.qualifier).asInstanceOf[Int] + eval(x.head).asInstanceOf[Int] - else { - def args: List[Object] = argss.flatMap((a: List[Term]) => a.map(b => eval(b).asInstanceOf[Object])) - jvmReflection.interpretStaticMethodCall(fn.symbol.owner, fn.symbol, args) - } + // println(fn.show) + def evaluatedArgs: List[Object] = argss.flatMap((a: List[Term]) => a.map(b => eval(b).asInstanceOf[Object])) + + import Term._ + fn.symbol match { + // TODO: obviously + case IsDefSymbol(sym) => + if(sym.name == "") jvmReflection.interpretNew(sym, evaluatedArgs) + else if(sym.name == "==") eval(Term.IsSelect.unapply(fn).get.qualifier).asInstanceOf[Int] == eval(argss.head.head).asInstanceOf[Int] + else if(sym.name == ">") eval(Term.IsSelect.unapply(fn).get.qualifier).asInstanceOf[Int] > eval(argss.head.head).asInstanceOf[Int] + else if(sym.name == "-") eval(Term.IsSelect.unapply(fn).get.qualifier).asInstanceOf[Int] - eval(argss.head.head).asInstanceOf[Int] + else if(sym.name == "+") eval(Term.IsSelect.unapply(fn).get.qualifier).asInstanceOf[Int] + eval(argss.head.head).asInstanceOf[Int] + else { + jvmReflection.interpretStaticMethodCall(fn.symbol.owner, fn.symbol, evaluatedArgs) + } + case _ => + if (fn.symbol.flags.isObject) { + jvmReflection.loadModule(fn.symbol.asVal.moduleClass.get) + } + // call to a static val + else { + jvmReflection.interpretStaticVal(fn.symbol.owner, fn.symbol) } } } + def eval(tree: Statement)(implicit env: Env): Any = { tree match { case Call(fn, argss) => - fn match { - case Term.Ident("println") => interpretCall(fn, argss) - case _ => if (fn.symbol.isDefinedInCurrentRun) interpretCall(fn, argss) else reflectCall(fn, argss) - } + if (fn.symbol.isDefinedInCurrentRun) interpretCall(fn, argss) else reflectCall(fn, argss) case Term.Assign(lhs, rhs) => env(lhs.symbol) match { diff --git a/tests/run-with-compiler/tasty-interpreter/JVMReflection.scala b/tests/run-with-compiler/tasty-interpreter/JVMReflection.scala index ea2529666c7c..e719d3c36a90 100644 --- a/tests/run-with-compiler/tasty-interpreter/JVMReflection.scala +++ b/tests/run-with-compiler/tasty-interpreter/JVMReflection.scala @@ -44,6 +44,12 @@ class JVMReflection[R <: Reflection & Singleton](val reflect: R) { method.invoke(instance, args: _*) } + def interpretNew(fn: Symbol, args: List[Object]): Object = { + val clazz = loadClass(fn.owner.fullName) + val constr = clazz.getConstructor(paramsSig(fn): _*) + constr.newInstance(args: _*).asInstanceOf[Object] + } + def getMethod(clazz: Class[_], name: String, paramClasses: List[Class[_]]): Method = { try clazz.getMethod(name, paramClasses: _*) catch { diff --git a/tests/run-with-compiler/tasty-interpreter/Precompiled.scala b/tests/run-with-compiler/tasty-interpreter/Precompiled.scala index f99ac8ba2b7c..93569a5ff8aa 100644 --- a/tests/run-with-compiler/tasty-interpreter/Precompiled.scala +++ b/tests/run-with-compiler/tasty-interpreter/Precompiled.scala @@ -10,7 +10,8 @@ object Precompiled { // Todo def staticMeth1() = 57 def staticMeth2(arg: Int) = arg - // def staticMeth3(arg: Bar): Int = arg.meth() + def staticMeth3(arg: Object): Int = 59 + def staticMeth4(arg: Bar): Int = arg.meth() override def toString() = "precompiledModule" } \ No newline at end of file From 9cf5c4d27959f0380fff1ef2f1d89fdcabc5185a Mon Sep 17 00:00:00 2001 From: Aggelos Biboudis Date: Fri, 14 Dec 2018 18:15:00 +0100 Subject: [PATCH 13/45] Adding reflective proxies --- .../run-with-compiler/tasty-interpreter.check | 4 +- .../tasty-interpreter/InterpretedMain.scala | 10 +++- .../tasty-interpreter/Interpreter.scala | 58 +++++++++++++++++-- .../tasty-interpreter/JVMReflection.scala | 4 ++ .../tasty-interpreter/Precompiled.scala | 11 +++- .../tasty-interpreter/Test.scala | 2 +- .../tasty-interpreter/notes.md | 16 +++++ 7 files changed, 94 insertions(+), 11 deletions(-) create mode 100644 tests/run-with-compiler/tasty-interpreter/notes.md diff --git a/tests/run-with-compiler/tasty-interpreter.check b/tests/run-with-compiler/tasty-interpreter.check index 96e1e1ddc414..663c28d296c9 100644 --- a/tests/run-with-compiler/tasty-interpreter.check +++ b/tests/run-with-compiler/tasty-interpreter.check @@ -22,4 +22,6 @@ precompiledModule 56 57 58 -59 \ No newline at end of file +59 +60 +61 \ No newline at end of file diff --git a/tests/run-with-compiler/tasty-interpreter/InterpretedMain.scala b/tests/run-with-compiler/tasty-interpreter/InterpretedMain.scala index 54cc7b5fd26a..cdaa228bf9cb 100644 --- a/tests/run-with-compiler/tasty-interpreter/InterpretedMain.scala +++ b/tests/run-with-compiler/tasty-interpreter/InterpretedMain.scala @@ -44,6 +44,14 @@ object IntepretedMain { println(Precompiled.staticMeth1()) println(Precompiled.staticMeth2(58)) println(Precompiled.staticMeth3(new Object)) - // println(Precompiled.staticMeth5(new Bar)) + println(Precompiled.staticMeth4(new Bar)) + println(Precompiled.staticMeth5(new Bar, 61)) + println(Precompiled.staticMeth4(new InterpretedBar)) + println(Precompiled.staticMeth5(new InterpretedBar, 61)) } } + +class InterpretedBar extends IFace { + def meth(): Int = 70 + def methA(x: Int): Int = x + 1 +} diff --git a/tests/run-with-compiler/tasty-interpreter/Interpreter.scala b/tests/run-with-compiler/tasty-interpreter/Interpreter.scala index dd10c9e15708..234d173c3ea9 100644 --- a/tests/run-with-compiler/tasty-interpreter/Interpreter.scala +++ b/tests/run-with-compiler/tasty-interpreter/Interpreter.scala @@ -36,8 +36,9 @@ class Interpreter[R <: Reflection & Singleton](val reflect: R)(implicit ctx: ref } def interpretCall(fn: Tree, argss: List[List[Term]])(implicit env: Env): Any = { + println(fn) + env.foreach(println) fn.symbol match { - // TODO: obviously case IsDefSymbol(sym) => val evaluatedArgs = argss.flatten.map(arg => new Eager(eval(arg))) val env1 = env ++ sym.tree.paramss.headOption.getOrElse(Nil).map(_.symbol).zip(evaluatedArgs) @@ -46,21 +47,57 @@ class Interpreter[R <: Reflection & Singleton](val reflect: R)(implicit ctx: ref } } - def reflectCall(fn: Tree, argss: List[List[Term]])(implicit env: Env): Any = { - // println(fn.show) - def evaluatedArgs: List[Object] = argss.flatMap((a: List[Term]) => a.map(b => eval(b).asInstanceOf[Object])) + def interpretNew(fn: Tree, argss: List[List[Term]])(implicit env: Env): Any = { + + fn.symbol.owner match { + case IsClassSymbol(sym) => + + val parentSymbols = sym.tree.parents.tail.map(_.asInstanceOf[TypeTree].symbol).head + import java.lang.reflect._ + val handler: InvocationHandler = new InvocationHandler() { + val instance = new Object + def invoke(proxy: Object, method: Method, args: scala.Array[Object]): Object = { + + // println(method) + val symbol = sym.methods.find(_.name == method.getName).get + + if(symbol.isDefinedInCurrentRun) { + symbol match { + case IsDefSymbol(symbol) => + val args1 =if(args == null) Nil else args.toList + val evaluatedArgs = args1.map(arg => new Eager(arg)) + + val env1 = env ++ symbol.tree.paramss.headOption.getOrElse(Nil).map(_.symbol).zip(evaluatedArgs) + println(symbol.tree) + eval(symbol.tree.rhs.get)(env1).asInstanceOf[Object] + } + } + else + method.invoke(instance, args: _*) + } + + } + val proxyClass: Class[_] = Proxy.getProxyClass(getClass.getClassLoader, jvmReflection.loadClass(parentSymbols.fullName)) + proxyClass.getConstructor(classOf[InvocationHandler]).newInstance(handler); + } + } + + def reflectCall(fn: Tree, argss: List[List[Term]])(implicit env: Env): Any = { import Term._ + println(fn.show) fn.symbol match { // TODO: obviously case IsDefSymbol(sym) => - if(sym.name == "") jvmReflection.interpretNew(sym, evaluatedArgs) + if(sym.name == "") jvmReflection.interpretNew(sym, evaluatedArgss(argss)) else if(sym.name == "==") eval(Term.IsSelect.unapply(fn).get.qualifier).asInstanceOf[Int] == eval(argss.head.head).asInstanceOf[Int] else if(sym.name == ">") eval(Term.IsSelect.unapply(fn).get.qualifier).asInstanceOf[Int] > eval(argss.head.head).asInstanceOf[Int] else if(sym.name == "-") eval(Term.IsSelect.unapply(fn).get.qualifier).asInstanceOf[Int] - eval(argss.head.head).asInstanceOf[Int] else if(sym.name == "+") eval(Term.IsSelect.unapply(fn).get.qualifier).asInstanceOf[Int] + eval(argss.head.head).asInstanceOf[Int] else { - jvmReflection.interpretStaticMethodCall(fn.symbol.owner, fn.symbol, evaluatedArgs) + val argss2 = evaluatedArgss(argss) + argss2.foreach(println) + jvmReflection.interpretStaticMethodCall(fn.symbol.owner, fn.symbol, argss2) } case _ => if (fn.symbol.flags.isObject) { @@ -73,9 +110,18 @@ class Interpreter[R <: Reflection & Singleton](val reflect: R)(implicit ctx: ref } } + def evaluatedArgss(argss: List[List[Term]])(implicit env: Env): List[Object] = argss.flatMap((a: List[Term]) => a.map(b => eval(b).asInstanceOf[Object])) + + def reflectNew(fn: Tree, argss: List[List[Term]])(implicit env: Env): Any = { + + jvmReflection.interpretNew(fn.symbol, evaluatedArgss(argss)) + } + def eval(tree: Statement)(implicit env: Env): Any = { tree match { + case Call(fn@ Term.Select(_, ""), argss) => + if (fn.symbol.isDefinedInCurrentRun) interpretNew(fn, argss) else reflectNew(fn, argss) case Call(fn, argss) => if (fn.symbol.isDefinedInCurrentRun) interpretCall(fn, argss) else reflectCall(fn, argss) diff --git a/tests/run-with-compiler/tasty-interpreter/JVMReflection.scala b/tests/run-with-compiler/tasty-interpreter/JVMReflection.scala index e719d3c36a90..e676d3ee9106 100644 --- a/tests/run-with-compiler/tasty-interpreter/JVMReflection.scala +++ b/tests/run-with-compiler/tasty-interpreter/JVMReflection.scala @@ -41,6 +41,10 @@ class JVMReflection[R <: Reflection & Singleton](val reflect: R) { def interpretStaticMethodCall(moduleClass: Symbol, fn: Symbol, args: List[Object]): Object = { val instance = loadModule(moduleClass) val method = getMethod(instance.getClass, fn.name, paramsSig(fn)) + println(">>>>>>") + println(moduleClass) + println(fn) + println(method) method.invoke(instance, args: _*) } diff --git a/tests/run-with-compiler/tasty-interpreter/Precompiled.scala b/tests/run-with-compiler/tasty-interpreter/Precompiled.scala index 93569a5ff8aa..df050222ae2b 100644 --- a/tests/run-with-compiler/tasty-interpreter/Precompiled.scala +++ b/tests/run-with-compiler/tasty-interpreter/Precompiled.scala @@ -1,6 +1,12 @@ -trait Bar { +class Bar extends IFace { + def meth(): Int = 60 + def methA(x: Int): Int = x +} + +trait IFace { def meth(): Int + def methA(x: Int): Int } object Precompiled { @@ -11,7 +17,8 @@ object Precompiled { def staticMeth1() = 57 def staticMeth2(arg: Int) = arg def staticMeth3(arg: Object): Int = 59 - def staticMeth4(arg: Bar): Int = arg.meth() + def staticMeth4(arg: IFace): Int = arg.meth() + def staticMeth5(arg: IFace, x: Int): Int = arg.methA(x) override def toString() = "precompiledModule" } \ No newline at end of file diff --git a/tests/run-with-compiler/tasty-interpreter/Test.scala b/tests/run-with-compiler/tasty-interpreter/Test.scala index 646bf90410ce..8b0c4b990b8b 100644 --- a/tests/run-with-compiler/tasty-interpreter/Test.scala +++ b/tests/run-with-compiler/tasty-interpreter/Test.scala @@ -3,7 +3,7 @@ import scala.tasty.file._ object Test { def main(args: Array[String]): Unit = { - ConsumeTasty("", List("IntepretedMain"), new TastyInterpreter) + ConsumeTasty("", List("IntepretedMain", "InterpretedBar"), new TastyInterpreter) } } diff --git a/tests/run-with-compiler/tasty-interpreter/notes.md b/tests/run-with-compiler/tasty-interpreter/notes.md new file mode 100644 index 000000000000..b3438f571d25 --- /dev/null +++ b/tests/run-with-compiler/tasty-interpreter/notes.md @@ -0,0 +1,16 @@ +## Design Notes + + +- Abstract platform operations +- Proxies + + // Foo defined in current run + class Foo extends IFace1 with IFace2 ... + new Foo.meth() + + // Bar is precompiled + class Bar { + def meth(x: IFace1) = x.meth() + } + + FooProxy(obj).meth() \ No newline at end of file From 8b076ba4c83441cb522957319755ffc9d6b05c0c Mon Sep 17 00:00:00 2001 From: Aggelos Biboudis Date: Mon, 17 Dec 2018 18:09:08 +0100 Subject: [PATCH 14/45] Cleanup --- tests/run-with-compiler/tasty-interpreter.check | 4 +++- .../tasty-interpreter/Interpreter.scala | 10 +++++----- .../tasty-interpreter/JVMReflection.scala | 8 ++++---- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/tests/run-with-compiler/tasty-interpreter.check b/tests/run-with-compiler/tasty-interpreter.check index 663c28d296c9..093c0329e272 100644 --- a/tests/run-with-compiler/tasty-interpreter.check +++ b/tests/run-with-compiler/tasty-interpreter.check @@ -24,4 +24,6 @@ precompiledModule 58 59 60 -61 \ No newline at end of file +61 +70 +62 \ No newline at end of file diff --git a/tests/run-with-compiler/tasty-interpreter/Interpreter.scala b/tests/run-with-compiler/tasty-interpreter/Interpreter.scala index 234d173c3ea9..6041eab70124 100644 --- a/tests/run-with-compiler/tasty-interpreter/Interpreter.scala +++ b/tests/run-with-compiler/tasty-interpreter/Interpreter.scala @@ -36,8 +36,8 @@ class Interpreter[R <: Reflection & Singleton](val reflect: R)(implicit ctx: ref } def interpretCall(fn: Tree, argss: List[List[Term]])(implicit env: Env): Any = { - println(fn) - env.foreach(println) + // println(fn) + // env.foreach(println) fn.symbol match { case IsDefSymbol(sym) => val evaluatedArgs = argss.flatten.map(arg => new Eager(eval(arg))) @@ -68,7 +68,7 @@ class Interpreter[R <: Reflection & Singleton](val reflect: R)(implicit ctx: ref val evaluatedArgs = args1.map(arg => new Eager(arg)) val env1 = env ++ symbol.tree.paramss.headOption.getOrElse(Nil).map(_.symbol).zip(evaluatedArgs) - println(symbol.tree) + // println(symbol.tree) eval(symbol.tree.rhs.get)(env1).asInstanceOf[Object] } } @@ -85,7 +85,7 @@ class Interpreter[R <: Reflection & Singleton](val reflect: R)(implicit ctx: ref def reflectCall(fn: Tree, argss: List[List[Term]])(implicit env: Env): Any = { import Term._ - println(fn.show) + // println(fn.show) fn.symbol match { // TODO: obviously case IsDefSymbol(sym) => @@ -96,7 +96,7 @@ class Interpreter[R <: Reflection & Singleton](val reflect: R)(implicit ctx: ref else if(sym.name == "+") eval(Term.IsSelect.unapply(fn).get.qualifier).asInstanceOf[Int] + eval(argss.head.head).asInstanceOf[Int] else { val argss2 = evaluatedArgss(argss) - argss2.foreach(println) + // argss2.foreach(println) jvmReflection.interpretStaticMethodCall(fn.symbol.owner, fn.symbol, argss2) } case _ => diff --git a/tests/run-with-compiler/tasty-interpreter/JVMReflection.scala b/tests/run-with-compiler/tasty-interpreter/JVMReflection.scala index e676d3ee9106..d50d26de002c 100644 --- a/tests/run-with-compiler/tasty-interpreter/JVMReflection.scala +++ b/tests/run-with-compiler/tasty-interpreter/JVMReflection.scala @@ -41,10 +41,10 @@ class JVMReflection[R <: Reflection & Singleton](val reflect: R) { def interpretStaticMethodCall(moduleClass: Symbol, fn: Symbol, args: List[Object]): Object = { val instance = loadModule(moduleClass) val method = getMethod(instance.getClass, fn.name, paramsSig(fn)) - println(">>>>>>") - println(moduleClass) - println(fn) - println(method) + // println(">>>>>>") + // println(moduleClass) + // println(fn) + // println(method) method.invoke(instance, args: _*) } From 6d96ba211a066f98dc10b5fffa6ed13daeae3d30 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 4 Jan 2019 10:50:29 +0100 Subject: [PATCH 15/45] Move classes into packages --- .../dotty/tools/dotc/BootstrappedOnlyCompilationTests.scala | 2 ++ .../tasty-interpreter.check | 0 .../tasty-interpreter/InterpretedMain.scala | 0 .../tasty-interpreter/Precompiled.scala | 0 .../tasty-interpreter/Test.scala | 3 ++- .../tasty-interpreter/interpreter}/Interpreter.scala | 3 +++ .../tasty-interpreter/interpreter/jvm}/JVMReflection.scala | 2 ++ .../tasty-interpreter/notes.md | 0 8 files changed, 9 insertions(+), 1 deletion(-) rename tests/{run-with-compiler => run-with-compiler-custom-args}/tasty-interpreter.check (100%) rename tests/{run-with-compiler => run-with-compiler-custom-args}/tasty-interpreter/InterpretedMain.scala (100%) rename tests/{run-with-compiler => run-with-compiler-custom-args}/tasty-interpreter/Precompiled.scala (100%) rename tests/{run-with-compiler => run-with-compiler-custom-args}/tasty-interpreter/Test.scala (95%) rename tests/{run-with-compiler/tasty-interpreter => run-with-compiler-custom-args/tasty-interpreter/interpreter}/Interpreter.scala (98%) rename tests/{run-with-compiler/tasty-interpreter => run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm}/JVMReflection.scala (98%) rename tests/{run-with-compiler => run-with-compiler-custom-args}/tasty-interpreter/notes.md (100%) diff --git a/compiler/test/dotty/tools/dotc/BootstrappedOnlyCompilationTests.scala b/compiler/test/dotty/tools/dotc/BootstrappedOnlyCompilationTests.scala index be9cffb8a598..7c9859a3f5ea 100644 --- a/compiler/test/dotty/tools/dotc/BootstrappedOnlyCompilationTests.scala +++ b/compiler/test/dotty/tools/dotc/BootstrappedOnlyCompilationTests.scala @@ -49,6 +49,7 @@ class BootstrappedOnlyCompilationTests extends ParallelTesting { @Test def posTwiceWithCompiler: Unit = { implicit val testGroup: TestGroup = TestGroup("posTwiceWithCompiler") compileFile("tests/pos-with-compiler/Labels.scala", withCompilerOptions) + + compileFile("tests/pos-with-compiler/Labels.scala", withCompilerOptions) + compileFile("tests/pos-with-compiler/Patterns.scala", withCompilerOptions) + compileList( "testNonCyclic", @@ -81,6 +82,7 @@ class BootstrappedOnlyCompilationTests extends ParallelTesting { @Test def runWithCompiler: Unit = { implicit val testGroup: TestGroup = TestGroup("runWithCompiler") compileFilesInDir("tests/run-with-compiler", withCompilerOptions) + + compileDir("tests/run-with-compiler-custom-args/tasty-interpreter", withCompilerOptions) + compileFile("tests/run-with-compiler-custom-args/staged-streams_1.scala", withCompilerOptions without "-Yno-deep-subtypes") }.checkRuns() diff --git a/tests/run-with-compiler/tasty-interpreter.check b/tests/run-with-compiler-custom-args/tasty-interpreter.check similarity index 100% rename from tests/run-with-compiler/tasty-interpreter.check rename to tests/run-with-compiler-custom-args/tasty-interpreter.check diff --git a/tests/run-with-compiler/tasty-interpreter/InterpretedMain.scala b/tests/run-with-compiler-custom-args/tasty-interpreter/InterpretedMain.scala similarity index 100% rename from tests/run-with-compiler/tasty-interpreter/InterpretedMain.scala rename to tests/run-with-compiler-custom-args/tasty-interpreter/InterpretedMain.scala diff --git a/tests/run-with-compiler/tasty-interpreter/Precompiled.scala b/tests/run-with-compiler-custom-args/tasty-interpreter/Precompiled.scala similarity index 100% rename from tests/run-with-compiler/tasty-interpreter/Precompiled.scala rename to tests/run-with-compiler-custom-args/tasty-interpreter/Precompiled.scala diff --git a/tests/run-with-compiler/tasty-interpreter/Test.scala b/tests/run-with-compiler-custom-args/tasty-interpreter/Test.scala similarity index 95% rename from tests/run-with-compiler/tasty-interpreter/Test.scala rename to tests/run-with-compiler-custom-args/tasty-interpreter/Test.scala index 8b0c4b990b8b..31c0660f6b00 100644 --- a/tests/run-with-compiler/tasty-interpreter/Test.scala +++ b/tests/run-with-compiler-custom-args/tasty-interpreter/Test.scala @@ -1,5 +1,6 @@ -import scala.tasty.Reflection import scala.tasty.file._ +import scala.tasty.interpreter.Interpreter +import scala.tasty.Reflection object Test { def main(args: Array[String]): Unit = { diff --git a/tests/run-with-compiler/tasty-interpreter/Interpreter.scala b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/Interpreter.scala similarity index 98% rename from tests/run-with-compiler/tasty-interpreter/Interpreter.scala rename to tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/Interpreter.scala index 6041eab70124..cb549832fb52 100644 --- a/tests/run-with-compiler/tasty-interpreter/Interpreter.scala +++ b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/Interpreter.scala @@ -1,3 +1,6 @@ +package scala.tasty.interpreter + +import scala.tasty.interpreter.jvm.JVMReflection import scala.tasty.Reflection trait Ref { diff --git a/tests/run-with-compiler/tasty-interpreter/JVMReflection.scala b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/JVMReflection.scala similarity index 98% rename from tests/run-with-compiler/tasty-interpreter/JVMReflection.scala rename to tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/JVMReflection.scala index d50d26de002c..cf45f0804cfb 100644 --- a/tests/run-with-compiler/tasty-interpreter/JVMReflection.scala +++ b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/JVMReflection.scala @@ -1,3 +1,5 @@ +package scala.tasty.interpreter.jvm + import scala.tasty.Reflection class JVMReflection[R <: Reflection & Singleton](val reflect: R) { diff --git a/tests/run-with-compiler/tasty-interpreter/notes.md b/tests/run-with-compiler-custom-args/tasty-interpreter/notes.md similarity index 100% rename from tests/run-with-compiler/tasty-interpreter/notes.md rename to tests/run-with-compiler-custom-args/tasty-interpreter/notes.md From 642c29f66483297fb9407e29547f22040b29cf49 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 4 Jan 2019 10:55:11 +0100 Subject: [PATCH 16/45] Move Ref and subclasses to to their own files --- .../interpreter/Interpreter.scala | 19 +------------------ .../interpreter/abstr/Eager.scala | 3 +++ .../interpreter/abstr/Lazy.scala | 5 +++++ .../interpreter/abstr/Ref.scala | 5 +++++ .../interpreter/abstr/Var.scala | 9 +++++++++ 5 files changed, 23 insertions(+), 18 deletions(-) create mode 100644 tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/abstr/Eager.scala create mode 100644 tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/abstr/Lazy.scala create mode 100644 tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/abstr/Ref.scala create mode 100644 tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/abstr/Var.scala diff --git a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/Interpreter.scala b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/Interpreter.scala index cb549832fb52..bae359200ee0 100644 --- a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/Interpreter.scala +++ b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/Interpreter.scala @@ -1,26 +1,9 @@ package scala.tasty.interpreter +import scala.tasty.interpreter.abstr.{Ref, Eager, Lazy, Var} import scala.tasty.interpreter.jvm.JVMReflection import scala.tasty.Reflection -trait Ref { - def get: Any -} - -class Eager(val get: Any) extends Ref - -class Lazy(thunk: => Any) extends Ref { - lazy val get: Any = thunk -} - -class Var(private var value: Any) extends Ref { - def get = value - - def update(rhs: Any): Unit = { - value = rhs - } -} - class Interpreter[R <: Reflection & Singleton](val reflect: R)(implicit ctx: reflect.Context) { import reflect._ diff --git a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/abstr/Eager.scala b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/abstr/Eager.scala new file mode 100644 index 000000000000..00271f50659f --- /dev/null +++ b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/abstr/Eager.scala @@ -0,0 +1,3 @@ +package scala.tasty.interpreter.abstr + +class Eager(val get: Any) extends Ref diff --git a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/abstr/Lazy.scala b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/abstr/Lazy.scala new file mode 100644 index 000000000000..65ae72841f88 --- /dev/null +++ b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/abstr/Lazy.scala @@ -0,0 +1,5 @@ +package scala.tasty.interpreter.abstr + +class Lazy(thunk: => Any) extends Ref { + lazy val get: Any = thunk +} diff --git a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/abstr/Ref.scala b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/abstr/Ref.scala new file mode 100644 index 000000000000..d06fcc6d1968 --- /dev/null +++ b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/abstr/Ref.scala @@ -0,0 +1,5 @@ +package scala.tasty.interpreter.abstr + +trait Ref { + def get: Any +} diff --git a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/abstr/Var.scala b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/abstr/Var.scala new file mode 100644 index 000000000000..59bee1382369 --- /dev/null +++ b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/abstr/Var.scala @@ -0,0 +1,9 @@ +package scala.tasty.interpreter.abstr + +class Var(private var value: Any) extends Ref { + def get = value + + def update(rhs: Any): Unit = { + value = rhs + } +} From c05a971ab5b84cf3cf8c5d739c9e724f3b578d1e Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 4 Jan 2019 10:57:50 +0100 Subject: [PATCH 17/45] Remove unnecessary ctx --- .../tasty-interpreter/interpreter/Interpreter.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/Interpreter.scala b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/Interpreter.scala index bae359200ee0..586237c9a798 100644 --- a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/Interpreter.scala +++ b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/Interpreter.scala @@ -4,7 +4,7 @@ import scala.tasty.interpreter.abstr.{Ref, Eager, Lazy, Var} import scala.tasty.interpreter.jvm.JVMReflection import scala.tasty.Reflection -class Interpreter[R <: Reflection & Singleton](val reflect: R)(implicit ctx: reflect.Context) { +class Interpreter[R <: Reflection & Singleton](val reflect: R) { import reflect._ val jvmReflection = new JVMReflection(reflect) From a5ae0222165ab36cadb5ff4f8da393b2acf32852 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 4 Jan 2019 11:51:34 +0100 Subject: [PATCH 18/45] Split tree interreter from jvm interpreter --- .../tasty-interpreter/Test.scala | 2 +- .../interpreter/Interpreter.scala | 161 ------------------ .../interpreter/TreeInterpreter.scala | 87 ++++++++++ .../interpreter/jvm/Interpreter.scala | 86 ++++++++++ 4 files changed, 174 insertions(+), 162 deletions(-) delete mode 100644 tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/Interpreter.scala create mode 100644 tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/TreeInterpreter.scala create mode 100644 tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala diff --git a/tests/run-with-compiler-custom-args/tasty-interpreter/Test.scala b/tests/run-with-compiler-custom-args/tasty-interpreter/Test.scala index 31c0660f6b00..767415e377f7 100644 --- a/tests/run-with-compiler-custom-args/tasty-interpreter/Test.scala +++ b/tests/run-with-compiler-custom-args/tasty-interpreter/Test.scala @@ -1,5 +1,5 @@ import scala.tasty.file._ -import scala.tasty.interpreter.Interpreter +import scala.tasty.interpreter.jvm.Interpreter import scala.tasty.Reflection object Test { diff --git a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/Interpreter.scala b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/Interpreter.scala deleted file mode 100644 index 586237c9a798..000000000000 --- a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/Interpreter.scala +++ /dev/null @@ -1,161 +0,0 @@ -package scala.tasty.interpreter - -import scala.tasty.interpreter.abstr.{Ref, Eager, Lazy, Var} -import scala.tasty.interpreter.jvm.JVMReflection -import scala.tasty.Reflection - -class Interpreter[R <: Reflection & Singleton](val reflect: R) { - import reflect._ - - val jvmReflection = new JVMReflection(reflect) - - type Env = Map[Symbol, Ref] - - object Call { - def unapply(arg: Tree): Option[(Tree, List[List[Term]])] = arg match { - case fn@ Term.Select(_, _) => Some((fn, Nil)) - case fn@ Term.Ident(_) => Some((fn, Nil)) - case Term.Apply(Call(fn, args1), args2) => Some((fn, args1 :+ args2)) - case Term.TypeApply(Call(fn, args), _) => Some((fn, args)) - case _ => None - } - } - - def interpretCall(fn: Tree, argss: List[List[Term]])(implicit env: Env): Any = { - // println(fn) - // env.foreach(println) - fn.symbol match { - case IsDefSymbol(sym) => - val evaluatedArgs = argss.flatten.map(arg => new Eager(eval(arg))) - val env1 = env ++ sym.tree.paramss.headOption.getOrElse(Nil).map(_.symbol).zip(evaluatedArgs) - eval(sym.tree.rhs.get)(env1) - case _ => env(fn.symbol).get - } - } - - def interpretNew(fn: Tree, argss: List[List[Term]])(implicit env: Env): Any = { - - fn.symbol.owner match { - case IsClassSymbol(sym) => - - val parentSymbols = sym.tree.parents.tail.map(_.asInstanceOf[TypeTree].symbol).head - import java.lang.reflect._ - val handler: InvocationHandler = new InvocationHandler() { - val instance = new Object - def invoke(proxy: Object, method: Method, args: scala.Array[Object]): Object = { - - // println(method) - val symbol = sym.methods.find(_.name == method.getName).get - - if(symbol.isDefinedInCurrentRun) { - symbol match { - case IsDefSymbol(symbol) => - val args1 =if(args == null) Nil else args.toList - val evaluatedArgs = args1.map(arg => new Eager(arg)) - - val env1 = env ++ symbol.tree.paramss.headOption.getOrElse(Nil).map(_.symbol).zip(evaluatedArgs) - // println(symbol.tree) - eval(symbol.tree.rhs.get)(env1).asInstanceOf[Object] - } - } - else - method.invoke(instance, args: _*) - } - - } - val proxyClass: Class[_] = Proxy.getProxyClass(getClass.getClassLoader, jvmReflection.loadClass(parentSymbols.fullName)) - proxyClass.getConstructor(classOf[InvocationHandler]).newInstance(handler); - - } - } - - def reflectCall(fn: Tree, argss: List[List[Term]])(implicit env: Env): Any = { - import Term._ - // println(fn.show) - fn.symbol match { - // TODO: obviously - case IsDefSymbol(sym) => - if(sym.name == "") jvmReflection.interpretNew(sym, evaluatedArgss(argss)) - else if(sym.name == "==") eval(Term.IsSelect.unapply(fn).get.qualifier).asInstanceOf[Int] == eval(argss.head.head).asInstanceOf[Int] - else if(sym.name == ">") eval(Term.IsSelect.unapply(fn).get.qualifier).asInstanceOf[Int] > eval(argss.head.head).asInstanceOf[Int] - else if(sym.name == "-") eval(Term.IsSelect.unapply(fn).get.qualifier).asInstanceOf[Int] - eval(argss.head.head).asInstanceOf[Int] - else if(sym.name == "+") eval(Term.IsSelect.unapply(fn).get.qualifier).asInstanceOf[Int] + eval(argss.head.head).asInstanceOf[Int] - else { - val argss2 = evaluatedArgss(argss) - // argss2.foreach(println) - jvmReflection.interpretStaticMethodCall(fn.symbol.owner, fn.symbol, argss2) - } - case _ => - if (fn.symbol.flags.isObject) { - jvmReflection.loadModule(fn.symbol.asVal.moduleClass.get) - } - // call to a static val - else { - jvmReflection.interpretStaticVal(fn.symbol.owner, fn.symbol) - } - } - } - - def evaluatedArgss(argss: List[List[Term]])(implicit env: Env): List[Object] = argss.flatMap((a: List[Term]) => a.map(b => eval(b).asInstanceOf[Object])) - - def reflectNew(fn: Tree, argss: List[List[Term]])(implicit env: Env): Any = { - - jvmReflection.interpretNew(fn.symbol, evaluatedArgss(argss)) - } - - - def eval(tree: Statement)(implicit env: Env): Any = { - tree match { - case Call(fn@ Term.Select(_, ""), argss) => - if (fn.symbol.isDefinedInCurrentRun) interpretNew(fn, argss) else reflectNew(fn, argss) - case Call(fn, argss) => - if (fn.symbol.isDefinedInCurrentRun) interpretCall(fn, argss) else reflectCall(fn, argss) - - case Term.Assign(lhs, rhs) => - env(lhs.symbol) match { - case varf: Var => varf.update(eval(rhs)) - } - - case Term.If(cond, thenp, elsep) => - if(eval(cond).asInstanceOf[Boolean]) - eval(thenp) - else - eval(elsep) - - case Term.While(cond, expr) => - while(eval(cond).asInstanceOf[Boolean]){ - eval(expr) - } - - case Term.Block(stats, expr) => - val newEnv = stats.foldLeft(env)((accEnv, stat) => stat match { - case ValDef(name, tpt, Some(rhs)) => - val evalRef: Ref = - if (stat.symbol.flags.isLazy) - // do not factor out rhs from here (laziness) - new Lazy(eval(rhs)(accEnv)) - else if (stat.symbol.flags.isMutable) - new Var(eval(rhs)(accEnv)) - else - new Eager(eval(rhs)(accEnv)) - - accEnv.updated(stat.symbol, evalRef) - case DefDef(_, _, _, _, _) => - // TODO: record the environment for closure purposes - accEnv - case stat => - eval(stat)(accEnv) - accEnv - }) - eval(expr)(newEnv) - - - case Term.Literal(const) => - const.value - - case Term.Typed(expr, _) => eval(expr) - - case _ => throw new MatchError(tree.show) - } - } -} \ No newline at end of file diff --git a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/TreeInterpreter.scala b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/TreeInterpreter.scala new file mode 100644 index 000000000000..a042d6a522bb --- /dev/null +++ b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/TreeInterpreter.scala @@ -0,0 +1,87 @@ +package scala.tasty.interpreter + +import scala.tasty.interpreter.abstr.{Ref, Eager, Lazy, Var} +import scala.tasty.interpreter.jvm.JVMReflection +import scala.tasty.Reflection + +abstract class TreeInterpreter[R <: Reflection & Singleton](val reflect: R) { + import reflect._ + + type Env = Map[Symbol, Ref] + + def interpretCall(fn: Tree, argss: List[List[Term]])(implicit env: Env): Any = { + fn.symbol match { + case IsDefSymbol(sym) => + val evaluatedArgs = argss.flatten.map(arg => new Eager(eval(arg))) + val env1 = env ++ sym.tree.paramss.headOption.getOrElse(Nil).map(_.symbol).zip(evaluatedArgs) + eval(sym.tree.rhs.get)(env1) + case _ => + env(fn.symbol).get + } + } + + def interpretNew(fn: Tree, argss: List[List[Term]])(implicit env: Env): Any + + def eval(tree: Statement)(implicit env: Env): Any = { + tree match { + case Call(fn, argss) => + fn match { + case Term.Select(_, "") => interpretNew(fn, argss) + case _ => interpretCall(fn, argss) + } + + case Term.Assign(lhs, rhs) => + env(lhs.symbol) match { + case varf: Var => varf.update(eval(rhs)) + } + + case Term.If(cond, thenp, elsep) => + if (eval(cond).asInstanceOf[Boolean]) eval(thenp) + else eval(elsep) + + case Term.While(cond, expr) => + while (eval(cond).asInstanceOf[Boolean]){ + eval(expr) + } + + case Term.Block(stats, expr) => + val newEnv = stats.foldLeft(env)((accEnv, stat) => stat match { + case ValDef(name, tpt, Some(rhs)) => + val evalRef: Ref = + if (stat.symbol.flags.isLazy) + // do not factor out rhs from here (laziness) + new Lazy(eval(rhs)(accEnv)) + else if (stat.symbol.flags.isMutable) + new Var(eval(rhs)(accEnv)) + else + new Eager(eval(rhs)(accEnv)) + + accEnv.updated(stat.symbol, evalRef) + case DefDef(_, _, _, _, _) => + // TODO: record the environment for closure purposes + accEnv + case stat => + eval(stat)(accEnv) + accEnv + }) + eval(expr)(newEnv) + + + case Term.Literal(const) => const.value + + case Term.Typed(expr, _) => eval(expr) + + case _ => throw new MatchError(tree.show) + } + } + + private object Call { + def unapply(arg: Tree): Option[(Tree, List[List[Term]])] = arg match { + case fn@ Term.Select(_, _) => Some((fn, Nil)) + case fn@ Term.Ident(_) => Some((fn, Nil)) + case Term.Apply(Call(fn, args1), args2) => Some((fn, args1 :+ args2)) + case Term.TypeApply(Call(fn, args), _) => Some((fn, args)) + case _ => None + } + } +} \ No newline at end of file diff --git a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala new file mode 100644 index 000000000000..47a7a694f5c1 --- /dev/null +++ b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala @@ -0,0 +1,86 @@ +package scala.tasty.interpreter +package jvm + +import scala.tasty.interpreter.abstr.{Ref, Eager, Lazy, Var} +import scala.tasty.interpreter.jvm.JVMReflection +import scala.tasty.Reflection + +class Interpreter[R <: Reflection & Singleton](reflect0: R) extends TreeInterpreter[R](reflect0) { + import reflect._ + + val jvmReflection = new JVMReflection(reflect) + + def interpretNew(fn: Tree, argss: List[List[Term]])(implicit env: Env): Any = { + if (fn.symbol.isDefinedInCurrentRun) { + // Best effort to try to create a proxy + fn.symbol.owner match { + case IsClassSymbol(sym) => + val parentSymbols = sym.tree.parents.tail.map(_.asInstanceOf[TypeTree].symbol).head + import java.lang.reflect._ + val handler: InvocationHandler = new InvocationHandler() { + val instance = new Object + + def invoke(proxy: Object, method: Method, args: scala.Array[Object]): Object = { + + // println(method) + val symbol = sym.methods.find(_.name == method.getName).get + + if (symbol.isDefinedInCurrentRun) { + symbol match { + case IsDefSymbol(symbol) => + val args1 = if (args == null) Nil else args.toList + val evaluatedArgs = args1.map(arg => new Eager(arg)) + + val env1 = env ++ symbol.tree.paramss.headOption.getOrElse(Nil).map(_.symbol).zip(evaluatedArgs) + // println(symbol.tree) + eval(symbol.tree.rhs.get)(env1).asInstanceOf[Object] + } + } + else + method.invoke(instance, args: _*) + } + + } + val proxyClass: Class[_] = Proxy.getProxyClass(getClass.getClassLoader, jvmReflection.loadClass(parentSymbols.fullName)) + proxyClass.getConstructor(classOf[InvocationHandler]).newInstance(handler); + } + } + else jvmReflection.interpretNew(fn.symbol, evaluatedArgss(argss)) + } + + override def interpretCall(fn: Tree, argss: List[List[Term]])(implicit env: Env): Any = { + if (fn.symbol.isDefinedInCurrentRun) super.interpretCall(fn, argss) + else { + import Term._ + // println(fn.show) + fn.symbol match { + // TODO: obviously + case IsDefSymbol(sym) => + if (sym.name == "") jvmReflection.interpretNew(sym, evaluatedArgss(argss)) + else if (sym.name == "==") eval(Term.IsSelect.unapply(fn).get.qualifier).asInstanceOf[Int] == eval(argss.head.head).asInstanceOf[Int] + else if (sym.name == ">") eval(Term.IsSelect.unapply(fn).get.qualifier).asInstanceOf[Int] > eval(argss.head.head).asInstanceOf[Int] + else if (sym.name == "-") eval(Term.IsSelect.unapply(fn).get.qualifier).asInstanceOf[Int] - eval(argss.head.head).asInstanceOf[Int] + else if (sym.name == "+") eval(Term.IsSelect.unapply(fn).get.qualifier).asInstanceOf[Int] + eval(argss.head.head).asInstanceOf[Int] + else { + val argss2 = evaluatedArgss(argss) + // argss2.foreach(println) + jvmReflection.interpretStaticMethodCall(fn.symbol.owner, fn.symbol, argss2) + } + case _ => + if (fn.symbol.flags.isObject) { + jvmReflection.loadModule(fn.symbol.asVal.moduleClass.get) + } + // call to a static val + else { + jvmReflection.interpretStaticVal(fn.symbol.owner, fn.symbol) + } + } + } + } + + def evaluatedArgss(argss: List[List[Term]])(implicit env: Env): List[Object] = argss.flatMap((a: List[Term]) => a.map(b => eval(b).asInstanceOf[Object])) + + def reflectNew(fn: Tree, argss: List[List[Term]])(implicit env: Env): Any = + jvmReflection.interpretNew(fn.symbol, evaluatedArgss(argss)) + +} \ No newline at end of file From 2fdd5dc9cb05baf4a821e07f7d7135cea924f2da Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 4 Jan 2019 12:02:02 +0100 Subject: [PATCH 19/45] Move logic out of eval --- .../interpreter/TreeInterpreter.scala | 72 ++++++++++--------- .../interpreter/jvm/Interpreter.scala | 3 - 2 files changed, 37 insertions(+), 38 deletions(-) diff --git a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/TreeInterpreter.scala b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/TreeInterpreter.scala index a042d6a522bb..9b2416a2f07d 100644 --- a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/TreeInterpreter.scala +++ b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/TreeInterpreter.scala @@ -22,6 +22,38 @@ abstract class TreeInterpreter[R <: Reflection & Singleton](val reflect: R) { def interpretNew(fn: Tree, argss: List[List[Term]])(implicit env: Env): Any + def interpretIf(cond: Term, thenp: Term, elsep: Term)(implicit env: Env): Any = + if (eval(cond).asInstanceOf[Boolean]) eval(thenp) + else eval(elsep) + + def interpretWhile(cond: Term, body: Term)(implicit env: Env): Any = + while (eval(cond).asInstanceOf[Boolean]) eval(body) + + def interpretBlock(stats: List[Statement], expr: Term)(implicit env: Env): Any = { + val newEnv = stats.foldLeft(env)((accEnv, stat) => stat match { + case ValDef(name, tpt, Some(rhs)) => + val evalRef: Ref = + if (stat.symbol.flags.isLazy) + // do not factor out rhs from here (laziness) + new Lazy(eval(rhs)(accEnv)) + else if (stat.symbol.flags.isMutable) + new Var(eval(rhs)(accEnv)) + else + new Eager(eval(rhs)(accEnv)) + + accEnv.updated(stat.symbol, evalRef) + case DefDef(_, _, _, _, _) => + // TODO: record the environment for closure purposes + accEnv + case stat => + eval(stat)(accEnv) + accEnv + }) + eval(expr)(newEnv) + } + + def interpretLiteral(const: Constant)(implicit env: Env): Any = const.value + def eval(tree: Statement)(implicit env: Env): Any = { tree match { case Call(fn, argss) => @@ -35,41 +67,11 @@ abstract class TreeInterpreter[R <: Reflection & Singleton](val reflect: R) { case varf: Var => varf.update(eval(rhs)) } - case Term.If(cond, thenp, elsep) => - if (eval(cond).asInstanceOf[Boolean]) eval(thenp) - else eval(elsep) - - case Term.While(cond, expr) => - while (eval(cond).asInstanceOf[Boolean]){ - eval(expr) - } - - case Term.Block(stats, expr) => - val newEnv = stats.foldLeft(env)((accEnv, stat) => stat match { - case ValDef(name, tpt, Some(rhs)) => - val evalRef: Ref = - if (stat.symbol.flags.isLazy) - // do not factor out rhs from here (laziness) - new Lazy(eval(rhs)(accEnv)) - else if (stat.symbol.flags.isMutable) - new Var(eval(rhs)(accEnv)) - else - new Eager(eval(rhs)(accEnv)) - - accEnv.updated(stat.symbol, evalRef) - case DefDef(_, _, _, _, _) => - // TODO: record the environment for closure purposes - accEnv - case stat => - eval(stat)(accEnv) - accEnv - }) - eval(expr)(newEnv) - - - case Term.Literal(const) => const.value - - case Term.Typed(expr, _) => eval(expr) + case Term.If(cond, thenp, elsep) => interpretIf(cond, thenp, elsep) + case Term.While(cond, body) => interpretWhile(cond, body) + case Term.Block(stats, expr) => interpretBlock(stats, expr) + case Term.Literal(const) => interpretLiteral(const) + case Term.Typed(expr, _) => eval(expr) case _ => throw new MatchError(tree.show) } diff --git a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala index 47a7a694f5c1..485971dfe9c7 100644 --- a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala +++ b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala @@ -80,7 +80,4 @@ class Interpreter[R <: Reflection & Singleton](reflect0: R) extends TreeInterpre def evaluatedArgss(argss: List[List[Term]])(implicit env: Env): List[Object] = argss.flatMap((a: List[Term]) => a.map(b => eval(b).asInstanceOf[Object])) - def reflectNew(fn: Tree, argss: List[List[Term]])(implicit env: Env): Any = - jvmReflection.interpretNew(fn.symbol, evaluatedArgss(argss)) - } \ No newline at end of file From 5d7f94087bdd222f9fa7a13697a7eaeb89a4ccc8 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 4 Jan 2019 13:53:16 +0100 Subject: [PATCH 20/45] Implement isInstanceOf and asInstanceOf --- .../tasty-interpreter.check | 7 +++++-- .../tasty-interpreter/InterpretedMain.scala | 9 +++++++-- .../interpreter/TreeInterpreter.scala | 17 +++++++++++------ .../interpreter/jvm/Interpreter.scala | 9 +++++++++ .../interpreter/jvm/JVMReflection.scala | 19 ++++++++++++++++--- 5 files changed, 48 insertions(+), 13 deletions(-) diff --git a/tests/run-with-compiler-custom-args/tasty-interpreter.check b/tests/run-with-compiler-custom-args/tasty-interpreter.check index 093c0329e272..0e8955fc5259 100644 --- a/tests/run-with-compiler-custom-args/tasty-interpreter.check +++ b/tests/run-with-compiler-custom-args/tasty-interpreter.check @@ -25,5 +25,8 @@ precompiledModule 59 60 61 -70 -62 \ No newline at end of file +62 +63 +true +false +64 \ No newline at end of file diff --git a/tests/run-with-compiler-custom-args/tasty-interpreter/InterpretedMain.scala b/tests/run-with-compiler-custom-args/tasty-interpreter/InterpretedMain.scala index cdaa228bf9cb..b791c198e00f 100644 --- a/tests/run-with-compiler-custom-args/tasty-interpreter/InterpretedMain.scala +++ b/tests/run-with-compiler-custom-args/tasty-interpreter/InterpretedMain.scala @@ -47,11 +47,16 @@ object IntepretedMain { println(Precompiled.staticMeth4(new Bar)) println(Precompiled.staticMeth5(new Bar, 61)) println(Precompiled.staticMeth4(new InterpretedBar)) - println(Precompiled.staticMeth5(new InterpretedBar, 61)) + println(Precompiled.staticMeth5(new InterpretedBar, 62)) + + val x6: Any = 64 + println(x6.isInstanceOf[Int]) + println(x6.isInstanceOf[Long]) + println(x6.asInstanceOf[Int]) } } class InterpretedBar extends IFace { - def meth(): Int = 70 + def meth(): Int = 62 def methA(x: Int): Int = x + 1 } diff --git a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/TreeInterpreter.scala b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/TreeInterpreter.scala index 9b2416a2f07d..ddba1e0d6b00 100644 --- a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/TreeInterpreter.scala +++ b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/TreeInterpreter.scala @@ -54,11 +54,16 @@ abstract class TreeInterpreter[R <: Reflection & Singleton](val reflect: R) { def interpretLiteral(const: Constant)(implicit env: Env): Any = const.value + def interpretIsInstanceOf(prefix: Term, tpt: TypeTree)(implicit env: Env): Any + def interpretAsInstanceOf(prefix: Term, tpt: TypeTree)(implicit env: Env): Any + def eval(tree: Statement)(implicit env: Env): Any = { tree match { - case Call(fn, argss) => + case Call(fn, targs, argss) => fn match { case Term.Select(_, "") => interpretNew(fn, argss) + case Term.Select(prefix, "isInstanceOf") => interpretIsInstanceOf(prefix, targs.head) + case Term.Select(prefix, "asInstanceOf") => interpretAsInstanceOf(prefix, targs.head) case _ => interpretCall(fn, argss) } @@ -78,11 +83,11 @@ abstract class TreeInterpreter[R <: Reflection & Singleton](val reflect: R) { } private object Call { - def unapply(arg: Tree): Option[(Tree, List[List[Term]])] = arg match { - case fn@ Term.Select(_, _) => Some((fn, Nil)) - case fn@ Term.Ident(_) => Some((fn, Nil)) - case Term.Apply(Call(fn, args1), args2) => Some((fn, args1 :+ args2)) - case Term.TypeApply(Call(fn, args), _) => Some((fn, args)) + def unapply(arg: Tree): Option[(Tree, List[TypeTree], List[List[Term]])] = arg match { + case fn@ Term.Select(_, _) => Some((fn, Nil, Nil)) + case fn@ Term.Ident(_) => Some((fn, Nil, Nil)) + case Term.Apply(Call(fn, targs, args1), args2) => Some((fn, targs, args1 :+ args2)) + case Term.TypeApply(Call(fn, _, _), targs) => Some((fn, targs, Nil)) case _ => None } } diff --git a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala index 485971dfe9c7..9970cd4a9416 100644 --- a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala +++ b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala @@ -80,4 +80,13 @@ class Interpreter[R <: Reflection & Singleton](reflect0: R) extends TreeInterpre def evaluatedArgss(argss: List[List[Term]])(implicit env: Env): List[Object] = argss.flatMap((a: List[Term]) => a.map(b => eval(b).asInstanceOf[Object])) + def interpretIsInstanceOf(prefix: Term, tpt: TypeTree)(implicit env: Env): Any = { + val o = eval(prefix) + jvmReflection.getClassOf(tpt.symbol).isInstance(o) + } + + def interpretAsInstanceOf(prefix: Term, tpt: TypeTree)(implicit env: Env): Any = { + val o = eval(prefix) + jvmReflection.getClassOf(tpt.symbol).cast(o) + } } \ No newline at end of file diff --git a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/JVMReflection.scala b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/JVMReflection.scala index cf45f0804cfb..c336129b28cd 100644 --- a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/JVMReflection.scala +++ b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/JVMReflection.scala @@ -13,7 +13,7 @@ class JVMReflection[R <: Reflection & Singleton](val reflect: R) { def loadModule(sym: Symbol): Object = { if (sym.owner.flags.isPackage) { // is top level object - val moduleClass = loadClass(sym.fullName) + val moduleClass = getClassOf(sym) moduleClass.getField(MODULE_INSTANCE_FIELD).get(null) } else { @@ -24,7 +24,20 @@ class JVMReflection[R <: Reflection & Singleton](val reflect: R) { } } - def loadClass(name: String): Class[_] = { + def getClassOf(sym: Symbol): Class[_] = { + sym.fullName match { + case "scala.Boolean" => classOf[java.lang.Boolean] + case "scala.Short" => classOf[java.lang.Short] + case "scala.Char" => classOf[java.lang.Character] + case "scala.Int" => classOf[java.lang.Integer] + case "scala.Long" => classOf[java.lang.Long] + case "scala.Float" => classOf[java.lang.Float] + case "scala.Double" => classOf[java.lang.Double] + case _ => loadClass(sym.fullName) + } + } + + def loadClass(name: String): Class[_] = { try classLoader.loadClass(name) catch { case _: ClassNotFoundException => @@ -51,7 +64,7 @@ class JVMReflection[R <: Reflection & Singleton](val reflect: R) { } def interpretNew(fn: Symbol, args: List[Object]): Object = { - val clazz = loadClass(fn.owner.fullName) + val clazz = getClassOf(fn.owner) val constr = clazz.getConstructor(paramsSig(fn): _*) constr.newInstance(args: _*).asInstanceOf[Object] } From 7e3b36ee2043010afa371c5fbfd0e9955b58da9b Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 4 Jan 2019 14:08:00 +0100 Subject: [PATCH 21/45] Abstract over interpreted object representation --- .../interpreter/TreeInterpreter.scala | 45 ++++++++++++++----- .../interpreter/jvm/Interpreter.scala | 11 ++++- 2 files changed, 44 insertions(+), 12 deletions(-) diff --git a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/TreeInterpreter.scala b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/TreeInterpreter.scala index ddba1e0d6b00..c303ef170431 100644 --- a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/TreeInterpreter.scala +++ b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/TreeInterpreter.scala @@ -9,7 +9,10 @@ abstract class TreeInterpreter[R <: Reflection & Singleton](val reflect: R) { type Env = Map[Symbol, Ref] - def interpretCall(fn: Tree, argss: List[List[Term]])(implicit env: Env): Any = { + /** Representation of objects and values in the interpreter */ + type AbstractAny + + def interpretCall(fn: Tree, argss: List[List[Term]])(implicit env: Env): AbstractAny = { fn.symbol match { case IsDefSymbol(sym) => val evaluatedArgs = argss.flatten.map(arg => new Eager(eval(arg))) @@ -20,16 +23,18 @@ abstract class TreeInterpreter[R <: Reflection & Singleton](val reflect: R) { } } - def interpretNew(fn: Tree, argss: List[List[Term]])(implicit env: Env): Any + def interpretNew(fn: Tree, argss: List[List[Term]])(implicit env: Env): AbstractAny - def interpretIf(cond: Term, thenp: Term, elsep: Term)(implicit env: Env): Any = + def interpretIf(cond: Term, thenp: Term, elsep: Term)(implicit env: Env): AbstractAny = if (eval(cond).asInstanceOf[Boolean]) eval(thenp) else eval(elsep) - def interpretWhile(cond: Term, body: Term)(implicit env: Env): Any = + def interpretWhile(cond: Term, body: Term)(implicit env: Env): AbstractAny = { while (eval(cond).asInstanceOf[Boolean]) eval(body) + interpretUnit() + } - def interpretBlock(stats: List[Statement], expr: Term)(implicit env: Env): Any = { + def interpretBlock(stats: List[Statement], expr: Term)(implicit env: Env): AbstractAny = { val newEnv = stats.foldLeft(env)((accEnv, stat) => stat match { case ValDef(name, tpt, Some(rhs)) => val evalRef: Ref = @@ -52,12 +57,13 @@ abstract class TreeInterpreter[R <: Reflection & Singleton](val reflect: R) { eval(expr)(newEnv) } - def interpretLiteral(const: Constant)(implicit env: Env): Any = const.value + def interpretUnit()(implicit env: Env): AbstractAny + def interpretLiteral(const: Constant)(implicit env: Env): AbstractAny - def interpretIsInstanceOf(prefix: Term, tpt: TypeTree)(implicit env: Env): Any - def interpretAsInstanceOf(prefix: Term, tpt: TypeTree)(implicit env: Env): Any + def interpretIsInstanceOf(prefix: Term, tpt: TypeTree)(implicit env: Env): AbstractAny + def interpretAsInstanceOf(prefix: Term, tpt: TypeTree)(implicit env: Env): AbstractAny - def eval(tree: Statement)(implicit env: Env): Any = { + def eval(tree: Statement)(implicit env: Env): AbstractAny = { tree match { case Call(fn, targs, argss) => fn match { @@ -69,7 +75,9 @@ abstract class TreeInterpreter[R <: Reflection & Singleton](val reflect: R) { case Term.Assign(lhs, rhs) => env(lhs.symbol) match { - case varf: Var => varf.update(eval(rhs)) + case varf: Var => + varf.update(eval(rhs)) + interpretUnit() } case Term.If(cond, thenp, elsep) => interpretIf(cond, thenp, elsep) @@ -82,6 +90,23 @@ abstract class TreeInterpreter[R <: Reflection & Singleton](val reflect: R) { } } + trait Ref { + def get: AbstractAny + } + + class Eager(val get: AbstractAny) extends Ref + + class Lazy(thunk: => AbstractAny) extends Ref { + lazy val get: AbstractAny = thunk + } + + class Var(private var value: AbstractAny) extends Ref { + def get = value + def update(rhs: AbstractAny): Unit = { + value = rhs + } + } + private object Call { def unapply(arg: Tree): Option[(Tree, List[TypeTree], List[List[Term]])] = arg match { case fn@ Term.Select(_, _) => Some((fn, Nil, Nil)) diff --git a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala index 9970cd4a9416..e26c3609de26 100644 --- a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala +++ b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala @@ -8,6 +8,9 @@ import scala.tasty.Reflection class Interpreter[R <: Reflection & Singleton](reflect0: R) extends TreeInterpreter[R](reflect0) { import reflect._ + // All references are represented by themselfs and values are boxed + type AbstractAny = Any + val jvmReflection = new JVMReflection(reflect) def interpretNew(fn: Tree, argss: List[List[Term]])(implicit env: Env): Any = { @@ -80,12 +83,16 @@ class Interpreter[R <: Reflection & Singleton](reflect0: R) extends TreeInterpre def evaluatedArgss(argss: List[List[Term]])(implicit env: Env): List[Object] = argss.flatMap((a: List[Term]) => a.map(b => eval(b).asInstanceOf[Object])) - def interpretIsInstanceOf(prefix: Term, tpt: TypeTree)(implicit env: Env): Any = { + def interpretUnit()(implicit env: Env): AbstractAny = ().asInstanceOf[Object] + + def interpretLiteral(const: Constant)(implicit env: Env): AbstractAny = const.value + + def interpretIsInstanceOf(prefix: Term, tpt: TypeTree)(implicit env: Env): AbstractAny = { val o = eval(prefix) jvmReflection.getClassOf(tpt.symbol).isInstance(o) } - def interpretAsInstanceOf(prefix: Term, tpt: TypeTree)(implicit env: Env): Any = { + def interpretAsInstanceOf(prefix: Term, tpt: TypeTree)(implicit env: Env): AbstractAny = { val o = eval(prefix) jvmReflection.getClassOf(tpt.symbol).cast(o) } From 4b1c8cdea6f1d40777a2d062b45cc3cd4c746a1b Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 4 Jan 2019 14:11:22 +0100 Subject: [PATCH 22/45] Pass evaluated prefix --- .../tasty-interpreter/interpreter/TreeInterpreter.scala | 8 ++++---- .../tasty-interpreter/interpreter/jvm/Interpreter.scala | 9 +++------ 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/TreeInterpreter.scala b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/TreeInterpreter.scala index c303ef170431..60891cb6d1e7 100644 --- a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/TreeInterpreter.scala +++ b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/TreeInterpreter.scala @@ -60,16 +60,16 @@ abstract class TreeInterpreter[R <: Reflection & Singleton](val reflect: R) { def interpretUnit()(implicit env: Env): AbstractAny def interpretLiteral(const: Constant)(implicit env: Env): AbstractAny - def interpretIsInstanceOf(prefix: Term, tpt: TypeTree)(implicit env: Env): AbstractAny - def interpretAsInstanceOf(prefix: Term, tpt: TypeTree)(implicit env: Env): AbstractAny + def interpretIsInstanceOf(o: AbstractAny, tpt: TypeTree)(implicit env: Env): AbstractAny + def interpretAsInstanceOf(o: AbstractAny, tpt: TypeTree)(implicit env: Env): AbstractAny def eval(tree: Statement)(implicit env: Env): AbstractAny = { tree match { case Call(fn, targs, argss) => fn match { case Term.Select(_, "") => interpretNew(fn, argss) - case Term.Select(prefix, "isInstanceOf") => interpretIsInstanceOf(prefix, targs.head) - case Term.Select(prefix, "asInstanceOf") => interpretAsInstanceOf(prefix, targs.head) + case Term.Select(prefix, "isInstanceOf") => interpretIsInstanceOf(eval(prefix), targs.head) + case Term.Select(prefix, "asInstanceOf") => interpretAsInstanceOf(eval(prefix), targs.head) case _ => interpretCall(fn, argss) } diff --git a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala index e26c3609de26..0a814697a4c2 100644 --- a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala +++ b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala @@ -87,13 +87,10 @@ class Interpreter[R <: Reflection & Singleton](reflect0: R) extends TreeInterpre def interpretLiteral(const: Constant)(implicit env: Env): AbstractAny = const.value - def interpretIsInstanceOf(prefix: Term, tpt: TypeTree)(implicit env: Env): AbstractAny = { - val o = eval(prefix) + def interpretIsInstanceOf(o: AbstractAny, tpt: TypeTree)(implicit env: Env): AbstractAny = jvmReflection.getClassOf(tpt.symbol).isInstance(o) - } - def interpretAsInstanceOf(prefix: Term, tpt: TypeTree)(implicit env: Env): AbstractAny = { - val o = eval(prefix) + def interpretAsInstanceOf(o: AbstractAny, tpt: TypeTree)(implicit env: Env): AbstractAny = jvmReflection.getClassOf(tpt.symbol).cast(o) - } + } \ No newline at end of file From e7e39f1ae1dbeec9eca2fd3e4f98e9a4185476b3 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 4 Jan 2019 14:35:04 +0100 Subject: [PATCH 23/45] Refactor Ref (now LocalValue) --- .../interpreter/TreeInterpreter.scala | 53 +++++++++---------- .../interpreter/abstr/Eager.scala | 3 -- .../interpreter/abstr/Lazy.scala | 5 -- .../interpreter/abstr/Ref.scala | 5 -- .../interpreter/abstr/Var.scala | 9 ---- .../interpreter/jvm/Interpreter.scala | 5 +- 6 files changed, 27 insertions(+), 53 deletions(-) delete mode 100644 tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/abstr/Eager.scala delete mode 100644 tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/abstr/Lazy.scala delete mode 100644 tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/abstr/Ref.scala delete mode 100644 tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/abstr/Var.scala diff --git a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/TreeInterpreter.scala b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/TreeInterpreter.scala index 60891cb6d1e7..822a9568c05c 100644 --- a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/TreeInterpreter.scala +++ b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/TreeInterpreter.scala @@ -1,13 +1,12 @@ package scala.tasty.interpreter -import scala.tasty.interpreter.abstr.{Ref, Eager, Lazy, Var} import scala.tasty.interpreter.jvm.JVMReflection import scala.tasty.Reflection abstract class TreeInterpreter[R <: Reflection & Singleton](val reflect: R) { import reflect._ - type Env = Map[Symbol, Ref] + type Env = Map[Symbol, LocalValue] /** Representation of objects and values in the interpreter */ type AbstractAny @@ -15,7 +14,7 @@ abstract class TreeInterpreter[R <: Reflection & Singleton](val reflect: R) { def interpretCall(fn: Tree, argss: List[List[Term]])(implicit env: Env): AbstractAny = { fn.symbol match { case IsDefSymbol(sym) => - val evaluatedArgs = argss.flatten.map(arg => new Eager(eval(arg))) + val evaluatedArgs = argss.flatten.map(arg => LocalValue.valFrom(eval(arg))) val env1 = env ++ sym.tree.paramss.headOption.getOrElse(Nil).map(_.symbol).zip(evaluatedArgs) eval(sym.tree.rhs.get)(env1) case _ => @@ -37,14 +36,11 @@ abstract class TreeInterpreter[R <: Reflection & Singleton](val reflect: R) { def interpretBlock(stats: List[Statement], expr: Term)(implicit env: Env): AbstractAny = { val newEnv = stats.foldLeft(env)((accEnv, stat) => stat match { case ValDef(name, tpt, Some(rhs)) => - val evalRef: Ref = - if (stat.symbol.flags.isLazy) - // do not factor out rhs from here (laziness) - new Lazy(eval(rhs)(accEnv)) - else if (stat.symbol.flags.isMutable) - new Var(eval(rhs)(accEnv)) - else - new Eager(eval(rhs)(accEnv)) + def evalRhs = eval(rhs)(accEnv) + val evalRef: LocalValue = + if (stat.symbol.flags.isLazy) LocalValue.lazyValFrom(evalRhs) + else if (stat.symbol.flags.isMutable) LocalValue.varFrom(evalRhs) + else LocalValue.valFrom(evalRhs) accEnv.updated(stat.symbol, evalRef) case DefDef(_, _, _, _, _) => @@ -57,7 +53,7 @@ abstract class TreeInterpreter[R <: Reflection & Singleton](val reflect: R) { eval(expr)(newEnv) } - def interpretUnit()(implicit env: Env): AbstractAny + def interpretUnit(): AbstractAny def interpretLiteral(const: Constant)(implicit env: Env): AbstractAny def interpretIsInstanceOf(o: AbstractAny, tpt: TypeTree)(implicit env: Env): AbstractAny @@ -74,11 +70,7 @@ abstract class TreeInterpreter[R <: Reflection & Singleton](val reflect: R) { } case Term.Assign(lhs, rhs) => - env(lhs.symbol) match { - case varf: Var => - varf.update(eval(rhs)) - interpretUnit() - } + env(lhs.symbol).update(eval(rhs)) case Term.If(cond, thenp, elsep) => interpretIf(cond, thenp, elsep) case Term.While(cond, body) => interpretWhile(cond, body) @@ -90,20 +82,25 @@ abstract class TreeInterpreter[R <: Reflection & Singleton](val reflect: R) { } } - trait Ref { + trait LocalValue { def get: AbstractAny + def update(rhs: AbstractAny): AbstractAny = throw new UnsupportedOperationException } - class Eager(val get: AbstractAny) extends Ref - - class Lazy(thunk: => AbstractAny) extends Ref { - lazy val get: AbstractAny = thunk - } - - class Var(private var value: AbstractAny) extends Ref { - def get = value - def update(rhs: AbstractAny): Unit = { - value = rhs + object LocalValue { + def valFrom(rhs: AbstractAny): LocalValue = new LocalValue { + def get: AbstractAny = rhs + } + def lazyValFrom(rhs: => AbstractAny): LocalValue = new LocalValue { + lazy val get: AbstractAny = rhs + } + def varFrom(rhs: AbstractAny): LocalValue = new LocalValue { + private[this] var value = rhs + def get = value + override def update(rhs: AbstractAny): AbstractAny = { + value = rhs + interpretUnit() + } } } diff --git a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/abstr/Eager.scala b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/abstr/Eager.scala deleted file mode 100644 index 00271f50659f..000000000000 --- a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/abstr/Eager.scala +++ /dev/null @@ -1,3 +0,0 @@ -package scala.tasty.interpreter.abstr - -class Eager(val get: Any) extends Ref diff --git a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/abstr/Lazy.scala b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/abstr/Lazy.scala deleted file mode 100644 index 65ae72841f88..000000000000 --- a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/abstr/Lazy.scala +++ /dev/null @@ -1,5 +0,0 @@ -package scala.tasty.interpreter.abstr - -class Lazy(thunk: => Any) extends Ref { - lazy val get: Any = thunk -} diff --git a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/abstr/Ref.scala b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/abstr/Ref.scala deleted file mode 100644 index d06fcc6d1968..000000000000 --- a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/abstr/Ref.scala +++ /dev/null @@ -1,5 +0,0 @@ -package scala.tasty.interpreter.abstr - -trait Ref { - def get: Any -} diff --git a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/abstr/Var.scala b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/abstr/Var.scala deleted file mode 100644 index 59bee1382369..000000000000 --- a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/abstr/Var.scala +++ /dev/null @@ -1,9 +0,0 @@ -package scala.tasty.interpreter.abstr - -class Var(private var value: Any) extends Ref { - def get = value - - def update(rhs: Any): Unit = { - value = rhs - } -} diff --git a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala index 0a814697a4c2..c965b1401012 100644 --- a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala +++ b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala @@ -1,7 +1,6 @@ package scala.tasty.interpreter package jvm -import scala.tasty.interpreter.abstr.{Ref, Eager, Lazy, Var} import scala.tasty.interpreter.jvm.JVMReflection import scala.tasty.Reflection @@ -32,7 +31,7 @@ class Interpreter[R <: Reflection & Singleton](reflect0: R) extends TreeInterpre symbol match { case IsDefSymbol(symbol) => val args1 = if (args == null) Nil else args.toList - val evaluatedArgs = args1.map(arg => new Eager(arg)) + val evaluatedArgs = args1.map(arg => LocalValue.valFrom(arg)) val env1 = env ++ symbol.tree.paramss.headOption.getOrElse(Nil).map(_.symbol).zip(evaluatedArgs) // println(symbol.tree) @@ -83,7 +82,7 @@ class Interpreter[R <: Reflection & Singleton](reflect0: R) extends TreeInterpre def evaluatedArgss(argss: List[List[Term]])(implicit env: Env): List[Object] = argss.flatMap((a: List[Term]) => a.map(b => eval(b).asInstanceOf[Object])) - def interpretUnit()(implicit env: Env): AbstractAny = ().asInstanceOf[Object] + def interpretUnit(): AbstractAny = ().asInstanceOf[Object] def interpretLiteral(const: Constant)(implicit env: Env): AbstractAny = const.value From 61de0fa9d477050e4916a64f2b06cc8622713471 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 4 Jan 2019 14:41:05 +0100 Subject: [PATCH 24/45] Remove deadcode --- .../tasty-interpreter/interpreter/jvm/Interpreter.scala | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala index c965b1401012..75c91849c771 100644 --- a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala +++ b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala @@ -58,8 +58,7 @@ class Interpreter[R <: Reflection & Singleton](reflect0: R) extends TreeInterpre fn.symbol match { // TODO: obviously case IsDefSymbol(sym) => - if (sym.name == "") jvmReflection.interpretNew(sym, evaluatedArgss(argss)) - else if (sym.name == "==") eval(Term.IsSelect.unapply(fn).get.qualifier).asInstanceOf[Int] == eval(argss.head.head).asInstanceOf[Int] + if (sym.name == "==") eval(Term.IsSelect.unapply(fn).get.qualifier).asInstanceOf[Int] == eval(argss.head.head).asInstanceOf[Int] else if (sym.name == ">") eval(Term.IsSelect.unapply(fn).get.qualifier).asInstanceOf[Int] > eval(argss.head.head).asInstanceOf[Int] else if (sym.name == "-") eval(Term.IsSelect.unapply(fn).get.qualifier).asInstanceOf[Int] - eval(argss.head.head).asInstanceOf[Int] else if (sym.name == "+") eval(Term.IsSelect.unapply(fn).get.qualifier).asInstanceOf[Int] + eval(argss.head.head).asInstanceOf[Int] From 4c5b39de35544a735b37f6d640ea9462267fa6ac Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 4 Jan 2019 14:58:25 +0100 Subject: [PATCH 25/45] Abstract over == --- .../tasty-interpreter/interpreter/TreeInterpreter.scala | 3 +++ .../tasty-interpreter/interpreter/jvm/Interpreter.scala | 5 +++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/TreeInterpreter.scala b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/TreeInterpreter.scala index 822a9568c05c..09d75e216544 100644 --- a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/TreeInterpreter.scala +++ b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/TreeInterpreter.scala @@ -59,6 +59,8 @@ abstract class TreeInterpreter[R <: Reflection & Singleton](val reflect: R) { def interpretIsInstanceOf(o: AbstractAny, tpt: TypeTree)(implicit env: Env): AbstractAny def interpretAsInstanceOf(o: AbstractAny, tpt: TypeTree)(implicit env: Env): AbstractAny + def interpretEqEq(x: AbstractAny, y: AbstractAny): AbstractAny + def eval(tree: Statement)(implicit env: Env): AbstractAny = { tree match { case Call(fn, targs, argss) => @@ -66,6 +68,7 @@ abstract class TreeInterpreter[R <: Reflection & Singleton](val reflect: R) { case Term.Select(_, "") => interpretNew(fn, argss) case Term.Select(prefix, "isInstanceOf") => interpretIsInstanceOf(eval(prefix), targs.head) case Term.Select(prefix, "asInstanceOf") => interpretAsInstanceOf(eval(prefix), targs.head) + case Term.Select(prefix, "==") => interpretEqEq(eval(prefix), eval(argss.head.head)) case _ => interpretCall(fn, argss) } diff --git a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala index 75c91849c771..74e578b77fa4 100644 --- a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala +++ b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala @@ -58,8 +58,7 @@ class Interpreter[R <: Reflection & Singleton](reflect0: R) extends TreeInterpre fn.symbol match { // TODO: obviously case IsDefSymbol(sym) => - if (sym.name == "==") eval(Term.IsSelect.unapply(fn).get.qualifier).asInstanceOf[Int] == eval(argss.head.head).asInstanceOf[Int] - else if (sym.name == ">") eval(Term.IsSelect.unapply(fn).get.qualifier).asInstanceOf[Int] > eval(argss.head.head).asInstanceOf[Int] + if (sym.name == ">") eval(Term.IsSelect.unapply(fn).get.qualifier).asInstanceOf[Int] > eval(argss.head.head).asInstanceOf[Int] else if (sym.name == "-") eval(Term.IsSelect.unapply(fn).get.qualifier).asInstanceOf[Int] - eval(argss.head.head).asInstanceOf[Int] else if (sym.name == "+") eval(Term.IsSelect.unapply(fn).get.qualifier).asInstanceOf[Int] + eval(argss.head.head).asInstanceOf[Int] else { @@ -91,4 +90,6 @@ class Interpreter[R <: Reflection & Singleton](reflect0: R) extends TreeInterpre def interpretAsInstanceOf(o: AbstractAny, tpt: TypeTree)(implicit env: Env): AbstractAny = jvmReflection.getClassOf(tpt.symbol).cast(o) + def interpretEqEq(x: AbstractAny, y: AbstractAny): AbstractAny = x == y + } \ No newline at end of file From f64013de512ef80fbeceb7d7ca712ff762e7fb8c Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 4 Jan 2019 15:37:42 +0100 Subject: [PATCH 26/45] Abstract over primitive numeric operations --- .../interpreter/TreeInterpreter.scala | 26 +++++++++ .../interpreter/jvm/Interpreter.scala | 53 ++++++++++++++++--- 2 files changed, 71 insertions(+), 8 deletions(-) diff --git a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/TreeInterpreter.scala b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/TreeInterpreter.scala index 09d75e216544..03bcd7234960 100644 --- a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/TreeInterpreter.scala +++ b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/TreeInterpreter.scala @@ -61,7 +61,13 @@ abstract class TreeInterpreter[R <: Reflection & Singleton](val reflect: R) { def interpretEqEq(x: AbstractAny, y: AbstractAny): AbstractAny + def interpretPrivitiveLt(x: AbstractAny, y: AbstractAny): AbstractAny + def interpretPrivitiveGt(x: AbstractAny, y: AbstractAny): AbstractAny + def interpretPrivitivePlus(x: AbstractAny, y: AbstractAny): AbstractAny + def interpretPrivitiveMinus(x: AbstractAny, y: AbstractAny): AbstractAny + def eval(tree: Statement)(implicit env: Env): AbstractAny = { + tree match { case Call(fn, targs, argss) => fn match { @@ -69,6 +75,15 @@ abstract class TreeInterpreter[R <: Reflection & Singleton](val reflect: R) { case Term.Select(prefix, "isInstanceOf") => interpretIsInstanceOf(eval(prefix), targs.head) case Term.Select(prefix, "asInstanceOf") => interpretAsInstanceOf(eval(prefix), targs.head) case Term.Select(prefix, "==") => interpretEqEq(eval(prefix), eval(argss.head.head)) + case Term.Select(prefix, name) if isNumericPrimitive(prefix.tpe) => + val lhs = eval(prefix) + val rhs = eval(argss.head.head) + name match { + case "+" => interpretPrivitivePlus(lhs, rhs) + case "-" => interpretPrivitiveMinus(lhs, rhs) + case "<" => interpretPrivitiveLt(lhs, rhs) + case ">" => interpretPrivitiveGt(lhs, rhs) + } case _ => interpretCall(fn, argss) } @@ -107,6 +122,17 @@ abstract class TreeInterpreter[R <: Reflection & Singleton](val reflect: R) { } } + private def isNumericPrimitive(tpe: Type): Boolean = { + // TODO: improve + tpe <:< definitions.ByteType || + tpe <:< definitions.CharType || + tpe <:< definitions.ShortType || + tpe <:< definitions.IntType || + tpe <:< definitions.LongType || + tpe <:< definitions.FloatType || + tpe <:< definitions.DoubleType + } + private object Call { def unapply(arg: Tree): Option[(Tree, List[TypeTree], List[List[Term]])] = arg match { case fn@ Term.Select(_, _) => Some((fn, Nil, Nil)) diff --git a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala index 74e578b77fa4..7710cead0c8c 100644 --- a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala +++ b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala @@ -58,14 +58,9 @@ class Interpreter[R <: Reflection & Singleton](reflect0: R) extends TreeInterpre fn.symbol match { // TODO: obviously case IsDefSymbol(sym) => - if (sym.name == ">") eval(Term.IsSelect.unapply(fn).get.qualifier).asInstanceOf[Int] > eval(argss.head.head).asInstanceOf[Int] - else if (sym.name == "-") eval(Term.IsSelect.unapply(fn).get.qualifier).asInstanceOf[Int] - eval(argss.head.head).asInstanceOf[Int] - else if (sym.name == "+") eval(Term.IsSelect.unapply(fn).get.qualifier).asInstanceOf[Int] + eval(argss.head.head).asInstanceOf[Int] - else { - val argss2 = evaluatedArgss(argss) - // argss2.foreach(println) - jvmReflection.interpretStaticMethodCall(fn.symbol.owner, fn.symbol, argss2) - } + val argss2 = evaluatedArgss(argss) + // argss2.foreach(println) + jvmReflection.interpretStaticMethodCall(fn.symbol.owner, fn.symbol, argss2) case _ => if (fn.symbol.flags.isObject) { jvmReflection.loadModule(fn.symbol.asVal.moduleClass.get) @@ -92,4 +87,46 @@ class Interpreter[R <: Reflection & Singleton](reflect0: R) extends TreeInterpre def interpretEqEq(x: AbstractAny, y: AbstractAny): AbstractAny = x == y + def interpretPrivitiveLt(x: AbstractAny, y: AbstractAny): AbstractAny = coerce(x, y)(_.lt(_, _)) + def interpretPrivitiveGt(x: AbstractAny, y: AbstractAny): AbstractAny = coerce(x, y)(_.gt(_, _)) + def interpretPrivitivePlus(x: AbstractAny, y: AbstractAny): AbstractAny = coerce(x, y)(_.plus(_, _)) + def interpretPrivitiveMinus(x: AbstractAny, y: AbstractAny): AbstractAny = coerce(x, y)(_.minus(_, _)) + + private def coerce(x: AbstractAny, y: AbstractAny)(body: (Numeric[AbstractAny], AbstractAny, AbstractAny) => AbstractAny): AbstractAny = { + def getNumericFor[T](implicit x: Numeric[T]): Numeric[AbstractAny] = + x.asInstanceOf[Numeric[Any]] + // TODO complete: Float Double Char + x match { + case x: Byte => + y match { + case y: Byte => body(getNumericFor[Byte], x, y) + case y: Short => body(getNumericFor[Short], x.toShort, y) + case y: Int => body(getNumericFor[Int], x.toInt, y) + case y: Long => body(getNumericFor[Long], x.toLong, y) + } + case x: Short => + y match { + case y: Byte => body(getNumericFor[Short], x, y.toShort) + case y: Short => body(getNumericFor[Short], x, y) + case y: Int => body(getNumericFor[Int], x.toInt, y) + case y: Long => body(getNumericFor[Long], x.toLong, y) + } + case x: Int => + y match { + case y: Byte => body(getNumericFor[Int], x, y.toInt) + case y: Short => body(getNumericFor[Int], x, y.toInt) + case y: Int => body(getNumericFor[Int], x, y) + case y: Long => body(getNumericFor[Long], x.toLong, y) + } + case x: Long => + y match { + case y: Byte => body(getNumericFor[Long], x, y.toLong) + case y: Short => body(getNumericFor[Long], x, y.toLong) + case y: Int => body(getNumericFor[Long], x, y.toLong) + case y: Long => body(getNumericFor[Long], x, y) + } + } + } + + } \ No newline at end of file From 5b30f9d5f54a48011888c13a1eaa6b56ba453d19 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 4 Jan 2019 16:06:33 +0100 Subject: [PATCH 27/45] Implement primitive divition --- .../interpreter/TreeInterpreter.scala | 30 +++++-- .../interpreter/jvm/Interpreter.scala | 81 +++++++++++++------ 2 files changed, 81 insertions(+), 30 deletions(-) diff --git a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/TreeInterpreter.scala b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/TreeInterpreter.scala index 03bcd7234960..fd6f16a7e6f6 100644 --- a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/TreeInterpreter.scala +++ b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/TreeInterpreter.scala @@ -65,6 +65,10 @@ abstract class TreeInterpreter[R <: Reflection & Singleton](val reflect: R) { def interpretPrivitiveGt(x: AbstractAny, y: AbstractAny): AbstractAny def interpretPrivitivePlus(x: AbstractAny, y: AbstractAny): AbstractAny def interpretPrivitiveMinus(x: AbstractAny, y: AbstractAny): AbstractAny + def interpretPrivitiveTimes(x: AbstractAny, y: AbstractAny): AbstractAny + def interpretPrivitiveDiv(x: AbstractAny, y: AbstractAny): AbstractAny + def interpretPrivitiveQuot(x: AbstractAny, y: AbstractAny): AbstractAny + def interpretPrivitiveRem(x: AbstractAny, y: AbstractAny): AbstractAny def eval(tree: Statement)(implicit env: Env): AbstractAny = { @@ -75,15 +79,25 @@ abstract class TreeInterpreter[R <: Reflection & Singleton](val reflect: R) { case Term.Select(prefix, "isInstanceOf") => interpretIsInstanceOf(eval(prefix), targs.head) case Term.Select(prefix, "asInstanceOf") => interpretAsInstanceOf(eval(prefix), targs.head) case Term.Select(prefix, "==") => interpretEqEq(eval(prefix), eval(argss.head.head)) - case Term.Select(prefix, name) if isNumericPrimitive(prefix.tpe) => + case Term.Select(prefix, name @ ("+" | "-" | "<" | ">" | "*" | "/" | "%")) if isNumericPrimitive(prefix.tpe) => val lhs = eval(prefix) val rhs = eval(argss.head.head) name match { case "+" => interpretPrivitivePlus(lhs, rhs) case "-" => interpretPrivitiveMinus(lhs, rhs) + case "*" => interpretPrivitiveTimes(lhs, rhs) case "<" => interpretPrivitiveLt(lhs, rhs) case ">" => interpretPrivitiveGt(lhs, rhs) } + case Term.Select(prefix, name @ ("/" | "%")) if isIntegralPrimitive(prefix.tpe) => + val lhs = eval(prefix) + val rhs = eval(argss.head.head) + if (name == "/") interpretPrivitiveQuot(lhs, rhs) + else interpretPrivitiveRem(lhs, rhs) + case Term.Select(prefix, name @ "/") if isFractionalPrimitive(prefix.tpe) => + val lhs = eval(prefix) + val rhs = eval(argss.head.head) + interpretPrivitiveDiv(lhs, rhs) case _ => interpretCall(fn, argss) } @@ -122,17 +136,21 @@ abstract class TreeInterpreter[R <: Reflection & Singleton](val reflect: R) { } } - private def isNumericPrimitive(tpe: Type): Boolean = { - // TODO: improve + private def isNumericPrimitive(tpe: Type): Boolean = + isIntegralPrimitive(tpe) || isFractionalPrimitive(tpe) + + private def isIntegralPrimitive(tpe: Type): Boolean = { tpe <:< definitions.ByteType || tpe <:< definitions.CharType || tpe <:< definitions.ShortType || tpe <:< definitions.IntType || - tpe <:< definitions.LongType || - tpe <:< definitions.FloatType || - tpe <:< definitions.DoubleType + tpe <:< definitions.LongType } + private def isFractionalPrimitive(tpe: Type): Boolean = + tpe <:< definitions.FloatType || tpe <:< definitions.DoubleType + + private object Call { def unapply(arg: Tree): Option[(Tree, List[TypeTree], List[List[Term]])] = arg match { case fn@ Term.Select(_, _) => Some((fn, Nil, Nil)) diff --git a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala index 7710cead0c8c..25524e8f2fcc 100644 --- a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala +++ b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala @@ -87,46 +87,79 @@ class Interpreter[R <: Reflection & Singleton](reflect0: R) extends TreeInterpre def interpretEqEq(x: AbstractAny, y: AbstractAny): AbstractAny = x == y - def interpretPrivitiveLt(x: AbstractAny, y: AbstractAny): AbstractAny = coerce(x, y)(_.lt(_, _)) - def interpretPrivitiveGt(x: AbstractAny, y: AbstractAny): AbstractAny = coerce(x, y)(_.gt(_, _)) - def interpretPrivitivePlus(x: AbstractAny, y: AbstractAny): AbstractAny = coerce(x, y)(_.plus(_, _)) - def interpretPrivitiveMinus(x: AbstractAny, y: AbstractAny): AbstractAny = coerce(x, y)(_.minus(_, _)) - - private def coerce(x: AbstractAny, y: AbstractAny)(body: (Numeric[AbstractAny], AbstractAny, AbstractAny) => AbstractAny): AbstractAny = { - def getNumericFor[T](implicit x: Numeric[T]): Numeric[AbstractAny] = - x.asInstanceOf[Numeric[Any]] + def interpretPrivitiveLt(x: AbstractAny, y: AbstractAny): AbstractAny = withNumeric(x, y)(_.lt(_, _)) + def interpretPrivitiveGt(x: AbstractAny, y: AbstractAny): AbstractAny = withNumeric(x, y)(_.gt(_, _)) + def interpretPrivitivePlus(x: AbstractAny, y: AbstractAny): AbstractAny = withNumeric(x, y)(_.plus(_, _)) + def interpretPrivitiveMinus(x: AbstractAny, y: AbstractAny): AbstractAny = withNumeric(x, y)(_.minus(_, _)) + def interpretPrivitiveTimes(x: AbstractAny, y: AbstractAny): AbstractAny = withNumeric(x, y)(_.times(_, _)) + def interpretPrivitiveDiv(x: AbstractAny, y: AbstractAny): AbstractAny = withFractional(x, y)(_.div(_, _)) + def interpretPrivitiveQuot(x: AbstractAny, y: AbstractAny): AbstractAny = withIntegral(x, y)(_.quot(_, _)) + def interpretPrivitiveRem(x: AbstractAny, y: AbstractAny): AbstractAny = withIntegral(x, y)(_.rem(_, _)) + + private def coerce(x: AbstractAny, y: AbstractAny): (AbstractAny, AbstractAny) = { // TODO complete: Float Double Char x match { case x: Byte => y match { - case y: Byte => body(getNumericFor[Byte], x, y) - case y: Short => body(getNumericFor[Short], x.toShort, y) - case y: Int => body(getNumericFor[Int], x.toInt, y) - case y: Long => body(getNumericFor[Long], x.toLong, y) + case y: Byte => (x, y) + case y: Short => (x.toShort, y) + case y: Int => (x.toInt, y) + case y: Long => (x.toLong, y) } case x: Short => y match { - case y: Byte => body(getNumericFor[Short], x, y.toShort) - case y: Short => body(getNumericFor[Short], x, y) - case y: Int => body(getNumericFor[Int], x.toInt, y) - case y: Long => body(getNumericFor[Long], x.toLong, y) + case y: Byte => (x, y.toShort) + case y: Short => (x, y) + case y: Int => (x.toInt, y) + case y: Long => (x.toLong, y) } case x: Int => y match { - case y: Byte => body(getNumericFor[Int], x, y.toInt) - case y: Short => body(getNumericFor[Int], x, y.toInt) - case y: Int => body(getNumericFor[Int], x, y) - case y: Long => body(getNumericFor[Long], x.toLong, y) + case y: Byte => (x, y.toInt) + case y: Short => (x, y.toInt) + case y: Int => (x, y) + case y: Long => (x.toLong, y) } case x: Long => y match { - case y: Byte => body(getNumericFor[Long], x, y.toLong) - case y: Short => body(getNumericFor[Long], x, y.toLong) - case y: Int => body(getNumericFor[Long], x, y.toLong) - case y: Long => body(getNumericFor[Long], x, y) + case y: Byte => (x, y.toLong) + case y: Short => (x, y.toLong) + case y: Int => (x, y.toLong) + case y: Long => (x, y) } } } + def withNumeric[T](x: AbstractAny, y: AbstractAny)(body: (Numeric[AbstractAny], AbstractAny, AbstractAny) => AbstractAny): AbstractAny = { + val (coX, coY) = coerce(x, y) + def getNumericFor[T](implicit x: Numeric[T]): Numeric[AbstractAny] = + x.asInstanceOf[Numeric[AbstractAny]] + coX match { + case _: Int => body(getNumericFor[Int], coX, coY) + case _: Long => body(getNumericFor[Long], coX, coY) + } + } + + def withIntegral[T](x: AbstractAny, y: AbstractAny)(body: (Integral[AbstractAny], AbstractAny, AbstractAny) => AbstractAny): AbstractAny = { + val (coX, coY) = coerce(x, y) + def getIntegralFor[T](implicit x: Integral[T]): Integral[AbstractAny] = + x.asInstanceOf[Integral[AbstractAny]] + coX match { + case _: Byte => body(getIntegralFor[Byte], coX, coY) + case _: Short => body(getIntegralFor[Short], coX, coY) + case _: Int => body(getIntegralFor[Int], coX, coY) + case _: Long => body(getIntegralFor[Long], coX, coY) + } + } + + def withFractional[T](x: AbstractAny, y: AbstractAny)(body: (Fractional[AbstractAny], AbstractAny, AbstractAny) => AbstractAny): AbstractAny = { + val (coX, coY) = coerce(x, y) + def getFractionalFor[T](implicit x: Fractional[T]): Fractional[AbstractAny] = + x.asInstanceOf[Fractional[AbstractAny]] + coX match { + case _: Float => body(getFractionalFor[Float], coX, coY) + case _: Double => body(getFractionalFor[Double], coX, coY) + } + } } \ No newline at end of file From 93ac8cd09e2c12ee0101e19522faa535fefbf040 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 4 Jan 2019 16:21:13 +0100 Subject: [PATCH 28/45] Add <= and => --- .../tasty-interpreter/interpreter/TreeInterpreter.scala | 6 +++++- .../tasty-interpreter/interpreter/jvm/Interpreter.scala | 2 ++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/TreeInterpreter.scala b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/TreeInterpreter.scala index fd6f16a7e6f6..9a791a2ea6ab 100644 --- a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/TreeInterpreter.scala +++ b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/TreeInterpreter.scala @@ -63,6 +63,8 @@ abstract class TreeInterpreter[R <: Reflection & Singleton](val reflect: R) { def interpretPrivitiveLt(x: AbstractAny, y: AbstractAny): AbstractAny def interpretPrivitiveGt(x: AbstractAny, y: AbstractAny): AbstractAny + def interpretPrivitiveLtEq(x: AbstractAny, y: AbstractAny): AbstractAny + def interpretPrivitiveGtEq(x: AbstractAny, y: AbstractAny): AbstractAny def interpretPrivitivePlus(x: AbstractAny, y: AbstractAny): AbstractAny def interpretPrivitiveMinus(x: AbstractAny, y: AbstractAny): AbstractAny def interpretPrivitiveTimes(x: AbstractAny, y: AbstractAny): AbstractAny @@ -79,7 +81,7 @@ abstract class TreeInterpreter[R <: Reflection & Singleton](val reflect: R) { case Term.Select(prefix, "isInstanceOf") => interpretIsInstanceOf(eval(prefix), targs.head) case Term.Select(prefix, "asInstanceOf") => interpretAsInstanceOf(eval(prefix), targs.head) case Term.Select(prefix, "==") => interpretEqEq(eval(prefix), eval(argss.head.head)) - case Term.Select(prefix, name @ ("+" | "-" | "<" | ">" | "*" | "/" | "%")) if isNumericPrimitive(prefix.tpe) => + case Term.Select(prefix, name @ ("+" | "-" | "*" | "<" | ">" | "<=" | "=>")) if isNumericPrimitive(prefix.tpe) => val lhs = eval(prefix) val rhs = eval(argss.head.head) name match { @@ -88,6 +90,8 @@ abstract class TreeInterpreter[R <: Reflection & Singleton](val reflect: R) { case "*" => interpretPrivitiveTimes(lhs, rhs) case "<" => interpretPrivitiveLt(lhs, rhs) case ">" => interpretPrivitiveGt(lhs, rhs) + case "<=" => interpretPrivitiveLtEq(lhs, rhs) + case ">=" => interpretPrivitiveGtEq(lhs, rhs) } case Term.Select(prefix, name @ ("/" | "%")) if isIntegralPrimitive(prefix.tpe) => val lhs = eval(prefix) diff --git a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala index 25524e8f2fcc..81eef38f03e1 100644 --- a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala +++ b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala @@ -89,6 +89,8 @@ class Interpreter[R <: Reflection & Singleton](reflect0: R) extends TreeInterpre def interpretPrivitiveLt(x: AbstractAny, y: AbstractAny): AbstractAny = withNumeric(x, y)(_.lt(_, _)) def interpretPrivitiveGt(x: AbstractAny, y: AbstractAny): AbstractAny = withNumeric(x, y)(_.gt(_, _)) + def interpretPrivitiveLtEq(x: AbstractAny, y: AbstractAny): AbstractAny = withNumeric(x, y)(_.lteq(_, _)) + def interpretPrivitiveGtEq(x: AbstractAny, y: AbstractAny): AbstractAny = withNumeric(x, y)(_.gteq(_, _)) def interpretPrivitivePlus(x: AbstractAny, y: AbstractAny): AbstractAny = withNumeric(x, y)(_.plus(_, _)) def interpretPrivitiveMinus(x: AbstractAny, y: AbstractAny): AbstractAny = withNumeric(x, y)(_.minus(_, _)) def interpretPrivitiveTimes(x: AbstractAny, y: AbstractAny): AbstractAny = withNumeric(x, y)(_.times(_, _)) From 73584acb956c78554ce9e256013d7b33938f7c5a Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 8 Jan 2019 13:18:06 +0100 Subject: [PATCH 29/45] Use custom output check as compileDir does use the checkfile --- .../tasty-interpreter.check | 32 ------------ .../tasty-interpreter/Test.scala | 52 ++++++++++++++++++- 2 files changed, 51 insertions(+), 33 deletions(-) delete mode 100644 tests/run-with-compiler-custom-args/tasty-interpreter.check diff --git a/tests/run-with-compiler-custom-args/tasty-interpreter.check b/tests/run-with-compiler-custom-args/tasty-interpreter.check deleted file mode 100644 index 0e8955fc5259..000000000000 --- a/tests/run-with-compiler-custom-args/tasty-interpreter.check +++ /dev/null @@ -1,32 +0,0 @@ -42 - -Hello - -42 - -43 - -if - -5 -4 -3 -2 -1 - -42 - -55 -precompiledModule -55 -56 -57 -58 -59 -60 -61 -62 -63 -true -false -64 \ No newline at end of file diff --git a/tests/run-with-compiler-custom-args/tasty-interpreter/Test.scala b/tests/run-with-compiler-custom-args/tasty-interpreter/Test.scala index 767415e377f7..d1bdab07580e 100644 --- a/tests/run-with-compiler-custom-args/tasty-interpreter/Test.scala +++ b/tests/run-with-compiler-custom-args/tasty-interpreter/Test.scala @@ -1,10 +1,60 @@ +import java.io.{ByteArrayOutputStream, PrintStream} + +import dotty.tools.dotc.util.DiffUtil + +import scala.io.Source import scala.tasty.file._ import scala.tasty.interpreter.jvm.Interpreter import scala.tasty.Reflection object Test { def main(args: Array[String]): Unit = { - ConsumeTasty("", List("IntepretedMain", "InterpretedBar"), new TastyInterpreter) + val ps = new ByteArrayOutputStream() + scala.Console.withOut(ps) { + ConsumeTasty("", List("IntepretedMain", "InterpretedBar"), new TastyInterpreter) + } + val expectedOutput = + """42 + | + |Hello + | + |42 + | + |43 + | + |if + | + |5 + |4 + |3 + |2 + |1 + | + |42 + | + |55 + |precompiledModule + |55 + |56 + |57 + |58 + |59 + |60 + |61 + |62 + |63 + |true + |false + |64 + |""".stripMargin + + val actualOutput = ps.toString + + assert(expectedOutput == actualOutput, + "\n>>>>>>>>>>>>>>>>>>\n" + + DiffUtil.mkColoredCodeDiff(actualOutput, expectedOutput, true) + + "<<<<<<<<<<<<<<<<<<" + ) } } From 74ffc6e16d6da86649f88b7aa12acca60b09b2b9 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 8 Jan 2019 13:19:16 +0100 Subject: [PATCH 30/45] Remove code added by mistake --- .../test/dotty/tools/dotc/BootstrappedOnlyCompilationTests.scala | 1 - 1 file changed, 1 deletion(-) diff --git a/compiler/test/dotty/tools/dotc/BootstrappedOnlyCompilationTests.scala b/compiler/test/dotty/tools/dotc/BootstrappedOnlyCompilationTests.scala index 7c9859a3f5ea..4785c67f4751 100644 --- a/compiler/test/dotty/tools/dotc/BootstrappedOnlyCompilationTests.scala +++ b/compiler/test/dotty/tools/dotc/BootstrappedOnlyCompilationTests.scala @@ -49,7 +49,6 @@ class BootstrappedOnlyCompilationTests extends ParallelTesting { @Test def posTwiceWithCompiler: Unit = { implicit val testGroup: TestGroup = TestGroup("posTwiceWithCompiler") compileFile("tests/pos-with-compiler/Labels.scala", withCompilerOptions) + - compileFile("tests/pos-with-compiler/Labels.scala", withCompilerOptions) + compileFile("tests/pos-with-compiler/Patterns.scala", withCompilerOptions) + compileList( "testNonCyclic", From 467d9af3384bd4df7d4464d25a80001cdbdde0a1 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 8 Jan 2019 13:44:49 +0100 Subject: [PATCH 31/45] Add eval log --- .../tasty-interpreter/Test.scala | 6 +- .../interpreter/TreeInterpreter.scala | 59 +++++++++++-------- 2 files changed, 37 insertions(+), 28 deletions(-) diff --git a/tests/run-with-compiler-custom-args/tasty-interpreter/Test.scala b/tests/run-with-compiler-custom-args/tasty-interpreter/Test.scala index d1bdab07580e..77e438d3f136 100644 --- a/tests/run-with-compiler-custom-args/tasty-interpreter/Test.scala +++ b/tests/run-with-compiler-custom-args/tasty-interpreter/Test.scala @@ -10,8 +10,10 @@ import scala.tasty.Reflection object Test { def main(args: Array[String]): Unit = { val ps = new ByteArrayOutputStream() - scala.Console.withOut(ps) { - ConsumeTasty("", List("IntepretedMain", "InterpretedBar"), new TastyInterpreter) + try scala.Console.withOut(ps) { + ConsumeTasty("", List("IntepretedMain", "InterpretedBar"), new TastyInterpreter) + } catch { + case e: Throwable => throw new Exception(ps.toString, e) } val expectedOutput = """42 diff --git a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/TreeInterpreter.scala b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/TreeInterpreter.scala index 9a791a2ea6ab..fe6d7955bc0d 100644 --- a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/TreeInterpreter.scala +++ b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/TreeInterpreter.scala @@ -6,6 +6,8 @@ import scala.tasty.Reflection abstract class TreeInterpreter[R <: Reflection & Singleton](val reflect: R) { import reflect._ + final val LOG = false + type Env = Map[Symbol, LocalValue] /** Representation of objects and values in the interpreter */ @@ -73,51 +75,56 @@ abstract class TreeInterpreter[R <: Reflection & Singleton](val reflect: R) { def interpretPrivitiveRem(x: AbstractAny, y: AbstractAny): AbstractAny def eval(tree: Statement)(implicit env: Env): AbstractAny = { - tree match { case Call(fn, targs, argss) => fn match { - case Term.Select(_, "") => interpretNew(fn, argss) - case Term.Select(prefix, "isInstanceOf") => interpretIsInstanceOf(eval(prefix), targs.head) - case Term.Select(prefix, "asInstanceOf") => interpretAsInstanceOf(eval(prefix), targs.head) - case Term.Select(prefix, "==") => interpretEqEq(eval(prefix), eval(argss.head.head)) + case Term.Select(_, "") => log("interpretNew", tree)(interpretNew(fn, argss)) + case Term.Select(prefix, "isInstanceOf") => log("interpretIsInstanceOf", tree)(interpretIsInstanceOf(eval(prefix), targs.head)) + case Term.Select(prefix, "asInstanceOf") => log("interpretAsInstanceOf", tree)(interpretAsInstanceOf(eval(prefix), targs.head)) + case Term.Select(prefix, "==") => log("interpretEqEq", tree)(interpretEqEq(eval(prefix), eval(argss.head.head))) case Term.Select(prefix, name @ ("+" | "-" | "*" | "<" | ">" | "<=" | "=>")) if isNumericPrimitive(prefix.tpe) => val lhs = eval(prefix) val rhs = eval(argss.head.head) name match { - case "+" => interpretPrivitivePlus(lhs, rhs) - case "-" => interpretPrivitiveMinus(lhs, rhs) - case "*" => interpretPrivitiveTimes(lhs, rhs) - case "<" => interpretPrivitiveLt(lhs, rhs) - case ">" => interpretPrivitiveGt(lhs, rhs) - case "<=" => interpretPrivitiveLtEq(lhs, rhs) - case ">=" => interpretPrivitiveGtEq(lhs, rhs) + case "+" => log("interpretPrivitivePlus", tree)(interpretPrivitivePlus(lhs, rhs)) + case "-" => log("interpretPrivitiveMinus", tree)(interpretPrivitiveMinus(lhs, rhs)) + case "*" => log("interpretPrivitiveTimes", tree)(interpretPrivitiveTimes(lhs, rhs)) + case "<" => log("interpretPrivitiveLt", tree)(interpretPrivitiveLt(lhs, rhs)) + case ">" => log("interpretPrivitiveGt", tree)(interpretPrivitiveGt(lhs, rhs)) + case "<=" => log("interpretPrivitiveLtEq", tree)(interpretPrivitiveLtEq(lhs, rhs)) + case ">=" => log("interpretPrivitiveGtEq", tree)(interpretPrivitiveGtEq(lhs, rhs)) } case Term.Select(prefix, name @ ("/" | "%")) if isIntegralPrimitive(prefix.tpe) => - val lhs = eval(prefix) - val rhs = eval(argss.head.head) - if (name == "/") interpretPrivitiveQuot(lhs, rhs) - else interpretPrivitiveRem(lhs, rhs) + def lhs = eval(prefix) + def rhs = eval(argss.head.head) + if (name == "/") log("interpretPrivitiveQuot", tree)(interpretPrivitiveQuot(lhs, rhs)) + else log("interpretPrivitiveRem", tree)(interpretPrivitiveRem(lhs, rhs)) case Term.Select(prefix, name @ "/") if isFractionalPrimitive(prefix.tpe) => - val lhs = eval(prefix) - val rhs = eval(argss.head.head) - interpretPrivitiveDiv(lhs, rhs) - case _ => interpretCall(fn, argss) + def lhs = eval(prefix) + def rhs = eval(argss.head.head) + log("interpretPrivitiveDiv", tree)(interpretPrivitiveDiv(lhs, rhs)) + case _ => log("interpretCall", tree)(interpretCall(fn, argss)) } case Term.Assign(lhs, rhs) => - env(lhs.symbol).update(eval(rhs)) + log("", tree)(env(lhs.symbol).update(eval(rhs))) - case Term.If(cond, thenp, elsep) => interpretIf(cond, thenp, elsep) - case Term.While(cond, body) => interpretWhile(cond, body) - case Term.Block(stats, expr) => interpretBlock(stats, expr) - case Term.Literal(const) => interpretLiteral(const) - case Term.Typed(expr, _) => eval(expr) + case Term.If(cond, thenp, elsep) => log("interpretIf", tree)(interpretIf(cond, thenp, elsep)) + case Term.While(cond, body) => log("interpretWhile", tree)(interpretWhile(cond, body)) + case Term.Block(stats, expr) => log("interpretBlock", tree)(interpretBlock(stats, expr)) + case Term.Literal(const) => log("interpretLiteral", tree)(interpretLiteral(const)) + case Term.Typed(expr, _) => log("", tree)(eval(expr)) case _ => throw new MatchError(tree.show) } } + inline def log[T](tag: => String, tree: => Statement)(thunk: => T): T = { + if (LOG) + println(s"#> $tag: ${tree.show}") + thunk + } + trait LocalValue { def get: AbstractAny def update(rhs: AbstractAny): AbstractAny = throw new UnsupportedOperationException From 0cfaefd4d4fb24ac9790151246b664b832545740 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 8 Jan 2019 14:12:24 +0100 Subject: [PATCH 32/45] Interpret method calls (not only static ones) --- .../tasty-interpreter/InterpretedMain.scala | 11 +++++ .../tasty-interpreter/Test.scala | 4 ++ .../interpreter/TreeInterpreter.scala | 18 +++++--- .../interpreter/jvm/Interpreter.scala | 41 ++++++++++++------- .../interpreter/jvm/JVMReflection.scala | 10 +++-- 5 files changed, 60 insertions(+), 24 deletions(-) diff --git a/tests/run-with-compiler-custom-args/tasty-interpreter/InterpretedMain.scala b/tests/run-with-compiler-custom-args/tasty-interpreter/InterpretedMain.scala index b791c198e00f..62e870a1b8ad 100644 --- a/tests/run-with-compiler-custom-args/tasty-interpreter/InterpretedMain.scala +++ b/tests/run-with-compiler-custom-args/tasty-interpreter/InterpretedMain.scala @@ -53,7 +53,18 @@ object IntepretedMain { println(x6.isInstanceOf[Int]) println(x6.isInstanceOf[Long]) println(x6.asInstanceOf[Int]) + + + val bar = new Bar + println(bar.meth() + 5) + println(bar.methA(66)) + + val ibar = new InterpretedBar + println(ibar.meth() + 5) + println(ibar.methA(67)) } + + def foo(x: Int): Unit = println(x) } class InterpretedBar extends IFace { diff --git a/tests/run-with-compiler-custom-args/tasty-interpreter/Test.scala b/tests/run-with-compiler-custom-args/tasty-interpreter/Test.scala index 77e438d3f136..24ad2f42a98f 100644 --- a/tests/run-with-compiler-custom-args/tasty-interpreter/Test.scala +++ b/tests/run-with-compiler-custom-args/tasty-interpreter/Test.scala @@ -48,6 +48,10 @@ object Test { |true |false |64 + |65 + |66 + |67 + |68 |""".stripMargin val actualOutput = ps.toString diff --git a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/TreeInterpreter.scala b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/TreeInterpreter.scala index fe6d7955bc0d..656594b417ca 100644 --- a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/TreeInterpreter.scala +++ b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/TreeInterpreter.scala @@ -13,14 +13,20 @@ abstract class TreeInterpreter[R <: Reflection & Singleton](val reflect: R) { /** Representation of objects and values in the interpreter */ type AbstractAny - def interpretCall(fn: Tree, argss: List[List[Term]])(implicit env: Env): AbstractAny = { + def interpretCall(fn: Term, argss: List[List[Term]])(implicit env: Env): AbstractAny = { + val env0 = fn match { + case Term.Select(prefix, _) => + val pre = eval(prefix) + env // FIXME add pre to the env as `this` + case _ => env + } fn.symbol match { case IsDefSymbol(sym) => val evaluatedArgs = argss.flatten.map(arg => LocalValue.valFrom(eval(arg))) - val env1 = env ++ sym.tree.paramss.headOption.getOrElse(Nil).map(_.symbol).zip(evaluatedArgs) + val env1 = env0 ++ sym.tree.paramss.headOption.getOrElse(Nil).map(_.symbol).zip(evaluatedArgs) eval(sym.tree.rhs.get)(env1) case _ => - env(fn.symbol).get + env0(fn.symbol).get } } @@ -163,9 +169,9 @@ abstract class TreeInterpreter[R <: Reflection & Singleton](val reflect: R) { private object Call { - def unapply(arg: Tree): Option[(Tree, List[TypeTree], List[List[Term]])] = arg match { - case fn@ Term.Select(_, _) => Some((fn, Nil, Nil)) - case fn@ Term.Ident(_) => Some((fn, Nil, Nil)) + def unapply(arg: Tree): Option[(Term, List[TypeTree], List[List[Term]])] = arg match { + case Term.IsSelect(fn) => Some((fn, Nil, Nil)) + case Term.IsIdent(fn) => Some((fn, Nil, Nil)) case Term.Apply(Call(fn, targs, args1), args2) => Some((fn, targs, args1 :+ args2)) case Term.TypeApply(Call(fn, _, _), targs) => Some((fn, targs, Nil)) case _ => None diff --git a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala index 81eef38f03e1..6b88ab1c4af2 100644 --- a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala +++ b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala @@ -50,24 +50,37 @@ class Interpreter[R <: Reflection & Singleton](reflect0: R) extends TreeInterpre else jvmReflection.interpretNew(fn.symbol, evaluatedArgss(argss)) } - override def interpretCall(fn: Tree, argss: List[List[Term]])(implicit env: Env): Any = { + override def interpretCall(fn: Term, argss: List[List[Term]])(implicit env: Env): Any = { if (fn.symbol.isDefinedInCurrentRun) super.interpretCall(fn, argss) else { import Term._ - // println(fn.show) - fn.symbol match { - // TODO: obviously - case IsDefSymbol(sym) => - val argss2 = evaluatedArgss(argss) - // argss2.foreach(println) - jvmReflection.interpretStaticMethodCall(fn.symbol.owner, fn.symbol, argss2) - case _ => - if (fn.symbol.flags.isObject) { - jvmReflection.loadModule(fn.symbol.asVal.moduleClass.get) + fn match { + case Select(prefix, _) => + fn.symbol match { + case IsDefSymbol(sym) => + val pre = eval(prefix).asInstanceOf[Object] + val argss2 = evaluatedArgss(argss) + jvmReflection.interpretMethodCall(pre, fn.symbol, argss2) + case _ => + // TODO not necesarly static? + jvmReflection.interpretStaticVal(fn.symbol.owner, fn.symbol) } - // call to a static val - else { - jvmReflection.interpretStaticVal(fn.symbol.owner, fn.symbol) + case _ => + // println(fn.show) + fn.symbol match { + // TODO: obviously + case IsDefSymbol(sym) => + val argss2 = evaluatedArgss(argss) + // argss2.foreach(println) + jvmReflection.interpretStaticMethodCall(fn.symbol.owner, fn.symbol, argss2) + case _ => + if (fn.symbol.flags.isObject) { + jvmReflection.loadModule(fn.symbol.asVal.moduleClass.get) + } + // call to a static val + else { + jvmReflection.interpretStaticVal(fn.symbol.owner, fn.symbol) + } } } } diff --git a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/JVMReflection.scala b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/JVMReflection.scala index c336129b28cd..92ed6188d1b5 100644 --- a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/JVMReflection.scala +++ b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/JVMReflection.scala @@ -54,12 +54,14 @@ class JVMReflection[R <: Reflection & Singleton](val reflect: R) { } def interpretStaticMethodCall(moduleClass: Symbol, fn: Symbol, args: List[Object]): Object = { + // TODO can we use interpretMethodCall instead? val instance = loadModule(moduleClass) val method = getMethod(instance.getClass, fn.name, paramsSig(fn)) - // println(">>>>>>") - // println(moduleClass) - // println(fn) - // println(method) + method.invoke(instance, args: _*) + } + + def interpretMethodCall(instance: Object, fn: Symbol, args: List[Object]): Object = { + val method = getMethod(instance.getClass, fn.name, paramsSig(fn)) method.invoke(instance, args: _*) } From 0cf9d10fa5d670fe787e4d1d4ab3a27f2ea262d4 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 8 Jan 2019 14:35:41 +0100 Subject: [PATCH 33/45] Add log to proxy calls --- .../tasty-interpreter/interpreter/jvm/Interpreter.scala | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala index 6b88ab1c4af2..72ce582857f9 100644 --- a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala +++ b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala @@ -23,6 +23,10 @@ class Interpreter[R <: Reflection & Singleton](reflect0: R) extends TreeInterpre val instance = new Object def invoke(proxy: Object, method: Method, args: scala.Array[Object]): Object = { + if (LOG) { + val proxyString = if (method.getName == "toString") method.invoke(instance) else proxy.toString + println(s"%> proxy call `$method` on `$proxyString` with args=${if (args == null) Nil else args.toList}") + } // println(method) val symbol = sym.methods.find(_.name == method.getName).get From 3f57db50281a9a30ee247e20c92ac314a3432b32 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 8 Jan 2019 14:38:08 +0100 Subject: [PATCH 34/45] Cleanup calls to Object proxy methods --- .../tasty-interpreter/interpreter/jvm/Interpreter.scala | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala index 72ce582857f9..201c728c0829 100644 --- a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala +++ b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala @@ -20,11 +20,10 @@ class Interpreter[R <: Reflection & Singleton](reflect0: R) extends TreeInterpre val parentSymbols = sym.tree.parents.tail.map(_.asInstanceOf[TypeTree].symbol).head import java.lang.reflect._ val handler: InvocationHandler = new InvocationHandler() { - val instance = new Object def invoke(proxy: Object, method: Method, args: scala.Array[Object]): Object = { if (LOG) { - val proxyString = if (method.getName == "toString") method.invoke(instance) else proxy.toString + val proxyString = if (method.getName == "toString") method.invoke(this) else proxy.toString println(s"%> proxy call `$method` on `$proxyString` with args=${if (args == null) Nil else args.toList}") } @@ -42,8 +41,10 @@ class Interpreter[R <: Reflection & Singleton](reflect0: R) extends TreeInterpre eval(symbol.tree.rhs.get)(env1).asInstanceOf[Object] } } - else - method.invoke(instance, args: _*) + else { + assert(method.getClass == classOf[Object]) + method.invoke(this, args: _*) + } } } From b7b101cd42498d32df1d48d20f9942afde219d1f Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 8 Jan 2019 15:12:29 +0100 Subject: [PATCH 35/45] Use implicit function types --- .../interpreter/TreeInterpreter.scala | 28 ++++++++++--------- .../interpreter/jvm/Interpreter.scala | 12 ++++---- 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/TreeInterpreter.scala b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/TreeInterpreter.scala index 656594b417ca..89853fd4b998 100644 --- a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/TreeInterpreter.scala +++ b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/TreeInterpreter.scala @@ -13,12 +13,14 @@ abstract class TreeInterpreter[R <: Reflection & Singleton](val reflect: R) { /** Representation of objects and values in the interpreter */ type AbstractAny - def interpretCall(fn: Term, argss: List[List[Term]])(implicit env: Env): AbstractAny = { + type Result = implicit Env => AbstractAny + + def interpretCall(fn: Term, argss: List[List[Term]]): Result = { val env0 = fn match { case Term.Select(prefix, _) => val pre = eval(prefix) - env // FIXME add pre to the env as `this` - case _ => env + implicitly[Env] // FIXME add pre to the env as `this` + case _ => implicitly[Env] } fn.symbol match { case IsDefSymbol(sym) => @@ -30,19 +32,19 @@ abstract class TreeInterpreter[R <: Reflection & Singleton](val reflect: R) { } } - def interpretNew(fn: Tree, argss: List[List[Term]])(implicit env: Env): AbstractAny + def interpretNew(fn: Tree, argss: List[List[Term]]): Result - def interpretIf(cond: Term, thenp: Term, elsep: Term)(implicit env: Env): AbstractAny = + def interpretIf(cond: Term, thenp: Term, elsep: Term): Result = if (eval(cond).asInstanceOf[Boolean]) eval(thenp) else eval(elsep) - def interpretWhile(cond: Term, body: Term)(implicit env: Env): AbstractAny = { + def interpretWhile(cond: Term, body: Term): Result = { while (eval(cond).asInstanceOf[Boolean]) eval(body) interpretUnit() } - def interpretBlock(stats: List[Statement], expr: Term)(implicit env: Env): AbstractAny = { - val newEnv = stats.foldLeft(env)((accEnv, stat) => stat match { + def interpretBlock(stats: List[Statement], expr: Term): Result = { + val newEnv = stats.foldLeft(implicitly[Env])((accEnv, stat) => stat match { case ValDef(name, tpt, Some(rhs)) => def evalRhs = eval(rhs)(accEnv) val evalRef: LocalValue = @@ -62,10 +64,10 @@ abstract class TreeInterpreter[R <: Reflection & Singleton](val reflect: R) { } def interpretUnit(): AbstractAny - def interpretLiteral(const: Constant)(implicit env: Env): AbstractAny + def interpretLiteral(const: Constant): Result - def interpretIsInstanceOf(o: AbstractAny, tpt: TypeTree)(implicit env: Env): AbstractAny - def interpretAsInstanceOf(o: AbstractAny, tpt: TypeTree)(implicit env: Env): AbstractAny + def interpretIsInstanceOf(o: AbstractAny, tpt: TypeTree): Result + def interpretAsInstanceOf(o: AbstractAny, tpt: TypeTree): Result def interpretEqEq(x: AbstractAny, y: AbstractAny): AbstractAny @@ -80,7 +82,7 @@ abstract class TreeInterpreter[R <: Reflection & Singleton](val reflect: R) { def interpretPrivitiveQuot(x: AbstractAny, y: AbstractAny): AbstractAny def interpretPrivitiveRem(x: AbstractAny, y: AbstractAny): AbstractAny - def eval(tree: Statement)(implicit env: Env): AbstractAny = { + def eval(tree: Statement): Result = { tree match { case Call(fn, targs, argss) => fn match { @@ -113,7 +115,7 @@ abstract class TreeInterpreter[R <: Reflection & Singleton](val reflect: R) { } case Term.Assign(lhs, rhs) => - log("", tree)(env(lhs.symbol).update(eval(rhs))) + log("", tree)(implicitly[Env].apply(lhs.symbol).update(eval(rhs))) case Term.If(cond, thenp, elsep) => log("interpretIf", tree)(interpretIf(cond, thenp, elsep)) case Term.While(cond, body) => log("interpretWhile", tree)(interpretWhile(cond, body)) diff --git a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala index 201c728c0829..04dd84d00448 100644 --- a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala +++ b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala @@ -12,7 +12,7 @@ class Interpreter[R <: Reflection & Singleton](reflect0: R) extends TreeInterpre val jvmReflection = new JVMReflection(reflect) - def interpretNew(fn: Tree, argss: List[List[Term]])(implicit env: Env): Any = { + def interpretNew(fn: Tree, argss: List[List[Term]]): Result = { if (fn.symbol.isDefinedInCurrentRun) { // Best effort to try to create a proxy fn.symbol.owner match { @@ -36,7 +36,7 @@ class Interpreter[R <: Reflection & Singleton](reflect0: R) extends TreeInterpre val args1 = if (args == null) Nil else args.toList val evaluatedArgs = args1.map(arg => LocalValue.valFrom(arg)) - val env1 = env ++ symbol.tree.paramss.headOption.getOrElse(Nil).map(_.symbol).zip(evaluatedArgs) + val env1 = implicitly[Env] ++ symbol.tree.paramss.headOption.getOrElse(Nil).map(_.symbol).zip(evaluatedArgs) // println(symbol.tree) eval(symbol.tree.rhs.get)(env1).asInstanceOf[Object] } @@ -55,7 +55,7 @@ class Interpreter[R <: Reflection & Singleton](reflect0: R) extends TreeInterpre else jvmReflection.interpretNew(fn.symbol, evaluatedArgss(argss)) } - override def interpretCall(fn: Term, argss: List[List[Term]])(implicit env: Env): Any = { + override def interpretCall(fn: Term, argss: List[List[Term]]): Result = { if (fn.symbol.isDefinedInCurrentRun) super.interpretCall(fn, argss) else { import Term._ @@ -95,12 +95,12 @@ class Interpreter[R <: Reflection & Singleton](reflect0: R) extends TreeInterpre def interpretUnit(): AbstractAny = ().asInstanceOf[Object] - def interpretLiteral(const: Constant)(implicit env: Env): AbstractAny = const.value + def interpretLiteral(const: Constant): Result = const.value - def interpretIsInstanceOf(o: AbstractAny, tpt: TypeTree)(implicit env: Env): AbstractAny = + def interpretIsInstanceOf(o: AbstractAny, tpt: TypeTree): Result = jvmReflection.getClassOf(tpt.symbol).isInstance(o) - def interpretAsInstanceOf(o: AbstractAny, tpt: TypeTree)(implicit env: Env): AbstractAny = + def interpretAsInstanceOf(o: AbstractAny, tpt: TypeTree): Result = jvmReflection.getClassOf(tpt.symbol).cast(o) def interpretEqEq(x: AbstractAny, y: AbstractAny): AbstractAny = x == y From fbc2ca285e0da202f0423e9df8a216e46b538f1b Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 8 Jan 2019 15:28:07 +0100 Subject: [PATCH 36/45] Create enviroment abtractions --- .../interpreter/TreeInterpreter.scala | 18 ++++++++++++++---- .../interpreter/jvm/Interpreter.scala | 8 ++++---- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/TreeInterpreter.scala b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/TreeInterpreter.scala index 89853fd4b998..1f88637e10e9 100644 --- a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/TreeInterpreter.scala +++ b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/TreeInterpreter.scala @@ -15,6 +15,14 @@ abstract class TreeInterpreter[R <: Reflection & Singleton](val reflect: R) { type Result = implicit Env => AbstractAny + def localValue(sym: Symbol)(implicit env: Env): LocalValue = env(sym) + + def withLocalValue[T](sym: Symbol, value: LocalValue)(in: implicit Env => T)(implicit env: Env): T = + in(env.updated(sym, value)) + + def withLocalValues[T](syms: List[Symbol], values: List[LocalValue])(in: implicit Env => T)(implicit env: Env): T = + in(env ++ syms.zip(values)) + def interpretCall(fn: Term, argss: List[List[Term]]): Result = { val env0 = fn match { case Term.Select(prefix, _) => @@ -25,10 +33,12 @@ abstract class TreeInterpreter[R <: Reflection & Singleton](val reflect: R) { fn.symbol match { case IsDefSymbol(sym) => val evaluatedArgs = argss.flatten.map(arg => LocalValue.valFrom(eval(arg))) - val env1 = env0 ++ sym.tree.paramss.headOption.getOrElse(Nil).map(_.symbol).zip(evaluatedArgs) - eval(sym.tree.rhs.get)(env1) + val syms = sym.tree.paramss.headOption.getOrElse(Nil).map(_.symbol) + withLocalValues(syms, evaluatedArgs) { + eval(sym.tree.rhs.get) + } case _ => - env0(fn.symbol).get + localValue(fn.symbol)(env0).get } } @@ -115,7 +125,7 @@ abstract class TreeInterpreter[R <: Reflection & Singleton](val reflect: R) { } case Term.Assign(lhs, rhs) => - log("", tree)(implicitly[Env].apply(lhs.symbol).update(eval(rhs))) + log("", tree)(localValue(lhs.symbol).update(eval(rhs))) case Term.If(cond, thenp, elsep) => log("interpretIf", tree)(interpretIf(cond, thenp, elsep)) case Term.While(cond, body) => log("interpretWhile", tree)(interpretWhile(cond, body)) diff --git a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala index 04dd84d00448..44c9fd94d41f 100644 --- a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala +++ b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala @@ -35,10 +35,10 @@ class Interpreter[R <: Reflection & Singleton](reflect0: R) extends TreeInterpre case IsDefSymbol(symbol) => val args1 = if (args == null) Nil else args.toList val evaluatedArgs = args1.map(arg => LocalValue.valFrom(arg)) - - val env1 = implicitly[Env] ++ symbol.tree.paramss.headOption.getOrElse(Nil).map(_.symbol).zip(evaluatedArgs) - // println(symbol.tree) - eval(symbol.tree.rhs.get)(env1).asInstanceOf[Object] + val syms = symbol.tree.paramss.headOption.getOrElse(Nil).map(_.symbol) + withLocalValues(syms, evaluatedArgs) { + eval(symbol.tree.rhs.get).asInstanceOf[Object] + } } } else { From e4d80929ce1724bbb8c46dc33303f13ecb0b362b Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 8 Jan 2019 15:44:44 +0100 Subject: [PATCH 37/45] Create interpretValGet --- .../interpreter/TreeInterpreter.scala | 26 ++++++---- .../interpreter/jvm/Interpreter.scala | 49 +++++++++---------- 2 files changed, 40 insertions(+), 35 deletions(-) diff --git a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/TreeInterpreter.scala b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/TreeInterpreter.scala index 1f88637e10e9..5c4e448f26a4 100644 --- a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/TreeInterpreter.scala +++ b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/TreeInterpreter.scala @@ -30,18 +30,17 @@ abstract class TreeInterpreter[R <: Reflection & Singleton](val reflect: R) { implicitly[Env] // FIXME add pre to the env as `this` case _ => implicitly[Env] } - fn.symbol match { - case IsDefSymbol(sym) => - val evaluatedArgs = argss.flatten.map(arg => LocalValue.valFrom(eval(arg))) - val syms = sym.tree.paramss.headOption.getOrElse(Nil).map(_.symbol) - withLocalValues(syms, evaluatedArgs) { - eval(sym.tree.rhs.get) - } - case _ => - localValue(fn.symbol)(env0).get + val evaluatedArgs = argss.flatten.map(arg => LocalValue.valFrom(eval(arg))) + val IsDefSymbol(sym) = fn.symbol + val syms = sym.tree.paramss.headOption.getOrElse(Nil).map(_.symbol) + withLocalValues(syms, evaluatedArgs) { + eval(sym.tree.rhs.get) } } + def interpretValGet(fn: Term): Result = + localValue(fn.symbol).get + def interpretNew(fn: Tree, argss: List[List[Term]]): Result def interpretIf(cond: Term, thenp: Term, elsep: Term): Result = @@ -121,7 +120,14 @@ abstract class TreeInterpreter[R <: Reflection & Singleton](val reflect: R) { def lhs = eval(prefix) def rhs = eval(argss.head.head) log("interpretPrivitiveDiv", tree)(interpretPrivitiveDiv(lhs, rhs)) - case _ => log("interpretCall", tree)(interpretCall(fn, argss)) + case _ => + fn.symbol match { + case IsDefSymbol(sym) => log("interpretCall", tree)(interpretCall(fn, argss)) + case _ => + assert(argss.isEmpty) + log("interpretValGet", tree)(interpretValGet(fn)) + + } } case Term.Assign(lhs, rhs) => diff --git a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala index 44c9fd94d41f..d400c4031f46 100644 --- a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala +++ b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala @@ -31,6 +31,7 @@ class Interpreter[R <: Reflection & Singleton](reflect0: R) extends TreeInterpre val symbol = sym.methods.find(_.name == method.getName).get if (symbol.isDefinedInCurrentRun) { + // TODO use `super.interpretCall(...)` symbol match { case IsDefSymbol(symbol) => val args1 = if (args == null) Nil else args.toList @@ -57,36 +58,34 @@ class Interpreter[R <: Reflection & Singleton](reflect0: R) extends TreeInterpre override def interpretCall(fn: Term, argss: List[List[Term]]): Result = { if (fn.symbol.isDefinedInCurrentRun) super.interpretCall(fn, argss) + else { + fn match { + case Term.Select(prefix, _) => + val IsDefSymbol(sym) = fn.symbol + val pre = eval(prefix).asInstanceOf[Object] + val argss2 = evaluatedArgss(argss) + jvmReflection.interpretMethodCall(pre, fn.symbol, argss2) + case _ => + val IsDefSymbol(sym) = fn.symbol + val argss2 = evaluatedArgss(argss) + jvmReflection.interpretStaticMethodCall(fn.symbol.owner, fn.symbol, argss2) + } + } + } + + override def interpretValGet(fn: Term): Result = { + if (fn.symbol.isDefinedInCurrentRun) super.interpretValGet(fn) else { import Term._ fn match { case Select(prefix, _) => - fn.symbol match { - case IsDefSymbol(sym) => - val pre = eval(prefix).asInstanceOf[Object] - val argss2 = evaluatedArgss(argss) - jvmReflection.interpretMethodCall(pre, fn.symbol, argss2) - case _ => - // TODO not necesarly static? - jvmReflection.interpretStaticVal(fn.symbol.owner, fn.symbol) - } + // FIXME not necesarly static + jvmReflection.interpretStaticVal(fn.symbol.owner, fn.symbol) case _ => - // println(fn.show) - fn.symbol match { - // TODO: obviously - case IsDefSymbol(sym) => - val argss2 = evaluatedArgss(argss) - // argss2.foreach(println) - jvmReflection.interpretStaticMethodCall(fn.symbol.owner, fn.symbol, argss2) - case _ => - if (fn.symbol.flags.isObject) { - jvmReflection.loadModule(fn.symbol.asVal.moduleClass.get) - } - // call to a static val - else { - jvmReflection.interpretStaticVal(fn.symbol.owner, fn.symbol) - } - } + if (fn.symbol.flags.isObject) + jvmReflection.loadModule(fn.symbol.asVal.moduleClass.get) + else + jvmReflection.interpretStaticVal(fn.symbol.owner, fn.symbol) } } } From b06850ae2ff1ea031037ab57d53bbfb3b857074a Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 8 Jan 2019 16:24:12 +0100 Subject: [PATCH 38/45] Reuse interpretCall in proxies --- .../interpreter/TreeInterpreter.scala | 20 ++++++++++++++----- .../interpreter/jvm/Interpreter.scala | 9 ++------- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/TreeInterpreter.scala b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/TreeInterpreter.scala index 5c4e448f26a4..a4b5a3524096 100644 --- a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/TreeInterpreter.scala +++ b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/TreeInterpreter.scala @@ -23,12 +23,22 @@ abstract class TreeInterpreter[R <: Reflection & Singleton](val reflect: R) { def withLocalValues[T](syms: List[Symbol], values: List[LocalValue])(in: implicit Env => T)(implicit env: Env): T = in(env ++ syms.zip(values)) + def interpretCall(instance: AbstractAny, sym: DefSymbol, args: List[AbstractAny]): Result = { + // TODO + // withLocalValue(`this`, instance) { + val syms = sym.tree.paramss.headOption.getOrElse(Nil).map(_.symbol) + withLocalValues(syms, args.map(LocalValue.valFrom(_))) { + eval(sym.tree.rhs.get) + } + // } + } + def interpretCall(fn: Term, argss: List[List[Term]]): Result = { - val env0 = fn match { - case Term.Select(prefix, _) => - val pre = eval(prefix) - implicitly[Env] // FIXME add pre to the env as `this` - case _ => implicitly[Env] + fn match { + case Term.Select (prefix, _) => + val pre = eval (prefix) + // TODO use + case _ => } val evaluatedArgs = argss.flatten.map(arg => LocalValue.valFrom(eval(arg))) val IsDefSymbol(sym) = fn.symbol diff --git a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala index d400c4031f46..9fae87d086b1 100644 --- a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala +++ b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala @@ -31,15 +31,10 @@ class Interpreter[R <: Reflection & Singleton](reflect0: R) extends TreeInterpre val symbol = sym.methods.find(_.name == method.getName).get if (symbol.isDefinedInCurrentRun) { - // TODO use `super.interpretCall(...)` + val argsList = if (args == null) Nil else args.toList symbol match { case IsDefSymbol(symbol) => - val args1 = if (args == null) Nil else args.toList - val evaluatedArgs = args1.map(arg => LocalValue.valFrom(arg)) - val syms = symbol.tree.paramss.headOption.getOrElse(Nil).map(_.symbol) - withLocalValues(syms, evaluatedArgs) { - eval(symbol.tree.rhs.get).asInstanceOf[Object] - } + interpretCall(this, symbol, argsList).asInstanceOf[Object] } } else { From ace99d933a11f625e0895754ee6a2b99962e8a88 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 8 Jan 2019 16:25:08 +0100 Subject: [PATCH 39/45] Add notes --- .../tasty-interpreter/notes.md | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/tests/run-with-compiler-custom-args/tasty-interpreter/notes.md b/tests/run-with-compiler-custom-args/tasty-interpreter/notes.md index b3438f571d25..ac461c1c3e3b 100644 --- a/tests/run-with-compiler-custom-args/tasty-interpreter/notes.md +++ b/tests/run-with-compiler-custom-args/tasty-interpreter/notes.md @@ -2,15 +2,13 @@ - Abstract platform operations + - Arrays - Proxies - - // Foo defined in current run - class Foo extends IFace1 with IFace2 ... - new Foo.meth() - - // Bar is precompiled - class Bar { - def meth(x: IFace1) = x.meth() - } - - FooProxy(obj).meth() \ No newline at end of file + - Enviroment of the object + - `this` in Env + - Class with fields + - Class with custom constructor (and secondary) + +- Stack + - local def env (closures) + - local class env From 844311236806686fc9366b72796b3b56e49cc41d Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 8 Jan 2019 17:14:22 +0100 Subject: [PATCH 40/45] Move TastyInterpreter --- .../tasty-interpreter/Test.scala | 23 +---------------- .../interpreter/TastyInterpreter.scala | 25 +++++++++++++++++++ 2 files changed, 26 insertions(+), 22 deletions(-) create mode 100644 tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/TastyInterpreter.scala diff --git a/tests/run-with-compiler-custom-args/tasty-interpreter/Test.scala b/tests/run-with-compiler-custom-args/tasty-interpreter/Test.scala index 24ad2f42a98f..99ba28b415b5 100644 --- a/tests/run-with-compiler-custom-args/tasty-interpreter/Test.scala +++ b/tests/run-with-compiler-custom-args/tasty-interpreter/Test.scala @@ -4,7 +4,7 @@ import dotty.tools.dotc.util.DiffUtil import scala.io.Source import scala.tasty.file._ -import scala.tasty.interpreter.jvm.Interpreter +import scala.tasty.interpreter.TastyInterpreter import scala.tasty.Reflection object Test { @@ -63,24 +63,3 @@ object Test { ) } } - -class TastyInterpreter extends TastyConsumer { - - final def apply(reflect: Reflection)(root: reflect.Tree): Unit = { - import reflect._ - object Traverser extends TreeTraverser { - - override def traverseTree(tree: Tree)(implicit ctx: Context): Unit = tree match { - // TODO: check the correct sig and object enclosement for main - case DefDef("main", _, _, _, Some(rhs)) => - val interpreter = new Interpreter(reflect) - - interpreter.eval(rhs)(Map.empty) - // TODO: recurse only for PackageDef, ClassDef - case tree => - super.traverseTree(tree) - } - } - Traverser.traverseTree(root)(reflect.rootContext) - } -} diff --git a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/TastyInterpreter.scala b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/TastyInterpreter.scala new file mode 100644 index 000000000000..9eaf6c02dc9d --- /dev/null +++ b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/TastyInterpreter.scala @@ -0,0 +1,25 @@ +package scala.tasty.interpreter + +import scala.tasty.Reflection +import scala.tasty.file.TastyConsumer + +class TastyInterpreter extends TastyConsumer { + + final def apply(reflect: Reflection)(root: reflect.Tree): Unit = { + import reflect._ + object Traverser extends TreeTraverser { + + override def traverseTree(tree: Tree)(implicit ctx: Context): Unit = tree match { + // TODO: check the correct sig and object enclosement for main + case DefDef("main", _, _, _, Some(rhs)) => + val interpreter = new jvm.Interpreter(reflect) + + interpreter.eval(rhs)(Map.empty) + // TODO: recurse only for PackageDef, ClassDef + case tree => + super.traverseTree(tree) + } + } + Traverser.traverseTree(root)(reflect.rootContext) + } +} \ No newline at end of file From ab9a60720a848b8e32051a758e026a2d72a2dafd Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 8 Jan 2019 18:00:03 +0100 Subject: [PATCH 41/45] Add infrastructure to interpret `run` test --- tests/pos/HelloWorld.check | 1 + .../tasty-interpreter/Test.scala | 51 +++++++++++++++---- tests/run/HelloWorld.scala | 3 ++ 3 files changed, 46 insertions(+), 9 deletions(-) create mode 100644 tests/pos/HelloWorld.check create mode 100644 tests/run/HelloWorld.scala diff --git a/tests/pos/HelloWorld.check b/tests/pos/HelloWorld.check new file mode 100644 index 000000000000..3b18e512dba7 --- /dev/null +++ b/tests/pos/HelloWorld.check @@ -0,0 +1 @@ +hello world diff --git a/tests/run-with-compiler-custom-args/tasty-interpreter/Test.scala b/tests/run-with-compiler-custom-args/tasty-interpreter/Test.scala index 99ba28b415b5..7ce8b20373be 100644 --- a/tests/run-with-compiler-custom-args/tasty-interpreter/Test.scala +++ b/tests/run-with-compiler-custom-args/tasty-interpreter/Test.scala @@ -1,6 +1,10 @@ -import java.io.{ByteArrayOutputStream, PrintStream} +import java.io.{ByteArrayOutputStream, File, PrintStream} +import dotty.tools.dotc.core.Contexts +import dotty.tools.dotc.reporting.Reporter +import dotty.tools.dotc.reporting.diagnostic.MessageContainer import dotty.tools.dotc.util.DiffUtil +import dotty.tools.io.Path import scala.io.Source import scala.tasty.file._ @@ -9,12 +13,8 @@ import scala.tasty.Reflection object Test { def main(args: Array[String]): Unit = { - val ps = new ByteArrayOutputStream() - try scala.Console.withOut(ps) { - ConsumeTasty("", List("IntepretedMain", "InterpretedBar"), new TastyInterpreter) - } catch { - case e: Throwable => throw new Exception(ps.toString, e) - } + + val actualOutput = interpret("")("IntepretedMain", "InterpretedBar") val expectedOutput = """42 | @@ -54,12 +54,45 @@ object Test { |68 |""".stripMargin - val actualOutput = ps.toString - assert(expectedOutput == actualOutput, "\n>>>>>>>>>>>>>>>>>>\n" + DiffUtil.mkColoredCodeDiff(actualOutput, expectedOutput, true) + "<<<<<<<<<<<<<<<<<<" ) + +// compileAndInterpret("HelloWorld") +// compileAndInterpret("i3518") +// compileAndInterpret("withIndex") + } + + def compileAndInterpret(testName: String) = { + val reproter = new Reporter { + def doReport(m: MessageContainer)(implicit ctx: Contexts.Context): Unit = println(m) + } + val out = java.nio.file.Paths.get("out/interpreted") + if (!java.nio.file.Files.exists(out)) + java.nio.file.Files.createDirectory(out) + dotty.tools.dotc.Main.process(Array("-classpath", System.getProperty("java.class.path"), "-d", out.toString, "tests/run/" + testName + ".scala"), reproter) + + val actualOutput = interpret(out.toString)("Test") + + val checkFile = java.nio.file.Paths.get("tests/run/" + testName + ".check") + val expectedOutput = Source.fromFile(checkFile.toFile).getLines().mkString("\n") + + assert(expectedOutput == actualOutput, + "\n>>>>>>>>>>>>>>>>>>\n" + + DiffUtil.mkColoredCodeDiff(actualOutput, expectedOutput, true) + + "<<<<<<<<<<<<<<<<<<" + ) + } + + def interpret(classpath: String*)(interpretedClasses: String*): String = { + val ps = new ByteArrayOutputStream() + try scala.Console.withOut(ps) { + ConsumeTasty(classpath.mkString(java.io.File.pathSeparatorChar.toString), interpretedClasses.toList, new TastyInterpreter) + } catch { + case e: Throwable => throw new Exception(ps.toString, e) + } + ps.toString } } diff --git a/tests/run/HelloWorld.scala b/tests/run/HelloWorld.scala new file mode 100644 index 000000000000..41be65f048d1 --- /dev/null +++ b/tests/run/HelloWorld.scala @@ -0,0 +1,3 @@ +object Test { + def main(args: Array[String]): Unit = println("hello world") +} From 18d4ca808e9b739124fb5a551bd75cf123f9f210 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 8 Jan 2019 18:08:14 +0100 Subject: [PATCH 42/45] Make HelloWorld pass the tests --- .../tasty-interpreter/Test.scala | 4 ++-- .../tasty-interpreter/interpreter/TreeInterpreter.scala | 7 ++++++- tests/{pos => run}/HelloWorld.check | 0 3 files changed, 8 insertions(+), 3 deletions(-) rename tests/{pos => run}/HelloWorld.check (100%) diff --git a/tests/run-with-compiler-custom-args/tasty-interpreter/Test.scala b/tests/run-with-compiler-custom-args/tasty-interpreter/Test.scala index 7ce8b20373be..04cc80d968cf 100644 --- a/tests/run-with-compiler-custom-args/tasty-interpreter/Test.scala +++ b/tests/run-with-compiler-custom-args/tasty-interpreter/Test.scala @@ -60,7 +60,7 @@ object Test { "<<<<<<<<<<<<<<<<<<" ) -// compileAndInterpret("HelloWorld") + compileAndInterpret("HelloWorld") // compileAndInterpret("i3518") // compileAndInterpret("withIndex") } @@ -77,7 +77,7 @@ object Test { val actualOutput = interpret(out.toString)("Test") val checkFile = java.nio.file.Paths.get("tests/run/" + testName + ".check") - val expectedOutput = Source.fromFile(checkFile.toFile).getLines().mkString("\n") + val expectedOutput = Source.fromFile(checkFile.toFile).getLines().mkString("", "\n", "\n") assert(expectedOutput == actualOutput, "\n>>>>>>>>>>>>>>>>>>\n" + diff --git a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/TreeInterpreter.scala b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/TreeInterpreter.scala index a4b5a3524096..ed692f6cc4b5 100644 --- a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/TreeInterpreter.scala +++ b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/TreeInterpreter.scala @@ -155,7 +155,12 @@ abstract class TreeInterpreter[R <: Reflection & Singleton](val reflect: R) { inline def log[T](tag: => String, tree: => Statement)(thunk: => T): T = { if (LOG) - println(s"#> $tag: ${tree.show}") + println( + s"""#> $tag: + |${tree.showCode} + |${tree.show} + | + |""".stripMargin) thunk } diff --git a/tests/pos/HelloWorld.check b/tests/run/HelloWorld.check similarity index 100% rename from tests/pos/HelloWorld.check rename to tests/run/HelloWorld.check From 085e1dd6b2c73a26f5d365ec42854c41c68e4f45 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Wed, 9 Jan 2019 11:20:22 +0100 Subject: [PATCH 43/45] Interpret varargs --- .../tasty-interpreter/interpreter/TreeInterpreter.scala | 3 +++ .../tasty-interpreter/interpreter/jvm/Interpreter.scala | 2 ++ 2 files changed, 5 insertions(+) diff --git a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/TreeInterpreter.scala b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/TreeInterpreter.scala index ed692f6cc4b5..48eaf9c59cb3 100644 --- a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/TreeInterpreter.scala +++ b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/TreeInterpreter.scala @@ -88,6 +88,8 @@ abstract class TreeInterpreter[R <: Reflection & Singleton](val reflect: R) { def interpretIsInstanceOf(o: AbstractAny, tpt: TypeTree): Result def interpretAsInstanceOf(o: AbstractAny, tpt: TypeTree): Result + def interpretRepeated(elems: List[AbstractAny]): AbstractAny + def interpretEqEq(x: AbstractAny, y: AbstractAny): AbstractAny def interpretPrivitiveLt(x: AbstractAny, y: AbstractAny): AbstractAny @@ -148,6 +150,7 @@ abstract class TreeInterpreter[R <: Reflection & Singleton](val reflect: R) { case Term.Block(stats, expr) => log("interpretBlock", tree)(interpretBlock(stats, expr)) case Term.Literal(const) => log("interpretLiteral", tree)(interpretLiteral(const)) case Term.Typed(expr, _) => log("", tree)(eval(expr)) + case Term.Repeated(elems, _) => log("", tree)(interpretRepeated(elems.map(elem => eval(elem)))) case _ => throw new MatchError(tree.show) } diff --git a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala index 9fae87d086b1..7a43e1f1b5c3 100644 --- a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala +++ b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala @@ -97,6 +97,8 @@ class Interpreter[R <: Reflection & Singleton](reflect0: R) extends TreeInterpre def interpretAsInstanceOf(o: AbstractAny, tpt: TypeTree): Result = jvmReflection.getClassOf(tpt.symbol).cast(o) + def interpretRepeated(elems: List[AbstractAny]): AbstractAny = elems.toSeq + def interpretEqEq(x: AbstractAny, y: AbstractAny): AbstractAny = x == y def interpretPrivitiveLt(x: AbstractAny, y: AbstractAny): AbstractAny = withNumeric(x, y)(_.lt(_, _)) From 4098c43b4befbabe55555ffd56a0e07857f4c1bc Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Wed, 9 Jan 2019 11:40:04 +0100 Subject: [PATCH 44/45] Add a list of interesting tests --- .../tasty-interpreter/Test.scala | 39 +++++++++++++------ 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/tests/run-with-compiler-custom-args/tasty-interpreter/Test.scala b/tests/run-with-compiler-custom-args/tasty-interpreter/Test.scala index 04cc80d968cf..96212d50d70b 100644 --- a/tests/run-with-compiler-custom-args/tasty-interpreter/Test.scala +++ b/tests/run-with-compiler-custom-args/tasty-interpreter/Test.scala @@ -60,30 +60,45 @@ object Test { "<<<<<<<<<<<<<<<<<<" ) - compileAndInterpret("HelloWorld") -// compileAndInterpret("i3518") -// compileAndInterpret("withIndex") + compileAndInterpret("HelloWorld.scala") + compileAndInterpret("nullInstanceEval.scala") + compileAndInterpret("t3327.scala") +// compileAndInterpret("t5614.scala") +// compileAndInterpret("t4054.scala") +// compileAndInterpret("sort.scala") +// compileAndInterpret("t0607.scala") +// compileAndInterpret("i4073b.scala") +// compileAndInterpret("i4430.scala") +// compileAndInterpret("nullAsInstanceOf.scala") +// compileAndInterpret("classof.scala") +// compileAndInterpret("null-hash.scala") +// compileAndInterpret("i3518.scala") +// compileAndInterpret("withIndex.scala") +// compileAndInterpret("unboxingBug.scala") +// compileAndInterpret("traitInit.scala") } - def compileAndInterpret(testName: String) = { + def compileAndInterpret(testFileName: String) = { val reproter = new Reporter { def doReport(m: MessageContainer)(implicit ctx: Contexts.Context): Unit = println(m) } val out = java.nio.file.Paths.get("out/interpreted") if (!java.nio.file.Files.exists(out)) java.nio.file.Files.createDirectory(out) - dotty.tools.dotc.Main.process(Array("-classpath", System.getProperty("java.class.path"), "-d", out.toString, "tests/run/" + testName + ".scala"), reproter) + dotty.tools.dotc.Main.process(Array("-classpath", System.getProperty("java.class.path"), "-d", out.toString, "tests/run/" + testFileName), reproter) val actualOutput = interpret(out.toString)("Test") - val checkFile = java.nio.file.Paths.get("tests/run/" + testName + ".check") - val expectedOutput = Source.fromFile(checkFile.toFile).getLines().mkString("", "\n", "\n") + val checkFile = java.nio.file.Paths.get("tests/run/" + testFileName.stripSuffix(".scala") + ".check") + if (java.nio.file.Files.exists(checkFile)) { + val expectedOutput = Source.fromFile(checkFile.toFile).getLines().mkString("", "\n", "\n") - assert(expectedOutput == actualOutput, - "\n>>>>>>>>>>>>>>>>>>\n" + - DiffUtil.mkColoredCodeDiff(actualOutput, expectedOutput, true) + - "<<<<<<<<<<<<<<<<<<" - ) + assert(expectedOutput == actualOutput, + "\n>>>>>>>>>>>>>>>>>>\n" + + DiffUtil.mkColoredCodeDiff(actualOutput, expectedOutput, true) + + "<<<<<<<<<<<<<<<<<<" + ) + } } def interpret(classpath: String*)(interpretedClasses: String*): String = { From 9a9bbbc949d9c77e2ca0d648dd32f07db528e73e Mon Sep 17 00:00:00 2001 From: Aggelos Biboudis Date: Fri, 25 Jan 2019 17:20:07 +0100 Subject: [PATCH 45/45] Rebased --- .../tools/dotc/tastyreflect/FlagSet.scala | 70 --------------- .../dotc/tastyreflect/FlagsOpsImpl.scala | 2 +- .../dotc/tastyreflect/SymbolOpsImpl.scala | 5 ++ library/src/scala/tasty/reflect/FlagSet.scala | 88 ------------------- .../src/scala/tasty/reflect/FlagsOps.scala | 4 +- .../src/scala/tasty/reflect/Printers.scala | 4 +- .../src/scala/tasty/reflect/SymbolOps.scala | 2 + .../interpreter/TreeInterpreter.scala | 4 +- .../interpreter/jvm/Interpreter.scala | 2 +- .../interpreter/jvm/JVMReflection.scala | 20 ++--- 10 files changed, 25 insertions(+), 176 deletions(-) delete mode 100644 compiler/src/dotty/tools/dotc/tastyreflect/FlagSet.scala delete mode 100644 library/src/scala/tasty/reflect/FlagSet.scala diff --git a/compiler/src/dotty/tools/dotc/tastyreflect/FlagSet.scala b/compiler/src/dotty/tools/dotc/tastyreflect/FlagSet.scala deleted file mode 100644 index 5aabdfc97b30..000000000000 --- a/compiler/src/dotty/tools/dotc/tastyreflect/FlagSet.scala +++ /dev/null @@ -1,70 +0,0 @@ -package dotty.tools.dotc.tastyreflect - -import dotty.tools.dotc.core.Flags -import dotty.tools.dotc.core.Flags._ - -class FlagSet(flags: Flags.FlagSet) extends scala.tasty.reflect.FlagSet { - - def isProtected: Boolean = flags.is(Protected) - def isAbstract: Boolean = flags.is(Abstract) - def isFinal: Boolean = flags.is(Final) - def isSealed: Boolean = flags.is(Sealed) - def isCase: Boolean = flags.is(Case) - def isImplicit: Boolean = flags.is(Implicit) - def isErased: Boolean = flags.is(Erased) - def isLazy: Boolean = flags.is(Lazy) - def isOverride: Boolean = flags.is(Override) - def isInline: Boolean = flags.is(Inline) - def isMacro: Boolean = flags.is(Macro) - def isStatic: Boolean = flags.is(JavaStatic) - def isObject: Boolean = flags.is(Module) - def isTrait: Boolean = flags.is(Trait) - def isLocal: Boolean = flags.is(Local) - def isSynthetic: Boolean = flags.is(Synthetic) - def isArtifact: Boolean = flags.is(Artifact) - def isMutable: Boolean = flags.is(Mutable) - def isFieldAccessor: Boolean = flags.is(Accessor) - def isCaseAcessor: Boolean = flags.is(CaseAccessor) - def isCovariant: Boolean = flags.is(Covariant) - def isContravariant: Boolean = flags.is(Contravariant) - def isScala2X: Boolean = flags.is(Scala2x) - def isDefaultParameterized: Boolean = flags.is(DefaultParameterized) - def isStable: Boolean = flags.is(Stable) - def isParam: Boolean = flags.is(Param) - def isParamAccessor: Boolean = flags.is(ParamAccessor) - def isPackage: Boolean = flags.is(Package) - - override def toString: String = { - val flags = List.newBuilder[String] - if (isProtected) flags += "protected " - if (isAbstract) flags += "abstract" - if (isFinal) flags += "final" - if (isSealed) flags += "sealed" - if (isCase) flags += "case" - if (isImplicit) flags += "implicit" - if (isErased) flags += "erased" - if (isLazy) flags += "lazy" - if (isOverride) flags += "override" - if (isInline) flags += "inline" - if (isMacro) flags += "macro" - if (isStatic) flags += "javaStatic" - if (isObject) flags += "object" - if (isTrait) flags += "trait" - if (isLocal) flags += "local" - if (isSynthetic) flags += "synthetic" - if (isArtifact) flags += "artifact" - if (isMutable) flags += "mutable" - if (isFieldAccessor) flags += "accessor" - if (isCaseAcessor) flags += "caseAccessor" - if (isCovariant) flags += "covariant" - if (isContravariant) flags += "contravariant" - if (isScala2X) flags += "scala2x" - if (isDefaultParameterized) flags += "defaultParameterized" - if (isStable) flags += "stable" - if (isParam) flags += "param" - if (isParamAccessor) flags += "paramAccessor" - if (isPackage) flags += "package" - flags.result().mkString("<", ",", ">") - } - -} diff --git a/compiler/src/dotty/tools/dotc/tastyreflect/FlagsOpsImpl.scala b/compiler/src/dotty/tools/dotc/tastyreflect/FlagsOpsImpl.scala index a65357d20b32..d71935e79a50 100644 --- a/compiler/src/dotty/tools/dotc/tastyreflect/FlagsOpsImpl.scala +++ b/compiler/src/dotty/tools/dotc/tastyreflect/FlagsOpsImpl.scala @@ -40,7 +40,7 @@ trait FlagsOpsImpl extends scala.tasty.reflect.FlagsOps with CoreImpl { def Contravariant: Flags = core.Flags.Contravariant def Scala2X: Flags = core.Flags.Scala2x def DefaultParameterized: Flags = core.Flags.DefaultParameterized - def Stable: Flags = core.Flags.StableRealizable + def StableRealizable: Flags = core.Flags.StableRealizable def Param: Flags = core.Flags.Param def ParamAccessor: Flags = core.Flags.ParamAccessor } diff --git a/compiler/src/dotty/tools/dotc/tastyreflect/SymbolOpsImpl.scala b/compiler/src/dotty/tools/dotc/tastyreflect/SymbolOpsImpl.scala index c4359beacb1e..824f003663c1 100644 --- a/compiler/src/dotty/tools/dotc/tastyreflect/SymbolOpsImpl.scala +++ b/compiler/src/dotty/tools/dotc/tastyreflect/SymbolOpsImpl.scala @@ -193,6 +193,11 @@ trait SymbolOpsImpl extends scala.tasty.reflect.SymbolOps with CoreImpl { val sym = symbol.moduleClass if (sym.exists) Some(sym.asClass) else None } + + def companionClass(implicit ctx: Context): Option[ClassSymbol] = { + val sym = symbol.companionClass + if (sym.exists) Some(sym.asClass) else None + } } object IsBindSymbol extends IsBindSymbolModule { diff --git a/library/src/scala/tasty/reflect/FlagSet.scala b/library/src/scala/tasty/reflect/FlagSet.scala deleted file mode 100644 index 67b11a7c35eb..000000000000 --- a/library/src/scala/tasty/reflect/FlagSet.scala +++ /dev/null @@ -1,88 +0,0 @@ -package scala.tasty.reflect - -trait FlagSet { - - /** Is this symbol `protected` */ - def isProtected: Boolean - - /** Is this symbol `abstract` */ - def isAbstract: Boolean - - /** Is this symbol `final` */ - def isFinal: Boolean - - /** Is this symbol `sealed` */ - def isSealed: Boolean - - /** Is this symbol `case` */ - def isCase: Boolean - - /** Is this symbol `implicit` */ - def isImplicit: Boolean - - /** Is this symbol `erased` */ - def isErased: Boolean - - /** Is this symbol `lazy` */ - def isLazy: Boolean - - /** Is this symbol `override` */ - def isOverride: Boolean - - /** Is this symbol `inline` */ - def isInline: Boolean - - /** Is this symbol markes as a macro. An inline method containing toplevel splices */ - def isMacro: Boolean - - /** Is this symbol marked as static. Mapped to static Java member */ - def isStatic: Boolean - - /** Is this symbol an object or its class (used for a ValDef or a ClassDef extends Modifier respectively) */ - def isObject: Boolean - - /** Is this symbol a trait */ - def isTrait: Boolean - - /** Is this symbol local? Used in conjunction with Private/private[Type] to mean private[this] extends Modifier proctected[this] */ - def isLocal: Boolean - - /** Was this symbol generated by Scala compiler */ - def isSynthetic: Boolean - - /** Is this symbol to be tagged Java Synthetic */ - def isArtifact: Boolean - - /** Is this symbol a `var` (when used on a ValDef) */ - def isMutable: Boolean - - /** Is this symbol a getter or a setter */ - def isFieldAccessor: Boolean - - /** Is this symbol a getter for case class parameter */ - def isCaseAcessor: Boolean - - /** Is this symbol a type parameter marked as covariant `+` */ - def isCovariant: Boolean - - /** Is this symbol a type parameter marked as contravariant `-` */ - def isContravariant: Boolean - - /** Was this symbol imported from Scala2.x */ - def isScala2X: Boolean - - /** Is this symbol a method with default parameters */ - def isDefaultParameterized: Boolean - - /** Is this symbol member that is assumed to be stable */ - def isStable: Boolean - - /** Is this symbol a parameter */ - def isParam: Boolean - - /** Is this symbol a parameter accessor */ - def isParamAccessor: Boolean - - /** Is this symbol a package */ - def isPackage: Boolean -} diff --git a/library/src/scala/tasty/reflect/FlagsOps.scala b/library/src/scala/tasty/reflect/FlagsOps.scala index d75c72cf2db7..eafef4cf3d01 100644 --- a/library/src/scala/tasty/reflect/FlagsOps.scala +++ b/library/src/scala/tasty/reflect/FlagsOps.scala @@ -93,8 +93,8 @@ trait FlagsOps extends Core { /** Is this symbol a method with default parameters */ def DefaultParameterized: Flags - /** Is this symbol member that is assumed to be stable */ - def Stable: Flags + /** Is this symbol member that is assumed to be stable and realizable */ + def StableRealizable: Flags /** Is this symbol a parameter */ def Param: Flags diff --git a/library/src/scala/tasty/reflect/Printers.scala b/library/src/scala/tasty/reflect/Printers.scala index 6486334ef52a..cc5b827d7f01 100644 --- a/library/src/scala/tasty/reflect/Printers.scala +++ b/library/src/scala/tasty/reflect/Printers.scala @@ -126,7 +126,7 @@ trait Printers if (flags.is(Flags.Contravariant)) flagList += "Flags.Contravariant" if (flags.is(Flags.Scala2X)) flagList += "Flags.Scala2X" if (flags.is(Flags.DefaultParameterized)) flagList += "Flags.DefaultParameterized" - if (flags.is(Flags.Stable)) flagList += "Flags.Stable" + if (flags.is(Flags.StableRealizable)) flagList += "Flags.StableRealizable" if (flags.is(Flags.Param)) flagList += "Flags.Param" if (flags.is(Flags.ParamAccessor)) flagList += "Flags.ParamAccessor" flagList.result().mkString(" | ") @@ -501,7 +501,7 @@ trait Printers if (flags.is(Flags.Contravariant)) flagList += "contravariant" if (flags.is(Flags.Scala2X)) flagList += "scala2x" if (flags.is(Flags.DefaultParameterized)) flagList += "defaultParameterized" - if (flags.is(Flags.Stable)) flagList += "stable" + if (flags.is(Flags.StableRealizable)) flagList += "stableRealizable" if (flags.is(Flags.Param)) flagList += "param" if (flags.is(Flags.ParamAccessor)) flagList += "paramAccessor" flagList.result().mkString("/*", " ", "*/") diff --git a/library/src/scala/tasty/reflect/SymbolOps.scala b/library/src/scala/tasty/reflect/SymbolOps.scala index b16307278b3e..cd69466efdf7 100644 --- a/library/src/scala/tasty/reflect/SymbolOps.scala +++ b/library/src/scala/tasty/reflect/SymbolOps.scala @@ -155,6 +155,8 @@ trait SymbolOps extends Core { /** The class symbol of the companion module class */ def moduleClass(implicit ctx: Context): Option[ClassSymbol] + + def companionClass(implicit ctx: Context): Option[ClassSymbol] } implicit def ValSymbolDeco(symbol: ValSymbol): ValSymbolAPI diff --git a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/TreeInterpreter.scala b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/TreeInterpreter.scala index 48eaf9c59cb3..8df8d08d65b2 100644 --- a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/TreeInterpreter.scala +++ b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/TreeInterpreter.scala @@ -67,8 +67,8 @@ abstract class TreeInterpreter[R <: Reflection & Singleton](val reflect: R) { case ValDef(name, tpt, Some(rhs)) => def evalRhs = eval(rhs)(accEnv) val evalRef: LocalValue = - if (stat.symbol.flags.isLazy) LocalValue.lazyValFrom(evalRhs) - else if (stat.symbol.flags.isMutable) LocalValue.varFrom(evalRhs) + if (stat.symbol.flags.is(Flags.Lazy)) LocalValue.lazyValFrom(evalRhs) + else if (stat.symbol.flags.is(Flags.Mutable)) LocalValue.varFrom(evalRhs) else LocalValue.valFrom(evalRhs) accEnv.updated(stat.symbol, evalRef) diff --git a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala index 7a43e1f1b5c3..e27e00d14c65 100644 --- a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala +++ b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala @@ -77,7 +77,7 @@ class Interpreter[R <: Reflection & Singleton](reflect0: R) extends TreeInterpre // FIXME not necesarly static jvmReflection.interpretStaticVal(fn.symbol.owner, fn.symbol) case _ => - if (fn.symbol.flags.isObject) + if (fn.symbol.flags.is(Flags.Object)) jvmReflection.loadModule(fn.symbol.asVal.moduleClass.get) else jvmReflection.interpretStaticVal(fn.symbol.owner, fn.symbol) diff --git a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/JVMReflection.scala b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/JVMReflection.scala index 92ed6188d1b5..3983dd298113 100644 --- a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/JVMReflection.scala +++ b/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/JVMReflection.scala @@ -11,16 +11,16 @@ class JVMReflection[R <: Reflection & Singleton](val reflect: R) { final val MODULE_INSTANCE_FIELD = "MODULE$" def loadModule(sym: Symbol): Object = { - if (sym.owner.flags.isPackage) { - // is top level object - val moduleClass = getClassOf(sym) - moduleClass.getField(MODULE_INSTANCE_FIELD).get(null) - } - else { - // nested object in an object - // val clazz = loadClass(sym.fullNameSeparated(FlatName)) - // clazz.getConstructor().newInstance().asInstanceOf[Object] - ??? + + sym.owner match { + case IsPackageSymbol(_) => + val moduleClass = getClassOf(sym) + moduleClass.getField(MODULE_INSTANCE_FIELD).get(null) + case _ => + // nested object in an object + // val clazz = loadClass(sym.fullNameSeparated(FlatName)) + // clazz.getConstructor().newInstance().asInstanceOf[Object] + ??? } }