aboutsummaryrefslogtreecommitdiff
path: root/src/njs_function.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/njs_function.c')
-rw-r--r--src/njs_function.c204
1 files changed, 95 insertions, 109 deletions
diff --git a/src/njs_function.c b/src/njs_function.c
index 0840d437..09bc2ebb 100644
--- a/src/njs_function.c
+++ b/src/njs_function.c
@@ -126,11 +126,25 @@ njs_function_name_set(njs_vm_t *vm, njs_function_t *function,
njs_object_prop_t *prop;
njs_flathsh_query_t lhq;
- prop = njs_object_prop_alloc(vm, name, 0);
- if (njs_slow_path(prop == NULL)) {
+ lhq.key_hash = NJS_ATOM_STRING_name;
+ lhq.replace = 0;
+ lhq.pool = vm->mem_pool;
+ lhq.proto = &njs_object_hash_proto;
+
+ ret = njs_flathsh_unique_insert(&function->object.hash, &lhq);
+ if (njs_slow_path(ret != NJS_OK)) {
+ njs_internal_error(vm, "lvlhsh insert failed");
return NJS_ERROR;
}
+ prop = lhq.value;
+
+ prop->type = NJS_PROPERTY;
+ prop->enumerable = 0;
+ prop->configurable = 1;
+ prop->writable = 0;
+ prop->u.value = *name;
+
symbol = 0;
if (njs_is_symbol(njs_prop_value(prop))) {
@@ -172,20 +186,6 @@ njs_function_name_set(njs_vm_t *vm, njs_function_t *function,
}
}
- prop->configurable = 1;
-
- lhq.value = prop;
- lhq.key_hash = NJS_ATOM_STRING_name;
- lhq.replace = 0;
- lhq.pool = vm->mem_pool;
- lhq.proto = &njs_object_hash_proto;
-
- ret = njs_flathsh_unique_insert(&function->object.hash, &lhq);
- if (njs_slow_path(ret != NJS_OK)) {
- njs_internal_error(vm, "lvlhsh insert failed");
- return NJS_ERROR;
- }
-
return NJS_OK;
}
@@ -327,12 +327,12 @@ njs_function_prototype_thrower(njs_vm_t *vm, njs_value_t *args,
const njs_object_prop_init_t njs_arguments_object_instance_properties[] =
{
{
- .atom_id = NJS_ATOM_STRING_callee,
.desc = {
+ .atom_id = NJS_ATOM_STRING_callee,
.type = NJS_ACCESSOR,
.u.accessor = njs_accessor(njs_function_prototype_thrower, 0,
njs_function_prototype_thrower, 0),
- .writable = NJS_ATTRIBUTE_UNSET,
+ .writable = 0,
},
},
};
@@ -394,6 +394,19 @@ njs_function_lambda_frame(njs_vm_t *vm, njs_function_t *function,
lambda = function->u.lambda;
+ /*
+ * Lambda frame has the following layout:
+ * njs_frame_t | p0 , p2, ..., pn | v0, v1, ..., vn
+ * where:
+ * p0, p1, ..., pn - pointers to arguments and locals,
+ * v0, v1, ..., vn - values of arguments and locals.
+ * n - number of arguments + locals.
+ *
+ * Normally, the pointers point to the values directly after them,
+ * but if a value was captured as a closure by an inner function,
+ * pn points to a value allocated from the heap.
+ */
+
args_count = njs_max(nargs, lambda->nargs);
value_count = args_count + lambda->nlocal;
@@ -523,7 +536,6 @@ njs_function_lambda_call(njs_vm_t *vm, njs_value_t *retval, void *promise_cap)
njs_value_t *args, **local, *value;
njs_value_t **cur_local, **cur_closures;
njs_function_t *function;
- njs_declaration_t *declr;
njs_function_lambda_t *lambda;
frame = (njs_frame_t *) vm->top_frame;
@@ -582,29 +594,6 @@ njs_function_lambda_call(njs_vm_t *vm, njs_value_t *retval, void *promise_cap)
vm->active_frame = frame;
- /* Closures */
-
- n = lambda->ndeclarations;
-
- while (n != 0) {
- n--;
-
- declr = &lambda->declarations[n];
- value = njs_scope_value(vm, declr->index);
-
- *value = *declr->value;
-
- function = njs_function_value_copy(vm, value);
- if (njs_slow_path(function == NULL)) {
- return NJS_ERROR;
- }
-
- ret = njs_function_capture_closure(vm, function, function->u.lambda);
- if (njs_slow_path(ret != NJS_OK)) {
- return ret;
- }
- }
-
ret = njs_vmcode_interpreter(vm, lambda->start, retval, promise_cap, NULL);
/* Restore current level. */
@@ -699,11 +688,11 @@ njs_function_frame_free(njs_vm_t *vm, njs_native_frame_t *native)
njs_int_t
njs_function_frame_save(njs_vm_t *vm, njs_frame_t *frame, u_char *pc)
{
- size_t args_count, value_count, n;
- njs_value_t *start, *end, *p, **new, *value, **local;
- njs_function_t *function;
+ size_t args_count, value_count, n;
+ njs_value_t **map, *value, **current_map;
+ njs_function_t *function;
+ njs_native_frame_t *active, *native;
njs_function_lambda_t *lambda;
- njs_native_frame_t *active, *native;
*frame = *vm->active_frame;
@@ -721,34 +710,37 @@ njs_function_frame_save(njs_vm_t *vm, njs_frame_t *frame, u_char *pc)
args_count = njs_max(native->nargs, lambda->nargs);
value_count = args_count + lambda->nlocal;
- new = (njs_value_t **) ((u_char *) native + NJS_FRAME_SIZE);
- value = (njs_value_t *) (new + value_count);
-
- native->arguments = value;
- native->local = new + njs_function_frame_args_count(active);
- native->pc = pc;
+ /*
+ * We need to save the current frame state because it will be freed
+ * when the function returns.
+ *
+ * To detect whether a value is captured as a closure,
+ * we check whether the pointer is within the frame. In this case
+ * the pointer is copied as is because the value it points to
+ * is already allocated in the heap and will not be freed.
+ * See njs_function_capture_closure() and njs_function_lambda_frame()
+ * for details.
+ */
- start = njs_function_frame_values(active, &end);
- p = native->arguments;
+ map = (njs_value_t **) ((u_char *) native + NJS_FRAME_SIZE);
+ value = (njs_value_t *) (map + value_count);
- while (start < end) {
- njs_value_assign(p, start++);
- *new++ = p++;
- }
+ current_map = (njs_value_t **) ((u_char *) active + NJS_FRAME_SIZE);
- /* Move all arguments. */
+ for (n = 0; n < value_count; n++) {
+ if (njs_is_value_allocated_on_frame(active, current_map[n])) {
+ map[n] = &value[n];
+ njs_value_assign(&value[n], current_map[n]);
- p = native->arguments;
- local = native->local + 1 /* this */;
-
- for (n = 0; n < function->args_count; n++) {
- if (!njs_is_valid(p)) {
- njs_set_undefined(p);
+ } else {
+ map[n] = current_map[n];
}
-
- *local++ = p++;
}
+ native->arguments = value;
+ native->local = map + args_count;
+ native->pc = pc;
+
return NJS_OK;
}
@@ -770,7 +762,6 @@ njs_int_t
njs_function_capture_closure(njs_vm_t *vm, njs_function_t *function,
njs_function_lambda_t *lambda)
{
- void *start, *end;
uint32_t n;
njs_value_t *value, **closure;
njs_native_frame_t *frame;
@@ -785,9 +776,6 @@ njs_function_capture_closure(njs_vm_t *vm, njs_function_t *function,
frame = frame->previous;
}
- start = frame;
- end = frame->free;
-
closure = njs_function_closures(function);
n = lambda->nclosures;
@@ -796,7 +784,7 @@ njs_function_capture_closure(njs_vm_t *vm, njs_function_t *function,
value = njs_scope_value(vm, lambda->closures[n]);
- if (start <= (void *) value && (void *) value < end) {
+ if (njs_is_value_allocated_on_frame(frame, value)) {
value = njs_scope_value_clone(vm, lambda->closures[n], value);
if (njs_slow_path(value == NULL)) {
return NJS_ERROR;
@@ -812,14 +800,14 @@ njs_function_capture_closure(njs_vm_t *vm, njs_function_t *function,
njs_inline njs_value_t *
-njs_function_closure_value(njs_vm_t *vm, njs_value_t **scope, njs_index_t index,
- void *start, void *end)
+njs_function_closure_value(njs_vm_t *vm, njs_native_frame_t *frame,
+ njs_value_t **scope, njs_index_t index)
{
njs_value_t *value, *newval;
value = scope[njs_scope_index_value(index)];
- if (start <= (void *) value && end > (void *) value) {
+ if (njs_is_value_allocated_on_frame(frame, value)) {
newval = njs_mp_alloc(vm->mem_pool, sizeof(njs_value_t));
if (njs_slow_path(newval == NULL)) {
njs_memory_error(vm);
@@ -839,7 +827,6 @@ njs_function_closure_value(njs_vm_t *vm, njs_value_t **scope, njs_index_t index,
njs_int_t
njs_function_capture_global_closures(njs_vm_t *vm, njs_function_t *function)
{
- void *start, *end;
uint32_t n;
njs_value_t *value, **refs, **global;
njs_index_t *indexes, index;
@@ -858,9 +845,6 @@ njs_function_capture_global_closures(njs_vm_t *vm, njs_function_t *function)
native = native->previous;
}
- start = native;
- end = native->free;
-
indexes = lambda->closures;
refs = njs_function_closures(function);
@@ -875,12 +859,12 @@ njs_function_capture_global_closures(njs_vm_t *vm, njs_function_t *function)
switch (njs_scope_index_type(index)) {
case NJS_LEVEL_LOCAL:
- value = njs_function_closure_value(vm, native->local, index,
- start, end);
+ value = njs_function_closure_value(vm, native, native->local,
+ index);
break;
case NJS_LEVEL_GLOBAL:
- value = njs_function_closure_value(vm, global, index, start, end);
+ value = njs_function_closure_value(vm, native, global, index);
break;
default:
@@ -910,14 +894,6 @@ njs_function_property_prototype_set(njs_vm_t *vm, njs_flathsh_t *hash,
njs_object_prop_t *prop;
njs_flathsh_query_t lhq;
- prop = njs_object_prop_alloc(vm, prototype, 0);
- if (njs_slow_path(prop == NULL)) {
- return NULL;
- }
-
- prop->writable = 1;
-
- lhq.value = prop;
lhq.key_hash = NJS_ATOM_STRING_prototype;
lhq.replace = 1;
lhq.pool = vm->mem_pool;
@@ -925,13 +901,21 @@ njs_function_property_prototype_set(njs_vm_t *vm, njs_flathsh_t *hash,
ret = njs_flathsh_unique_insert(hash, &lhq);
- if (njs_fast_path(ret == NJS_OK)) {
- return njs_prop_value(prop);
+ if (njs_slow_path(ret != NJS_OK)) {
+ njs_internal_error(vm, "lvlhsh insert failed");
+ return NULL;
}
- njs_internal_error(vm, "lvlhsh insert failed");
+ prop = lhq.value;
+
+ prop->type = NJS_PROPERTY;
+ prop->enumerable = 0;
+ prop->configurable = 0;
+ prop->writable = 1;
+ prop->u.value = *prototype;
+
+ return njs_prop_value(prop);
- return NULL;
}
@@ -950,9 +934,8 @@ njs_function_prototype_create(njs_vm_t *vm, njs_object_prop_t *prop,
uint32_t unused, njs_value_t *value, njs_value_t *setval,
njs_value_t *retval)
{
- njs_value_t *proto, proto_value, *cons;
- njs_object_t *prototype;
- njs_function_t *function;
+ njs_value_t *proto, proto_value, *cons;
+ njs_object_t *prototype;
if (setval == NULL) {
prototype = njs_object_alloc(vm);
@@ -965,11 +948,6 @@ njs_function_prototype_create(njs_vm_t *vm, njs_object_prop_t *prop,
setval = &proto_value;
}
- function = njs_function_value_copy(vm, value);
- if (njs_slow_path(function == NULL)) {
- return NJS_ERROR;
- }
-
proto = njs_function_property_prototype_set(vm, njs_object_hash(value),
setval);
if (njs_slow_path(proto == NULL)) {
@@ -1049,7 +1027,7 @@ njs_function_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
}
}
- njs_chb_append_literal(&chain, "){");
+ njs_chb_append_literal(&chain, "\n){\n");
if (nargs > 1) {
ret = njs_value_to_chain(vm, &chain, njs_argument(args, nargs - 1));
@@ -1058,7 +1036,7 @@ njs_function_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
}
}
- njs_chb_append_literal(&chain, "})");
+ njs_chb_append_literal(&chain, "\n})");
ret = njs_chb_join(&chain, &str);
if (njs_slow_path(ret != NJS_OK)) {
@@ -1125,7 +1103,15 @@ njs_function_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
njs_chb_destroy(&chain);
- lambda = ((njs_vmcode_function_t *) generator.code_start)->lambda;
+ if ((code->end - code->start)
+ != (sizeof(njs_vmcode_function_t) + sizeof(njs_vmcode_return_t))
+ || ((njs_vmcode_generic_t *) code->start)->code != NJS_VMCODE_FUNCTION)
+ {
+ njs_syntax_error(vm, "single function literal required");
+ return NJS_ERROR;
+ }
+
+ lambda = ((njs_vmcode_function_t *) code->start)->lambda;
function = njs_function_alloc(vm, lambda, (njs_bool_t) async);
if (njs_slow_path(function == NULL)) {
@@ -1446,23 +1432,23 @@ static const njs_object_prop_init_t njs_function_prototype_properties[] =
NJS_DECLARE_PROP_NATIVE(STRING_bind, njs_function_prototype_bind, 1, 0),
{
- .atom_id = NJS_ATOM_STRING_caller,
.desc = {
+ .atom_id = NJS_ATOM_STRING_caller,
.type = NJS_ACCESSOR,
.u.accessor = njs_accessor(njs_function_prototype_thrower, 0,
njs_function_prototype_thrower, 0),
- .writable = NJS_ATTRIBUTE_UNSET,
+ .writable = 0,
.configurable = 1,
},
},
{
- .atom_id = NJS_ATOM_STRING_arguments,
.desc = {
+ .atom_id = NJS_ATOM_STRING_arguments,
.type = NJS_ACCESSOR,
.u.accessor = njs_accessor(njs_function_prototype_thrower, 0,
njs_function_prototype_thrower, 0),
- .writable = NJS_ATTRIBUTE_UNSET,
+ .writable = 0,
.configurable = 1,
},
},