]> git.kaiwu.me - njs.git/commitdiff
Object to external string convertion.
authorIgor Sysoev <igor@sysoev.ru>
Sun, 27 Dec 2015 12:26:11 +0000 (15:26 +0300)
committerIgor Sysoev <igor@sysoev.ru>
Sun, 27 Dec 2015 12:26:11 +0000 (15:26 +0300)
njs/njs_function.c
njs/njs_string.c
njs/njs_string.h
njs/njs_vm.c
njs/njs_vm.h
njs/test/njs_unit_test.c

index 9c210be84500195b7191e3fb9001223c09b1bddb..af5efaf57ef8a71154c21b046bbaae3c191d6f9b 100644 (file)
@@ -99,6 +99,7 @@ njs_function_frame_alloc(njs_vm_t *vm, size_t size)
     frame->free_size = spare_size - size;
     frame->free = (u_char *) frame + size;
 
+    frame->ctor = 0;
     frame->reentrant = 0;
     frame->trap_reference = 0;
 
index 9af2d0babf45e6ff90bcda32d373d59fb302be60..020a80da0170e432abcfa1995b5360ba893ecb8e 100644 (file)
@@ -1415,6 +1415,44 @@ njs_string_prototype_match(njs_vm_t *vm, njs_param_t *param)
 }
 
 
+njs_ret_t
+njs_primitive_value_to_string(njs_vm_t *vm, njs_value_t *dst,
+    const njs_value_t *src)
+{
+    const njs_value_t   *value;
+
+    switch (src->type) {
+
+    case NJS_NULL:
+        value = &njs_string_null;
+        break;
+
+    case NJS_VOID:
+        value = &njs_string_void;
+        break;
+
+    case NJS_BOOLEAN:
+        value = njs_is_true(src) ? &njs_string_true : &njs_string_false;
+        break;
+
+    case NJS_NUMBER:
+        return njs_number_to_string(vm, dst, src);
+
+    case NJS_STRING:
+        /* GC: njs_retain(src); */
+        value = src;
+        break;
+
+    default:
+        return NXT_ERROR;
+    }
+
+    *dst = *value;
+
+    return NXT_OK;
+}
+
+
 njs_ret_t
 njs_value_to_string(njs_vm_t *vm, njs_value_t *dst, const njs_value_t *src)
 {
@@ -1492,47 +1530,6 @@ njs_value_to_string(njs_vm_t *vm, njs_value_t *dst, const njs_value_t *src)
 }
 
 
