12
12
#include "ngx_js.h"
13
13
14
14
15
+ #define NGX_MAX_JOB_ITERATIONS 0x1000
16
+
17
+
15
18
typedef struct {
16
19
ngx_queue_t labels ;
17
20
} ngx_js_console_t ;
@@ -683,6 +686,42 @@ ngx_njs_clone(ngx_js_ctx_t *ctx, ngx_js_loc_conf_t *cf, void *external)
683
686
}
684
687
685
688
689
+ static njs_int_t
690
+ ngx_njs_execute_pending_jobs (njs_vm_t * vm , ngx_js_ctx_t * ctx )
691
+ {
692
+ njs_int_t ret , job_count ;
693
+ ngx_str_t exception ;
694
+
695
+ job_count = 0 ;
696
+
697
+ for ( ;; ) {
698
+ ret = njs_vm_execute_pending_job (vm );
699
+ if (ret <= NJS_OK ) {
700
+ if (ret == NJS_ERROR ) {
701
+ ngx_js_exception (vm , & exception );
702
+
703
+ ngx_log_error (NGX_LOG_ERR , ctx -> log , 0 ,
704
+ "js job exception: %V" , & exception );
705
+ return NGX_ERROR ;
706
+ }
707
+
708
+ break ;
709
+ }
710
+
711
+ job_count ++ ;
712
+
713
+ if (job_count >= NGX_MAX_JOB_ITERATIONS ) {
714
+ ngx_log_error (NGX_LOG_ERR , ctx -> log , 0 ,
715
+ "js job queue processing exceeded %ui iterations" ,
716
+ NGX_MAX_JOB_ITERATIONS );
717
+ return NGX_ERROR ;
718
+ }
719
+ }
720
+
721
+ return NGX_OK ;
722
+ }
723
+
724
+
686
725
static ngx_int_t
687
726
ngx_engine_njs_call (ngx_js_ctx_t * ctx , ngx_str_t * fname ,
688
727
njs_opaque_value_t * args , njs_uint_t nargs )
@@ -698,6 +737,11 @@ ngx_engine_njs_call(ngx_js_ctx_t *ctx, ngx_str_t *fname,
698
737
699
738
vm = ctx -> engine -> u .njs .vm ;
700
739
740
+ ret = ngx_njs_execute_pending_jobs (vm , ctx );
741
+ if (ret != NGX_OK ) {
742
+ return NGX_ERROR ;
743
+ }
744
+
701
745
func = njs_vm_function (vm , & name );
702
746
if (func == NULL ) {
703
747
ngx_log_error (NGX_LOG_ERR , ctx -> log , 0 ,
@@ -716,18 +760,27 @@ ngx_engine_njs_call(ngx_js_ctx_t *ctx, ngx_str_t *fname,
716
760
return NGX_ERROR ;
717
761
}
718
762
719
- for ( ;; ) {
720
- ret = njs_vm_execute_pending_job (vm );
721
- if (ret <= NJS_OK ) {
722
- if (ret == NJS_ERROR ) {
723
- ngx_js_exception (vm , & exception );
763
+ ret = ngx_njs_execute_pending_jobs (vm , ctx );
764
+ if (ret != NGX_OK ) {
765
+ return NGX_ERROR ;
766
+ }
724
767
725
- ngx_log_error (NGX_LOG_ERR , ctx -> log , 0 ,
726
- "js job exception: %V" , & exception );
727
- return NGX_ERROR ;
728
- }
768
+ njs_value_t * val = njs_value_arg (& ctx -> retval );
769
+ if (njs_value_is_promise (val )) {
770
+ njs_promise_type_t state = njs_promise_state (val );
729
771
730
- break ;
772
+ if (state == NJS_PROMISE_FULFILL ) {
773
+ njs_value_assign (val , njs_promise_result (val ));
774
+
775
+ } else if (state == NJS_PROMISE_REJECTED ) {
776
+ njs_vm_throw (vm , njs_promise_result (val ));
777
+
778
+ } else if (state == NJS_PROMISE_PENDING &&
779
+ njs_rbtree_is_empty (& ctx -> waiting_events ))
780
+ {
781
+ ngx_log_error (NGX_LOG_ERR , ctx -> log , 0 ,
782
+ "js promise pending, no jobs, no waiting_events" );
783
+ return NGX_ERROR ;
731
784
}
732
785
}
733
786
@@ -741,6 +794,7 @@ ngx_engine_njs_external(ngx_engine_t *engine)
741
794
return njs_vm_external_ptr (engine -> u .njs .vm );
742
795
}
743
796
797
+
744
798
static ngx_int_t
745
799
ngx_engine_njs_pending (ngx_engine_t * e )
746
800
{
@@ -754,8 +808,9 @@ ngx_engine_njs_string(ngx_engine_t *e, njs_opaque_value_t *value,
754
808
{
755
809
ngx_int_t rc ;
756
810
njs_str_t s ;
811
+ njs_value_t * val = njs_value_arg (value );
757
812
758
- rc = ngx_js_string (e -> u .njs .vm , njs_value_arg ( value ) , & s );
813
+ rc = ngx_js_string (e -> u .njs .vm , val , & s );
759
814
760
815
str -> data = s .start ;
761
816
str -> len = s .length ;
@@ -875,38 +930,60 @@ ngx_engine_qjs_compile(ngx_js_loc_conf_t *conf, ngx_log_t *log, u_char *start,
875
930
876
931
877
932
static JSValue
878
- js_std_await (JSContext * ctx , JSValue obj )
933
+ ngx_qjs_await (JSContext * ctx , JSValue obj )
879
934
{
880
- int state , err ;
881
- JSValue ret ;
882
- JSContext * ctx1 ;
935
+ int rc , job_count ;
936
+ JSValue ret ;
937
+ JSRuntime * rt ;
938
+ JSContext * ctx1 ;
939
+ ngx_js_ctx_t * js_ctx ;
940
+
941
+ rt = JS_GetRuntime (ctx );
942
+ js_ctx = ngx_qjs_external_ctx (ctx , JS_GetContextOpaque (ctx ));
943
+
944
+ job_count = 0 ;
945
+
946
+ for ( ;; ) {
947
+ rc = JS_ExecutePendingJob (rt , & ctx1 );
948
+ if (rc <= 0 ) {
949
+ if (rc == -1 ) {
950
+ JS_FreeValue (ctx , obj );
951
+ return JS_EXCEPTION ;
952
+ }
953
+ break ;
954
+ }
955
+
956
+ job_count ++ ;
957
+
958
+ if (job_count >= NGX_MAX_JOB_ITERATIONS ) {
959
+ JS_FreeValue (ctx , obj );
960
+ return JS_ThrowInternalError (ctx , "js job queue processing "
961
+ "exceeded %u iterations" ,
962
+ NGX_MAX_JOB_ITERATIONS );
963
+ }
964
+ }
883
965
884
- for (;; ) {
885
- state = JS_PromiseState (ctx , obj );
966
+ if ( JS_IsObject ( obj ) ) {
967
+ JSPromiseStateEnum state = JS_PromiseState (ctx , obj );
886
968
if (state == JS_PROMISE_FULFILLED ) {
887
969
ret = JS_PromiseResult (ctx , obj );
888
970
JS_FreeValue (ctx , obj );
889
- break ;
890
-
971
+ return ret ;
891
972
} else if (state == JS_PROMISE_REJECTED ) {
892
- ret = JS_Throw ( ctx , JS_PromiseResult (ctx , obj ) );
973
+ JSValue rejection = JS_PromiseResult (ctx , obj );
893
974
JS_FreeValue (ctx , obj );
894
- break ;
895
-
975
+ return JS_Throw (ctx , rejection );
896
976
} else if (state == JS_PROMISE_PENDING ) {
897
- err = JS_ExecutePendingJob (JS_GetRuntime (ctx ), & ctx1 );
898
- if (err < 0 ) {
899
- /* js_std_dump_error(ctx1); */
977
+ if (js_ctx && njs_rbtree_is_empty (& js_ctx -> waiting_events )) {
978
+ JS_FreeValue (ctx , obj );
979
+ return JS_ThrowInternalError (ctx , "js promise pending, "
980
+ "no jobs, no waiting_events" );
900
981
}
901
-
902
- } else {
903
- /* not a promise */
904
- ret = obj ;
905
- break ;
982
+ return obj ;
906
983
}
907
984
}
908
985
909
- return ret ;
986
+ return obj ;
910
987
}
911
988
912
989
@@ -1000,12 +1077,13 @@ ngx_qjs_clone(ngx_js_ctx_t *ctx, ngx_js_loc_conf_t *cf, void *external)
1000
1077
goto destroy ;
1001
1078
}
1002
1079
1003
- rv = js_std_await (cx , rv );
1080
+ rv = ngx_qjs_await (cx , rv );
1004
1081
if (JS_IsException (rv )) {
1005
1082
ngx_qjs_exception (engine , & exception );
1006
1083
1007
1084
ngx_log_error (NGX_LOG_ERR , ctx -> log , 0 , "js eval exception: %V" ,
1008
1085
& exception );
1086
+
1009
1087
goto destroy ;
1010
1088
}
1011
1089
@@ -1023,6 +1101,44 @@ ngx_qjs_clone(ngx_js_ctx_t *ctx, ngx_js_loc_conf_t *cf, void *external)
1023
1101
}
1024
1102
1025
1103
1104
+ static ngx_int_t
1105
+ ngx_qjs_execute_pending_jobs (ngx_js_ctx_t * ctx , JSRuntime * rt )
1106
+ {
1107
+ int rc , job_count ;
1108
+ ngx_str_t exception ;
1109
+ JSContext * cx ;
1110
+
1111
+ job_count = 0 ;
1112
+
1113
+ for ( ;; ) {
1114
+ rc = JS_ExecutePendingJob (rt , & cx );
1115
+ if (rc <= 0 ) {
1116
+ if (rc == -1 ) {
1117
+ ngx_qjs_exception (ctx -> engine , & exception );
1118
+
1119
+ ngx_log_error (NGX_LOG_ERR , ctx -> log , 0 ,
1120
+ "js job exception: %V" , & exception );
1121
+
1122
+ return NGX_ERROR ;
1123
+ }
1124
+
1125
+ break ;
1126
+ }
1127
+
1128
+ job_count ++ ;
1129
+
1130
+ if (job_count >= NGX_MAX_JOB_ITERATIONS ) {
1131
+ ngx_log_error (NGX_LOG_ERR , ctx -> log , 0 ,
1132
+ "js job queue processing exceeded %ui iterations" ,
1133
+ NGX_MAX_JOB_ITERATIONS );
1134
+ return NGX_ERROR ;
1135
+ }
1136
+ }
1137
+
1138
+ return NGX_OK ;
1139
+ }
1140
+
1141
+
1026
1142
static ngx_int_t
1027
1143
ngx_engine_qjs_call (ngx_js_ctx_t * ctx , ngx_str_t * fname ,
1028
1144
njs_opaque_value_t * args , njs_uint_t nargs )
@@ -1031,9 +1147,15 @@ ngx_engine_qjs_call(ngx_js_ctx_t *ctx, ngx_str_t *fname,
1031
1147
JSValue fn , val ;
1032
1148
ngx_str_t exception ;
1033
1149
JSRuntime * rt ;
1034
- JSContext * cx , * cx1 ;
1150
+ JSContext * cx ;
1035
1151
1036
1152
cx = ctx -> engine -> u .qjs .ctx ;
1153
+ rt = JS_GetRuntime (cx );
1154
+
1155
+ rc = ngx_qjs_execute_pending_jobs (ctx , rt );
1156
+ if (rc != NGX_OK ) {
1157
+ return NGX_ERROR ;
1158
+ }
1037
1159
1038
1160
fn = ngx_qjs_value (cx , fname );
1039
1161
if (!JS_IsFunction (cx , fn )) {
@@ -1058,21 +1180,31 @@ ngx_engine_qjs_call(ngx_js_ctx_t *ctx, ngx_str_t *fname,
1058
1180
JS_FreeValue (cx , ngx_qjs_arg (ctx -> retval ));
1059
1181
ngx_qjs_arg (ctx -> retval ) = val ;
1060
1182
1061
- rt = JS_GetRuntime (cx );
1183
+ rc = ngx_qjs_execute_pending_jobs (ctx , rt );
1184
+ if (rc != NGX_OK ) {
1185
+ return NGX_ERROR ;
1186
+ }
1062
1187
1063
- for ( ;; ) {
1064
- rc = JS_ExecutePendingJob (rt , & cx1 );
1065
- if (rc <= 0 ) {
1066
- if (rc == -1 ) {
1067
- ngx_qjs_exception (ctx -> engine , & exception );
1188
+ JSValue retval = ngx_qjs_arg (ctx -> retval );
1189
+ if (JS_IsObject (retval )) {
1190
+ JSPromiseStateEnum state = JS_PromiseState (cx , retval );
1068
1191
1069
- ngx_log_error (NGX_LOG_ERR , ctx -> log , 0 ,
1070
- "js job exception: %V" , & exception );
1192
+ if (state == JS_PROMISE_FULFILLED ) {
1193
+ JSValue result = JS_PromiseResult (cx , retval );
1194
+ JS_FreeValue (cx , ngx_qjs_arg (ctx -> retval ));
1195
+ ngx_qjs_arg (ctx -> retval ) = result ;
1071
1196
1072
- return NGX_ERROR ;
1073
- }
1197
+ } else if (state == JS_PROMISE_REJECTED ) {
1198
+ JSValue rejection = JS_PromiseResult (cx , retval );
1199
+ JS_FreeValue (cx , ngx_qjs_arg (ctx -> retval ));
1200
+ ngx_qjs_arg (ctx -> retval ) = JS_Throw (cx , rejection );
1074
1201
1075
- break ;
1202
+ } else if (state == JS_PROMISE_PENDING &&
1203
+ njs_rbtree_is_empty (& ctx -> waiting_events ))
1204
+ {
1205
+ ngx_log_error (NGX_LOG_ERR , ctx -> log , 0 ,
1206
+ "js promise pending, no jobs, no waiting_events" );
1207
+ return NGX_ERROR ;
1076
1208
}
1077
1209
}
1078
1210
@@ -1098,7 +1230,9 @@ static ngx_int_t
1098
1230
ngx_engine_qjs_string (ngx_engine_t * e , njs_opaque_value_t * value ,
1099
1231
ngx_str_t * str )
1100
1232
{
1101
- return ngx_qjs_dump_obj (e , ngx_qjs_arg (* value ), str );
1233
+ JSValue val = ngx_qjs_arg (* value );
1234
+
1235
+ return ngx_qjs_dump_obj (e , val , str );
1102
1236
}
1103
1237
1104
1238
0 commit comments