v.gen.js: refactor code, fix alias codegen, `-stats` now again works with tests on the JS backend (#11512)

pull/11518/head
playX 2021-09-16 14:00:15 +03:00 committed by GitHub
parent ead5e66afd
commit c175b4fd48
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 214 additions and 386 deletions

View File

@ -2,6 +2,7 @@ module os
#const $fs = require('fs');
#const $path = require('path');
#const tty = require('tty')
pub const (
path_delimiter = '/'
@ -95,3 +96,10 @@ pub fn execute(cmd string) Result {
output: stdout
}
}
pub fn is_atty(fd int) int {
res := 0
#res.val = +tty.isatty(fd.val)
return res
}

View File

@ -258,6 +258,9 @@ pub fn (v &Builder) get_user_files() []string {
if v.pref.is_test && v.pref.is_stats {
user_files << os.join_path(preludes_path, 'tests_with_stats.v')
}
if v.pref.backend.is_js() && v.pref.is_stats && v.pref.is_test {
user_files << os.join_path(preludes_path, 'stats_import.js.v')
}
if v.pref.is_prof {
user_files << os.join_path(preludes_path, 'profiled_program.v')
}

View File

@ -12,6 +12,9 @@ fn (mut g JsGen) gen_sumtype_equality_fn(left_type ast.Type) string {
g.sumtype_fn_definitions << ptr_styp
info := left.sym.sumtype_info()
mut fn_builder := strings.new_builder(512)
defer {
g.definitions.writeln(fn_builder.str())
}
fn_builder.writeln('function ${ptr_styp}_sumtype_eq(a,b) {')
fn_builder.writeln('\tlet aProto = Object.getPrototypeOf(a);')
fn_builder.writeln('\tlet bProto = Object.getPrototypeOf(b);')
@ -48,7 +51,7 @@ fn (mut g JsGen) gen_sumtype_equality_fn(left_type ast.Type) string {
}
fn_builder.writeln('\treturn new bool(false);')
fn_builder.writeln('}')
g.definitions.writeln(fn_builder.str())
return ptr_styp
}

View File

@ -120,10 +120,11 @@ pub fn (mut g JsGen) base_type(t ast.Type) string {
}
pub fn (mut g JsGen) typ(t ast.Type) string {
sym := g.table.get_type_symbol(t)
sym := g.table.get_final_type_symbol(t)
if sym.kind == .voidptr {
return 'any'
}
styp := g.base_type(t)
return styp
}

View File

@ -3,6 +3,7 @@ module js
import v.ast
import v.util
import v.parser
import strings
fn (mut g JsGen) js_mname(name_ string) string {
mut is_js := false
@ -348,3 +349,178 @@ fn (mut g JsGen) gen_call_expr(it ast.CallExpr) {
}
g.call_stack.delete_last()
}
enum FnGenType {
function
struct_method
alias_method
iface_method
}
fn (g &JsGen) fn_gen_type(it &ast.FnDecl) FnGenType {
if it.is_method && g.table.get_type_symbol(it.params[0].typ).kind == .alias {
return .alias_method
} else if it.is_method && g.table.get_type_symbol(it.params[0].typ).kind == .interface_ {
return .iface_method
} else if it.is_method || it.no_body {
return .struct_method
} else {
return .function
}
}
fn (mut g JsGen) gen_fn_decl(it ast.FnDecl) {
res := g.fn_gen_type(it)
if it.language == .js {
return
}
if g.inside_builtin {
g.builtin_fns << it.name
}
cur_fn_decl := g.fn_decl
g.gen_method_decl(it, res)
g.fn_decl = cur_fn_decl
}
fn fn_has_go(node ast.FnDecl) bool {
mut has_go := false
for stmt in node.stmts {
if stmt is ast.ExprStmt {
if stmt.expr is ast.GoExpr {
has_go = true
break
}
}
}
return has_go
}
fn (mut g JsGen) generic_fn_name(types []ast.Type, before string, is_decl bool) string {
if types.len == 0 {
return before
}
mut name := before + '_T'
for typ in types {
name += '_' + strings.repeat_string('__ptr__', typ.nr_muls()) + g.typ(typ.set_nr_muls(0))
}
return name
}
fn (mut g JsGen) gen_method_decl(it ast.FnDecl, typ FnGenType) {
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
}
node := it
mut name := it.name
if name in ['+', '-', '*', '/', '%', '<', '=='] {
name = util.replace_op(name)
}
if node.is_method {
unwrapped_rec_sym := g.table.get_type_symbol(g.unwrap_generic(node.receiver.typ))
if unwrapped_rec_sym.kind == .placeholder {
return
}
name = g.cc_type(node.receiver.typ, false) + '_' + name
}
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'
}
has_go := fn_has_go(it)
if it.is_pub && !it.is_method {
g.push_pub_var(name)
}
is_main := it.name == 'main.main'
g.gen_attrs(it.attrs)
if is_main {
// there is no concept of main in JS but we do have iife
g.writeln('/* program entry point */')
// g.write('(')
if has_go {
g.write('async ')
}
g.write('function js_main(')
} else if it.is_anon {
g.write('function (')
} else {
c := name[0]
if c in [`+`, `-`, `*`, `/`] {
name = util.replace_op(name)
}
// type_name := g.typ(it.return_type)
// generate jsdoc for the function
g.doc.gen_fn(it)
if has_go {
g.write('async ')
}
g.write('function ')
g.write('${name}(')
if it.is_pub && !it.is_method {
g.push_pub_var(name)
}
}
mut args := it.params
g.fn_args(args, it.is_variadic)
g.write(') {')
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($arg_name);')
} else {
if arg.typ.is_ptr() || arg.is_mut {
g.writeln('$arg_name = new \$ref($arg_name)')
}
}
}
g.stmts(it.stmts)
g.writeln('}')
if is_main {
// g.write(')')
}
g.writeln('')
for attr in it.attrs {
match attr.name {
'export' {
g.writeln('globalThis.$attr.arg = ${g.js_name(it.name)};')
}
else {}
}
}
g.fn_decl = voidptr(0)
}
fn (mut g JsGen) fn_args(args []ast.Param, is_variadic bool) {
for i, arg in args {
name := g.js_name(arg.name)
is_varg := i == args.len - 1 && is_variadic
if is_varg {
g.write('...$name')
} else {
g.write(name)
}
// if its not the last argument
if i < args.len - 1 {
g.write(', ')
}
}
}

