diff --git a/vlib/v/gen/js/auto_str_methods.v b/vlib/v/gen/js/auto_str_methods.v index 0db5073b3e..157da30d0d 100644 --- a/vlib/v/gen/js/auto_str_methods.v +++ b/vlib/v/gen/js/auto_str_methods.v @@ -221,7 +221,7 @@ fn (mut g JsGen) gen_str_for_option(typ ast.Type, styp string, str_fn_name strin g.definitions.writeln('function ${str_fn_name}(it) { return indent_${str_fn_name}(it, 0); }') g.definitions.writeln('function indent_${str_fn_name}(it, indent_count) {') g.definitions.writeln('\tlet res;') - g.definitions.writeln('\tif (it.state == 0) {') + g.definitions.writeln('\tif (it.state.val == 0) {') if sym.kind == .string { tmp_res := '${parent_str_fn_name}(it.data)' g.definitions.writeln('\t\tres = ${str_intp_sq(tmp_res)};') @@ -281,7 +281,7 @@ fn (mut g JsGen) gen_str_for_multi_return(info ast.MultiReturn, styp string, str } else { deref, deref_label := deref_kind(str_method_expects_ptr, is_arg_ptr, typ) fn_builder.writeln('\t\tstrings__Builder_write_string(sb, new string("$deref_label"));') - fn_builder.writeln('\tstrings__Builder_write_string(sb, ${arg_str_fn_name}( $deref a[$i]));') + fn_builder.writeln('\tstrings__Builder_write_string(sb, ${arg_str_fn_name}( a[$i] $deref ));') } if i != info.types.len - 1 { fn_builder.writeln('\tstrings__Builder_write_string(sb, new string(", "));') @@ -385,6 +385,26 @@ fn (mut g JsGen) gen_str_for_interface(info ast.Interface, styp string, str_fn_n } fn (mut g JsGen) gen_str_for_union_sum_type(info ast.SumType, styp string, str_fn_name string) { + g.definitions.writeln('function ${str_fn_name}(x) { return indent_${str_fn_name}(x, 0); }') + mut fn_builder := strings.new_builder(512) + fn_builder.writeln('function indent_${str_fn_name}(x, indent_count) {') + for typ in info.variants { + typ_str := g.typ(typ) + mut func_name := g.get_str_fn(typ) + sym := g.table.get_type_symbol(typ) + sym_has_str_method, str_method_expects_ptr, _ := sym.str_method_info() + deref := if sym_has_str_method && str_method_expects_ptr { + ' ' + } else { + if typ.is_ptr() { '.valueOf()' } else { ' ' } + } + if should_use_indent_func(sym.kind) && !sym_has_str_method { + func_name = 'indent_$func_name' + } + fn_builder.writeln('if (x instanceof $typ_str) { return ${func_name}(x$deref); }') + } + fn_builder.writeln('builtin__panic(new string("unknown sum type value"));\n}') + g.definitions.writeln(fn_builder.str()) } fn (mut g JsGen) fn_decl_str(info ast.FnType) string { diff --git a/vlib/v/gen/js/builtin_types.v b/vlib/v/gen/js/builtin_types.v index d24cdfff48..337a32a78b 100644 --- a/vlib/v/gen/js/builtin_types.v +++ b/vlib/v/gen/js/builtin_types.v @@ -14,6 +14,13 @@ fn (mut g JsGen) to_js_typ_def_val(s string) string { return dval } +fn (mut g JsGen) copy_val(t ast.Type, tmp string) string { + fun := g.get_copy_fn(t) + temp := g.new_tmp_var() + g.writeln('let $temp = ${fun}($tmp);') + return temp +} + fn (mut g JsGen) to_js_typ_val(t ast.Type) string { sym := g.table.get_type_symbol(t) mut styp := '' diff --git a/vlib/v/gen/js/deep_copy.v b/vlib/v/gen/js/deep_copy.v new file mode 100644 index 0000000000..0613f4cf25 --- /dev/null +++ b/vlib/v/gen/js/deep_copy.v @@ -0,0 +1,227 @@ +module js + +import v.ast +import strings +import v.util + +[inline] +fn styp_to_copy_fn_name(styp string) string { + return styp.replace_each(['*', '', '.', '__', ' ', '__']) + '_\$copy' +} + +fn (mut g JsGen) get_copy_fn(typ ast.Type) string { + mut unwrapped := g.unwrap_generic(typ).set_nr_muls(0).clear_flag(.variadic) + if g.pref.nofloat { + if typ == ast.f32_type { + unwrapped = ast.u32_type + } else if typ == ast.f64_type { + unwrapped = ast.u64_type + } + } + if typ.has_flag(.optional) { + unwrapped.set_flag(.optional) + } + styp := g.typ(unwrapped) + mut sym := g.table.get_type_symbol(unwrapped) + mut copy_fn_name := styp_to_copy_fn_name(styp) + if mut sym.info is ast.Alias { + if sym.info.is_import { + sym = g.table.get_type_symbol(sym.info.parent_type) + copy_fn_name = styp_to_copy_fn_name(sym.name) + } + } + g.copy_types << StrType{ + typ: unwrapped + styp: styp + } + return copy_fn_name +} + +fn (mut g JsGen) gen_copy_for_option(typ ast.Type, styp string, copy_fn_name string) { + g.definitions.writeln('function ${copy_fn_name}(it) { return it; }') +} + +fn (mut g JsGen) gen_copy_for_alias(info ast.Alias, styp string, copy_fn_name string) { + parent_copy_fn_name := g.get_str_fn(info.parent_type) + + g.definitions.writeln('function ${copy_fn_name}(it) {') + g.definitions.writeln('\tlet res = ${parent_copy_fn_name}(it);') + g.definitions.writeln('\treturn res;') + g.definitions.writeln('}') +} + +fn (mut g JsGen) gen_copy_for_multi_return(info ast.MultiReturn, styp string, copy_fn_name string) { + mut fn_builder := strings.new_builder(512) + fn_builder.writeln('function ${copy_fn_name}(a) {') + fn_builder.writeln('\tlet arr = []') + for i, typ in info.types { + sym := g.table.get_type_symbol(typ) + arg_copy_fn_name := g.get_copy_fn(typ) + + if sym.kind in [.f32, .f64] { + if sym.kind == .f32 { + fn_builder.writeln('\tarr.push(new f32(a[$i].val));') + } else { + fn_builder.writeln('\tarr.push(new f64(a[$i].val));') + } + } else if sym.kind == .string { + fn_builder.writeln('\tarr.push(new string(a[$i].str +""));') + } else if sym.kind == .function { + fn_builder.writeln('\tarr.push(a[$i]);') + } else { + fn_builder.writeln('\tarr.push(${arg_copy_fn_name}(a[$i]));') + } + } + fn_builder.writeln('\treturn arr;') + fn_builder.writeln('}') + g.definitions.writeln(fn_builder.str()) +} + +fn (mut g JsGen) gen_copy_for_enum(info ast.Enum, styp string, copy_fn_name string) { + g.definitions.writeln('function ${copy_fn_name}(it) { return it; }') +} + +fn (mut g JsGen) gen_copy_for_union_sum_type(info ast.SumType, styp string, copy_fn_name string) { + mut fn_builder := strings.new_builder(512) + fn_builder.writeln('function ${copy_fn_name}(x) {') + for typ in info.variants { + typ_str := g.typ(typ) + mut func_name := g.get_copy_fn(typ) + fn_builder.writeln('if (x instanceof $typ_str) { return ${func_name}(x); }') + } + fn_builder.writeln('builtin__panic(new string("unknown sum type value"));\n}') + g.definitions.writeln(fn_builder.str()) +} + +fn (mut g JsGen) gen_copy_for_interface(info ast.Interface, styp string, copy_fn_name string) { + mut fn_builder := strings.new_builder(512) + mut clean_interface_v_type_name := styp.replace('__', '.') + if styp.ends_with('*') { + clean_interface_v_type_name = '&' + clean_interface_v_type_name.replace('*', '') + } + if clean_interface_v_type_name.contains('_T_') { + clean_interface_v_type_name = + clean_interface_v_type_name.replace('Array_', '[]').replace('_T_', '<').replace('_', ', ') + + '>' + } + clean_interface_v_type_name = util.strip_main_name(clean_interface_v_type_name) + fn_builder.writeln('function ${copy_fn_name}(x) { return x; }') + /* + for typ in info.types { + subtype := g.table.get_type_symbol(typ) + mut func_name := g.get_copy_fn(typ) + if typ == ast.string_type { + + fn_builder.write_string('\tif (x instanceof string)') + fn_builder.write_string(' return new string(x.str + "");') + } else { + /* + mut val := '${func_name}(${deref}($subtype.cname*)x._$subtype.cname' + if should_use_indent_func(subtype.kind) && !sym_has_str_method { + val += ', indent_count' + } + val += ')' + val = val + */ + res := '"TODO' + fn_builder.write_string('\tif (x instanceof ${g.typ(typ)})') + fn_builder.write_string(' return ${func_name}(x);\n') + } + } + fn_builder.writeln('\tbuiltin__panic("unknown interface value");') + fn_builder.writeln('}')*/ + g.definitions.writeln(fn_builder.str()) +} + +fn (mut g JsGen) gen_copy_for_fn_type(info ast.FnType, styp string, copy_fn_name string) { + g.definitions.writeln('function $copy_fn_name (x) { return x; }') +} + +fn (mut g JsGen) gen_copy_for_array(info ast.Array, styp string, copy_fn_name string) { + g.definitions.writeln('function $copy_fn_name (x) { return x; }') +} + +fn (mut g JsGen) gen_copy_for_array_fixed(info ast.ArrayFixed, styp string, copy_fn_name string) { + g.definitions.writeln('function $copy_fn_name (x) { return x; }') +} + +fn (mut g JsGen) gen_copy_for_map(info ast.Map, styp string, copy_fn_name string) { + g.definitions.writeln('function $copy_fn_name (x) { return x; }') +} + +fn (mut g JsGen) gen_copy_for_struct(info ast.Struct, styp string, copy_fn_name string) { + mut fn_builder := strings.new_builder(512) + defer { + g.definitions.writeln(fn_builder.str()) + } + + fn_builder.writeln('function ${copy_fn_name}(it) {') + + tmp := g.new_tmp_var() + fn_builder.writeln('\tlet $tmp = new ${styp}({});') + for field in info.fields { + func_name := g.get_copy_fn(field.typ) + fn_builder.writeln('\t${tmp}.$field.name = ${func_name}(it.$field.name);') + } + fn_builder.writeln('\treturn $tmp;\n}') +} + +fn (mut g JsGen) final_gen_copy(typ StrType) { + if typ in g.generated_copy_fns { + return + } + g.generated_copy_fns << typ + sym := g.table.get_type_symbol(typ.typ) + if sym.has_method('\$copy') && !typ.typ.has_flag(.optional) { + return + } + styp := typ.styp + copy_fn_name := styp_to_copy_fn_name(styp) + if typ.typ.has_flag(.optional) { + g.gen_copy_for_option(typ.typ, styp, copy_fn_name) + return + } + match sym.kind { + .byte, .u8, .u16, .u32, .u64, .i16, .int, .i64, .isize, .usize, .bool, .int_literal, + .float_literal, .f32, .f64 { + g.definitions.writeln('function ${sym.cname}_\$copy(it) { return new ${sym.cname}(it.val); }') + return + } + else {} + } + match mut sym.info { + ast.Alias { + g.gen_copy_for_alias(sym.info, styp, copy_fn_name) + } + ast.Array { + g.gen_copy_for_array(sym.info, styp, copy_fn_name) + } + ast.ArrayFixed { + g.gen_copy_for_array_fixed(sym.info, styp, copy_fn_name) + } + ast.Enum { + g.gen_copy_for_enum(sym.info, styp, copy_fn_name) + } + ast.FnType { + g.gen_copy_for_fn_type(sym.info, styp, copy_fn_name) + } + ast.Struct { + g.gen_copy_for_struct(sym.info, styp, copy_fn_name) + } + ast.Map { + g.gen_copy_for_map(sym.info, styp, copy_fn_name) + } + ast.MultiReturn { + g.gen_copy_for_multi_return(sym.info, styp, copy_fn_name) + } + ast.SumType { + g.gen_copy_for_union_sum_type(sym.info, styp, copy_fn_name) + } + ast.Interface { + g.gen_copy_for_interface(sym.info, styp, copy_fn_name) + } + else { + verror("could not generate string method $copy_fn_name for type '$styp'") + } + } +} diff --git a/vlib/v/gen/js/fn.v b/vlib/v/gen/js/fn.v index e7356e8f75..cd5bf1a5f6 100644 --- a/vlib/v/gen/js/fn.v +++ b/vlib/v/gen/js/fn.v @@ -428,6 +428,7 @@ fn (mut g JsGen) gen_fn_decl(it ast.FnDecl) { return } cur_fn_decl := g.fn_decl + g.fn_decl = unsafe { &it } g.gen_method_decl(it, res) g.fn_decl = cur_fn_decl } @@ -601,3 +602,75 @@ fn (mut g JsGen) fn_args(args []ast.Param, is_variadic bool) { } } } + +fn (mut g JsGen) gen_anon_fn(mut fun ast.AnonFn) { + if fun.has_gen { + return + } + fun.has_gen = true + it := fun.decl + unsafe { + g.fn_decl = &it + } + cur_fn_save := g.table.cur_fn + defer { + g.table.cur_fn = cur_fn_save + } + unsafe { + g.table.cur_fn = &it + } + mut name := it.name + if name in ['+', '-', '*', '/', '%', '<', '=='] { + name = util.replace_op(name) + } + g.writeln('(function () { ') + mut inherited2copy := map[string]string{} + for inherited in fun.inherited_vars { + if !inherited.is_mut { + copy := g.copy_val(inherited.typ, inherited.name) + inherited2copy[inherited.name] = copy + } + } + + name = g.js_name(name) + + name = g.generic_fn_name(g.table.cur_concrete_types, name, true) + if name in parser.builtin_functions { + name = 'builtin__$name' + } + if it.is_pub && !it.is_method { + g.push_pub_var(name) + } + g.gen_attrs(it.attrs) + + g.write('return function (') + + mut args := it.params + + g.fn_args(args, it.is_variadic) + g.writeln(') {') + + g.inc_indent() + for i, arg in args { + is_varg := i == args.len - 1 && it.is_variadic + arg_name := g.js_name(arg.name) + if is_varg { + g.writeln('$arg_name = new array(new array_buffer({arr: $arg_name,len: new int(${arg_name}.length),index_start: new int(0)}));') + } else { + if arg.typ.is_ptr() || arg.is_mut { + g.writeln('$arg_name = new \$ref($arg_name)') + } + } + } + + for inherited in fun.inherited_vars { + if !inherited.is_mut { + g.writeln('let $inherited.name = ${inherited2copy[inherited.name]};') + } + } + g.stmts(it.stmts) + g.dec_indent() + g.writeln('}})()') + + g.fn_decl = voidptr(0) +} diff --git a/vlib/v/gen/js/js.v b/vlib/v/gen/js/js.v index 0fdf02dc29..0a3a788e3a 100644 --- a/vlib/v/gen/js/js.v +++ b/vlib/v/gen/js/js.v @@ -66,13 +66,16 @@ mut: fn_decl &ast.FnDecl // pointer to the FnDecl we are currently inside otherwise 0 generated_str_fns []StrType str_types []StrType // types that need automatic str() generation - array_fn_definitions []string // array equality functions that have been defined - map_fn_definitions []string // map equality functions that have been defined - struct_fn_definitions []string // struct equality functions that have been defined - sumtype_fn_definitions []string // sumtype equality functions that have been defined - alias_fn_definitions []string // alias equality functions that have been defined - auto_fn_definitions []string // auto generated functions defination list - anon_fn_definitions []string // anon generated functions defination list + copy_types []StrType // types that need to be deep copied + generated_copy_fns []StrType + array_fn_definitions []string // array equality functions that have been defined + map_fn_definitions []string // map equality functions that have been defined + struct_fn_definitions []string // struct equality functions that have been defined + sumtype_fn_definitions []string // sumtype equality functions that have been defined + alias_fn_definitions []string // alias equality functions that have been defined + auto_fn_definitions []string // auto generated functions defination list + anon_fn_definitions []string // anon generated functions defination list + copy_fn_definitions []string method_fn_decls map[string][]ast.FnDecl builtin_fns []string // Functions defined in `builtin` empty_line bool @@ -162,6 +165,9 @@ pub fn gen(files []&ast.File, table &ast.Table, pref &pref.Preferences) string { for i := 0; i < g.str_types.len; i++ { g.final_gen_str(g.str_types[i]) } + for i := 0; i < g.copy_types.len; i++ { + g.final_gen_copy(g.copy_types[i]) + } if g.pref.is_test { g.gen_js_main_for_tests() } @@ -607,14 +613,14 @@ fn (mut g JsGen) gen_alias_type_decl(node ast.AliasTypeDecl) { fn (mut g JsGen) stmt_no_semi(node ast.Stmt) { g.stmt_start_pos = g.out.len - match node { + match mut node { ast.EmptyStmt {} ast.AsmStmt { panic('inline asm is not supported by js') } ast.AssertStmt { g.write_v_source_line_info(node.pos) - g.gen_assert_stmt(node) + g.gen_assert_stmt(mut node) } ast.AssignStmt { g.write_v_source_line_info(node.pos) @@ -648,7 +654,7 @@ fn (mut g JsGen) stmt_no_semi(node ast.Stmt) { } ast.FnDecl { g.write_v_source_line_info(node.pos) - g.fn_decl = unsafe { &node } + g.gen_fn_decl(node) } ast.ForCStmt { @@ -710,14 +716,14 @@ fn (mut g JsGen) stmt_no_semi(node ast.Stmt) { fn (mut g JsGen) stmt(node ast.Stmt) { g.stmt_start_pos = g.out.len - match node { + match mut node { ast.EmptyStmt {} ast.AsmStmt { panic('inline asm is not supported by js') } ast.AssertStmt { g.write_v_source_line_info(node.pos) - g.gen_assert_stmt(node) + g.gen_assert_stmt(mut node) } ast.AssignStmt { g.write_v_source_line_info(node.pos) @@ -751,7 +757,6 @@ fn (mut g JsGen) stmt(node ast.Stmt) { } ast.FnDecl { g.write_v_source_line_info(node.pos) - g.fn_decl = unsafe { &node } g.gen_fn_decl(node) } ast.ForCStmt { @@ -808,7 +813,7 @@ fn (mut g JsGen) stmt(node ast.Stmt) { g.gen_struct_decl(node) } ast.TypeDecl { - match node { + match mut node { ast.AliasTypeDecl { g.gen_alias_type_decl(node) } @@ -820,10 +825,10 @@ fn (mut g JsGen) stmt(node ast.Stmt) { fn (mut g JsGen) expr(node ast.Expr) { // NB: please keep the type names in the match here in alphabetical order: - match node { + match mut node { ast.EmptyExpr {} ast.AnonFn { - g.gen_fn_decl(node.decl) + g.gen_anon_fn(mut node) } ast.ArrayDecompose {} ast.ArrayInit { @@ -1091,7 +1096,7 @@ fn (mut g JsGen) gen_ctemp_var(tvar ast.CTempVar) { fn (mut g JsGen) gen_assert_metainfo(node ast.AssertStmt) string { mod_path := g.file.path - fn_name := g.fn_decl.name + fn_name := if g.fn_decl == voidptr(0) || g.fn_decl.is_anon { 'anon' } else { g.fn_decl.name } line_nr := node.pos.line_nr src := node.expr.str() metaname := 'v_assert_meta_info_$g.new_tmp_var()' @@ -1176,8 +1181,7 @@ fn (mut g JsGen) gen_assert_single_expr(expr ast.Expr, typ ast.Type) { } // TODO -fn (mut g JsGen) gen_assert_stmt(orig_node ast.AssertStmt) { - mut node := orig_node +fn (mut g JsGen) gen_assert_stmt(mut node ast.AssertStmt) { if !node.is_used { return } @@ -1192,18 +1196,18 @@ fn (mut g JsGen) gen_assert_stmt(orig_node ast.AssertStmt) { node.expr.right = subst_expr } } - mut a := node + g.write('if( ') - g.expr(a.expr) + g.expr(node.expr) g.write('.valueOf() ) {') - s_assertion := a.expr.str().replace('"', "'") + s_assertion := node.expr.str().replace('"', "'") mut mod_path := g.file.path.replace('\\', '\\\\') if g.is_test { - metaname_ok := g.gen_assert_metainfo(a) + metaname_ok := g.gen_assert_metainfo(node) g.writeln(' g_test_oks++;') g.writeln(' main__cb_assertion_ok($metaname_ok);') g.writeln('} else {') - metaname_fail := g.gen_assert_metainfo(a) + metaname_fail := g.gen_assert_metainfo(node) g.writeln(' g_test_fails++;') g.writeln(' main__cb_assertion_failed($metaname_fail);') g.writeln(' builtin__exit(1);') @@ -1212,7 +1216,8 @@ fn (mut g JsGen) gen_assert_stmt(orig_node ast.AssertStmt) { } g.writeln('} else {') g.inc_indent() - g.writeln('builtin__eprintln(new string("$mod_path:${a.pos.line_nr + 1}: FAIL: fn ${g.fn_decl.name}(): assert $s_assertion"));') + fname := if g.fn_decl == voidptr(0) || g.fn_decl.is_anon { 'anon' } else { g.fn_decl.name } + g.writeln('builtin__eprintln(new string("$mod_path:${node.pos.line_nr + 1}: FAIL: fn ${fname}(): assert $s_assertion"));') g.writeln('builtin__exit(1);') g.dec_indent() g.writeln('}') @@ -1772,6 +1777,12 @@ fn (mut g JsGen) gen_hash_stmt(it ast.HashStmt) { g.writeln(it.val) } +fn (mut g JsGen) gen_sumtype_decl(it ast.SumTypeDecl) { + name := g.js_name(it.name) + g.push_pub_var('/** @type $name */\n\t\t$name') + g.writeln('function ${g.js_name(it.name)} (arg) { return arg; }') +} + fn (mut g JsGen) gen_struct_decl(node ast.StructDecl) { mut name := node.name if name.starts_with('JS.') { @@ -3216,6 +3227,10 @@ fn (mut g JsGen) gen_type_cast_expr(it ast.CastExpr) { || (it.expr is ast.FloatLiteral && it.typ in ast.float_type_idxs)) // Skip cast if type is the same as the parrent caster tsym := g.table.get_final_type_symbol(it.typ) + if tsym.kind == .sum_type { + g.expr(it.expr) + return + } if it.expr is ast.IntegerLiteral && (tsym.kind == .i64 || tsym.kind == .u64) { g.write('new ')