]> git.kaiwu.me - njs.git/commitdiff
Moving value methods to njs_value.c.
authorhongzhidao <hongzhidao@gmail.com>
Sat, 6 Jul 2019 14:27:14 +0000 (10:27 -0400)
committerhongzhidao <hongzhidao@gmail.com>
Sat, 6 Jul 2019 14:27:14 +0000 (10:27 -0400)
auto/sources
njs/njs_core.h
njs/njs_value.c [new file with mode: 0644]
njs/njs_value.h [new file with mode: 0644]
njs/njs_vm.c
njs/njs_vm.h

index b24d1a419a91c54b6e8ceb2d82c6e207b9a46a7c..d8c078e6ade41653a7456e845c5b87cea29f0760 100644 (file)
@@ -30,6 +30,7 @@ NXT_TEST_SRCS=" \
 
 NJS_LIB_SRCS=" \
    njs/njs.c \
+   njs/njs_value.c \
    njs/njs_vm.c \
    njs/njs_boolean.c \
    njs/njs_number.c \
index f8ec3d23d68a89c2597be02fa4a57193648f6dce..cb04ad182035129d48e4fb69dcb3d36cc97a625f 100644 (file)
@@ -32,6 +32,7 @@
 #include <nxt_sprintf.h>
 
 #include <njs.h>
+#include <njs_value.h>
 #include <njs_vm.h>
 #include <njs_variable.h>
 #include <njs_lexer.h>
