Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/njs_atom_defs.h
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,7 @@ NJS_DEF_STRING(getMonth, "getMonth", 0, 0)
NJS_DEF_STRING(global, "global", 0, 0)
NJS_DEF_STRING(globalThis, "globalThis", 0, 0)
NJS_DEF_STRING(groups, "groups", 0, 0)
NJS_DEF_STRING(hasOwn, "hasOwn", 0, 0)
NJS_DEF_STRING(hasOwnProperty, "hasOwnProperty", 0, 0)
NJS_DEF_STRING(hasInstance, "hasInstance", 0, 0)
NJS_DEF_STRING(hypot, "hypot", 0, 0)
Expand Down
68 changes: 67 additions & 1 deletion src/njs_date.c
Original file line number Diff line number Diff line change
Expand Up @@ -411,7 +411,8 @@ njs_date_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
} else if (nargs == 2) {
if (njs_is_object(&args[1])) {
if (!njs_is_date(&args[1])) {
ret = njs_value_to_primitive(vm, &args[1], &args[1], 0);
ret = njs_value_to_primitive(vm, &args[1], &args[1],
NJS_HINT_NONE);
if (njs_slow_path(ret != NJS_OK)) {
return ret;
}
Expand Down Expand Up @@ -1381,6 +1382,65 @@ njs_date_prototype_set_time(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
}


static njs_int_t
njs_date_prototype_to_primitive(njs_vm_t *vm, njs_value_t *args,
njs_uint_t nargs, njs_index_t unused, njs_value_t *retval)
{
njs_int_t ret;
njs_uint_t hint;

if (njs_slow_path(!njs_is_date(&args[0]))) {
njs_type_error(vm, "cannot convert %s to date",
njs_type_string(args[0].type));

return NJS_ERROR;
}

if (njs_slow_path(nargs <= 1)) {
goto error;
}

if (njs_slow_path(!njs_is_string(&args[1]))) {
ret = njs_value_to_string(vm, &args[1], &args[1]);
if (njs_slow_path(ret != NJS_OK)) {
return ret;
}
}

ret = njs_atom_atomize_key(vm, &args[1]);
if (njs_slow_path(ret != NJS_OK)) {
return ret;
}

switch (args[1].atom_id) {
case NJS_ATOM_STRING_number:
hint = NJS_HINT_NUMBER;
break;

case NJS_ATOM_STRING_string:
case NJS_ATOM_STRING_default:
hint = NJS_HINT_STRING;
break;

default:
error:
njs_type_error(vm, "invalid hint");
return NJS_ERROR;

}

ret = njs_value_to_primitive(vm, &args[0], &args[0],
hint | NJS_HINT_FORCE_ORDINARY);
if (njs_slow_path(ret != NJS_OK)) {
return ret;
}

njs_value_assign(retval, &args[0]);

return NJS_OK;
}


static njs_int_t
njs_date_prototype_set_fields(njs_vm_t *vm, njs_value_t *args,
njs_uint_t nargs, njs_index_t magic, njs_value_t *retval)
Expand Down Expand Up @@ -1643,6 +1703,12 @@ static const njs_object_prop_init_t njs_date_prototype_properties[] =
NJS_DECLARE_PROP_NATIVE(STRING_setUTCFullYear,
njs_date_prototype_set_fields, 3,
njs_date_magic2(NJS_DATE_YR, 3, 0)),

/* NJS_DECLARE_PROP_NATIVE, but not writable */
NJS_DECLARE_PROP_VALUE(SYMBOL_toPrimitive,
njs_native_function2(njs_date_prototype_to_primitive,
1, 0),
NJS_OBJECT_PROP_VALUE_C),
};


Expand Down
76 changes: 54 additions & 22 deletions src/njs_object.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ static njs_int_t njs_object_define_properties(njs_vm_t *vm, njs_value_t *args,
njs_uint_t nargs, njs_index_t unused, njs_value_t *retval);
static njs_int_t njs_object_set_prototype(njs_vm_t *vm, njs_object_t *object,
const njs_value_t *value);
static njs_int_t njs_object_prototype_has_own_property(njs_vm_t *vm,
njs_value_t *args, njs_uint_t nargs, njs_index_t magic,
njs_value_t *retval);


njs_object_t *
Expand Down Expand Up @@ -2274,8 +2277,10 @@ static const njs_object_prop_init_t njs_object_constructor_properties[] =
NJS_DECLARE_PROP_NATIVE(STRING_assign, njs_object_assign, 2, 0),

NJS_DECLARE_PROP_NATIVE(STRING_is, njs_object_is, 2, 0),
};

NJS_DECLARE_PROP_NATIVE(STRING_hasOwn,
njs_object_prototype_has_own_property, 2, 1),
};

