From e3cf95b05837fe53abc737258f99f316846546ee Mon Sep 17 00:00:00 2001 From: playX Date: Wed, 28 Jul 2021 13:01:00 +0300 Subject: [PATCH] js: add more tests for array, support array insert_many, minor fixes for references (#10983) --- vlib/builtin/js/array.js.v | 30 ++++ vlib/v/gen/js/builtin_types.v | 17 +++ vlib/v/gen/js/fast_deep_equal.js | 5 +- vlib/v/gen/js/js.v | 47 ++++++- vlib/v/gen/js/temp_fast_deep_equal.v | 3 +- vlib/v/gen/js/tests/testdata/array.out | 64 +++++++++ vlib/v/gen/js/tests/testdata/array.v | 186 +++++++++++++++++++++++++ 7 files changed, 342 insertions(+), 10 deletions(-) create mode 100644 vlib/v/gen/js/tests/testdata/array.out create mode 100644 vlib/v/gen/js/tests/testdata/array.v diff --git a/vlib/builtin/js/array.js.v b/vlib/builtin/js/array.js.v index 20f21ff4bd..4a342d5272 100644 --- a/vlib/builtin/js/array.js.v +++ b/vlib/builtin/js/array.js.v @@ -4,6 +4,7 @@ struct array { arr JS.Array pub: len int + cap int } #function flatIntoArray(target, source, sourceLength, targetIndex, depth) { @@ -50,6 +51,14 @@ pub fn (a array) repeat_to_depth(count int, depth int) array { return arr } +// last returns the last element of the array. +pub fn (a array) last() voidptr { + mut res := voidptr(0) + #res = a.arr[a.len-1]; + + return res +} + fn (a array) get(ix int) voidptr { mut result := voidptr(0) #result = a.arr[ix] @@ -104,6 +113,10 @@ pub fn (mut a array) insert(i int, val voidptr) { #a.arr.splice(i,0,val) } +pub fn (mut a array) insert_many(i int, val voidptr, size int) { + #a.arr.splice(i,0,...val.slice(0,+size)) +} + pub fn (mut a array) join(separator string) string { mut res := '' #res = new builtin.string(a.arr.join(separator +'')); @@ -115,7 +128,24 @@ fn (a array) push(val voidptr) { #a.arr.push(val) } +pub fn (a array) str() string { + mut res := '' + #res = new builtin.string(a + '') + + return res +} + #array.prototype[Symbol.iterator] = function () { return this.arr[Symbol.iterator](); } #array.prototype.entries = function () { return this.arr.entries(); } #array.prototype.map = function(callback) { return this.arr.map(callback); } #array.prototype.filter = function(callback) { return this.arr.filter(callback); } +#Object.defineProperty(array.prototype,'cap',{ get: function () { return this.len; } }) +// delete deletes array element at index `i`. +pub fn (mut a array) delete(i int) { + a.delete_many(i, 1) +} + +// delete_many deletes `size` elements beginning with index `i` +pub fn (mut a array) delete_many(i int, size int) { + #a.arr.splice(i.valueOf(),size.valueOf()) +} diff --git a/vlib/v/gen/js/builtin_types.v b/vlib/v/gen/js/builtin_types.v index 26ac15d5d1..87b9ab77c7 100644 --- a/vlib/v/gen/js/builtin_types.v +++ b/vlib/v/gen/js/builtin_types.v @@ -38,6 +38,9 @@ fn (mut g JsGen) to_js_typ_val(t ast.Type) string { .struct_ { styp = 'new ${g.js_name(sym.name)}(${g.to_js_typ_def_val(sym.name)})' } + .voidptr { + styp = 'null' + } else { // TODO styp = 'undefined' @@ -100,6 +103,9 @@ fn (mut g JsGen) sym_to_js_typ(sym ast.TypeSymbol) string { .array { styp = 'array' } + .voidptr { + styp = 'any' + } else { // TODO styp = 'undefined' @@ -358,6 +364,17 @@ fn (mut g JsGen) gen_builtin_type_defs() { eq: 'vEq(this, other)' ) } + 'any' { + g.gen_builtin_prototype( + typ_name: typ_name + val_name: 'any' + default_value: 'null' + constructor: 'this.val = any' + value_of: 'this.val' + to_string: '"&" + this.val' + eq: 'this == other' // compare by ptr + ) + } else {} } } diff --git a/vlib/v/gen/js/fast_deep_equal.js b/vlib/v/gen/js/fast_deep_equal.js index 4a0296cb9a..9022d08444 100644 --- a/vlib/v/gen/js/fast_deep_equal.js +++ b/vlib/v/gen/js/fast_deep_equal.js @@ -5,7 +5,8 @@ function vEq(a, b) { if (a && b && typeof a == 'object' && typeof b == 'object') { if (a.constructor !== b.constructor) return false; - + a = a.valueOf(); + b = b.valueOf(); var length, i, keys; if (Array.isArray(a)) { length = a.length; @@ -62,5 +63,5 @@ function vEq(a, b) { } // true if both NaN, false otherwise - return a!==a && b!==b; + return a !== a && b !== b; }; \ No newline at end of file diff --git a/vlib/v/gen/js/js.v b/vlib/v/gen/js/js.v index 574c0ce1bd..d6b029767a 100644 --- a/vlib/v/gen/js/js.v +++ b/vlib/v/gen/js/js.v @@ -25,7 +25,7 @@ const ( 'Array', 'Map'] // used to generate type structs v_types = ['i8', 'i16', 'int', 'i64', 'byte', 'u16', 'u32', 'u64', 'f32', 'f64', - 'int_literal', 'float_literal', 'size_t', 'bool', 'string', 'map', 'array'] + 'int_literal', 'float_literal', 'size_t', 'bool', 'string', 'map', 'array', 'any'] shallow_equatables = [ast.Kind.i8, .i16, .int, .i64, .byte, .u16, .u32, .u64, .f32, .f64, .int_literal, .float_literal, .size_t, .bool, .string] ) @@ -682,9 +682,7 @@ fn (mut g JsGen) expr(node ast.Expr) { if node.op in [.amp, .mul] { // C pointers/references: ignore them if node.op == .amp { - type_sym := g.table.get_type_symbol(node.right_type) - - if !type_sym.is_primitive() && !node.right_type.is_pointer() { + if !node.right_type.is_pointer() { // kind of weird way to handle references but it allows us to access type methods easily. g.write('(function(x) {') g.write(' return { val: x, __proto__: Object.getPrototypeOf(x), valueOf: function() { return this.val; } }})( ') @@ -696,12 +694,14 @@ fn (mut g JsGen) expr(node ast.Expr) { } else { g.write('(') g.expr(node.right) - g.write(').val') + g.write(').valueOf()') } } else { g.write(node.op.str()) + g.write('(') g.expr(node.right) g.write('.valueOf()') + g.write(')') } } ast.RangeExpr { @@ -1103,7 +1103,7 @@ fn (mut g JsGen) gen_for_in_stmt(it ast.ForInStmt) { g.expr(it.cond) g.write('; $i < ') g.expr(it.high) - g.writeln('; ++$i) {') + g.writeln('; $i = new builtin.int($i + 1)) {') g.inside_loop = false g.stmts(it.stmts) g.writeln('}') @@ -1379,6 +1379,7 @@ fn (mut g JsGen) gen_call_expr(it ast.CallExpr) { g.write('return builtin.unwrap(') } g.expr(it.left) + if it.is_method { // foo.bar.baz() sym := g.table.get_type_symbol(it.receiver_type) g.write('.') @@ -1410,11 +1411,42 @@ fn (mut g JsGen) gen_call_expr(it ast.CallExpr) { } else {} } + g.write('it => ') g.expr(node.args[0].expr) g.write(')') return } + + left_sym := g.table.get_type_symbol(it.left_type) + if left_sym.kind == .array { + node := it + match node.name { + 'insert' { + arg2_sym := g.table.get_type_symbol(node.args[1].typ) + is_arg2_array := arg2_sym.kind == .array && node.args[1].typ == node.left_type + if is_arg2_array { + g.write('insert_many(') + } else { + g.write('insert(') + } + + g.expr(node.args[0].expr) + g.write(',') + if is_arg2_array { + g.expr(node.args[1].expr) + g.write('.arr,') + g.expr(node.args[1].expr) + g.write('.len') + } else { + g.expr(node.args[1].expr) + } + g.write(')') + return + } + else {} + } + } } else { if name in g.builtin_fns { g.write('builtin.') @@ -1647,8 +1679,9 @@ fn (mut g JsGen) gen_infix_expr(it ast.InfixExpr) { g.write('Array.prototype.push.call(') g.expr(it.left) g.write('.arr,') + array_info := l_sym.info as ast.Array // arr << [1, 2] - if r_sym.kind == .array { + if r_sym.kind == .array && array_info.elem_type != it.right_type { g.write('...') } g.expr(it.right) diff --git a/vlib/v/gen/js/temp_fast_deep_equal.v b/vlib/v/gen/js/temp_fast_deep_equal.v index 553fc3d10f..dac4f24cda 100644 --- a/vlib/v/gen/js/temp_fast_deep_equal.v +++ b/vlib/v/gen/js/temp_fast_deep_equal.v @@ -9,7 +9,8 @@ function vEq(a, b) { if (a && b && typeof a == 'object' && typeof b == 'object') { if (a.constructor !== b.constructor) return false; - + a = a.valueOf(); + b = b.valueOf(); var length, i, keys; if (Array.isArray(a)) { length = a.length; diff --git a/vlib/v/gen/js/tests/testdata/array.out b/vlib/v/gen/js/tests/testdata/array.out new file mode 100644 index 0000000000..134a2cfeb3 --- /dev/null +++ b/vlib/v/gen/js/tests/testdata/array.out @@ -0,0 +1,64 @@ +3 +1 +2 +4 +255 +256 +2 +4 +131 +4 +1 +2 +3 +5 +4 +4 +[1,5,2,3,4] +5 +4 +5 +[1,5,2,3,4] +[5,2,3,4] +4 +[5,3,4] +3 +[5,3] +2 +[2.5,3.25,4.5,5.75] +true +true +true +true +true +true +true +true +true +true +true +1 +2 +3 +10000 +234 +0 +1 +0 +1 +3 +[1,3] +3 +2 +3 +4 +1 +4 +5 +2 +5 +0 +1 +1.1 +[1,2,3,4] +[1,5,6,2,3,4] \ No newline at end of file diff --git a/vlib/v/gen/js/tests/testdata/array.v b/vlib/v/gen/js/tests/testdata/array.v new file mode 100644 index 0000000000..bc23e9264f --- /dev/null +++ b/vlib/v/gen/js/tests/testdata/array.v @@ -0,0 +1,186 @@ +struct Chunk { + val string +} + +struct Kkk { + q []Chunk +} + +fn main() { + { + // test pointer + mut arr := []&int{} + a := 1 + b := 2 + c := 3 + arr << &a + arr << &b + arr << &c + assert *arr[0] == 1 + arr[1] = &c + assert *arr[1] == 3 + mut d_arr := [arr] // [][]&int + d_arr << arr + println(*d_arr[0][1]) // 3 + println(*d_arr[1][0]) // 1 + } + { + // test assign + mut arr := [2, 4, 8, 16, 32, 64, 128] + arr[0] = 2 + arr[1] &= 255 + arr[2] |= 255 + arr[3] <<= 4 + arr[4] >>= 4 + arr[5] %= 5 + arr[6] ^= 3 + println(arr[0]) + println(arr[1]) + println(arr[2]) + println(arr[3]) + println(arr[4]) + println(arr[5]) + println(arr[6]) + } + { + // test ints + mut a := [1, 5, 2, 3] + println(a.len) // 4 + println(a[0]) + println(a[2]) + println(a.last()) + + a << 4 + println(a.len) + println(a[4]) + println(a.last()) + + s := a.str() + println(s) + println(a[1]) + println(a.last()) + } + { + // test deleting + mut a := [1, 5, 2, 3, 4] + + println(a.len) + println(a.str()) + + a.delete(0) + + println(a.str()) + println(a.len) // 4 + + a.delete(1) + + println(a.str()) + println(a.len) + a.delete(a.len - 1) + + println(a.str()) + println(a.len) + } + { + // test slice delete + mut a := [1.5, 2.5, 3.25, 4.5, 5.75] + b := a[2..4] + a.delete(0) + // assert a == [2.5, 3.25, 4.5, 5.75] + // assert b == [3.25, 4.5] + println(a) + println(a == [2.5, 3.25, 4.5, 5.75]) + println(b == [3.25, 4.5]) + a = [3.75, 4.25, -1.5, 2.25, 6.0] + c := a[..3] + a.delete(2) + println(a == [3.75, 4.25, 2.25, 6.0]) + println(c == [3.75, 4.25, -1.5]) + } + { + // test delete many + mut a := [1, 2, 3, 4, 5, 6, 7, 8, 9] + b := a[2..6] + a.delete_many(4, 3) + println(a == [1, 2, 3, 4, 8, 9]) + println(b == [3, 4, 5, 6]) + + c := a[..a.len] + a.delete_many(2, 0) // this should just clone + a[1] = 17 + + println(a == [1, 17, 3, 4, 8, 9]) + println(c == [1, 2, 3, 4, 8, 9]) + a.delete_many(0, a.len) + println(a == []int{}) + } + { + // test short + a := [1, 2, 3] + println(a.len == 3) + println(a.cap == 3) + println(a[0]) + println(a[1]) + println(a[2]) + } + { + // test large + mut a := [0].repeat(0) + for i in 0 .. 10000 { + a << i + } + println(a.len) + println(a[234]) + } + { + // test empty + mut chunks := []Chunk{} + a := Chunk{} + println(chunks.len) + chunks << a + println(chunks.len) + chunks = [] + println(chunks.len) + chunks << a + println(chunks.len) + } + { + // test push + mut a := []int{} + a << 1 + a << 3 + println(a[1]) + println(a.str()) + } + { + // test insert + mut a := [1, 2] + a.insert(0, 3) + println(a[0]) + println(a[2]) + println(a.len) + a.insert(1, 4) + println(a[1]) + println(a[2]) + println(a.len) + a.insert(4, 5) + println(a[4]) + println(a[3]) + println(a.len) + mut b := []f64{} + println(b.len) + b.insert(0, f64(1.1)) + println(b.len) + println(b[0]) + } + { + // test insert many + mut a := [3, 4] + a.insert(0, [1, 2]) + println(a) + + b := [5, 6] + a.insert(1, b) + println(a) + } +}