diff --git a/njs/njs_value.c b/njs/njs_value.c
new file mode 100644 (file)
index 0000000..bda00a0
--- /dev/null
@@ -0,0 +1,418 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#include <njs_core.h>
+#include <string.h>
+
+
+const njs_value_t  njs_value_null =         njs_value(NJS_NULL, 0, 0.0);
+const njs_value_t  njs_value_undefined =    njs_value(NJS_UNDEFINED, 0, NAN);
+const njs_value_t  njs_value_false =        njs_value(NJS_BOOLEAN, 0, 0.0);
+const njs_value_t  njs_value_true =         njs_value(NJS_BOOLEAN, 1, 1.0);
+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);
+
+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");
+const njs_value_t  njs_string_undefined =   njs_string("undefined");
+const njs_value_t  njs_string_boolean =     njs_string("boolean");
+const njs_value_t  njs_string_false =       njs_string("false");
+const njs_value_t  njs_string_true =        njs_string("true");
+const njs_value_t  njs_string_number =      njs_string("number");
+const njs_value_t  njs_string_minus_zero =  njs_string("-0");
+const njs_value_t  njs_string_minus_infinity =
+                                            njs_string("-Infinity");
+const njs_value_t  njs_string_plus_infinity =
+                                            njs_string("Infinity");
+const njs_value_t  njs_string_nan =         njs_string("NaN");
+const njs_value_t  njs_string_string =      njs_string("string");
+const njs_value_t  njs_string_object =      njs_string("object");
+const njs_value_t  njs_string_function =    njs_string("function");
+const njs_value_t  njs_string_memory_error = njs_string("MemoryError");
+
+
+void
+njs_value_retain(njs_value_t *value)
+{
+    njs_string_t  *string;
+
+    if (njs_is_string(value)) {
+
+        if (value->long_string.external != 0xff) {
+            string = value->long_string.data;
+
+            nxt_thread_log_debug("retain:%uxD \"%*s\"", string->retain,
+                                 value->long_string.size, string->start);
+
+            if (string->retain != 0xffff) {
+                string->retain++;
+            }
+        }
+    }
+}
+
+
+void
+njs_value_release(njs_vm_t *vm, njs_value_t *value)
+{
+    njs_string_t  *string;
+
+    if (njs_is_string(value)) {
+
+        if (value->long_string.external != 0xff) {
+            string = value->long_string.data;
+
+            nxt_thread_log_debug("release:%uxD \"%*s\"", string->retain,
+                                 value->long_string.size, string->start);
+
+            if (string->retain != 0xffff) {
+                string->retain--;
+
+#if 0
+                if (string->retain == 0) {
+                    if ((u_char *) string + sizeof(njs_string_t)
+                        != string->start)
+                    {
+                        nxt_memcache_pool_free(vm->mem_pool,
+                                               string->start);
+                    }
+
+                    nxt_memcache_pool_free(vm->mem_pool, string);
+                }
+#endif
+            }
+        }
+    }
+}
+
+
+nxt_bool_t
+njs_values_strict_equal(const njs_value_t *val1, const njs_value_t *val2)
+{
+    size_t        size, length1, length2;
+    const u_char  *start1, *start2;
+
+    if (val1->type != val2->type) {
+        return 0;
+    }
+
+    if (njs_is_numeric(val1)) {
+
+        if (njs_is_undefined(val1)) {
+            return 1;
+        }
+
+        /* Infinities are handled correctly by comparision. */
+        return (val1->data.u.number == val2->data.u.number);
+    }
+
+    if (njs_is_string(val1)) {
+        size = val1->short_string.size;
+
+        if (size != val2->short_string.size) {
+            return 0;
+        }
+
+        if (size != NJS_STRING_LONG) {
+            length1 = val1->short_string.length;
+            length2 = val2->short_string.length;
+
+            /*
+             * Using full memcmp() comparison if at least one string
+             * is a Byte string.
+             */
+            if (length1 != 0 && length2 != 0 && length1 != length2) {
+                return 0;
+            }
+
+            start1 = val1->short_string.start;
+            start2 = val2->short_string.start;
+
+        } else {
+            size = val1->long_string.size;
+
+            if (size != val2->long_string.size) {
+                return 0;
+            }
+
+            length1 = val1->long_string.data->length;
+            length2 = val2->long_string.data->length;
+
+            /*
+             * Using full memcmp() comparison if at least one string
+             * is a Byte string.
+             */
+            if (length1 != 0 && length2 != 0 && length1 != length2) {
+                return 0;
+            }
+
+            start1 = val1->long_string.data->start;
+            start2 = val2->long_string.data->start;
+        }
+
+        return (memcmp(start1, start2, size) == 0);
+    }
+
+    return (val1->data.u.object == val2->data.u.object);
+}
+
+
+/*
+ * 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_ret_t
+njs_value_to_primitive(njs_vm_t *vm, njs_value_t *value, nxt_uint_t hint)
+{
+    njs_ret_t           ret;
+    njs_value_t         *retval;
+    njs_function_t      *function;
+    njs_object_prop_t   *prop;
+    nxt_lvlhsh_query_t  lhq;
+
+    static const uint32_t  hashes[] = {
+        NJS_VALUE_OF_HASH,
+        NJS_TO_STRING_HASH,
+    };
+
+    static const nxt_str_t  names[] = {
+        nxt_string("valueOf"),
+        nxt_string("toString"),
+    };
+
+    if (!njs_is_primitive(value)) {
+        retval = &vm->top_frame->trap_scratch;
+
+        if (!njs_is_primitive(retval)) {
+
+            for ( ;; ) {
+                ret = NXT_ERROR;
+
+                if (njs_is_object(value) && vm->top_frame->trap_tries < 2) {
+                    hint ^= vm->top_frame->trap_tries++;
+
+                    lhq.key_hash = hashes[hint];
+                    lhq.key = names[hint];
+
+                    prop = njs_object_property(vm, value->data.u.object, &lhq);
+
+                    if (nxt_fast_path(prop != NULL)) {
+
+                        if (!njs_is_function(&prop->value)) {
+                            /* Try the second method. */
+                            continue;
+                        }
+
+                        function = prop->value.data.u.function;
+
+                        ret = njs_function_apply(vm, function, value, 1,
+                                                 (njs_index_t) retval);
+                        /*
+                         * njs_function_apply() can return
+                         *   NXT_OK, NJS_APPLIED, NXT_ERROR, NXT_AGAIN.
+                         */
+                        if (nxt_fast_path(ret == NXT_OK)) {
+
+                            if (njs_is_primitive(&vm->retval)) {
+                                retval = &vm->retval;
+                                break;
+                            }
+
+                            /* Try the second method. */
+                            continue;
+                        }
+
+                        if (ret == NJS_APPLIED) {
+                            /*
+                             * A user-defined method or continuation have
+                             * been prepared to run.  The method will return
+                             * to the current instruction and will restart it.
+                             */
+                            ret = 0;
+                        }
+                    }
+                }
+
+                if (ret == NXT_ERROR) {
+                    njs_type_error(vm,
+                                   "Cannot convert object to primitive value");
+                }
+
+                return ret;
+            }
+        }
+
+        *value = *retval;
+
+        njs_set_invalid(retval);
+    }
+
+    vm->top_frame->trap_tries = 0;
+
+    return 1;
+}
+
+
+njs_array_t *
+njs_value_enumerate(njs_vm_t *vm, const njs_value_t *value,
+    njs_object_enum_t kind, nxt_bool_t all)
+{
+    njs_object_value_t  obj_val;
+
+    if (njs_is_object(value)) {
+        return njs_object_enumerate(vm, value->data.u.object, kind, all);
+    }
+
+    if (value->type != NJS_STRING) {
+        return njs_array_alloc(vm, 0, NJS_ARRAY_SPARE);
+    }
+
+    obj_val.object = vm->string_object;
+    obj_val.value = *value;
+
+    return njs_object_enumerate(vm, (njs_object_t *) &obj_val, kind, all);
+}
+
+
+njs_array_t *
+njs_value_own_enumerate(njs_vm_t *vm, const njs_value_t *value,
+    njs_object_enum_t kind, nxt_bool_t all)
+{
+    njs_object_value_t  obj_val;
+
+    if (njs_is_object(value)) {
+        return njs_object_own_enumerate(vm, value->data.u.object, kind, all);
+    }
+
+    if (value->type != NJS_STRING) {
+        return njs_array_alloc(vm, 0, NJS_ARRAY_SPARE);
+    }
+
+    obj_val.object = vm->string_object;
+    obj_val.value = *value;
+
+    return njs_object_own_enumerate(vm, (njs_object_t *) &obj_val, kind, all);
+}
+
+
+const char *
+njs_type_string(njs_value_type_t type)
+{
+    switch (type) {
+    case NJS_NULL:
+        return "null";
+
+    case NJS_UNDEFINED:
+        return "undefined";
+
+    case NJS_BOOLEAN:
+        return "boolean";
+
+    case NJS_NUMBER:
+        return "number";
+
+    case NJS_STRING:
+        return "string";
+
+    case NJS_EXTERNAL:
+        return "external";
+
+    case NJS_INVALID:
+        return "invalid";
+
+    case NJS_OBJECT:
+        return "object";
+
+    case NJS_ARRAY:
+        return "array";
+
+    case NJS_OBJECT_BOOLEAN:
+        return "object boolean";
+
+    case NJS_OBJECT_NUMBER:
+        return "object number";
+
+    case NJS_OBJECT_STRING:
+        return "object string";
+
+    case NJS_FUNCTION:
+        return "function";
+
+    case NJS_REGEXP:
+        return "regexp";
+
+    case NJS_DATE:
+        return "date";
+
+    case NJS_OBJECT_ERROR:
+        return "error";
+
+    case NJS_OBJECT_EVAL_ERROR:
+        return "eval error";
+
+    case NJS_OBJECT_INTERNAL_ERROR:
+        return "internal error";
+
+    case NJS_OBJECT_RANGE_ERROR:
+        return "range error";
+
+    case NJS_OBJECT_REF_ERROR:
+        return "reference error";
+
+    case NJS_OBJECT_SYNTAX_ERROR:
+        return "syntax error";
+
+    case NJS_OBJECT_TYPE_ERROR:
+        return "type error";
+
+    case NJS_OBJECT_URI_ERROR:
+        return "uri error";
+
+    default:
+        return NULL;
+    }
+}
+
+
+const char *
+njs_arg_type_string(uint8_t arg)
+{
+    switch (arg) {
+    case NJS_SKIP_ARG:
+        return "skip";
+
+    case NJS_NUMBER_ARG:
+        return "number";
+
+    case NJS_INTEGER_ARG:
+        return "integer";
+
+    case NJS_STRING_ARG:
+        return "string";
+
+    case NJS_OBJECT_ARG:
+        return "object";
+
+    case NJS_STRING_OBJECT_ARG:
+        return "string object";
+
+    case NJS_FUNCTION_ARG:
+        return "function";
+
+    case NJS_REGEXP_ARG:
+        return "regexp";
+
+    case NJS_DATE_ARG:
+        return "date";
+
+    default:
+        return "unknown";
+    }
+}
diff --git a/njs/njs_value.h b/njs/njs_value.h
new file mode 100644 (file)
index 0000000..b2c9884
--- /dev/null
@@ -0,0 +1,660 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#ifndef _NJS_VALUE_H_INCLUDED_
+#define _NJS_VALUE_H_INCLUDED_
+
+
+#include <nxt_trace.h>
+#include <nxt_queue.h>
+#include <nxt_regex.h>
+#include <nxt_random.h>
+#include <nxt_djb_hash.h>
+#include <nxt_mp.h>
+
+#include <math.h>
+
+
+/*
+ * The order of the enum is used in njs_vmcode_typeof()
+ * and njs_object_prototype_to_string().
+ */
+
+typedef enum {
+    NJS_NULL                  = 0x00,
+    NJS_UNDEFINED             = 0x01,
+
+    /* The order of the above type is used in njs_is_null_or_undefined(). */
+
+    NJS_BOOLEAN               = 0x02,
+    /*
+     * The order of the above type is used in
+     * njs_is_null_or_undefined_or_boolean().
+     */
+    NJS_NUMBER                = 0x03,
+    /*
+     * The order of the above type is used in njs_is_numeric().
+     * Booleans, null and void values can be used in mathematical operations:
+     *   a numeric value of the true value is one,
+     *   a numeric value of the null and false values is zero,
+     *   a numeric value of the void value is NaN.
+     */
+    NJS_STRING                = 0x04,
+
+    /* The order of the above type is used in njs_is_primitive(). */
+
+    NJS_DATA                  = 0x05,
+
+    /* The type is external code. */
+    NJS_EXTERNAL              = 0x06,
+
+    /*
+     * The invalid value type is used:
+     *   for uninitialized array members,
+     *   to detect non-declared explicitly or implicitly variables,
+     *   for native property getters.
+     */
+    NJS_INVALID               = 0x07,
+
+    /*
+     * The object types are >= NJS_OBJECT, this is used in njs_is_object().
+     * NJS_OBJECT_BOOLEAN, NJS_OBJECT_NUMBER, and NJS_OBJECT_STRING must be
+     * in the same order as NJS_BOOLEAN, NJS_NUMBER, and NJS_STRING.  It is
+     * used in njs_primitive_prototype_index().  The order of object types
+     * is used in vm->prototypes and vm->constructors arrays.
+     */
+    NJS_OBJECT                = 0x10,
+    NJS_ARRAY                 = 0x11,
+    NJS_OBJECT_BOOLEAN        = 0x12,
+    NJS_OBJECT_NUMBER         = 0x13,
+    NJS_OBJECT_STRING         = 0x14,
+    NJS_FUNCTION              = 0x15,
+    NJS_REGEXP                = 0x16,
+    NJS_DATE                  = 0x17,
+    NJS_OBJECT_ERROR          = 0x18,
+    NJS_OBJECT_EVAL_ERROR     = 0x19,
+    NJS_OBJECT_INTERNAL_ERROR = 0x1a,
+    NJS_OBJECT_RANGE_ERROR    = 0x1b,
+    NJS_OBJECT_REF_ERROR      = 0x1c,
+    NJS_OBJECT_SYNTAX_ERROR   = 0x1d,
+    NJS_OBJECT_TYPE_ERROR     = 0x1e,
+    NJS_OBJECT_URI_ERROR      = 0x1f,
+    NJS_OBJECT_VALUE          = 0x20,
+#define NJS_TYPE_MAX         (NJS_OBJECT_VALUE + 1)
+} njs_value_type_t;
+
+
+/*
+ * njs_prop_handler_t operates as a property getter and/or setter.
+ * The handler receives NULL setval if it is invoked in GET context and
+ * non-null otherwise.
+ *
+ * njs_prop_handler_t is expected to return:
+ *   NXT_OK - handler executed successfully;
+ *   NXT_ERROR - some error, vm->retval contains appropriate exception;
+ *   NXT_DECLINED - handler was applied to inappropriate object.
+ */
+typedef njs_ret_t (*njs_prop_handler_t) (njs_vm_t *vm, njs_value_t *value,
+    njs_value_t *setval, njs_value_t *retval);
+typedef njs_ret_t (*njs_function_native_t) (njs_vm_t *vm, njs_value_t *args,
+    nxt_uint_t nargs, njs_index_t retval);
+
+
+typedef struct njs_string_s           njs_string_t;
+typedef struct njs_object_s           njs_object_t;
+typedef struct njs_object_value_s     njs_object_value_t;
+typedef struct njs_function_lambda_s  njs_function_lambda_t;
+typedef struct njs_regexp_pattern_s   njs_regexp_pattern_t;
+typedef struct njs_array_s            njs_array_t;
+typedef struct njs_regexp_s           njs_regexp_t;
+typedef struct njs_date_s             njs_date_t;
+typedef struct njs_property_next_s    njs_property_next_t;
+typedef struct njs_object_init_s      njs_object_init_t;
+
+
+union njs_value_s {
+    /*
+     * The njs_value_t size is 16 bytes and must be aligned to 16 bytes
+     * to provide 4 bits to encode scope in njs_index_t.  This space is
+     * used to store short strings.  The maximum size of a short string
+     * is 14 (NJS_STRING_SHORT).  If the short_string.size field is 15
+     * (NJS_STRING_LONG) then the size is in the long_string.size field
+     * and the long_string.data field points to a long string.
+     *
+     * The number of the string types is limited to 2 types to minimize
+     * overhead of processing string fields.  It is also possible to add
+     * strings with size from 14 to 254 which size and length are stored in
+     * the string_size and string_length byte wide fields.  This will lessen
+     * the maximum size of short string to 13.
+     */
+    struct {
+        njs_value_type_t              type:8;  /* 6 bits */
+        /*
+         * The truth field is set during value assignment and then can be
+         * quickly tested by logical and conditional operations regardless
+         * of value type.  The truth field coincides with short_string.size
+         * and short_string.length so when string size and length are zero
+         * the string's value is false.
+         */
+        uint8_t                       truth;
+
+        uint16_t                      _spare1;
+        uint32_t                      _spare2;
+
+        union {
+            double                    number;
+            njs_object_t              *object;
+            njs_array_t               *array;
+            njs_object_value_t        *object_value;
+            njs_function_t            *function;
+            njs_function_lambda_t     *lambda;
+            njs_regexp_t              *regexp;
+            njs_date_t                *date;
+            njs_prop_handler_t        prop_handler;
+            njs_value_t               *value;
+            njs_property_next_t       *next;
+            void                      *data;
+        } u;
+    } data;
+
+    struct {
+        njs_value_type_t              type:8;  /* 6 bits */
+
+#define NJS_STRING_SHORT              14
+#define NJS_STRING_LONG               15
+
+        uint8_t                       size:4;
+        uint8_t                       length:4;
+
+        u_char                        start[NJS_STRING_SHORT];
+    } short_string;
+
+    struct {
+        njs_value_type_t              type:8;  /* 6 bits */
+        uint8_t                       truth;
+
+        /* 0xff if data is external string. */
+        uint8_t                       external;
+        uint8_t                       _spare;
+
+        uint32_t                      size;
+        njs_string_t                  *data;
+    } long_string;
+
+    struct {
+        njs_value_type_t              type:8;  /* 6 bits */
+        uint8_t                       truth;
+
+        uint16_t                      _spare;
+
+        uint32_t                      index;
+        const njs_extern_t            *proto;
+    } external;
+
+    njs_value_type_t                  type:8;  /* 6 bits */
+};
+
+
+struct njs_object_s {
+    /* A private hash of njs_object_prop_t. */
+    nxt_lvlhsh_t                      hash;
+
+    /* A shared hash of njs_object_prop_t. */
+    nxt_lvlhsh_t                      shared_hash;
+
+    /* An object __proto__. */
+    njs_object_t                      *__proto__;
+
+    /* The type is used in constructor prototypes. */
+    njs_value_type_t                  type:8;
+    uint8_t                           shared;     /* 1 bit */
+    uint8_t                           extensible; /* 1 bit */
+};
+
+
+struct njs_object_value_s {
+    njs_object_t                      object;
+    /* The value can be unaligned since it never used in nJSVM operations. */
+    njs_value_t                       value;
+};
+
+
+struct njs_array_s {
+    njs_object_t                      object;
+    uint32_t                          size;
+    uint32_t                          length;
+    njs_value_t                       *start;
+    njs_value_t                       *data;
+};
+
+
+typedef struct {
+    union {
+        uint32_t                      count;
+        njs_value_t                   values;
+    } u;
+
+    njs_value_t                       values[1];
+} njs_closure_t;
+
+
+#define NJS_ARGS_TYPES_MAX            5
+
+struct njs_function_s {
+    njs_object_t                      object;
+
+    uint8_t                           args_types[NJS_ARGS_TYPES_MAX];
+    uint8_t                           args_offset;
+    uint8_t                           continuation_size;
+
+    /* Function is a closure. */
+    uint8_t                           closure:1;
+
+    uint8_t                           native:1;
+    uint8_t                           ctor:1;
+
+    union {
+        njs_function_lambda_t         *lambda;
+        njs_function_native_t         native;
+    } u;
+
+    njs_value_t                       *bound;
+#if (NXT_SUNC)
+    njs_closure_t                     *closures[1];
+#else
+    njs_closure_t                     *closures[];
+#endif
+};
+
+
+struct njs_regexp_s {
+    njs_object_t                      object;
+    uint32_t                          last_index;
+    njs_regexp_pattern_t              *pattern;
+    /*
+     * This string value can be unaligned since
+     * it never used in nJSVM operations.
+     */
+    njs_value_t                       string;
+};
+
+
+struct njs_date_s {
+    njs_object_t                      object;
+    double                            time;
+};
+
+
+typedef union {
+    njs_object_t                      object;
+    njs_object_value_t                object_value;
+    njs_array_t                       array;
+    njs_function_t                    function;
+    njs_regexp_t                      regexp;
+    njs_date_t                        date;
+} njs_object_prototype_t;
+
+
+typedef enum {
+    NJS_ENUM_KEYS,
+    NJS_ENUM_VALUES,
+    NJS_ENUM_BOTH,
+} njs_object_enum_t;
+
+
+#define njs_value(_type, _truth, _number) {                                   \
+    .data = {                                                                 \
+        .type = _type,                                                        \
+        .truth = _truth,                                                      \
+        .u.number = _number,                                                  \
+    }                                                                         \
+}
+
+
+#define njs_string(s) {                                                       \
+    .short_string = {                                                         \
+        .type = NJS_STRING,                                                   \
+        .size = nxt_length(s),                                                \
+        .length = nxt_length(s),                                              \
+        .start = s,                                                           \
+    }                                                                         \
+}
+
+
+/* NJS_STRING_LONG is set for both big and little endian platforms. */
+
+#define njs_long_string(s) {                                                  \
+    .long_string = {                                                          \
+        .type = NJS_STRING,                                                   \
+        .truth = (NJS_STRING_LONG << 4) | NJS_STRING_LONG,                    \
+        .size = nxt_length(s),                                                \
+        .data = & (njs_string_t) {                                            \
+            .start = (u_char *) s,                                            \
+            .length = nxt_length(s),                                          \
+        }                                                                     \
+    }                                                                         \
+}
+
+
+#define njs_native_function(_function, _size, ...) {                          \
+    .data = {                                                                 \
+        .type = NJS_FUNCTION,                                                 \
+        .truth = 1,                                                           \
+        .u.function = & (njs_function_t) {                                    \
+            .native = 1,                                                      \
+            .continuation_size = _size,                                       \
+            .args_types = { __VA_ARGS__ },                                    \
+            .args_offset = 1,                                                 \
+            .u.native = _function,                                            \
+            .object = { .type = NJS_FUNCTION,                                 \
+                        .shared = 1,                                          \
+                        .extensible = 1 },                                    \
+        }                                                                     \
+    }                                                                         \
+}
+
+
+#define njs_prop_handler(_handler) {                                          \
+    .data = {                                                                 \
+        .type = NJS_INVALID,                                                  \
+        .truth = 1,                                                           \
+        .u = { .prop_handler = _handler }                                     \
+    }                                                                         \
+}
+
+
+#define njs_is_null(value)                                                    \
+    ((value)->type == NJS_NULL)
+
+
+#define njs_is_undefined(value)                                               \
+    ((value)->type == NJS_UNDEFINED)
+
+
+#define njs_is_null_or_undefined(value)                                       \
+    ((value)->type <= NJS_UNDEFINED)
+
+
+#define njs_is_boolean(value)                                                 \
+    ((value)->type == NJS_BOOLEAN)
+
+
+#define njs_is_null_or_undefined_or_boolean(value)                            \
+    ((value)->type <= NJS_BOOLEAN)
+
+
+#define njs_is_true(value)                                                    \
+    ((value)->data.truth != 0)
+
+
+#define njs_is_number(value)                                                  \
+    ((value)->type == NJS_NUMBER)
+
+
+/* Testing for NaN first generates a better code at least on i386/amd64. */
+
+#define njs_is_number_true(num)                                               \
+    (!isnan(num) && num != 0)
+
+
+#define njs_is_numeric(value)                                                 \
+    ((value)->type <= NJS_NUMBER)
+
+
+#define njs_is_string(value)                                                  \
+    ((value)->type == NJS_STRING)
+
+#define njs_is_error(value)                                                   \
+    ((value)->type >= NJS_OBJECT_ERROR                                        \
+     && (value)->type <= NJS_OBJECT_URI_ERROR)
+
+
+/*
+ * The truth field coincides with short_string.size and short_string.length
+ * so when string size and length are zero the string's value is false and
+ * otherwise is true.
+ */
+#define njs_string_truth(value, size)
+
+
+#define njs_string_get(value, str)                                            \
+    do {                                                                      \
+        if ((value)->short_string.size != NJS_STRING_LONG) {                  \
+            (str)->length = (value)->short_string.size;                       \
+            (str)->start = (u_char *) (value)->short_string.start;            \
+                                                                              \
+        } else {                                                              \
+            (str)->length = (value)->long_string.size;                        \
+            (str)->start = (u_char *) (value)->long_string.data->start;       \
+        }                                                                     \
+    } while (0)
+
+
+#define njs_string_short_start(value)                                         \
+    (value)->short_string.start
+
+
+#define njs_string_short_set(value, _size, _length)                           \
+    do {                                                                      \
+        (value)->type = NJS_STRING;                                           \
+        njs_string_truth(value, _size);                                       \
+        (value)->short_string.size = _size;                                   \
+        (value)->short_string.length = _length;                               \
+    } while (0)
+
+
+#define njs_string_length_set(value, _length)                                 \
+    do {                                                                      \
+        if ((value)->short_string.size != NJS_STRING_LONG) {                  \
+            (value)->short_string.length = length;                            \
+                                                                              \
+        } else {                                                              \
+            (value)->long_string.data->length = length;                       \
+        }                                                                     \
+    } while (0)
+
+#define njs_is_primitive(value)                                               \
+    ((value)->type <= NJS_STRING)
+
+
+#define njs_is_data(value)                                                    \
+    ((value)->type == NJS_DATA)
+
+
+#define njs_is_object(value)                                                  \
+    ((value)->type >= NJS_OBJECT)
+
+
+#define njs_is_object_value(value)                                            \
+    ((value)->type == NJS_OBJECT_VALUE)
+
+
+#define njs_object_value_type(type)                                           \
+    (type + NJS_OBJECT)
+
+
+#define njs_is_array(value)                                                   \
+    ((value)->type == NJS_ARRAY)
+
+
+#define njs_is_function(value)                                                \
+    ((value)->type == NJS_FUNCTION)
+
+
+#define njs_is_regexp(value)                                                  \
+    ((value)->type == NJS_REGEXP)
+
+
+#define njs_is_date(value)                                                    \
+    ((value)->type == NJS_DATE)
+
+
+#define njs_is_external(value)                                                \
+    ((value)->type == NJS_EXTERNAL)
+
+
+#define njs_is_valid(value)                                                   \
+    ((value)->type != NJS_INVALID)
+
+
+#define njs_bool(value)                                                       \
+    ((value)->data.truth)
+
+
+#define njs_number(value)                                                     \
+    ((value)->data.u.number)
+
+
+#define njs_data(value)                                                       \
+    ((value)->data.u.data)
+
+
+#define njs_function(value)                                                   \
+    ((value)->data.u.function)
+
+
+#define njs_object(value)                                                     \
+    ((value)->data.u.object)
+
+
+#define njs_object_hash(value)                                                \
+    (&(value)->data.u.object->hash)
+
+
+#define njs_array(value)                                                      \
+    ((value)->data.u.array)
+
+
+#define njs_array_len(value)                                                  \
+    ((value)->data.u.array->length)
+
+
+#define njs_array_start(value)                                                \
+    ((value)->data.u.array->start)
+
+
+#define njs_set_undefined(value)                                              \
+    *(value) = njs_value_undefined
+
+
+#define njs_set_boolean(value, yn)                                            \
+    *(value) = yn ? njs_value_true : njs_value_false
+
+
+#define njs_set_true(value)                                                   \
+    *(value) = njs_value_true
+
+
+#define njs_set_false(value)                                                  \
+    *(value) = njs_value_false
+
+
+nxt_inline void
+njs_set_number(njs_value_t *value, double num)
+{
+    value->data.u.number = num;
+    value->type = NJS_NUMBER;
+    value->data.truth = njs_is_number_true(num);
+}
+
+
+nxt_inline void
+njs_set_data(njs_value_t *value, void *data)
+{
+    value->data.u.data = data;
+    value->type = NJS_DATA;
+    value->data.truth = 1;
+}
+
+
+nxt_inline void
+njs_set_object(njs_value_t *value, njs_object_t *object)
+{
+    value->data.u.object = object;
+    value->type = NJS_OBJECT;
+    value->data.truth = 1;
+}
+
+
+nxt_inline void
+njs_set_array(njs_value_t *value, njs_array_t *array)
+{
+    value->data.u.array = array;
+    value->type = NJS_ARRAY;
+    value->data.truth = 1;
+}
+
+
+#define njs_set_invalid(value)                                                \
+    (value)->type = NJS_INVALID
+
+
+#if 0 /* GC: todo */
+
+#define njs_retain(value)                                                     \
+    do {                                                                      \
+        if ((value)->data.truth == NJS_STRING_LONG) {                         \
+            njs_value_retain(value);                                          \
+        }                                                                     \
+    } while (0)
+
+
+#define njs_release(vm, value)                                                \
+    do {                                                                      \
+        if ((value)->data.truth == NJS_STRING_LONG) {                         \
+            njs_value_release((vm), (value));                                 \
+        }                                                                     \
+    } while (0)
+
+#else
+
+#define njs_retain(value)
+#define njs_release(vm, value)
+
+#endif
+
+
+void njs_value_retain(njs_value_t *value);
+void njs_value_release(njs_vm_t *vm, njs_value_t *value);
+nxt_bool_t njs_values_strict_equal(const njs_value_t *val1,
+    const njs_value_t *val2);
+njs_ret_t njs_value_to_primitive(njs_vm_t *vm, njs_value_t *value,
+    nxt_uint_t hint);
+njs_array_t *njs_value_enumerate(njs_vm_t *vm, const njs_value_t *value,
+    njs_object_enum_t kind, nxt_bool_t all);
+njs_array_t *njs_value_own_enumerate(njs_vm_t *vm, const njs_value_t *value,
+    njs_object_enum_t kind, nxt_bool_t all);
+const char *njs_type_string(njs_value_type_t type);
+const char *njs_arg_type_string(uint8_t arg);
+
+
+extern const njs_value_t  njs_value_null;
+extern const njs_value_t  njs_value_undefined;
+extern const njs_value_t  njs_value_false;
+extern const njs_value_t  njs_value_true;
+extern const njs_value_t  njs_value_zero;
+extern const njs_value_t  njs_value_nan;
+extern const njs_value_t  njs_value_invalid;
+
+extern const njs_value_t  njs_string_empty;
+extern const njs_value_t  njs_string_comma;
+extern const njs_value_t  njs_string_null;
+extern const njs_value_t  njs_string_undefined;
+extern const njs_value_t  njs_string_boolean;
+extern const njs_value_t  njs_string_false;
+extern const njs_value_t  njs_string_true;
+extern const njs_value_t  njs_string_number;
+extern const njs_value_t  njs_string_minus_zero;
+extern const njs_value_t  njs_string_minus_infinity;
+extern const njs_value_t  njs_string_plus_infinity;
+extern const njs_value_t  njs_string_nan;
+extern const njs_value_t  njs_string_string;
+extern const njs_value_t  njs_string_object;
+extern const njs_value_t  njs_string_function;
+extern const njs_value_t  njs_string_memory_error;
+
+
+#endif /* _NJS_VALUE_H_INCLUDED_ */
index e64d867686e768d6fa3156ecb5f618fdb8445f0e..a5bbabcaf2421a4fa28b0061b270fd047520cda6 100644 (file)
@@ -52,8 +52,6 @@ static njs_ret_t njs_vmcode_string_argument(njs_vm_t *vm, njs_value_t *invld1,
     njs_value_t *inlvd2);
 static njs_ret_t njs_vmcode_primitive_argument(njs_vm_t *vm,
     njs_value_t *invld1, njs_value_t *inlvd2);
