static ngx_int_t
ngx_http_js_init_vm(ngx_http_request_t *r, njs_int_t proto_id)
{
+ ngx_engine_t *engine;
ngx_http_js_ctx_t *ctx;
ngx_pool_cleanup_t *cln;
ngx_http_js_loc_conf_t *jlcf;
return NGX_OK;
}
- ctx->engine = jlcf->engine->clone((ngx_js_ctx_t *) ctx,
- (ngx_js_loc_conf_t *) jlcf, proto_id, r);
- if (ctx->engine == NULL) {
+ engine = jlcf->engine->clone((ngx_js_ctx_t *) ctx,
+ (ngx_js_loc_conf_t *) jlcf, proto_id, r);
+ if (engine == NULL) {
return NGX_ERROR;
}
- ngx_log_debug3(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
- "http js vm clone %s: %p from: %p", jlcf->engine->name,
- ctx->engine, jlcf->engine);
-
cln = ngx_pool_cleanup_add(r->pool, 0);
if (cln == NULL) {
+ ngx_js_clone_abort((ngx_js_ctx_t *) ctx, engine);
return NGX_ERROR;
}
+ ctx->engine = engine;
+
+ ngx_log_debug3(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
+ "http js vm clone %s: %p from: %p", jlcf->engine->name,
+ ctx->engine, jlcf->engine);
+
cln->handler = ngx_http_js_cleanup_ctx;
cln->data = ctx;
proto_id, njs_vm_external_ptr(engine->u.njs.vm),
0);
if (rc != NJS_OK) {
+ ngx_js_clone_abort(ctx, engine);
return NULL;
}
req = js_malloc(cx, sizeof(ngx_http_qjs_request_t));
if (req == NULL) {
+ JS_FreeValue(cx, request);
return JS_ThrowOutOfMemory(cx);
}
if (JS_NewClass(JS_GetRuntime(cx), NGX_QJS_CLASS_ID_HTTP_REQUEST,
&ngx_http_qjs_request_class) < 0)
{
- return NULL;
+ goto failed;
}
proto = JS_NewObject(cx);
if (JS_IsException(proto)) {
- return NULL;
+ goto failed;
}
JS_SetPropertyFunctionList(cx, proto, ngx_http_qjs_ext_request,
if (JS_NewClass(JS_GetRuntime(cx), NGX_QJS_CLASS_ID_HTTP_FORM,
&ngx_http_qjs_request_form_class) < 0)
{
- return NULL;
+ goto failed;
}
proto = JS_NewObject(cx);
if (JS_IsException(proto)) {
- return NULL;
+ goto failed;
}
JS_SetPropertyFunctionList(cx, proto, ngx_http_qjs_ext_request_form,
if (JS_NewClass(JS_GetRuntime(cx), NGX_QJS_CLASS_ID_HTTP_PERIODIC,
&ngx_http_qjs_periodic_class) < 0)
{
- return NULL;
+ goto failed;
}
proto = JS_NewObject(cx);
if (JS_IsException(proto)) {
- return NULL;
+ goto failed;
}
JS_SetPropertyFunctionList(cx, proto, ngx_http_qjs_ext_periodic,
if (JS_NewClass(JS_GetRuntime(cx), NGX_QJS_CLASS_ID_HTTP_VARS,
&ngx_http_qjs_variables_class) < 0)
{
- return NULL;
+ goto failed;
}
if (JS_NewClass(JS_GetRuntime(cx), NGX_QJS_CLASS_ID_HTTP_HEADERS_IN,
&ngx_http_qjs_headers_in_class) < 0)
{
- return NULL;
+ goto failed;
}
if (JS_NewClass(JS_GetRuntime(cx), NGX_QJS_CLASS_ID_HTTP_HEADERS_OUT,
&ngx_http_qjs_headers_out_class) < 0)
{
- return NULL;
+ goto failed;
}
}
- hctx = (ngx_http_js_ctx_t *) ctx;
- hctx->body_filter = ngx_http_qjs_body_filter;
-
if (proto_id == ngx_http_js_request_proto_id) {
proto_id = NGX_QJS_CLASS_ID_HTTP_REQUEST;
proto_id = NGX_QJS_CLASS_ID_HTTP_PERIODIC;
}
- ngx_qjs_arg(hctx->args[0]) = ngx_http_qjs_request_make(cx, proto_id,
- external);
- if (JS_IsException(ngx_qjs_arg(hctx->args[0]))) {
- return NULL;
+ ngx_qjs_arg(ctx->args[0]) = ngx_http_qjs_request_make(cx, proto_id,
+ external);
+ if (JS_IsException(ngx_qjs_arg(ctx->args[0]))) {
+ goto failed;
}
+ hctx = (ngx_http_js_ctx_t *) ctx;
+ hctx->body_filter = ngx_http_qjs_body_filter;
+
return engine;
+
+failed:
+
+ ngx_js_clone_abort(ctx, engine);
+
+ return NULL;
}
#endif
static ngx_int_t ngx_engine_njs_pending(ngx_engine_t *engine);
static ngx_int_t ngx_engine_njs_string(ngx_engine_t *e,
njs_opaque_value_t *value, ngx_str_t *str);
+static void ngx_njs_clear_events(ngx_js_ctx_t *ctx);
static void ngx_engine_njs_destroy(ngx_engine_t *e, ngx_js_ctx_t *ctx,
ngx_js_loc_conf_t *conf);
static ngx_int_t ngx_js_init_preload_vm(njs_vm_t *vm, ngx_js_loc_conf_t *conf);
static ngx_int_t ngx_js_integer_in_range(double num);
+static intptr_t ngx_js_event_rbtree_compare(njs_rbtree_node_t *node1,
+ njs_rbtree_node_t *node2);
static ngx_int_t ngx_njs_execute_pending_jobs(njs_vm_t *vm, ngx_log_t *log);
static njs_int_t ngx_njs_await(njs_vm_t *vm, ngx_log_t *log,
static ngx_int_t ngx_engine_qjs_pending(ngx_engine_t *engine);
static ngx_int_t ngx_engine_qjs_string(ngx_engine_t *e,
njs_opaque_value_t *value, ngx_str_t *str);
+static void ngx_qjs_clear_events(ngx_js_ctx_t *ctx);
+static void ngx_qjs_detach_ctx(ngx_js_ctx_t *ctx, JSContext *cx);
static JSValue ngx_qjs_process_getter(JSContext *ctx, JSValueConst this_val);
static JSValue ngx_qjs_ext_set_timeout(JSContext *cx, JSValueConst this_val,
engine = njs_mp_zalloc(mp, sizeof(ngx_engine_t));
if (engine == NULL) {
+ njs_mp_destroy(mp);
return NULL;
}
case NGX_ENGINE_NJS:
rc = ngx_engine_njs_init(engine, opts);
if (rc != NGX_OK) {
- return NULL;
+ goto failed;
}
engine->name = "njs";
case NGX_ENGINE_QJS:
rc = ngx_engine_qjs_init(engine, opts);
if (rc != NGX_OK) {
- return NULL;
+ goto failed;
}
engine->name = "QuickJS";
#endif
default:
- return NULL;
+ goto failed;
}
return engine;
+
+failed:
+
+ njs_mp_destroy(mp);
+
+ return NULL;
}
if (njs_vm_start(vm, njs_value_arg(&retval)) == NJS_ERROR) {
ngx_js_log_exception(vm, ctx->log, "exception");
- goto destroy;
+ goto failed;
}
ret = ngx_njs_await(vm, ctx->log, njs_value_arg(&retval));
if (ret == NGX_ERROR) {
- goto destroy;
+ goto failed;
}
return engine;
+failed:
+
+ ngx_js_clone_abort(ctx, engine);
+
+ return NULL;
+
destroy:
njs_vm_destroy(vm);
static void
-ngx_engine_njs_destroy(ngx_engine_t *e, ngx_js_ctx_t *ctx,
- ngx_js_loc_conf_t *conf)
+ngx_njs_clear_events(ngx_js_ctx_t *ctx)
{
- njs_int_t ret;
ngx_js_event_t *event;
njs_rbtree_node_t *node;
+ node = njs_rbtree_min(&ctx->waiting_events);
+
+ while (njs_rbtree_is_there_successor(&ctx->waiting_events, node)) {
+ event = (ngx_js_event_t *) ((u_char *) node
+ - offsetof(ngx_js_event_t, node));
+
+ if (event->destructor != NULL) {
+ event->destructor(event);
+ }
+
+ node = njs_rbtree_node_successor(&ctx->waiting_events, node);
+ }
+
+ ctx->event_id = 0;
+ njs_rbtree_init(&ctx->waiting_events, ngx_js_event_rbtree_compare);
+}
+
+
+static void
+ngx_engine_njs_destroy(ngx_engine_t *e, ngx_js_ctx_t *ctx,
+ ngx_js_loc_conf_t *conf)
+{
+ njs_int_t ret;
+
if (ctx != NULL) {
ret = njs_vm_call_exit_hook(e->u.njs.vm);
if (ret != NJS_OK) {
(void) ngx_njs_execute_pending_jobs(e->u.njs.vm, ctx->log);
- node = njs_rbtree_min(&ctx->waiting_events);
-
- while (njs_rbtree_is_there_successor(&ctx->waiting_events, node)) {
- event = (ngx_js_event_t *) ((u_char *) node
- - offsetof(ngx_js_event_t, node));
-
- if (event->destructor != NULL) {
- event->destructor(event);
- }
-
- node = njs_rbtree_node_successor(&ctx->waiting_events, node);
- }
+ ngx_njs_clear_events(ctx);
if (ngx_js_unhandled_rejection(ctx)) {
ngx_js_log_exception(e->u.njs.vm, ctx->log, "unhandled rejection");
}
+void
+ngx_js_clone_abort(ngx_js_ctx_t *ctx, ngx_engine_t *engine)
+{
+ /* The clone must not be published in ctx->engine before this call. */
+
+ if (engine->type == NGX_ENGINE_NJS) {
+ ngx_njs_clear_events(ctx);
+ ctx->rejected_promises = NULL;
+ njs_vm_destroy(engine->u.njs.vm);
+ return;
+ }
+
+#if (NJS_HAVE_QUICKJS)
+ JSRuntime *rt;
+ JSContext *cx;
+
+ ngx_qjs_clear_events(ctx);
+ cx = engine->u.qjs.ctx;
+
+ if (cx != NULL) {
+ rt = JS_GetRuntime(cx);
+
+ JS_SetHostPromiseRejectionTracker(rt, NULL, NULL);
+ ngx_qjs_detach_ctx(ctx, cx);
+
+ JS_FreeContext(cx);
+ JS_FreeRuntime(rt);
+ }
+
+ njs_mp_destroy(engine->pool);
+#endif
+}
+
+
#if (NJS_HAVE_QUICKJS)
static ngx_int_t
engine->u.qjs.ctx = qjs_new_context(rt, opts->u.qjs.addons);
if (engine->u.qjs.ctx == NULL) {
+ JS_FreeRuntime(rt);
return NGX_ERROR;
}
engine = njs_mp_alloc(mp, sizeof(ngx_engine_t));
if (engine == NULL) {
+ njs_mp_destroy(mp);
return NULL;
}
memcpy(engine, cf->engine, sizeof(ngx_engine_t));
engine->pool = mp;
+ engine->u.qjs.ctx = NULL;
ctx->conf = cf;
if (cf->reuse_queue != NULL) {
rt = JS_NewRuntime();
if (rt == NULL) {
- return NULL;
+ goto failed;
}
JS_SetRuntimeOpaque(rt, JS_GetRuntimeOpaque(
cx = qjs_new_context(rt, JS_GetContextOpaque(cf->engine->u.qjs.ctx));
if (cx == NULL) {
JS_FreeRuntime(rt);
- return NULL;
+ goto failed;
}
engine->u.qjs.ctx = cx;
if (mod[i].init(cx, mod[i].name) == NULL) {
ngx_log_error(NGX_LOG_ERR, ctx->log, 0,
"js native module init failed: %s", mod[i].name);
- goto destroy;
+ goto failed;
}
}
}
JS_READ_OBJ_BYTECODE);
if (JS_IsException(rv)) {
ngx_qjs_log_exception(engine, ctx->log, "load module exception");
- goto destroy;
+ goto failed;
}
if (i != length - 1) {
if (JS_ResolveModule(cx, rv) < 0) {
ngx_log_error(NGX_LOG_ERR, ctx->log, 0, "js resolve module failed");
- goto destroy;
+ JS_FreeValue(cx, rv);
+ goto failed;
}
rv = JS_EvalFunction(cx, rv);
if (JS_IsException(rv)) {
ngx_qjs_log_exception(engine, ctx->log, "eval exception");
- goto destroy;
+ goto failed;
}
rc = ngx_qjs_await(cx, ctx->log, &rv);
JS_FreeValue(cx, rv);
if (rc == NGX_ERROR) {
ngx_qjs_log_exception(engine, ctx->log, "await exception");
- goto destroy;
+ goto failed;
}
return engine;
-destroy:
+failed:
- JS_FreeContext(cx);
- JS_FreeRuntime(rt);
- njs_mp_destroy(mp);
+ ngx_js_clone_abort(ctx, engine);
return NULL;
}
}
+static void
+ngx_qjs_detach_ctx(ngx_js_ctx_t *ctx, JSContext *cx)
+{
+ JSClassID class_id;
+ ngx_js_opaque_t *opaque;
+
+ if (JS_IsObject(ngx_qjs_arg(ctx->args[0]))) {
+ class_id = JS_GetClassID(ngx_qjs_arg(ctx->args[0]));
+ opaque = JS_GetOpaque(ngx_qjs_arg(ctx->args[0]), class_id);
+
+ if (opaque != NULL) {
+ opaque->external = NULL;
+ }
+ }
+
+ JS_FreeValue(cx, ngx_qjs_arg(ctx->args[0]));
+ JS_FreeValue(cx, ngx_qjs_arg(ctx->retval));
+
+ ngx_qjs_arg(ctx->args[0]) = JS_UNDEFINED;
+ ngx_qjs_arg(ctx->retval) = JS_UNDEFINED;
+
+ JS_SetContextOpaque(cx, NULL);
+}
+
+
+static void
+ngx_qjs_clear_events(ngx_js_ctx_t *ctx)
+{
+ ngx_qjs_event_t *event;
+ njs_rbtree_node_t *node;
+
+ node = njs_rbtree_min(&ctx->waiting_events);
+
+ while (njs_rbtree_is_there_successor(&ctx->waiting_events, node)) {
+ event = (ngx_qjs_event_t *) ((u_char *) node
+ - offsetof(ngx_qjs_event_t, node));
+
+ if (event->destructor != NULL) {
+ event->destructor(event);
+ }
+
+ node = njs_rbtree_node_successor(&ctx->waiting_events, node);
+ }
+
+ ctx->event_id = 0;
+ njs_rbtree_init(&ctx->waiting_events, ngx_js_event_rbtree_compare);
+}
+
+
static void
ngx_js_cleanup_reuse_ctx(void *data)
{
uint32_t i, length;
JSRuntime *rt;
JSContext *cx;
- JSClassID class_id;
ngx_uint_t reusable;
JSMemoryUsage stats;
- ngx_qjs_event_t *event;
- ngx_js_opaque_t *opaque;
- njs_rbtree_node_t *node;
ngx_pool_cleanup_t *cln;
ngx_js_code_entry_t *pc;
reusable = 0;
}
- node = njs_rbtree_min(&ctx->waiting_events);
-
- while (njs_rbtree_is_there_successor(&ctx->waiting_events, node)) {
- event = (ngx_qjs_event_t *) ((u_char *) node
- - offsetof(ngx_qjs_event_t, node));
-
- if (event->destructor != NULL) {
- event->destructor(event);
- }
-
- node = njs_rbtree_node_successor(&ctx->waiting_events, node);
- }
+ ngx_qjs_clear_events(ctx);
if (ngx_qjs_unhandled_rejection(ctx)) {
ngx_qjs_log_exception(e, ctx->log, "unhandled rejection");
}
JS_SetHostPromiseRejectionTracker(JS_GetRuntime(cx), NULL, NULL);
-
- class_id = JS_GetClassID(ngx_qjs_arg(ctx->args[0]));
- opaque = JS_GetOpaque(ngx_qjs_arg(ctx->args[0]), class_id);
- opaque->external = NULL;
-
- JS_FreeValue(cx, ngx_qjs_arg(ctx->args[0]));
- JS_FreeValue(cx, ngx_qjs_arg(ctx->retval));
+ ngx_qjs_detach_ctx(ctx, cx);
if (JS_IsJobPending(JS_GetRuntime(cx))) {
ngx_log_error(NGX_LOG_ERR, ctx->log, 0,
ngx_int_t ngx_js_log_exception(njs_vm_t *vm, ngx_log_t *log, const char *txt);
ngx_engine_t *ngx_njs_clone(ngx_js_ctx_t *ctx, ngx_js_loc_conf_t *cf,
void *external);
+void ngx_js_clone_abort(ngx_js_ctx_t *ctx, ngx_engine_t *engine);
#define NGX_CHB_CTX_INIT(chain, pool) \
njs_chb_init(chain, pool, (njs_chb_alloc_t) ngx_palloc, NULL)
static ngx_int_t
ngx_stream_js_init_vm(ngx_stream_session_t *s, njs_int_t proto_id)
{
+ ngx_engine_t *engine;
ngx_pool_cleanup_t *cln;
ngx_stream_js_ctx_t *ctx;
ngx_stream_js_srv_conf_t *jscf;
return NGX_OK;
}
- ctx->engine = jscf->engine->clone((ngx_js_ctx_t *) ctx,
- (ngx_js_loc_conf_t *) jscf, proto_id, s);
- if (ctx->engine == NULL) {
+ engine = jscf->engine->clone((ngx_js_ctx_t *) ctx,
+ (ngx_js_loc_conf_t *) jscf, proto_id, s);
+ if (engine == NULL) {
return NGX_ERROR;
}
- ngx_log_debug3(NGX_LOG_DEBUG_STREAM, ctx->log, 0,
- "stream js vm clone %s: %p from: %p", jscf->engine->name,
- ctx->engine, jscf->engine);
-
cln = ngx_pool_cleanup_add(s->connection->pool, 0);
if (cln == NULL) {
+ ngx_js_clone_abort((ngx_js_ctx_t *) ctx, engine);
return NGX_ERROR;
}
+ ctx->engine = engine;
+
+ ngx_log_debug3(NGX_LOG_DEBUG_STREAM, ctx->log, 0,
+ "stream js vm clone %s: %p from: %p", jscf->engine->name,
+ ctx->engine, jscf->engine);
+
cln->handler = ngx_stream_js_cleanup;
cln->data = s;
return NULL;
}
- sctx = (ngx_stream_js_ctx_t *) ctx;
- sctx->run_event = ngx_stream_js_run_event;
- sctx->body_filter = ngx_stream_njs_body_filter;
-
rc = njs_vm_external_create(engine->u.njs.vm, njs_value_arg(&ctx->args[0]),
proto_id, njs_vm_external_ptr(engine->u.njs.vm),
0);
if (rc != NJS_OK) {
+ ngx_js_clone_abort(ctx, engine);
return NULL;
}
+ sctx = (ngx_stream_js_ctx_t *) ctx;
+ sctx->run_event = ngx_stream_js_run_event;
+ sctx->body_filter = ngx_stream_njs_body_filter;
+
return engine;
}
ses = js_malloc(cx, sizeof(ngx_stream_qjs_session_t));
if (ses == NULL) {
+ JS_FreeValue(cx, session);
return JS_ThrowOutOfMemory(cx);
}
if (JS_NewClass(JS_GetRuntime(cx), NGX_QJS_CLASS_ID_STREAM_SESSION,
&ngx_stream_qjs_session_class) < 0)
{
- return NULL;
+ goto failed;
}
proto = JS_NewObject(cx);
if (JS_IsException(proto)) {
- return NULL;
+ goto failed;
}
JS_SetPropertyFunctionList(cx, proto, ngx_stream_qjs_ext_session,
if (JS_NewClass(JS_GetRuntime(cx), NGX_QJS_CLASS_ID_STREAM_PERIODIC,
&ngx_stream_qjs_periodic_class) < 0)
{
- return NULL;
+ goto failed;
}
proto = JS_NewObject(cx);
if (JS_IsException(proto)) {
- return NULL;
+ goto failed;
}
JS_SetPropertyFunctionList(cx, proto, ngx_stream_qjs_ext_periodic,
if (JS_NewClass(JS_GetRuntime(cx), NGX_QJS_CLASS_ID_STREAM_FLAGS,
&ngx_stream_qjs_flags_class) < 0)
{
- return NULL;
+ goto failed;
}
proto = JS_NewObject(cx);
if (JS_IsException(proto)) {
- return NULL;
+ goto failed;
}
JS_SetPropertyFunctionList(cx, proto, ngx_stream_qjs_ext_flags,
if (JS_NewClass(JS_GetRuntime(cx), NGX_QJS_CLASS_ID_STREAM_VARS,
&ngx_stream_qjs_variables_class) < 0)
{
- return NULL;
+ goto failed;
}
}
- sctx = (ngx_stream_js_ctx_t *) ctx;
- sctx->run_event = ngx_stream_qjs_run_event;
- sctx->body_filter = ngx_stream_qjs_body_filter;
-
if (proto_id == ngx_stream_js_session_proto_id) {
proto_id = NGX_QJS_CLASS_ID_STREAM_SESSION;
ngx_qjs_arg(ctx->args[0]) = ngx_stream_qjs_session_make(cx, proto_id,
external);
if (JS_IsException(ngx_qjs_arg(ctx->args[0]))) {
- return NULL;
+ goto failed;
}
+ sctx = (ngx_stream_js_ctx_t *) ctx;
+ sctx->run_event = ngx_stream_qjs_run_event;
+ sctx->body_filter = ngx_stream_qjs_body_filter;
+
return engine;
+
+failed:
+
+ ngx_js_clone_abort(ctx, engine);
+
+ return NULL;
}
--- /dev/null
+#!/usr/bin/perl
+
+# (C) Dmitry Volyntsev
+# (C) F5, Inc.
+
+# Tests for cleanup after a JavaScript context clone failure.
+
+###############################################################################
+
+use warnings;
+use strict;
+
+use Test::More;
+
+BEGIN { use FindBin; chdir($FindBin::Bin); }
+
+use lib 'lib';
+use Test::Nginx;
+
+###############################################################################
+
+select STDERR; $| = 1;
+select STDOUT; $| = 1;
+
+my $t = Test::Nginx->new()->has(qw/http/)
+ ->write_file_expand('nginx.conf', <<'EOF');
+
+%%TEST_GLOBALS%%
+
+daemon off;
+
+events {
+}
+
+http {
+ %%TEST_GLOBALS_HTTP%%
+
+ js_engine njs;
+ js_import test.js;
+
+ server {
+ listen 127.0.0.1:8080;
+ server_name localhost;
+
+ location /failed {
+ js_content test.content;
+ js_body_filter test.filter;
+ }
+
+ location /alive {
+ return 200 alive;
+ }
+ }
+}
+
+EOF
+
+$t->write_file('test.js', <<'EOF');
+ Promise.reject(new Error('unhandled'));
+ throw new Error('clone failed');
+
+ function content(r) {
+ r.return(200, 'unexpected');
+ }
+
+ function filter(r, data, flags) {
+ r.sendBuffer(data, flags);
+ }
+
+ export default { content, filter };
+
+EOF
+
+$t->try_run('no njs available')->plan(2);
+
+###############################################################################
+
+is(http_get('/failed'), '', 'clone failure');
+like(http_get('/alive'), qr/200 OK/, 'worker remains alive');
+
+###############################################################################
return NULL;
}
+ global_obj = JS_UNDEFINED;
+
JS_AddIntrinsicBaseObjects(ctx);
JS_AddIntrinsicDate(ctx);
JS_AddIntrinsicRegExp(ctx);
for (module = qjs_modules; *module != NULL; module++) {
if ((*module)->init(ctx, (*module)->name) == NULL) {
- return NULL;
+ goto failed;
}
}
if (addons != NULL) {
for (module = addons; *module != NULL; module++) {
if ((*module)->init(ctx, (*module)->name) == NULL) {
- return NULL;
+ goto failed;
}
}
}
global_obj = JS_GetGlobalObject(ctx);
if (qjs_add_intrinsic_njs(ctx, global_obj) < 0) {
- return NULL;
+ goto failed;
}
if (qjs_add_intrinsic_text_decoder(ctx, global_obj) < 0) {
- return NULL;
+ goto failed;
}
if (qjs_add_intrinsic_text_encoder(ctx, global_obj) < 0) {
- return NULL;
+ goto failed;
}
if (qjs_add_intrinsic_btoa_atob(ctx, global_obj) < 0) {
- return NULL;
+ goto failed;
}
prop = JS_NewAtom(ctx, "eval");
if (prop == JS_ATOM_NULL) {
- return NULL;
+ goto failed;
}
ret = JS_DeleteProperty(ctx, global_obj, prop, 0);
JS_FreeAtom(ctx, prop);
if (ret < 0) {
- return NULL;
+ goto failed;
}
prop = JS_NewAtom(ctx, "Function");
if (prop == JS_ATOM_NULL) {
- return NULL;
+ goto failed;
}
ret = JS_DeleteProperty(ctx, global_obj, prop, 0);
JS_FreeAtom(ctx, prop);
if (ret < 0) {
- return NULL;
+ goto failed;
}
JS_FreeValue(ctx, global_obj);
return ctx;
+
+failed:
+
+ JS_FreeValue(ctx, global_obj);
+ JS_FreeContext(ctx);
+
+ return NULL;
}