This closes #255 issue on Github.
static const njs_object_prop_t njs_global_this_object_properties[] =
{
+ {
+ .type = NJS_PROPERTY,
+ .name = njs_wellknown_symbol(NJS_SYMBOL_TO_STRING_TAG),
+ .value = njs_string("global"),
+ .configurable = 1,
+ },
+
/* Global constants. */
{
static const njs_object_prop_t njs_njs_object_properties[] =
{
+ {
+ .type = NJS_PROPERTY,
+ .name = njs_wellknown_symbol(NJS_SYMBOL_TO_STRING_TAG),
+ .value = njs_string("njs"),
+ .configurable = 1,
+ },
+
{
.type = NJS_PROPERTY,
.name = njs_string("version"),
static const njs_object_prop_t njs_process_object_properties[] =
{
+ {
+ .type = NJS_PROPERTY,
+ .name = njs_wellknown_symbol(NJS_SYMBOL_TO_STRING_TAG),
+ .value = njs_string("process"),
+ .configurable = 1,
+ },
+
{
.type = NJS_PROPERTY_HANDLER,
.name = njs_string("argv"),
}
-static njs_int_t
-njs_hash_prototype_to_string(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
- njs_index_t unused)
-{
- static const njs_value_t string = njs_string("[object Hash]");
-
- vm->retval = string;
-
- return NJS_OK;
-}
-
-
static const njs_object_prop_t njs_hash_prototype_properties[] =
{
{
{
.type = NJS_PROPERTY,
- .name = njs_string("toString"),
- .value = njs_native_function(njs_hash_prototype_to_string, 0),
- .writable = 1,
+ .name = njs_wellknown_symbol(NJS_SYMBOL_TO_STRING_TAG),
+ .value = njs_string("Hash"),
.configurable = 1,
},
}
-static njs_int_t
-njs_hmac_prototype_to_string(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
- njs_index_t unused)
-{
- static const njs_value_t string = njs_string("[object Hmac]");
-
- vm->retval = string;
-
- return NJS_OK;
-}
-
-
static const njs_object_prop_t njs_hmac_prototype_properties[] =
{
{
{
.type = NJS_PROPERTY,
- .name = njs_string("toString"),
- .value = njs_native_function(njs_hmac_prototype_to_string, 0),
- .writable = 1,
+ .name = njs_wellknown_symbol(NJS_SYMBOL_TO_STRING_TAG),
+ .value = njs_string("Hmac"),
.configurable = 1,
},
{
njs_function_t *parse;
- parse = njs_function(&njs_json_object_properties[0].value);
+ parse = njs_function(&njs_json_object_properties[1].value);
return njs_vm_call(vm, parse, args, nargs);
}
{
njs_function_t *stringify;
- stringify = njs_function(&njs_json_object_properties[1].value);
+ stringify = njs_function(&njs_json_object_properties[2].value);
return njs_vm_call(vm, stringify, args, nargs);
}
#define njs_json_stringify_append(str, len) \
- ret = njs_json_buf_append(stringify, str, len); \
+ ret = njs_json_buf_append(stringify, (char *) str, len); \
if (ret != NJS_OK) { \
goto memory_error; \
}
if (stringify->space.length != 0) { \
njs_json_stringify_append("\n", 1); \
for (i = 0; i < (njs_int_t) (times) - 1; i++) { \
- njs_json_stringify_append((char *) stringify->space.start, \
+ njs_json_stringify_append(stringify->space.start, \
stringify->space.length); \
} \
}
static const njs_object_prop_t njs_json_object_properties[] =
{
- /* JSON.parse(). */
+ {
+ .type = NJS_PROPERTY,
+ .name = njs_wellknown_symbol(NJS_SYMBOL_TO_STRING_TAG),
+ .value = njs_string("JSON"),
+ .configurable = 1,
+ },
+
{
.type = NJS_PROPERTY,
.name = njs_string("parse"),
.configurable = 1,
},
- /* JSON.stringify(). */
{
.type = NJS_PROPERTY,
.name = njs_string("stringify"),
njs_int_t i;
njs_int_t ret;
njs_str_t str;
- njs_value_t *key, *val, ext_val;
+ njs_value_t *key, *val, tag, ext_val;
njs_object_t *object;
njs_json_state_t *state;
+ njs_string_prop_t string;
njs_object_prop_t *prop;
njs_lvlhsh_query_t lhq;
njs_json_stringify_t *stringify, dump_stringify;
switch (state->type) {
case NJS_JSON_OBJECT:
if (state->index == 0) {
+ ret = njs_object_string_tag(vm, &state->value, &tag);
+ if (njs_slow_path(ret == NJS_ERROR)) {
+ return ret;
+ }
+
+ if (ret == NJS_OK) {
+ (void) njs_string_prop(&string, &tag);
+ njs_json_stringify_append(string.start, string.size)
+ njs_json_stringify_append(" ", 1);
+ }
+
njs_json_stringify_append("{", 1);
njs_json_stringify_indent(stringify->depth + 1);
}
state->written = 1;
njs_key_string_get(vm, key, &lhq.key);
- njs_json_stringify_append((char *) lhq.key.start, lhq.key.length);
+ njs_json_stringify_append(lhq.key.start, lhq.key.length);
njs_json_stringify_append(":", 1);
if (stringify->space.length != 0) {
njs_json_stringify_append(" ", 1);
#define njs_lvlhsh_init(lh) \
(lh)->slot = NULL
+
+#define njs_lvlhsh_eq(lhl, lhr) \
+ ((lhl)->slot == (lhr)->slot)
+
/*
* njs_lvlhsh_find() finds a hash element. If the element has been
* found then it is stored in the lhq->value and njs_lvlhsh_find()
static const njs_object_prop_t njs_math_object_properties[] =
{
+ {
+ .type = NJS_PROPERTY,
+ .name = njs_wellknown_symbol(NJS_SYMBOL_TO_STRING_TAG),
+ .value = njs_string("Math"),
+ .configurable = 1,
+ },
+
{
.type = NJS_PROPERTY,
.name = njs_string("E"),
static const njs_value_t njs_object_date_string = njs_string("[object Date]");
static const njs_value_t njs_object_error_string =
njs_string("[object Error]");
+static const njs_value_t njs_object_arguments_string =
+ njs_long_string("[object Arguments]");
njs_int_t
njs_object_prototype_to_string(njs_vm_t *vm, njs_value_t *args,
njs_uint_t nargs, njs_index_t unused)
{
+ u_char *p;
+ njs_int_t ret;
+ njs_value_t tag, *value;
+ njs_string_prop_t string;
const njs_value_t *name;
static const njs_value_t *class_name[NJS_VALUE_TYPE_MAX] = {
&njs_object_object_string,
};
- name = class_name[args[0].type];
+ value = njs_argument(args, 0);
+ name = class_name[value->type];
+
+ if (njs_is_null_or_undefined(value)) {
+ vm->retval = *name;
+
+ return NJS_OK;
+ }
- if (njs_is_error(&args[0])) {
+ if (njs_is_error(value)) {
name = &njs_object_error_string;
}
- if (njs_fast_path(name != NULL)) {
+ if (njs_is_object(value)
+ && njs_lvlhsh_eq(&njs_object(value)->shared_hash,
+ &vm->shared->arguments_object_instance_hash))
+ {
+ name = &njs_object_arguments_string;
+ }
+
+ ret = njs_object_string_tag(vm, value, &tag);
+ if (njs_slow_path(ret == NJS_ERROR)) {
+ return ret;
+ }
+
+ if (ret == NJS_DECLINED) {
+ if (njs_slow_path(name == NULL)) {
+ njs_internal_error(vm, "Unknown value type");
+
+ return NJS_ERROR;
+ }
+
vm->retval = *name;
return NJS_OK;
}
- njs_internal_error(vm, "Unknown value type");
+ (void) njs_string_prop(&string, &tag);
- return NJS_ERROR;
+ p = njs_string_alloc(vm, &vm->retval, string.size + njs_length("[object ]"),
+ string.length + njs_length("[object ]"));
+ if (njs_slow_path(p == NULL)) {
+ return NJS_ERROR;
+ }
+
+ p = njs_cpymem(p, "[object ", 8);
+ p = njs_cpymem(p, string.start, string.size);
+ *p = ']';
+
+ return NJS_OK;
}
}
+njs_inline njs_int_t
+njs_object_string_tag(njs_vm_t *vm, njs_value_t *value, njs_value_t *tag)
+{
+ njs_int_t ret;
+
+ static const njs_value_t to_string_tag =
+ njs_wellknown_symbol(NJS_SYMBOL_TO_STRING_TAG);
+
+ ret = njs_value_property(vm, value, njs_value_arg(&to_string_tag), tag);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
+
+ if (!njs_is_string(tag)) {
+ return NJS_DECLINED;
+ }
+
+ return NJS_OK;
+}
+
+
extern const njs_object_type_init_t njs_obj_type_init;
static const njs_object_prop_t njs_symbol_prototype_properties[] =
{
+ {
+ .type = NJS_PROPERTY,
+ .name = njs_wellknown_symbol(NJS_SYMBOL_TO_STRING_TAG),
+ .value = njs_string("Symbol"),
+ .configurable = 1,
+ },
+
{
.type = NJS_PROPERTY_HANDLER,
.name = njs_string("__proto__"),
{ njs_str("(function () {arguments = [];})"),
njs_str("SyntaxError: Identifier \"arguments\" is forbidden as left-hand in assignment in 1") },
+ { njs_str("(function(){return arguments;})()"),
+ njs_str("[object Arguments]") },
+
{ njs_str("(function(){return arguments[0];})(1,2,3)"),
njs_str("1") },
/* arrow functions + global this. */
{ njs_str("(() => this)()"),
- njs_str("[object Object]") },
+ njs_str("[object global]") },
{ njs_str("(() => this).call('abc')"),
- njs_str("[object Object]") },
+ njs_str("[object global]") },
{ njs_str("(() => this).apply('abc')"),
- njs_str("[object Object]") },
+ njs_str("[object global]") },
{ njs_str("(() => this).bind('abc')()"),
- njs_str("[object Object]") },
+ njs_str("[object global]") },
{ njs_str("(function() { return (() => this); })()()"),
njs_str("undefined") },
/* global this. */
{ njs_str("this"),
- njs_str("[object Object]") },
+ njs_str("[object global]") },
{ njs_str("Object.getOwnPropertyDescriptor(this, 'NaN').value"),
njs_str("NaN") },
njs_str("TypeError: right argument is not callable") },
{ njs_str("njs"),
- njs_str("[object Object]") },
+ njs_str("[object njs]") },
{ njs_str("var o = Object(); o"),
njs_str("[object Object]") },
njs_str("true") },
{ njs_str("Object.prototype.toString.call(Symbol.prototype)"),
- njs_str("[object Object]") },
+ njs_str("[object Symbol]") },
{ njs_str("Symbol.prototype.toString()"),
njs_str("TypeError: unexpected value type:object") },
"while(n--) o[Symbol()] = 'test'; o[''];"),
njs_str("undefined") },
+ { njs_str("["
+ " Object.prototype,"
+ " Symbol.prototype,"
+ " Math,"
+ " JSON,"
+ " process,"
+ " njs,"
+ " this,"
+ "]"
+ ".map(v=>Object.getOwnPropertyDescriptor(v, Symbol.toStringTag))"
+ ".map(d=>{if (d && !d.writable && !d.enumerable && d.configurable) return d.value})"
+ ".map(v=>njs.dump(v))"),
+ njs_str("undefined,Symbol,Math,JSON,process,njs,global") },
+
/* String */
{ njs_str("String()"),
{ njs_str("Object.prototype.toString.call(true)"),
njs_str("[object Boolean]") },
+ { njs_str("Boolean.prototype[Symbol.toStringTag] = 'XXX';"
+ "Object.prototype.toString.call(true)"),
+ njs_str("[object XXX]") },
+
{ njs_str("Object.prototype.toString.call(1)"),
njs_str("[object Number]") },
{ njs_str("Object.prototype.toString.call([])"),
njs_str("[object Array]") },
+ { njs_str("var a = []; a[Symbol.toStringTag] = 'XXX';"
+ "Object.prototype.toString.call(a)"),
+ njs_str("[object XXX]") },
+
{ njs_str("Object.prototype.toString.call(new Object(true))"),
njs_str("[object Boolean]") },
{ njs_str("Object.prototype.toString.call(function(){})"),
njs_str("[object Function]") },
+ { njs_str("var f = ()=>1; f[Symbol.toStringTag] = 'α'.repeat(32);"
+ "var toStr = Object.prototype.toString.call(f); [toStr, toStr.length]"),
+ njs_str("[object αααααααααααααααααααααααααααααααα],41") },
+
{ njs_str("Object.prototype.toString.call(/./)"),
njs_str("[object RegExp]") },
+ { njs_str("Object.prototype.toString.call(Math)"),
+ njs_str("[object Math]") },
+
+ { njs_str("Object.prototype.toString.call(JSON)"),
+ njs_str("[object JSON]") },
+
{ njs_str("var p = { a:5 }; var o = Object.create(p); o.a"),
njs_str("5") },
{ njs_str("Math.trunc()"),
njs_str("NaN") },
- /* ES5FIX: "[object Math]". */
-
{ njs_str("Math"),
- njs_str("[object Object]") },
+ njs_str("[object Math]") },
{ njs_str("Math.x = function (x) {return 2*x;}; Math.x(3)"),
njs_str("6") },
{ njs_str("this.Math = 1; Math"),
njs_str("1") },
+ { njs_str("JSON"),
+ njs_str("[object JSON]") },
+
{ njs_str("JSON === JSON"),
njs_str("true") },
{ njs_str("njs.dump($r.header)"),
njs_str("{type:\"object\",props:[\"getter\",\"keys\"]}") },
- { njs_str("njs.dump(njs) == `{version:'${njs.version}'}`"),
+ { njs_str("njs.dump(njs) == `njs {version:'${njs.version}'}`"),
njs_str("true") },
{ njs_str("njs.dump(-0)"),
/* require('crypto').createHash() */
- { njs_str("require('crypto').createHash('sha1')"),
- njs_str("[object Hash]") },
-
- { njs_str("Object.prototype.toString.call(require('crypto').createHash('sha1'))"),
- njs_str("[object Object]") },
+ { njs_str("var h = require('crypto').createHash('sha1');"
+ "[Object.prototype.toString.call(h), njs.dump(h),h]"),
+ njs_str("[object Hash],Hash {},[object Hash]") },
{ njs_str("var h = require('crypto').createHash('sha1');"
"var Hash = h.constructor; "
/* require('crypto').createHmac() */
- { njs_str("require('crypto').createHmac('sha1', '')"),
- njs_str("[object Hmac]") },
+ { njs_str("var h = require('crypto').createHmac('sha1', '');"
+ "[Object.prototype.toString.call(h), njs.dump(h),h]"),
+ njs_str("[object Hmac],Hmac {},[object Hmac]") },
{ njs_str("var h = require('crypto').createHmac('md5', '');"
"h.digest('hex')"),