-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);
@@ -65,36 +63,6 @@ static njs_ret_t njs_vm_add_backtrace_entry(njs_vm_t *vm, njs_frame_t *frame);
 void njs_debug(njs_index_t index, njs_value_t *value);
 
 
-const njs_value_t  njs_value_null =         njs_value(NJS_NULL, 0, 0.0);
-const njs_value_t  njs_value_undefined =    njs_value(NJS_UNDEFINED, 0, NAN);
-const njs_value_t  njs_value_false =        njs_value(NJS_BOOLEAN, 0, 0.0);
-const njs_value_t  njs_value_true =         njs_value(NJS_BOOLEAN, 1, 1.0);
-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);
-
-
-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");
-const njs_value_t  njs_string_undefined =   njs_string("undefined");
-const njs_value_t  njs_string_boolean =     njs_string("boolean");
-const njs_value_t  njs_string_false =       njs_string("false");
-const njs_value_t  njs_string_true =        njs_string("true");
-const njs_value_t  njs_string_number =      njs_string("number");
-const njs_value_t  njs_string_minus_zero =  njs_string("-0");
-const njs_value_t  njs_string_minus_infinity =
-                                            njs_string("-Infinity");
-const njs_value_t  njs_string_plus_infinity =
-                                            njs_string("Infinity");
-const njs_value_t  njs_string_nan =         njs_string("NaN");
-const njs_value_t  njs_string_string =      njs_string("string");
-const njs_value_t  njs_string_object =      njs_string("object");
-const njs_value_t  njs_string_function =    njs_string("function");
-
-const njs_value_t  njs_string_memory_error = njs_string("MemoryError");
-
-
 const nxt_str_t  njs_entry_main =           nxt_string("main");
 const nxt_str_t  njs_entry_module =         nxt_string("module");
 const nxt_str_t  njs_entry_native =         nxt_string("native");
