Skip to content

Commit 6937b9e

Browse files
committed
[js] Support throwing and handling control exceptions.
1 parent 622ee56 commit 6937b9e

File tree

4 files changed

+91
-25
lines changed

4 files changed

+91
-25
lines changed

src/vm/js/Operations.nqp

Lines changed: 17 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -753,27 +753,19 @@ class QAST::OperationsJS {
753753
my str $try_ret := '';
754754
my $set_try_ret := '';
755755

756-
my $handle := '';
756+
757+
my @handlers;
758+
757759
for @children -> $type, $handler {
758-
if $type eq 'CATCH' {
759-
my $catch_body := $comp.as_js($handler, :want($T_OBJ));
760-
$handle := Chunk.void(
761-
"$unwind_marker = \{\};\n",
762-
"$handler_ctx.\$\$CATCH = function() \{\n",
763-
$catch_body,
764-
"return {$catch_body.expr};\n",
765-
"\};\n",
766-
"$handler_ctx.unwind = $unwind_marker;\n"
767-
);
768-
}
769-
elsif $type eq 'CONTROL' {
770-
# STUB - we are nooping this, needs to be implemented
771-
}
772-
else {
773-
return $comp.NYI("Not implemented type with handle: $type");
774-
}
760+
my $catch_body := $comp.as_js($handler, :want($T_OBJ));
761+
@handlers.push("$handler_ctx.\$\${$type} = function() \{\n");
762+
@handlers.push($catch_body);
763+
@handlers.push( "return {$catch_body.expr};\n" ~ "\};\n");
775764
}
776765

766+
@handlers.push("$unwind_marker = \{\};\n");
767+
@handlers.push("$handler_ctx.unwind = $unwind_marker;\n");
768+
777769
{
778770
my $*CTX := $handler_ctx;
779771
my $body := $comp.as_js($protected, :$want);
@@ -801,7 +793,7 @@ class QAST::OperationsJS {
801793

802794
return Chunk.new($want, $try_ret, [
803795
"var $handler_ctx = new nqp.Ctx($outer_ctx, $outer_ctx, $outer_ctx.\$\$callThis, $outer_ctx.\$\$codeRefAttr);\n",
804-
$handle,
796+
Chunk.void(|@handlers),
805797
"try \{",
806798
$body,
807799
# HACK we need to check $body.type if we handle something like return
@@ -831,7 +823,12 @@ class QAST::OperationsJS {
831823
add_simple_op('setpayload', $T_OBJ, [$T_OBJ, $T_OBJ], :side_effects);
832824
add_simple_op('getpayload', $T_OBJ, [$T_OBJ, $T_OBJ]);
833825

834-
add_simple_op('setmessage', $T_OBJ, [$T_OBJ, $T_OBJ], :side_effects);
826+
add_simple_op('setmessage', $T_STR, [$T_OBJ, $T_STR], :side_effects);
827+
add_simple_op('getmessage', $T_STR, [$T_OBJ]);
828+
829+
add_simple_op('setextype', $T_INT, [$T_OBJ, $T_INT], :side_effects);
830+
add_simple_op('getextype', $T_INT, [$T_OBJ]);
831+
835832
add_simple_op('getmessage', $T_STR, [$T_OBJ]);
836833

837834
add_simple_op('newexception', $T_OBJ, [], :side_effects);

src/vm/js/const_map.nqp

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,18 @@ my %const_map := nqp::hash(
2222
'HLL_ROLE_HASH', 5,
2323
'HLL_ROLE_CODE', 6,
2424

25-
'CONTROL_TAKE', 32,
26-
'CONTROL_LAST', 16,
2725
'CONTROL_NEXT', 4,
2826
'CONTROL_REDO', 8,
27+
'CONTROL_LAST', 16,
2928
'CONTROL_RETURN', 32,
30-
'CONTROL_SUCCEED', 128,
31-
'CONTROL_PROCEED', 256,
32-
'CONTROL_WARN', 64,
29+
'CONTROL_TAKE', 128,
30+
'CONTROL_WARN', 256,
31+
'CONTROL_SUCCEED', 512,
32+
'CONTROL_PROCEED', 1024,
33+
'CONTROL_LABELED', 4096,
34+
'CONTROL_AWAIT', 8192,
35+
'CONTROL_EMIT', 16384,
36+
'CONTROL_DONE', 32768,
3337

3438
'STAT_EXISTS', 0,
3539
'STAT_FILESIZE', 1,

src/vm/js/nqp-runtime/core.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1147,3 +1147,16 @@ op.split = function(hllName, separator, string) {
11471147
op.exception = function() {
11481148
return exceptionsStack[exceptionsStack.length - 1];
11491149
};
1150+
1151+
op.setextype = function(exception, category) {
1152+
exception.$$category = category;
1153+
return exception;
1154+
};
1155+
1156+
op.getextype = function(exception) {
1157+
if (exception.$$category === undefined) {
1158+
return 0;
1159+
} else {
1160+
return exception.$$category;
1161+
}
1162+
};

src/vm/js/nqp-runtime/ctx.js

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,21 @@ var NQPObject = require('./nqp-object.js');
66
var Null = require('./null.js');
77
var exceptionsStack = require('./exceptions-stack.js');
88

9+
var categoryToName = {
10+
4: 'NEXT',
11+
8: 'REDO',
12+
16: 'LAST',
13+
32: 'RETURN',
14+
128: 'TAKE',
15+
256: 'WARN',
16+
512: 'SUCCEED',
17+
1024: 'PROCEED',
18+
4096: 'LABELED',
19+
8192: 'AWAIT',
20+
16384: 'EMIT',
21+
32768: 'DONE'
22+
};
23+
924
class Ctx extends NQPObject {
1025
constructor(callerCtx, outerCtx, callThis, codeRefAttr) {
1126
super();
@@ -19,8 +34,45 @@ class Ctx extends NQPObject {
1934
return (this.$$callThis instanceof CodeRef ? this.$$callThis : this.$$callThis[this.$$codeRefAttr]);
2035
}
2136

37+
propagateControlException(exception) {
38+
var handler = '$$' + categoryToName[exception.$$category];
39+
40+
var ctx = this;
41+
42+
while (ctx) {
43+
if (ctx[handler] || ctx.$$CONTROL) {
44+
exception.caught = ctx;
45+
exception.resume = false;
46+
ctx.exception = exception;
47+
48+
exceptionsStack.push(exception);
49+
try {
50+
if (ctx[handler]) {
51+
ctx.unwind.ret = ctx[handler]();
52+
} else {
53+
ctx.unwind.ret = ctx.$$CONTROL();
54+
}
55+
} finally {
56+
exceptionsStack.pop();
57+
}
58+
59+
if (exception.resume) {
60+
return;
61+
} else {
62+
throw ctx.unwind;
63+
}
64+
}
65+
ctx = ctx.$$caller;
66+
}
67+
68+
throw exception;
69+
}
70+
2271
propagateException(exception) {
72+
if (exception.$$category) this.propagateControlException(exception);
73+
2374
var ctx = this;
75+
2476
while (ctx) {
2577
if (ctx.$$CATCH) {
2678
exception.caught = ctx;

0 commit comments

Comments
 (0)