1
1
package dotty .tools .dotc
2
2
package interpreter
3
3
4
+ import java .io .{PrintWriter , StringWriter }
5
+
4
6
import dotty .tools .dotc .ast .tpd
5
7
import dotty .tools .dotc .ast .Trees ._
6
8
import dotty .tools .dotc .core .Constants ._
7
9
import dotty .tools .dotc .core .Contexts ._
8
- import dotty .tools .dotc .core .Flags ._
9
10
import dotty .tools .dotc .core .Decorators ._
11
+ import dotty .tools .dotc .core .Names ._
10
12
import dotty .tools .dotc .core .Symbols ._
11
13
import dotty .tools .dotc .quoted .Quoted
14
+ import dotty .tools .dotc .util .Positions .Position
15
+
12
16
import scala .reflect .ClassTag
13
17
import java .net .URLClassLoader
18
+ import java .lang .reflect .Constructor
19
+ import java .lang .reflect .Method
14
20
15
21
/** Tree interpreter that can interpret
16
22
* * Literal constants
@@ -37,11 +43,14 @@ class Interpreter(implicit ctx: Context) {
37
43
interpretTreeImpl(tree) match {
38
44
case obj : T => Some (obj)
39
45
case obj =>
46
+ // TODO upgrade to a full type tag check or something similar
40
47
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
42
49
}
43
50
} catch {
44
- case _ : StopInterpretation => None
51
+ case ex : StopInterpretation =>
52
+ ctx.error(ex.msg, ex.pos)
53
+ None
45
54
}
46
55
}
47
56
@@ -50,56 +59,88 @@ class Interpreter(implicit ctx: Context) {
50
59
*
51
60
* If some error is encountered while interpreting a ctx.error is emited and a StopInterpretation is thrown.
52
61
*/
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 {
99
136
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)
103
144
}
104
145
}
105
146
@@ -114,6 +155,6 @@ class Interpreter(implicit ctx: Context) {
114
155
}
115
156
116
157
/** 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
118
159
119
160
}
0 commit comments