@@ -239,13 +239,30 @@ class QAST::CompilerJS does DWIMYNameMangling does SerializeOnce {
239
239
}
240
240
241
241
242
- method ctx_for_var ($ var ) {
242
+ method ctx_for_var ($ var , : $ from_outer ) {
243
243
my $ info := self ;
244
+ my int $ depth := 0 ;
245
+
246
+ my $ reached_closure_template := 0 ;
247
+
248
+ if $ from_outer {
249
+ $ reached_closure_template := $ info . qast. blocktype ne ' immediate' ;
250
+ $ depth := $ depth + 1 if $ reached_closure_template ;
251
+ $ info := $ info . outer;
252
+ }
253
+
254
+
244
255
while $ info {
256
+ $ reached_closure_template := $ reached_closure_template || $ info . qast. blocktype ne ' immediate' ;
257
+
245
258
if $ info . has_own_variable($ var . name ) {
259
+ % * USED_CTXS {$ info . ctx} := $ depth unless nqp ::existskey(% * USED_CTXS , $ info . ctx);
246
260
return $ info . ctx;
247
261
}
248
262
$ info := $ info . outer;
263
+
264
+ $ depth := $ depth + 1 if $ reached_closure_template ;
265
+
249
266
}
250
267
}
251
268
}
@@ -786,13 +803,32 @@ class QAST::CompilerJS does DWIMYNameMangling does SerializeOnce {
786
803
nqp ::existskey(% ! cuids , $ node . cuid);
787
804
}
788
805
806
+ has % ! serialized_code_ref_info ;
807
+
808
+ my class SerializedCodeRefInfo {
809
+ has $ ! closure_template ;
810
+ has $ ! lexicals_type_info ;
811
+ has $ ! outer_cuid ;
812
+ has $ ! static_lexicals ;
813
+ has $ ! statevars ;
814
+ method outer_cuid () {$ ! outer_cuid }
815
+ method lexicals_type_info () {$ ! lexicals_type_info }
816
+ method closure_template () {$ ! closure_template }
817
+ method static_lexicals () {$ ! static_lexicals }
818
+ method statevars () {$ ! statevars }
819
+ }
820
+
789
821
method setup_cuids () {
790
822
my @ declared ;
791
823
my @ vars ;
792
824
for % ! cuids {
793
825
my str $ var := self . mangled_cuid($ _ . key );
794
826
@ vars . push ($ var );
795
- @ declared . push (" $ var = new nqp.CodeRef({ quote_string($ _ . value . name )} ,{ quote_string($ _ . key )} )" );
827
+
828
+ my int $ has_statevars := nqp ::existskey(% ! serialized_code_ref_info , $ _ . key ) && % ! serialized_code_ref_info {$ _ . key }. statevars;
829
+
830
+ my $ class := $ has_statevars ?? ' CodeRefWithStateVars' !! ' CodeRef' ;
831
+ @ declared . push (" $ var = new nqp.$ class({quote_string($ _ . value . name ) },{ quote_string($ _ . key )} )" );
796
832
}
797
833
@ declared . push (" cuids = [{ nqp :: join (' ,' , @ vars )} ]" );
798
834
self . declare_js_vars(@ declared );
@@ -829,18 +865,6 @@ class QAST::CompilerJS does DWIMYNameMangling does SerializeOnce {
829
865
}
830
866
}
831
867
832
- has % ! serialized_code_ref_info ;
833
-
834
- my class SerializedCodeRefInfo {
835
- has $ ! closure_template ;
836
- has $ ! lexicals_type_info ;
837
- has $ ! outer_cuid ;
838
- has $ ! static_lexicals ;
839
- method outer_cuid () {$ ! outer_cuid }
840
- method lexicals_type_info () {$ ! lexicals_type_info }
841
- method closure_template () {$ ! closure_template }
842
- method static_lexicals () {$ ! static_lexicals }
843
- }
844
868
845
869
method type_info_for_lexicals (BlockInfo $ block ) {
846
870
my @ type_info ;
@@ -885,39 +909,38 @@ class QAST::CompilerJS does DWIMYNameMangling does SerializeOnce {
885
909
}
886
910
887
911
self . wrap_static_block($ block_info . outer, @ clone_inners , -> {
888
- if % * BLOCKS_DONE {$ kv . key } {
912
+ my $ outer := $ block_info . outer. ctx;
913
+ if self . has_closure_template($ block_info ) {
914
+ if nqp ::existskey($ block . captured_inners, $ kv . key ) {
915
+ @ clone_inners . push (" $ reg = $ cuid. captureAndClosureCtx ($ outer ) ;\n " );
916
+ }
917
+ else {
918
+ @ clone_inners . push (" $ reg = $ cuid. closureCtx ($ outer ) ;\n " );
919
+ }
920
+ }
921
+ else {
922
+ unless % * BLOCKS_DONE {$ kv . key } {
923
+ nqp ::die(" //clone_inners - broken block: { $ kv . key } " );
924
+ }
925
+
889
926
# Avoid emitting duplicated code with both .capture and .closure
890
927
if nqp ::existskey($ block . captured_inners, $ kv . key ) {
891
- if self . has_closure_template($ block_info ) {
892
- @ clone_inners . push (" $ reg = $ cuid. captureAndClosureCtx ({self . outer_ctxs($ block_info ) });\n " );
893
- }
894
- else {
895
- my $ outer := self . is_serializable($ kv . key ) ?? $ block_info . outer. ctx !! ' null' ;
896
- @ clone_inners . push (" $ reg = $ cuid. captureAndClosure ($ outer , " ) ;
897
- @ clone_inners. push (% * BLOCKS_DONE {$ kv . key }) ;
898
- @ clone_inners. push (" ) ;\n " );
899
- }
928
+ my $ set_outer := self . is_serializable($ kv . key ) ?? $ outer !! ' null' ;
929
+ @ clone_inners . push (" $ reg = $ cuid. captureAndClosure ($ set_outer , " ) ;
930
+ @ clone_inners. push (% * BLOCKS_DONE {$ kv . key }) ;
931
+ @ clone_inners. push (" ) ;\n " );
900
932
}
901
933
else {
902
- if self . has_closure_template($ block_info ) {
903
- @ clone_inners . push (" $ reg = $ cuid. closureCtx ({self . outer_ctxs($ block_info ) });\n " );
934
+ @ clone_inners . push (" $ reg = $ cuid .closure" );
935
+ @ clone_inners . push (% * BLOCKS_DONE {$ kv . key });
936
+ if self . is_serializable($ kv . key ) {
937
+ @ clone_inners . push (" .setOuter($ outer );\n " );
904
938
}
905
939
else {
906
- @ clone_inners . push (" $ reg = $ cuid .closure" );
907
- @ clone_inners . push (% * BLOCKS_DONE {$ kv . key });
908
- if self . is_serializable($ kv . key ) {
909
- @ clone_inners . push (" .setOuter(" ~ $ block_info . outer. ctx ~ " );\n " );
910
- }
911
- else {
912
- @ clone_inners . push (" ;\n " );
913
- }
940
+ @ clone_inners . push (" ;\n " );
914
941
}
915
942
}
916
943
}
917
- else {
918
- nqp ::die(" //Broken block: { $ kv . key } " );
919
- }
920
-
921
944
});
922
945
}
923
946
Chunk. void (| @ clone_inners );
@@ -940,7 +963,7 @@ class QAST::CompilerJS does DWIMYNameMangling does SerializeOnce {
940
963
my $ block_info := % * BLOCKS_INFO {$ kv . key };
941
964
self . wrap_static_block($ block_info . outer, @ capture_inners , -> {
942
965
if self . has_closure_template($ block_info ) {
943
- @ capture_inners . push (" $ reg = $ cuid. captureCtx ({self . outer_ctxs( $ block_info ) });\n " );
966
+ @ capture_inners . push (" $ reg = $ cuid. captureCtx ({$ block_info . outer . ctx }) ;\n " );
944
967
} else {
945
968
@ capture_inners . push (" $ reg = $ cuid .capture" );
946
969
@@ -959,22 +982,13 @@ class QAST::CompilerJS does DWIMYNameMangling does SerializeOnce {
959
982
Chunk. void (| @ capture_inners );
960
983
}
961
984
962
- method outer_ctxs (BlockInfo $ block ) {
963
- my @ ctxs ;
964
- my $ info := $ block . outer;
965
-
966
- # Avoid the ctx from the fake outer ctx
967
- while $ info && ! ($ info . ctx eq ' null' && ! $ info . outer) {
968
- @ ctxs . unshift ($ info . ctx);
969
- $ info := $ info . outer;
970
- }
971
- nqp :: join (' ,' , @ ctxs );
972
- }
973
-
974
985
method compile_block (QAST ::Block $ node , $ outer , $ outer_loop , : $ want , : @ extra_args = []) {
975
986
976
987
my str $ outer_ctx := try $ * CTX // " null" ;
977
988
989
+ my $ outer_used_ctx := try % * USED_CTXS ;
990
+
991
+
978
992
if self . is_known_cuid($ node ) {
979
993
}
980
994
else {
@@ -993,9 +1007,19 @@ class QAST::CompilerJS does DWIMYNameMangling does SerializeOnce {
993
1007
994
1008
my int $ body_want := $ node . blocktype eq ' immediate' ?? $ want !! $ T_OBJ ;
995
1009
1010
+ my int $ has_closure_template := $ node . blocktype ne ' immediate' ;
1011
+
1012
+ my % * USED_CTXS ;
1013
+
1014
+ % * USED_CTXS := $ outer_used_ctx unless $ has_closure_template ;
1015
+
996
1016
my $ stmts := self . compile_all_the_statements($ node , $ body_want );
997
1017
998
- my str $ create_ctx := self . create_ctx($ * CTX , : code_ref(' this' ));
1018
+
1019
+ my $ outer_ctx := $ has_closure_template ?? " this.outerCtx" !! ($ * BLOCK . outer ?? $ * BLOCK . outer. ctx !! ' null' );
1020
+ my str $ create_ctx := " var $ * CTX = new nqp.Ctx(caller_ctx, $ outer_ctx , this);\n " ;
1021
+
1022
+ % * USED_CTXS {$ * CTX } := 0 ;
999
1023
1000
1024
my $ sig := self . compile_sig($ * BLOCK . params);
1001
1025
@@ -1032,7 +1056,6 @@ class QAST::CompilerJS does DWIMYNameMangling does SerializeOnce {
1032
1056
$ pass_exceptions_end ,
1033
1057
" \} "
1034
1058
];
1035
- % * BLOCKS_DONE {$ node . cuid} := Chunk. void (" (" , | @ function , " )" );
1036
1059
1037
1060
if $ * BLOCK . statevars {
1038
1061
my @ vars ;
@@ -1045,42 +1068,66 @@ class QAST::CompilerJS does DWIMYNameMangling does SerializeOnce {
1045
1068
% * BLOCKS_STATEVARS {$ node . cuid} := " var " ~ nqp :: join (' ,' , @ vars ) ~ " ;\n " ;
1046
1069
}
1047
1070
1048
- if 1 { # TODO make sure that only blocks that take part in serialization have that info emitted
1049
- my $ outer_cuid ;
1050
- if nqp :: defined ($ * BLOCK . outer) && $ * BLOCK . outer. cuid {
1051
- $ outer_cuid := self . mangled_cuid($ * BLOCK . outer. cuid);
1052
- }
1053
- my str $ lexicals_type_info := self . type_info_for_lexicals($ * BLOCK );
1054
- my $ closure_template ;
1055
- if $ node . blocktype ne ' immediate' {
1056
- my @ closure_template := nqp :: clone (@ function );
1057
- @ closure_template . unshift (
1058
- " function({ self . outer_ctxs($ * BLOCK )} ) \{\n "
1059
- ~ % * BLOCKS_STATEVARS {$ node . cuid}
1060
- ~ " return " );
1061
- @ closure_template . push (' }' );
1062
- $ closure_template := Chunk. new ($ T_NONVAL , ' ' , @ closure_template );
1063
- }
1071
+ my $ outer_cuid ;
1072
+ if nqp :: defined ($ * BLOCK . outer) && $ * BLOCK . outer. cuid {
1073
+ $ outer_cuid := self . mangled_cuid($ * BLOCK . outer. cuid);
1074
+ }
1075
+ my str $ lexicals_type_info := self . type_info_for_lexicals($ * BLOCK );
1076
+ my $ closure_template ;
1064
1077
1065
- my @ static ;
1066
- for $ * BLOCK . variables -> $ var {
1067
- if $ var . decl eq ' static' {
1068
- @ static . push (quote_string($ var . name ) ~ ' : ' ~ self . value_as_js($ var . value ));
1078
+ my int $ statevars ;
1079
+
1080
+ if $ has_closure_template {
1081
+ my @ closure_template := nqp :: clone (@ function );
1082
+
1083
+
1084
+ my @ used_ctxs ;
1085
+ for % * USED_CTXS -> $ kv {
1086
+ my int $ depth := $ kv . value ;
1087
+ next if $ depth == 0 ;
1088
+ my str $ ctx := ' this.outerCtx' ;
1089
+ while $ depth > 1 {
1090
+ $ ctx := $ ctx ~ ' .$$outer' ;
1091
+ $ depth := $ depth - 1 ;
1069
1092
}
1093
+ @ used_ctxs . push (" let { $ kv . key } = $ ctx ;\n " );
1070
1094
}
1071
1095
1072
- my $ static_lexicals ;
1073
- if + @ static {
1074
- $ static_lexicals := ' {' ~ nqp :: join (' ,' , @ static ) ~ ' }' ;
1096
+ nqp :: splice (@ closure_template , @ used_ctxs , 5 , 0 );
1097
+
1098
+ $ statevars := nqp ::existskey(% * BLOCKS_STATEVARS , $ node . cuid);
1099
+
1100
+ @ closure_template . unshift (
1101
+ " function() \{\n "
1102
+ ~ % * BLOCKS_STATEVARS {$ node . cuid}
1103
+ ~ " return " ) if $ statevars ;
1104
+ @ closure_template . push (' }' ) if $ statevars ;
1105
+
1106
+ $ closure_template := Chunk. new ($ T_NONVAL , ' ' , @ closure_template );
1107
+ }
1108
+ else {
1109
+ % * BLOCKS_DONE {$ node . cuid} := Chunk. void (" (" , | @ function , " )" );
1110
+ }
1111
+
1112
+ my @ static ;
1113
+ for $ * BLOCK . variables -> $ var {
1114
+ if $ var . decl eq ' static' {
1115
+ @ static . push (quote_string($ var . name ) ~ ' : ' ~ self . value_as_js($ var . value ));
1075
1116
}
1117
+ }
1076
1118
1077
- % ! serialized_code_ref_info {$ node . cuid} := SerializedCodeRefInfo. new (
1078
- : $ closure_template ,
1079
- : $ outer_cuid ,
1080
- : $ lexicals_type_info ,
1081
- : $ static_lexicals
1082
- );
1119
+ my $ static_lexicals ;
1120
+ if + @ static {
1121
+ $ static_lexicals := ' {' ~ nqp :: join (' ,' , @ static ) ~ ' }' ;
1083
1122
}
1123
+
1124
+ % ! serialized_code_ref_info {$ node . cuid} := SerializedCodeRefInfo. new (
1125
+ : $ statevars ,
1126
+ : $ closure_template ,
1127
+ : $ outer_cuid ,
1128
+ : $ lexicals_type_info ,
1129
+ : $ static_lexicals
1130
+ );
1084
1131
}
1085
1132
1086
1133
if $ node . blocktype eq ' raw' {
@@ -1129,13 +1176,6 @@ class QAST::CompilerJS does DWIMYNameMangling does SerializeOnce {
1129
1176
$ prefix ~ $ ! unique_vars ;
1130
1177
}
1131
1178
1132
- method outer_ctx () {
1133
- $ * BLOCK . outer ?? $ * BLOCK . outer. ctx !! ' null' ;
1134
- }
1135
-
1136
- method create_ctx ($ name , : $ code_ref ) {
1137
- " var $ name = new nqp.Ctx(caller_ctx, this.forcedOuterCtx || { self . outer_ctx} , $ code_ref );\n " ;
1138
- }
1139
1179
1140
1180
multi method as_js (QAST ::IVal $ node , : $ want ) {
1141
1181
Chunk. new ($ T_INT ,' (' ~ $ node . value ()~ ' )' , : $ node );
0 commit comments