@@ -255,61 +223,6 @@ start:
 }
 
 
-void
-njs_value_retain(njs_value_t *value)
-{
-    njs_string_t  *string;
-
-    if (njs_is_string(value)) {
-
-        if (value->long_string.external != 0xff) {
-            string = value->long_string.data;
-
-            nxt_thread_log_debug("retain:%uxD \"%*s\"", string->retain,
-                                 value->long_string.size, string->start);
-
-            if (string->retain != 0xffff) {
-                string->retain++;
-            }
-        }
-    }
-}
-
-
-void
-njs_value_release(njs_vm_t *vm, njs_value_t *value)
-{
-    njs_string_t  *string;
-
-    if (njs_is_string(value)) {
-
-        if (value->long_string.external != 0xff) {
-            string = value->long_string.data;
-
-            nxt_thread_log_debug("release:%uxD \"%*s\"", string->retain,
-                                 value->long_string.size, string->start);
-
-            if (string->retain != 0xffff) {
-                string->retain--;
-
-#if 0
-                if (string->retain == 0) {
-                    if ((u_char *) string + sizeof(njs_string_t)
-                        != string->start)
-                    {
-                        nxt_memcache_pool_free(vm->mem_pool,
-                                               string->start);
-                    }
-
-                    nxt_memcache_pool_free(vm->mem_pool, string);
-                }
-#endif
-            }
-        }
-    }
-}
-
-
 njs_ret_t
 njs_vmcode_object(njs_vm_t *vm, njs_value_t *invld1, njs_value_t *invld2)
 {
@@ -1748,77 +1661,6 @@ njs_vmcode_strict_not_equal(njs_vm_t *vm, njs_value_t *val1, njs_value_t *val2)
 }
 
 
