diff --git a/.gitignore b/.gitignore index 1153b96..58b4f8a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,9 @@ +.idea/ +.zig-cache/ +zig-out/ + +node_modules/ + *.o zig-cache/zigdom.h zigdom.o diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..8db60ca --- /dev/null +++ b/.prettierrc @@ -0,0 +1,3 @@ +{ + "singleQuote": true +} diff --git a/README.md b/README.md index a2663fa..cf0ff3d 100644 --- a/README.md +++ b/README.md @@ -2,4 +2,6 @@ An example demonstrating Zig interacting with the DOM via JS. -Compile with `zig build-lib zigdom.zig -target wasm32-freestanding -dynamic -OReleaseSmall` +Forked from https://github.com/shritesh/zig-wasm-dom + +Compile with `zig build` diff --git a/build.zig b/build.zig new file mode 100644 index 0000000..7eb8c46 --- /dev/null +++ b/build.zig @@ -0,0 +1,30 @@ +const std = @import("std"); + +const number_of_pages = 1; +const max_number_of_pages = 2; + +pub fn build(b: *std.Build) void { + const target = b.resolveTargetQuery(.{ + .cpu_arch = .wasm32, + .os_tag = .freestanding, + }); + + const exe = b.addExecutable(.{ + .name = "zigdom", + .root_source_file = b.path("src/zigdom.zig"), + .target = target, + .optimize = .ReleaseSmall, + }); + + // + exe.global_base = 0; + exe.entry = .disabled; + exe.rdynamic = true; + // exe.import_memory = true; + exe.stack_size = std.wasm.page_size / 2; + + exe.initial_memory = std.wasm.page_size * number_of_pages; + exe.max_memory = std.wasm.page_size * max_number_of_pages; + + b.installArtifact(exe); +} diff --git a/index.html b/index.html index c71d44c..56d440d 100644 --- a/index.html +++ b/index.html @@ -10,12 +10,6 @@ - \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..6a2a33d --- /dev/null +++ b/package.json @@ -0,0 +1,15 @@ +{ + "name": "zig-wasm-dom", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "devDependencies": { + "prettier": "^3.3.3" + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..8ed2712 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,24 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + devDependencies: + prettier: + specifier: ^3.3.3 + version: 3.3.3 + +packages: + + prettier@3.3.3: + resolution: {integrity: sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==} + engines: {node: '>=14'} + hasBin: true + +snapshots: + + prettier@3.3.3: {} diff --git a/zigdom.zig b/src/zigdom.zig similarity index 82% rename from zigdom.zig rename to src/zigdom.zig index 37be0a2..2411082 100644 --- a/zigdom.zig +++ b/src/zigdom.zig @@ -1,11 +1,13 @@ // https://github.com/shritesh/zig-wasm-dom -extern "document" fn query_selector(selector_ptr: [*]const u8, selector_len: usize) usize; -extern "document" fn create_element(tag_name_ptr: [*]const u8, tag_name_len: usize) usize; -extern "document" fn create_text_node(data_ptr: [*]const u8, data_len: usize) usize; -extern "element" fn set_attribute(element_id: usize, name_ptr: [*]const u8, name_len: usize, value_ptr: [*]const u8, value_len: usize) void; -extern "element" fn get_attribute(element_id: usize, name_ptr: [*]const u8, name_len: usize, value_ptr: *[*]u8, value_len: *usize) bool; -extern "event_target" fn add_event_listener(event_target_id: usize, event_ptr: [*]const u8, event_len: usize, event_id: usize) void; -extern "window" fn alert(msg_ptr: [*]const u8, msg_len: usize) void; +const string = [*]const u8; + +extern "document" fn query_selector(selector_ptr: string, selector_len: usize) usize; +extern "document" fn create_element(tag_name_ptr: string, tag_name_len: usize) usize; +extern "document" fn create_text_node(data_ptr: string, data_len: usize) usize; +extern "element" fn set_attribute(element_id: usize, name_ptr: string, name_len: usize, value_ptr: string, value_len: usize) void; +extern "element" fn get_attribute(element_id: usize, name_ptr: string, name_len: usize, value_ptr: *[*]u8, value_len: *usize) bool; +extern "event_target" fn add_event_listener(event_target_id: usize, event_ptr: string, event_len: usize, event_id: usize) void; +extern "window" fn alert(msg_ptr: string, msg_len: usize) void; extern "node" fn append_child(node_id: usize, child_id: usize) usize; extern "zig" fn release_object(object_id: usize) void; @@ -112,11 +114,12 @@ fn launch() !void { } fn attach_listener(node: usize, event_name: []const u8, event_id: eventId) void { - add_event_listener(node, event_name.ptr, event_name.len, @enumToInt(event_id)); + add_event_listener(node, event_name.ptr, event_name.len, @intFromEnum(event_id)); } -export fn dispatchEvent(id: u32) void { - switch (@intToEnum(eventId, id)) { +export fn dispatch_event(id: u32) void { + const e: eventId = @enumFromInt(id); + switch (e) { eventId.Submit => on_submit_event(), eventId.Clear => on_clear_event(), } @@ -151,8 +154,8 @@ export fn launch_export() bool { } export fn _wasm_alloc(len: usize) u32 { - var buf = std.heap.page_allocator.alloc(u8, len) catch { + const buf = std.heap.page_allocator.alloc(u8, len) catch { return 0; }; - return @ptrToInt(buf.ptr); + return @intFromPtr(buf.ptr); } diff --git a/zig-out/bin/zigdom.wasm b/zig-out/bin/zigdom.wasm new file mode 100755 index 0000000..7b3a17e Binary files /dev/null and b/zig-out/bin/zigdom.wasm differ diff --git a/zigdom.js b/zigdom.js index fb6108c..bd4c61a 100644 --- a/zigdom.js +++ b/zigdom.js @@ -1,42 +1,54 @@ -const getString = function(ptr, len) { +const zigdom = { + objects: [], + exports: undefined, +}; + +window.zigdom = zigdom; + +function getString(ptr, len) { const slice = zigdom.exports.memory.buffer.slice(ptr, ptr + len); const textDecoder = new TextDecoder(); return textDecoder.decode(slice); -}; +} -const pushObject = function(object) { +function pushObject(object) { return zigdom.objects.push(object); -}; +} -const getObject = function(objId) { +function getObject(objId) { return zigdom.objects[objId - 1]; -}; +} -const dispatch = function(eventId) { - return function() { - zigdom.exports.dispatchEvent(eventId); +function dispatch(eventId) { + return function () { + zigdom.exports.dispatch_event(eventId); }; -}; +} -const elementSetAttribute = function( +function elementSetAttribute( node_id, name_ptr, name_len, value_ptr, - value_len + value_len, ) { const node = getObject(node_id); const attribute_name = getString(name_ptr, name_len); const value = getString(value_ptr, value_len); node[attribute_name] = value; -}; +} -const elementGetAttribute = function( +function writeU32ToMemory(memory, address, value) { + const mem_result_address = new DataView(memory.buffer, address, 4); + mem_result_address.setUint32(0, value, true); +} + +function elementGetAttribute( node_id, name_ptr, name_len, result_address_ptr, - result_address_len_ptr + result_address_len_ptr, ) { const node = getObject(node_id); const attribute_name = getString(name_ptr, name_len); @@ -44,7 +56,7 @@ const elementGetAttribute = function( // convert result into Uint8Array const textEncoder = new TextEncoder(); const resultArray = textEncoder.encode(result); - var len = resultArray.length; + const len = resultArray.length; if (len === 0) { return false; @@ -53,61 +65,44 @@ const elementGetAttribute = function( // allocate required number of bytes const ptr = zigdom.exports._wasm_alloc(len); if (ptr === 0) { - throw "Cannot allocate memory"; + throw 'Cannot allocate memory'; } - // write the array to the memory - const mem_result = new DataView(zigdom.exports.memory.buffer, ptr, len); - for (let i = 0; i < len; ++i) { - mem_result.setUint8(i, resultArray[i], true); - } + const uint8array = new Uint8Array(zigdom.exports.memory.buffer); + uint8array.set(resultArray, ptr); // write the address of the result array to result_address_ptr - const mem_result_address = new DataView( - zigdom.exports.memory.buffer, - result_address_ptr, - 32 / 8 - ); - mem_result_address.setUint32(0, ptr, true); - - //write the size of the result array to result_address_ptr_len_ptr - const mem_result_address_len = new DataView( - zigdom.exports.memory.buffer, - result_address_len_ptr, - 32 / 8 - ); - mem_result_address_len.setUint32(0, len, true); + writeU32ToMemory(zigdom.exports.memory, result_address_ptr, ptr); + + // write the size of the result array to result_address_ptr_len_ptr + writeU32ToMemory(zigdom.exports.memory, result_address_len_ptr, len); // return if success? (optional) return true; -}; -const eventTargetAddEventListener = function( - objId, - event_ptr, - event_len, - eventId -) { +} + +function eventTargetAddEventListener(objId, event_ptr, event_len, eventId) { const node = getObject(objId); const ev = getString(event_ptr, event_len); node.addEventListener(ev, dispatch(eventId)); -}; +} -const documentQuerySelector = function(selector_ptr, selector_len) { +function documentQuerySelector(selector_ptr, selector_len) { const selector = getString(selector_ptr, selector_len); return pushObject(document.querySelector(selector)); -}; +} -const documentCreateElement = function(tag_name_ptr, tag_name_len) { +function documentCreateElement(tag_name_ptr, tag_name_len) { const tag_name = getString(tag_name_ptr, tag_name_len); return pushObject(document.createElement(tag_name)); -}; +} -const documentCreateTextNode = function(data_ptr, data_len) { - data = getString(data_ptr, data_len); +function documentCreateTextNode(data_ptr, data_len) { + const data = getString(data_ptr, data_len); return pushObject(document.createTextNode(data)); -}; +} -const nodeAppendChild = function(node_id, child_id) { +function nodeAppendChild(node_id, child_id) { const node = getObject(node_id); const child = getObject(child_id); @@ -116,49 +111,61 @@ const nodeAppendChild = function(node_id, child_id) { } return pushObject(node.appendChild(child)); -}; +} -const windowAlert = function(msg_ptr, msg_len) { +function windowAlert(msg_ptr, msg_len) { const msg = getString(msg_ptr, msg_len); + // console.log('>', msg); alert(msg); -}; +} -const zigReleaseObject = function(object_id) { +function zigReleaseObject(object_id) { zigdom.objects[object_id - 1] = undefined; -}; +} + +function launch(wasmInstance) { + zigdom.exports = wasmInstance.instance.exports; -const launch = function(result) { - zigdom.exports = result.instance.exports; if (!zigdom.exports.launch_export()) { - throw "Launch Error"; + throw 'Launch Error'; } -}; +} -var zigdom = { - objects: [], - imports: { - document: { - query_selector: documentQuerySelector, - create_element: documentCreateElement, - create_text_node: documentCreateTextNode - }, - element: { - set_attribute: elementSetAttribute, - get_attribute: elementGetAttribute - }, - event_target: { - add_event_listener: eventTargetAddEventListener - }, - node: { - append_child: nodeAppendChild - }, - window: { - alert: windowAlert - }, - zig: { - release_object: zigReleaseObject - } +const wasmImports = { + document: { + query_selector: documentQuerySelector, + create_element: documentCreateElement, + create_text_node: documentCreateTextNode, + }, + element: { + set_attribute: elementSetAttribute, + get_attribute: elementGetAttribute, + }, + event_target: { + add_event_listener: eventTargetAddEventListener, + }, + node: { + append_child: nodeAppendChild, + }, + window: { + alert: windowAlert, + }, + zig: { + release_object: zigReleaseObject, }, - launch: launch, - exports: undefined }; + +async function run() { + const response = await fetch('zig-out/bin/zigdom.wasm'); + + if (!response.ok) { + throw new Error("wasm file can't be loaded"); + } + + const buffer = await response.arrayBuffer(); + const instance = await WebAssembly.instantiate(buffer, wasmImports); + + launch(instance); +} + +run(); diff --git a/zigdom.wasm b/zigdom.wasm deleted file mode 100755 index e7fffe5..0000000 Binary files a/zigdom.wasm and /dev/null differ