const njs_object_init_t njs_object_constructor_init = {
njs_object_constructor_properties,
Expand Down Expand Up @@ -2590,27 +2595,47 @@ njs_object_to_string(njs_vm_t *vm, njs_value_t *this, njs_value_t *retval)

static njs_int_t
njs_object_prototype_has_own_property(njs_vm_t *vm, njs_value_t *args,
njs_uint_t nargs, njs_index_t unused, njs_value_t *retval)
njs_uint_t nargs, njs_index_t magic, njs_value_t *retval)
{
njs_int_t ret;
njs_value_t *value, *property, lvalue;
njs_property_query_t pq;

value = njs_argument(args, 0);
if (magic == 0) {
/* hasOwnProperty. */

if (njs_is_null_or_undefined(value)) {
njs_type_error(vm, "cannot convert %s argument to object",
njs_type_string(value->type));
return NJS_ERROR;
}
property = njs_lvalue_arg(&lvalue, args, nargs, 1 + magic);
if (njs_slow_path(!njs_is_key(property))) {
ret = njs_value_to_key(vm, property, property);
if (njs_slow_path(ret != NJS_OK)) {
return NJS_ERROR;
}
}

property = njs_lvalue_arg(&lvalue, args, nargs, 1);
value = njs_argument(args, magic);
if (njs_is_null_or_undefined(value)) {
njs_type_error(vm, "cannot convert %s argument to object",
njs_type_string(value->type));
return NJS_ERROR;
}

if (njs_slow_path(!njs_is_key(property))) {
ret = njs_value_to_key(vm, property, property);
if (njs_slow_path(ret != NJS_OK)) {
} else {
/* hasOwn. */

value = njs_lvalue_arg(&lvalue, args, nargs, magic);
if (njs_is_null_or_undefined(value)) {
njs_type_error(vm, "cannot convert %s argument to object",
njs_type_string(value->type));
return NJS_ERROR;
}

property = njs_lvalue_arg(&lvalue, args, nargs, 1 + magic);
if (njs_slow_path(!njs_is_key(property))) {
ret = njs_value_to_key(vm, property, property);
if (njs_slow_path(ret != NJS_OK)) {
return NJS_ERROR;
}
}
}

njs_property_query_init(&pq, NJS_PROPERTY_QUERY_GET, 1);
Expand Down Expand Up @@ -2642,14 +2667,6 @@ njs_object_prototype_prop_is_enumerable(njs_vm_t *vm, njs_value_t *args,
njs_object_prop_t *prop;
njs_property_query_t pq;

value = njs_argument(args, 0);

if (njs_is_null_or_undefined(value)) {
njs_type_error(vm, "cannot convert %s argument to object",
njs_type_string(value->type));
return NJS_ERROR;
}

property = njs_lvalue_arg(&lvalue, args, nargs, 1);

if (njs_slow_path(!njs_is_key(property))) {
Expand All @@ -2659,6 +2676,14 @@ njs_object_prototype_prop_is_enumerable(njs_vm_t *vm, njs_value_t *args,
}
}

value = njs_argument(args, 0);

if (njs_is_null_or_undefined(value)) {
njs_type_error(vm, "cannot convert %s argument to object",
njs_type_string(value->type));
return NJS_ERROR;
}

njs_property_query_init(&pq, NJS_PROPERTY_QUERY_GET, 1);

ret = njs_property_query_val(vm, &pq, value, property);
Expand Down Expand Up @@ -2689,15 +2714,20 @@ njs_object_prototype_is_prototype_of(njs_vm_t *vm, njs_value_t *args,
njs_value_t *prototype, *value;
njs_object_t *object, *proto;

value = njs_arg(args, nargs, 1);

if (njs_slow_path(!njs_is_object(value))) {
goto ret_false;
}

if (njs_slow_path(njs_is_null_or_undefined(njs_argument(args, 0)))) {
njs_type_error(vm, "cannot convert undefined to object");
return NJS_ERROR;
}

prototype = &args[0];
value = njs_arg(args, nargs, 1);

if (njs_is_object(prototype) && njs_is_object(value)) {
if (njs_is_object(prototype)) {
proto = njs_object(prototype);
object = njs_object(value);

Expand All @@ -2712,6 +2742,8 @@ njs_object_prototype_is_prototype_of(njs_vm_t *vm, njs_value_t *args,
} while (object != NULL);
}

ret_false:

njs_set_boolean(retval, 0);

return NJS_OK;
Expand Down
3 changes: 2 additions & 1 deletion src/njs_object.h
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,8 @@ njs_value_to_key2(njs_vm_t *vm, njs_value_t *dst, njs_value_t *value,

} else {
if (convert) {
ret = njs_value_to_primitive(vm, &primitive, value, 1);
ret = njs_value_to_primitive(vm, &primitive, value,
NJS_HINT_STRING);

} else {
ret = njs_object_to_string(vm, value, &primitive);
Expand Down
6 changes: 6 additions & 0 deletions src/njs_symbol.c
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,12 @@ static const njs_object_prop_init_t njs_symbol_prototype_properties[] =
NJS_DECLARE_PROP_NATIVE(STRING_valueOf, njs_symbol_prototype_value_of,
0, 0),

/* NJS_DECLARE_PROP_NATIVE, but not writable */
NJS_DECLARE_PROP_VALUE(SYMBOL_toPrimitive,
njs_native_function2(njs_symbol_prototype_value_of,
1, 0),
NJS_OBJECT_PROP_VALUE_C),

NJS_DECLARE_PROP_NATIVE(STRING_toString,
njs_symbol_prototype_to_string, 0, 0),

Expand Down
71 changes: 58 additions & 13 deletions src/njs_value.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,26 +30,27 @@ 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, NAN);
const njs_value_t njs_value_invalid = njs_value(NJS_INVALID, 0, 0.0);

/*
* A hint value is 0 for numbers and 1 for strings. The value chooses
* method calls order specified by ECMAScript 5.1: "valueOf", "toString"
* for numbers and "toString", "valueOf" for strings.
*/

njs_int_t
njs_value_to_primitive(njs_vm_t *vm, njs_value_t *dst, njs_value_t *value,
njs_uint_t hint)
{
njs_int_t ret;
njs_uint_t tries;
njs_value_t method, retval;
njs_uint_t tries, force_ordinary;
njs_value_t method, retval, arguments[2];
njs_flathsh_query_t fhq;

static const uint32_t atoms[] = {
NJS_ATOM_STRING_valueOf,
NJS_ATOM_STRING_toString,
};

static const njs_uint_t atom_by_hint[] = {
NJS_ATOM_STRING_number,
NJS_ATOM_STRING_string,
NJS_ATOM_STRING_default,
};

if (njs_is_primitive(value)) {
*dst = *value;
return NJS_OK;
Expand All @@ -58,42 +59,86 @@ njs_value_to_primitive(njs_vm_t *vm, njs_value_t *dst, njs_value_t *value,
tries = 0;
fhq.proto = &njs_object_hash_proto;

if (!njs_is_object(value)) {
goto error;
}

force_ordinary = hint & NJS_HINT_FORCE_ORDINARY;
hint &= ~NJS_HINT_FORCE_ORDINARY;

if (force_ordinary) {
goto ordinary;
}

fhq.key_hash = NJS_ATOM_SYMBOL_toPrimitive;

ret = njs_object_property(vm, njs_object(value), &fhq, &method);
if (njs_slow_path(ret == NJS_ERROR)) {
return ret;
}

if (njs_is_function(&method)) {
arguments[0] = *value;

njs_atom_to_value(vm, &arguments[1], atom_by_hint[hint]);

ret = njs_function_apply(vm, njs_function(&method), arguments, 2,
&retval);
if (njs_slow_path(ret != NJS_OK)) {
return ret;
}

if (njs_is_primitive(&retval)) {
goto done;
}

goto error;
}

ordinary:

if (hint != NJS_HINT_STRING) {
hint = NJS_HINT_NUMBER;
}

for ( ;; ) {
ret = NJS_ERROR;

if (njs_is_object(value) && tries < 2) {
if (tries < 2) {
hint ^= tries++;

fhq.key_hash = atoms[hint];

ret = njs_object_property(vm, njs_object(value), &fhq, &method);

if (njs_slow_path(ret == NJS_ERROR)) {
return ret;
}

if (njs_is_function(&method)) {
ret = njs_function_apply(vm, njs_function(&method), value, 1,
&retval);

if (njs_slow_path(ret != NJS_OK)) {
return ret;
}

if (njs_is_primitive(&retval)) {
break;
goto done;
}
}

/* Try the second method. */
continue;
}
}

error:

njs_type_error(vm, "Cannot convert object to primitive value");

return ret;
return NJS_ERROR;
}

done:

*dst = retval;

return NJS_OK;
Expand Down
Loading