-nxt_bool_t
-njs_values_strict_equal(const njs_value_t *val1, const njs_value_t *val2)
-{
-    size_t        size, length1, length2;
-    const u_char  *start1, *start2;
-
-    if (val1->type != val2->type) {
-        return 0;
-    }
-
-    if (njs_is_numeric(val1)) {
-
-        if (njs_is_undefined(val1)) {
-            return 1;
-        }
-
-        /* Infinities are handled correctly by comparision. */
-        return (val1->data.u.number == val2->data.u.number);
-    }
-
-    if (njs_is_string(val1)) {
-        size = val1->short_string.size;
-
-        if (size != val2->short_string.size) {
-            return 0;
-        }
-
-        if (size != NJS_STRING_LONG) {
-            length1 = val1->short_string.length;
-            length2 = val2->short_string.length;
-
-            /*
-             * Using full memcmp() comparison if at least one string
-             * is a Byte string.
-             */
-            if (length1 != 0 && length2 != 0 && length1 != length2) {
-                return 0;
-            }
-
-            start1 = val1->short_string.start;
-            start2 = val2->short_string.start;
-
-        } else {
-            size = val1->long_string.size;
-
-            if (size != val2->long_string.size) {
-                return 0;
-            }
-
-            length1 = val1->long_string.data->length;
-            length2 = val2->long_string.data->length;
-
-            /*
-             * Using full memcmp() comparison if at least one string
-             * is a Byte string.
-             */
-            if (length1 != 0 && length2 != 0 && length1 != length2) {
-                return 0;
-            }
-
-            start1 = val1->long_string.data->start;
-            start2 = val2->long_string.data->start;
-        }
-
-        return (memcmp(start1, start2, size) == 0);
-    }
-
-    return (val1->data.u.object == val2->data.u.object);
-}
-
-
 njs_ret_t
 njs_vmcode_move(njs_vm_t *vm, njs_value_t *value, njs_value_t *invld)
 {
@@ -2094,122 +1936,6 @@ njs_vmcode_function_call(njs_vm_t *vm, njs_value_t *invld, njs_value_t *retval)
 }
 
 
