static njs_vm_t * ngx_http_js_compile(ngx_conf_t *cf, ngx_str_t *script) { u_char *start, *end; nxt_int_t rc; nxt_str_t s; njs_vm_t *vm; nxt_lvlhsh_t externals; njs_vm_shared_t *shared; nxt_mem_cache_pool_t *mcp; mcp = ngx_http_js_create_mem_cache_pool(); if (mcp == NULL) { return NULL; } shared = NULL; nxt_lvlhsh_init(&externals); if (njs_add_external(&externals, mcp, 0, ngx_http_js_externals, nxt_nitems(ngx_http_js_externals)) != NJS_OK) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "could not add js externals"); return NULL; } vm = njs_vm_create(mcp, &shared, &externals); if (vm == NULL) { return NULL; } start = script->data; end = start + script->len; rc = njs_vm_compile(vm, &start, end); if (rc != NJS_OK) { njs_vm_exception(vm, &s); ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "js compilation error: \"%*s\"", s.len, s.data); return NULL; } if (start != end) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "extra characters in js script: \"%*s\"", end - start, start); return NULL; } return vm; }
nxt_int_t njs_array_prototype_hash(njs_vm_t *vm, nxt_lvlhsh_t *hash) { return njs_object_hash_create(vm, hash, njs_array_prototype_properties, nxt_nitems(njs_array_prototype_properties)); }
static void njs_disassemble(u_char *start, u_char *end) { u_char *p; nxt_str_t *name; nxt_uint_t n; const char *sign; njs_code_name_t *code_name; njs_vmcode_jump_t *jump; njs_vmcode_1addr_t *code1; njs_vmcode_2addr_t *code2; njs_vmcode_3addr_t *code3; njs_vmcode_array_t *array; njs_vmcode_catch_t *catch; njs_vmcode_try_end_t *try_end; njs_vmcode_try_start_t *try_start; njs_vmcode_operation_t operation; njs_vmcode_cond_jump_t *cond_jump; njs_vmcode_test_jump_t *test_jump; njs_vmcode_prop_next_t *prop_next; njs_vmcode_equal_jump_t *equal; njs_vmcode_prop_foreach_t *prop_foreach; njs_vmcode_method_frame_t *method; p = start; /* * On some 32-bit platform uintptr_t is int and compilers warn * about %l format modifier. size_t has the size as pointer so * there is no run-time overhead. */ while (p < end) { operation = *(njs_vmcode_operation_t *) p; if (operation == njs_vmcode_array) { array = (njs_vmcode_array_t *) p; p += sizeof(njs_vmcode_array_t); printf("ARRAY %04zX %zd\n", (size_t) array->retval, (size_t) array->length); continue; } if (operation == njs_vmcode_if_true_jump) { cond_jump = (njs_vmcode_cond_jump_t *) p; p += sizeof(njs_vmcode_cond_jump_t); sign = (cond_jump->offset >= 0) ? "+" : ""; printf("JUMP IF TRUE %04zX %s%zd\n", (size_t) cond_jump->cond, sign, (size_t) cond_jump->offset); continue; } if (operation == njs_vmcode_if_false_jump) { cond_jump = (njs_vmcode_cond_jump_t *) p; p += sizeof(njs_vmcode_cond_jump_t); sign = (cond_jump->offset >= 0) ? "+" : ""; printf("JUMP IF FALSE %04zX %s%zd\n", (size_t) cond_jump->cond, sign, (size_t) cond_jump->offset); continue; } if (operation == njs_vmcode_jump) { jump = (njs_vmcode_jump_t *) p; p += sizeof(njs_vmcode_jump_t); sign = (jump->offset >= 0) ? "+" : ""; printf("JUMP %s%zd\n", sign, (size_t) jump->offset); continue; } if (operation == njs_vmcode_if_equal_jump) { equal = (njs_vmcode_equal_jump_t *) p; p += sizeof(njs_vmcode_equal_jump_t); printf("JUMP IF EQUAL %04zX %04zX +%zd\n", (size_t) equal->value1, (size_t) equal->value2, (size_t) equal->offset); continue; } if (operation == njs_vmcode_test_if_true) { test_jump = (njs_vmcode_test_jump_t *) p; p += sizeof(njs_vmcode_test_jump_t); printf("TEST IF TRUE %04zX %04zX +%zd\n", (size_t) test_jump->retval, (size_t) test_jump->value, (size_t) test_jump->offset); continue; } if (operation == njs_vmcode_test_if_false) { test_jump = (njs_vmcode_test_jump_t *) p; p += sizeof(njs_vmcode_test_jump_t); printf("TEST IF FALSE %04zX %04zX +%zd\n", (size_t) test_jump->retval, (size_t) test_jump->value, (size_t) test_jump->offset); continue; } if (operation == njs_vmcode_method_frame) { method = (njs_vmcode_method_frame_t *) p; p += sizeof(njs_vmcode_method_frame_t); printf("METHOD FRAME %04zX %04zX %d\n", (size_t) method->object, (size_t) method->method, method->code.nargs); continue; } if (operation == njs_vmcode_property_foreach) { prop_foreach = (njs_vmcode_prop_foreach_t *) p; p += sizeof(njs_vmcode_prop_foreach_t); printf("PROPERTY FOREACH %04zX %04zX +%zd\n", (size_t) prop_foreach->next, (size_t) prop_foreach->object, (size_t) prop_foreach->offset); continue; } if (operation == njs_vmcode_property_next) { prop_next = (njs_vmcode_prop_next_t *) p; p += sizeof(njs_vmcode_prop_next_t); printf("PROPERTY NEXT %04zX %04zX %04zX %zd\n", (size_t) prop_next->retval, (size_t) prop_next->object, (size_t) prop_next->next, (size_t) prop_next->offset); continue; } if (operation == njs_vmcode_try_start) { try_start = (njs_vmcode_try_start_t *) p; p += sizeof(njs_vmcode_try_start_t); printf("TRY START %04zX +%zd\n", (size_t) try_start->value, (size_t) try_start->offset); continue; } if (operation == njs_vmcode_catch) { catch = (njs_vmcode_catch_t *) p; p += sizeof(njs_vmcode_catch_t); printf("CATCH %04zX +%zd\n", (size_t) catch->exception, (size_t) catch->offset); continue; } if (operation == njs_vmcode_try_end) { try_end = (njs_vmcode_try_end_t *) p; p += sizeof(njs_vmcode_try_end_t); printf("TRY END +%zd\n", (size_t) try_end->offset); continue; } code_name = code_names; n = nxt_nitems(code_names); do { if (operation == code_name->operation) { name = &code_name->name; if (code_name->size == sizeof(njs_vmcode_3addr_t)) { code3 = (njs_vmcode_3addr_t *) p; printf("%*s %04zX %04zX %04zX\n", (int) name->len, name->data, (size_t) code3->dst, (size_t) code3->src1, (size_t) code3->src2); } else if (code_name->size == sizeof(njs_vmcode_2addr_t)) { code2 = (njs_vmcode_2addr_t *) p; printf("%*s %04zX %04zX\n", (int) name->len, name->data, (size_t) code2->dst, (size_t) code2->src); } else if (code_name->size == sizeof(njs_vmcode_1addr_t)) { code1 = (njs_vmcode_1addr_t *) p; printf("%*s %04zX\n", (int) name->len, name->data, (size_t) code1->index); } p += code_name->size; goto next; } code_name++; n--; } while (n != 0); p += sizeof(njs_vmcode_operation_t); printf("UNKNOWN %04zX\n", (size_t) (uintptr_t) operation); next: continue; }
.name = njs_string("length"), .value = njs_value(NJS_NUMBER, 1, 2.0), }, /* RegExp.prototype. */ { .type = NJS_NATIVE_GETTER, .name = njs_string("prototype"), .value = njs_native_getter(njs_object_prototype_create), }, }; const njs_object_init_t njs_regexp_constructor_init = { njs_regexp_constructor_properties, nxt_nitems(njs_regexp_constructor_properties), }; static const njs_object_prop_t njs_regexp_prototype_properties[] = { { .type = NJS_NATIVE_GETTER, .name = njs_string("lastIndex"), .value = njs_native_getter(njs_regexp_prototype_last_index), }, { .type = NJS_NATIVE_GETTER, .name = njs_string("global"), .value = njs_native_getter(njs_regexp_prototype_global),
static char * ngx_http_js_include(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_http_js_loc_conf_t *jlcf = conf; size_t size; u_char *start, *end; ssize_t n; ngx_fd_t fd; ngx_str_t *value, file; nxt_int_t rc; nxt_str_t text, ext, *export; nxt_lvlhsh_t externals; ngx_file_info_t fi; njs_vm_shared_t *shared; ngx_pool_cleanup_t *cln; nxt_mem_cache_pool_t *mcp; if (jlcf->vm) { return "is duplicate"; } value = cf->args->elts; file = value[1]; if (ngx_conf_full_name(cf->cycle, &file, 1) != NGX_OK) { return NGX_CONF_ERROR; } fd = ngx_open_file(file.data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0); if (fd == NGX_INVALID_FILE) { ngx_conf_log_error(NGX_LOG_EMERG, cf, ngx_errno, ngx_open_file_n " \"%s\" failed", file.data); return NGX_CONF_ERROR; } if (ngx_fd_info(fd, &fi) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_EMERG, cf->log, ngx_errno, ngx_fd_info_n " \"%s\" failed", file.data); (void) ngx_close_file(fd); return NGX_CONF_ERROR; } size = ngx_file_size(&fi); start = ngx_pnalloc(cf->pool, size); if (start == NULL) { (void) ngx_close_file(fd); return NGX_CONF_ERROR; } n = ngx_read_fd(fd, start, size); if (n == -1) { ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno, ngx_read_fd_n " \"%s\" failed", file.data); (void) ngx_close_file(fd); return NGX_CONF_ERROR; } if ((size_t) n != size) { ngx_log_error(NGX_LOG_ALERT, cf->log, 0, ngx_read_fd_n " has read only %z of %O from \"%s\"", n, size, file.data); (void) ngx_close_file(fd); return NGX_CONF_ERROR; } if (ngx_close_file(fd) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno, ngx_close_file_n " %s failed", file.data); } end = start + size; mcp = ngx_http_js_create_mem_cache_pool(); if (mcp == NULL) { return NGX_CONF_ERROR; } cln = ngx_pool_cleanup_add(cf->pool, 0); if (cln == NULL) { return NULL; } cln->handler = ngx_http_js_cleanup_mem_cache_pool; cln->data = mcp; shared = NULL; nxt_lvlhsh_init(&externals); if (njs_vm_external_add(&externals, mcp, 0, ngx_http_js_externals, nxt_nitems(ngx_http_js_externals)) != NJS_OK) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "could not add js externals"); return NGX_CONF_ERROR; } jlcf->vm = njs_vm_create(mcp, &shared, &externals); if (jlcf->vm == NULL) { return NGX_CONF_ERROR; } rc = njs_vm_compile(jlcf->vm, &start, end, NULL, &export); if (rc != NJS_OK) { njs_vm_exception(jlcf->vm, &text); ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%*s, included", text.length, text.start); return NGX_CONF_ERROR; } if (start != end) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "extra characters in js script: \"%*s\", included", end - start, start); return NGX_CONF_ERROR; } ext = nxt_string_value("$r"); if (njs_vm_external(jlcf->vm, NULL, &ext, &jlcf->args[0]) != NJS_OK) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "js external \"%*s\" not found", ext.length, ext.start); return NGX_CONF_ERROR; } ext = nxt_string_value("response"); rc = njs_vm_external(jlcf->vm, &jlcf->args[0], &ext, &jlcf->args[1]); if (rc != NXT_OK) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "js external \"$r.%*s\" not found", ext.length, ext.start); return NGX_CONF_ERROR; } return NGX_CONF_OK; }
NULL, NULL, NULL, NULL, NULL, ngx_http_js_ext_finish, 0 }, }; static njs_external_t ngx_http_js_ext_request[] = { { nxt_string("response"), NJS_EXTERN_OBJECT, ngx_http_js_ext_response, nxt_nitems(ngx_http_js_ext_response), NULL, NULL, NULL, NULL, NULL, NULL, 0 }, { nxt_string("log"), NJS_EXTERN_METHOD, NULL, 0, NULL, NULL, NULL,
/* Object.prototype. */ { njs_native_getter(njs_object_prototype_create), njs_string("prototype"), NJS_NATIVE_GETTER, 0, 0, 0, }, /* Object.create(). */ { njs_native_function(njs_object_create, 0), njs_string("create"), NJS_METHOD, 0, 0, 0, }, }; const njs_object_init_t njs_object_constructor_init = { njs_object_constructor_properties, nxt_nitems(njs_object_constructor_properties), }; static njs_ret_t njs_object_prototype_get_proto(njs_vm_t *vm, njs_value_t *value) { njs_object_t *proto; proto = value->data.u.object->__proto__; if (nxt_fast_path(proto != NULL)) { vm->retval.data.u.object = proto; vm->retval.type = NJS_OBJECT; vm->retval.data.truth = 1;