From f584e70cf2fe862a93c1521b9d67eb2c83bdc4d2 Mon Sep 17 00:00:00 2001 From: playX Date: Thu, 25 Nov 2021 17:49:53 +0300 Subject: [PATCH] js: add fixes for generic structures and functions; add promise API; refactor modules (#12575) --- examples/js_dom_cube/cube.js.v | 46 +- examples/js_dom_cube/index.html | 4 +- examples/js_dom_draw/draw.js.v | 6 +- vlib/js/dom/dom.js.v | 979 ++++++++++++++++++++++++++++++ vlib/js/dom/dom.v | 2 + vlib/js/js.v | 3 + vlib/js/promise/promise.js.v | 79 +++ vlib/js/promise/promise.v | 3 + vlib/js/promise/promise_test.js.v | 16 + vlib/v/gen/js/fn.v | 32 +- vlib/v/gen/js/js.v | 54 +- 11 files changed, 1170 insertions(+), 54 deletions(-) create mode 100644 vlib/js/dom/dom.js.v create mode 100644 vlib/js/dom/dom.v create mode 100644 vlib/js/js.v create mode 100644 vlib/js/promise/promise.js.v create mode 100644 vlib/js/promise/promise.v create mode 100644 vlib/js/promise/promise_test.js.v diff --git a/examples/js_dom_cube/cube.js.v b/examples/js_dom_cube/cube.js.v index 3e5758daf0..198af4b8f8 100644 --- a/examples/js_dom_cube/cube.js.v +++ b/examples/js_dom_cube/cube.js.v @@ -1,4 +1,4 @@ -import jsdom +import js.dom import math const ( @@ -198,7 +198,8 @@ const ( ) fn get_webgl() (&JS.HTMLCanvasElement, JS.WebGLRenderingContext) { - elem := jsdom.document.getElementById('myCanvas'.str) or { panic('cannot get canvas') } + JS.console.log(dom.document) + elem := dom.document.getElementById('myCanvas'.str) or { panic('cannot get canvas') } match elem { JS.HTMLCanvasElement { webgl := elem.getContext('experimental-webgl'.str, js_undefined()) or { @@ -326,21 +327,20 @@ fn animate(mut state State, time f64) { rotate_x(mut state.mo_matrix, state.phi) rotate_y(mut state.mo_matrix, state.theta) state.time_old = time - state.gl.enable(jsdom.gl_depth_test()) + state.gl.enable(dom.gl_depth_test()) state.gl.clearColor(0.5, 0.5, 0.5, 0.9) state.gl.clearDepth(1.0) state.gl.viewport(0.0, 0.0, state.canvas.width, state.canvas.height) - state.gl.clear(JS.Number(int(jsdom.gl_color_buffer_bit()) | int(jsdom.gl_depth_buffer_bit()))) + state.gl.clear(JS.Number(int(dom.gl_color_buffer_bit()) | int(dom.gl_depth_buffer_bit()))) state.gl.uniformMatrix4fv(state.pmatrix, JS.Boolean(false), state.proj_matrix.to_number_array()) state.gl.uniformMatrix4fv(state.vmatrix, JS.Boolean(false), state.view_matrix.to_number_array()) state.gl.uniformMatrix4fv(state.mmatrix, JS.Boolean(false), state.mo_matrix.to_number_array()) - state.gl.bindBuffer(jsdom.gl_element_array_buffer(), state.index_buffer) - state.gl.drawElements(jsdom.gl_triangles(), indices.len, jsdom.gl_unsigned_short(), - 0) + state.gl.bindBuffer(dom.gl_element_array_buffer(), state.index_buffer) + state.gl.drawElements(dom.gl_triangles(), indices.len, dom.gl_unsigned_short(), 0) - jsdom.window().requestAnimationFrame(fn [mut state] (time JS.Number) { + dom.window().requestAnimationFrame(fn [mut state] (time JS.Number) { animate(mut state, f64(time)) }) } @@ -349,29 +349,29 @@ fn main() { canvas, gl := get_webgl() vertex_buffer := gl.createBuffer() ? - gl.bindBuffer(jsdom.gl_array_buffer(), vertex_buffer) - gl.bufferData(jsdom.gl_array_buffer(), float32_array(vertices), jsdom.gl_static_draw()) + gl.bindBuffer(dom.gl_array_buffer(), vertex_buffer) + gl.bufferData(dom.gl_array_buffer(), float32_array(vertices), dom.gl_static_draw()) color_buffer := gl.createBuffer() ? - gl.bindBuffer(jsdom.gl_array_buffer(), color_buffer) - gl.bufferData(jsdom.gl_array_buffer(), float32_array(colors), jsdom.gl_static_draw()) + gl.bindBuffer(dom.gl_array_buffer(), color_buffer) + gl.bufferData(dom.gl_array_buffer(), float32_array(colors), dom.gl_static_draw()) index_buffer := gl.createBuffer() ? - gl.bindBuffer(jsdom.gl_element_array_buffer(), index_buffer) - gl.bufferData(jsdom.gl_element_array_buffer(), uint16_array(indices), jsdom.gl_static_draw()) + gl.bindBuffer(dom.gl_element_array_buffer(), index_buffer) + gl.bufferData(dom.gl_element_array_buffer(), uint16_array(indices), dom.gl_static_draw()) - vert_shader := gl.createShader(jsdom.gl_vertex_shader()) ? + vert_shader := gl.createShader(dom.gl_vertex_shader()) ? gl.shaderSource(vert_shader, vert_code.str) gl.compileShader(vert_shader) - if !bool(JS.Boolean(gl.getShaderParameter(vert_shader, jsdom.gl_compile_status()))) { + if !bool(JS.Boolean(gl.getShaderParameter(vert_shader, dom.gl_compile_status()))) { panic('An error occurred when compiling vertex shader: ${string(gl.getShaderInfoLog(vert_shader))}') } - frag_shader := gl.createShader(jsdom.gl_fragment_shader()) ? + frag_shader := gl.createShader(dom.gl_fragment_shader()) ? gl.shaderSource(frag_shader, frag_code.str) gl.compileShader(frag_shader) - if !bool(JS.Boolean(gl.getShaderParameter(frag_shader, jsdom.gl_compile_status()))) { + if !bool(JS.Boolean(gl.getShaderParameter(frag_shader, dom.gl_compile_status()))) { panic('An error occurred when compiling fragment shader: ${string(gl.getShaderInfoLog(frag_shader))}') } @@ -380,7 +380,7 @@ fn main() { gl.attachShader(shader_program, frag_shader) gl.linkProgram(shader_program) - if !bool(JS.Boolean(gl.getProgramParameter(shader_program, jsdom.gl_link_status()))) { + if !bool(JS.Boolean(gl.getProgramParameter(shader_program, dom.gl_link_status()))) { panic('unable to initialize the shader program: ${string(gl.getProgramInfoLog(shader_program))}') } @@ -388,15 +388,15 @@ fn main() { vmatrix := gl.getUniformLocation(shader_program, 'Vmatrix'.str) ? mmatrix := gl.getUniformLocation(shader_program, 'Mmatrix'.str) ? - gl.bindBuffer(jsdom.gl_array_buffer(), vertex_buffer) + gl.bindBuffer(dom.gl_array_buffer(), vertex_buffer) position := gl.getAttribLocation(shader_program, 'position'.str) - gl.vertexAttribPointer(position, JS.Number(3), jsdom.gl_float(), JS.Boolean(false), + gl.vertexAttribPointer(position, JS.Number(3), dom.gl_float(), JS.Boolean(false), JS.Number(0), JS.Number(0)) gl.enableVertexAttribArray(position) - gl.bindBuffer(jsdom.gl_array_buffer(), color_buffer) + gl.bindBuffer(dom.gl_array_buffer(), color_buffer) color := gl.getAttribLocation(shader_program, 'color'.str) - gl.vertexAttribPointer(color, JS.Number(3), jsdom.gl_float(), JS.Boolean(false), JS.Number(0), + gl.vertexAttribPointer(color, JS.Number(3), dom.gl_float(), JS.Boolean(false), JS.Number(0), JS.Number(0)) gl.enableVertexAttribArray(color) gl.useProgram(shader_program) diff --git a/examples/js_dom_cube/index.html b/examples/js_dom_cube/index.html index ce0e50d29d..60a730b19c 100644 --- a/examples/js_dom_cube/index.html +++ b/examples/js_dom_cube/index.html @@ -1,4 +1,6 @@ - + \ No newline at end of file diff --git a/examples/js_dom_draw/draw.js.v b/examples/js_dom_draw/draw.js.v index 7add029c06..f14cd1dbd4 100644 --- a/examples/js_dom_draw/draw.js.v +++ b/examples/js_dom_draw/draw.js.v @@ -1,4 +1,4 @@ -import jsdom +import js.dom fn get_canvas(elem JS.HTMLElement) &JS.HTMLCanvasElement { match elem { @@ -30,8 +30,8 @@ mut: } fn main() { - window := jsdom.window() - document := jsdom.document + window := dom.window() + document := dom.document clear_btn := document.getElementById('clearButton'.str) ? canvas_elem := document.getElementById('canvas'.str) ? canvas := get_canvas(canvas_elem) diff --git a/vlib/js/dom/dom.js.v b/vlib/js/dom/dom.js.v new file mode 100644 index 0000000000..59c6b88e7f --- /dev/null +++ b/vlib/js/dom/dom.js.v @@ -0,0 +1,979 @@ +module dom + +pub struct CanvasRenderingContext2DSettings { +pub mut: + alpha bool + color_space string + desynchronized bool + will_read_frequently bool +} + +pub fn (settings CanvasRenderingContext2DSettings) to_js() JS.Any { + mut object := JS.Any{} + #object = { alpha: settings.alpha, colorSpace: settings.color_space.str, desynchronized: settings.desynchronized.val, willReadFrequently: settings.will_read_frequently.val }; + + return object +} + +pub interface JS.DOMMatrix2DInit { +mut: + a JS.Number + b JS.Number + c JS.Number + d JS.Number + e JS.Number + f JS.Number + m11 JS.Number + m12 JS.Number + m21 JS.Number + m22 JS.Number + m41 JS.Number + m42 JS.Number +} + +pub interface JS.DOMMatrixInit { + JS.DOMMatrix2DInit + is2D JS.Boolean +mut: + m13 JS.Number + m14 JS.Number + m23 JS.Number + m24 JS.Number + m31 JS.Number + m32 JS.Number + m33 JS.Number + m34 JS.Number + m43 JS.Number + m44 JS.Number +} + +pub interface JS.DOMMatrix { + is2D JS.Boolean + isIdentity JS.Boolean + flipX() JS.DOMMatrix + flipY() JS.DOMMatrix + inverse() JS.DOMMatrix + multiply(other JS.DOMMatrix) JS.DOMMatrix + rotate(rotX JS.Number, rotY JS.Number, rotZ JS.Number) JS.DOMMatrix + rotateAxisAngle(x JS.Number, y JS.Number, z JS.Number, angle JS.Number) JS.DOMMatrix + scale(scaleX JS.Number, scaleY JS.Number, scaleZ JS.Number, originX JS.Number, originY JS.Number, originZ JS.Number) JS.DOMMatrix + scale3d(scale JS.Number, originX JS.Number, originY JS.Number, originZ JS.Number) JS.DOMMatrix + skewX(sx JS.Number) JS.DOMMatrix + skewY(sy JS.Number) JS.DOMMatrix + translate(tx JS.Number, ty JS.Number, tz JS.Number) JS.DOMMatrix + invertSelf() JS.DOMMatrix + multiplySelf(other JS.DOMMatrix) JS.DOMMatrix + preMultiplySelf(other JS.DOMMatrix) JS.DOMMatrix + rotateAxisAngleSelf(x JS.Number, y JS.Number, z JS.Number, angle JS.Number) JS.DOMMatrix + rotateFromVectorSelf(x JS.Number, y JS.Number) JS.DOMMatrix + rotateSelf(rotX JS.Number, rotY JS.Number, rotZ JS.Number) JS.DOMMatrix + scale3dSelf(scale JS.Number, originX JS.Number, originY JS.Number, originZ JS.Number) JS.DOMMatrix + scaleSelf(scaleX JS.Number, scaleY JS.Number, scaleZ JS.Number, originX JS.Number, originY JS.Number, originZ JS.Number) JS.DOMMatrix + toString() JS.String +mut: + a JS.Number + b JS.Number + c JS.Number + d JS.Number + e JS.Number + f JS.Number + 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 +} + +pub type SVGMatrix = JS.DOMMatrix +pub type WebKitCSSMatrix = JS.DOMMatrix + +[use_new] +pub fn JS.DOMMatrix.prototype.constructor(init JS.Array) JS.DOMMatrix + +pub interface JS.DOMPoint { + matrixTransform(matrix JS.DOMMatrix) JS.DOMPoint +mut: + w JS.Number + x JS.Number + y JS.Number + z JS.Number +} + +[use_new] +pub fn JS.DOMPoint.prototype.constructor(x JS.Number, y JS.Number, z JS.Number, w JS.Number) JS.DOMPoint + +pub interface JS.DOMQuad { + p1 JS.DOMPoint + p2 JS.DOMPoint + p3 JS.DOMPoint + p4 JS.DOMPoint + getBounds() JS.DOMRect +} + +[use_new] +pub fn JS.DOMQuad.prototype.constructor(p1 JS.DOMPoint, p2 JS.DOMPoint, p3 JS.DOMPoint, p4 JS.DOMPoint) JS.DOMQuad +pub fn JS.DOMQuad.fromQuad(other JS.DOMQuad) JS.DOMQuad +pub fn JS.DOMQuad.fromRect(other JS.DOMRect) JS.DOMRect + +pub interface JS.DOMRect { + bottom JS.Number + left JS.Number + right JS.Number + top JS.Number +mut: + height JS.Number + width JS.Number + x JS.Number + y JS.Number +} + +[use_new] +pub fn JS.DOMRect.prototype.constructor(x JS.Number, y JS.Number, width JS.Number, height JS.Number) JS.DOMRect + +pub interface JS.DOMStringList { + length JS.Number + contains(JS.String) JS.Boolean + item(index JS.Number) ?JS.String +} + +pub interface JS.DOMRectList { + length JS.Number + contains(JS.String) JS.Boolean + item(index JS.Number) ?JS.Rect +} + +pub type DOMTokenListForEachCb = fn (JS.String, JS.Number, JS.DOMTokenList) + +pub interface JS.DOMTokenList { + length JS.Number + toString() JS.String + add(tokens ...JS.Any) ?JS.Any + contains(token JS.String) JS.Boolean + item(index JS.Number) ?JS.String + remove(tokens ...JS.Any) ?JS.Any + replace(token JS.String, newToken JS.String) JS.Boolean + supports(token JS.String) JS.Boolean + toggle(token JS.String, force JS.Boolean) JS.Boolean + forEach(cb DOMTokenListForEachCb, thisArg JS.Any) +mut: + value JS.String +} + +pub struct JS.EventListenerOptions { + capture bool +} + +pub interface JS.EventTarget { + addEventListener(event JS.String, cb EventCallback, options JS.EventListenerOptions) + dispatchEvent(event JS.Event) JS.Boolean + removeEventListener(event JS.String, cb EventCallback, options JS.EventListenerOptions) +} + +pub interface JS.Node { + JS.EventTarget + baseURI JS.String + childNodes JS.Any + firstChild JS.ChildNode + isConnected JS.Boolean + lastChild JS.ChildNode + nextSibling JS.ChildNode + nodeName JS.String + nodeType JS.Number + ownerDocument JS.Document + parentElement JS.HTMLElement + parentNode JS.ParentNode + previousSibling JS.ChildNode + appendChild(node JS.Node) JS.Node + cloneNode(deep JS.Boolean) JS.Node + compareDocumentPosition(other JS.Node) JS.Number + contains(other JS.Node) JS.Boolean + getRootNode(composed JS.Boolean) JS.Node + hasChildNodes() JS.Boolean + insertBefore(node JS.Node, child JS.Node) JS.Node + isEqualNode(otherNode JS.Node) JS.Boolean + isSameNode(otherNode JS.Node) JS.Boolean + lookupPrefix(namespace JS.String) JS.String + normalize() + removeChild(child JS.Node) JS.Node + replaceChild(node JS.Node, child JS.Node) JS.Npde +mut: + nodeValue JS.String + textContent JS.String +} + +pub interface JS.ChildNode { + JS.Node + after(nodes ...JS.Any) + before(nodes ...JS.Any) + remove() + replaceWith(nodes ...JS.Any) +} + +pub interface JS.ParentNode { + JS.Node + childElementCount JS.Number + children JS.HTMLCollection +} + +pub type WindowProxy = JS.Window + +pub interface JS.Document { + JS.Node + all JS.HTMLAllCollection + anchros JS.HTMLCollection + applets JS.HTMLCollection + characterSet JS.String + charset JS.String + compatMode JS.String + contentType JS.String + documentURI JS.String + documentElement JS.HTMLElement + hidden JS.Boolean + head JS.HTMLHeadElement + fullscreenEnabled JS.Boolean + fullscreen JS.Boolean + lastModified JS.String + inputEncoding JS.String + implementation JS.DOMImplementation + doctype JS.DocumentType + embeds JS.HTMLCollection + forms JS.HTMLCollection + getElementById(id JS.String) ?JS.HTMLElement + adoptNode(node JS.Node) JS.Node + close() + createAttribute(name JS.String) JS.Attr + createAttributeNS(namespace JS.String, qualifiedName JS.String) JS.Attr + createCDATASection(data JS.String) JS.CDATASection + createComment(data JS.String) JS.Comment + createDocumentFragment() JS.DocumentFragment + createElement(tagName JS.String) JS.HTMLElement + createElementNS(namespaceURI JS.String, qualifiedName JS.String) JS.Element + createEvent(event JS.String) JS.Event + createTextNode(data JS.String) JS.Text + elementFromPoint(x JS.Number, y JS.Number) ?JS.Element + elementsFromPoint(x JS.Number, y JS.Number) JS.Array + execCommand(commandId JS.String, showUI JS.Boolean, value JS.String) JS.Boolean + hasFocus() JS.Boolean + open(url JS.String, name JS.String, features JS.String) ?WindowProxy + queryCommandEnabled(commandId JS.String) JS.Boolean + queryCommandIndeterm(commandId JS.String) JS.Boolean + queryCommandState(commandId JS.String) JS.String + write(text ...JS.Any) + writeln(text ...JS.Any) +mut: + bgColor JS.String + fgColor JS.String + body JS.HTMLElement + cookie JS.String + domain JS.String + designMode JS.String + dir JS.String + vlinkColor JS.String +} + +pub fn document_url(doc JS.Document) JS.String { + mut url := JS.String{} + #url = doc.URL; + + return url +} + +pub interface JS.Element { + JS.Node + classList JS.DOMTokenList + clientHeight JS.Number + clientLeft JS.Number + clientTop JS.Number + clientWidth JS.Number + localName JS.String + namespaceURI JS.String + ownerDocument JS.Document + part JS.DOMTokenList + prefix JS.String + scrollHeight JS.Number + scrollWidth JS.Number + tagName JS.String + closest(selector JS.String) ?JS.Element + getAttribute(qualifiedName JS.String) ?JS.String + getAttributeNS(namespace JS.String, localName JS.String) ?JS.String + getAttributeNames() JS.Array + getClientRects() JS.DOMRectList + getBoundingClientRect() JS.DOMRect + scrollTo(x JS.Number, y JS.Number) + scroll(x JS.Number, y JS.Number) + scrollBy(x JS.Number, y JS.Number) + toggleAttribute(qualifiedName JS.String, force JS.Boolean) JS.Boolean + getElementsByClassName(className JS.String) JS.HTMLCollection + getElementsByTagName(qualifiedName JS.String) JS.HTMLCollection + getEelementsByTagNameNS(namespaecURI JS.String, localName JS.String) JS.HTMLCollection + hasAttribute(qualifiedName JS.String) JS.Boolean + hasAttributeNS(namespace JS.String, localName JS.String) JS.Boolean + hasAttributes() JS.Boolean + hasPointerCapture(pointerId JS.Number) JS.Boolean + matches(selectors JS.String) JS.Boolean +mut: + className JS.String + id JS.String + onfullscreenchange fn (this JS.Element, ev JS.Event) JS.Any + onfullscreenerror fn (this JS.Element, ev JS.Event) JS.Any + outerHTML JS.String + scrollLeft JS.Number + scrollTop JS.Number + slot JS.String +} + +pub const ( + document = JS.Document{} +) + +pub fn window() JS.Window { + mut x := JS.Any(voidptr(0)) + #x = window; + + return x +} + +fn init() { + #js__dom__document = document; +} + +pub type EventCallback = fn (JS.Event) + +// event_listener returns proper listener callback. This function is useful when you need access to `this` value +// that is EventTarget. When you need access only to Event itself you can just use `fn (JS.Event)` as listener. +pub fn event_listener(callback fn (JS.EventTarget, JS.Event)) EventCallback { + return fn [callback] (event JS.Event) { + mut target := JS.EventTarget(voidptr(0)) + #target = this; + callback(target, event) + } +} + +pub interface JS.HTMLCollection { + length JS.Number + item(idx JS.Number) ?JS.Any + namedItem(name JS.String) ?JS.Any +} + +pub interface JS.HTMLElement { + JS.Element + accessKeyLabel JS.String + offsetHeight JS.Number + offsetLeft JS.Number + offsetParent JS.Any + offsetTop JS.Number + offsetWidth JS.Number + click() +mut: + accessKey JS.String + autocapitalize JS.String + dir JS.String + draggable JS.Boolean + hidden JS.Boolean + innerText JS.String + lang JS.String + outerText JS.String + spellcheck JS.Boolean + title JS.String + translate JS.Boolean +} + +pub fn JS.HTMLElement.prototype.constructor() JS.HTMLElement + +pub interface JS.HTMLEmbedElement { + getSVGDocument() ?JS.Document +mut: + align JS.String + height JS.String + src JS.String + width JS.String +} + +pub fn html_embed_type(embed JS.HTMLEmbedElement) JS.String { + mut str := JS.String{} + #str = embed.type + + return str +} + +pub fn JS.HTMLEmbedElement.prototype.constructor() JS.HTMLEmbedElement + +pub type CanvasContext = JS.CanvasRenderingContext2D + | JS.WebGL2RenderingContext + | JS.WebGLRenderingContext + +pub interface JS.HTMLCanvasElement { + JS.HTMLElement + getContext(contextId JS.String, options JS.Any) ?CanvasContext +mut: + height JS.Number + width JS.Number +} + +pub type FillStyle = JS.CanvasGradient | JS.CanvasPattern | JS.String + +pub interface JS.CanvasRenderingContext2D { + canvas JS.HTMLCanvasElement + beginPath() + clip(path JS.Path2D, fillRule JS.String) + fill(path JS.Path2D, fillRule JS.String) + isPointInPath(path JS.Path2D, x JS.Number, y JS.Number, fillRule JS.String) JS.Boolean + isPointInStroke(path JS.Path2D, x JS.Number, y JS.Number) JS.Boolean + stoke(path JS.Path2D) + createLinearGradient(x0 JS.Number, y0 JS.Number, x1 JS.Number, y1 JS.Number) JS.CanvasGradient + createRadialGradient(x0 JS.Number, y0 JS.Number, r0 JS.Number, x1 JS.Number, y1 JS.Number, r1 JS.Number) JS.CanvasGradient + createPattern(image JS.CanvasImageSource, repetition JS.String) ?JS.CanvasPattern + arc(x JS.Number, y JS.Number, radius JS.Number, startAngle JS.Number, endAngle JS.Number, counterclockwise JS.Boolean) + arcTo(x1 JS.Number, y1 JS.Number, x2 JS.Number, y2 JS.Number, radius JS.Number) + bezierCurveTo(cp1x JS.Number, cp1y JS.Number, cp2x JS.Number, cp2y JS.Number, x JS.Number, y JS.Number) + closePath() + ellipse(x JS.Number, y JS.Number, radiusX JS.Number, radiusY JS.Number, rotation JS.Number, startAngle JS.Number, endAngle JS.Number, counterclockwise JS.Boolean) + lineTo(x JS.Number, y JS.Number) + moveTo(x JS.Number, y JS.Number) + quadraticCurveTo(cpx JS.Number, cpy JS.Number, x JS.Number, y JS.Number) + rect(x JS.Number, y JS.Number, w JS.Number, h JS.Number) + getLineDash() JS.Array + setLineDash(segments JS.Array) + clearRect(x JS.Number, y JS.Number, w JS.Number, h JS.Number) + fillRect(x JS.Number, y JS.Number, w JS.null, h JS.Number) + strokeRect(x JS.Number, y JS.Number, w JS.Number, h JS.Number) + getTransformt() JS.DOMMatrix + resetTransform() + rotate(angle JS.Number) + scale(x JS.Number, y JS.Number) + setTransform(matrix JS.DOMMatrix) + transform(a JS.Number, b JS.Number, c JS.Number, d JS.Number, e JS.Number, f JS.Number) + translate(x JS.Number, y JS.Number) + drawFocusIfNeeded(path JS.Path2D, element JS.Element) + stroke() +mut: + lineCap JS.String + lineDashOffset JS.Number + lineJoin JS.String + lineWidth JS.Number + miterLimit JS.Number + fillStyle FillStyle + strokeStyle FillStyle + globalAlpha JS.Number + globalCompositeOperation JS.String +} + +pub interface JS.CanvasGradient { + addColorStop(offset JS.Number, color JS.String) +} + +pub interface JS.CanvasPattern { + setTransform(transform JS.DOMMatrix) +} + +pub type OnDeviceMotion = fn (ev JS.DeviceMotionEvent) JS.Any + +pub type OnDeviceOrientation = fn (ev JS.DeviceOrientationEvent) JS.Any + +pub fn on_device_motion(cb fn (win JS.Window, ev JS.DeviceMotionEvent) JS.Any) OnDeviceMotion { + clos := fn [cb] (ev JS.DeviceMotionEvent) JS.Any { + mut win := JS.Any(voidptr(0)) + #win = this; + + return cb(win, ev) + } + return clos +} + +pub fn on_device_orientation(cb fn (win JS.Window, ev JS.DeviceOrientationEvent) JS.Any) OnDeviceOrientation { + clos := fn [cb] (ev JS.DeviceOrientationEvent) JS.Any { + mut win := JS.Any(voidptr(0)) + #win = this; + + return cb(win, ev) + } + return clos +} + +pub type AnimationFrameCallback = fn (JS.Number) + +pub interface JS.Window { + JS.EventTarget + closed JS.Boolean + devicePixelRatio JS.Number + document JS.Document + frameElement JS.Element + innerHeight JS.Number + innerWidth JS.Number + length JS.Number + outerHeight JS.Number + outerWidth JS.Number + screenLeft JS.Number + screenTop JS.Number + screenX JS.Number + screenY JS.Number + scrollX JS.Number + scrollY JS.Number + alert(message JS.Any) + blur() + cancelIdleCallback(handle JS.Number) + captureEvents() + close() + confirm(message JS.String) JS.Boolean + focus() + moveBy(x JS.Number, y JS.Number) + moveTo(x JS.Number, y JS.Number) + print() + prompt(message JS.String, default_ JS.String) ?JS.String + stop() + resizeBy(x JS.Number, y JS.Number) + resizeTo(width JS.Number, height JS.Number) + scroll(x JS.Number, y JS.Number) + scrollBy(x JS.Number, y JS.Number) + scrollTo(x JS.Number, y JS.Number) + requestAnimationFrame(callback AnimationFrameCallback) +mut: + name string + opener JS.Any + ondevicemotion OnDeviceMotion + ondeviceorientation OnDeviceOrientation +} + +pub interface JS.Path2D {} + +pub struct JS.DeviceMotionEventAcceleration { + x JS.Number + y JS.Number + z JS.Number +} + +pub struct JS.DeviceMotionEventRotationRate { + alpha JS.Number + beta JS.Number + gamma JS.Number +} + +pub interface JS.DeviceMotionEvent { + JS.Event + interval JS.Number + acceleration JS.DeviceMotionEventAcceleration + accelerationIncludingGravity JS.DeviceMotionEventAcceleration + rotationRate JS.DeviceMotionEventRotationRate +} + +pub interface JS.DeviceOrientationEvent { + JS.Event + absolute JS.Boolean + alpha JS.Number + beta JS.Number + gamma JS.Number +} + +pub interface JS.DocumentType { + JS.Node + JS.ChildNode + name JS.String + ownerDocument JS.Document + publicId JS.String + systemId JS.String +} + +[single_impl] +pub interface JS.WebGLProgram {} + +[single_impl] +pub interface JS.WebGLShader {} + +[single_impl] +pub interface JS.WebGLBuffer {} + +[single_impl] +pub interface JS.WebGLFramebuffer {} + +[single_impl] +pub interface JS.WebGLRenderbuffer {} + +[single_impl] +pub interface JS.WebGLTexture {} + +[single_impl] +pub interface JS.WebGLUniformLocation {} + +[single_impl] +pub interface JS.WebGLVertexArrayObject {} + +pub interface JS.WebGLRenderingContext { + canvas JS.HTMLCanvasElement + drawingBufferHeight JS.Number + drawingBufferWidth JS.Number + activeTexture(texture JS.Number) + attachShader(program JS.WebGLProgram, shader JS.WebGLProgram) + linkProgram(program JS.WebGLProgram) + bindAttribLocation(program JS.WebGLProgram, index JS.Number, name JS.String) + bindBuffer(target JS.Number, buffer JS.WebGLBuffer) + bindFramebuffer(target JS.Number, buffer JS.WebGLFrameBuffer) + bindRenderbuffer(target JS.Number, renderbuffer JS.WebGLRenderbuffer) + bindTexture(target JS.Number, texture JS.WebGLTexture) + clear(mask JS.Number) + clearColor(red JS.Number, green JS.Number, blue JS.Number, alpha JS.Number) + clearDepth(depth JS.Number) + clearStencil(s JS.Number) + colorMask(red JS.Boolean, green JS.Boolean, blue JS.Boolean, alpha JS.Boolean) + compileShader(shader JS.WebGLShader) + createBuffer() ?JS.WebGLBuffer + createFramebuffer() ?JS.WebGLFrameBuffer + createProgram() ?JS.WebGLProgram + createRenderbuffer() ?JS.WebGLRenderbuffer + createShader(typ JS.Number) ?JS.WebGLShader + createTexture() ?JS.WebGLTexture + cullFace(mode JS.Number) + deleteBuffer(buffer JS.WebGLBuffer) + deleteFramebuffer(buffer JS.WebGLFrameBuffer) + deleteProgram(program JS.WebGLProgram) + deleteRenderbuffer(buffer JS.WebGLRenderbuffer) + deleteShader(shader JS.WebGLShader) + deleteTexture(texture JS.WebGLTexture) + depthFunc(func JS.Number) + depthMask(flag JS.Boolean) + depthRange(zNear JS.Number, zFar JS.Number) + detachShader(program JS.WebGLProgram, shader JS.WebGLShader) + disable(cap JS.Number) + disableVertexAttribArray(index JS.Number) + drawArrays(mode JS.Number, first JS.Number, count JS.Number) + drawElements(mode JS.Number, count JS.Number, typ JS.Number, offset JS.Number) + enable(cap JS.Number) + enableVertexAttribArray(index JS.Number) + finish() + flush() + framebufferRenderbuffer(target JS.Number, attachment JS.Number, renderbuffertarget JS.Number, renderbuffer JS.WebGLRenderbuffer) + framebufferTexture2D(target JS.Number, attachment JS.Number, textarget JS.Number, texture JS.WebGLTexture, level JS.Number) + frontFace(mode JS.Number) + generateMipmap(target JS.Number) + getError() JS.Number + getExtension(name JS.String) JS.Any + getParameter(name JS.Number) JS.Any + getProgramParameter(program JS.WebGLProgram, pname JS.Number) JS.Any + getShaderSource(shader JS.WebGLShader) ?JS.String + bufferData(target JS.Number, data JS.TypedArray, usage JS.Number) + shaderSource(shader JS.WebGLShader, source JS.String) + getShaderParameter(shader JS.WebGLShader, pname JS.Number) JS.Any + vertexAttribPointer(index JS.Number, size JS.Number, typ JS.Number, normalized JS.Boolean, stride JS.Number, offset JS.Number) + getAttribLocation(program JS.WebGLProgram, name JS.String) JS.Number + useProgram(program JS.WebGLProgram) + getUniformLocation(program JS.WebGLProgram, name JS.String) ?JS.WebGLUniformLocation + uniformMatrix2fv(location JS.WebGLUniformLocation, transpose JS.Boolean, value JS.Array) + uniformMatrix3fv(location JS.WebGLUniformLocation, transpose JS.Boolean, value JS.Array) + uniformMatrix4fv(location JS.WebGLUniformLocation, transpose JS.Boolean, value JS.Array) + getProgramInfoLog(program JS.WebGLProgram) JS.String + getShaderInfoLog(shader JS.WebGLShader) JS.String + viewport(x JS.Number, y JS.Number, width JS.Number, height JS.Number) + scissor(x JS.Number, y JS.Number, width JS.Number, height JS.Number) + stencilFunc(func JS.Number, ref JS.Number, mask JS.Number) + stencilFuncSeparate(face JS.Number, func JS.Number, ref JS.Number, mask JS.Number) + stencilMask(mask JS.Number) + stencilMaskSeparate(face JS.Number, mask JS.Number) + stencilOp(fail JS.Number, zfail JS.Number, zpass JS.Number) + stencilOpSeparate(face JS.Number, fail JS.Number, zfail JS.Number, zpass JS.Number) + texParameterf(target JS.Number, pname JS.Number, param JS.Number) + texParameteri(target JS.Number, pname JS.Number, param JS.Number) + uniform1f(location JS.WebGLUniformLocation, x JS.Number) + uniform1i(location JS.WebGLUniformLocation, x JS.Number) + uniform2f(location JS.WebGLUniformLocation, x JS.Number, y JS.Number) + uniform2i(location JS.WebGLUniformLocation, x JS.Number, y JS.Number) + uniform3f(location JS.WebGLUniformLocation, x JS.Number, y JS.Number, z JS.Number) + uniform3i(location JS.WebGLUniformLocation, x JS.Number, y JS.Number, z JS.Number) + uniform4f(location JS.WebGLUniformLocation, x JS.Number, y JS.Number, z JS.Number, w JS.Number) + uniform4i(location JS.WebGLUniformLocation, x JS.Number, y JS.Number, z JS.Number, w JS.Number) + validateProgram(program JS.WebGLProgram) + vertexAttrib1f(index JS.Number, x JS.Number) + vertexAttrib1fv(index JS.Number, values JS.Array) + vertexAttrib2f(index JS.Number, x JS.Number, y JS.Number) + vertexAttrib2fv(index JS.Number, x JS.Number, y JS.Number, values JS.Array) + vertexAttrib3f(index JS.Number, x JS.Number, y JS.Number, z JS.Number) + vertexAttrib3fv(index JS.Number, x JS.Number, y JS.Number, z JS.Number, values JS.Array) + vertexAttrib4f(index JS.Number, x JS.Number, y JS.Number, z JS.Number, w JS.Number) + vertexAttrib4fv(index JS.Number, x JS.Number, y JS.Number, z JS.Number, w JS.Number, values JS.Array) + bufferSubData(target JS.Number, offset JS.Number, data JS.TypedArray) + compressedTexImage2D(target JS.Number, level JS.Number, internalformat JS.Number, width JS.Number, height JS.Number, border JS.Number, data JS.TypedArray) + compressedTexSubImage2D(target JS.Number, level JS.Number, xoffset JS.Number, yoffset JS.Number, width JS.Number, height JS.Number, format JS.Number, data JS.TypedArray) + readPixels(x JS.Number, y JS.Number, width JS.Number, height JS.Number, format JS.Number, typ JS.Number, border JS.Number, pixels JS.TypedArray) + texImage2D(target JS.Number, level JS.Number, internalformat JS.Number, format JS.Number, source JS.Node) +} + +pub interface JS.WebGL2RenderingContext { + JS.WebGLRenderingContext +} + +pub fn gl_vertex_shader() JS.Number { + mut num := JS.Number{} + #num = WebGLRenderingContext.VERTEX_SHADER; + + return num +} + +pub fn gl_fragment_shader() JS.Number { + mut num := JS.Number{} + #num = WebGLRenderingContext.FRAGMENT_SHADER; + + return num +} + +pub fn gl_element_array_buffer() JS.Number { + mut num := JS.Number{} + #num = WebGLRenderingContext.ELEMENT_ARRAY_BUFFER; + + return num +} + +pub fn gl_array_buffer() JS.Number { + mut num := JS.Number{} + #num = WebGLRenderingContext.ARRAY_BUFFER; + + return num +} + +pub fn gl_color_buffer_bit() JS.Number { + mut num := JS.Number{} + #num = WebGLRenderingContext.COLOR_BUFFER_BIT; + + return num +} + +pub fn gl_depth_buffer_bit() JS.Number { + mut num := JS.Number{} + #num = WebGLRenderingContext.COLOR_BUFFER_BIT; + + return num +} + +pub fn gl_triangles() JS.Number { + mut num := JS.Number{} + #num = WebGLRenderingContext.TRIANGLES; + + return num +} + +pub fn gl_unsigned_short() JS.Number { + mut num := JS.Number{} + #num = WebGLRenderingContext.UNSIGNED_SHORT; + + return num +} + +pub fn gl_static_draw() JS.Number { + mut num := JS.Number{} + #num = WebGLRenderingContext.STATIC_DRAW; + + return num +} + +pub fn gl_link_status() JS.Number { + mut num := JS.Number{} + #num = WebGLRenderingContext.LINK_STATUS; + + return num +} + +pub fn gl_compile_status() JS.Number { + mut num := JS.Number{} + #num = WebGLRenderingContext.COMPILE_STATUS; + + return num +} + +pub fn gl_float() JS.Number { + mut num := JS.Number{} + #num = WebGLRenderingContext.FLOAT; + + return num +} + +pub fn gl_depth_test() JS.Number { + mut num := JS.Number{} + #num = WebGLRenderingContext.DEPTH_TEST; + + return num +} + +// Event is an event which takes place in the DOM. +pub interface JS.Event { + JS.EventTarget + bubbles JS.Boolean + cancelable JS.Boolean + composed JS.Boolean + currentTarget JS.EventTarget + defaultPrevented JS.Boolean + eventPhase JS.Number + isTrusted JS.Boolean + srcElement JS.EventTarget + timeStamp JS.DOMHighResTimeStamp // composedPath returns the invocation target objects of event's path. + composedPath() JS.Array + initEvent(typ JS.String, bubbles JS.Boolean, cancelable JS.Boolean) + preventDefault() + stopImmediatePropagation() + stopPropagation() +mut: + returnValue JS.Boolean +} + +pub fn event_type(ev JS.Event) string { + res := '' + #res.str = ev.type; + + return res +} + +pub fn create_event(typ string, bubbles bool, cancelable bool, composed bool) JS.Event { + mut ev := JS.Event(voidptr(0)) + #ev = new Event(typ.str,bubbles.val,cancelable.val,composed.val); + + return ev +} + +pub interface JS.ErrorEvent { + JS.Event + colno JS.Number + error JS.Number + filename JS.Number + lineno JS.Number + message JS.String +} + +[use_new] +pub fn JS.ErrorEvent.prototype.constructor(typ JS.String) JS.ErrorEvent + +pub interface JS.UIEvent { + JS.Event + detail JS.Number + view JS.Any +} + +[use_new] +pub fn JS.UIEvent.prototype.constructor(typ JS.String, dict JS.UIEventDict) JS.UIEvent + +pub struct JS.EventInit { + bubbles JS.Boolean + cancelable JS.Boolean + composed JS.Boolean +} + +pub struct JS.UIEventInitDict { + bubbles JS.Boolean + cancelable JS.Boolean + composed JS.Boolean + detail JS.Number + view JS.Any + which JS.Number +} + +pub interface JS.MouseEvent { + JS.UIEvent + altKey JS.Boolean + button JS.Number + buttons JS.Number + clientX JS.Number + clientY JS.Number + ctrlKey JS.Number + metaKey JS.Number + movementX JS.Number + movementY JS.Number + offsetX JS.Number + offsetY JS.Number + pageX JS.Number + pageY JS.Number + relatedTarget JS.Any + screenX JS.Number + screenY JS.Number + shiftKey JS.Boolean + x JS.Number + y JS.Number + getModifierState(keyArg JS.String) JS.Boolean +} + +pub interface JS.PointerEvent { + JS.MouseEvent + height JS.Number + isPrimary JS.Boolean + pointerId JS.Number + pointerType JS.String + pressure JS.Number + tangentialPressure JS.Number + tiltX JS.Number + tiltY JS.Number + twist JS.Number + width JS.Number + getCoalescedEvents() JS.Array + getPredictedEvents() JS.Array +} + +pub interface JS.Gamepad { + axes JS.Array // Array + buttons JS.Array // Array + connected JS.Boolean + hapticActuators JS.Array // Array + id JS.String + index JS.Number + mapping JS.String + timestamp JS.Number +} + +[single_impl] +pub interface JS.GamepadButton { + pressed JS.Boolean + touched JS.Boolean + value JS.Number +} + +[single_impl] +pub interface JS.GamepadHapticActuator { +} + +pub interface JS.GamepadEvent { + JS.Event + gamepad JS.Gamepad +} + +pub interface JS.HashChangeEvent { + JS.Event + newURL JS.String + oldURL JS.String +} + +pub interface JS.MessageEvent { + JS.Event + data JS.Any + lastEventId JS.String + origin JS.String + ports JS.Array + source JS.Any +} + +pub interface JS.MessagePort { + JS.EventTarget + close() + portMessage(message JS.Any, transfer JS.Array) + start() +} + +pub interface JS.PageTransitionEvent { + JS.Event + persisted JS.Boolean +} + +pub interface JS.PopStateEvent { + JS.Event + state JS.Any +} + +pub interface JS.ProgressEvent { + lenghtComputable JS.Boolean + loaded JS.Number + target JS.Any + total JS.Number +} diff --git a/vlib/js/dom/dom.v b/vlib/js/dom/dom.v new file mode 100644 index 0000000000..2633432a40 --- /dev/null +++ b/vlib/js/dom/dom.v @@ -0,0 +1,2 @@ +// DOM API wrapper +module dom diff --git a/vlib/js/js.v b/vlib/js/js.v new file mode 100644 index 0000000000..9ae5b29e04 --- /dev/null +++ b/vlib/js/js.v @@ -0,0 +1,3 @@ +// Module containing various JS APIs wrappers. + +module js diff --git a/vlib/js/promise/promise.js.v b/vlib/js/promise/promise.js.v new file mode 100644 index 0000000000..2ca539fc9d --- /dev/null +++ b/vlib/js/promise/promise.js.v @@ -0,0 +1,79 @@ +module promise + +pub interface JS.Promise { + then(onFullfilled JS.Any, onRejected JS.Any) + catch(onCatch JS.Any) JS.Promise + finally(callback JS.Any) JS.Promise +} + +[use_new] +pub fn JS.Promise.prototype.constructor(JS.Any) JS.Promise +pub fn JS.Promise.reject(JS.Any) JS.Promise +pub fn JS.Promise.resolve(JS.Any) JS.Promise +pub fn JS.Promise.race(JS.Array) JS.Promise + +// The Promise object represents the eventual completion (or failure) +// of an asynchronous operation and its resulting value. +pub struct Promise { +mut: + promise JS.Promise [noinit] +} + +pub fn new(executor fn (resolve fn (T), reject fn (E))) Promise { + promise := JS.Promise.prototype.constructor(executor) + return Promise{promise} +} + +pub fn (p Promise) then(on_fullfilled fn (T), on_rejected fn (E)) { + p.promise.then(on_fullfilled, on_rejected) +} + +// catch method returns a Promise and deals with rejected cases only. +pub fn (p Promise) catch(callback fn (error JS.Any)) Promise { + promise := p.promise.catch(callback) + return Promise{promise} +} + +pub fn (p Promise) finally(callback fn ()) Promise { + promise := p.promise.finally(callback) + return Promise{promise} +} + +// reject returns promise which was rejected because of specified error +pub fn reject(error E) Promise { + promise := JS.Promise.reject(error) + return Promise{promise} +} + +// resolve returns promise which was resolved with specified value +pub fn resolve(result T) Promise { + promise := JS.Promise.resolve(error) + return Promise{promise} +} + +// race returns returns a promise that fulfills or rejects as soon as one of +// the promises in an iterable fulfills or rejects, with the value or reason from that promise. +pub fn race(promises []Promise) Promise { + promises_ := JS.Array.prototype.constructor() + + for elem in promises { + promises_.push(elem.promise) + } + + promise := JS.Promise.race(promises_) + return Promise{promise} +} + +pub fn JS.Promise.all(JS.Array) JS.Promise +pub fn JS.Promise.allSettled(JS.Array) JS.Promise + +/* +// all takes an iterable of promises as an input, and returns a single Promise that resolves to an array of +// the results of the input promises +pub fn all(array []JS.Promise) Promise { + mut promise := JS.Promise(JS.Any(voidptr(0))) + #promise = Promise.all(array.arr.arr); + + return Promise{promise} +} +*/ diff --git a/vlib/js/promise/promise.v b/vlib/js/promise/promise.v new file mode 100644 index 0000000000..ba48de86a9 --- /dev/null +++ b/vlib/js/promise/promise.v @@ -0,0 +1,3 @@ +// Promise API wrapper + +module promise diff --git a/vlib/js/promise/promise_test.js.v b/vlib/js/promise/promise_test.js.v new file mode 100644 index 0000000000..8f9449ddf0 --- /dev/null +++ b/vlib/js/promise/promise_test.js.v @@ -0,0 +1,16 @@ +fn test_promise() { + // TODO: For some reason compiler errors: "error: unknown function: js.promise.new", fix this + /* + p := promise.new(fn (resolve_ fn (x int), reject_ fn (x f64)) { + println('Promise code') + assert true + resolve_(42) + }) + p.then(fn (val int) { + println('resolved') + assert val == 42 + }, fn (fail f64) { + println('rejected') + assert false + })*/ +} diff --git a/vlib/v/gen/js/fn.v b/vlib/v/gen/js/fn.v index c4f76ff46f..aba03a9dc1 100644 --- a/vlib/v/gen/js/fn.v +++ b/vlib/v/gen/js/fn.v @@ -194,14 +194,16 @@ fn (mut g JsGen) method_call(node ast.CallExpr) { g.get_str_fn(rec_type) } mut unwrapped_rec_type := node.receiver_type - if g.table.cur_fn.generic_names.len > 0 { + if g.fn_decl != 0 && g.fn_decl.generic_names.len > 0 { // in generic fn unwrapped_rec_type = g.unwrap_generic(node.receiver_type) - } else { + } else { // in non-generic fn sym := g.table.get_type_symbol(node.receiver_type) match sym.info { ast.Struct, ast.Interface, ast.SumType { generic_names := sym.info.generic_types.map(g.table.get_type_symbol(it).name) - if utyp := g.table.resolve_generic_to_concrete(node.receiver_type, generic_names, + // see comment at top of vlib/v/gen/c/utils.v + mut muttable := unsafe { &ast.Table(g.table) } + if utyp := muttable.resolve_generic_to_concrete(node.receiver_type, generic_names, sym.info.concrete_types) { unwrapped_rec_type = utyp @@ -296,7 +298,7 @@ fn (mut g JsGen) method_call(node ast.CallExpr) { } mut name := util.no_dots('${receiver_type_name}_$node.name') - name = g.generic_fn_name(node.concrete_types, name, false) + // name = g.generic_fn_name(node.concrete_types, name, false) g.write('${name}(') g.expr(it.left) g.gen_deref_ptr(it.left_type) @@ -539,6 +541,21 @@ 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) { + node := it + if node.generic_names.len > 0 && g.cur_concrete_types.len == 0 { // need the cur_concrete_type check to avoid inf. recursion + // loop thru each generic type and generate a function + for concrete_types in g.table.fn_generic_types[node.name] { + if g.pref.is_verbose { + syms := concrete_types.map(g.table.get_type_symbol(it)) + the_type := syms.map(it.name).join(', ') + println('gen fn `$node.name` for type `$the_type`') + } + g.cur_concrete_types = concrete_types + g.gen_method_decl(node, typ) + } + g.cur_concrete_types = [] + return + } cur_fn_decl := g.fn_decl unsafe { g.fn_decl = &it @@ -550,22 +567,17 @@ fn (mut g JsGen) gen_method_decl(it ast.FnDecl, typ FnGenType) { 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) + // name = g.generic_fn_name(g.table.cur_concrete_types, name, true) if name in parser.builtin_functions { name = 'builtin__$name' } diff --git a/vlib/v/gen/js/js.v b/vlib/v/gen/js/js.v index abff4585ca..2966f9f8c8 100644 --- a/vlib/v/gen/js/js.v +++ b/vlib/v/gen/js/js.v @@ -85,6 +85,7 @@ mut: sourcemap &sourcemap.SourceMap // maps lines in generated javascrip file to original source files and line comptime_var_type_map map[string]ast.Type defer_ifdef string + cur_concrete_types []ast.Type out strings.Builder = strings.new_builder(128) array_sort_fn map[string]bool wasm_export map[string][]string @@ -1012,15 +1013,12 @@ fn (mut g JsGen) expr(node ast.Expr) { g.gen_string_literal(node) } ast.StructInit { - // TODO: once generic fns/unwrap_generic is implemented - // if node.unresolved { - // g.expr(ast.resolve_init(node, g.unwrap_generic(node.typ), g.table)) - // } else { - // // `user := User{name: 'Bob'}` - // g.gen_struct_init(node) - // } - // `user := User{name: 'Bob'}` - g.gen_struct_init(node) + if node.unresolved { + resolved := ast.resolve_init(node, g.unwrap_generic(node.typ), g.table) + g.expr(resolved) + } else { + g.gen_struct_init(node) + } } ast.TypeNode { typ := g.unwrap_generic(node.typ) @@ -1262,8 +1260,9 @@ fn (mut g JsGen) gen_assign_stmt(stmt ast.AssignStmt, semicolon bool) { continue } } - mut styp := g.typ(stmt.left_types[i]) - l_sym := g.table.get_type_symbol(stmt.left_types[i]) + + mut styp := if stmt.left_types.len > i { g.typ(stmt.left_types[i]) } else { '' } + // l_sym := g.table.get_type_symbol(stmt.left_types[i]) if !g.inside_loop && styp.len > 0 { g.doc.gen_typ(styp) } @@ -1324,6 +1323,7 @@ fn (mut g JsGen) gen_assign_stmt(stmt ast.AssignStmt, semicolon bool) { if is_assign && array_set { g.write('new ${styp}(') g.expr(left) + l_sym := g.table.get_type_symbol(stmt.left_types[i]) if l_sym.kind == .string { g.write('.str') } else { @@ -1366,11 +1366,13 @@ fn (mut g JsGen) gen_assign_stmt(stmt ast.AssignStmt, semicolon bool) { } } } else if is_assign && !array_set { + l_sym := g.table.get_type_symbol(stmt.left_types[i]) if l_sym.kind == .string { g.write('.str') } else { g.write('.val') } + if !array_set { g.write(' = ') } @@ -1418,9 +1420,13 @@ fn (mut g JsGen) gen_assign_stmt(stmt ast.AssignStmt, semicolon bool) { } } // TODO: Multiple types?? - should_cast := + + should_cast := if stmt.left_types.len == 0 { + false + } else { (g.table.type_kind(stmt.left_types.first()) in js.shallow_equatables) - && (g.cast_stack.len <= 0 || stmt.left_types.first() != g.cast_stack.last()) + && (g.cast_stack.len <= 0 || stmt.left_types.first() != g.cast_stack.last()) + } if should_cast { g.cast_stack << stmt.left_types.first() @@ -1538,7 +1544,7 @@ fn (mut g JsGen) gen_expr_stmt_no_semi(it ast.ExprStmt) { // 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)) - mut styp := sym.cname + mut styp := sym.cname.replace('>', '').replace('<', '') match mut sym.info { ast.Struct, ast.Interface, ast.SumType { if sym.info.is_generic { @@ -3231,7 +3237,9 @@ fn (mut g JsGen) gen_struct_init(it ast.StructInit) { } g.inc_indent() for i, field in it.fields { - g.write('$field.name: ') + if field.name.len != 0 { + g.write('$field.name: ') + } g.expr(field.expr) if i < it.fields.len - 1 { g.write(',') @@ -3461,8 +3469,20 @@ fn (mut g JsGen) gen_float_literal_expr(it ast.FloatLiteral) { fn (mut g JsGen) unwrap_generic(typ ast.Type) ast.Type { if typ.has_flag(.generic) { - if t_typ := g.table.resolve_generic_to_concrete(typ, g.table.cur_fn.generic_names, - g.table.cur_concrete_types) + /* + resolve_generic_to_concrete should not mutate the table. + It mutates if the generic type is for example []T and the + concrete type is an array type that has not been registered + yet. This should have already happened in the checker, since + it also calls resolve_generic_to_concrete. g.table is made + non-mut to make sure no one else can accidentally mutates the table. + */ + mut muttable := unsafe { &ast.Table(g.table) } + if t_typ := muttable.resolve_generic_to_concrete(typ, if g.fn_decl != 0 { + g.fn_decl.generic_names + } else { + []string{} + }, g.cur_concrete_types) { return t_typ }