11package dotty .tools .dotc
22package interpreter
33
4+ import java .io .{PrintWriter , StringWriter }
5+
46import dotty .tools .dotc .ast .tpd
57import dotty .tools .dotc .ast .Trees ._
68import dotty .tools .dotc .core .Constants ._
79import dotty .tools .dotc .core .Contexts ._
8- import dotty .tools .dotc .core .Flags ._
910import dotty .tools .dotc .core .Decorators ._
11+ import dotty .tools .dotc .core .Names ._
1012import dotty .tools .dotc .core .Symbols ._
1113import dotty .tools .dotc .quoted .Quoted
14+ import dotty .tools .dotc .util .Positions .Position
15+
1216import scala .reflect .ClassTag
1317import java .net .URLClassLoader
18+ import java .lang .reflect .Constructor
19+ import java .lang .reflect .Method
1420
1521/** Tree interpreter that can interpret
1622 * * Literal constants
@@ -37,11 +43,14 @@ class Interpreter(implicit ctx: Context) {
3743 interpretTreeImpl(tree) match {
3844 case obj : T => Some (obj)
3945 case obj =>
46+ // TODO upgrade to a full type tag check or something similar
4047 ctx.error(s " Interpreted tree returned a result of an unexpected type. Expected ${ct.runtimeClass} but was ${obj.getClass}" , tree.pos)
41- throw new StopInterpretation
48+ None
4249 }
4350 } catch {
44- case _ : StopInterpretation => None
51+ case ex : StopInterpretation =>
52+ ctx.error(ex.msg, ex.pos)
53+ None
4554 }
4655 }
4756
@@ -50,56 +59,88 @@ class Interpreter(implicit ctx: Context) {
5059 *
5160 * If some error is encountered while interpreting a ctx.error is emited and a StopInterpretation is thrown.
5261 */
53- private def interpretTreeImpl (tree : Tree ): Object = {
54- try {
55- tree match {
56- case Quoted (quotedTree) => RawQuoted (quotedTree)
57-
58- case Literal (Constant (c)) => c.asInstanceOf [AnyRef ]
59-
60- case Apply (fn, args) if fn.symbol.isConstructor =>
61- val cls = fn.symbol.owner
62- val clazz = classLoader.loadClass(cls.symbol.fullName.toString)
63- val paramClasses = paramsSig(fn.symbol)
64- val args1 : List [Object ] = args.map(arg => interpretTreeImpl(arg))
65- clazz.getConstructor(paramClasses : _* ).newInstance(args1 : _* ).asInstanceOf [Object ]
66-
67- case Apply (fun, args) if fun.symbol.isStatic =>
68- val clazz = classLoader.loadClass(fun.symbol.owner.companionModule.fullName.toString)
69- val paramClasses = paramsSig(fun.symbol)
70- val args1 : List [Object ] = args.map(arg => interpretTreeImpl(arg))
71- val method = clazz.getMethod(fun.symbol.name.toString, paramClasses : _* )
72- method.invoke(null , args1 : _* )
73-
74- case tree : RefTree if tree.symbol.isStatic =>
75- val clazz = classLoader.loadClass(tree.symbol.owner.companionModule.fullName.toString)
76- val method = clazz.getMethod(tree.name.toString)
77- method.invoke(null )
78-
79- case tree : RefTree if tree.symbol.is(Module ) =>
80- ??? // TODO
81-
82- case Inlined (_, bindings, expansion) =>
83- if (bindings.nonEmpty) ??? // TODO evaluate bindings and add environment
84- interpretTreeImpl(expansion)
85- case _ =>
86- val msg =
87- if (tree.tpe.derivesFrom(defn.QuotedExprClass )) " Quote needs to be explicit or a call to a static method"
88- else " Value needs to be a explicit or a call to a static method"
89- ctx.error(msg, tree.pos)
90- throw new StopInterpretation
91- }
92- } catch {
93- case ex : NoSuchMethodException =>
94- ctx.error(" Could not find interpreted method in classpath: " + ex.getMessage, tree.pos)
95- throw new StopInterpretation
96- case ex : ClassNotFoundException =>
97- ctx.error(" Could not find interpreted class in classpath: " + ex.getMessage, tree.pos)
98- throw new StopInterpretation
62+ private def interpretTreeImpl (tree : Tree ): Object = { // TODO add environment
63+ implicit val pos : Position = tree.pos
64+ tree match {
65+ case Quoted (quotedTree) => RawQuoted (quotedTree)
66+
67+ case Literal (Constant (c)) => c.asInstanceOf [AnyRef ]
68+
69+ case Apply (fn, args) if fn.symbol.isConstructor =>
70+ val clazz = loadClass(fn.symbol.owner.symbol.fullName)
71+ val paramClasses = paramsSig(fn.symbol)
72+ val interpretedArgs = args.map(arg => interpretTreeImpl(arg))
73+ val constructor = getConstructor(clazz, paramClasses)
74+ interpreted(constructor.newInstance(interpretedArgs : _* ))
75+
76+ case _ : RefTree | _ : Apply if tree.symbol.isStatic =>
77+ val clazz = loadClass(tree.symbol.owner.companionModule.fullName)
78+ val paramClasses = paramsSig(tree.symbol)
79+
80+ val interpretedArgs = Array .newBuilder[Object ]
81+ def interpretArgs (tree : Tree ): Unit = tree match {
82+ case Apply (fn, args) =>
83+ interpretArgs(fn)
84+ args.foreach(arg => interpretedArgs += interpretTreeImpl(arg))
85+ case _ =>
86+ }
87+ interpretArgs(tree)
88+
89+ val method = getMethod(clazz, tree.symbol.name, paramClasses)
90+ interpreted(method.invoke(null , interpretedArgs.result(): _* ))
91+
92+ // case tree: RefTree if tree.symbol.is(Module) => // TODO
93+ // case Block(stats, expr) => // TODO evaluate bindings add environment
94+ // case ValDef(_, _, rhs) => // TODO evaluate bindings add environment
95+
96+ case Inlined (_, bindings, expansion) =>
97+ assert(bindings.isEmpty) // TODO evaluate bindings and add environment
98+ interpretTreeImpl(expansion)
99+ case _ =>
100+ // TODO Add more precise descriptions of why it could not be interpreted.
101+ // This should be done after the full interpreter is implemented.
102+ throw new StopInterpretation (s " Could not interpret ${tree.show}" , tree.pos)
103+ }
104+ }
105+
106+ private def loadClass (name : Name )(implicit pos : Position ): Class [_] = {
107+ try classLoader.loadClass(name.toString)
108+ catch {
109+ case _ : ClassNotFoundException =>
110+ val msg = s " Could not find interpreted class $name in classpath "
111+ throw new StopInterpretation (msg, pos)
112+ }
113+ }
114+
115+ private def getMethod (clazz : Class [_], name : Name , paramClasses : List [Class [_]])(implicit pos : Position ): Method = {
116+ try clazz.getMethod(name.toString, paramClasses : _* )
117+ catch {
118+ case _ : NoSuchMethodException =>
119+ val msg = s " Could not find interpreted method ${clazz.getCanonicalName}. $name with parameters $paramClasses"
120+ throw new StopInterpretation (msg, pos)
121+ }
122+ }
123+
124+ private def getConstructor (clazz : Class [_], paramClasses : List [Class [_]])(implicit pos : Position ): Constructor [Object ] = {
125+ try clazz.getConstructor(paramClasses : _* ).asInstanceOf [Constructor [Object ]]
126+ catch {
127+ case _ : NoSuchMethodException =>
128+ val msg = s " Could not find interpreted constructor of ${clazz.getCanonicalName} with parameters $paramClasses"
129+ throw new StopInterpretation (msg, pos)
130+ }
131+ }
132+
133+ private def interpreted [T ](thunk : => T )(implicit pos : Position ): T = {
134+ try thunk
135+ catch {
99136 case ex : RuntimeException =>
100- ex.printStackTrace()
101- ctx.error(" A runtime exception occurred while interpreting: " + ex.getMessage, tree.pos)
102- throw new StopInterpretation
137+ val sw = new StringWriter ()
138+ sw.write(" A runtime exception occurred while interpreting" )
139+ sw.write(ex.getMessage)
140+ sw.write(" \n " )
141+ ex.printStackTrace(new PrintWriter (sw))
142+ sw.write(" \n " )
143+ throw new StopInterpretation (sw.toString, pos)
103144 }
104145 }
105146
@@ -114,6 +155,6 @@ class Interpreter(implicit ctx: Context) {
114155 }
115156
116157 /** Exception that stops interpretation if some issue is found */
117- private class StopInterpretation extends Exception
158+ private class StopInterpretation ( val msg : String , val pos : Position ) extends Exception
118159
119160}
0 commit comments