]> git.kaiwu.me - njs.git/commitdiff
Math object.
authorIgor Sysoev <igor@sysoev.ru>
Wed, 23 Mar 2016 12:27:14 +0000 (15:27 +0300)
committerIgor Sysoev <igor@sysoev.ru>
Wed, 23 Mar 2016 12:27:14 +0000 (15:27 +0300)
17 files changed:
Makefile
njs/njs_array.c
njs/njs_builtin.c
njs/njs_disassembler.c
njs/njs_function.c
njs/njs_generator.c
njs/njs_lexer_keyword.c
njs/njs_math.c [new file with mode: 0644]
njs/njs_math.h [new file with mode: 0644]
njs/njs_object.c
njs/njs_object.h
njs/njs_parser.c
njs/njs_parser.h
njs/njs_regexp.c
njs/njs_vm.c
njs/njs_vm.h
njs/test/njs_unit_test.c

index d13489a168bd7da74881db18b91cef0223bf9c02..168795b3dbcf0c40de1f4491fd74947afa7941e2 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -18,6 +18,7 @@ $(NXT_BUILDDIR)/libnjs.a: \
        $(NXT_BUILDDIR)/njs_array.o \
        $(NXT_BUILDDIR)/njs_function.o \
        $(NXT_BUILDDIR)/njs_regexp.o \
+       $(NXT_BUILDDIR)/njs_math.o \
        $(NXT_BUILDDIR)/njs_extern.o \
        $(NXT_BUILDDIR)/njs_variable.o \
        $(NXT_BUILDDIR)/njs_builtin.o \
@@ -46,6 +47,7 @@ $(NXT_BUILDDIR)/libnjs.a: \
                $(NXT_BUILDDIR)/njs_array.o \
                $(NXT_BUILDDIR)/njs_function.o \
                $(NXT_BUILDDIR)/njs_regexp.o \
+               $(NXT_BUILDDIR)/njs_math.o \
                $(NXT_BUILDDIR)/njs_extern.o \
                $(NXT_BUILDDIR)/njs_variable.o \
                $(NXT_BUILDDIR)/njs_builtin.o \
@@ -220,6 +222,18 @@ $(NXT_BUILDDIR)/njs_regexp.o: \
                -I$(NXT_LIB) -Injs $(NXT_PCRE_CFLAGS) \
                njs/njs_regexp.c
 
+$(NXT_BUILDDIR)/njs_math.o: \
+       $(NXT_BUILDDIR)/libnxt.a \
+       njs/njscript.h \
+       njs/njs_vm.h \
+       njs/njs_object.h \
+       njs/njs_math.h \
+       njs/njs_math.c \
+
+       $(NXT_CC) -c -o $(NXT_BUILDDIR)/njs_math.o $(NXT_CFLAGS) \
+               -I$(NXT_LIB) -Injs \
+               njs/njs_math.c
+
 $(NXT_BUILDDIR)/njs_extern.o: \
        $(NXT_BUILDDIR)/libnxt.a \
        njs/njscript.h \
index ffed79ae2e59c6e02eebd4f7ccf864a4e899e3cb..35d4b01bad8d00ef4fa070f0ab121a222277d97a 100644 (file)
@@ -129,6 +129,7 @@ njs_array_alloc(njs_vm_t *vm, uint32_t length, uint32_t spare)
     nxt_lvlhsh_init(&array->object.hash);
     nxt_lvlhsh_init(&array->object.shared_hash);
     array->object.__proto__ = &vm->prototypes[NJS_PROTOTYPE_ARRAY];
+    array->object.shared = 0;
     array->size = size;
     array->length = length;
 
index d50953ea3959246527ede57f2f19932c356dc933..965d455962df37be3a938bc8d31b944dc8c1490d 100644 (file)
@@ -19,6 +19,7 @@
 #include <njs_array.h>
 #include <njs_function.h>
 #include <njs_regexp.h>
+#include <njs_math.h>
 #include <string.h>
 
 
