property manipulations and to have correct Function.prototype.
for (i = NJS_FUNCTION_OBJECT; i < NJS_FUNCTION_MAX; i++) {
functions[i].native = 1;
functions[i].args_offset = 1;
+ functions[i].shared = 0;
functions[i].u.native = native_functions[i].native;
functions[i].args_types[0] = native_functions[i].args_types[0];
functions[i].args_types[1] = native_functions[i].args_types[1];
nxt_string("OBJECT ") },
{ njs_vmcode_function, sizeof(njs_vmcode_function_t),
nxt_string("FUNCTION ") },
+ { njs_vmcode_function_copy, sizeof(njs_vmcode_function_copy_t),
+ nxt_string("FUNCTION COPY ") },
{ njs_vmcode_regexp, sizeof(njs_vmcode_regexp_t),
nxt_string("REGEXP ") },
function = nxt_mem_cache_zalloc(vm->mem_cache_pool, sizeof(njs_function_t));
if (nxt_fast_path(function != NULL)) {
- function->object.__proto__ = &vm->prototypes[NJS_PROTOTYPE_FUNCTION];
function->args_offset = 1;
+ function->shared = 1;
function->u.lambda = nxt_mem_cache_zalloc(vm->mem_cache_pool,
sizeof(njs_function_lambda_t));
}
+njs_function_t *
+njs_function_value_copy(njs_vm_t *vm, njs_value_t *value)
+{
+ njs_function_t *function;
+
+ function = value->data.u.function;
+
+ if (!function->shared) {
+ return function;
+ }
+
+ function = nxt_mem_cache_alloc(vm->mem_cache_pool, sizeof(njs_function_t));
+
+ if (nxt_fast_path(function != NULL)) {
+ *function = *value->data.u.function;
+ function->object.__proto__ = &vm->prototypes[NJS_PROTOTYPE_FUNCTION];
+ function->shared = 0;
+ value->data.u.function = function;
+ }
+
+ return function;
+}
+
+
njs_ret_t
njs_function_native_frame(njs_vm_t *vm, njs_function_t *function,
const njs_value_t *this, njs_value_t *args, nxt_uint_t nargs,
*function = *args[0].data.u.function;
function->object.__proto__ = &vm->prototypes[NJS_PROTOTYPE_FUNCTION];
+ function->shared = 0;
if (nargs == 1) {
args = (njs_value_t *) &njs_value_void;
njs_function_t *njs_function_alloc(njs_vm_t *vm);
+njs_function_t *njs_function_value_copy(njs_vm_t *vm, njs_value_t *value);
njs_native_frame_t *njs_function_frame_alloc(njs_vm_t *vm, size_t size);
njs_ret_t njs_function_constructor(njs_vm_t *vm, njs_value_t *args,
nxt_uint_t nargs, njs_index_t unused);
static nxt_int_t njs_generator(njs_vm_t *vm, njs_parser_t *parser,
njs_parser_node_t *node);
+static nxt_int_t njs_generate_name(njs_vm_t *vm, njs_parser_t *parser,
+ njs_parser_node_t *node);
static nxt_int_t njs_generate_variable(njs_parser_t *parser,
njs_parser_node_t *node);
static nxt_int_t njs_generate_if_statement(njs_vm_t *vm, njs_parser_t *parser,
njs_parser_node_t *node);
static nxt_int_t njs_generate_method_call(njs_vm_t *vm, njs_parser_t *parser,
njs_parser_node_t *node);
+static nxt_noinline nxt_int_t njs_generate_call(njs_vm_t *vm,
+ njs_parser_t *parser, njs_parser_node_t *node);
static nxt_int_t njs_generate_try_statement(njs_vm_t *vm, njs_parser_t *parser,
njs_parser_node_t *node);
static nxt_int_t njs_generate_throw_statement(njs_vm_t *vm,
return NXT_OK;
case NJS_TOKEN_NAME:
- return njs_generate_variable(parser, node);
+ return njs_generate_name(vm, parser, node);
case NJS_TOKEN_FUNCTION:
return njs_generate_function_declaration(vm, parser, node);
}
+static nxt_int_t
+njs_generate_name(njs_vm_t *vm, njs_parser_t *parser, njs_parser_node_t *node)
+{
+ njs_index_t index;
+ njs_value_t *value;
+ njs_vmcode_function_copy_t *copy;
+
+ index = node->u.variable->index;
+ value = njs_variable_value(parser, index);
+
+ if (value->type == NJS_FUNCTION) {
+
+ node->index = njs_generator_dest_index(vm, parser, node);
+ if (nxt_slow_path(node->index == NJS_INDEX_ERROR)) {
+ return node->index;
+ }
+
+ njs_generate_code(parser, njs_vmcode_function_copy_t, copy);
+ copy->code.operation = njs_vmcode_function_copy;
+ copy->code.operands = NJS_VMCODE_2OPERANDS;
+ copy->code.retval = NJS_VMCODE_RETVAL;
+ copy->retval = node->index;
+ copy->function = index;
+
+ return NXT_OK;
+ }
+
+ return njs_generate_variable(parser, node);
+}
+
+
static nxt_int_t
njs_generate_variable(njs_parser_t *parser, njs_parser_node_t *node)
{
njs_generate_assignment(njs_vm_t *vm, njs_parser_t *parser,
njs_parser_node_t *node)
{
- nxt_int_t ret;
- njs_value_t *value;
- njs_parser_node_t *lvalue, *expr, *object, *property;
- njs_vmcode_move_t *move;
- njs_vmcode_prop_set_t *prop_set;
+ nxt_int_t ret;
+ njs_value_t *value;
+ njs_parser_node_t *lvalue, *expr, *object, *property;
+ njs_vmcode_move_t *move;
+ njs_vmcode_prop_set_t *prop_set;
lvalue = node->left;
expr = node->right;
njs_generate_function_call(njs_vm_t *vm, njs_parser_t *parser,
njs_parser_node_t *node)
{
- uintptr_t nargs;
nxt_int_t ret;
- njs_index_t retval;
- njs_parser_node_t *arg, *name;
- njs_vmcode_move_t *move;
- njs_vmcode_function_call_t *call;
+ njs_parser_node_t *name;
njs_vmcode_function_frame_t *func;
if (node->left != NULL) {
return ret;
}
- nargs = 0;
-
- for (arg = node->right; arg != NULL; arg = arg->right) {
- nargs++;
-
- ret = njs_generator(vm, parser, arg->left);
- if (nxt_slow_path(ret != NXT_OK)) {
- return ret;
- }
+ ret = njs_generate_call(vm, parser, node);
- if (arg->index != arg->left->index) {
- njs_generate_code(parser, njs_vmcode_move_t, move);
- move->code.operation = njs_vmcode_move;
- move->code.operands = NJS_VMCODE_2OPERANDS;
- move->code.retval = NJS_VMCODE_RETVAL;
- move->dst = arg->index;
- move->src = arg->left->index;
- }
- }
-
- func->nargs = nargs;
-
- retval = njs_generator_dest_index(vm, parser, node);
- if (nxt_slow_path(retval == NJS_INDEX_ERROR)) {
- return retval;
+ if (nxt_fast_path(ret >= 0)) {
+ func->nargs = ret;
+ return NXT_OK;
}
- node->index = retval;
-
- njs_generate_code(parser, njs_vmcode_function_call_t, call);
- call->code.operation = njs_vmcode_function_call;
- call->code.operands = NJS_VMCODE_1OPERAND;
- call->code.retval = NJS_VMCODE_NO_RETVAL;
- call->retval = retval;
-
- return NXT_OK;
+ return ret;
}
njs_generate_method_call(njs_vm_t *vm, njs_parser_t *parser,
njs_parser_node_t *node)
{
- uintptr_t nargs;
- nxt_int_t ret;
- njs_index_t retval;
- njs_parser_node_t *arg, *prop;
- njs_vmcode_move_t *move;
- njs_vmcode_method_frame_t *method;
- njs_vmcode_function_call_t *call;
+ nxt_int_t ret;
+ njs_parser_node_t *prop;
+ njs_vmcode_method_frame_t *method;
prop = node->left;
return ret;
}
+ ret = njs_generate_call(vm, parser, node);
+
+ if (nxt_fast_path(ret >= 0)) {
+ method->nargs = ret;
+ return NXT_OK;
+ }
+
+ return ret;
+}
+
+
+static nxt_noinline nxt_int_t
+njs_generate_call(njs_vm_t *vm, njs_parser_t *parser, njs_parser_node_t *node)
+{
+ nxt_int_t ret;
+ nxt_uint_t nargs;
+ njs_index_t retval;
+ njs_parser_node_t *arg;
+ njs_vmcode_move_t *move;
+ njs_vmcode_function_call_t *call;
+
nargs = 0;
for (arg = node->right; arg != NULL; arg = arg->right) {
}
}
- method->nargs = nargs;
-
retval = njs_generator_dest_index(vm, parser, node);
if (nxt_slow_path(retval == NJS_INDEX_ERROR)) {
return retval;
call->code.retval = NJS_VMCODE_NO_RETVAL;
call->retval = retval;
- return NXT_OK;
+ return nargs;
}
value = njs_variable_value(parser, node->index);
value->data.u.function = function;
value->type = NJS_FUNCTION;
+ value->data.truth = 1;
parser = njs_parser_function_create(vm, parser);
if (nxt_slow_path(parser == NULL)) {
value->data.u.function = function;
value->type = NJS_FUNCTION;
+ value->data.truth = 1;
lambda = function->u.lambda;
} else {
break;
}
+ parser->code_size += sizeof(njs_vmcode_function_copy_t);
node->lvalue = NJS_LVALUE_ENABLED;
node->u.variable = var;
break;
njs_property_query_t *pq, njs_value_t *object, int32_t index);
static njs_ret_t njs_object_property_query(njs_vm_t *vm,
njs_property_query_t *pq, njs_value_t *value, njs_object_t *object);
-static njs_ret_t njs_function_private_copy(njs_vm_t *vm,
+static njs_ret_t njs_method_private_copy(njs_vm_t *vm,
njs_property_query_t *pq);
static nxt_noinline uint32_t njs_integer_value(double num);
static nxt_noinline njs_ret_t njs_values_equal(njs_value_t *val1,
njs_ret_t
-njs_vmcode_function(njs_vm_t *vm, njs_value_t *invld1,
- njs_value_t *invld2)
+njs_vmcode_function(njs_vm_t *vm, njs_value_t *invld1, njs_value_t *invld2)
{
njs_function_t *function;
njs_vmcode_function_t *code;
code = (njs_vmcode_function_t *) vm->current;
function->u.lambda = code->lambda;
+
vm->retval.data.u.function = function;
vm->retval.type = NJS_FUNCTION;
vm->retval.data.truth = 1;
}
+njs_ret_t
+njs_vmcode_function_copy(njs_vm_t *vm, njs_value_t *value, njs_value_t *invld)
+{
+ njs_function_t *function;
+
+ if (njs_is_function(value)) {
+
+ function = njs_function_value_copy(vm, value);
+
+ if (nxt_fast_path(function != NULL)) {
+ vm->retval.data.u.function = function;
+ vm->retval.type = NJS_FUNCTION;
+ vm->retval.data.truth = 1;
+
+ return sizeof(njs_vmcode_function_copy_t);
+ }
+
+ return NXT_ERROR;
+ }
+
+ vm->retval = *value;
+
+ njs_retain(value);
+
+ return sizeof(njs_vmcode_function_copy_t);
+}
+
+
njs_ret_t
njs_vmcode_regexp(njs_vm_t *vm, njs_value_t *invld1, njs_value_t *invld2)
{
case NJS_METHOD:
if (pq.shared) {
- ret = njs_function_private_copy(vm, &pq);
+ ret = njs_method_private_copy(vm, &pq);
if (nxt_slow_path(ret != NXT_OK)) {
return ret;
njs_property_query(njs_vm_t *vm, njs_property_query_t *pq, njs_value_t *object,
njs_value_t *property)
{
- double num;
- int32_t index;
- uint32_t (*hash)(const void *, size_t);
- njs_ret_t ret;
- njs_extern_t *ext;
- njs_object_t *obj;
+ double num;
+ int32_t index;
+ uint32_t (*hash)(const void *, size_t);
+ njs_ret_t ret;
+ njs_extern_t *ext;
+ njs_object_t *obj;
+ njs_function_t *function;
hash = nxt_djb_hash;
case NJS_OBJECT_BOOLEAN:
case NJS_OBJECT_NUMBER:
case NJS_OBJECT_STRING:
- case NJS_FUNCTION:
case NJS_REGEXP:
obj = object->data.u.object;
break;
+ case NJS_FUNCTION:
+ function = njs_function_value_copy(vm, object);
+ if (nxt_slow_path(function == NULL)) {
+ return NXT_ERROR;
+ }
+
+ obj = &function->object;
+ break;
+
case NJS_EXTERNAL:
ext = object->data.u.external;
static njs_ret_t
-njs_function_private_copy(njs_vm_t *vm, njs_property_query_t *pq)
+njs_method_private_copy(njs_vm_t *vm, njs_property_query_t *pq)
{
- njs_function_t *func;
+ njs_function_t *function;
njs_object_prop_t *prop, *shared;
prop = nxt_mem_cache_alloc(vm->mem_cache_pool, sizeof(njs_object_prop_t));
shared = pq->lhq.value;
*prop = *shared;
- func = nxt_mem_cache_alloc(vm->mem_cache_pool, sizeof(njs_function_t));
- if (nxt_slow_path(func == NULL)) {
+ function = njs_function_value_copy(vm, &prop->value);
+ if (nxt_slow_path(function == NULL)) {
return NXT_ERROR;
}
- *func = *prop->value.data.u.function;
- func->object.__proto__ = &vm->prototypes[NJS_PROTOTYPE_FUNCTION];
- prop->value.data.u.function = func;
-
pq->lhq.replace = 0;
pq->lhq.value = prop;
pq->lhq.pool = vm->mem_cache_pool;
njs_object_t object;
uint8_t args_types[NJS_ARGS_TYPES_MAX];
+ uint8_t args_offset;
+
+ /*
+ * TODO Shared
+ * When function object is used as value: in assignments,
+ * as function argument, as property and as object to get properties.
+ */
#if (NXT_64BIT)
uint8_t native;
uint8_t continuation_size;
- uint32_t args_offset;
+ uint8_t shared;
#else
uint8_t native;
uint8_t continuation_size;
- uint16_t args_offset;
+ uint8_t shared;
#endif
union {
.continuation_size = _size, \
.args_types = { __VA_ARGS__ }, \
.args_offset = 1, \
+ .shared = 1, \
.u.native = _function, \
} \
} \
} njs_vmcode_function_t;
+typedef struct {
+ njs_vmcode_t code;
+ njs_index_t retval;
+ njs_index_t function;
+} njs_vmcode_function_copy_t;
+
+
typedef struct {
njs_vmcode_t code;
njs_index_t retval;
njs_value_t *inlvd2);
njs_ret_t njs_vmcode_function(njs_vm_t *vm, njs_value_t *inlvd1,
njs_value_t *invld2);
+njs_ret_t njs_vmcode_function_copy(njs_vm_t *vm, njs_value_t *value,
+ njs_value_t *invld);
njs_ret_t njs_vmcode_regexp(njs_vm_t *vm, njs_value_t *inlvd1,
njs_value_t *invld2);
{ nxt_string("[].slice.call()"),
nxt_string("TypeError") },
+ { nxt_string("function f(a) {} ; var a = f; var b = f; a === b"),
+ nxt_string("true") },
+
+ { nxt_string("function f() {} ; f.toString()"),
+ nxt_string("[object Function]") },
+
+ { nxt_string("function f() {}; f"),
+ nxt_string("[object Function]") },
+
+ { nxt_string("function f() {}; f = f + 1; f"),
+ nxt_string("[object Function]1") },
+
+#if 0
+ { nxt_string("function f() {}; f += 1; f"),
+ nxt_string("[object Function]1") },
+#endif
+
+#if 0
+ { nxt_string("function f() {}; function g() { return f }; g()"),
+ nxt_string("[object Function]") },
+#endif
+
+ { nxt_string("function f(a) { return this+a }; var a = f; a.call('0', 1)"),
+ nxt_string("01") },
+
+ { nxt_string("function f(a) { return this+a }; f.call('0', 1)"),
+ nxt_string("01") },
+
+ { nxt_string("function f(a) { return this+a };"
+ "function g(f, a, b) { return f.call(a, b) }; g(f, '0', 1)"),
+ nxt_string("01") },
+
+ { nxt_string("function f(a) { return this+a };"
+ "o = { g: function (f, a, b) { return f.call(a, b) } };"
+ "o.g(f, '0', 1)"),
+ nxt_string("01") },
+
+ { nxt_string("var concat = ''.concat; concat(1,2,3)"),
+ nxt_string("TypeError") },
+
+ { nxt_string("var concat = ''.concat; concat.call(1,2,3)"),
+ nxt_string("123") },
+
+ { nxt_string("var concat = ''.concat; concat.yes = 'OK';"
+ "concat.call(1,2,3, concat.yes)"),
+ nxt_string("123OK") },
+
{ nxt_string("var f = function(a, b) { return this + a + b }"
"var b = f.bind('1'); b('2', '3')"),
nxt_string("123") },