@@ -1375,7 +1375,12 @@ class QAST::OperationsJS {
1375
1375
add_simple_op(' curcode' , $ T_OBJ , []);
1376
1376
add_simple_op(' callercode' , $ T_OBJ , []);
1377
1377
1378
- method compile_op ($ comp , $ op , : $ want ) {
1378
+ add_simple_op(' continuationreset' , $ T_OBJ , [$ T_OBJ , $ T_OBJ ], : sideffects, : ctx);
1379
+ add_simple_op(' continuationinvoke' , $ T_OBJ , [$ T_OBJ , $ T_OBJ ], : sideffects, : ctx);
1380
+ add_simple_op(' continuationcontrol' , $ T_OBJ , [$ T_INT , $ T_OBJ , $ T_OBJ ], : sideffects);
1381
+
1382
+ method compile_op ($ comp , $ op , : $ want , : $ cps ) {
1383
+ # TODO cps
1379
1384
my str $ name := $ op . op;
1380
1385
if nqp ::existskey(% ops , $ name ) {
1381
1386
% ops {$ name }($ comp , $ op , : $ want );
@@ -2274,13 +2279,17 @@ class QAST::CompilerJS does DWIMYNameMangling does SerializeOnce {
2274
2279
}
2275
2280
}
2276
2281
2277
- method compile_sig (@ params ) {
2282
+ method compile_sig (@ params , : $ cps ) {
2278
2283
my $ slurpy_named ; # *%foo
2279
2284
my $ slurpy ; # *@foo
2280
2285
2281
2286
my @ sig := [' caller_ctx' ,' _NAMED' ];
2282
2287
my @ setup ;
2283
2288
2289
+ if $ cps {
2290
+ @ sig . push (' cont' );
2291
+ }
2292
+
2284
2293
my $ bind_named := ' ' ;
2285
2294
for @ params {
2286
2295
if $ _ . slurpy {
@@ -2491,7 +2500,7 @@ class QAST::CompilerJS does DWIMYNameMangling does SerializeOnce {
2491
2500
# TODO
2492
2501
}
2493
2502
2494
- proto method as_js ($ node , : $ want ) {
2503
+ proto method as_js ($ node , : $ want , : $ cps ) {
2495
2504
if nqp :: defined ($ want ) {
2496
2505
if nqp ::istype($ node , QAST ::Want) {
2497
2506
self . NYI(" QAST::Want" );
@@ -2518,7 +2527,7 @@ class QAST::CompilerJS does DWIMYNameMangling does SerializeOnce {
2518
2527
}
2519
2528
2520
2529
# TODO save the value of the last statement
2521
- method compile_all_the_statements (QAST ::Stmts $ node , $ want , : $ resultchild ) {
2530
+ method compile_all_the_statements (QAST ::Stmts $ node , $ want , : $ resultchild , : $ cps ) {
2522
2531
my @ setup ;
2523
2532
my @ stmts := $ node . list;
2524
2533
my int $ n := + @ stmts ;
@@ -2533,15 +2542,15 @@ class QAST::CompilerJS does DWIMYNameMangling does SerializeOnce {
2533
2542
2534
2543
my int $ i := 0 ;
2535
2544
for @ stmts {
2536
- my $ chunk := self . as_js($ _ , : want($ i == $ resultchild ?? $ want !! $ T_VOID ));
2545
+ my $ chunk := self . as_js($ _ , : want($ i == $ resultchild ?? $ want !! $ T_VOID ), : $ cps );
2537
2546
$ result := $ chunk . expr if $ i == $ resultchild ;
2538
2547
nqp :: push (@ setup , $ chunk );
2539
2548
$ i := $ i + 1 ;
2540
2549
}
2541
2550
Chunk. new ($ want , $ result , @ setup );
2542
2551
}
2543
2552
2544
- multi method as_js (QAST ::Block $ node , : $ want ) {
2553
+ multi method as_js (QAST ::Block $ node , : $ want , : $ cps ) {
2545
2554
my $ outer := try $ * BLOCK ;
2546
2555
my $ outer_loop := try $ * LOOP ;
2547
2556
self . compile_block($ node , $ outer , $ outer_loop , : $ want );
@@ -2640,6 +2649,8 @@ class QAST::CompilerJS does DWIMYNameMangling does SerializeOnce {
2640
2649
2641
2650
@ clone_inners . push (" $ reg = $ cuid .closure" );
2642
2651
@ clone_inners . push (% * BLOCKS_DONE {$ _ . key });
2652
+ @ clone_inners . push (" .cps" );
2653
+ @ clone_inners . push (% * BLOCKS_DONE_CPS {$ _ . key });
2643
2654
@ clone_inners . push (" ;\n " );
2644
2655
}
2645
2656
Chunk. void (| @ clone_inners );
@@ -2680,11 +2691,27 @@ class QAST::CompilerJS does DWIMYNameMangling does SerializeOnce {
2680
2691
2681
2692
my $ sig := self . compile_sig($ * BLOCK . params);
2682
2693
2694
+ my @ function := [
2695
+ " function({ $ sig . expr} ) \{\n " ,
2696
+ self . setup_setting($ node ),
2697
+ self . declare_js_vars($ * BLOCK . tmps),
2698
+ self . declare_js_vars($ * BLOCK . js_lexicals),
2699
+ $ create_ctx ,
2700
+ $ sig ,
2701
+ self . clone_inners($ * BLOCK ),
2702
+ self . capture_inners($ * BLOCK ),
2703
+ $ stmts ,
2704
+ " return { $ stmts . expr} ;\n " ,
2705
+ " \} "
2706
+ ];
2683
2707
2708
+ # The CPS version
2684
2709
2710
+ my $ stmts_cps := self . compile_all_the_statements($ node , $ body_want , : cps);
2685
2711
2712
+ my $ sig_cps := self . compile_sig($ * BLOCK . params, : cps);
2686
2713
2687
- my @ function := [
2714
+ my @ function_cps := [
2688
2715
" function({ $ sig . expr} ) \{\n " ,
2689
2716
self . setup_setting($ node ),
2690
2717
self . declare_js_vars($ * BLOCK . tmps),
@@ -2699,6 +2726,9 @@ class QAST::CompilerJS does DWIMYNameMangling does SerializeOnce {
2699
2726
];
2700
2727
2701
2728
% * BLOCKS_DONE {$ node . cuid} := Chunk. void (" (" , | @ function , " )" );
2729
+
2730
+ % * BLOCKS_DONE_CPS {$ node . cuid} := Chunk. void (" (" , | @ function_cps , " )" );
2731
+
2702
2732
2703
2733
if self . is_block_part_of_sc($ node ) {
2704
2734
if $ node . blocktype eq ' immediate' {
@@ -2764,29 +2794,29 @@ class QAST::CompilerJS does DWIMYNameMangling does SerializeOnce {
2764
2794
" var $ name = new nqp.Ctx(caller_ctx, { self . outer_ctx} );\n " ;
2765
2795
}
2766
2796
2767
- multi method as_js (QAST ::IVal $ node , : $ want ) {
2797
+ multi method as_js (QAST ::IVal $ node , : $ want , : $ cps ) {
2768
2798
Chunk. new ($ T_INT ,' (' ~ $ node . value ()~ ' )' ,[],: $ node );
2769
2799
}
2770
2800
2771
- multi method as_js (QAST ::NVal $ node , : $ want ) {
2801
+ multi method as_js (QAST ::NVal $ node , : $ want , : $ cps ) {
2772
2802
Chunk. new ($ T_NUM ,' (' ~ $ node . value ()~ ' )' ,[],: $ node );
2773
2803
}
2774
2804
2775
- multi method as_js (QAST ::SVal $ node , : $ want ) {
2805
+ multi method as_js (QAST ::SVal $ node , : $ want , : $ cps ) {
2776
2806
Chunk. new ($ T_STR ,quote_string($ node . value ()),[],: $ node );
2777
2807
}
2778
2808
2779
- multi method as_js (QAST ::BVal $ node , : $ want ) {
2809
+ multi method as_js (QAST ::BVal $ node , : $ want , : $ cps ) {
2780
2810
self . as_js($ node . value , : $ want );
2781
2811
}
2782
2812
2783
2813
# Helps with register allocation on other backends
2784
2814
# We don't do allocate registers so just ignore that
2785
- multi method as_js (QAST ::Stmt $ node , : $ want ) {
2786
- self . as_js($ node [0 ], : $ want );
2815
+ multi method as_js (QAST ::Stmt $ node , : $ want , : $ cps ) {
2816
+ self . as_js($ node [0 ], : $ want , : $ cps );
2787
2817
}
2788
2818
2789
- multi method as_js (QAST ::Stmts $ node , : $ want ) {
2819
+ multi method as_js (QAST ::Stmts $ node , : $ want , : $ cps ) {
2790
2820
# for performance purposes we use the native js lexicals as much as possible, that means we need hacks for things that other backends can do easily with all the various ctx ops
2791
2821
if self . is_ctxsave($ node ) {
2792
2822
my @ lexicals ;
@@ -2795,17 +2825,17 @@ class QAST::CompilerJS does DWIMYNameMangling does SerializeOnce {
2795
2825
}
2796
2826
Chunk. void (" nqp.ctxsave(\{ { nqp :: join (' ,' , @ lexicals )} \} );\n " );
2797
2827
} else {
2798
- self . compile_all_the_statements($ node , $ want );
2828
+ self . compile_all_the_statements($ node , $ want , : $ cps );
2799
2829
}
2800
2830
}
2801
2831
2802
- multi method as_js (QAST ::VM $ node , : $ want ) {
2832
+ multi method as_js (QAST ::VM $ node , : $ want , : $ cps ) {
2803
2833
# We ignore QAST::VM as we don't support a js specific one, and the ones nqp generate contain parrot specific stuff we don't care about.
2804
2834
Chunk. new ($ T_VOID ,' ' ,[]);
2805
2835
}
2806
2836
2807
- multi method as_js (QAST ::Op $ node , : $ want ) {
2808
- QAST ::OperationsJS. compile_op(self , $ node , : $ want );
2837
+ multi method as_js (QAST ::Op $ node , : $ want , : $ cps ) {
2838
+ QAST ::OperationsJS. compile_op(self , $ node , : $ want , : $ cps );
2809
2839
}
2810
2840
2811
2841
method create_sc ($ ast ) {
@@ -2894,7 +2924,7 @@ class QAST::CompilerJS does DWIMYNameMangling does SerializeOnce {
2894
2924
}
2895
2925
}
2896
2926
2897
- multi method as_js (QAST ::CompUnit $ node , : $ want ) {
2927
+ multi method as_js (QAST ::CompUnit $ node , : $ want , : $ cps ) {
2898
2928
# Should have a single child which is the outer block.
2899
2929
if + @ ($ node ) != 1 || ! nqp ::istype($ node [0 ], QAST ::Block) {
2900
2930
nqp ::die(" QAST::CompUnit should have one child that is a QAST::Block" );
@@ -2907,6 +2937,7 @@ class QAST::CompilerJS does DWIMYNameMangling does SerializeOnce {
2907
2937
2908
2938
# Blocks we've seen while compiling.
2909
2939
my % * BLOCKS_DONE ;
2940
+ my % * BLOCKS_DONE_CPS ;
2910
2941
2911
2942
# A fake outer block
2912
2943
my $ * BLOCK := BlockInfo. new (NQPMu, NQPMu);
@@ -2988,12 +3019,13 @@ class QAST::CompilerJS does DWIMYNameMangling does SerializeOnce {
2988
3019
}
2989
3020
}
2990
3021
2991
- multi method as_js (QAST ::Var $ node , : $ want ) {
3022
+ multi method as_js (QAST ::Var $ node , : $ want , : $ cps ) {
2992
3023
self . declare_var($ node );
2993
3024
self . compile_var($ node );
2994
3025
}
2995
3026
2996
- multi method as_js (QAST ::VarWithFallback $ node , : $ want ) {
3027
+ multi method as_js (QAST ::VarWithFallback $ node , : $ want , : $ cps ) {
3028
+ # TODO CPS
2997
3029
my $ var := self . compile_var($ node );
2998
3030
if $ var . type == $ T_OBJ {
2999
3031
my $ fallback := self . as_js($ node . fallback, : want($ T_OBJ ));
@@ -3009,7 +3041,8 @@ class QAST::CompilerJS does DWIMYNameMangling does SerializeOnce {
3009
3041
}
3010
3042
}
3011
3043
3012
- multi method as_js (QAST ::Regex $ node , : $ want ) {
3044
+ multi method as_js (QAST ::Regex $ node , : $ want , : $ cps ) {
3045
+ # TODO CPS
3013
3046
RegexCompiler. new (compiler => self ). compile($ node );
3014
3047
}
3015
3048
@@ -3020,17 +3053,17 @@ class QAST::CompilerJS does DWIMYNameMangling does SerializeOnce {
3020
3053
" nqp.wval({ quote_string($ handle )} ,$ idx )" ;
3021
3054
}
3022
3055
3023
- multi method as_js (QAST ::WVal $ node , : $ want ) {
3056
+ multi method as_js (QAST ::WVal $ node , : $ want , : $ cps ) {
3024
3057
Chunk. new ($ T_OBJ , self . value_as_js($ node . value ), []);
3025
3058
}
3026
3059
3027
3060
method var_is_lexicalish (QAST ::Var $ var ) {
3028
3061
$ var . scope eq ' lexical' || $ var . scope eq ' typevar' ;
3029
3062
}
3030
3063
3031
- method as_js_clear_bindval ($ node , : $ want ) {
3064
+ method as_js_clear_bindval ($ node , : $ want , : $ cps ) {
3032
3065
my $ * BINDVAL := 0 ;
3033
- self . as_js($ node , : $ want );
3066
+ self . as_js($ node , : $ want , : $ cps );
3034
3067
}
3035
3068
3036
3069
method is_dynamic_var ($ var ) {
@@ -3053,10 +3086,10 @@ class QAST::CompilerJS does DWIMYNameMangling does SerializeOnce {
3053
3086
Chunk. new ($ T_OBJ , " nqp.op.atpos({ $ array_chunk . expr} ,{ $ index_chunk . expr} )" , [$ array_chunk , $ index_chunk ], : node($ node ));
3054
3087
}
3055
3088
3056
- method compile_var (QAST ::Var $ var ) {
3089
+ method compile_var (QAST ::Var $ var , : $ cps ) {
3057
3090
if self . var_is_lexicalish($ var ) && self . is_dynamic_var($ var ) {
3058
3091
if $ * BINDVAL {
3059
- my $ bindval := self . as_js_clear_bindval($ * BINDVAL , : want($ T_OBJ ));
3092
+ my $ bindval := self . as_js_clear_bindval($ * BINDVAL , : want($ T_OBJ ), : $ cps );
3060
3093
if $ var . decl eq ' var' {
3061
3094
self . stored_result(Chunk. new ($ T_OBJ , " ({ $ * CTX } [{ quote_string($ var . name )} ] = { $ bindval . expr} )" , [$ bindval ]));
3062
3095
} else {
@@ -3075,7 +3108,7 @@ class QAST::CompilerJS does DWIMYNameMangling does SerializeOnce {
3075
3108
if $ * BINDVAL {
3076
3109
# TODO better source mapping
3077
3110
# TODO use the proper type
3078
- my $ bindval := self . as_js_clear_bindval($ * BINDVAL , : want($ T_OBJ ));
3111
+ my $ bindval := self . as_js_clear_bindval($ * BINDVAL , : want($ T_OBJ ), : $ cps );
3079
3112
Chunk. new ($ type ,$ mangled , [$ bindval ,' (' ~ $ mangled ~ ' = (' ~ $ bindval . expr ~ " ));\n " ]);
3080
3113
} else {
3081
3114
# TODO get the proper type
@@ -3110,17 +3143,17 @@ class QAST::CompilerJS does DWIMYNameMangling does SerializeOnce {
3110
3143
# TODO take second argument into account
3111
3144
# TODO figure out if the second argument can be always assumed to be a WVal
3112
3145
# TODO types
3113
- my $ self := self . as_js_clear_bindval($ var [0 ], : want($ T_OBJ ));
3146
+ my $ self := self . as_js_clear_bindval($ var [0 ], : want($ T_OBJ ), : $ cps );
3114
3147
my $ attr := Chunk. new ($ T_OBJ , " { $ self . expr} [{ quote_string($ var . name )} ]" , [$ self ]);
3115
3148
if $ * BINDVAL {
3116
- my $ bindval := self . as_js_clear_bindval($ * BINDVAL , : want($ T_OBJ ));
3149
+ my $ bindval := self . as_js_clear_bindval($ * BINDVAL , : want($ T_OBJ ), : $ cps );
3117
3150
Chunk. new ($ T_OBJ , $ bindval . expr, [$ attr , $ bindval , " { $ attr . expr} = { $ bindval . expr} ;\n " ]);
3118
3151
} else {
3119
3152
$ attr ;
3120
3153
}
3121
3154
} elsif ($ var . scope eq ' contextual' ) {
3122
3155
if $ * BINDVAL {
3123
- my $ bindval := self . as_js_clear_bindval($ * BINDVAL , : want($ T_OBJ ));
3156
+ my $ bindval := self . as_js_clear_bindval($ * BINDVAL , : want($ T_OBJ ), : $ cps );
3124
3157
self . stored_result(Chunk. new ($ T_OBJ , " { $ * CTX } .bind_dynamic({ quote_string($ var . name )} ,{ $ bindval . expr} )" , [$ bindval ]));
3125
3158
} else {
3126
3159
Chunk. new ($ T_OBJ , " { $ * CTX } .lookup_dynamic({ quote_string($ var . name )} )" , []);
@@ -3148,7 +3181,7 @@ class QAST::CompilerJS does DWIMYNameMangling does SerializeOnce {
3148
3181
3149
3182
3150
3183
3151
- multi method as_js ($ unknown , : $ want ) {
3184
+ multi method as_js ($ unknown , : $ want , : $ cps ) {
3152
3185
self . NYI(" Unimplemented QAST node type: " ~ $ unknown . HOW . name ($ unknown ));
3153
3186
}
3154
3187
0 commit comments