js: support JS interfaces (#12426)

pull/12441/head
playX 2021-11-11 15:36:32 +03:00 committed by GitHub
parent 015cfdb49f
commit a4c57ba56e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 245 additions and 205 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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