Skip to content

Commit 0413c53

Browse files
committed
Add DOMRefTypes example
1 parent c8555b0 commit 0413c53

File tree

10 files changed

+339
-0
lines changed

10 files changed

+339
-0
lines changed

DOMRefTypes/.editorconfig

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[*]
2+
indent_style = space
3+
indent_size = 2
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"swiftPM": {
3+
"swiftSDK": "swift-DEVELOPMENT-SNAPSHOT-2025-08-02-a_wasm-embedded"
4+
}
5+
}

DOMRefTypes/Package.swift

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// swift-tools-version: 6.0
2+
// The swift-tools-version declares the minimum version of Swift required to build this package.
3+
4+
//===----------------------------------------------------------------------===//
5+
//
6+
// This source file is part of the Swift open source project
7+
//
8+
// Copyright (c) 2024 Apple Inc. and the Swift project authors
9+
// Licensed under Apache License v2.0 with Runtime Library Exception
10+
//
11+
// See http://swift.org/LICENSE.txt for license information
12+
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
13+
//
14+
//===----------------------------------------------------------------------===//
15+
16+
import PackageDescription
17+
18+
let linkerSettings: [LinkerSetting] = [
19+
.unsafeFlags([
20+
"-Xclang-linker", "-mexec-model=reactor",
21+
"-Xlinker", "--export-if-defined=__main_argc_argv",
22+
]),
23+
]
24+
25+
let package = Package(
26+
name: "Guest",
27+
targets: [
28+
.target(
29+
name: "externref",
30+
),
31+
.executableTarget(
32+
name: "RefsTest",
33+
dependencies: ["externref"],
34+
linkerSettings: linkerSettings,
35+
),
36+
]
37+
)
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
@main
2+
struct Entrypoint {
3+
static func main() {
4+
var h1 = Document.global.createElement(name: JSString("h1"))
5+
let body = Document.global.body
6+
body.append(child: h1)
7+
h1.innerHTML = JSString("Hello, world!")
8+
}
9+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import externref
2+
3+
struct JSObject: ~Copyable {
4+
fileprivate let ref: ExternRefIndex
5+
6+
deinit {
7+
freeExternRef(ref)
8+
}
9+
}
10+
11+
struct JSArray: ~Copyable {
12+
private let ref: ExternRefIndex
13+
14+
init() {
15+
self.ref = emptyArray()
16+
}
17+
18+
func append(_ object: borrowing JSObject) {
19+
arrayPush(ref, object.ref)
20+
}
21+
}
22+
23+
struct JSString: ~Copyable {
24+
fileprivate let ref: ExternRefIndex
25+
26+
deinit {
27+
freeExternRef(self.ref)
28+
}
29+
}
30+
31+
extension JSString {
32+
init(_ string: StaticString) {
33+
self.ref = bridgeString(string.utf8Start, string.utf8CodeUnitCount)
34+
}
35+
36+
}
37+
38+
struct HTMLElement: ~Copyable {
39+
fileprivate let ref: ExternRefIndex
40+
41+
func append(child: borrowing HTMLElement) {
42+
appendChild(self.ref, child.ref)
43+
}
44+
45+
static let innerHTMLName = JSString("innerHTML")
46+
47+
var innerHTML: JSString {
48+
49+
get {
50+
JSString(ref: getProp(self.ref, Self.innerHTMLName.ref))
51+
}
52+
53+
set {
54+
setProp(self.ref, Self.innerHTMLName.ref, newValue.ref)
55+
}
56+
}
57+
}
58+
59+
struct Document: ~Copyable {
60+
fileprivate let object: JSObject
61+
62+
static let global = Document(object: JSObject(ref: getDocument()))
63+
64+
static let bodyName = JSString("body")
65+
66+
func createElement(name: borrowing JSString) -> HTMLElement {
67+
.init(ref: externref.createElement(name.ref))
68+
}
69+
70+
var body: HTMLElement {
71+
HTMLElement(ref: getProp(self.object.ref, Self.bodyName.ref))
72+
}
73+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
#include "dom.h"
2+
#include "refs.h"
3+
#include <stdint.h>
4+
5+
static __externref_t table[0];
6+
7+
typedef __externref_t (*__funcref funcref_t)(__externref_t);
8+
static funcref_t ftable[0];
9+
10+
static int nextAvailableTableIndex = 0;
11+
static const int defaultTableGrowSize = 256;
12+
13+
void freeExternRef(ExternRefIndex ref) {
14+
__builtin_wasm_table_set(table, ref.index, __builtin_wasm_ref_null_extern());
15+
}
16+
17+
ExternRefIndex tableAppend(__externref_t ref) {
18+
ExternRefIndex idx = { .index = nextAvailableTableIndex++ };
19+
20+
if (idx.index >= __builtin_wasm_table_size(table)) {
21+
__builtin_wasm_table_grow(table, __builtin_wasm_ref_null_extern(), defaultTableGrowSize);
22+
}
23+
24+
__builtin_wasm_table_set(table, idx.index, ref);
25+
26+
return idx;
27+
}
28+
29+
ExternRefIndex createElement(ExternRefIndex name) {
30+
return tableAppend(createElementJS(__builtin_wasm_table_get(table, name.index)));
31+
}
32+
33+
ExternRefIndex getDocument() {
34+
return tableAppend(getDocumentJS());
35+
}
36+
37+
ExternRefIndex getProp(ExternRefIndex self, ExternRefIndex name) {
38+
return tableAppend(getPropJS(__builtin_wasm_table_get(table, self.index), __builtin_wasm_table_get(table, name.index)));
39+
}
40+
41+
int getIntProp(ExternRefIndex self, ExternRefIndex name) {
42+
return getIntPropJS(__builtin_wasm_table_get(table, self.index), __builtin_wasm_table_get(table, name.index));
43+
}
44+
45+
void setProp(ExternRefIndex self, ExternRefIndex name, ExternRefIndex val) {
46+
setPropJS(__builtin_wasm_table_get(table, self.index), __builtin_wasm_table_get(table, name.index), __builtin_wasm_table_get(table, val.index));
47+
}
48+
49+
void appendChild(ExternRefIndex self, ExternRefIndex child) {
50+
appendChildJS(__builtin_wasm_table_get(table, self.index), __builtin_wasm_table_get(table, child.index));
51+
}
52+
53+
ExternRefIndex bridgeString(const uint8_t *str, size_t bytes) {
54+
return tableAppend(bridgeStringJS(str, bytes));
55+
}
56+
57+
ExternRefIndex emptyArray() {
58+
return tableAppend(emptyArrayJS());
59+
}
60+
61+
void arrayPush(ExternRefIndex self, ExternRefIndex element) {
62+
arrayPushJS(__builtin_wasm_table_get(table, self.index), __builtin_wasm_table_get(table, element.index));
63+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
#pragma once
2+
3+
#include <stdint.h>
4+
5+
__attribute__((import_module("js")))
6+
__attribute__((import_name("getDocument"))) __externref_t
7+
getDocumentJS(void);
8+
9+
__attribute__((import_module("js")))
10+
__attribute__((import_name("emptyDictionary"))) __externref_t
11+
emptyDictionaryJS(void);
12+
13+
__attribute__((import_module("js")))
14+
__attribute__((import_name("emptyArray"))) __externref_t
15+
emptyArrayJS(void);
16+
17+
__attribute__((import_module("js")))
18+
__attribute__((import_name("arrayPush"))) void
19+
arrayPushJS(__externref_t self, __externref_t element);
20+
21+
__attribute__((import_module("js")))
22+
__attribute__((import_name("bridgeString"))) __externref_t
23+
bridgeStringJS(const uint8_t *str, uint32_t bytes);
24+
25+
__attribute__((import_module("js")))
26+
__attribute__((import_name("setProp"))) void
27+
setPropJS(__externref_t self, __externref_t name, __externref_t val);
28+
29+
__attribute__((import_module("js")))
30+
__attribute__((import_name("getProp"))) __externref_t
31+
getPropJS(__externref_t self, __externref_t name);
32+
33+
34+
__attribute__((import_module("js")))
35+
__attribute__((import_name("getIntProp"))) int
36+
getIntPropJS(__externref_t self, __externref_t name);
37+
38+
__attribute__((import_module("document")))
39+
__attribute__((import_name("createElement"))) __externref_t
40+
createElementJS(__externref_t name);
41+
42+
__attribute__((import_module("document")))
43+
__attribute__((import_name("appendChild"))) void
44+
appendChildJS(__externref_t self, __externref_t child);
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#pragma once
2+
3+
#include <stddef.h>
4+
#include <stdint.h>
5+
6+
#ifdef __cplusplus
7+
extern "C" {
8+
#endif /* __cplusplus */
9+
10+
typedef struct ExternRefIndex {
11+
int index;
12+
} ExternRefIndex;
13+
14+
void freeExternRef(ExternRefIndex);
15+
16+
ExternRefIndex createElement(ExternRefIndex name);
17+
ExternRefIndex getDocument(void);
18+
ExternRefIndex getProp(ExternRefIndex self, ExternRefIndex name);
19+
int getIntProp(ExternRefIndex self, ExternRefIndex name);
20+
void setProp(ExternRefIndex self, ExternRefIndex name, ExternRefIndex val);
21+
void appendChild(ExternRefIndex self, ExternRefIndex child);
22+
ExternRefIndex bridgeString(const uint8_t *str, size_t bytes);
23+
ExternRefIndex emptyArray(void);
24+
void arrayPush(ExternRefIndex self, ExternRefIndex element);
25+
26+
#ifdef __cplusplus
27+
}
28+
#endif /* __cplusplus */

DOMRefTypes/index.html

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<!-- This source file is part of the Swift open source project
2+
3+
Copyright (c) 2024 Apple Inc. and the Swift project authors
4+
Licensed under Apache License v2.0 with Runtime Library Exception
5+
6+
See https://swift.org/LICENSE.txt for license information
7+
See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -->
8+
9+
<html>
10+
<head>
11+
<meta charset="utf-8">
12+
<title>Swift Audio Workstation</title>
13+
<script type="module" src="index.js">
14+
</script>
15+
<style>
16+
body {
17+
background-color: black;
18+
padding: 1rem;
19+
}
20+
h1 {
21+
font-family: sans-serif;
22+
color: white;
23+
}
24+
</style>
25+
</head>
26+
<body>
27+
<h1 id="wasm-logger">Wasm Reference Types Demo</h1>
28+
</body>
29+
</html>

DOMRefTypes/index.js

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift open source project
4+
//
5+
// Copyright (c) 2024 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
14+
const decoder = new TextDecoder();
15+
16+
const moduleInstances = [];
17+
function wasmMemoryAsString(i, address, byteCount) {
18+
return decoder.decode(moduleInstances[i].exports.memory.buffer.slice(address, address + byteCount));
19+
}
20+
21+
function wasmMemoryAsFloat32Array(i, address, byteCount) {
22+
return new Float32Array(moduleInstances[i].exports.memory.buffer.slice(address, address + byteCount));
23+
}
24+
25+
const importsObject = {
26+
js: {
27+
getDocument: () => document,
28+
emptyDictionary: () => { return {} },
29+
emptyArray: () => [],
30+
bridgeString: (address, count) => wasmMemoryAsString(0, address, count),
31+
setProp: (self, name, val) => { self[name] = val; },
32+
getProp: (self, name) => self[name],
33+
getIntProp: (self, name) => self[name],
34+
},
35+
36+
document: {
37+
createElement: (name) => document.createElement(name),
38+
appendChild: (element, child) => element.appendChild(child),
39+
}
40+
};
41+
42+
const { instance, module } = await WebAssembly.instantiateStreaming(
43+
fetch(".build/release/RefsTest.wasm"),
44+
importsObject
45+
);
46+
moduleInstances.push(instance);
47+
48+
instance.exports.__main_argc_argv();

0 commit comments

Comments
 (0)