js: copy variables passed to closures (#12328)

pull/12298/head
playX 2021-10-28 19:31:56 +03:00 committed by GitHub
parent 0e95e4d7b4
commit fa02418a55
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 369 additions and 27 deletions

View File

@ -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 {

View File

@ -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 := ''

View File

@ -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'")
}
}
}

View File

@ -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)
}

View File

@ -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 ')