-const char *
-njs_type_string(njs_value_type_t type)
-{
-    switch (type) {
-    case NJS_NULL:
-        return "null";
-
-    case NJS_UNDEFINED:
-        return "undefined";
-
-    case NJS_BOOLEAN:
-        return "boolean";
-
-    case NJS_NUMBER:
-        return "number";
-
-    case NJS_STRING:
-        return "string";
-
-    case NJS_EXTERNAL:
-        return "external";
-
-    case NJS_INVALID:
-        return "invalid";
-
-    case NJS_OBJECT:
-        return "object";
-
-    case NJS_ARRAY:
-        return "array";
-
-    case NJS_OBJECT_BOOLEAN:
-        return "object boolean";
-
-    case NJS_OBJECT_NUMBER:
-        return "object number";
-
-    case NJS_OBJECT_STRING:
-        return "object string";
-
-    case NJS_FUNCTION:
-        return "function";
-
-    case NJS_REGEXP:
-        return "regexp";
-
-    case NJS_DATE:
-        return "date";
-
-    case NJS_OBJECT_ERROR:
-        return "error";
-
-    case NJS_OBJECT_EVAL_ERROR:
-        return "eval error";
-
-    case NJS_OBJECT_INTERNAL_ERROR:
-        return "internal error";
-
-    case NJS_OBJECT_RANGE_ERROR:
-        return "range error";
-
-    case NJS_OBJECT_REF_ERROR:
-        return "reference error";
-
-    case NJS_OBJECT_SYNTAX_ERROR:
-        return "syntax error";
-
-    case NJS_OBJECT_TYPE_ERROR:
-        return "type error";
-
-    case NJS_OBJECT_URI_ERROR:
-        return "uri error";
-
-    default:
-        return NULL;
-    }
-}
-
-
-const char *
-njs_arg_type_string(uint8_t arg)
-{
-    switch (arg) {
-    case NJS_SKIP_ARG:
-        return "skip";
-
-    case NJS_NUMBER_ARG:
-        return "number";
-
-    case NJS_INTEGER_ARG:
-        return "integer";
-
-    case NJS_STRING_ARG:
-        return "string";
-
-    case NJS_OBJECT_ARG:
-        return "object";
-
-    case NJS_STRING_OBJECT_ARG:
-        return "string object";
-
-    case NJS_FUNCTION_ARG:
-        return "function";
-
-    case NJS_REGEXP_ARG:
-        return "regexp";
-
-    case NJS_DATE_ARG:
-        return "date";
-
-    default:
-        return "unknown";
-    }
-}
-
-
 njs_ret_t
 njs_vmcode_return(njs_vm_t *vm, njs_value_t *invld, njs_value_t *retval)
 {
@@ -2728,7 +2454,7 @@ njs_vmcode_number_primitive(njs_vm_t *vm, njs_value_t *invld, njs_value_t *narg)
 
     value = &vm->top_frame->trap_values[(uintptr_t) narg];
 
-    ret = njs_primitive_value(vm, value, 0);
+    ret = njs_value_to_primitive(vm, value, 0);
 
     if (nxt_fast_path(ret > 0)) {
 
@@ -2757,7 +2483,7 @@ njs_vmcode_string_primitive(njs_vm_t *vm, njs_value_t *invld, njs_value_t *narg)
 
     value = &vm->top_frame->trap_values[(uintptr_t) narg];
 
-    ret = njs_primitive_value(vm, value, 1);
+    ret = njs_value_to_primitive(vm, value, 1);
 
     if (nxt_fast_path(ret > 0)) {
         ret = njs_primitive_value_to_string(vm, value, value);
@@ -2787,7 +2513,7 @@ njs_vmcode_addition_primitive(njs_vm_t *vm, njs_value_t *invld,
      */
     hint = njs_is_date(value);
 
-    ret = njs_primitive_value(vm, value, hint);
+    ret = njs_value_to_primitive(vm, value, hint);
 
     if (nxt_fast_path(ret > 0)) {
         return sizeof(njs_vmcode_1addr_t);
@@ -2806,7 +2532,7 @@ njs_vmcode_comparison_primitive(njs_vm_t *vm, njs_value_t *invld,
 
     value = &vm->top_frame->trap_values[(uintptr_t) narg];
 
-    ret = njs_primitive_value(vm, value, 0);
+    ret = njs_value_to_primitive(vm, value, 0);
 
     if (nxt_fast_path(ret > 0)) {
         return sizeof(njs_vmcode_1addr_t);
@@ -2826,7 +2552,7 @@ njs_vmcode_number_argument(njs_vm_t *vm, njs_value_t *invld1,
 
     value = &vm->top_frame->trap_values[0];
 
-    ret = njs_primitive_value(vm, value, 0);
+    ret = njs_value_to_primitive(vm, value, 0);
 
     if (nxt_fast_path(ret > 0)) {
 
@@ -2861,7 +2587,7 @@ njs_vmcode_string_argument(njs_vm_t *vm, njs_value_t *invld1,
 
     value = &vm->top_frame->trap_values[0];
 
-    ret = njs_primitive_value(vm, value, 1);
+    ret = njs_value_to_primitive(vm, value, 1);
 
     if (nxt_fast_path(ret > 0)) {
         ret = njs_primitive_value_to_string(vm, value, value);
@@ -2887,7 +2613,7 @@ njs_vmcode_primitive_argument(njs_vm_t *vm, njs_value_t *invld1,
 
     value = &vm->top_frame->trap_values[0];
 
-    ret = njs_primitive_value(vm, value, 0);
+    ret = njs_value_to_primitive(vm, value, 0);
 
     if (nxt_fast_path(ret > 0)) {
         *vm->top_frame->trap_values[1].data.u.value = *value;
@@ -2945,146 +2671,6 @@ njs_vmcode_restart(njs_vm_t *vm, njs_value_t *invld1, njs_value_t *invld2)
 }
 
 
-/*
- * 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.
- */
-
-static njs_ret_t
-njs_primitive_value(njs_vm_t *vm, njs_value_t *value, nxt_uint_t hint)
-{
-    njs_ret_t           ret;
-    njs_value_t         *retval;
-    njs_function_t      *function;
-    njs_object_prop_t   *prop;
-    nxt_lvlhsh_query_t  lhq;
-
-    static const uint32_t  hashes[] = {
-        NJS_VALUE_OF_HASH,
-        NJS_TO_STRING_HASH,
-    };
-
-    static const nxt_str_t  names[] = {
-        nxt_string("valueOf"),
-        nxt_string("toString"),
-    };
-
-    if (!njs_is_primitive(value)) {
-        retval = &vm->top_frame->trap_scratch;
-
-        if (!njs_is_primitive(retval)) {
-
-            for ( ;; ) {
-                ret = NXT_ERROR;
-
-                if (njs_is_object(value) && vm->top_frame->trap_tries < 2) {
-                    hint ^= vm->top_frame->trap_tries++;
-
-                    lhq.key_hash = hashes[hint];
-                    lhq.key = names[hint];
-
-                    prop = njs_object_property(vm, value->data.u.object, &lhq);
-
-                    if (nxt_fast_path(prop != NULL)) {
-
-                        if (!njs_is_function(&prop->value)) {
-                            /* Try the second method. */
-                            continue;
-                        }
-
-                        function = prop->value.data.u.function;
-
-                        ret = njs_function_apply(vm, function, value, 1,
-                                                 (njs_index_t) retval);
-                        /*
-                         * njs_function_apply() can return
-                         *   NXT_OK, NJS_APPLIED, NXT_ERROR, NXT_AGAIN.
-                         */
-                        if (nxt_fast_path(ret == NXT_OK)) {
-
-                            if (njs_is_primitive(&vm->retval)) {
-                                retval = &vm->retval;
-                                break;
-                            }
-
-                            /* Try the second method. */
-                            continue;
-                        }
-
-                        if (ret == NJS_APPLIED) {
-                            /*
-                             * A user-defined method or continuation have
-                             * been prepared to run.  The method will return
-                             * to the current instruction and will restart it.
-                             */
-                            ret = 0;
-                        }
-                    }
-                }
-
-                if (ret == NXT_ERROR) {
-                    njs_type_error(vm,
-                                   "Cannot convert object to primitive value");
-                }
-
-                return ret;
-            }
-        }
-
-        *value = *retval;
-
-        njs_set_invalid(retval);
-    }
-
-    vm->top_frame->trap_tries = 0;
-
-    return 1;
-}
-
-
-njs_array_t *
-njs_value_enumerate(njs_vm_t *vm, const njs_value_t *value,
-    njs_object_enum_t kind, nxt_bool_t all)
-{
-    njs_object_value_t  obj_val;
-
-    if (njs_is_object(value)) {
-        return njs_object_enumerate(vm, value->data.u.object, kind, all);
-    }
-
-    if (value->type != NJS_STRING) {
-        return njs_array_alloc(vm, 0, NJS_ARRAY_SPARE);
-    }
-
-    obj_val.object = vm->string_object;
-    obj_val.value = *value;
-
-    return njs_object_enumerate(vm, (njs_object_t *) &obj_val, kind, all);
-}
-
-
-njs_array_t *
-njs_value_own_enumerate(njs_vm_t *vm, const njs_value_t *value,
-    njs_object_enum_t kind, nxt_bool_t all)
-{
-    njs_object_value_t  obj_val;
-
-    if (njs_is_object(value)) {
-        return njs_object_own_enumerate(vm, value->data.u.object, kind, all);
-    }
-
-    if (value->type != NJS_STRING) {
-        return njs_array_alloc(vm, 0, NJS_ARRAY_SPARE);
-    }
-
-    obj_val.object = vm->string_object;
-    obj_val.value = *value;
-
-    return njs_object_own_enumerate(vm, (njs_object_t *) &obj_val, kind, all);
-}
-
-
 njs_ret_t
 njs_vm_value_to_string(njs_vm_t *vm, nxt_str_t *dst, const njs_value_t *src)
 {
@@ -3198,7 +2784,7 @@ njs_vmcode_value_to_string(njs_vm_t *vm, njs_value_t *invld1,
 {
     njs_ret_t  ret;
 
-    ret = njs_primitive_value(vm, &vm->top_frame->trap_values[0], 1);
+    ret = njs_value_to_primitive(vm, &vm->top_frame->trap_values[0], 1);
 
     if (nxt_fast_path(ret > 0)) {
         return NJS_STOP;
@@ -3380,3 +2966,4 @@ njs_lvlhsh_free(void *data, void *p, size_t size)
 {
     nxt_mp_free(data, p);
 }
+
index 6fc9c4b56cf649c42ee4b831873af71ee3e30b09..e6029c11921f4ea4b6adc63086675c78c6fcf152 100644 (file)
@@ -8,16 +8,6 @@
 #define _NJS_VM_H_INCLUDED_
 
 
-#include <nxt_trace.h>
-#include <nxt_queue.h>
-#include <nxt_regex.h>
-#include <nxt_random.h>
-#include <nxt_djb_hash.h>
-#include <nxt_mp.h>
-
-#include <math.h>
-
-
 #define NJS_MAX_STACK_SIZE       (16 * 1024 * 1024)
 
 /*
@@ -73,291 +63,12 @@ typedef enum {
 #define NJS_PROPERTY_QUERY_DELETE  2
 
 
-/*
- * The order of the enum is used in njs_vmcode_typeof()
- * and njs_object_prototype_to_string().
- */
-
-typedef enum {
-    NJS_NULL                  = 0x00,
-    NJS_UNDEFINED             = 0x01,
-
-    /* The order of the above type is used in njs_is_null_or_undefined(). */
-
-    NJS_BOOLEAN               = 0x02,
-    /*
-     * The order of the above type is used in
-     * njs_is_null_or_undefined_or_boolean().
-     */
-    NJS_NUMBER                = 0x03,
-    /*
-     * The order of the above type is used in njs_is_numeric().
-     * Booleans, null and void values can be used in mathematical operations:
-     *   a numeric value of the true value is one,
-     *   a numeric value of the null and false values is zero,
-     *   a numeric value of the void value is NaN.
-     */
-    NJS_STRING                = 0x04,
-
-    /* The order of the above type is used in njs_is_primitive(). */
-
-    NJS_DATA                  = 0x05,
-
-    /* The type is external code. */
-    NJS_EXTERNAL              = 0x06,
-
-    /*
-     * The invalid value type is used:
-     *   for uninitialized array members,
-     *   to detect non-declared explicitly or implicitly variables,
-     *   for native property getters.
-     */
-    NJS_INVALID               = 0x07,
-
-    /*
-     * The object types are >= NJS_OBJECT, this is used in njs_is_object().
-     * NJS_OBJECT_BOOLEAN, NJS_OBJECT_NUMBER, and NJS_OBJECT_STRING must be
-     * in the same order as NJS_BOOLEAN, NJS_NUMBER, and NJS_STRING.  It is
-     * used in njs_primitive_prototype_index().  The order of object types
-     * is used in vm->prototypes and vm->constructors arrays.
-     */
-    NJS_OBJECT                = 0x10,
-    NJS_ARRAY                 = 0x11,
-    NJS_OBJECT_BOOLEAN        = 0x12,
-    NJS_OBJECT_NUMBER         = 0x13,
-    NJS_OBJECT_STRING         = 0x14,
-    NJS_FUNCTION              = 0x15,
-    NJS_REGEXP                = 0x16,
-    NJS_DATE                  = 0x17,
-    NJS_OBJECT_ERROR          = 0x18,
-    NJS_OBJECT_EVAL_ERROR     = 0x19,
-    NJS_OBJECT_INTERNAL_ERROR = 0x1a,
-    NJS_OBJECT_RANGE_ERROR    = 0x1b,
-    NJS_OBJECT_REF_ERROR      = 0x1c,
-    NJS_OBJECT_SYNTAX_ERROR   = 0x1d,
-    NJS_OBJECT_TYPE_ERROR     = 0x1e,
-    NJS_OBJECT_URI_ERROR      = 0x1f,
-    NJS_OBJECT_VALUE          = 0x20,
-#define NJS_TYPE_MAX         (NJS_OBJECT_VALUE + 1)
-} njs_value_type_t;
-
-
-typedef struct njs_parser_s           njs_parser_t;
-typedef struct njs_generator_s        njs_generator_t;
-
-/*
- * njs_prop_handler_t operates as a property getter and/or setter.
- * The handler receives NULL setval if it is invoked in GET context and
- * non-null otherwise.
- *
- * njs_prop_handler_t is expected to return:
- *   NXT_OK - handler executed successfully;
- *   NXT_ERROR - some error, vm->retval contains appropriate exception;
- *   NXT_DECLINED - handler was applied to inappropriate object.
- */
-typedef njs_ret_t (*njs_prop_handler_t) (njs_vm_t *vm, njs_value_t *value,
-    njs_value_t *setval, njs_value_t *retval);
-typedef njs_ret_t (*njs_function_native_t) (njs_vm_t *vm, njs_value_t *args,
-    nxt_uint_t nargs, njs_index_t retval);
-
-
-typedef struct njs_string_s           njs_string_t;
-typedef struct njs_object_s           njs_object_t;
-typedef struct njs_object_init_s      njs_object_init_t;
-typedef struct njs_object_value_s     njs_object_value_t;
-typedef struct njs_array_s            njs_array_t;
-typedef struct njs_function_lambda_s  njs_function_lambda_t;
-typedef struct njs_regexp_s           njs_regexp_t;
-typedef struct njs_regexp_pattern_s   njs_regexp_pattern_t;
-typedef struct njs_date_s             njs_date_t;
 typedef struct njs_frame_s            njs_frame_t;
 typedef struct njs_native_frame_s     njs_native_frame_t;
-typedef struct njs_property_next_s    njs_property_next_t;
+typedef struct njs_parser_s           njs_parser_t;
 typedef struct njs_parser_scope_s     njs_parser_scope_t;
 typedef struct njs_parser_node_s      njs_parser_node_t;
-
-
-union njs_value_s {
-    /*
-     * The njs_value_t size is 16 bytes and must be aligned to 16 bytes
-     * to provide 4 bits to encode scope in njs_index_t.  This space is
-     * used to store short strings.  The maximum size of a short string
-     * is 14 (NJS_STRING_SHORT).  If the short_string.size field is 15
-     * (NJS_STRING_LONG) then the size is in the long_string.size field
-     * and the long_string.data field points to a long string.
-     *
-     * The number of the string types is limited to 2 types to minimize
-     * overhead of processing string fields.  It is also possible to add
-     * strings with size from 14 to 254 which size and length are stored in
-     * the string_size and string_length byte wide fields.  This will lessen
-     * the maximum size of short string to 13.
-     */
-    struct {
-        njs_value_type_t              type:8;  /* 6 bits */
-        /*
-         * The truth field is set during value assignment and then can be
-         * quickly tested by logical and conditional operations regardless
-         * of value type.  The truth field coincides with short_string.size
-         * and short_string.length so when string size and length are zero
-         * the string's value is false.
-         */
-        uint8_t                       truth;
-
-        uint16_t                      _spare1;
-        uint32_t                      _spare2;
-
-        union {
-            double                    number;
-            njs_object_t              *object;
-            njs_array_t               *array;
-            njs_object_value_t        *object_value;
-            njs_function_t            *function;
-            njs_function_lambda_t     *lambda;
-            njs_regexp_t              *regexp;
-            njs_date_t                *date;
-            njs_prop_handler_t        prop_handler;
-            njs_value_t               *value;
-            njs_property_next_t       *next;
-            void                      *data;
-        } u;
-    } data;
-
-    struct {
-        njs_value_type_t              type:8;  /* 6 bits */
-
-#define NJS_STRING_SHORT              14
-#define NJS_STRING_LONG               15
-
-        uint8_t                       size:4;
-        uint8_t                       length:4;
-
-        u_char                        start[NJS_STRING_SHORT];
-    } short_string;
-
-    struct {
-        njs_value_type_t              type:8;  /* 6 bits */
-        uint8_t                       truth;
-
-        /* 0xff if data is external string. */
-        uint8_t                       external;
-        uint8_t                       _spare;
-
-        uint32_t                      size;
-        njs_string_t                  *data;
-    } long_string;
-
-    struct {
-        njs_value_type_t              type:8;  /* 6 bits */
-        uint8_t                       truth;
-
-        uint16_t                      _spare;
-
-        uint32_t                      index;
-        const njs_extern_t            *proto;
-    } external;
-
-    njs_value_type_t                  type:8;  /* 6 bits */
-};
-
-
-struct njs_object_s {
-    /* A private hash of njs_object_prop_t. */
-    nxt_lvlhsh_t                      hash;
-
-    /* A shared hash of njs_object_prop_t. */
-    nxt_lvlhsh_t                      shared_hash;
-
-    /* An object __proto__. */
-    njs_object_t                      *__proto__;
-
-    /* The type is used in constructor prototypes. */
-    njs_value_type_t                  type:8;
-    uint8_t                           shared;     /* 1 bit */
-    uint8_t                           extensible; /* 1 bit */
-};
-
-
-struct njs_object_value_s {
-    njs_object_t                      object;
-    /* The value can be unaligned since it never used in nJSVM operations. */
-    njs_value_t                       value;
-};
-
-
-struct njs_array_s {
-    njs_object_t                      object;
-    uint32_t                          size;
-    uint32_t                          length;
-    njs_value_t                       *start;
-    njs_value_t                       *data;
-};
-
-
-typedef struct {
-    union {
-        uint32_t                      count;
-        njs_value_t                   values;
-    } u;
-
-    njs_value_t                       values[1];
-} njs_closure_t;
-
-
-#define NJS_ARGS_TYPES_MAX            5
-
-struct njs_function_s {
-    njs_object_t                      object;
-
-    uint8_t                           args_types[NJS_ARGS_TYPES_MAX];
-    uint8_t                           args_offset;
-    uint8_t                           continuation_size;
-
-    /* Function is a closure. */
-    uint8_t                           closure:1;
-
-    uint8_t                           native:1;
-    uint8_t                           ctor:1;
-
-    union {
-        njs_function_lambda_t         *lambda;
-        njs_function_native_t         native;
-    } u;
-
-    njs_value_t                       *bound;
-#if (NXT_SUNC)
-    njs_closure_t                     *closures[1];
-#else
-    njs_closure_t                     *closures[];
-#endif
-};
-
-
-struct njs_regexp_s {
-    njs_object_t                      object;
-    uint32_t                          last_index;
-    njs_regexp_pattern_t              *pattern;
-    /*
-     * This string value can be unaligned since
-     * it never used in nJSVM operations.
-     */
-    njs_value_t                       string;
-};
-
-
-struct njs_date_s {
-    njs_object_t                      object;
-    double                            time;
-};
-
-
-typedef union {
-    njs_object_t                      object;
-    njs_object_value_t                object_value;
-    njs_array_t                       array;
-    njs_function_t                    function;
-    njs_regexp_t                      regexp;
-    njs_date_t                        date;
-} njs_object_prototype_t;
+typedef struct njs_generator_s        njs_generator_t;
 
 
 typedef struct {
@@ -367,324 +78,10 @@ typedef struct {
 } njs_backtrace_entry_t;
 
 
-#define njs_value(_type, _truth, _number) {                                   \
-    .data = {                                                                 \
-        .type = _type,                                                        \
-        .truth = _truth,                                                      \
-        .u.number = _number,                                                  \
-    }                                                                         \
-}
-
-
-#define njs_string(s) {                                                       \
-    .short_string = {                                                         \
-        .type = NJS_STRING,                                                   \
-        .size = nxt_length(s),                                                \
-        .length = nxt_length(s),                                              \
-        .start = s,                                                           \
-    }                                                                         \
-}
-
-
-/* NJS_STRING_LONG is set for both big and little endian platforms. */
-
-#define njs_long_string(s) {                                                  \
-    .long_string = {                                                          \
-        .type = NJS_STRING,                                                   \
-        .truth = (NJS_STRING_LONG << 4) | NJS_STRING_LONG,                    \
-        .size = nxt_length(s),                                                \
-        .data = & (njs_string_t) {                                            \
-            .start = (u_char *) s,                                            \
-            .length = nxt_length(s),                                          \
-        }                                                                     \
-    }                                                                         \
-}
-
-
-#define njs_native_function(_function, _size, ...) {                          \
-    .data = {                                                                 \
-        .type = NJS_FUNCTION,                                                 \
-        .truth = 1,                                                           \
-        .u.function = & (njs_function_t) {                                    \
-            .native = 1,                                                      \
-            .continuation_size = _size,                                       \
-            .args_types = { __VA_ARGS__ },                                    \
-            .args_offset = 1,                                                 \
-            .u.native = _function,                                            \
-            .object = { .type = NJS_FUNCTION,                                 \
-                        .shared = 1,                                          \
-                        .extensible = 1 },                                    \
-        }                                                                     \
-    }                                                                         \
-}
-
-
-#define njs_prop_handler(_handler) {                                          \
-    .data = {                                                                 \
-        .type = NJS_INVALID,                                                  \
-        .truth = 1,                                                           \
-        .u = { .prop_handler = _handler }                                     \
-    }                                                                         \
-}
-
-
 typedef njs_ret_t (*njs_vmcode_operation_t)(njs_vm_t *vm, njs_value_t *value1,
     njs_value_t *value2);
 
 
-#define njs_is_null(value)                                                    \
-    ((value)->type == NJS_NULL)
-
-
-#define njs_is_undefined(value)                                               \
-    ((value)->type == NJS_UNDEFINED)
-
-
-#define njs_is_null_or_undefined(value)                                       \
-    ((value)->type <= NJS_UNDEFINED)
-
-
-#define njs_is_boolean(value)                                                 \
-    ((value)->type == NJS_BOOLEAN)
-
-
-#define njs_is_null_or_undefined_or_boolean(value)                            \
-    ((value)->type <= NJS_BOOLEAN)
-
-
-#define njs_is_true(value)                                                    \
-    ((value)->data.truth != 0)
-
-
-#define njs_is_number(value)                                                  \
-    ((value)->type == NJS_NUMBER)
-
-
-/* Testing for NaN first generates a better code at least on i386/amd64. */
-
-#define njs_is_number_true(num)                                               \
-    (!isnan(num) && num != 0)
-
-
-#define njs_is_numeric(value)                                                 \
-    ((value)->type <= NJS_NUMBER)
-
-
-#define njs_is_string(value)                                                  \
-    ((value)->type == NJS_STRING)
-
-#define njs_is_error(value)                                                   \
-    ((value)->type >= NJS_OBJECT_ERROR                                        \
-     && (value)->type <= NJS_OBJECT_URI_ERROR)
-
-
-/*
- * The truth field coincides with short_string.size and short_string.length
- * so when string size and length are zero the string's value is false and
- * otherwise is true.
- */
-#define njs_string_truth(value, size)
-
-
-#define njs_string_get(value, str)                                            \
-    do {                                                                      \
-        if ((value)->short_string.size != NJS_STRING_LONG) {                  \
-            (str)->length = (value)->short_string.size;                       \
-            (str)->start = (u_char *) (value)->short_string.start;            \
-                                                                              \
-        } else {                                                              \
-            (str)->length = (value)->long_string.size;                        \
-            (str)->start = (u_char *) (value)->long_string.data->start;       \
-        }                                                                     \
-    } while (0)
-
-
-#define njs_string_short_start(value)                                         \
-    (value)->short_string.start
-
-
-#define njs_string_short_set(value, _size, _length)                           \
-    do {                                                                      \
-        (value)->type = NJS_STRING;                                           \
-        njs_string_truth(value, _size);                                       \
-        (value)->short_string.size = _size;                                   \
-        (value)->short_string.length = _length;                               \
-    } while (0)
-
-
-#define njs_string_length_set(value, _length)                                 \
-    do {                                                                      \
-        if ((value)->short_string.size != NJS_STRING_LONG) {                  \
-            (value)->short_string.length = length;                            \
-                                                                              \
-        } else {                                                              \
-            (value)->long_string.data->length = length;                       \
-        }                                                                     \
-    } while (0)
-
-#define njs_is_primitive(value)                                               \
-    ((value)->type <= NJS_STRING)
-
-
-#define njs_is_data(value)                                                    \
-    ((value)->type == NJS_DATA)
-
-
-#define njs_is_object(value)                                                  \
-    ((value)->type >= NJS_OBJECT)
-
-
-#define njs_is_object_value(value)                                            \
-    ((value)->type == NJS_OBJECT_VALUE)
-
-
-#define njs_object_value_type(type)                                           \
-    (type + NJS_OBJECT)
-
-
-#define njs_is_array(value)                                                   \
-    ((value)->type == NJS_ARRAY)
-
-
-#define njs_is_function(value)                                                \
-    ((value)->type == NJS_FUNCTION)
-
-
-#define njs_is_regexp(value)                                                  \
-    ((value)->type == NJS_REGEXP)
-
-
-#define njs_is_date(value)                                                    \
-    ((value)->type == NJS_DATE)
-
-
-#define njs_is_external(value)                                                \
-    ((value)->type == NJS_EXTERNAL)
-
-
-#define njs_is_valid(value)                                                   \
-    ((value)->type != NJS_INVALID)
-
-
-#define njs_bool(value)                                                       \
-    ((value)->data.truth)
-
-
-#define njs_number(value)                                                     \
-    ((value)->data.u.number)
-
-
-#define njs_data(value)                                                       \
-    ((value)->data.u.data)
-
-
-#define njs_function(value)                                                   \
-    ((value)->data.u.function)
-
-
-#define njs_object(value)                                                     \
-    ((value)->data.u.object)
-
-
-#define njs_object_hash(value)                                                \
-    (&(value)->data.u.object->hash)
-
-
-#define njs_array(value)                                                      \
-    ((value)->data.u.array)
-
-
-#define njs_array_len(value)                                                  \
-    ((value)->data.u.array->length)
-
-
-#define njs_array_start(value)                                                \
-    ((value)->data.u.array->start)
-
-
-#define njs_set_undefined(value)                                              \
-    *(value) = njs_value_undefined
-
-
-#define njs_set_boolean(value, yn)                                            \
-    *(value) = yn ? njs_value_true : njs_value_false
-
-
-#define njs_set_true(value)                                                   \
-    *(value) = njs_value_true
-
-
-#define njs_set_false(value)                                                  \
-    *(value) = njs_value_false
-
-
-nxt_inline void
-njs_set_number(njs_value_t *value, double num)
-{
-    value->data.u.number = num;
-    value->type = NJS_NUMBER;
-    value->data.truth = njs_is_number_true(num);
-}
-
-
-nxt_inline void
-njs_set_data(njs_value_t *value, void *data)
-{
-    value->data.u.data = data;
-    value->type = NJS_DATA;
-    value->data.truth = 1;
-}
-
-
-nxt_inline void
-njs_set_object(njs_value_t *value, njs_object_t *object)
-{
-    value->data.u.object = object;
-    value->type = NJS_OBJECT;
-    value->data.truth = 1;
-}
-
-
-nxt_inline void
-njs_set_array(njs_value_t *value, njs_array_t *array)
-{
-    value->data.u.array = array;
-    value->type = NJS_ARRAY;
-    value->data.truth = 1;
-}
-
-
-#define njs_set_invalid(value)                                                \
-    (value)->type = NJS_INVALID
-
-
-#if 0 /* GC: todo */
-
-#define njs_retain(value)                                                     \
-    do {                                                                      \
-        if ((value)->data.truth == NJS_STRING_LONG) {                         \
-            njs_value_retain(value);                                          \
-        }                                                                     \
-    } while (0)
-
-
-#define njs_release(vm, value)                                                \
-    do {                                                                      \
-        if ((value)->data.truth == NJS_STRING_LONG) {                         \
-            njs_value_release((vm), (value));                                 \
-        }                                                                     \
-    } while (0)
-
-#else
-
-#define njs_retain(value)
-
-
-#define njs_release(vm, value)
-
-#endif
-
-
 #define NJS_VMCODE_3OPERANDS   0
 #define NJS_VMCODE_2OPERANDS   1
 #define NJS_VMCODE_1OPERAND    2
@@ -1261,18 +658,8 @@ struct njs_vm_shared_s {
 };
 
 
-typedef enum {
-    NJS_ENUM_KEYS,
-    NJS_ENUM_VALUES,
-    NJS_ENUM_BOTH,
-} njs_object_enum_t;
-
-
 nxt_int_t njs_vmcode_interpreter(njs_vm_t *vm);
 
-void njs_value_retain(njs_value_t *value);
-void njs_value_release(njs_vm_t *vm, njs_value_t *value);
-
 njs_ret_t njs_vmcode_object(njs_vm_t *vm, njs_value_t *inlvd1,
     njs_value_t *inlvd2);
 njs_ret_t njs_vmcode_array(njs_vm_t *vm, njs_value_t *inlvd1,
@@ -1413,15 +800,6 @@ njs_ret_t njs_vmcode_finally(njs_vm_t *vm, njs_value_t *invld,
 njs_ret_t njs_vmcode_reference_error(njs_vm_t *vm, njs_value_t *invld1,
     njs_value_t *invld2);
 
-nxt_bool_t njs_values_strict_equal(const njs_value_t *val1,
-    const njs_value_t *val2);
-
-const char *njs_type_string(njs_value_type_t type);
-const char *njs_arg_type_string(uint8_t arg);
-
-njs_ret_t njs_native_function_arguments(njs_vm_t *vm, njs_value_t *args,
-    uint8_t *args_types, nxt_uint_t nargs);
-
 nxt_int_t njs_builtin_objects_create(njs_vm_t *vm);
 nxt_int_t njs_builtin_objects_clone(njs_vm_t *vm);
 nxt_int_t njs_builtin_match_native_function(njs_vm_t *vm,
@@ -1432,32 +810,6 @@ nxt_array_t *njs_vm_backtrace(njs_vm_t *vm);
 void *njs_lvlhsh_alloc(void *data, size_t size);
 void njs_lvlhsh_free(void *data, void *p, size_t size);
 
-njs_array_t * njs_value_enumerate(njs_vm_t *vm, const njs_value_t *value,
-    njs_object_enum_t kind, nxt_bool_t all);
-njs_array_t * njs_value_own_enumerate(njs_vm_t *vm, const njs_value_t *value,
-    njs_object_enum_t kind, nxt_bool_t all);
-
-extern const njs_value_t  njs_value_undefined;
-extern const njs_value_t  njs_value_null;
-extern const njs_value_t  njs_value_false;
-extern const njs_value_t  njs_value_true;
-extern const njs_value_t  njs_value_zero;
-extern const njs_value_t  njs_value_nan;
-extern const njs_value_t  njs_value_invalid;
-
-extern const njs_value_t  njs_string_empty;
-extern const njs_value_t  njs_string_comma;
-extern const njs_value_t  njs_string_undefined;
-extern const njs_value_t  njs_string_null;
-extern const njs_value_t  njs_string_false;
-extern const njs_value_t  njs_string_true;
-extern const njs_value_t  njs_string_native;
-extern const njs_value_t  njs_string_minus_zero;
-extern const njs_value_t  njs_string_minus_infinity;
-extern const njs_value_t  njs_string_plus_infinity;
-extern const njs_value_t  njs_string_nan;
-extern const njs_value_t  njs_string_internal_error;
-extern const njs_value_t  njs_string_memory_error;
 
 extern const nxt_str_t    njs_entry_main;
 extern const nxt_str_t    njs_entry_module;