@@ -33,7 +34,7 @@ njs_builtin_objects_create(njs_vm_t *vm)
 {
     nxt_int_t                         ret;
     nxt_uint_t                        i;
-    njs_object_t                      *prototypes;
+    njs_object_t                      *objects, *prototypes;
     njs_function_t                    *functions;
 
     static const njs_object_init_t    *prototype_init[] = {
@@ -71,6 +72,10 @@ njs_builtin_objects_create(njs_vm_t *vm)
         { njs_eval_function,        { 0 } },
     };
 
+    static const njs_object_init_t    *objects_init[] = {
+        &njs_math_object_init,
+    };
+
     static const njs_object_prop_t  null_proto_property = {
         .type = NJS_WHITEOUT,
         .name = njs_string("__proto__"),
@@ -83,6 +88,19 @@ njs_builtin_objects_create(njs_vm_t *vm)
         return NXT_ERROR;
     }
 
+    objects = vm->shared->objects;
+
+    for (i = NJS_OBJECT_MATH; i < NJS_OBJECT_MAX; i++) {
+        ret = njs_object_hash_create(vm, &objects[i].shared_hash,
+                                     objects_init[i]->properties,
+                                     objects_init[i]->items);
+        if (nxt_slow_path(ret != NXT_OK)) {
+            return NXT_ERROR;
+        }
+
+        objects[i].shared = 1;
+    }
+
     prototypes = vm->shared->prototypes;
 
     for (i = NJS_PROTOTYPE_OBJECT; i < NJS_PROTOTYPE_MAX; i++) {
@@ -99,9 +117,9 @@ njs_builtin_objects_create(njs_vm_t *vm)
     functions = vm->shared->functions;
 
     for (i = NJS_FUNCTION_OBJECT; i < NJS_FUNCTION_MAX; i++) {
+        functions[i].object.shared = 0;
         functions[i].native = 1;
         functions[i].args_offset = 1;
-        functions[i].shared = 0;
         functions[i].u.native = native_functions[i].native;
         functions[i].args_types[0] = native_functions[i].args_types[0];
         functions[i].args_types[1] = native_functions[i].args_types[1];
index ef4893b701e5e557c1190968a29b17d893d55841..a75608d30cacc678ecc9a671782c081e2581723b 100644 (file)
@@ -35,10 +35,10 @@ static njs_code_name_t  code_names[] = {
           nxt_string("OBJECT          ") },
     { njs_vmcode_function, sizeof(njs_vmcode_function_t),
           nxt_string("FUNCTION        ") },
-    { njs_vmcode_function_copy, sizeof(njs_vmcode_function_copy_t),
-          nxt_string("FUNCTION COPY   ") },
     { njs_vmcode_regexp, sizeof(njs_vmcode_regexp_t),
           nxt_string("REGEXP          ") },
+    { njs_vmcode_object_copy, sizeof(njs_vmcode_object_copy_t),
+          nxt_string("OBJECT COPY     ") },
 
     { njs_vmcode_property_get, sizeof(njs_vmcode_prop_get_t),
           nxt_string("PROPERTY GET    ") },
index 7586133a108324c290a860c901b21ca8f5877d66..058da84fdc7d5cbf8193f530f892dca270a77504 100644 (file)
@@ -31,8 +31,15 @@ njs_function_alloc(njs_vm_t *vm)
     function = nxt_mem_cache_zalloc(vm->mem_cache_pool, sizeof(njs_function_t));
 
     if (nxt_fast_path(function != NULL)) {
+        /*
+         * nxt_mem_cache_zalloc() does also:
+         *   nxt_lvlhsh_init(&function->object.hash);
+         *   nxt_lvlhsh_init(&function->object.shared_hash);
+         *   function->object.__proto__ = NULL;
+         */
+
+        function->object.shared = 1;
         function->args_offset = 1;
-        function->shared = 1;
 
         function->u.lambda = nxt_mem_cache_zalloc(vm->mem_cache_pool,
                                                  sizeof(njs_function_lambda_t));
@@ -52,7 +59,7 @@ njs_function_value_copy(njs_vm_t *vm, njs_value_t *value)
 
     function = value->data.u.function;
 
-    if (!function->shared) {
+    if (!function->object.shared) {
         return function;
     }
 
@@ -61,7 +68,7 @@ njs_function_value_copy(njs_vm_t *vm, njs_value_t *value)
     if (nxt_fast_path(function != NULL)) {
         *function = *value->data.u.function;
         function->object.__proto__ = &vm->prototypes[NJS_PROTOTYPE_FUNCTION];
-        function->shared = 0;
+        function->object.shared = 0;
         value->data.u.function = function;
     }
 
@@ -458,7 +465,7 @@ njs_function_prototype_bind(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
     *function = *args[0].data.u.function;
 
     function->object.__proto__ = &vm->prototypes[NJS_PROTOTYPE_FUNCTION];
-    function->shared = 0;
+    function->object.shared = 0;
 
     if (nargs == 1) {
         args = (njs_value_t *) &njs_value_void;
index c6eb141adb85032cbe001ae11dc3d0388759e9d0..2cbfa542090c45411b3a13a794a8c9abd48d61c6 100644 (file)
@@ -25,6 +25,8 @@ static nxt_int_t njs_generator(njs_vm_t *vm, njs_parser_t *parser,
     njs_parser_node_t *node);
 static nxt_int_t njs_generate_name(njs_vm_t *vm, njs_parser_t *parser,
     njs_parser_node_t *node);
+static nxt_int_t njs_generate_builtin_object(njs_vm_t *vm, njs_parser_t *parser,
+    njs_parser_node_t *node);
 static nxt_int_t njs_generate_variable(njs_parser_t *parser,
     njs_parser_node_t *node);
 static nxt_int_t njs_generate_if_statement(njs_vm_t *vm, njs_parser_t *parser,
@@ -289,6 +291,9 @@ njs_generator(njs_vm_t *vm, njs_parser_t *parser, njs_parser_node_t *node)
     case NJS_TOKEN_NAME:
         return njs_generate_name(vm, parser, node);
 
+    case NJS_TOKEN_MATH:
+        return njs_generate_builtin_object(vm, parser, node);
+
     case NJS_TOKEN_FUNCTION:
         return njs_generate_function_declaration(vm, parser, node);
 
@@ -319,9 +324,9 @@ njs_generator(njs_vm_t *vm, njs_parser_t *parser, njs_parser_node_t *node)
 static nxt_int_t
 njs_generate_name(njs_vm_t *vm, njs_parser_t *parser, njs_parser_node_t *node)
 {
-    njs_index_t                 index;
-    njs_value_t                 *value;
-    njs_vmcode_function_copy_t  *copy;
+    njs_index_t               index;
+    njs_value_t               *value;
+    njs_vmcode_object_copy_t  *copy;
 
     index = node->u.variable->index;
     value = njs_variable_value(parser, index);
@@ -333,12 +338,12 @@ njs_generate_name(njs_vm_t *vm, njs_parser_t *parser, njs_parser_node_t *node)
             return node->index;
         }
 
-        njs_generate_code(parser, njs_vmcode_function_copy_t, copy);
-        copy->code.operation = njs_vmcode_function_copy;
+        njs_generate_code(parser, njs_vmcode_object_copy_t, copy);
+        copy->code.operation = njs_vmcode_object_copy;
         copy->code.operands = NJS_VMCODE_2OPERANDS;
         copy->code.retval = NJS_VMCODE_RETVAL;
         copy->retval = node->index;
-        copy->function = index;
+        copy->object = index;
 
         return NXT_OK;
     }
@@ -347,6 +352,31 @@ njs_generate_name(njs_vm_t *vm, njs_parser_t *parser, njs_parser_node_t *node)
 }
 
 
+static nxt_int_t
+njs_generate_builtin_object(njs_vm_t *vm, njs_parser_t *parser,
+    njs_parser_node_t *node)
+{
+    njs_index_t               index;
+    njs_vmcode_object_copy_t  *copy;
+
+    index = node->index;
+
+    node->index = njs_generator_dest_index(vm, parser, node);
+    if (nxt_slow_path(node->index == NJS_INDEX_ERROR)) {
+        return node->index;
+    }
+
+    njs_generate_code(parser, njs_vmcode_object_copy_t, copy);
+    copy->code.operation = njs_vmcode_object_copy;
+    copy->code.operands = NJS_VMCODE_2OPERANDS;
+    copy->code.retval = NJS_VMCODE_RETVAL;
+    copy->retval = node->index;
+    copy->object = index;
+
+    return NXT_OK;
+}
+
+
 static nxt_int_t
 njs_generate_variable(njs_parser_t *parser, njs_parser_node_t *node)
 {
index a632b6c3d57bd6f0ce3e3dafb8a4579757b85fba..ff9c5c88afb5f364c833d812fe96d22e3e4c2dda 100644 (file)
@@ -73,6 +73,7 @@ static const njs_keyword_t  njs_keywords[] = {
     /* Builtin objects. */
 
     { nxt_string("this"),          NJS_TOKEN_THIS, 0 },
+    { nxt_string("Math"),          NJS_TOKEN_MATH, 0 },
 
     /* Builtin functions. */
 
diff --git a/njs/njs_math.c b/njs/njs_math.c
new file mode 100644 (file)
index 0000000..7beada5
--- /dev/null
@@ -0,0 +1,554 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#include <nxt_types.h>
+#include <nxt_clang.h>
+#include <nxt_stub.h>
+#include <nxt_array.h>
+#include <nxt_lvlhsh.h>
+#include <nxt_mem_cache_pool.h>
+#include <njscript.h>
+#include <njs_vm.h>
+#include <njs_number.h>
+#include <njs_object.h>
+#include <njs_function.h>
+#include <math.h>
+
+
+static njs_ret_t
+njs_object_math_abs(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
+    njs_index_t unused)
+{
+    double  num;
+
+    if (nargs > 1) {
+        num = args[1].data.u.number;
+
+    } else {
+        num = NJS_NAN;
+    }
+
+    njs_number_set(&vm->retval, fabs(num));
+
+    return NXT_OK;
+}
+
+
+static njs_ret_t
+njs_object_math_acos(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
+    njs_index_t unused)
+{
+    double  num;
+
+    if (nargs > 1) {
+        num = args[1].data.u.number;
+
+    } else {
+        num = NJS_NAN;
+    }
+
+    njs_number_set(&vm->retval, acos(num));
+
+    return NXT_OK;
+}
+
+
+static njs_ret_t
+njs_object_math_asin(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
+    njs_index_t unused)
+{
+    double  num;
+
+    if (nargs > 1) {
+        num = args[1].data.u.number;
+
+    } else {
+        num = NJS_NAN;
+    }
+
+    njs_number_set(&vm->retval, asin(num));
+
+    return NXT_OK;
+}
+
+
+static njs_ret_t
+njs_object_math_atan(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
+    njs_index_t unused)
+{
+    double  num;
+
+    if (nargs > 1) {
+        num = args[1].data.u.number;
+
+    } else {
+        num = NJS_NAN;
+    }
+
+    njs_number_set(&vm->retval, atan(num));
+
+    return NXT_OK;
+}
+
+
+static njs_ret_t
+njs_object_math_atan2(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
+    njs_index_t unused)
+{
+    double  y, x;
+
+    y = NJS_NAN;
+    x = NJS_NAN;
+
+    if (nargs > 1) {
+        y = args[1].data.u.number;
+    }
+
+    if (nargs > 2) {
+        x = args[2].data.u.number;
+    }
+
+    njs_number_set(&vm->retval, atan2(y, x));
+
+    return NXT_OK;
+}
+
+
+static njs_ret_t
+njs_object_math_ceil(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
+    njs_index_t unused)
+{
+    double  num;
+
+    if (nargs > 1) {
+        num = args[1].data.u.number;
+
+    } else {
+        num = NJS_NAN;
+    }
+
+    njs_number_set(&vm->retval, ceil(num));
+
+    return NXT_OK;
+}
+
+
+static njs_ret_t
+njs_object_math_cos(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
+    njs_index_t unused)
+{
+    double  num;
+
+    if (nargs > 1) {
+        num = args[1].data.u.number;
+
+    } else {
+        num = NJS_NAN;
+    }
+
+    njs_number_set(&vm->retval, cos(num));
+
+    return NXT_OK;
+}
+
+
+static njs_ret_t
+njs_object_math_exp(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
+    njs_index_t unused)
+{
+    double  num;
+
+    if (nargs > 1) {
+        num = args[1].data.u.number;
+
+    } else {
+        num = NJS_NAN;
+    }
+
+    njs_number_set(&vm->retval, exp(num));
+
+    return NXT_OK;
+}
+
+
+static njs_ret_t
+njs_object_math_floor(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
+    njs_index_t unused)
+{
+    double  num;
+
+    if (nargs > 1) {
+        num = args[1].data.u.number;
+
+    } else {
+        num = NJS_NAN;
+    }
+
+    njs_number_set(&vm->retval, floor(num));
+
+    return NXT_OK;
+}
+
+
+static njs_ret_t
+njs_object_math_log(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
+    njs_index_t unused)
+{
+    double  num;
+
+    if (nargs > 1) {
+        num = args[1].data.u.number;
+
+    } else {
+        num = NJS_NAN;
+    }
+
+    njs_number_set(&vm->retval, log(num));
+
+    return NXT_OK;
+}
+
+
+static njs_ret_t
+njs_object_math_max(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
+    njs_index_t unused)
+{
+    double      num;
+    nxt_uint_t  i;
+
+    if (nargs > 1) {
+        for (i = 1; i < nargs; i++) {
+            if (!njs_is_numeric(&args[i])) {
+                vm->frame->trap_scratch.data.u.value = &args[i];
+                return NJS_TRAP_NUMBER_ARG;
+            }
+        }
+
+        num = args[1].data.u.number;
+
+        for (i = 2; i < nargs; i++) {
+            num = fmax(num, args[i].data.u.number);
+        }
+
+    } else {
+        num = -NJS_INFINITY;
+    }
+
+    njs_number_set(&vm->retval, num);
+
+    return NXT_OK;
+}
+
+
+static njs_ret_t
+njs_object_math_min(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
+    njs_index_t unused)
+{
+    double      num;
+    nxt_uint_t  i;
+
+    if (nargs > 1) {
+        for (i = 1; i < nargs; i++) {
+            if (!njs_is_numeric(&args[i])) {
+                vm->frame->trap_scratch.data.u.value = &args[i];
+                return NJS_TRAP_NUMBER_ARG;
+            }
+        }
+
+        num = args[1].data.u.number;
+
+        for (i = 2; i < nargs; i++) {
+            num = fmin(num, args[i].data.u.number);
+        }
+
+    } else {
+        num = NJS_INFINITY;
+    }
+
+    njs_number_set(&vm->retval, num);
+
+    return NXT_OK;
+}
+
+
+static njs_ret_t
+njs_object_math_pow(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
+    njs_index_t unused)
+{
+    double  base, exponent;
+
+    base = NJS_NAN;
+    exponent = NJS_NAN;
+
+    if (nargs > 1) {
+        base = args[1].data.u.number;
+    }
+
+    if (nargs > 2) {
+        exponent = args[2].data.u.number;
+    }
+
+    njs_number_set(&vm->retval, pow(base, exponent));
+
+    return NXT_OK;
+}
+
+
+static njs_ret_t
+njs_object_math_round(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
+    njs_index_t unused)
+{
+    double  num;
+
+    if (nargs > 1) {
+        num = args[1].data.u.number;
+
+    } else {
+        num = NJS_NAN;
+    }
+
+    njs_number_set(&vm->retval, round(num));
+
+    return NXT_OK;
+}
+
+
+static njs_ret_t
+njs_object_math_sin(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
+    njs_index_t unused)
+{
+    double  num;
+
+    if (nargs > 1) {
+        num = args[1].data.u.number;
+
+    } else {
+        num = NJS_NAN;
+    }
+
+    njs_number_set(&vm->retval, sin(num));
+
+    return NXT_OK;
+}
+
+
+static njs_ret_t
+njs_object_math_sqrt(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
+    njs_index_t unused)
+{
+    double  num;
+
+    if (nargs > 1) {
+        num = args[1].data.u.number;
+
+    } else {
+        num = NJS_NAN;
+    }
+
+    njs_number_set(&vm->retval, sqrt(num));
+
+    return NXT_OK;
+}
+
+
+static njs_ret_t
+njs_object_math_tan(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
+    njs_index_t unused)
+{
+    double  num;
+
+    if (nargs > 1) {
+        num = args[1].data.u.number;
+
+    } else {
+        num = NJS_NAN;
+    }
+
+    njs_number_set(&vm->retval, tan(num));
+
+    return NXT_OK;
+}
+
+
+static const njs_object_prop_t  njs_math_object_properties[] =
+{
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("E"),
+        .value = njs_value(NJS_NUMBER, 1, 2.718281828459045),
+    },
+
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("LN10"),
+        .value = njs_value(NJS_NUMBER, 1, 2.302585092994046),
+    },
+
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("LN2"),
+        .value = njs_value(NJS_NUMBER, 1, 0.6931471805599453),
+    },
+
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("LOG10E"),
+        .value = njs_value(NJS_NUMBER, 1, 0.4342944819032518),
+    },
+
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("LOG2E"),
+        .value = njs_value(NJS_NUMBER, 1, 1.4426950408889634),
+    },
+
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("PI"),
+        .value = njs_value(NJS_NUMBER, 1, 3.141592653589793),
+    },
+
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("SQRT1_2"),
+        .value = njs_value(NJS_NUMBER, 1, 0.7071067811865476),
+    },
+
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("SQRT2"),
+        .value = njs_value(NJS_NUMBER, 1, 1.4142135623730951),
+    },
+
+    {
+        .type = NJS_NATIVE_GETTER,
+        .name = njs_string("__proto__"),
+        .value = njs_native_getter(njs_object_prototype_get_proto),
+    },
+
+    {
+        .type = NJS_METHOD,
+        .name = njs_string("abs"),
+        .value = njs_native_function(njs_object_math_abs, 0,
+                     NJS_SKIP_ARG, NJS_NUMBER_ARG),
+    },
+
+    {
+        .type = NJS_METHOD,
+        .name = njs_string("acos"),
+        .value = njs_native_function(njs_object_math_acos, 0,
+                     NJS_SKIP_ARG, NJS_NUMBER_ARG),
+    },
+
+    {
+        .type = NJS_METHOD,
+        .name = njs_string("asin"),
+        .value = njs_native_function(njs_object_math_asin, 0,
+                     NJS_SKIP_ARG, NJS_NUMBER_ARG),
+    },
+
+    {
+        .type = NJS_METHOD,
+        .name = njs_string("atan"),
+        .value = njs_native_function(njs_object_math_atan, 0,
+                     NJS_SKIP_ARG, NJS_NUMBER_ARG),
+    },
+
+    {
+        .type = NJS_METHOD,
+        .name = njs_string("atan2"),
+        .value = njs_native_function(njs_object_math_atan2, 0,
+                     NJS_SKIP_ARG, NJS_NUMBER_ARG, NJS_NUMBER_ARG),
+    },
+
+    {
+        .type = NJS_METHOD,
+        .name = njs_string("ceil"),
+        .value = njs_native_function(njs_object_math_ceil, 0,
+                     NJS_SKIP_ARG, NJS_NUMBER_ARG),
+    },
+
+    {
+        .type = NJS_METHOD,
+        .name = njs_string("cos"),
+        .value = njs_native_function(njs_object_math_cos, 0,
+                     NJS_SKIP_ARG, NJS_NUMBER_ARG),
+    },
+
+    {
+        .type = NJS_METHOD,
+        .name = njs_string("exp"),
+        .value = njs_native_function(njs_object_math_exp, 0,
+                     NJS_SKIP_ARG, NJS_NUMBER_ARG),
+    },
+
+    {
+        .type = NJS_METHOD,
+        .name = njs_string("floor"),
+        .value = njs_native_function(njs_object_math_floor, 0,
+                     NJS_SKIP_ARG, NJS_NUMBER_ARG),
+    },
+
+    {
+        .type = NJS_METHOD,
+        .name = njs_string("log"),
+        .value = njs_native_function(njs_object_math_log, 0,
+                     NJS_SKIP_ARG, NJS_NUMBER_ARG),
+    },
+
+    {
+        .type = NJS_METHOD,
+        .name = njs_string("max"),
+        .value = njs_native_function(njs_object_math_max, 0, 0),
+    },
+
+    {
+        .type = NJS_METHOD,
+        .name = njs_string("min"),
+        .value = njs_native_function(njs_object_math_min, 0, 0),
+    },
+
+    {
+        .type = NJS_METHOD,
+        .name = njs_string("pow"),
+        .value = njs_native_function(njs_object_math_pow, 0,
+                     NJS_SKIP_ARG, NJS_NUMBER_ARG, NJS_NUMBER_ARG),
+    },
+
+    {
+        .type = NJS_METHOD,
+        .name = njs_string("round"),
+        .value = njs_native_function(njs_object_math_round, 0,
+                     NJS_SKIP_ARG, NJS_NUMBER_ARG),
+    },
+
+    {
+        .type = NJS_METHOD,
+        .name = njs_string("sin"),
+        .value = njs_native_function(njs_object_math_sin, 0,
+                     NJS_SKIP_ARG, NJS_NUMBER_ARG),
+    },
+
+    {
+        .type = NJS_METHOD,
+        .name = njs_string("sqrt"),
+        .value = njs_native_function(njs_object_math_sqrt, 0,
+                     NJS_SKIP_ARG, NJS_NUMBER_ARG),
+    },
+
+    {
+        .type = NJS_METHOD,
+        .name = njs_string("tan"),
+        .value = njs_native_function(njs_object_math_tan, 0,
+                     NJS_SKIP_ARG, NJS_NUMBER_ARG),
+    },
+};
+
+
+const njs_object_init_t  njs_math_object_init = {
+    njs_math_object_properties,
+    nxt_nitems(njs_math_object_properties),
+};
diff --git a/njs/njs_math.h b/njs/njs_math.h
new file mode 100644 (file)
index 0000000..11f8c16
--- /dev/null
@@ -0,0 +1,17 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#ifndef _NJS_MATH_H_INCLUDED_
+#define _NJS_MATH_H_INCLUDED_
+
+
+#include <math.h>
+
+
+extern const njs_object_init_t  njs_math_object_init;
+
+
+#endif /* _NJS_MATH_H_INCLUDED_ */
index 8da185acc18ea09a5ef88c20cf21d12ebb78468f..71f312f777e66067a5738ed0b300ebad0fa2c55d 100644 (file)
@@ -34,6 +34,31 @@ njs_object_alloc(njs_vm_t *vm)
         nxt_lvlhsh_init(&object->hash);
         nxt_lvlhsh_init(&object->shared_hash);
         object->__proto__ = &vm->prototypes[NJS_PROTOTYPE_OBJECT];
