From a4c57ba56ea249a7ae2630eb2ca417d1eb6e2996 Mon Sep 17 00:00:00 2001 From: playX Date: Thu, 11 Nov 2021 15:36:32 +0300 Subject: [PATCH] js: support JS interfaces (#12426) --- vlib/builtin/js/array.js.v | 2 +- vlib/builtin/js/builtin.js.v | 63 --------------- vlib/builtin/js/jsfns.js.v | 76 ++++++++++++------ vlib/builtin/js/string.js.v | 49 ++++++++---- vlib/jsdom/matrix.js.v | 145 +++++++++++++++++----------------- vlib/jsdom/path2d.js.v | 4 +- vlib/jsdom/point.js.v | 24 +++--- vlib/v/checker/checker.v | 37 ++++++++- vlib/v/gen/js/builtin_types.v | 2 +- vlib/v/gen/js/fn.v | 8 +- vlib/v/gen/js/js.v | 27 ++++++- vlib/v/parser/struct.v | 13 ++- 12 files changed, 245 insertions(+), 205 deletions(-) diff --git a/vlib/builtin/js/array.js.v b/vlib/builtin/js/array.js.v index d20f4084ed..cf006bdb22 100644 --- a/vlib/builtin/js/array.js.v +++ b/vlib/builtin/js/array.js.v @@ -147,7 +147,7 @@ pub fn (a array) repeat(count int) array { } } -#function makeEmptyArray() { return new array(new array_buffer({})); } +#function makeEmptyArray() { return new array(new array_buffer({ arr: [], len: new int(0), index_start: new int(0), cap: new int(0) })); } #function makeEmtpyJSArray() { return new Array(); } fn JS.makeEmptyArray() array diff --git a/vlib/builtin/js/builtin.js.v b/vlib/builtin/js/builtin.js.v index 1d6fdd254d..b5092ba8aa 100644 --- a/vlib/builtin/js/builtin.js.v +++ b/vlib/builtin/js/builtin.js.v @@ -104,66 +104,3 @@ pub fn (f float_literal) str() string { return res } - -pub fn tof64(n JS.Number) f64 { - res := f64(0.0) - #res.val = n; - - return res -} - -pub fn tof32(n JS.Number) f32 { - res := f32(0.0) - #res.val = n; - - return res -} - -pub fn toi(n JS.Number) int { - res := int(0) - #res.val = Math.floor(n); - - return res -} - -pub fn f64tonum(n f64) JS.Number { - mut res := JS.Number{} - #res = n.val; - - return res -} - -pub fn itonum(n int) JS.Number { - mut res := JS.Number{} - #res = n.val; - - return res -} - -pub fn i64tobigint(n i64) JS.BigInt { - mut res := JS.BigInt{} - #res = n.val; - - return res -} - -pub fn u64tobigint(n u64) JS.BigInt { - mut res := JS.BigInt{} - #res = n.val; - - return res -} - -pub fn tobool(b JS.Boolean) bool { - res := false - #res.val = b; - - return res -} - -pub fn booltojs(b bool) JS.Boolean { - mut res := JS.Boolean{} - #res = b.val; - - return res -} diff --git a/vlib/builtin/js/jsfns.js.v b/vlib/builtin/js/jsfns.js.v index 8bb8eba296..bc1e0ece5e 100644 --- a/vlib/builtin/js/jsfns.js.v +++ b/vlib/builtin/js/jsfns.js.v @@ -7,21 +7,62 @@ module builtin -pub struct JS.BigInt {} +pub interface JS.Object {} -pub struct JS.Number {} +pub interface JS.BigInt { + JS.Any +} -pub struct JS.String { +pub interface JS.Number { + JS.Any +} + +pub interface JS.String { + JS.Any + length JS.Number + charAt(index JS.Number) JS.String + charCodeAt(index JS.Number) JS.Number + toUpperCase() JS.String + toLowerCase() JS.String + concat(a JS.String) JS.String + includes(substr JS.String) JS.Boolean + endsWith(substr JS.String) JS.Boolean + startsWith(substr JS.String) JS.Boolean + slice(a JS.Number, b JS.Number) JS.String + split(dot JS.String) JS.Array + indexOf(needle JS.String) JS.Number + lastIndexOf(needle JS.String) JS.Number +} + +pub interface JS.Boolean { + JS.Any +} + +pub interface JS.Map { + JS.Any + size JS.Number + clear() + delete(key JS.Any) JS.Boolean + get(key JS.Any) JS.Any + has(key JS.Any) JS.Any + set(key JS.Any, val JS.Any) +} + +#function Any(val) { return val; } + +pub interface JS.Any {} + +pub interface JS.Array { + JS.Any // map(fn (JS.Any) JS.Any) JS.Array + map(JS.Any) JS.Array + push(JS.Any) JS.Any + pop() JS.Any + at(JS.Number) JS.Any +mut: length JS.Number } -pub struct JS.Boolean {} - -pub struct JS.Array { - length JS.Number -} - -pub struct JS.Map {} +pub fn JS.Array.prototype.constructor(...any) JS.Array // browser: https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Global_Objects/Error // node: https://nodejs.org/api/errors.html#errors_class_error @@ -110,18 +151,3 @@ fn JS.Math.tan(f64) f64 // JSON fn JS.JSON.stringify(any) string fn JS.JSON.parse(string) any - -// String -fn (v JS.String) slice(a int, b int) JS.String -fn (v JS.String) split(dot JS.String) []JS.String -fn (s JS.String) indexOf(needle JS.String) int -fn (s JS.String) lastIndexOf(needle JS.String) int - -fn (s JS.String) charAt(i int) JS.String -fn (s JS.String) charCodeAt(i int) byte -fn (s JS.String) toUpperCase() JS.String -fn (s JS.String) toLowerCase() JS.String -fn (s JS.String) concat(a JS.String) JS.String -fn (s JS.String) includes(substr JS.String) bool -fn (s JS.String) endsWith(substr JS.String) bool -fn (s JS.String) startsWith(substr JS.String) bool diff --git a/vlib/builtin/js/string.js.v b/vlib/builtin/js/string.js.v index 7fecc19ef9..65ac080267 100644 --- a/vlib/builtin/js/string.js.v +++ b/vlib/builtin/js/string.js.v @@ -25,7 +25,7 @@ pub fn (s string) substr(start int, end int) string { } pub fn (s string) after(dot string) string { - return string(s.str.slice(s.str.lastIndexOf(dot.str) + 1, int(s.str.length))) + return string(s.str.slice(JS.Number(int(s.str.lastIndexOf(dot.str)) + 1), s.str.length)) } pub fn (s string) after_char(dot byte) string { @@ -34,7 +34,7 @@ pub fn (s string) after_char(dot byte) string { } pub fn (s string) all_after(dot string) string { - pos := if dot.len == 0 { -1 } else { s.str.indexOf(dot.str) } + pos := if dot.len == 0 { -1 } else { int(s.str.indexOf(dot.str)) } if pos == -1 { return s.clone() } @@ -43,7 +43,7 @@ pub fn (s string) all_after(dot string) string { // why does this exist? pub fn (s string) all_after_last(dot string) string { - pos := if dot.len == 0 { -1 } else { s.str.lastIndexOf(dot.str) } + pos := if dot.len == 0 { -1 } else { int(s.str.lastIndexOf(dot.str)) } if pos == -1 { return s.clone() } @@ -51,7 +51,7 @@ pub fn (s string) all_after_last(dot string) string { } pub fn (s string) all_before(dot string) string { - pos := if dot.len == 0 { -1 } else { s.str.indexOf(dot.str) } + pos := if dot.len == 0 { -1 } else { int(s.str.indexOf(dot.str)) } if pos == -1 { return s.clone() } @@ -60,7 +60,7 @@ pub fn (s string) all_before(dot string) string { } pub fn (s string) all_before_last(dot string) string { - pos := if dot.len == 0 { -1 } else { s.str.lastIndexOf(dot.str) } + pos := if dot.len == 0 { -1 } else { int(s.str.lastIndexOf(dot.str)) } if pos == -1 { return s.clone() } @@ -72,16 +72,27 @@ pub fn (s string) bool() bool { } pub fn (s string) split(dot string) []string { - mut arr := s.str.split(dot.str).map(string(it)) - #arr = new array(new array_buffer({arr,index_start: new int(0),len: new int(arr.length)})) + tmparr := s.str.split(dot.str).map(fn (it JS.Any) JS.Any { + res := '' + #res.str = it + + return res + }) + _ := tmparr + mut arr := []string{} + #arr = new array(new array_buffer({arr: tmparr,index_start: new int(0),len: new int(tmparr.length)})) return arr } pub fn (s string) bytes() []byte { sep := '' - mut arr := s.str.split(sep.str).map(it.charCodeAt(0)) - #arr = new array(new array_buffer({arr,index_start: new int(0),len: new int(arr.length)})) + tmparr := s.str.split(sep.str).map(fn (it JS.Any) JS.Any { + return JS.Any(byte(JS.String(it).charCodeAt(0))) + }) + _ := tmparr + mut arr := []byte{} + #arr = new array(new array_buffer({arr: tmparr,index_start: new int(0),len: new int(tmparr.length)})) return arr } @@ -96,13 +107,14 @@ pub fn (s string) clone() string { } pub fn (s string) contains(substr string) bool { - return s.str.includes(substr.str) + return bool(s.str.includes(substr.str)) } pub fn (s string) contains_any(chars string) bool { sep := '' - for x in chars.str.split(sep.str) { - if s.str.includes(x) { + res := chars.str.split(sep.str) + for i in 0 .. int(res.length) { + if bool(s.str.includes(JS.String(res.at(JS.Number(i))))) { return true } } @@ -114,7 +126,7 @@ pub fn (s string) contains_any_substr(chars []string) bool { return true } for x in chars { - if s.str.includes(x.str) { + if bool(s.str.includes(x.str)) { return true } } @@ -124,7 +136,12 @@ pub fn (s string) contains_any_substr(chars []string) bool { pub fn (s string) count(substr string) int { // TODO: "error: `[]JS.String` is not a struct" when returning arr.length or arr.len arr := s.str.split(substr.str) - return native_str_arr_len(arr) + len := int(arr.length) + if len == 0 { + return 0 + } else { + return len - 1 + } } pub fn (s string) ends_with(p string) bool { @@ -135,7 +152,7 @@ pub fn (s string) ends_with(p string) bool { } pub fn (s string) starts_with(p string) bool { - return s.str.startsWith(p.str) + return bool(s.str.startsWith(p.str)) } pub fn (s string) fields() []string { @@ -170,7 +187,7 @@ pub fn (s string) fields() []string { } pub fn (s string) find_between(start string, end string) string { - return string(s.str.slice(s.str.indexOf(start.str) + 1, s.str.indexOf(end.str))) + return string(s.str.slice(JS.Number(int(s.str.indexOf(start.str)) + 1), s.str.indexOf(end.str))) } // unnecessary in the JS backend, implemented for api parity. diff --git a/vlib/jsdom/matrix.js.v b/vlib/jsdom/matrix.js.v index d80f714337..6b47623d57 100644 --- a/vlib/jsdom/matrix.js.v +++ b/vlib/jsdom/matrix.js.v @@ -1,37 +1,36 @@ module jsdom -pub struct JS.DOMMatrix { -pub: - is_2d JS.Boolean [noinit] - is_identity JS.Boolean [noinit] -pub mut: - m11 JS.Number [noinit] - m12 JS.Number [noinit] - m13 JS.Number [noinit] - m14 JS.Number [noinit] - m21 JS.Number [noinit] - m22 JS.Number [noinit] - m23 JS.Number [noinit] - m24 JS.Number [noinit] - m31 JS.Number [noinit] - m32 JS.Number [noinit] - m33 JS.Number [noinit] - m34 JS.Number [noinit] - m41 JS.Number [noinit] - m42 JS.Number [noinit] - m43 JS.Number [noinit] - m44 JS.Number [noinit] - a JS.Number [noinit] - b JS.Number [noinit] - c JS.Number [noinit] - d JS.Number [noinit] - e JS.Number [noinit] - f JS.Number [noinit] +pub interface JS.DOMMatrix { + is_2d JS.Boolean + is_identity JS.Boolean +mut: + m11 JS.Number + m12 JS.Number + m13 JS.Number + m14 JS.Number + m21 JS.Number + m22 JS.Number + m23 JS.Number + m24 JS.Number + m31 JS.Number + m32 JS.Number + m33 JS.Number + m34 JS.Number + m41 JS.Number + m42 JS.Number + m43 JS.Number + m44 JS.Number + a JS.Number + b JS.Number + c JS.Number + d JS.Number + e JS.Number + f JS.Number } pub struct DOMMatrix { mut: - matrix JS.DOMMatrix [noinit] + matrix JS.DOMMatrix = JS.DOMMatrix(voidptr(0)) } pub fn (matrix DOMMatrix) str() string { @@ -48,7 +47,7 @@ pub fn new_matrix(init []f64) DOMMatrix { _ := val #tmp.push(val); } - mut m := JS.DOMMatrix{} + mut m := JS.DOMMatrix(voidptr(0)) #m = new DOMMatrix(tmp); return DOMMatrix{m} @@ -190,177 +189,177 @@ pub fn (m DOMMatrix) is_2d() bool { } pub fn (m DOMMatrix) a() f64 { - return tof64(m.matrix.a) + return f64(m.matrix.a) } pub fn (m DOMMatrix) b() f64 { - return tof64(m.matrix.b) + return f64(m.matrix.b) } pub fn (m DOMMatrix) c() f64 { - return tof64(m.matrix.c) + return f64(m.matrix.c) } pub fn (m DOMMatrix) d() f64 { - return tof64(m.matrix.d) + return f64(m.matrix.d) } pub fn (m DOMMatrix) e() f64 { - return tof64(m.matrix.e) + return f64(m.matrix.e) } pub fn (m DOMMatrix) f() f64 { - return tof64(m.matrix.f) + return f64(m.matrix.f) } pub fn (mut m DOMMatrix) set_a(a f64) { - m.matrix.a = f64tonum(a) + m.matrix.a = JS.Number(a) } pub fn (mut m DOMMatrix) set_b(b f64) { - m.matrix.b = f64tonum(b) + m.matrix.b = JS.Number(b) } pub fn (mut m DOMMatrix) set_c(c f64) { - m.matrix.c = f64tonum(c) + m.matrix.c = JS.Number(c) } pub fn (mut m DOMMatrix) set_d(d f64) { - m.matrix.d = f64tonum(d) + m.matrix.d = JS.Number(d) } pub fn (mut m DOMMatrix) set_e(e f64) { - m.matrix.e = f64tonum(e) + m.matrix.e = JS.Number(e) } pub fn (mut m DOMMatrix) set_f(f f64) { - m.matrix.f = f64tonum(f) + m.matrix.f = JS.Number(f) } pub fn (m DOMMatrix) m11() f64 { - return tof64(m.matrix.m11) + return f64(m.matrix.m11) } pub fn (m DOMMatrix) m12() f64 { - return tof64(m.matrix.m12) + return f64(m.matrix.m12) } pub fn (m DOMMatrix) m13() f64 { - return tof64(m.matrix.m13) + return f64(m.matrix.m13) } pub fn (m DOMMatrix) m14() f64 { - return tof64(m.matrix.m14) + return f64(m.matrix.m14) } pub fn (m DOMMatrix) m21() f64 { - return tof64(m.matrix.m21) + return f64(m.matrix.m21) } pub fn (m DOMMatrix) m22() f64 { - return tof64(m.matrix.m22) + return f64(m.matrix.m22) } pub fn (m DOMMatrix) m23() f64 { - return tof64(m.matrix.m23) + return f64(m.matrix.m23) } pub fn (m DOMMatrix) m24() f64 { - return tof64(m.matrix.m24) + return f64(m.matrix.m24) } pub fn (m DOMMatrix) m31() f64 { - return tof64(m.matrix.m31) + return f64(m.matrix.m31) } pub fn (m DOMMatrix) m32() f64 { - return tof64(m.matrix.m32) + return f64(m.matrix.m32) } pub fn (m DOMMatrix) m33() f64 { - return tof64(m.matrix.m33) + return f64(m.matrix.m33) } pub fn (m DOMMatrix) m34() f64 { - return tof64(m.matrix.m34) + return f64(m.matrix.m34) } pub fn (m DOMMatrix) m41() f64 { - return tof64(m.matrix.m41) + return f64(m.matrix.m41) } pub fn (m DOMMatrix) m42() f64 { - return tof64(m.matrix.m42) + return f64(m.matrix.m42) } pub fn (m DOMMatrix) m43() f64 { - return tof64(m.matrix.m43) + return f64(m.matrix.m43) } pub fn (m DOMMatrix) m44() f64 { - return tof64(m.matrix.m44) + return f64(m.matrix.m44) } pub fn (mut m DOMMatrix) set_m11(x f64) { - m.matrix.m11 = f64tonum(x) + m.matrix.m11 = JS.Number(x) } pub fn (mut m DOMMatrix) set_m12(x f64) { - m.matrix.m12 = f64tonum(x) + m.matrix.m12 = JS.Number(x) } pub fn (mut m DOMMatrix) set_m13(x f64) { - m.matrix.m13 = f64tonum(x) + m.matrix.m13 = JS.Number(x) } pub fn (mut m DOMMatrix) set_m14(x f64) { - m.matrix.m14 = f64tonum(x) + m.matrix.m14 = JS.Number(x) } pub fn (mut m DOMMatrix) set_m21(x f64) { - m.matrix.m21 = f64tonum(x) + m.matrix.m21 = JS.Number(x) } pub fn (mut m DOMMatrix) set_m22(x f64) { - m.matrix.m22 = f64tonum(x) + m.matrix.m22 = JS.Number(x) } pub fn (mut m DOMMatrix) set_m23(x f64) { - m.matrix.m23 = f64tonum(x) + m.matrix.m23 = JS.Number(x) } pub fn (mut m DOMMatrix) set_m24(x f64) { - m.matrix.m24 = f64tonum(x) + m.matrix.m24 = JS.Number(x) } pub fn (mut m DOMMatrix) set_m31(x f64) { - m.matrix.m31 = f64tonum(x) + m.matrix.m31 = JS.Number(x) } pub fn (mut m DOMMatrix) set_m32(x f64) { - m.matrix.m32 = f64tonum(x) + m.matrix.m32 = JS.Number(x) } pub fn (mut m DOMMatrix) set_m33(x f64) { - m.matrix.m33 = f64tonum(x) + m.matrix.m33 = JS.Number(x) } pub fn (mut m DOMMatrix) set_m34(x f64) { - m.matrix.m34 = f64tonum(x) + m.matrix.m34 = JS.Number(x) } pub fn (mut m DOMMatrix) set_m41(x f64) { - m.matrix.m41 = f64tonum(x) + m.matrix.m41 = JS.Number(x) } pub fn (mut m DOMMatrix) set_m42(x f64) { - m.matrix.m42 = f64tonum(x) + m.matrix.m42 = JS.Number(x) } pub fn (mut m DOMMatrix) set_m43(x f64) { - m.matrix.m43 = f64tonum(x) + m.matrix.m43 = JS.Number(x) } pub fn (mut m DOMMatrix) set_m44(x f64) { - m.matrix.m44 = f64tonum(x) + m.matrix.m44 = JS.Number(x) } diff --git a/vlib/jsdom/path2d.js.v b/vlib/jsdom/path2d.js.v index 1fd64f6fd0..4883d63322 100644 --- a/vlib/jsdom/path2d.js.v +++ b/vlib/jsdom/path2d.js.v @@ -51,11 +51,11 @@ pub fn (path Path2D) rect(x f64, y f64, width f64, height f64) { } pub fn (path Path2D) line_to(x f64, y f64) { - path.path.lineTo(f64tonum(x), f64tonum(y)) + path.path.lineTo(JS.Number(x), JS.Number(y)) } pub fn (path Path2D) move_to(x f64, y f64) { - path.path.lineTo(f64tonum(x), f64tonum(y)) + path.path.lineTo(JS.Number(x), JS.Number(y)) } pub fn (path Path2D) close_path() { diff --git a/vlib/jsdom/point.js.v b/vlib/jsdom/point.js.v index eb7704c323..21997b52c6 100644 --- a/vlib/jsdom/point.js.v +++ b/vlib/jsdom/point.js.v @@ -16,44 +16,44 @@ mut: pub fn new_dompoint(x f64, y f64, z f64, w f64) DOMPoint { mut point := DOMPoint{} - point.point.x = f64tonum(x) - point.point.y = f64tonum(y) - point.point.z = f64tonum(z) - point.point.w = f64tonum(w) + point.point.x = JS.Number(x) + point.point.y = JS.Number(y) + point.point.z = JS.Number(z) + point.point.w = JS.Number(w) return point } pub fn (p DOMPoint) x() f64 { - return tof64(p.point.x) + return f64(p.point.x) } pub fn (p DOMPoint) y() f64 { - return tof64(p.point.y) + return f64(p.point.y) } pub fn (p DOMPoint) z() f64 { - return tof64(p.point.z) + return f64(p.point.z) } pub fn (p DOMPoint) w() f64 { - return tof64(p.point.w) + return f64(p.point.w) } pub fn (mut p DOMPoint) set_x(x f64) { - p.point.x = f64tonum(x) + p.point.x = JS.Number(x) } pub fn (mut p DOMPoint) set_y(y f64) { - p.point.y = f64tonum(y) + p.point.y = JS.Number(y) } pub fn (mut p DOMPoint) set_z(z f64) { - p.point.z = f64tonum(z) + p.point.z = JS.Number(z) } pub fn (mut p DOMPoint) set_w(w f64) { - p.point.w = f64tonum(w) + p.point.w = JS.Number(w) } pub fn (p DOMPoint) matrix_transform(matrix DOMMatrix) DOMPoint { diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 2d641ca248..7cd6b6680c 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -458,6 +458,7 @@ pub fn (mut c Checker) expand_iface_embeds(idecl &ast.InterfaceDecl, level int, pub fn (mut c Checker) interface_decl(mut node ast.InterfaceDecl) { c.check_valid_pascal_case(node.name, 'interface name', node.pos) mut decl_sym := c.table.get_type_symbol(node.typ) + is_js := node.language == .js if mut decl_sym.info is ast.Interface { if node.ifaces.len > 0 { all_ifaces := c.expand_iface_embeds(node, 0, node.ifaces) @@ -552,8 +553,26 @@ pub fn (mut c Checker) interface_decl(mut node ast.InterfaceDecl) { c.check_valid_snake_case(method.name, 'method name', method.pos) } c.ensure_type_exists(method.return_type, method.return_type_pos) or { return } - for param in method.params { + if is_js { + mtyp := c.table.get_type_symbol(method.return_type) + if (mtyp.language != .js && !method.return_type.is_void()) + && !mtyp.name.starts_with('JS.') { + c.error('method $method.name returns non JS type', method.pos) + } + } + for j, param in method.params { + if j == 0 && is_js { + continue // no need to check first param + } c.ensure_type_exists(param.typ, param.pos) or { return } + if is_js { + ptyp := c.table.get_type_symbol(param.typ) + if ptyp.kind != .function && ptyp.language != .js + && !ptyp.name.starts_with('JS.') { + c.error('method `$method.name` accepts non JS type as parameter', + method.pos) + } + } } for field in node.fields { field_sym := c.table.get_type_symbol(field.typ) @@ -573,6 +592,12 @@ pub fn (mut c Checker) interface_decl(mut node ast.InterfaceDecl) { c.check_valid_snake_case(field.name, 'field name', field.pos) } c.ensure_type_exists(field.typ, field.pos) or { return } + if is_js { + tsym := c.table.get_type_symbol(field.typ) + if tsym.language != .js && !tsym.name.starts_with('JS.') { + c.error('field `$field.name` uses non JS type', field.pos) + } + } if field.typ == node.typ { c.error('recursive interface fields are not allowed because they cannot be initialised', field.type_pos) @@ -1916,7 +1941,7 @@ pub fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type { } node.left_type = left_type // Set default values for .return_type & .receiver_type too, - // or there will be hard to diagnose 0 type panics in cgen. + // or there will be hard tRo diagnose 0 type panics in cgen. node.return_type = left_type node.receiver_type = left_type @@ -3067,6 +3092,11 @@ fn (mut c Checker) type_implements(typ ast.Type, interface_type ast.Type, pos to utyp := c.unwrap_generic(typ) typ_sym := c.table.get_type_symbol(utyp) mut inter_sym := c.table.get_type_symbol(interface_type) + + // small hack for JS.Any type. Since `any` in regular V is getting deprecated we have our own JS.Any type for JS backend. + if typ_sym.name == 'JS.Any' { + return true + } if mut inter_sym.info is ast.Interface { mut generic_type := interface_type mut generic_info := inter_sym.info @@ -3105,7 +3135,8 @@ fn (mut c Checker) type_implements(typ ast.Type, interface_type ast.Type, pos to // `none` "implements" the Error interface return true } - if typ_sym.kind == .interface_ && inter_sym.kind == .interface_ { + if typ_sym.kind == .interface_ && inter_sym.kind == .interface_ && styp != 'JS.Any' + && inter_sym.name != 'JS.Any' { c.error('cannot implement interface `$inter_sym.name` with a different interface `$styp`', pos) } diff --git a/vlib/v/gen/js/builtin_types.v b/vlib/v/gen/js/builtin_types.v index 46e04d2e80..e9f4ee2354 100644 --- a/vlib/v/gen/js/builtin_types.v +++ b/vlib/v/gen/js/builtin_types.v @@ -40,7 +40,7 @@ fn (mut g JsGen) to_js_typ_val(t ast.Type) string { styp = 'new map(new Map())' } .array { - styp = '$prefix${g.sym_to_js_typ(sym)}()' + styp = 'empty_array()' } .struct_ { styp = 'new ${g.js_name(sym.name)}(${g.to_js_typ_def_val(sym.name)})' diff --git a/vlib/v/gen/js/fn.v b/vlib/v/gen/js/fn.v index cd5bf1a5f6..2019624743 100644 --- a/vlib/v/gen/js/fn.v +++ b/vlib/v/gen/js/fn.v @@ -459,6 +459,7 @@ fn (mut g JsGen) generic_fn_name(types []ast.Type, before string, is_decl bool) } fn (mut g JsGen) gen_method_decl(it ast.FnDecl, typ FnGenType) { + cur_fn_decl := g.fn_decl unsafe { g.fn_decl = &it } @@ -528,7 +529,7 @@ fn (mut g JsGen) gen_method_decl(it ast.FnDecl, typ FnGenType) { mut args := it.params g.fn_args(args, it.is_variadic) - g.write(') {') + g.writeln(') {') for i, arg in args { is_varg := i == args.len - 1 && it.is_variadic arg_name := g.js_name(arg.name) @@ -584,7 +585,7 @@ fn (mut g JsGen) gen_method_decl(it ast.FnDecl, typ FnGenType) { } } - g.fn_decl = voidptr(0) + g.fn_decl = cur_fn_decl } fn (mut g JsGen) fn_args(args []ast.Param, is_variadic bool) { @@ -609,6 +610,7 @@ fn (mut g JsGen) gen_anon_fn(mut fun ast.AnonFn) { } fun.has_gen = true it := fun.decl + cur_fn_decl := g.fn_decl unsafe { g.fn_decl = &it } @@ -672,5 +674,5 @@ fn (mut g JsGen) gen_anon_fn(mut fun ast.AnonFn) { g.dec_indent() g.writeln('}})()') - g.fn_decl = voidptr(0) + g.fn_decl = cur_fn_decl } diff --git a/vlib/v/gen/js/js.v b/vlib/v/gen/js/js.v index 7fe6037fe1..962739d153 100644 --- a/vlib/v/gen/js/js.v +++ b/vlib/v/gen/js/js.v @@ -173,11 +173,13 @@ pub fn gen(files []&ast.File, table &ast.Table, pref &pref.Preferences) string { } g.enter_namespace('main') // generate JS methods for interface methods - for _, iface_types in g.table.iface_types { + for iface_name, iface_types in g.table.iface_types { + iface := g.table.find_type(iface_name) or { panic('unreachable: interface must exist') } for ty in iface_types { sym := g.table.get_type_symbol(ty) - for method in sym.methods { - p_sym := g.table.get_type_symbol(method.params[0].typ) + + for method in iface.methods { + p_sym := g.table.get_type_symbol(ty) mname := g.js_name(p_sym.name) + '_' + method.name g.write('${g.js_name(sym.name)}.prototype.$method.name = function(') for i, param in method.params { @@ -1702,6 +1704,10 @@ fn (mut g JsGen) gen_import_stmt(it ast.Import) { } fn (mut g JsGen) gen_interface_decl(it ast.InterfaceDecl) { + if it.language != .v { + // JS interfaces do not need codegen + return + } // JS is dynamically typed, so we don't need any codegen at all // We just need the JSDoc so TypeScript type checking works g.doc.gen_interface(it) @@ -1829,6 +1835,17 @@ fn (mut g JsGen) gen_struct_decl(node ast.StructDecl) { etyp := g.typ(embed.typ) g.writeln('...${g.js_name(etyp)}.prototype,') } + for iface, iface_types in g.table.iface_types { + if iface.starts_with('JS.') { + for ty in iface_types { + sym := g.table.get_type_symbol(ty) + + if sym.name == node.name { + g.writeln('...${g.js_name(iface)}.prototype,') + } + } + } + } fns := g.method_fn_decls[name] // gen toString method fn_names := fns.map(it.name) @@ -2335,7 +2352,9 @@ fn (mut g JsGen) match_expr_sumtype(node ast.MatchExpr, is_expr bool, cond_var M g.write(' instanceof ') g.expr(branch.exprs[sumtype_index]) } else if sym.kind == .interface_ { - g.write('.val') + if !sym.name.starts_with('JS.') { + g.write('.val') + } if branch.exprs[sumtype_index] is ast.TypeNode { g.write(' instanceof ') g.expr(branch.exprs[sumtype_index]) diff --git a/vlib/v/parser/struct.v b/vlib/v/parser/struct.v index d4b7105f4f..7453e6e61c 100644 --- a/vlib/v/parser/struct.v +++ b/vlib/v/parser/struct.v @@ -450,7 +450,12 @@ fn (mut p Parser) interface_decl() ast.InterfaceDecl { name_pos := p.tok.position() p.check_for_impure_v(language, name_pos) modless_name := p.check_name() - interface_name := p.prepend_mod(modless_name).clone() + mut interface_name := '' + if language == .js { + interface_name = 'JS.' + modless_name + } else { + interface_name = p.prepend_mod(modless_name) + } generic_types, _ := p.parse_generic_types() // println('interface decl $interface_name') p.check(.lcbr) @@ -472,6 +477,7 @@ fn (mut p Parser) interface_decl() ast.InterfaceDecl { is_generic: generic_types.len > 0 generic_types: generic_types } + language: language ) if reg_idx == -1 { p.error_with_pos('cannot register interface `$interface_name`, another type with this name exists', @@ -492,8 +498,11 @@ fn (mut p Parser) interface_decl() ast.InterfaceDecl { for p.tok.kind != .rcbr && p.tok.kind != .eof { if p.tok.kind == .name && p.tok.lit.len > 0 && p.tok.lit[0].is_capital() { iface_pos := p.tok.position() - iface_name := p.tok.lit + mut iface_name := p.tok.lit iface_type := p.parse_type() + if iface_name == 'JS' { + iface_name = p.table.get_type_symbol(iface_type).name + } comments := p.eat_comments() ifaces << ast.InterfaceEmbedding{ name: iface_name