-njs_ret_t
-njs_value_to_ext_string(njs_vm_t *vm, nxt_str_t *dst, const njs_value_t *src)
-{
-    u_char       *start;
-    size_t       size;
-    njs_ret_t    ret;
-    njs_value_t  value;
-
-    if (nxt_fast_path(src != NULL)) {
-        ret = njs_value_to_string(vm, &value, src);
-
-        if (nxt_fast_path(ret == NXT_OK)) {
-            size = value.short_string.size;
-
-            if (size != NJS_STRING_LONG) {
-                start = nxt_mem_cache_alloc(vm->mem_cache_pool, size);
-                if (nxt_slow_path(start == NULL)) {
-                    return NXT_ERROR;
-                }
-
-                memcpy(start, value.short_string.start, size);
-
-            } else {
-                size = value.data.string_size;
-                start = value.data.u.string->start;
-            }
-
-            dst->len = size;
-            dst->data = start;
-        }
-
-        return ret;
-    }
-
-    dst->len = 0;
-    dst->data = NULL;
-
-    return NXT_OK;
-}
-
-
 double
 njs_string_to_number(njs_value_t *value)
 {
index 383724530942f857a27f5eafd1dabafe4e730499..40fb2552441e6b7a63443f93ba86d90bc21b9911 100644 (file)
@@ -98,9 +98,9 @@ const u_char *njs_string_offset(const u_char *start, const u_char *end,
     size_t index);
 nxt_noinline uint32_t njs_string_index(njs_string_prop_t *string,
     uint32_t offset);
-njs_ret_t njs_value_to_string(njs_vm_t *vm, njs_value_t *dst,
+njs_ret_t njs_primitive_value_to_string(njs_vm_t *vm, njs_value_t *dst,
     const njs_value_t *src);
-njs_ret_t njs_value_to_ext_string(njs_vm_t *vm, nxt_str_t *dst,
+njs_ret_t njs_value_to_string(njs_vm_t *vm, njs_value_t *dst,
     const njs_value_t *src);
 double njs_string_to_number(njs_value_t *value);
 
index a72102f1d1c3645fca15a7fbee047519b109c6b8..01626a65b80a98f31d6089957b00b979caba4a9a 100644 (file)
@@ -94,6 +94,9 @@ static njs_ret_t njs_primitive_value(njs_vm_t *vm, njs_value_t *value,
     nxt_uint_t hint);
 static njs_ret_t njs_vmcode_restart(njs_vm_t *vm, njs_value_t *invld1,
     njs_value_t *invld2);
+static njs_ret_t njs_object_value_to_string(njs_vm_t *vm, njs_value_t *value);
+static njs_ret_t njs_vmcode_value_to_string(njs_vm_t *vm, njs_value_t *invld1,
+    njs_value_t *invld2);
 
 void njs_debug(njs_index_t index, njs_value_t *value);
 
@@ -136,7 +139,7 @@ const njs_value_t  njs_exception_memory_error =    njs_string("MemoryError");
  * values is passed as arguments although they are not always used.
  */
 
-nxt_int_t
+nxt_noinline nxt_int_t
 njs_vmcode_interpreter(njs_vm_t *vm)
 {
     u_char                *catch;
@@ -921,7 +924,7 @@ njs_property_query(njs_vm_t *vm, njs_property_query_t *pq, njs_value_t *object,
 
     if (nxt_fast_path(njs_is_primitive(property))) {
 
-        ret = njs_value_to_string(vm, &pq->value, property);
+        ret = njs_primitive_value_to_string(vm, &pq->value, property);
 
         if (nxt_fast_path(ret == NXT_OK)) {
 
@@ -2561,8 +2564,6 @@ njs_vm_trap(njs_vm_t *vm, nxt_uint_t trap, njs_value_t *value1,
         return NXT_ERROR;
     }
 
-    frame->ctor = 0;
-
     /*
      * The values[0] is scratch value for results of "valueOf" and
      * "toString" methods.  The values[1] and values[2] are original
@@ -2626,9 +2627,8 @@ njs_vmcode_number_primitive(njs_vm_t *vm, njs_value_t *invld, njs_value_t *narg)
 static njs_ret_t
 njs_vmcode_string_primitive(njs_vm_t *vm, njs_value_t *invld, njs_value_t *narg)
 {
-    njs_ret_t          ret;
-    njs_value_t        *value;
-    const njs_value_t  *string;
+    njs_ret_t    ret;
+    njs_value_t  *value;
 
     value = njs_native_data(vm->frame);
     value = &value[(uintptr_t) narg + 1];
@@ -2636,40 +2636,11 @@ njs_vmcode_string_primitive(njs_vm_t *vm, njs_value_t *invld, njs_value_t *narg)
     ret = njs_primitive_value(vm, value, 1);
 
     if (nxt_fast_path(ret > 0)) {
+        ret = njs_primitive_value_to_string(vm, value, value);
 
-        if (!njs_is_string(value)) {
-
-            switch (value->type) {
-
-            case NJS_NULL:
-                string = &njs_string_null;
-                break;
-
-            case NJS_VOID:
-                string = &njs_string_void;
-                break;
-
-            case NJS_BOOLEAN:
-                string = njs_is_true(value) ? &njs_string_true:
-                                              &njs_string_false;
-                break;
-
-            case NJS_NUMBER:
-                ret = njs_number_to_string(vm, value, value);
-                if (nxt_fast_path(ret == NXT_OK)) {
-                    goto done;
-                }
-
-            default:
-                return NXT_ERROR;
-            }
-
-            *value = *string;
+        if (nxt_fast_path(ret == NXT_OK)) {
+            return sizeof(njs_vmcode_1addr_t);
         }
-
-    done:
-
-        ret = sizeof(njs_vmcode_1addr_t);
     }
 
     return ret;
@@ -2812,6 +2783,138 @@ njs_vmcode_restart(njs_vm_t *vm, njs_value_t *invld1, njs_value_t *invld2)
 }
 
 
+njs_ret_t
+njs_value_to_ext_string(njs_vm_t *vm, nxt_str_t *dst, const njs_value_t *src)
+{
+    u_char       *start;
+    size_t       size;
+    njs_ret_t    ret;
+    njs_value_t  value;
+
+    if (nxt_fast_path(src != NULL)) {
+
+        value = *src;
+
+        if (nxt_slow_path(!njs_is_primitive(&value))) {
+
+            ret = njs_object_value_to_string(vm, &value);
+
+            if (nxt_slow_path(ret != NXT_OK)) {
+                goto fail;
+            }
+        }
+
+        ret = njs_primitive_value_to_string(vm, &value, &value);
+
+        if (nxt_fast_path(ret == NXT_OK)) {
+            size = value.short_string.size;
+
+            if (size != NJS_STRING_LONG) {
+                start = nxt_mem_cache_alloc(vm->mem_cache_pool, size);
+                if (nxt_slow_path(start == NULL)) {
+                    return NXT_ERROR;
+                }
+
+                memcpy(start, value.short_string.start, size);
+
+            } else {
+                size = value.data.string_size;
+                start = value.data.u.string->start;
+            }
+
+            dst->len = size;
+            dst->data = start;
+
+            return NXT_OK;
+        }
+    }
+
+fail:
+
+    dst->len = 0;
+    dst->data = NULL;
+
+    return NXT_ERROR;
+
+}
+
+
+static njs_ret_t
+njs_object_value_to_string(njs_vm_t *vm, njs_value_t *value)
+{
+    size_t              size;
+    njs_ret_t           ret;
+    njs_value_t         *retval;
+    njs_native_frame_t  *frame;
+
+    static const njs_vmcode_1addr_t  value_to_string[] = {
+        { .code = { .operation = njs_vmcode_value_to_string,
+                    .operands =  NJS_VMCODE_NO_OPERAND,
+                    .retval = NJS_VMCODE_NO_RETVAL } },
+    };
+
+    size = NJS_NATIVE_FRAME_SIZE + 2 * sizeof(njs_value_t);
+
+    frame = njs_function_frame_alloc(vm, size);
+    if (nxt_slow_path(frame == NULL)) {
+        return NXT_ERROR;
+    }
+
+    /*
+     * Execute the single njs_vmcode_value_to_string() instruction.  The
+     * retval[0] is scratch value for results of "toString" or "valueOf"
+     * methods.  The retval[1] is an original object value which will be
+     * replaced with primitive value returned by "toString" or "valueOf"
+     * methods.  The scratch value is stored separately to preserve the
+     * original object value for the second "valueOf" method call if the
+     * first "toString" method call will return non-primitive value.
+     */
+
+    frame->u.return_address = vm->current;
+    vm->current = (u_char *) value_to_string;
+
+    retval = njs_native_data(vm->frame);
+    njs_set_invalid(&retval[0]);
+    retval[1] = *value;
+
+    ret = njs_vmcode_interpreter(vm);
+
+    if (ret == NJS_STOP) {
+        ret = NXT_OK;
+        *value = retval[1];
+    }
+
+    vm->current = frame->u.return_address;
+    vm->frame = frame->previous;
+    vm->scopes[NJS_SCOPE_CALLEE_ARGUMENTS] = frame->previous->arguments;
+
+    if (frame->first) {
+        nxt_mem_cache_free(vm->mem_cache_pool, frame);
+    }
+
+    return ret;
+}
+
+
+static njs_ret_t
+njs_vmcode_value_to_string(njs_vm_t *vm, njs_value_t *invld1,
+    njs_value_t *invld2)
+{
+    njs_ret_t    ret;
+    njs_value_t  *value;
+
+    value = njs_native_data(vm->frame);
+
+    ret = njs_primitive_value(vm, &value[1], 1);
+
+    if (nxt_fast_path(ret > 0)) {
+        return NJS_STOP;
+    }
+
+    return ret;
+}
+
+
 nxt_noinline void
 njs_number_set(njs_value_t *value, double num)
 {
index 2b00c15091ea228cedeeadab8115d70f6ba40019..ec61ae78a6e22fbc9ddb96cd1b2ab7cce226646d 100644 (file)
@@ -871,6 +871,8 @@ njs_ret_t njs_vmcode_catch(njs_vm_t *vm, njs_value_t *invld,
 njs_ret_t njs_vmcode_finally(njs_vm_t *vm, njs_value_t *invld,
     njs_value_t *retval);
 
+njs_ret_t njs_value_to_ext_string(njs_vm_t *vm, nxt_str_t *dst,
+    const njs_value_t *src);
 void njs_number_set(njs_value_t *value, double num);
 
 nxt_int_t njs_builtin_objects_create(njs_vm_t *vm);
index 31f6a105a2962bf21551d91094c20ca2ee83189a..36245ef4285ff6577d84ac959c67993880dc9368 100644 (file)
@@ -2465,6 +2465,22 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("$r.nonexistent"),
       nxt_string("undefined") },
 
+    { nxt_string("var a = { toString: function() { return 1 } }; a"),
+      nxt_string("1") },
+
+    { nxt_string("var a = { valueOf: function() { return 1 } };  a"),
+      nxt_string("[object Object]") },
+
+    { nxt_string("var a = { toString: 2,"
+                 "          valueOf: function() { return 1 } };  a"),
+      nxt_string("1") },
+
+    { nxt_string("var a = { toString: function() { return [] },"
+                 "          valueOf: function() { return 1 } };  a"),
+      nxt_string("1") },
+
+    /**/
+
     { nxt_string("'абвгдеёжзийклмнопрстуфхцчшщъыьэюя'.charCodeAt(5)"),
       nxt_string("1077") },