View File

@ -9,12 +9,6 @@ import v.util.version
import v.depgraph
import encoding.base64
import v.gen.js.sourcemap
import v.parser
struct MutArg {
tmp_var string
expr ast.Expr = ast.empty_expr()
}
const (
// https://ecma-international.org/ecma-262/#sec-reserved-words
@ -285,24 +279,24 @@ pub fn (mut g JsGen) gen_js_main_for_tests() {
g.writeln('')
g.writeln('globalThis.VTEST=1')
if g.pref.is_stats {
g.writeln('let bt = start_testing($all_tfuncs.len, "$g.pref.path")')
g.writeln('let bt = main__start_testing(new int($all_tfuncs.len), new string("$g.pref.path"))')
}
for tname in all_tfuncs {
tcname := g.js_name(tname)
if g.pref.is_stats {
g.writeln('bt.testing_step_start("$tcname")')
g.writeln('main__BenchedTests_testing_step_start(bt,new string("$tcname"))')
}
g.writeln('try { ${tcname}(); } catch (_e) {} ')
if g.pref.is_stats {
g.writeln('bt.testing_step_end();')
g.writeln('main__BenchedTests_testing_step_end(bt);')
}
}
g.writeln('')
if g.pref.is_stats {
g.writeln('bt.end_testing();')
g.writeln('main__BenchedTests_end_testing(bt);')
}
g.dec_indent()
g.writeln('}')
@ -489,62 +483,6 @@ fn (mut g JsGen) get_alias(name string) string {
}
fn (mut g JsGen) js_name(name_ string) string {
/*
mut is_js := false
is_overload := ['+', '-', '*', '/', '==', '<', '>']
mut name := name_
if name.starts_with('JS.') {
name = name[3..]
is_js = true
}
ns := get_ns(name)
name = if name in is_overload {
match name {
'+' {
'\$add'
}
'-' {
'\$sub'
}
'/' {
'\$div'
}
'*' {
'\$mul'
}
'%' {
'\$mod'
}
'==' {
'eq'
}
'>' {
'\$gt'
}
'<' {
'\$lt'
}
else {
''
}
}
} else if g.ns == 0 {
name
} else if ns == g.ns.name {
name.split('.').last()
} else {
g.get_alias(name)
}
mut parts := name.split('.')
if !is_js {
for i, p in parts {
if p in js.js_reserved {
parts[i] = 'v_$p'
}
}
}
return parts.join('.')*/
mut name := name_
if name.starts_with('JS.') {
name = name[3..]
@ -558,11 +496,9 @@ fn (mut g JsGen) js_name(name_ string) string {
}
fn (mut g JsGen) stmts(stmts []ast.Stmt) {
g.inc_indent()
for stmt in stmts {
g.stmt(stmt)
}
g.dec_indent()
}
[inline]
@ -619,6 +555,11 @@ fn (mut g JsGen) gen_global_decl(node ast.GlobalDecl) {
}
}
fn (mut g JsGen) gen_alias_type_decl(node ast.AliasTypeDecl) {
name := if g.ns.name == 'builtin' { node.name } else { '${g.js_name(g.ns.name)}__$node.name' }
g.writeln('function ${name}(val) { return val; }')
}
fn (mut g JsGen) stmt_no_semi(node ast.Stmt) {
g.stmt_start_pos = g.out.len
match node {
@ -822,7 +763,12 @@ fn (mut g JsGen) stmt(node ast.Stmt) {
g.gen_struct_decl(node)
}
ast.TypeDecl {
// skip JS has no typedecl
match node {
ast.AliasTypeDecl {
g.gen_alias_type_decl(node)
}
else {}
}
}
}
}
@ -1275,7 +1221,9 @@ fn (mut g JsGen) gen_attrs(attrs []ast.Attr) {
fn (mut g JsGen) gen_block(it ast.Block) {
g.writeln('{')
g.inc_indent()
g.stmts(it.stmts)
g.dec_indent()
g.writeln('}')
}
@ -1338,59 +1286,9 @@ fn (mut g JsGen) gen_expr_stmt_no_semi(it ast.ExprStmt) {
g.expr(it.expr)
}
enum FnGenType {
function
struct_method
alias_method
iface_method
}
fn (g &JsGen) fn_gen_type(it &ast.FnDecl) FnGenType {
if it.is_method && g.table.get_type_symbol(it.params[0].typ).kind == .alias {
return .alias_method
} else if it.is_method && g.table.get_type_symbol(it.params[0].typ).kind == .interface_ {
return .iface_method
} else if it.is_method || it.no_body {
return .struct_method
} else {
return .function
}
}
fn (mut g JsGen) gen_fn_decl(it ast.FnDecl) {
res := g.fn_gen_type(it)
if it.language == .js {
return
}
/*
if res == .struct_method {
// Struct methods are handled by class generation code.
return
}*/
if g.inside_builtin {
g.builtin_fns << it.name
}
cur_fn_decl := g.fn_decl
g.gen_method_decl(it, res)
g.fn_decl = cur_fn_decl
}
fn fn_has_go(node ast.FnDecl) bool {
mut has_go := false
for stmt in node.stmts {
if stmt is ast.ExprStmt {
if stmt.expr is ast.GoExpr {
has_go = true
break
}
}
}
return has_go
}
// cc_type whether to prefix 'struct' or not (C__Foo -> struct Foo)
fn (mut g JsGen) cc_type(typ ast.Type, is_prefix_struct bool) string {
sym := g.table.get_type_symbol(g.unwrap_generic(typ))
sym := g.table.get_final_type_symbol(g.unwrap_generic(typ))
mut styp := sym.cname
match mut sym.info {
ast.Struct, ast.Interface, ast.SumType {
@ -1408,231 +1306,6 @@ fn (mut g JsGen) cc_type(typ ast.Type, is_prefix_struct bool) string {
return styp
}
fn (mut g JsGen) generic_fn_name(types []ast.Type, before string, is_decl bool) string {
if types.len == 0 {
return before
}
mut name := before + '_T'
for typ in types {
name += '_' + strings.repeat_string('__ptr__', typ.nr_muls()) + g.typ(typ.set_nr_muls(0))
}
return name
}
fn (mut g JsGen) gen_method_decl(it ast.FnDecl, typ FnGenType) {
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
}
node := it
mut name := it.name
if name in ['+', '-', '*', '/', '%', '<', '=='] {
name = util.replace_op(name)
}
if node.is_method {
unwrapped_rec_sym := g.table.get_type_symbol(g.unwrap_generic(node.receiver.typ))
if unwrapped_rec_sym.kind == .placeholder {
return
}
name = g.cc_type(node.receiver.typ, false) + '_' + name
}
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'
}
has_go := fn_has_go(it)
if it.is_pub && !it.is_method {
g.push_pub_var(name)
}
is_main := it.name == 'main.main'
g.gen_attrs(it.attrs)
if is_main {
// there is no concept of main in JS but we do have iife
g.writeln('/* program entry point */')
// g.write('(')
if has_go {
g.write('async ')
}
g.write('function js_main(')
} else if it.is_anon {
g.write('function (')
} else {
c := name[0]
if c in [`+`, `-`, `*`, `/`] {
name = util.replace_op(name)
}
// type_name := g.typ(it.return_type)
// generate jsdoc for the function
g.doc.gen_fn(it)
if has_go {
g.write('async ')
}
g.write('function ')
g.write('${name}(')
if it.is_pub && !it.is_method {
g.push_pub_var(name)
}
}
mut args := it.params
g.fn_args(args, it.is_variadic)
g.write(') {')
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($arg_name);')
} else {
if arg.typ.is_ptr() || arg.is_mut {
g.writeln('$arg_name = new \$ref($arg_name)')
}
}
}
g.stmts(it.stmts)
g.writeln('}')
if is_main {
// g.write(')')
}
g.writeln('')
for attr in it.attrs {
match attr.name {
'export' {
g.writeln('globalThis.$attr.arg = ${g.js_name(it.name)};')
}
else {}
}
}
/*
if typ == .alias_method || typ == .iface_method {
sym := g.table.get_final_type_symbol(it.params[0].typ.set_nr_muls(0))
name := g.js_name(sym.name)
if name in js.v_types {
g.writeln('')
}
g.writeln('${name}.prototype.$it.name = function ')
}
has_go := fn_has_go(it)
is_main := it.name == 'main.main'
g.gen_attrs(it.attrs)
if is_main {
// there is no concept of main in JS but we do have iife
g.writeln('/* program entry point */')
g.write('(')
if has_go {
g.write('async ')
}
g.write('function(')
} else if it.is_anon {
g.write('function (')
} else {
mut name := g.js_name(it.name)
c := name[0]
if c in [`+`, `-`, `*`, `/`] {
name = util.replace_op(name)
}
// type_name := g.typ(it.return_type)
// generate jsdoc for the function
g.doc.gen_fn(it)
if has_go {
g.write('async ')
}
if !it.is_method {
g.write('function ')
} else {
if it.attrs.contains('js_getter') {
g.write('get ')
} else if it.attrs.contains('js_setter') {
g.write('set ')
}
}
g.write('${name}(')
if it.is_pub && !it.is_method {
g.push_pub_var(name)
}
}
mut args := it.params
if it.is_method {
args = args[1..]
}
g.fn_args(args, it.is_variadic)
if it.is_method {
if args.len > 0 {
g.write(', ')
}
if it.params[0].is_mut || it.params[0].typ.is_ptr() {
g.write('${it.params[0].name} = new \$ref(this)')
} else {
g.write('${it.params[0].name} = this')
}
}
g.writeln(') {')
for i, arg in args {
is_varg := i == args.len - 1 && it.is_variadic
name := g.js_name(arg.name)
if is_varg {
g.writeln('$name = new array($name);')
} else {
if arg.typ.is_ptr() || arg.is_mut {
g.writeln('$name = new \$ref($name)')
}
}
}
g.stmts(it.stmts)
g.writeln('}')
for attr in it.attrs {
match attr.name {
'export' {
g.writeln('globalThis.$attr.arg = ${g.js_name(it.name)};')
}
else {}
}
}
if is_main {
g.write(')();')
} else if typ != .struct_method {
// g.write(';')
}
if typ == .struct_method || typ == .alias_method || typ == .iface_method {
g.writeln('\n')
}
*/
g.fn_decl = voidptr(0)
}
fn (mut g JsGen) fn_args(args []ast.Param, is_variadic bool) {
for i, arg in args {
name := g.js_name(arg.name)
is_varg := i == args.len - 1 && is_variadic
if is_varg {
g.write('...$name')
} else {
g.write(name)
}
// if its not the last argument
if i < args.len - 1 {
g.write(', ')
}
}
}
fn (mut g JsGen) gen_for_c_stmt(it ast.ForCStmt) {
g.inside_loop = true
g.write('for (')
@ -1918,11 +1591,6 @@ fn (mut g JsGen) gen_struct_decl(node ast.StructDecl) {
g.writeln('};\n')
g.dec_indent()
/*
for cfn in fns {
g.gen_method_decl(cfn, .struct_method)
}*/
if node.is_pub {
g.push_pub_var(name)
}
@ -2553,11 +2221,12 @@ fn (mut g JsGen) gen_index_expr(expr ast.IndexExpr) {
g.write('.valueOf()')
}
g.write('.arr')
g.write('[+')
g.write('[Number(')
g.cast_stack << ast.int_type_idx
g.expr(expr.index)
g.write('.valueOf()')
g.cast_stack.delete_last()
g.write(']')
g.write(')]')
}
}
@ -2617,37 +2286,6 @@ fn (mut g JsGen) gen_infix_expr(it ast.InfixExpr) {
g.write('.valueOf()')
g.write(')')
} else if it.op == .eq || it.op == .ne {
/*
has_operator_overloading := g.table.type_has_method(l_sym, '==')
if has_operator_overloading {
g.expr(it.left)
g.gen_deref_ptr(it.left_type)
g.write('.eq(')
g.expr(it.right)
g.gen_deref_ptr(it.right_type)
g.write(')')
// Shallow equatables
} else if l_sym.kind in js.shallow_equatables && r_sym.kind in js.shallow_equatables {
// wrap left expr in parens so binary operations will work correctly.
g.write('(')
g.expr(it.left)
g.gen_deref_ptr(it.left_type)
g.write(')')
g.write('.eq(')
g.cast_stack << int(l_sym.kind)
g.expr(it.right)
g.gen_deref_ptr(it.right_type)
g.cast_stack.delete_last()
g.write(')')
} else {
g.write('vEq(')
g.expr(it.left)
g.gen_deref_ptr(it.left_type)
g.write(', ')
g.expr(it.right)
g.gen_deref_ptr(it.right_type)
g.write(')')
}*/
node := it
left := g.unwrap(node.left_type)
right := g.unwrap(node.right_type)

View File

@ -1 +0,0 @@
module stats_import