Skip to content

Commit 97aabc5

Browse files
authored
Add DOMRefTypes example (#41)
This provides an example of DOM manipulation using reference types. With all optimizations enabled, final `.build/release/RefsTest.wasm` binary built using Embedded Swift SDK is only 1.2 kB.
1 parent f100996 commit 97aabc5

File tree

12 files changed

+412
-0
lines changed

12 files changed

+412
-0
lines changed

.github/workflows/pull_request.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,16 @@ jobs:
3535
enable_windows_checks: false
3636
swift_flags: --package-path WebGPUDemo
3737

38+
dom-ref-types:
39+
name: Build DOMRefTypes Demo
40+
uses: swiftlang/github-workflows/.github/workflows/swift_package_test.yml@main
41+
with:
42+
enable_embedded_wasm_sdk_build: true
43+
enable_linux_checks: false
44+
enable_macos_checks: false
45+
enable_windows_checks: false
46+
swift_flags: --package-path DOMRefTypes
47+
3848
soundness:
3949
name: Soundness
4050
uses: swiftlang/github-workflows/.github/workflows/soundness.yml@main

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.2
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: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift open source project
4+
//
5+
// Copyright (c) 2024-2025 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+
@main
14+
struct Entrypoint {
15+
static func main() {
16+
var h1 = Document.global.createElement(name: JSString("h1"))
17+
let body = Document.global.body
18+
body.append(child: h1)
19+
h1.innerHTML = JSString("Hello, world!")
20+
}
21+
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift open source project
4+
//
5+
// Copyright (c) 2024-2025 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+
import externref
14+
15+
struct JSObject: ~Copyable {
16+
fileprivate let ref: ExternRefIndex
17+
18+
deinit {
19+
freeExternRef(ref)
20+
}
21+
}
22+
23+
struct JSArray: ~Copyable {
24+
private let ref: ExternRefIndex
25+
26+
init() {
27+
self.ref = emptyArray()
28+
}
29+
30+
func append(_ object: borrowing JSObject) {
31+
arrayPush(ref, object.ref)
32+
}
33+
}
34+
35+
struct JSString: ~Copyable {
36+
fileprivate let ref: ExternRefIndex
37+
38+
deinit {
39+
freeExternRef(self.ref)
40+
}
41+
}
42+
43+
extension JSString {
44+
init(_ string: StaticString) {
45+
self.ref = bridgeString(string.utf8Start, string.utf8CodeUnitCount)
46+
}
47+
48+
}
49+
50+
struct HTMLElement: ~Copyable {
51+
fileprivate let ref: ExternRefIndex
52+
53+
func append(child: borrowing HTMLElement) {
54+
appendChild(self.ref, child.ref)
55+
}
56+
57+
static let innerHTMLName = JSString("innerHTML")
58+
59+
var innerHTML: JSString {
60+
61+
get {
62+
JSString(ref: getProp(self.ref, Self.innerHTMLName.ref))
63+
}
64+
65+
set {
66+
setProp(self.ref, Self.innerHTMLName.ref, newValue.ref)
67+
}
68+
}
69+
}
70+
71+
struct Document: ~Copyable {
72+
fileprivate let object: JSObject
73+
74+
static let global = Document(object: JSObject(ref: getDocument()))
75+
76+
static let bodyName = JSString("body")
77+
78+
func createElement(name: borrowing JSString) -> HTMLElement {
79+
.init(ref: externref.createElement(name.ref))
80+
}
81+
82+
var body: HTMLElement {
83+
HTMLElement(ref: getProp(self.object.ref, Self.bodyName.ref))
84+
}
85+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift open source project
4+
//
5+
// Copyright (c) 2024-2025 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+
#include "dom.h"
14+
#include "refs.h"
15+
#include <stdint.h>
16+
17+
static __externref_t table[0];
18+
19+
typedef __externref_t (*__funcref funcref_t)(__externref_t);
20+
static funcref_t ftable[0];
21+
22+
static int nextAvailableTableIndex = 0;
23+
static const int defaultTableGrowSize = 256;
24+
25+
void freeExternRef(ExternRefIndex ref) {
26+
__builtin_wasm_table_set(table, ref.index, __builtin_wasm_ref_null_extern());
27+
}
28+
29+
ExternRefIndex tableAppend(__externref_t ref) {
30+
ExternRefIndex idx = { .index = nextAvailableTableIndex++ };
31+
32+
if (idx.index >= __builtin_wasm_table_size(table)) {
33+
__builtin_wasm_table_grow(table, __builtin_wasm_ref_null_extern(), defaultTableGrowSize);
34+
}
35+
36+
__builtin_wasm_table_set(table, idx.index, ref);
37+
38+
return idx;
39+
}
40+
41+
ExternRefIndex createElement(ExternRefIndex name) {
42+
return tableAppend(createElementJS(__builtin_wasm_table_get(table, name.index)));
43+
}
44+
45+
ExternRefIndex getDocument() {
46+
return tableAppend(getDocumentJS());
47+
}
48+
49+
ExternRefIndex getProp(ExternRefIndex self, ExternRefIndex name) {
50+
return tableAppend(getPropJS(__builtin_wasm_table_get(table, self.index), __builtin_wasm_table_get(table, name.index)));
51+
}
52+
53+
int getIntProp(ExternRefIndex self, ExternRefIndex name) {
54+
return getIntPropJS(__builtin_wasm_table_get(table, self.index), __builtin_wasm_table_get(table, name.index));
55+
}
56+
57+
void setProp(ExternRefIndex self, ExternRefIndex name, ExternRefIndex val) {
58+
setPropJS(__builtin_wasm_table_get(table, self.index), __builtin_wasm_table_get(table, name.index), __builtin_wasm_table_get(table, val.index));
59+
}
60+
61+
void appendChild(ExternRefIndex self, ExternRefIndex child) {
62+
appendChildJS(__builtin_wasm_table_get(table, self.index), __builtin_wasm_table_get(table, child.index));
63+
}
64+
65+
ExternRefIndex bridgeString(const uint8_t *str, size_t bytes) {
66+
return tableAppend(bridgeStringJS(str, bytes));
67+
}
68+
69+
ExternRefIndex emptyArray() {
70+
return tableAppend(emptyArrayJS());
71+
}
72+
73+
void arrayPush(ExternRefIndex self, ExternRefIndex element) {
74+
arrayPushJS(__builtin_wasm_table_get(table, self.index), __builtin_wasm_table_get(table, element.index));
75+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift open source project
4+
//
5+
// Copyright (c) 2024-2025 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+
#pragma once
14+
15+
#include <stdint.h>
16+
17+
__attribute__((import_module("js")))
18+
__attribute__((import_name("getDocument"))) __externref_t
19+
getDocumentJS(void);
20+
21+
__attribute__((import_module("js")))
22+
__attribute__((import_name("emptyDictionary"))) __externref_t
23+
emptyDictionaryJS(void);
24+
25+
__attribute__((import_module("js")))
26+
__attribute__((import_name("emptyArray"))) __externref_t
27+
emptyArrayJS(void);
28+
29+
__attribute__((import_module("js")))
30+
__attribute__((import_name("arrayPush"))) void
31+
arrayPushJS(__externref_t self, __externref_t element);
32+
33+
__attribute__((import_module("js")))
34+
__attribute__((import_name("bridgeString"))) __externref_t
35+
bridgeStringJS(const uint8_t *str, uint32_t bytes);
36+
37+
__attribute__((import_module("js")))
38+
__attribute__((import_name("setProp"))) void
39+
setPropJS(__externref_t self, __externref_t name, __externref_t val);
40+
41+
__attribute__((import_module("js")))
42+
__attribute__((import_name("getProp"))) __externref_t
43+
getPropJS(__externref_t self, __externref_t name);
44+
45+
46+
__attribute__((import_module("js")))
47+
__attribute__((import_name("getIntProp"))) int
48+
getIntPropJS(__externref_t self, __externref_t name);
49+
50+
__attribute__((import_module("document")))
51+
__attribute__((import_name("createElement"))) __externref_t
52+
createElementJS(__externref_t name);
53+
54+
__attribute__((import_module("document")))
55+
__attribute__((import_name("appendChild"))) void
56+
appendChildJS(__externref_t self, __externref_t child);
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift open source project
4+
//
5+
// Copyright (c) 2024-2025 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+
#pragma once
14+
15+
#include <stddef.h>
16+
#include <stdint.h>
17+
18+
#ifdef __cplusplus
19+
extern "C" {
20+
#endif /* __cplusplus */
21+
22+
typedef struct ExternRefIndex {
23+
int index;
24+
} ExternRefIndex;
25+
26+
void freeExternRef(ExternRefIndex);
27+
28+
ExternRefIndex createElement(ExternRefIndex name);
29+
ExternRefIndex getDocument(void);
30+
ExternRefIndex getProp(ExternRefIndex self, ExternRefIndex name);
31+
int getIntProp(ExternRefIndex self, ExternRefIndex name);
32+
void setProp(ExternRefIndex self, ExternRefIndex name, ExternRefIndex val);
33+
void appendChild(ExternRefIndex self, ExternRefIndex child);
34+
ExternRefIndex bridgeString(const uint8_t *str, size_t bytes);
35+
ExternRefIndex emptyArray(void);
36+
void arrayPush(ExternRefIndex self, ExternRefIndex element);
37+
38+
#ifdef __cplusplus
39+
}
40+
#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 for WebAssembly Examples</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>

0 commit comments

Comments
 (0)