Skip to content

Commit fd6cc07

Browse files
committed
Add js.try_catch JS virtualization method
1 parent 672efd6 commit fd6cc07

File tree

11 files changed

+122
-11
lines changed

11 files changed

+122
-11
lines changed

core/shared/main/scala/utils/package.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,8 @@ package object utils {
228228
def TODO(msg: Any, cond: Bool): Unit = if (cond) TODO(msg)
229229
def die: Nothing = lastWords("Program reached an unexpected state.")
230230
def lastWords(msg: String): Nothing = throw new Exception(s"Internal Error: $msg")
231-
def wat(msg: String, wat: Any): Nothing = lastWords(s"$msg ($wat)")
231+
def wat(msg: String, obj: Any): Nothing = lastWords(s"$msg ($obj)")
232+
def wat(obj: Any): Nothing = wat(s"unexpected value", obj)
232233

233234
/** To make Scala unexhaustivity warnings believed to be spurious go away,
234235
* while clearly indicating the intent. */

hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,12 @@ class Lowering()(using Config, TL, Raise, State, Ctx):
190190
}(Nil)
191191
case ref @ st.Ref(sym) =>
192192
sym match
193+
case ctx.builtins.source.bms | ctx.builtins.js.bms =>
194+
raise:
195+
ErrorReport(
196+
msg"Module '${sym.nme}' is virtual (i.e., \"compiler fiction\"); cannot be used directly" -> t.toLoc ::
197+
Nil, S(t), source = Diagnostic.Source.Compilation)
198+
return End("error")
193199
case sym: BuiltinSymbol =>
194200
warnStmt
195201
if sym.binary then
@@ -318,6 +324,8 @@ class Lowering()(using Config, TL, Raise, State, Ctx):
318324
subTerm_nonTail(arg): ar =>
319325
k(Call(fr, Arg(spread = true, ar) :: Nil)(isMlsFun, true))
320326
f match
327+
case t if t.symbol.isDefined && (t.symbol.get is ctx.builtins.js.try_catch) =>
328+
conclude(Value.Ref(State.runtimeSymbol).selN(Tree.Ident("try_catch")))
321329
// * Due to whacky JS semantics, we need to make sure that selections leading to a call
322330
// * are preserved in the call and not moved to a temporary variable.
323331
case sel @ Sel(prefix, nme) =>

hkmc2/shared/src/main/scala/hkmc2/semantics/Elaborator.scala

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -103,15 +103,20 @@ object Elaborator:
103103
val Object = assumeBuiltinCls("Object")
104104
val untyped = assumeBuiltinTpe("untyped")
105105
// println(s"Builtins: $Int, $Num, $Str, $untyped")
106-
object source:
107-
private val module = assumeBuiltinMod("source")
108-
private def assumeObject(nme: Str): BlockMemberSymbol =
106+
class VirtualModule(val module: ModuleSymbol):
107+
val bms = getBuiltin(module.nme) match
108+
case S(Ctx.RefElem(bms: BlockMemberSymbol)) => bms
109+
case huh => wat(huh)
110+
protected def assumeObject(nme: Str): BlockMemberSymbol =
109111
module.tree.definedSymbols.get(nme).getOrElse:
110112
throw new NoSuchElementException:
111113
s"builtin module symbol source.$nme. we have"
114+
object source extends VirtualModule(assumeBuiltinMod("source")):
112115
val line = assumeObject("line")
113116
val name = assumeObject("name")
114117
val file = assumeObject("file")
118+
object js extends VirtualModule(assumeBuiltinMod("js")):
119+
val try_catch = assumeObject("try_catch")
115120
def getBuiltinOp(op: Str): Opt[Str] =
116121
if getBuiltin(op).isDefined then builtinBinOps.get(op) else N
117122
/** Classes that do not use `instanceof` in pattern matching. */

hkmc2/shared/src/test/mlscript-compile/Iter.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,7 @@ Iter1 = class Iter {
307307
next = tmp1;
308308
scrut = next.done;
309309
if (scrut === true) {
310-
throw new globalThis.Error.class("Empty iterator");
310+
throw new globalThis.Error("Empty iterator");
311311
} else {
312312
tmp2 = runtime.Unit;
313313
}

hkmc2/shared/src/test/mlscript-compile/Runtime.mjs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import runtime from "./Runtime.mjs";
2+
import RuntimeJS from "./RuntimeJS.mjs";
23
let Runtime1;
34
Runtime1 = class Runtime {
45
static {
@@ -10,6 +11,7 @@ Runtime1 = class Runtime {
1011
};
1112
this.Unit = new Unit$class;
1213
this.Unit.class = Unit$class;
14+
this.try_catch = RuntimeJS.try_catch;
1315
this.MatchResult = function MatchResult(captures1) {
1416
return new MatchResult.class(captures1);
1517
};

hkmc2/shared/src/test/mlscript-compile/Runtime.mls

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import "./RuntimeJS.mjs"
12

23

34
module Runtime with ...
@@ -20,6 +21,9 @@ fun deboundMethod(mtdName, clsName) =
2021
"[debinding error] Method '" + mtdName + "' of class '" + clsName + "' was accessed without being called."
2122

2223

24+
val try_catch = RuntimeJS.try_catch
25+
26+
2327
// For `pattern` definitions
2428
class MatchResult(captures)
2529
class MatchFailure(errors)
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
2+
const RuntimeJS = {
3+
try_catch(computation, onError) {
4+
try { return computation() }
5+
catch (error) { return onError(error) }
6+
}
7+
}
8+
9+
export default RuntimeJS;
10+

hkmc2/shared/src/test/mlscript/bbml/bbPrelude.mls

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,16 @@ declare class Str(length: Int, concat: Str -> Str)
2929

3030
declare class Error(msg: Str)
3131

32-
declare module Predef
3332

3433
declare module source with
35-
object line: Int
36-
object name: Str
37-
object file: Str
34+
object
35+
line: Int
36+
name: Str
37+
file: Str
38+
39+
declare module js with
40+
fun try_catch
41+
3842

3943
declare fun run: [T] -> CodeBase[out T, out Nothing, out Any] -> T
4044
declare fun log: Str -> Any

hkmc2/shared/src/test/mlscript/decls/Prelude.mls

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ declare class String
1515
declare class RegExp
1616
declare class Set
1717
declare class Map
18-
declare class Error(msg)
19-
declare class TypeError(msg)
18+
declare class Error
19+
declare class TypeError
2020
declare class Symbol
2121

2222
// MLscript-specific types
@@ -49,4 +49,8 @@ declare module source with
4949
name
5050
file
5151

52+
declare module js with
53+
fun
54+
try_catch
55+
5256

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
:js
2+
3+
4+
// * `try`/`catch`
5+
6+
js.try_catch(() => 123, id)
7+
//│ = 123
8+
9+
js.try_catch(() => 123(), id)
10+
//│ = TypeError: 123 is not a function
11+
12+
js.try_catch of
13+
() => 123()
14+
case { TypeError then 666, e then throw e }
15+
//│ = 666
16+
17+
:re
18+
js.try_catch of
19+
() => throw Error("whoops")
20+
case TypeError then 666
21+
//│ ═══[RUNTIME ERROR] Error: match error
22+
23+
:re
24+
js.try_catch of
25+
() => throw Error("whoops")
26+
case { TypeError then 666, e then throw e }
27+
//│ ═══[RUNTIME ERROR] Error: whoops
28+
29+

0 commit comments

Comments
 (0)