@@ -23,6 +23,7 @@ abstract class CodeBuilder:
23
23
24
24
25
25
class JSBuilder (using TL , State , Ctx ) extends CodeBuilder :
26
+ import JSBuilder .*
26
27
27
28
def checkMLsCalls : Bool = false
28
29
def checkSelections : Bool = false
@@ -76,6 +77,8 @@ class JSBuilder(using TL, State, Ctx) extends CodeBuilder:
76
77
else summon[Scope ].findThis_!(ts)
77
78
case _ => summon[Scope ].lookup_!(l)
78
79
80
+ def runtimeVar (using Raise , Scope ): Document = getVar(State .runtimeSymbol)
81
+
79
82
def argument (a : Arg )(using Raise , Scope ): Document =
80
83
if a.spread then doc " ... ${result(a.value)}" else result(a.value)
81
84
@@ -94,7 +97,7 @@ class JSBuilder(using TL, State, Ctx) extends CodeBuilder:
94
97
95
98
def result (r : Result )(using Raise , Scope ): Document = r match
96
99
case Value .This (sym) => summon[Scope ].findThis_!(sym)
97
- case Value .Lit (Tree .StrLit (value)) => JSBuilder . makeStringLiteral(value)
100
+ case Value .Lit (Tree .StrLit (value)) => makeStringLiteral(value)
98
101
case Value .Lit (lit) => lit.idStr
99
102
case Value .Ref (l : BuiltinSymbol ) =>
100
103
if l.nullary then l.nme
@@ -128,20 +131,20 @@ class JSBuilder(using TL, State, Ctx) extends CodeBuilder:
128
131
val argsDoc = args.map(argument).mkDocument(" , " )
129
132
if c.isMlsFun
130
133
then if checkMLsCalls
131
- then doc " ${getVar( State .runtimeSymbol)} .checkCall( ${base}( ${argsDoc})) "
134
+ then doc " $runtimeVar .checkCall( ${base}( ${argsDoc})) "
132
135
else doc " ${base}( ${argsDoc}) "
133
- else doc " ${getVar( State .runtimeSymbol)} .safeCall( ${base}( ${argsDoc})) "
136
+ else doc " $runtimeVar .safeCall( ${base}( ${argsDoc})) "
134
137
case Value .Lam (ps, bod) => scope.nest givenIn :
135
138
val (params, bodyDoc) = setupFunction(none, ps, bod)
136
139
doc " ( $params) => ${ braced(bodyDoc) }"
137
140
case Select (qual, id) =>
138
141
val name = id.name
139
142
doc " ${result(qual)}${
140
- if JSBuilder . isValidFieldName(name)
143
+ if isValidFieldName(name)
141
144
then doc " . $name"
142
145
else name.toIntOption match
143
146
case S (index) => s " [ $index] "
144
- case N => s " [ ${JSBuilder . makeStringLiteral(name)}] "
147
+ case N => s " [ ${makeStringLiteral(name)}] "
145
148
}"
146
149
case DynSelect (qual, fld, ai) =>
147
150
doc " ${result(qual)}[ ${result(fld)}] "
@@ -199,7 +202,9 @@ class JSBuilder(using TL, State, Ctx) extends CodeBuilder:
199
202
val name = if sym.nameIsMeaningful then S (sym.nme) else N
200
203
val (params, bodyDoc) = setupFunction(name, ps, result)
201
204
if sym.nameIsMeaningful then
202
- doc " ${getVar(sym)} = function ${sym.nme}( $params) ${ braced(bodyDoc) }; "
205
+ // If the name is not valid JavaScript identifiers, do not use it in the generated function.
206
+ val nme = if isValidIdentifier(sym.nme) then sym.nme else " "
207
+ doc " ${getVar(sym)} = function $nme( $params) ${ braced(bodyDoc) }; "
203
208
else
204
209
// in JS, let name = (0, function (args) => {} ) prevents function's name from being bound to `name`
205
210
doc " ${getVar(sym)} = (undefined, function ( $params) ${ braced(bodyDoc) }); "
@@ -262,9 +267,9 @@ class JSBuilder(using TL, State, Ctx) extends CodeBuilder:
262
267
.flatMap:
263
268
case td @ FunDefn (_, _, ps :: pss, bod) => S :
264
269
doc " # get ${td.sym.nme}$$ __checkNotMethod() { ${
265
- getVar( State .runtimeSymbol)
266
- }.deboundMethod( ${JSBuilder . makeStringLiteral(td.sym.nme)}, ${
267
- JSBuilder . makeStringLiteral(sym.nme)
270
+ runtimeVar
271
+ }.deboundMethod( ${makeStringLiteral(td.sym.nme)}, ${
272
+ makeStringLiteral(sym.nme)
268
273
}); } "
269
274
case _ => N
270
275
.mkDocument(" " )
@@ -286,10 +291,10 @@ class JSBuilder(using TL, State, Ctx) extends CodeBuilder:
286
291
else doc """ # ${mtdPrefix}toString() { return " ${sym.nme}${
287
292
if paramsOpt.isEmpty then doc """ " """
288
293
else doc """ (" + ${
289
- ctorFields.headOption.fold(" \"\" " )(f => " globalThis.Predef .render(this" + fieldSelect(f._1.name) + " )" )
294
+ ctorFields.headOption.fold(" \"\" " )(f => " runtime .render(this" + fieldSelect(f._1.name) + " )" )
290
295
}${
291
296
ctorFields.tailOption.fold(" " )(_.map(f =>
292
- """ + ", " + globalThis.Predef .render(this""" + fieldSelect(f._1.name) + " )" ).mkString)
297
+ """ + ", " + runtime .render(this""" + fieldSelect(f._1.name) + " )" ).mkString)
293
298
} + ")" """
294
299
}; } """
295
300
} #} # } "
@@ -325,7 +330,8 @@ class JSBuilder(using TL, State, Ctx) extends CodeBuilder:
325
330
val funBod = pss.foldRight(bod):
326
331
case (psDoc, doc_) => doc " ( $psDoc) => $doc_"
327
332
val funBodRet = if pss.isEmpty then funBod else braced(doc " # return $funBod" )
328
- S (doc " function ${sym.nme}( $ps) ${ funBodRet }" )
333
+ val nme = if isValidIdentifier(sym.nme) then sym.nme else " "
334
+ S (doc " function $nme( $ps) ${ funBodRet }" )
329
335
case Nil => N
330
336
331
337
ownr match
@@ -464,17 +470,14 @@ class JSBuilder(using TL, State, Ctx) extends CodeBuilder:
464
470
465
471
def worksheet (p : Program )(using Raise , Scope ): (Document , Document ) =
466
472
reserveNames(p)
467
- p.imports.foreach: i =>
468
- i._1 -> scope.allocateName(i._1)
469
- val imps = p.imports.map: i =>
470
- val v = doc " this. ${getVar(i._1)}"
471
- doc """ $v = await import(" ${i._2.toString
472
- }"); # if ( $v.default !== undefined) $v = $v.default; """
473
- blockPreamble(p.main) -> (imps.mkDocument(doc " # " ) :/: returningTerm(p.main, endSemi = false ).stripBreaks)
473
+ lazy val imps = p.imports.map: i =>
474
+ doc """ ${getVar(i._1)} = await import(" ${i._2.toString}").then(m => m.default ?? m); """
475
+ blockPreamble(p.imports.map(_._1).toSeq ++ p.main.definedVars.toSeq) ->
476
+ (imps.mkDocument(doc " # " ) :/: returningTerm(p.main, endSemi = false ).stripBreaks)
474
477
475
- def blockPreamble (t : Block )(using Raise , Scope ): Document =
478
+ def blockPreamble (ss : Iterable [ Symbol ] )(using Raise , Scope ): Document =
476
479
// TODO document: mutable var assnts require the lookup
477
- val vars = t.definedVars.toSeq. filter(scope.lookup(_).isEmpty).sortBy(_.uid).iterator.map(l =>
480
+ val vars = ss. filter(scope.lookup(_).isEmpty).toArray .sortBy(_.uid).iterator.map(l =>
478
481
l -> scope.allocateName(l))
479
482
if vars.isEmpty then doc " " else
480
483
doc " # let " :: vars.map: (_, nme) =>
@@ -483,7 +486,7 @@ class JSBuilder(using TL, State, Ctx) extends CodeBuilder:
483
486
:: doc " ; "
484
487
485
488
def block (t : Block , endSemi : Bool )(using Raise , Scope ): Document =
486
- blockPreamble(t) :: returningTerm(t, endSemi)
489
+ blockPreamble(t.definedVars ) :: returningTerm(t, endSemi)
487
490
488
491
def body (t : Block , endSemi : Bool )(using Raise , Scope ): Document = scope.nest givenIn :
489
492
block(t, endSemi)
@@ -616,12 +619,12 @@ trait JSBuilderArgNumSanityChecks(using Config, Elaborator.State)
616
619
val paramRest = params.restParam.map(p => Scope .scope.allocateName(p.sym))
617
620
val paramsStr = Scope .scope.allocateName(functionParamVarargSymbol)
618
621
val functionName = JSBuilder .makeStringLiteral(name.fold(" " )(n => s " ${JSBuilder .escapeStringCharacters(n)}" ))
619
- val checkArgsNum = doc " \n globalThis.Predef .checkArgs( $functionName, ${params.paramCountLB}, ${params.paramCountUB.toString}, $paramsStr.length); "
622
+ val checkArgsNum = doc " \n $runtimeVar .checkArgs( $functionName, ${params.paramCountLB}, ${params.paramCountUB.toString}, $paramsStr.length); "
620
623
val paramsAssign = paramsList.zipWithIndex.map{(nme, i) =>
621
624
doc " \n let ${nme} = ${paramsStr}[ $i]; " }.mkDocument(" " )
622
625
val restAssign = paramRest match
623
626
case N => doc " "
624
- case S (p) => doc " \n let $p = runtime .Tuple.slice( $paramsStr, ${params.paramCountLB}, 0); "
627
+ case S (p) => doc " \n let $p = $runtimeVar .Tuple.slice( $paramsStr, ${params.paramCountLB}, 0); "
625
628
(doc " ... $paramsStr" , doc " $checkArgsNum$paramsAssign$restAssign${this .body(body, endSemi = false )}" )
626
629
else
627
630
super .setupFunction(name, params, body)
0 commit comments