&njs_math_object_init,
};
- static const njs_object_prop_t null_proto_property = {
+ static const njs_object_prop_t null_proto_property = {
.type = NJS_WHITEOUT,
.name = njs_string("__proto__"),
.value = njs_value(NJS_NULL, 0, 0.0),
};
+ static const njs_object_prop_t function_prototype_property = {
+ .type = NJS_NATIVE_GETTER,
+ .name = njs_string("prototype"),
+ .value = njs_native_getter(njs_function_prototype_create),
+ };
+
ret = njs_object_hash_create(vm, &vm->shared->null_proto_hash,
&null_proto_property, 1);
if (nxt_slow_path(ret != NXT_OK)) {
return NXT_ERROR;
}
+ ret = njs_object_hash_create(vm, &vm->shared->function_prototype_hash,
+ &function_prototype_property, 1);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return NXT_ERROR;
+ }
+
objects = vm->shared->objects;
for (i = NJS_OBJECT_MATH; i < NJS_OBJECT_MAX; i++) {
prototypes = vm->shared->prototypes;
for (i = NJS_PROTOTYPE_OBJECT; i < NJS_PROTOTYPE_MAX; i++) {
- /* TODO: shared hash: prototype & constructor getters, methods */
-
ret = njs_object_hash_create(vm, &prototypes[i].shared_hash,
prototype_init[i]->properties,
prototype_init[i]->items);
}
}
- /* TODO: create function shared hash: prototype+contructor getter */
-
return NXT_OK;
}
/*
* nxt_mem_cache_zalloc() does also:
* nxt_lvlhsh_init(&function->object.hash);
- * nxt_lvlhsh_init(&function->object.shared_hash);
* function->object.__proto__ = NULL;
*/
+ function->object.shared_hash = vm->shared->function_prototype_hash;
function->object.shared = 1;
function->args_offset = 1;
}
+/*
+ * The "prototype" property of user defined functions is created on
+ * demand in private hash of the functions by the "prototype" getter.
+ * The getter creates a copy of function which is private to nJSVM,
+ * adds a "prototype" object property to the copy, and then adds a
+ * "constructor" property in the prototype object. The "constructor"
+ * property points to the copy of function:
+ * "F.prototype.constructor === F"
+ */
+
+njs_ret_t
+njs_function_prototype_create(njs_vm_t *vm, njs_value_t *value)
+{
+ njs_value_t *proto;
+
+ proto = njs_function_property_prototype_create(vm, value);
+
+ if (nxt_fast_path(proto != NULL)) {
+ vm->retval = *proto;
+ return NXT_OK;
+ }
+
+ return NXT_ERROR;
+}
+
+
+njs_value_t *
+njs_function_property_prototype_create(njs_vm_t *vm, njs_value_t *value)
+{
+ njs_value_t *proto, *cons;
+ njs_object_t *prototype;
+ njs_function_t *function;
+
+ prototype = njs_object_alloc(vm);
+
+ if (nxt_slow_path(prototype == NULL)) {
+ return NULL;
+ }
+
+ function = njs_function_value_copy(vm, value);
+
+ if (nxt_slow_path(function == NULL)) {
+ return NULL;
+ }
+
+ proto = njs_property_prototype_create(vm, &function->object.hash,
+ prototype);
+ if (nxt_slow_path(proto == NULL)) {
+ return NULL;
+ }
+
+ cons = njs_property_constructor_create(vm, &prototype->hash, value);
+
+ if (nxt_fast_path(cons != NULL)) {
+ return proto;
+ }
+
+ return NULL;
+}
+
+
njs_ret_t
njs_function_constructor(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
njs_index_t unused)
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_prototype_create(njs_vm_t *vm, njs_value_t *value);
+njs_value_t *njs_function_property_prototype_create(njs_vm_t *vm,
+ njs_value_t *value);
njs_ret_t njs_function_constructor(njs_vm_t *vm, njs_value_t *args,
nxt_uint_t nargs, njs_index_t unused);
njs_ret_t njs_function_apply(njs_vm_t *vm, njs_function_t *function,
njs_ret_t
njs_object_prototype_create(njs_vm_t *vm, njs_value_t *value)
{
- int32_t index;
- nxt_int_t ret;
- njs_function_t *function;
- njs_object_prop_t *prop;
- nxt_lvlhsh_query_t lhq;
-
- static const njs_value_t prototype = njs_string("prototype");
+ int32_t index;
+ njs_value_t *proto;
+ njs_function_t *function;
+ proto = NULL;
function = value->data.u.function;
index = function - vm->functions;
- if (index < 0 && index > NJS_PROTOTYPE_MAX) {
- vm->retval = njs_value_void;
- return NXT_OK;
+ if (index >= 0 && index < NJS_PROTOTYPE_MAX) {
+ proto = njs_property_prototype_create(vm, &function->object.hash,
+ &vm->prototypes[index]);
+ }
+
+ if (proto == NULL) {
+ proto = (njs_value_t *) &njs_value_void;
}
- prop = njs_object_prop_alloc(vm, &prototype);
+ vm->retval = *proto;
+
+ return NXT_OK;
+}
+
+
+njs_value_t *
+njs_property_prototype_create(njs_vm_t *vm, nxt_lvlhsh_t *hash,
+ njs_object_t *prototype)
+{
+ nxt_int_t ret;
+ njs_object_prop_t *prop;
+ nxt_lvlhsh_query_t lhq;
+
+ static const njs_value_t prototype_string = njs_string("prototype");
+
+ prop = njs_object_prop_alloc(vm, &prototype_string);
if (nxt_slow_path(prop == NULL)) {
- return NXT_ERROR;
+ return NULL;
}
- prop->value.data.u.object = &vm->prototypes[index];
+ /* GC */
+
+ prop->value.data.u.object = prototype;
prop->value.type = NJS_OBJECT;
prop->value.data.truth = 1;
lhq.pool = vm->mem_cache_pool;
lhq.proto = &njs_object_hash_proto;
- ret = nxt_lvlhsh_insert(&function->object.hash, &lhq);
+ ret = nxt_lvlhsh_insert(hash, &lhq);
if (nxt_fast_path(ret == NXT_OK)) {
- vm->retval = prop->value;
+ return &prop->value;
}
- /* TODO: exception NXT_ERROR. */
+ /* Memory allocation or NXT_DECLINED error. */
+ vm->exception = &njs_exception_internal_error;
- return ret;
+ return NULL;
}
static njs_ret_t
njs_object_prototype_create_constructor(njs_vm_t *vm, njs_value_t *value)
{
- int32_t index;
- nxt_int_t ret;
- njs_value_t *constructor;
- njs_object_t *prototype;
- njs_object_prop_t *prop;
- nxt_lvlhsh_query_t lhq;
-
- static const njs_value_t constructor_string = njs_string("constructor");
+ int32_t index;
+ njs_value_t *cons;
+ njs_object_t *prototype;
if (njs_is_object(value)) {
prototype = value->data.u.object;
found:
+ cons = njs_property_constructor_create(vm, &prototype->hash,
+ &vm->scopes[NJS_SCOPE_GLOBAL][index]);
+ if (nxt_fast_path(cons != NULL)) {
+ vm->retval = *cons;
+ return NXT_OK;
+ }
+
+ return NXT_ERROR;
+}
+
+
+njs_value_t *
+njs_property_constructor_create(njs_vm_t *vm, nxt_lvlhsh_t *hash,
+ njs_value_t *constructor)
+{
+ nxt_int_t ret;
+ njs_object_prop_t *prop;
+ nxt_lvlhsh_query_t lhq;
+
+ static const njs_value_t constructor_string = njs_string("constructor");
+
prop = njs_object_prop_alloc(vm, &constructor_string);
if (nxt_slow_path(prop == NULL)) {
- return NXT_ERROR;
+ return NULL;
}
/* GC */
- constructor = &vm->scopes[NJS_SCOPE_GLOBAL][index];
prop->value = *constructor;
-
prop->enumerable = 0;
lhq.value = prop;
lhq.pool = vm->mem_cache_pool;
lhq.proto = &njs_object_hash_proto;
- ret = nxt_lvlhsh_insert(&prototype->hash, &lhq);
+ ret = nxt_lvlhsh_insert(hash, &lhq);
if (nxt_fast_path(ret == NXT_OK)) {
- vm->retval = *constructor;
+ return &prop->value;
}
- return ret;
+ /* Memory allocation or NXT_DECLINED error. */
+ vm->exception = &njs_exception_internal_error;
+
+ return NULL;
}
njs_object_prop_t *njs_object_prop_alloc(njs_vm_t *vm, const njs_value_t *name);
njs_ret_t njs_primitive_prototype_get_proto(njs_vm_t *vm, njs_value_t *value);
njs_ret_t njs_object_prototype_create(njs_vm_t *vm, njs_value_t *value);
+njs_value_t *njs_property_prototype_create(njs_vm_t *vm, nxt_lvlhsh_t *hash,
+ njs_object_t *prototype);
njs_ret_t njs_object_prototype_get_proto(njs_vm_t *vm, njs_value_t *value);
+njs_value_t *njs_property_constructor_create(njs_vm_t *vm, nxt_lvlhsh_t *hash,
+ njs_value_t *constructor);
njs_ret_t njs_object_prototype_to_string(njs_vm_t *vm, njs_value_t *args,
nxt_uint_t nargs, njs_index_t unused);
njs_value_t *val2);
static nxt_noinline nxt_bool_t njs_values_strict_equal(njs_value_t *val1,
njs_value_t *val2);
+static njs_object_t *njs_function_new_object(njs_vm_t *vm, njs_value_t *value);
static njs_ret_t njs_vmcode_continuation(njs_vm_t *vm, njs_value_t *invld1,
njs_value_t *invld2);
static njs_native_frame_t *
const njs_value_t njs_value_zero = njs_value(NJS_NUMBER, 0, 0.0);
const njs_value_t njs_value_nan = njs_value(NJS_NUMBER, 0, NJS_NAN);
+
const njs_value_t njs_string_empty = njs_string("");
const njs_value_t njs_string_comma = njs_string(",");
const njs_value_t njs_string_null = njs_string("null");
}
if (func->code.ctor) {
- object = njs_object_alloc(vm);
-
+ object = njs_function_new_object(vm, value);
if (nxt_slow_path(object == NULL)) {
return NXT_ERROR;
}
}
+static njs_object_t *
+njs_function_new_object(njs_vm_t *vm, njs_value_t *value)
+{
+ nxt_int_t ret;
+ njs_value_t *proto;
+ njs_object_t *object;
+ njs_function_t *function;
+ njs_object_prop_t *prop;
+ nxt_lvlhsh_query_t lhq;
+
+ object = njs_object_alloc(vm);
+
+ if (nxt_fast_path(object != NULL)) {
+
+ lhq.key_hash = NJS_PROTOTYPE_HASH;
+ lhq.key.len = sizeof("prototype") - 1;
+ lhq.key.data = (u_char *) "prototype";
+ lhq.proto = &njs_object_hash_proto;
+ function = value->data.u.function;
+
+ ret = nxt_lvlhsh_find(&function->object.hash, &lhq);
+
+ if (ret == NXT_OK) {
+ prop = lhq.value;
+ proto = &prop->value;
+
+ } else {
+ proto = njs_function_property_prototype_create(vm, value);
+ }
+
+ if (nxt_fast_path(proto != NULL)) {
+ object->__proto__ = proto->data.u.object;
+ return object;
+ }
+ }
+
+ return NULL;
+}
+
+
njs_ret_t
njs_vmcode_method_frame(njs_vm_t *vm, njs_value_t *object, njs_value_t *name)
{
nxt_lvlhsh_t keywords_hash;
nxt_lvlhsh_t values_hash;
nxt_lvlhsh_t null_proto_hash;
+ nxt_lvlhsh_t function_prototype_hash;
njs_object_t objects[NJS_OBJECT_MAX];
"o.a"),
nxt_string("7") },
+ { nxt_string("function F(a, b) { return }"
+ "F.prototype.constructor === F"),
+ nxt_string("true") },
+
+ { nxt_string("function F() { return }"
+ "F.prototype.ok = 'OK';"
+ "var o = new F(); o.ok"),
+ nxt_string("OK") },
+
+ { nxt_string("function F() { return }"
+ "var o = new F();"
+ "o.constructor === F"),
+ nxt_string("true") },
+
{ nxt_string("function a() { return function(x) { return x + 1 } }"
"b = a(); b(2)"),
nxt_string("3") },