@@ -34,6 +34,7 @@ import javax.script.*
34
34
import scala .collection .mutable
35
35
import scala .jdk .CollectionConverters .*
36
36
import scala .util .Try
37
+ import scala .util .control .NonFatal
37
38
import scala .util .matching .Regex
38
39
39
40
/**
@@ -67,12 +68,12 @@ class ScalaAdaptedScriptEngine(engine: ScriptEngine) extends AbstractScriptEngin
67
68
68
69
// Scala 3.2.2 ignores bindings, emulate binding using setup script
69
70
// Create a line with variable declaration for each binding item
70
- val lines =
71
+ val transfers : mutable. Seq [ Seq [ String ]] =
71
72
for
72
73
scope <- context.getScopes.asScala
73
74
bindings <- Option (context.getBindings(scope)).map(_.asScala) // bindings in context can be null
74
75
yield {
75
- for (name, value) <- bindings yield {
76
+ for (name, value) <- bindings.toSeq yield {
76
77
if isValidVariableName(name) then
77
78
val validName = addBackticksIfNeeded(name)
78
79
value match
@@ -85,12 +86,22 @@ class ScalaAdaptedScriptEngine(engine: ScriptEngine) extends AbstractScriptEngin
85
86
case v : Byte => s " val $validName : Byte = $v"
86
87
case v : Boolean => s " val $validName : Int = $v"
87
88
case v : AnyRef =>
88
- _transfer = v
89
- val typeName = Option (v).map(_.getClass.getCanonicalName).getOrElse(" AnyRef" )
89
+ val transferIndex = BindingSupport .nextTransferIndex
90
+ BindingSupport .__transfer(transferIndex) = v
91
+ val typeName = Option (v)
92
+ .map { oo =>
93
+ val tt : Array [_] = oo.getClass.getTypeParameters
94
+ tt.foreach(t => log(s " ${oo.getClass.getCanonicalName} TYPE PARAM: ${t.getClass.getName}" ))
95
+ val p = tt.map(_ => " _" ).mkString(" [" , " ," , " ]" )
96
+ val n = oo.getClass.getCanonicalName
97
+ if tt.nonEmpty then n + p else n
98
+ }
99
+ .getOrElse(" AnyRef" )
90
100
val validTypeName = addBackticksIfNeeded(typeName)
91
101
s """
92
102
|val $validName : $validTypeName = {
93
- | val t = org.scijava.plugins.scripting.scala.ScalaAdaptedScriptEngine._transfer
103
+ | val t = org.scijava.plugins.scripting.scala.ScalaAdaptedScriptEngine.BindingSupport
104
+ | ._transfer( $transferIndex)
94
105
| t.asInstanceOf[ $validTypeName]
95
106
|} """ .stripMargin
96
107
case v : Unit =>
@@ -100,26 +111,45 @@ class ScalaAdaptedScriptEngine(engine: ScriptEngine) extends AbstractScriptEngin
100
111
}
101
112
}
102
113
103
- val script = lines
114
+ val script = transfers
104
115
.flatten
105
116
.filter(_.nonEmpty)
106
117
.mkString(" \n " )
107
118
108
- if script.nonEmpty then
109
- evalInner(script, context)
119
+ evalInner(script, context)
110
120
111
121
end emulateBinding
112
122
113
- private def evalInner (script : String , context : ScriptContext ) =
114
- class WriterOutputStream (w : Writer ) extends OutputStream :
115
- override def write (b : Int ): Unit = w.write(b)
123
+ private def evalInner (script : String , context : ScriptContext ): AnyRef =
124
+ log(
125
+ s """
126
+ |LOG[evalInner] script
127
+ |BEGIN
128
+ |---------------------------
129
+ | $script
130
+ |---------------------------
131
+ |END
132
+ | """ .stripMargin
133
+ )
134
+ if script.trim.isEmpty then
135
+ log(" LOG[evalInner] script is empty, skipping evaluation" )
136
+ null
137
+ else
138
+ class WriterOutputStream (w : Writer ) extends OutputStream :
139
+ override def write (b : Int ): Unit = w.write(b)
116
140
117
- // Redirect output to writes provided by context
118
- Console .withOut(WriterOutputStream (context.getWriter)) {
119
- Console .withErr(WriterOutputStream (context.getErrorWriter)) {
120
- engine.eval(script, context)
121
- }
122
- }
141
+ try
142
+ // Redirect output to writes provided by context
143
+ Console .withOut(WriterOutputStream (context.getWriter)) {
144
+ Console .withErr(WriterOutputStream (context.getErrorWriter)) {
145
+ engine.eval(script, context)
146
+ }
147
+ }
148
+ catch
149
+ case NonFatal (t) =>
150
+ log(s " LOG[evalInner] in eval: $t" )
151
+ t.printStackTrace()
152
+ throw t
123
153
124
154
private def stringFromReader (in : Reader ) =
125
155
val out = new StringWriter ()
@@ -156,9 +186,15 @@ class ScalaAdaptedScriptEngine(engine: ScriptEngine) extends AbstractScriptEngin
156
186
value
157
187
end get
158
188
189
+ private def log (msg : String ): Unit = {
190
+ if ScalaAdaptedScriptEngine .DEBUG then
191
+ Console .out.println(msg)
192
+ }
193
+
159
194
end ScalaAdaptedScriptEngine
160
195
161
196
object ScalaAdaptedScriptEngine :
197
+ private val DEBUG : Boolean = false
162
198
private lazy val variableNamePattern = """ ^[a-zA-Z_$][a-zA-Z_$0-9]*$""" .r
163
199
private val scala3Keywords = Seq (
164
200
" abstract" ,
@@ -204,10 +240,6 @@ object ScalaAdaptedScriptEngine:
204
240
" yield"
205
241
)
206
242
207
- /** Do not use externally despite it is declared public. IT is public so it is accessible from scripts */
208
- // noinspection ScalaWeakerAccess
209
- var _transfer : Object = _
210
-
211
243
private def isValidVariableName (name : String ): Boolean = variableNamePattern.matches(name)
212
244
213
245
private [scala] def addBackticksIfNeeded (referenceName : String ): String =
@@ -216,4 +248,32 @@ object ScalaAdaptedScriptEngine:
216
248
.map(n => if scala3Keywords.contains(n) then s " ` $n` " else n)
217
249
.mkString(" ." )
218
250
251
+ /**
252
+ * Temporary support for implementing binding in the script engine.
253
+ * It has limited capacity and does not free memory.
254
+ * Access to storage is public, so it is visible from scripts.
255
+ */
256
+ // noinspection ScalaWeakerAccess
257
+ object BindingSupport :
258
+ private val MaxTransfers : Int = 1024 * 1024
259
+
260
+ /**
261
+ * Do not use externally despite it is declared public.
262
+ * It is public so it is accessible from scripts that are used to emulate variable binding
263
+ */
264
+ // noinspection ScalaWeakerAccess,ScalaUnusedSymbol
265
+ def _transfer : Seq [AnyRef ] = __transfer.toSeq
266
+
267
+ private [scala] val __transfer : mutable.ListBuffer [AnyRef ] = mutable.ListBuffer .empty[AnyRef ]
268
+
269
+ private var lastTransferIndex = - 1
270
+
271
+ private [scala] def nextTransferIndex : Int =
272
+ if lastTransferIndex + 1 >= MaxTransfers then
273
+ throw new IllegalStateException (" ScalaAdaptedScriptEngine: maximum transfer limit reached" )
274
+
275
+ lastTransferIndex += 1
276
+ __transfer.append(null )
277
+ lastTransferIndex
278
+
219
279
end ScalaAdaptedScriptEngine
0 commit comments