From cb98a83f77ca7a3eb0f43c013cdce1f8ee65deb5 Mon Sep 17 00:00:00 2001 From: Dusty Miller Date: Mon, 22 Dec 2025 20:16:24 -0600 Subject: Update letter.odin Fix MAX_RUNE to include planes up to 16. --- core/unicode/letter.odin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/unicode/letter.odin b/core/unicode/letter.odin index a1024dd6c..5191dd900 100644 --- a/core/unicode/letter.odin +++ b/core/unicode/letter.odin @@ -2,7 +2,7 @@ package unicode import "base:runtime" -MAX_RUNE :: '\U00010fff' // Maximum valid unicode code point +MAX_RUNE :: '\U0010ffff' // Maximum valid unicode code point REPLACEMENT_CHAR :: '\ufffd' // Represented an invalid code point MAX_ASCII :: '\u007f' // Maximum ASCII value MAX_LATIN1 :: '\u00ff' // Maximum Latin-1 value -- cgit v1.2.3 From 7e39239907885447b73c8c03648a85554552f1bc Mon Sep 17 00:00:00 2001 From: Karl Zylinski Date: Fri, 26 Dec 2025 23:02:35 +0100 Subject: WebGL: Add GetActiveAttrib binding --- core/sys/wasm/js/odin.js | 21 +++++++++++++++++++++ vendor/wasm/WebGL/webgl.odin | 44 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 64 insertions(+), 1 deletion(-) diff --git a/core/sys/wasm/js/odin.js b/core/sys/wasm/js/odin.js index ef203e24d..ca4af18b8 100644 --- a/core/sys/wasm/js/odin.js +++ b/core/sys/wasm/js/odin.js @@ -625,6 +625,26 @@ class WebGLInterface { this.ctx.generateMipmap(target); }, + GetActiveAttrib: (program, index, size_ptr, type_ptr, name_buf_ptr, name_buf_len, name_len_ptr) => { + let info = this.ctx.getActiveAttrib(program, index); + + if (size_ptr) { + this.mem.storeInt(size_ptr, info.size); + } + + if (type_ptr) { + this.mem.storeI32(type_ptr, info.type); + } + + if name_buf_ptr && name_buf_len > 0 { + let n = Math.min(name_buf_len, info.name.length); + let name = info.name.substring(0, n); + this.mem.loadBytes(name_buf_ptr, name_buf_len).set(new TextEncoder().encode(name)); + this.mem.storeInt(name_len_ptr, n); + } else if name_len_ptr { + this.mem.storeInt(name_len_ptr, info.name.length); + } + }, GetAttribLocation: (program, name_ptr, name_len) => { let name = this.mem.loadString(name_ptr, name_len); @@ -1840,6 +1860,7 @@ function odinSetupDefaultImports(wasmMemoryInterface, consoleElement, memory) { return false; }, + // Writes a struct of type `Gamepad_State`, see `core/sys/wasm/js/events.odin` get_gamepad_state: (gamepad_id, ep) => { let index = gamepad_id; let gps = navigator.getGamepads(); diff --git a/vendor/wasm/WebGL/webgl.odin b/vendor/wasm/WebGL/webgl.odin index 5616f3660..1c3bda2e6 100644 --- a/vendor/wasm/WebGL/webgl.odin +++ b/vendor/wasm/WebGL/webgl.odin @@ -3,6 +3,7 @@ package webgl foreign import "webgl" import glm "core:math/linalg/glsl" +import "base:runtime" Enum :: distinct u32 @@ -25,6 +26,12 @@ ContextAttribute :: enum u32 { } ContextAttributes :: distinct bit_set[ContextAttribute; u32] +ActiveInfo :: struct { + size: int, + type: Enum, + name: string, +} + DEFAULT_CONTEXT_ATTRIBUTES :: ContextAttributes{} @(default_calling_convention="contextless") @@ -260,7 +267,6 @@ UniformMatrix4fv :: proc "contextless" (location: i32, m: glm.mat4) { value := transmute([4*4]f32)m _UniformMatrix4fv(location, &value[0]) } - GetShaderiv :: proc "contextless" (shader: Shader, pname: Enum) -> (p: i32) { foreign webgl { @(link_name="GetShaderiv") @@ -270,6 +276,42 @@ GetShaderiv :: proc "contextless" (shader: Shader, pname: Enum) -> (p: i32) { return } +GetActiveAttribBuf :: proc "contextless" (program: Program, index: u32, name_buf: []byte) -> (info: ActiveInfo) { + foreign webgl { + @(link_name="GetActiveAttrib") + _GetActiveAttrib :: proc "contextless" (shader: Program, index: u32, size: ^int, type: ^Enum, name_buf: []byte, name_len: ^int) --- + } + name_len: int + _GetActiveAttrib(program, index, &info.size, &info.type, name_buf, &name_len) + info.name = string(name_buf[:name_len]) + return +} + +GetActiveAttribAlloc :: proc(program: Program, index: u32, allocator: runtime.Allocator, loc := #caller_location) -> (info: ActiveInfo) { + foreign webgl { + @(link_name="GetActiveAttrib") + _GetActiveAttrib :: proc "contextless" (shader: Program, index: u32, size: ^int, type: ^Enum, name_buf: []byte, name_len: ^int) --- + } + + name_len: int + + // Passing {} to the buf but giving it a name_len ptr will write the needed len into that int + _GetActiveAttrib(program, index, &info.size, &info.type, {}, &name_len) + + if name_len > 0 { + name_buf := make([]byte, name_len, allocator, loc) + _GetActiveAttrib(program, index, &info.size, &info.type, name_buf, &name_len) + assert(name_len == len(name_buf)) + info.name = string(name_buf[:name_len]) + } + + return +} + +GetActiveAttrib :: proc { + GetActiveAttribBuf, + GetActiveAttribAlloc, +} GetProgramInfoLog :: proc "contextless" (program: Program, buf: []byte) -> string { foreign webgl { -- cgit v1.2.3 From 7dee25bdcc069bbb0ceb5d4a1ac1a9e3009bfb83 Mon Sep 17 00:00:00 2001 From: Karl Zylinski Date: Fri, 26 Dec 2025 23:34:45 +0100 Subject: More allocator-aware webgl bindings and added more missing bindings. --- core/sys/wasm/js/odin.js | 21 +++++++++++++++++++++ vendor/wasm/WebGL/webgl.odin | 37 +++++++++++++++++++++++++++++++++++++ vendor/wasm/WebGL/webgl2.odin | 25 ++++++++++++++++++++++++- 3 files changed, 82 insertions(+), 1 deletion(-) diff --git a/core/sys/wasm/js/odin.js b/core/sys/wasm/js/odin.js index ca4af18b8..b0a01f66c 100644 --- a/core/sys/wasm/js/odin.js +++ b/core/sys/wasm/js/odin.js @@ -646,6 +646,27 @@ class WebGLInterface { } }, + GetActiveUniform: (program, index, size_ptr, type_ptr, name_buf_ptr, name_buf_len, name_len_ptr) => { + let info = this.ctx.getActiveUniform(program, index); + + if (size_ptr) { + this.mem.storeInt(size_ptr, info.size); + } + + if (type_ptr) { + this.mem.storeI32(type_ptr, info.type); + } + + if name_buf_ptr && name_buf_len > 0 { + let n = Math.min(name_buf_len, info.name.length); + let name = info.name.substring(0, n); + this.mem.loadBytes(name_buf_ptr, name_buf_len).set(new TextEncoder().encode(name)); + this.mem.storeInt(name_len_ptr, n); + } else if name_len_ptr { + this.mem.storeInt(name_len_ptr, info.name.length); + } + }, + GetAttribLocation: (program, name_ptr, name_len) => { let name = this.mem.loadString(name_ptr, name_len); return this.ctx.getAttribLocation(this.programs[program], name); diff --git a/vendor/wasm/WebGL/webgl.odin b/vendor/wasm/WebGL/webgl.odin index 1c3bda2e6..601071c3a 100644 --- a/vendor/wasm/WebGL/webgl.odin +++ b/vendor/wasm/WebGL/webgl.odin @@ -313,6 +313,43 @@ GetActiveAttrib :: proc { GetActiveAttribAlloc, } +GetActiveUniformBuf :: proc "contextless" (program: Program, index: u32, name_buf: []byte) -> (info: ActiveInfo) { + foreign webgl { + @(link_name="GetActiveUniform") + _GetActiveUniform :: proc "contextless" (shader: Program, index: u32, size: ^int, type: ^Enum, name_buf: []byte, name_len: ^int) --- + } + name_len: int + _GetActiveUniform(program, index, &info.size, &info.type, name_buf, &name_len) + info.name = string(name_buf[:name_len]) + return +} + +GetActiveUniformAlloc :: proc(program: Program, index: u32, allocator: runtime.Allocator, loc := #caller_location) -> (info: ActiveInfo) { + foreign webgl { + @(link_name="GetActiveUniform") + _GetActiveUniform :: proc "contextless" (shader: Program, index: u32, size: ^int, type: ^Enum, name_buf: []byte, name_len: ^int) --- + } + + name_len: int + + // Passing {} to the buf but giving it a name_len ptr will write the needed len into that int + _GetActiveUniform(program, index, &info.size, &info.type, {}, &name_len) + + if name_len > 0 { + name_buf := make([]byte, name_len, allocator, loc) + _GetActiveUniform(program, index, &info.size, &info.type, name_buf, &name_len) + assert(name_len == len(name_buf)) + info.name = string(name_buf[:name_len]) + } + + return +} + +GetActiveUniform :: proc { + GetActiveUniformBuf, + GetActiveUniformAlloc, +} + GetProgramInfoLog :: proc "contextless" (program: Program, buf: []byte) -> string { foreign webgl { @(link_name="GetProgramInfoLog") diff --git a/vendor/wasm/WebGL/webgl2.odin b/vendor/wasm/WebGL/webgl2.odin index 66a739303..3494559a5 100644 --- a/vendor/wasm/WebGL/webgl2.odin +++ b/vendor/wasm/WebGL/webgl2.odin @@ -3,6 +3,7 @@ package webgl foreign import "webgl2" import "base:intrinsics" +import "base:runtime" import glm "core:math/linalg/glsl" Query :: distinct u32 @@ -109,7 +110,7 @@ foreign webgl2 { BindVertexArray :: proc(vertexArray: VertexArrayObject) --- } -GetActiveUniformBlockName :: proc(program: Program, uniformBlockIndex: i32, buf: []byte) -> string { +GetActiveUniformBlockNameBuf :: proc(program: Program, uniformBlockIndex: i32, buf: []byte) -> string { foreign webgl2 { _GetActiveUniformBlockName :: proc "contextless" (program: Program, uniformBlockIndex: i32, buf: []byte, length: ^int) --- } @@ -118,6 +119,28 @@ GetActiveUniformBlockName :: proc(program: Program, uniformBlockIndex: i32, buf: return string(buf[:n]) } +GetActiveUniformBlockNameAlloc :: proc(program: Program, uniformBlockIndex: i32, allocator: runtime.Allocator, loc := #caller_location) -> string { + foreign webgl2 { + _GetActiveUniformBlockName :: proc "contextless" (program: Program, uniformBlockIndex: i32, buf: []byte, length: ^int) --- + } + n: int + _GetActiveUniformBlockName(program, uniformBlockIndex, {}, &n) + + if n > 0 { + buf := make([]byte, n, allocator, loc) + _GetActiveUniformBlockName(program, uniformBlockIndex, buf, &n) + assert(n == len(buf)) + return string(buf[:n]) + } + + return "" +} + +GetActiveUniformBlockName :: proc { + GetActiveUniformBlockNameBuf, + GetActiveUniformBlockNameAlloc, +} + Uniform1uiv :: proc "contextless" (location: i32, v: u32) { Uniform1ui(location, v) -- cgit v1.2.3 From 159eab133b4b6dc9df6896d003134c1f0ccfde92 Mon Sep 17 00:00:00 2001 From: Karl Zylinski Date: Sat, 27 Dec 2025 01:41:59 +0100 Subject: More webgl bindings and fixes --- core/sys/wasm/js/odin.js | 29 ++++++++++++++++++++++------- vendor/wasm/WebGL/webgl2.odin | 15 ++++++++++++++- 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/core/sys/wasm/js/odin.js b/core/sys/wasm/js/odin.js index b0a01f66c..0c617dd8e 100644 --- a/core/sys/wasm/js/odin.js +++ b/core/sys/wasm/js/odin.js @@ -626,7 +626,7 @@ class WebGLInterface { }, GetActiveAttrib: (program, index, size_ptr, type_ptr, name_buf_ptr, name_buf_len, name_len_ptr) => { - let info = this.ctx.getActiveAttrib(program, index); + const info = this.ctx.getActiveAttrib(this.programs[program], index); if (size_ptr) { this.mem.storeInt(size_ptr, info.size); @@ -636,18 +636,18 @@ class WebGLInterface { this.mem.storeI32(type_ptr, info.type); } - if name_buf_ptr && name_buf_len > 0 { + if (name_buf_ptr && name_buf_len > 0) { let n = Math.min(name_buf_len, info.name.length); let name = info.name.substring(0, n); this.mem.loadBytes(name_buf_ptr, name_buf_len).set(new TextEncoder().encode(name)); this.mem.storeInt(name_len_ptr, n); - } else if name_len_ptr { + } else if (name_len_ptr) { this.mem.storeInt(name_len_ptr, info.name.length); } }, GetActiveUniform: (program, index, size_ptr, type_ptr, name_buf_ptr, name_buf_len, name_len_ptr) => { - let info = this.ctx.getActiveUniform(program, index); + let info = this.ctx.getActiveUniform(this.programs[program], index); if (size_ptr) { this.mem.storeInt(size_ptr, info.size); @@ -657,12 +657,12 @@ class WebGLInterface { this.mem.storeI32(type_ptr, info.type); } - if name_buf_ptr && name_buf_len > 0 { + if (name_buf_ptr && name_buf_len > 0) { let n = Math.min(name_buf_len, info.name.length); let name = info.name.substring(0, n); this.mem.loadBytes(name_buf_ptr, name_buf_len).set(new TextEncoder().encode(name)); this.mem.storeInt(name_len_ptr, n); - } else if name_len_ptr { + } else if (name_len_ptr) { this.mem.storeInt(name_len_ptr, info.name.length); } }, @@ -1301,7 +1301,6 @@ class WebGLInterface { this.assertWebGL2(); return this.ctx.getUniformBlockIndex(this.programs[program], this.mem.loadString(uniformBlockName_ptr, uniformBlockName_len)); }, - // any getActiveUniformBlockParameter(WebGLProgram program, GLuint uniformBlockIndex, GLenum pname); GetActiveUniformBlockName: (program, uniformBlockIndex, buf_ptr, buf_len, length_ptr) => { this.assertWebGL2(); let name = this.ctx.getActiveUniformBlockName(this.programs[program], uniformBlockIndex); @@ -1311,6 +1310,22 @@ class WebGLInterface { this.mem.loadBytes(buf_ptr, buf_len).set(new TextEncoder().encode(name)) this.mem.storeInt(length_ptr, n); }, + GetActiveUniforms: (program, uniformIndices_ptr, uniformIndices_len, pname, res_ptr) => { + thid.assertWebGL2(); + let indices = this.mem.loadU32Array(uniformIndices_ptr, uniformIndices_len); + this.ctx.getActiveUniforms(this.programs[program], indices, pname) + this.mem.loadI32Array(res_ptr, indices.length).set(indices) + }, + GetActiveUniformBlockParameter: (program, uniformBlockIndex, pname, params_ptr) => { + this.assertWebGL2(); + let res = this.ctx.getActiveUniformBlockParameter(this.programs[program], uniformBlockIndex, pname); + + if (e instanceof Uint32Array) { // for pname GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES + this.mem.loadU32Array(params_ptr, res.length).set(res) + } else { + this.mem.storeI32(params_ptr, res) + } + }, UniformBlockBinding: (program, uniformBlockIndex, uniformBlockBinding) => { this.assertWebGL2(); this.ctx.uniformBlockBinding(this.programs[program], uniformBlockIndex, uniformBlockBinding); diff --git a/vendor/wasm/WebGL/webgl2.odin b/vendor/wasm/WebGL/webgl2.odin index 3494559a5..1e36fd0fd 100644 --- a/vendor/wasm/WebGL/webgl2.odin +++ b/vendor/wasm/WebGL/webgl2.odin @@ -104,6 +104,12 @@ foreign webgl2 { GetUniformBlockIndex :: proc(program: Program, uniformBlockName: string) -> i32 --- UniformBlockBinding :: proc(program: Program, uniformBlockIndex: i32, uniformBlockBinding: i32) --- + // if `pname` is `UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES` then an array will be written at + // `params`, in that case the length `params` need to have is given first querying using `pname` + // `UNIFORM_BLOCK_ACTIVE_UNIFORMS`. + GetActiveUniformBlockParameter :: proc(program: Program, uniformBlockIndex: i32, pname: Enum, params: [^]i32) --- + GetActiveUniforms :: proc(program: Program, uniformIndices: []u32, pname: Enum, res: [^]i32) --- + CreateVertexArray :: proc() -> VertexArrayObject --- DeleteVertexArray :: proc(vertexArray: VertexArrayObject) --- IsVertexArray :: proc(vertexArray: VertexArrayObject) -> bool --- @@ -112,6 +118,7 @@ foreign webgl2 { GetActiveUniformBlockNameBuf :: proc(program: Program, uniformBlockIndex: i32, buf: []byte) -> string { foreign webgl2 { + @(link_name="GetActiveUniformBlockName") _GetActiveUniformBlockName :: proc "contextless" (program: Program, uniformBlockIndex: i32, buf: []byte, length: ^int) --- } n: int @@ -121,6 +128,7 @@ GetActiveUniformBlockNameBuf :: proc(program: Program, uniformBlockIndex: i32, b GetActiveUniformBlockNameAlloc :: proc(program: Program, uniformBlockIndex: i32, allocator: runtime.Allocator, loc := #caller_location) -> string { foreign webgl2 { + @(link_name="GetActiveUniformBlockName") _GetActiveUniformBlockName :: proc "contextless" (program: Program, uniformBlockIndex: i32, buf: []byte, length: ^int) --- } n: int @@ -141,7 +149,6 @@ GetActiveUniformBlockName :: proc { GetActiveUniformBlockNameAlloc, } - Uniform1uiv :: proc "contextless" (location: i32, v: u32) { Uniform1ui(location, v) } @@ -157,6 +164,7 @@ Uniform4uiv :: proc "contextless" (location: i32, v: glm.uvec4) { UniformMatrix3x2fv :: proc "contextless" (location: i32, m: glm.mat3x2) { foreign webgl2 { + @(link_name="UniformMatrix3x2fv") _UniformMatrix3x2fv :: proc "contextless" (location: i32, addr: [^]f32) --- } array := intrinsics.matrix_flatten(m) @@ -164,6 +172,7 @@ UniformMatrix3x2fv :: proc "contextless" (location: i32, m: glm.mat3x2) { } UniformMatrix4x2fv :: proc "contextless" (location: i32, m: glm.mat4x2) { foreign webgl2 { + @(link_name="UniformMatrix4x2fv") _UniformMatrix4x2fv :: proc "contextless" (location: i32, addr: [^]f32) --- } array := intrinsics.matrix_flatten(m) @@ -171,6 +180,7 @@ UniformMatrix4x2fv :: proc "contextless" (location: i32, m: glm.mat4x2) { } UniformMatrix2x3fv :: proc "contextless" (location: i32, m: glm.mat2x3) { foreign webgl2 { + @(link_name="UniformMatrix2x3fv") _UniformMatrix2x3fv :: proc "contextless" (location: i32, addr: [^]f32) --- } array := intrinsics.matrix_flatten(m) @@ -178,6 +188,7 @@ UniformMatrix2x3fv :: proc "contextless" (location: i32, m: glm.mat2x3) { } UniformMatrix4x3fv :: proc "contextless" (location: i32, m: glm.mat4x3) { foreign webgl2 { + @(link_name="UniformMatrix4x3fv") _UniformMatrix4x3fv :: proc "contextless" (location: i32, addr: [^]f32) --- } array := intrinsics.matrix_flatten(m) @@ -185,6 +196,7 @@ UniformMatrix4x3fv :: proc "contextless" (location: i32, m: glm.mat4x3) { } UniformMatrix2x4fv :: proc "contextless" (location: i32, m: glm.mat2x4) { foreign webgl2 { + @(link_name="UniformMatrix2x4fv") _UniformMatrix2x4fv :: proc "contextless" (location: i32, addr: [^]f32) --- } array := intrinsics.matrix_flatten(m) @@ -192,6 +204,7 @@ UniformMatrix2x4fv :: proc "contextless" (location: i32, m: glm.mat2x4) { } UniformMatrix3x4fv :: proc "contextless" (location: i32, m: glm.mat3x4) { foreign webgl2 { + @(link_name="UniformMatrix3x4fv") _UniformMatrix3x4fv :: proc "contextless" (location: i32, addr: [^]f32) --- } array := intrinsics.matrix_flatten(m) -- cgit v1.2.3 From 3f9aefda202f9677987d72c0be52417772315edc Mon Sep 17 00:00:00 2001 From: Karl Zylinski Date: Mon, 29 Dec 2025 17:34:01 +0100 Subject: Added CheckFramebufferStatus to WebGL bindings --- core/sys/wasm/js/odin.js | 3 +++ vendor/wasm/WebGL/webgl.odin | 1 + 2 files changed, 4 insertions(+) diff --git a/core/sys/wasm/js/odin.js b/core/sys/wasm/js/odin.js index 0c617dd8e..e90ccc124 100644 --- a/core/sys/wasm/js/odin.js +++ b/core/sys/wasm/js/odin.js @@ -616,6 +616,9 @@ class WebGLInterface { FramebufferTexture2D: (target, attachment, textarget, texture, level) => { this.ctx.framebufferTexture2D(target, attachment, textarget, this.textures[texture], level); }, + CheckFramebufferStatus: (target) => { + return this.ctx.checkFramebufferStatus(target) + }, FrontFace: (mode) => { this.ctx.frontFace(mode); }, diff --git a/vendor/wasm/WebGL/webgl.odin b/vendor/wasm/WebGL/webgl.odin index 601071c3a..d607ec743 100644 --- a/vendor/wasm/WebGL/webgl.odin +++ b/vendor/wasm/WebGL/webgl.odin @@ -113,6 +113,7 @@ foreign webgl { Flush :: proc() --- FramebufferRenderbuffer :: proc(target, attachment, renderbufertarget: Enum, renderbuffer: Renderbuffer) --- FramebufferTexture2D :: proc(target, attachment, textarget: Enum, texture: Texture, level: i32) --- + CheckFramebufferStatus :: proc(target: Enum) -> Enum --- FrontFace :: proc(mode: Enum) --- GenerateMipmap :: proc(target: Enum) --- -- cgit v1.2.3 From fb52238e369dc5c1712d2eca2c787473af59bca3 Mon Sep 17 00:00:00 2001 From: Karl Zylinski Date: Mon, 29 Dec 2025 17:42:19 +0100 Subject: Fix bugs in odin.js:GetActiveUniformBlockName --- core/sys/wasm/js/odin.js | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/core/sys/wasm/js/odin.js b/core/sys/wasm/js/odin.js index e90ccc124..063fd990c 100644 --- a/core/sys/wasm/js/odin.js +++ b/core/sys/wasm/js/odin.js @@ -1304,17 +1304,21 @@ class WebGLInterface { this.assertWebGL2(); return this.ctx.getUniformBlockIndex(this.programs[program], this.mem.loadString(uniformBlockName_ptr, uniformBlockName_len)); }, - GetActiveUniformBlockName: (program, uniformBlockIndex, buf_ptr, buf_len, length_ptr) => { + GetActiveUniformBlockName: (program, uniformBlockIndex, name_buf_ptr, name_buf_len, name_length_ptr) => { this.assertWebGL2(); let name = this.ctx.getActiveUniformBlockName(this.programs[program], uniformBlockIndex); - let n = Math.min(buf_len, name.length); - name = name.substring(0, n); - this.mem.loadBytes(buf_ptr, buf_len).set(new TextEncoder().encode(name)) - this.mem.storeInt(length_ptr, n); + if (name_buf_ptr && name_buf_len > 0) { + let n = Math.min(name_buf_len, name.length); + name = name.substring(0, n); + this.mem.loadBytes(name_buf_ptr, name_buf_len).set(new TextEncoder().encode(name)); + this.mem.storeInt(name_length_ptr, n); + } else if (name_length_ptr) { + this.mem.storeInt(name_length_ptr, name.length); + } }, GetActiveUniforms: (program, uniformIndices_ptr, uniformIndices_len, pname, res_ptr) => { - thid.assertWebGL2(); + this.assertWebGL2(); let indices = this.mem.loadU32Array(uniformIndices_ptr, uniformIndices_len); this.ctx.getActiveUniforms(this.programs[program], indices, pname) this.mem.loadI32Array(res_ptr, indices.length).set(indices) @@ -1323,7 +1327,7 @@ class WebGLInterface { this.assertWebGL2(); let res = this.ctx.getActiveUniformBlockParameter(this.programs[program], uniformBlockIndex, pname); - if (e instanceof Uint32Array) { // for pname GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES + if (res instanceof Uint32Array) { // for pname GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES this.mem.loadU32Array(params_ptr, res.length).set(res) } else { this.mem.storeI32(params_ptr, res) -- cgit v1.2.3 From 47f20c0ffb1d9e48e437c92e3312b4a14d3b8443 Mon Sep 17 00:00:00 2001 From: Karl Zylinski Date: Mon, 29 Dec 2025 20:57:25 +0100 Subject: Added 'set_document_title' to be able to rename the tab --- core/sys/wasm/js/dom.odin | 2 ++ core/sys/wasm/js/odin.js | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/core/sys/wasm/js/dom.odin b/core/sys/wasm/js/dom.odin index a4f8d7687..6dd9dbf45 100644 --- a/core/sys/wasm/js/dom.odin +++ b/core/sys/wasm/js/dom.odin @@ -22,6 +22,8 @@ foreign dom_lib { window_set_scroll :: proc(x, y: f64) --- set_element_style :: proc(id: string, key: string, value: string) --- + + set_document_title :: proc(title: string) --- } get_element_value_string :: proc "contextless" (id: string, buf: []byte) -> string { diff --git a/core/sys/wasm/js/odin.js b/core/sys/wasm/js/odin.js index 063fd990c..19ac3093e 100644 --- a/core/sys/wasm/js/odin.js +++ b/core/sys/wasm/js/odin.js @@ -2050,6 +2050,11 @@ function odinSetupDefaultImports(wasmMemoryInterface, consoleElement, memory) { } }, + set_document_title: (title_ptr, title_len) => { + let title = wasmMemoryInterface.loadString(title_ptr, title_len); + document.title = title; + }, + get_element_key_f64: (id_ptr, id_len, key_ptr, key_len) => { let id = wasmMemoryInterface.loadString(id_ptr, id_len); let key = wasmMemoryInterface.loadString(key_ptr, key_len); -- cgit v1.2.3 From ba68d75c6ff163c8c09793c9f42bab8229b6f490 Mon Sep 17 00:00:00 2001 From: dozn Date: Mon, 29 Dec 2025 12:23:54 -0800 Subject: Add JSON5/SJSON Comments When Marshalling Allows user-facing JSON5/SJSON to have comments explaining field usage. `json.Marshal_Options.pretty` must be enabled since we only use single-line comments (not to mention it wouldn't be terribly useful without `pretty` set anyways). We don't escape anything, so `\n` will display as "\n", but you're still able to enter in a proper newline character and it'll be displayed on multiple lines. --- core/encoding/json/marshal.odin | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/core/encoding/json/marshal.odin b/core/encoding/json/marshal.odin index e563c326a..bed2018e3 100644 --- a/core/encoding/json/marshal.odin +++ b/core/encoding/json/marshal.odin @@ -413,6 +413,12 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err: } opt_write_iteration(w, opt, first_iteration) or_return + + if opt.pretty { + comment := reflect.struct_tag_get(reflect.Struct_Tag(info.tags[i]), "jsoncomment") + opt_write_comment(w, opt, &comment) or_return + } + first_iteration = false if json_name != "" { opt_write_key(w, opt, json_name) or_return @@ -533,6 +539,26 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err: return } +// Newlines are split into multiple comment lines +opt_write_comment :: proc(w: io.Writer, opt: ^Marshal_Options, comment: ^string) -> (err: io.Error) { + if comment^ == "" { + return nil + } + + switch opt.spec { + case .JSON5, .MJSON: + for line in strings.split_iterator(comment, "\n") { + io.write_string(w, "// ") or_return + io.write_string(w, line) or_return + io.write_rune(w, '\n') or_return + opt_write_indentation(w, opt) or_return + } + case .JSON: return nil + } + + return nil +} + // write key as quoted string or with optional quotes in mjson opt_write_key :: proc(w: io.Writer, opt: ^Marshal_Options, name: string) -> (err: io.Error) { switch opt.spec { -- cgit v1.2.3 From 0bf4ffe46943f8f8bfe2f4f077c92be1512d117a Mon Sep 17 00:00:00 2001 From: Krzesimir Nowak Date: Mon, 29 Dec 2025 18:39:31 +0100 Subject: Fix handling of #force_inline --- src/parser.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/parser.cpp b/src/parser.cpp index b2ff55396..823021e04 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -2183,7 +2183,7 @@ gb_internal Ast *parse_force_inlining_operand(AstFile *f, Token token) { return expr; } if (e->kind != Ast_ProcLit && e->kind != Ast_CallExpr) { - syntax_error(expr, "%.*s must be followed by a procedure literal or call, got %.*s", LIT(token.string), LIT(ast_strings[expr->kind])); + syntax_error(expr, "%.*s must be followed by a procedure literal or call, got %.*s", LIT(token.string), LIT(ast_strings[e->kind])); return ast_bad_expr(f, token, f->curr_token); } ProcInlining pi = ProcInlining_none; @@ -2197,17 +2197,17 @@ gb_internal Ast *parse_force_inlining_operand(AstFile *f, Token token) { if (pi != ProcInlining_none) { if (e->kind == Ast_ProcLit) { - if (expr->ProcLit.inlining != ProcInlining_none && - expr->ProcLit.inlining != pi) { + if (e->ProcLit.inlining != ProcInlining_none && + e->ProcLit.inlining != pi) { syntax_error(expr, "Cannot apply both '#force_inline' and '#force_no_inline' to a procedure literal"); } - expr->ProcLit.inlining = pi; + e->ProcLit.inlining = pi; } else if (e->kind == Ast_CallExpr) { - if (expr->CallExpr.inlining != ProcInlining_none && - expr->CallExpr.inlining != pi) { + if (e->CallExpr.inlining != ProcInlining_none && + e->CallExpr.inlining != pi) { syntax_error(expr, "Cannot apply both '#force_inline' and '#force_no_inline' to a procedure call"); } - expr->CallExpr.inlining = pi; + e->CallExpr.inlining = pi; } } -- cgit v1.2.3 From affaefc13ace1ac95b97c043314da13d4587eb44 Mon Sep 17 00:00:00 2001 From: dozn Date: Tue, 30 Dec 2025 04:42:06 -0800 Subject: Moved `first_iteration` up a couple lines so it makes sense readability-wise. --- core/encoding/json/marshal.odin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/encoding/json/marshal.odin b/core/encoding/json/marshal.odin index bed2018e3..acf12331a 100644 --- a/core/encoding/json/marshal.odin +++ b/core/encoding/json/marshal.odin @@ -413,13 +413,13 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err: } opt_write_iteration(w, opt, first_iteration) or_return + first_iteration = false if opt.pretty { comment := reflect.struct_tag_get(reflect.Struct_Tag(info.tags[i]), "jsoncomment") opt_write_comment(w, opt, &comment) or_return } - first_iteration = false if json_name != "" { opt_write_key(w, opt, json_name) or_return } else { -- cgit v1.2.3 From 589dbbddd20f444786e1d71258faf2f692450c49 Mon Sep 17 00:00:00 2001 From: dozn Date: Tue, 30 Dec 2025 17:02:06 -0800 Subject: [encoding/json] Fix Escapes When Marshalling Fixes https://github.com/odin-lang/Odin/issues/6060 and https://github.com/odin-lang/Odin/issues/6075 The boolean "true" was setting `html_safe` instead of `for_json`. Chalk one up for the "use enums instead of booleans" crowd. --- core/encoding/json/marshal.odin | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/encoding/json/marshal.odin b/core/encoding/json/marshal.odin index acf12331a..c4e348aa8 100644 --- a/core/encoding/json/marshal.odin +++ b/core/encoding/json/marshal.odin @@ -122,9 +122,9 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err: case runtime.Type_Info_Rune: r := a.(rune) - io.write_byte(w, '"') or_return - io.write_escaped_rune(w, r, '"', true) or_return - io.write_byte(w, '"') or_return + io.write_byte(w, '"') or_return + io.write_escaped_rune(w, r, '"', for_json = true) or_return + io.write_byte(w, '"') or_return case runtime.Type_Info_Float: switch f in a { -- cgit v1.2.3 From e4bf5476c3cc218b1265b2cb90c3b4fe3c9d3df8 Mon Sep 17 00:00:00 2001 From: ske Date: Tue, 30 Dec 2025 22:08:40 -0300 Subject: Fix literal endianness (fix #6068) --- src/llvm_backend_const.cpp | 23 +++++++--- tests/issues/run.bat | 1 + tests/issues/run.sh | 1 + tests/issues/test_issue_6068.odin | 92 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 112 insertions(+), 5 deletions(-) create mode 100644 tests/issues/test_issue_6068.odin diff --git a/src/llvm_backend_const.cpp b/src/llvm_backend_const.cpp index 22e124792..9c407be00 100644 --- a/src/llvm_backend_const.cpp +++ b/src/llvm_backend_const.cpp @@ -498,6 +498,13 @@ gb_internal LLVMValueRef lb_big_int_to_llvm(lbModule *m, Type *original_type, Bi if (big_int_is_zero(a)) { return LLVMConstNull(lb_type(m, original_type)); } + + BigInt val = {}; + big_int_init(&val, a); + + if (big_int_is_neg(&val)) { + mp_incr(&val); + } size_t sz = cast(size_t)type_size_of(original_type); u64 rop64[4] = {}; // 2 u64 is the maximum we will ever need, so doubling it will be fine :P @@ -509,7 +516,7 @@ gb_internal LLVMValueRef lb_big_int_to_llvm(lbModule *m, Type *original_type, Bi size_t nails = 0; mp_endian endian = MP_LITTLE_ENDIAN; - max_count = mp_pack_count(a, nails, size); + max_count = mp_pack_count(&val, nails, size); if (sz < max_count) { debug_print_big_int(a); gb_printf_err("%s -> %tu\n", type_to_string(original_type), sz); @@ -520,7 +527,7 @@ gb_internal LLVMValueRef lb_big_int_to_llvm(lbModule *m, Type *original_type, Bi mp_err err = mp_pack(rop, sz, &written, MP_LSB_FIRST, size, endian, nails, - a); + &val); GB_ASSERT(err == MP_OKAY); if (!is_type_endian_little(original_type)) { @@ -531,12 +538,18 @@ gb_internal LLVMValueRef lb_big_int_to_llvm(lbModule *m, Type *original_type, Bi } } + if (big_int_is_neg(a)) { + // sizeof intead of sz for sign extend to work properly + for (size_t i = 0; i < sizeof rop64; i++) { + rop[i] = ~rop[i]; + } + } + + big_int_dealloc(&val); + GB_ASSERT(!is_type_array(original_type)); LLVMValueRef value = LLVMConstIntOfArbitraryPrecision(lb_type(m, original_type), cast(unsigned)((sz+7)/8), cast(u64 *)rop); - if (big_int_is_neg(a)) { - value = LLVMConstNeg(value); - } return value; } diff --git a/tests/issues/run.bat b/tests/issues/run.bat index bbd7cbc90..0ceaf554c 100644 --- a/tests/issues/run.bat +++ b/tests/issues/run.bat @@ -26,6 +26,7 @@ set COMMON=-define:ODIN_TEST_FANCY=false -file -vet -strict-style -ignore-unused ..\..\..\odin build ..\test_issue_5097-2.odin %COMMON% || exit /b ..\..\..\odin build ..\test_issue_5265.odin %COMMON% || exit /b ..\..\..\odin test ..\test_issue_5699.odin %COMMON% || exit /b +..\..\..\odin test ..\test_issue_6068.odin %COMMON% || exit /b @echo off diff --git a/tests/issues/run.sh b/tests/issues/run.sh index a9a4bb88d..ce02ba20a 100755 --- a/tests/issues/run.sh +++ b/tests/issues/run.sh @@ -33,6 +33,7 @@ $ODIN build ../test_issue_5097.odin $COMMON $ODIN build ../test_issue_5097-2.odin $COMMON $ODIN build ../test_issue_5265.odin $COMMON $ODIN test ../test_issue_5699.odin $COMMON +$ODIN test ../test_issue_6068.odin $COMMON set +x diff --git a/tests/issues/test_issue_6068.odin b/tests/issues/test_issue_6068.odin new file mode 100644 index 000000000..011c95442 --- /dev/null +++ b/tests/issues/test_issue_6068.odin @@ -0,0 +1,92 @@ +// Tests issue #6068 https://github.com/odin-lang/Odin/issues/6068 +package test_issues + +import "core:testing" + +@test +test_issue_6068 :: proc(t: ^testing.T) { + { + check_be : i128be = -1 + check_le : i128le = -1 + value := -1 + reverse := i128be(value) + + // test variable + testing.expect(t, i128be(value) == check_be) + testing.expect(t, i128be(-1) == check_be) + testing.expect(t, cast(i128be)value == check_be) + testing.expect(t, cast(i128be)-1 == check_be) + testing.expect(t, i128be(int(-1)) == check_be) + testing.expect(t, cast(i128be)int(-1) == check_be) + testing.expect(t, i128le(value) == check_le) + testing.expect(t, i128le(-1) == check_le) + testing.expect(t, cast(i128le)value == check_le) + testing.expect(t, cast(i128le)-1 == check_le) + testing.expect(t, i128le(int(-1)) == check_le) + testing.expect(t, cast(i128le)int(-1) == check_le) + testing.expect(t, i128le(reverse) == check_le) + testing.expect(t, cast(i128le)reverse == check_le) + + // test literal + testing.expect(t, i128be(value) == -1) + testing.expect(t, i128be(-1) == -1) + testing.expect(t, cast(i128be)value == -1) + testing.expect(t, cast(i128be)-1 == -1) + testing.expect(t, i128be(int(-1)) == -1) + testing.expect(t, cast(i128be)int(-1) == -1) + testing.expect(t, i128le(value) == -1) + testing.expect(t, i128le(-1) == -1) + testing.expect(t, cast(i128le)value == -1) + testing.expect(t, cast(i128le)-1 == -1) + testing.expect(t, i128le(int(-1)) == -1) + testing.expect(t, cast(i128le)int(-1) == -1) + testing.expect(t, i128le(reverse) == -1) + testing.expect(t, cast(i128le)reverse == -1) + } + + // NOTE(ske): [llvm_backend_const.cpp:lb_big_int_to_llvm] + // floats behaved wonky when I tested because I forgot to sign extend whole + // rop so I added more tests here to be safe + { + check_be : f64be = -1.234 + check_le : f64le = -1.234 + value : f64 = -1.234 + reverse := f64be(value) + + // test variable + testing.expect(t, f64be(value) == check_be) + testing.expect(t, f64be(-1.234) == check_be) + testing.expect(t, cast(f64be)value == check_be) + testing.expect(t, cast(f64be)-1.234 == check_be) + testing.expect(t, f64be(int(-1.234)) == check_be) + testing.expect(t, cast(f64be)int(-1.234) == check_be) + testing.expect(t, f64le(value) == check_le) + testing.expect(t, f64le(-1.234) == check_le) + testing.expect(t, cast(f64le)value == check_le) + testing.expect(t, cast(f64le)-1.234 == check_le) + testing.expect(t, f64le(int(-1.234)) == check_le) + testing.expect(t, cast(f64le)int(-1.234) == check_le) + testing.expect(t, f64le(reverse) == check_le) + testing.expect(t, cast(f64le)reverse == check_le) + + // test literal + testing.expect(t, f64be(value) == -1.234) + testing.expect(t, f64be(-1.234) == -1.234) + testing.expect(t, cast(f64be)value == -1.234) + testing.expect(t, cast(f64be)-1.234 == -1.234) + testing.expect(t, f64be(int(-1.234)) == -1.234) + testing.expect(t, cast(f64be)int(-1.234) == -1.234) + testing.expect(t, f64le(value) == -1.234) + testing.expect(t, f64le(-1.234) == -1.234) + testing.expect(t, cast(f64le)value == -1.234) + testing.expect(t, cast(f64le)-1.234 == -1.234) + testing.expect(t, f64le(int(-1.234)) == -1.234) + testing.expect(t, cast(f64le)int(-1.234) == -1.234) + testing.expect(t, f64le(reverse) == -1.234) + testing.expect(t, cast(f64le)reverse == -1.234) + } + + testing.expect(t, i64be(-1) + i64be(1) == 0) + testing.expect(t, i64le(-1) + i64le(i64be(1)) == 0) + testing.expect(t, i64be(-7) * i64be(7) == -49) +} -- cgit v1.2.3 From 2700fa86d647ed27f4f7f008ef7d15299bfd6671 Mon Sep 17 00:00:00 2001 From: ske Date: Tue, 30 Dec 2025 22:24:10 -0300 Subject: fix typo --- src/llvm_backend_const.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/llvm_backend_const.cpp b/src/llvm_backend_const.cpp index 9c407be00..8ce2137ab 100644 --- a/src/llvm_backend_const.cpp +++ b/src/llvm_backend_const.cpp @@ -539,7 +539,7 @@ gb_internal LLVMValueRef lb_big_int_to_llvm(lbModule *m, Type *original_type, Bi } if (big_int_is_neg(a)) { - // sizeof intead of sz for sign extend to work properly + // sizeof instead of sz for sign extend to work properly for (size_t i = 0; i < sizeof rop64; i++) { rop[i] = ~rop[i]; } -- cgit v1.2.3 From 8fea2b783c8f35dc6cd5963a2ed30cb176cc9889 Mon Sep 17 00:00:00 2001 From: dozn Date: Tue, 30 Dec 2025 18:10:07 -0800 Subject: [encoding/json] Allow Unmarshalling to `rune` Fixes https://github.com/odin-lang/Odin/issues/6061 --- core/encoding/json/types.odin | 1 + core/encoding/json/unmarshal.odin | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/core/encoding/json/types.odin b/core/encoding/json/types.odin index 1da17a0db..77cc7db85 100644 --- a/core/encoding/json/types.odin +++ b/core/encoding/json/types.odin @@ -76,6 +76,7 @@ Error :: enum { Invalid_Number, String_Not_Terminated, Invalid_String, + Invalid_Rune, // Parsing Errors diff --git a/core/encoding/json/unmarshal.odin b/core/encoding/json/unmarshal.odin index 3cdc6429d..58365b684 100644 --- a/core/encoding/json/unmarshal.odin +++ b/core/encoding/json/unmarshal.odin @@ -225,6 +225,15 @@ unmarshal_string_token :: proc(p: ^Parser, val: any, token: Token, ti: ^reflect. } ok = true return + case rune: + for rne, i in str { + if i > 0 { + dst = {} + return false, .Invalid_Rune + } + dst = rne + } + return true, nil } #partial switch variant in ti.variant { -- cgit v1.2.3