+        object->shared = 0;
+    }
+
+    return object;
+}
+
+
+njs_object_t *
+njs_object_value_copy(njs_vm_t *vm, njs_value_t *value)
+{
+    njs_object_t  *object;
+
+    object = value->data.u.object;
+
+    if (!object->shared) {
+        return object;
+    }
+
+    object = nxt_mem_cache_alloc(vm->mem_cache_pool, sizeof(njs_object_t));
+
+    if (nxt_fast_path(object != NULL)) {
+        *object = *value->data.u.object;
+        object->__proto__ = &vm->prototypes[NJS_PROTOTYPE_OBJECT];
+        object->shared = 0;
+        value->data.u.object = object;
     }
 
     return object;
@@ -51,6 +76,7 @@ njs_object_value_alloc(njs_vm_t *vm, const njs_value_t *value, nxt_uint_t type)
     if (nxt_fast_path(ov != NULL)) {
         nxt_lvlhsh_init(&ov->object.hash);
         nxt_lvlhsh_init(&ov->object.shared_hash);
+        ov->object.shared = 0;
 
         index = njs_primitive_prototype_index(type);
         ov->object.__proto__ = &vm->prototypes[index];
@@ -413,7 +439,7 @@ const njs_object_init_t  njs_object_constructor_init = {
 };
 
 
-static njs_ret_t
+njs_ret_t
 njs_object_prototype_get_proto(njs_vm_t *vm, njs_value_t *value)
 {
     njs_object_t  *proto;
index 87bdc57fb86364ba6b4440f9c8a7dd3398ff733f..4ab6ed0801344e4de54282ce3e0c19fe637e196f 100644 (file)
@@ -44,6 +44,7 @@ struct njs_object_init_s {
 
 
 njs_object_t *njs_object_alloc(njs_vm_t *vm);
+njs_object_t *njs_object_value_copy(njs_vm_t *vm, njs_value_t *value);
 njs_object_t *njs_object_value_alloc(njs_vm_t *vm, const njs_value_t *value,
     nxt_uint_t type);
 njs_object_prop_t *njs_object_property(njs_vm_t *vm, njs_object_t *obj,
@@ -55,6 +56,7 @@ njs_ret_t njs_object_constructor(njs_vm_t *vm, njs_value_t *args,
 njs_object_prop_t *njs_object_prop_alloc(njs_vm_t *vm, const njs_value_t *name);
 njs_ret_t njs_primitive_prototype_get_proto(njs_vm_t *vm, njs_value_t *value);
 njs_ret_t njs_object_prototype_create(njs_vm_t *vm, njs_value_t *value);
+njs_ret_t njs_object_prototype_get_proto(njs_vm_t *vm, njs_value_t *value);
 njs_ret_t njs_object_prototype_to_string(njs_vm_t *vm, njs_value_t *args,
     nxt_uint_t nargs, njs_index_t unused);
 
index 39bf3ce05dcce570e06d4f1c9624d9e1375b1b04..636c0ef321f1cfeb922c972fcbe10a5cc688faba 100644 (file)
@@ -68,6 +68,8 @@ static njs_token_t njs_parser_throw_statement(njs_vm_t *vm,
     njs_parser_t *parser);
 static njs_token_t njs_parser_grouping_expression(njs_vm_t *vm,
     njs_parser_t *parser);
+static njs_token_t njs_parser_builtin_object(njs_vm_t *vm, njs_parser_t *parser,
+    njs_parser_node_t *node);
 static njs_token_t njs_parser_object(njs_vm_t *vm, njs_parser_t *parser,
     njs_parser_node_t *obj);
 static njs_token_t njs_parser_array(njs_vm_t *vm, njs_parser_t *parser,
@@ -1470,7 +1472,7 @@ njs_parser_terminal(njs_vm_t *vm, njs_parser_t *parser, njs_token_t token)
             break;
         }
 
-        parser->code_size += sizeof(njs_vmcode_function_copy_t);
+        parser->code_size += sizeof(njs_vmcode_object_copy_t);
         node->lvalue = NJS_LVALUE_ENABLED;
         node->u.variable = var;
         break;
@@ -1588,6 +1590,9 @@ njs_parser_terminal(njs_vm_t *vm, njs_parser_t *parser, njs_token_t token)
         node->index = NJS_INDEX_THIS;
         break;
 
+    case NJS_TOKEN_MATH:
+        return njs_parser_builtin_object(vm, parser, node);
+
     case NJS_TOKEN_OBJECT_CONSTRUCTOR:
         node->index = NJS_INDEX_OBJECT;
         break;
@@ -1631,6 +1636,36 @@ njs_parser_terminal(njs_vm_t *vm, njs_parser_t *parser, njs_token_t token)
 }
 
 
+static njs_token_t
+njs_parser_builtin_object(njs_vm_t *vm, njs_parser_t *parser,
+    njs_parser_node_t *node)
+{
+    nxt_uint_t      index, level;
+    njs_value_t     *value;
+    njs_variable_t  *var;
+
+    var = njs_parser_variable(vm, parser, &level);
+    if (nxt_slow_path(var == NULL)) {
+        return NJS_TOKEN_ERROR;
+    }
+
+    var->state = NJS_VARIABLE_DECLARED;
+    node->index = var->index;
+
+    value = njs_variable_value(parser, node->index);
+
+    index = node->token - NJS_TOKEN_FIRST_OBJECT;
+    value->data.u.object = &vm->shared->objects[index];
+    value->type = NJS_OBJECT;
+    value->data.truth = 1;
+
+    parser->node = node;
+    parser->code_size += sizeof(njs_vmcode_object_copy_t);
+
+    return njs_lexer_token(parser->lexer);
+}
+
+
 static njs_token_t
 njs_parser_object(njs_vm_t *vm, njs_parser_t *parser, njs_parser_node_t *obj)
 {
index a0a56837f2b53e2a4a25951f18c1adc3430bf476..cd16e335cf8e0001b58573c28fd5d544e36a0bae 100644 (file)
@@ -159,6 +159,10 @@ typedef enum {
 
     NJS_TOKEN_THIS,
 
+#define NJS_TOKEN_FIRST_OBJECT     NJS_TOKEN_MATH
+
+    NJS_TOKEN_MATH,
+
     NJS_TOKEN_OBJECT_CONSTRUCTOR,
     NJS_TOKEN_ARRAY_CONSTRUCTOR,
     NJS_TOKEN_FUNCTION_CONSTRUCTOR,
index cfc8114617e5e60d3b0b277f6488b5b8c765e542..608a2f4564c1f260c549b35f85ab28c01c575c0f 100644 (file)
@@ -353,6 +353,7 @@ njs_regexp_alloc(njs_vm_t *vm, njs_regexp_pattern_t *pattern)
         nxt_lvlhsh_init(&regexp->object.hash);
         nxt_lvlhsh_init(&regexp->object.shared_hash);
         regexp->object.__proto__ = &vm->prototypes[NJS_PROTOTYPE_REGEXP];
+        regexp->object.shared = 0;
         regexp->last_index = 0;
         regexp->pattern = pattern;
     }
index d9fc903ae85e63fd1a2892a53c02b58166d68bca..9d1df6df41ad5e80a0670a2535264422fb0df4e6 100644 (file)
@@ -416,34 +416,6 @@ njs_vmcode_function(njs_vm_t *vm, njs_value_t *invld1, njs_value_t *invld2)
 }
 
 
-njs_ret_t
-njs_vmcode_function_copy(njs_vm_t *vm, njs_value_t *value, njs_value_t *invld)
-{
-    njs_function_t  *function;
-
-    if (njs_is_function(value)) {
-
-        function = njs_function_value_copy(vm, value);
-
-        if (nxt_fast_path(function != NULL)) {
-            vm->retval.data.u.function = function;
-            vm->retval.type = NJS_FUNCTION;
-            vm->retval.data.truth = 1;
-
-            return sizeof(njs_vmcode_function_copy_t);
-        }
-
-        return NXT_ERROR;
-    }
-
-    vm->retval = *value;
-
-    njs_retain(value);
-
-    return sizeof(njs_vmcode_function_copy_t);
-}
-
-
 njs_ret_t
 njs_vmcode_regexp(njs_vm_t *vm, njs_value_t *invld1, njs_value_t *invld2)
 {
@@ -466,6 +438,42 @@ njs_vmcode_regexp(njs_vm_t *vm, njs_value_t *invld1, njs_value_t *invld2)
 }
 
 
+njs_ret_t
+njs_vmcode_object_copy(njs_vm_t *vm, njs_value_t *value, njs_value_t *invld)
+{
+    njs_object_t    *object;
+    njs_function_t  *function;
+
+    switch (value->type) {
+
+    case NJS_OBJECT:
+        object = njs_object_value_copy(vm, value);
+        if (nxt_slow_path(object == NULL)) {
+            return NXT_ERROR;
+        }
+
+        break;
+
+    case NJS_FUNCTION:
+        function = njs_function_value_copy(vm, value);
+        if (nxt_slow_path(function == NULL)) {
+            return NXT_ERROR;
+        }
+
+        break;
+
+    default:
+        break;
+    }
+
+    vm->retval = *value;
+
+    njs_retain(value);
+
+    return sizeof(njs_vmcode_object_copy_t);
+}
+
+
 njs_ret_t
 njs_vmcode_property_get(njs_vm_t *vm, njs_value_t *object,
     njs_value_t *property)
index b754cfb1324dcc0587f386cebe02af160259d0db..19a822aa90e46e4cbf88994cc54af68780c9c038 100644 (file)
@@ -128,6 +128,8 @@ struct njs_object_s {
 
     /* An object __proto__. */
     njs_object_t                      *__proto__;
+
+    uint32_t                          shared;  /* 1 bit */
 };
 
 
@@ -148,11 +150,9 @@ struct njs_function_s {
 #if (NXT_64BIT)
     uint8_t                           native;
     uint8_t                           continuation_size;
-    uint8_t                           shared;
 #else
     uint8_t                           native;
     uint8_t                           continuation_size;
-    uint8_t                           shared;
 #endif
 
     union {
@@ -278,11 +278,11 @@ union njs_value_s {
         .type = NJS_FUNCTION,                                                 \
         .truth = 1,                                                           \
         .u.function = & (njs_function_t) {                                    \
+            .object.shared = 1,                                               \
             .native = 1,                                                      \
             .continuation_size = _size,                                       \
             .args_types = { __VA_ARGS__ },                                    \
             .args_offset = 1,                                                 \
-            .shared = 1,                                                      \
             .u.native = _function,                                            \
         }                                                                     \
     }                                                                         \
@@ -482,15 +482,15 @@ typedef struct {
 typedef struct {
     njs_vmcode_t               code;
     njs_index_t                retval;
-    njs_index_t                function;
-} njs_vmcode_function_copy_t;
+    njs_regexp_pattern_t       *pattern;
+} njs_vmcode_regexp_t;
 
 
 typedef struct {
     njs_vmcode_t               code;
     njs_index_t                retval;
-    njs_regexp_pattern_t       *pattern;
-} njs_vmcode_regexp_t;
+    njs_index_t                object;
+} njs_vmcode_object_copy_t;
 
 
 typedef struct {
@@ -686,6 +686,12 @@ enum njs_functions_e {
 };
 
 
+enum njs_object_e {
+    NJS_OBJECT_MATH =        0,
+#define NJS_OBJECT_MAX       (NJS_OBJECT_MATH + 1)
+};
+
+
 #define njs_scope_index(value)                                                \
     ((njs_index_t) ((value) << NJS_SCOPE_SHIFT))
 
@@ -782,6 +788,8 @@ struct njs_vm_shared_s {
     nxt_lvlhsh_t             values_hash;
     nxt_lvlhsh_t             null_proto_hash;
 
+    njs_object_t             objects[NJS_OBJECT_MAX];
+
     /*
      * The prototypes and functions arrays must be togther because they are
      * copied to njs_vm_t by single memcpy() in njs_builtin_objects_clone().
@@ -802,10 +810,10 @@ njs_ret_t njs_vmcode_array(njs_vm_t *vm, njs_value_t *inlvd1,
     njs_value_t *inlvd2);
 njs_ret_t njs_vmcode_function(njs_vm_t *vm, njs_value_t *inlvd1,
     njs_value_t *invld2);
-njs_ret_t njs_vmcode_function_copy(njs_vm_t *vm, njs_value_t *value,
-    njs_value_t *invld);
 njs_ret_t njs_vmcode_regexp(njs_vm_t *vm, njs_value_t *inlvd1,
     njs_value_t *invld2);
+njs_ret_t njs_vmcode_object_copy(njs_vm_t *vm, njs_value_t *value,
+    njs_value_t *invld);
 
 njs_ret_t njs_vmcode_property_get(njs_vm_t *vm, njs_value_t *object,
     njs_value_t *property);
index b83395fb09eaee56f79fffd6275f868198610418..2ac4b13374e2f71e34646696171378676ad1b33c 100644 (file)
@@ -3914,6 +3914,68 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("eval()"),
       nxt_string("") },
 
+    /* Math. */
+
+    { nxt_string("Math.PI"),
+      nxt_string("3.14159") },
+
+    { nxt_string("Math.E"),
+      nxt_string("2.71828") },
+
+    { nxt_string("Math.SQRT2"),
+      nxt_string("1.41421") },
+
+    { nxt_string("Math.abs(5)"),
+      nxt_string("5") },
+
+    { nxt_string("Math.abs(-5)"),
+      nxt_string("5") },
+
+    { nxt_string("Math.abs('5.0')"),
+      nxt_string("5") },
+
+    { nxt_string("Math.abs('abc')"),
+      nxt_string("NaN") },
+
+    { nxt_string("Math.max()"),
+      nxt_string("-Infinity") },
+
+    { nxt_string("Math.max(null)"),
+      nxt_string("0") },
+
+    { nxt_string("Math.max(undefined)"),
+      nxt_string("NaN") },
+
+    { nxt_string("Math.max('1', '2', '5')"),
+      nxt_string("5") },
+
+    { nxt_string("Math.min()"),
+      nxt_string("Infinity") },
+
+    { nxt_string("Math.min(null)"),
+      nxt_string("0") },
+
+    { nxt_string("Math.min(undefined)"),
+      nxt_string("NaN") },
+
+    { nxt_string("Math.min('1', '2', '5')"),
+      nxt_string("1") },
+
+    { nxt_string("Math.pow(2, 5)"),
+      nxt_string("32") },
+
+    { nxt_string("Math.pow(2)"),
+      nxt_string("NaN") },
+
+    { nxt_string("Math.pow()"),
+      nxt_string("NaN") },
+
+    /* ES5: Must be "[object Math]". */
+    { nxt_string("Math"),
+      nxt_string("[object Object]") },
+
+    /* External interface. */
+
     { nxt_string("function f(req) { return req.uri }"),
       nxt_string("АБВ") },