├── .github
└── workflows
│ └── ci.yml
├── .gitignore
├── .gitmodules
├── Cargo.toml
├── README.md
├── WASI_SDK
├── build.sh
├── crates
├── repl-example
│ ├── .cargo
│ │ └── config
│ ├── Cargo.toml
│ └── src
│ │ └── main.rs
├── spidermonkey-wasm-sys
│ ├── .cargo
│ │ └── config
│ ├── Cargo.toml
│ ├── LICENSE
│ ├── build.rs
│ ├── src
│ │ ├── api.cpp
│ │ ├── api.h
│ │ ├── jsclass.rs
│ │ ├── jsgc.rs
│ │ ├── jsrealm.rs
│ │ ├── jsval.rs
│ │ └── lib.rs
│ └── tests
│ │ └── integration.rs
└── spidermonkey-wasm
│ ├── .cargo
│ └── config
│ ├── Cargo.toml
│ ├── LICENSE
│ ├── src
│ ├── compilation_options.rs
│ ├── handle.rs
│ ├── js.rs
│ ├── lib.rs
│ ├── rooted.rs
│ ├── runtime.rs
│ └── utf8_source.rs
│ └── tests
│ ├── compile.rs
│ ├── context_opts.rs
│ ├── eval.rs
│ └── realm_opts.rs
└── update-wasi-sdk.sh
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | pull_request:
8 |
9 | jobs:
10 | checks:
11 | name: Test + Lint
12 | runs-on: ubuntu-latest
13 | steps:
14 | - uses: actions/checkout@v2
15 | with:
16 | submodules: recursive
17 |
18 | - name: Download WASI-SDK
19 | run: sudo ./update-wasi-sdk.sh
20 |
21 | - name: Download SpiderMonkey build artifacts
22 | run: |
23 | cd crates/spidermonkey-wasm-sys/spidermonkey-wasm-build/
24 | ./download.sh
25 | cd -
26 |
27 | - name: Install Rust
28 | uses: actions-rs/toolchain@v1
29 | with:
30 | profile: default
31 | toolchain: 1.57.0
32 | default: true
33 | components: clippy, rustfmt
34 | target: wasm32-wasi
35 |
36 | - name: Cache .cargo
37 | uses: actions/cache@v1
38 | with:
39 | path: ~/.cargo
40 | key: ${{ runner.os }}-cargo-${{ hashFiles('Cargo.toml') }}
41 | restore-keys: |
42 | ${{ runner.os }}-cargo-${{ hashFiles('Cargo.toml') }}
43 | ${{ runner.os }}-cargo
44 |
45 | - name: Cache cargo target
46 | uses: actions/cache@v1
47 | with:
48 | path: target
49 | key: ${{ runner.os }}-cargo-target-${{ hashFiles('Cargo.toml') }}
50 | restore-keys: |
51 | ${{ runner.os }}-cargo-target-${{ hashFiles('Cargo.toml') }}
52 | ${{ runner.os }}-cargo-target
53 |
54 | - name: Download wasmtime
55 | env:
56 | WASMTIME_VERSION: 0.35.0
57 | run: |
58 | wget -nv 'https://github.com/bytecodealliance/wasmtime/releases/download/v${{ env.WASMTIME_VERSION }}/wasmtime-v${{ env.WASMTIME_VERSION }}-x86_64-linux.tar.xz' -O /tmp/wasmtime.tar.xz
59 | mkdir /tmp/wasmtime
60 | tar xvf /tmp/wasmtime.tar.xz --strip-components=1 -C /tmp/wasmtime
61 | echo "/tmp/wasmtime" >> $GITHUB_PATH
62 |
63 |
64 | - name: Install cargo-wasi
65 | run: cargo install cargo-wasi
66 |
67 | - name: Debug build
68 | run: |
69 | cd crates/spidermonkey-wasm
70 | cargo build
71 | cd -
72 |
73 | - name: Release build
74 | run: |
75 | cd crates/spidermonkey-wasm
76 | cargo build --release
77 | cd -
78 |
79 | - name: Test sys
80 | run: |
81 | cd crates/spidermonkey-wasm-sys
82 | cargo wasi test
83 | cd -
84 |
85 | - name: Test spidermonkey-wasm
86 | run: |
87 | cd crates/spidermonkey-wasm
88 | cargo wasi test
89 | cd -
90 |
91 | - name: Check repl-example
92 | run: |
93 | ./build.sh check -p repl-example
94 |
95 | - name: Lint
96 | run: cargo fmt -- --check
97 |
98 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /target
2 | Cargo.lock
3 | .DS_Store
4 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "crates/spidermonkey-wasm-sys/spidermonkey-wasm-build"]
2 | path = crates/spidermonkey-wasm-sys/spidermonkey-wasm-build
3 | url = https://github.com/bytecodealliance/spidermonkey-wasm-build
4 |
--------------------------------------------------------------------------------
/Cargo.toml:
--------------------------------------------------------------------------------
1 | [workspace]
2 | members = [
3 | "crates/spidermonkey-wasm-sys",
4 | "crates/spidermonkey-wasm",
5 | "crates/repl-example",
6 | ]
7 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
spidermonkey-wasm-rs
3 |
4 |
A Bytecode Alliance project
5 |
6 | Experimental Rust bindings and generic builtins for SpiderMonkey for the wasm32-wasi
target
7 |
8 |

9 |
10 |
11 | ## Requirements
12 | - [cargo-wasi](https://github.com/bytecodealliance/cargo-wasi) for testing
13 | - Rust 1.57.0
14 | - WASI-SDK 12 at /opt/wasi-sdk/wasi-sdk-12.0 (can be downloaded by executing `sudo ./update-wasi-sdk.sh`)
15 |
16 | ## Development
17 | - `git submodule update --recursive --init` to pull in [spidermonkey-wasm-build](https://github.com/bytecodealliance/spidermonkey-wasm-build)
18 | - `cd crates/spidermonkey-wasm-sys/spidermonkey-wasm-build && ./download.sh` to pull in SpiderMonkey build artifacts
19 | - In `cd crates/spidermonkey-wasm`, run:
20 | - `cargo build` to build
21 | - `cargo wasi test` to run tests
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/WASI_SDK:
--------------------------------------------------------------------------------
1 | 12
2 |
--------------------------------------------------------------------------------
/build.sh:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env bash
2 |
3 | set -ex
4 |
5 | export CXX="/opt/wasi-sdk/wasi-sdk-12.0/bin/clang++ --sysroot=/opt/wasi-sdk/wasi-sdk-12.0/share/wasi-sysroot"
6 | export CXXFLAGS="-fno-exceptions -DRUST_CXX_NO_EXCEPTIONS"
7 | export AR="/opt/wasi-sdk/wasi-sdk-12.0/bin/ar"
8 | export LIBCLANG_PATH="/opt/wasi-sdk/wasi-sdk-12.0/share/wasi-sysroot/lib/wasm32-wasi"
9 | export LIBCLANG_RT_PATH="/opt/wasi-sdk/wasi-sdk-12.0/lib/clang/11.0.0/lib/wasi"
10 |
11 | if [ $# -eq 0 ]; then
12 | cargo build --release --target=wasm32-wasi
13 | else
14 | cargo "$@" --release --target=wasm32-wasi
15 | fi
16 |
--------------------------------------------------------------------------------
/crates/repl-example/.cargo/config:
--------------------------------------------------------------------------------
1 | [build]
2 | target = "wasm32-wasi"
3 |
--------------------------------------------------------------------------------
/crates/repl-example/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "repl-example"
3 | version = "0.1.0"
4 | edition = "2021"
5 |
6 | [dependencies]
7 | spidermonkey-wasm = { path = "../spidermonkey-wasm" }
8 | anyhow = "1.0.55"
9 | promptly = "=0.3.0"
10 |
--------------------------------------------------------------------------------
/crates/repl-example/src/main.rs:
--------------------------------------------------------------------------------
1 | use spidermonkey_wasm::{
2 | compilation_options::CompilationOptions,
3 | handle::{HandleObject, HandleValue},
4 | js, root,
5 | runtime::Runtime,
6 | utf8_source::Utf8Source,
7 | JSAutoRealm, JSContext, JSGCReason, JSGCStatus, OnJSGCCallback,
8 | };
9 |
10 | extern "C" fn gc_callback(
11 | _: *mut JSContext,
12 | status: JSGCStatus,
13 | reason: JSGCReason,
14 | _: *mut std::os::raw::c_void,
15 | ) {
16 | println!("GC: {:?}", reason);
17 | println!("GC: {:?}", status);
18 | }
19 |
20 | fn main() {
21 | let runtime = Runtime::new().unwrap();
22 | let global_class = js::make_default_global_class();
23 | let realm_opts = js::make_default_realm_options();
24 | let context = runtime.cx();
25 |
26 | root!(with(context);
27 | let global_object = js::new_global_object(context, &global_class, &realm_opts);
28 | );
29 |
30 | let global_object_handle = global_object.handle();
31 | let _ar = JSAutoRealm::new(context, global_object_handle.get());
32 |
33 | runtime.set_gc_callback(OnJSGCCallback(gc_callback));
34 |
35 | do_loop(&runtime, global_object_handle);
36 | }
37 |
38 | fn do_loop(runtime: &Runtime, global: HandleObject) {
39 | let mut lineno = 1;
40 |
41 | loop {
42 | let startline: usize = lineno;
43 | let input: String = buffer(&runtime, global, &mut lineno, startline);
44 |
45 | eval(&runtime, &input, startline);
46 |
47 | js::run_jobs(runtime.cx());
48 | }
49 | }
50 |
51 | fn eval(runtime: &Runtime, buffer: &str, at: usize) {
52 | let context = runtime.cx();
53 | let compilation_opts = CompilationOptions::new(context, at, false, "repl".into()).unwrap();
54 | let mut script = Utf8Source::new(context, buffer).unwrap();
55 |
56 | root!(with(context); let mut ret_val = js::undefined_value(););
57 |
58 | runtime
59 | .eval(&compilation_opts, &mut script, ret_val.mut_handle())
60 | .unwrap_or_else(|_| {
61 | js::report_exception(context).unwrap();
62 | });
63 |
64 | runtime.maybe_gc();
65 |
66 | let result = fmt_result(&runtime, ret_val.handle());
67 |
68 | println!("{}", result);
69 | }
70 |
71 | fn fmt_result(runtime: &Runtime, result: HandleValue) -> String {
72 | let context = runtime.cx();
73 |
74 | if result.get().is_string() {
75 | root!(with(context); let js_string = result.get().to_string(););
76 | return js::to_rust_string(context, js_string.handle());
77 | }
78 |
79 | root!(with(context); let mut js_string = js::to_string(context, result) ;);
80 |
81 | js::to_rust_string(context, js_string.handle())
82 | }
83 |
84 | fn buffer(runtime: &Runtime, global: HandleObject, lineno: &mut usize, startline: usize) -> String {
85 | let mut buffer: String = "".into();
86 |
87 | loop {
88 | let prompt = if startline == *lineno {
89 | "sm-wasm > "
90 | } else {
91 | ".. "
92 | };
93 |
94 | let input: String = promptly::prompt(prompt).unwrap();
95 |
96 | buffer.push_str(&input);
97 | *lineno += 1;
98 |
99 | if js::is_compilable_unit(runtime.cx(), global, &buffer) {
100 | break;
101 | }
102 | }
103 |
104 | return buffer;
105 | }
106 |
--------------------------------------------------------------------------------
/crates/spidermonkey-wasm-sys/.cargo/config:
--------------------------------------------------------------------------------
1 | [build]
2 | target = "wasm32-wasi"
3 |
4 | [env]
5 | CXX = "/opt/wasi-sdk/wasi-sdk-12.0/bin/clang++ --sysroot=/opt/wasi-sdk/wasi-sdk-12.0/share/wasi-sysroot"
6 | CXXFLAGS = "-fno-exceptions -DRUST_CXX_NO_EXCEPTIONS"
7 | AR = "/opt/wasi-sdk/wasi-sdk-12.0/bin/ar"
8 | LIBCLANG_PATH = "/opt/wasi-sdk/wasi-sdk-12.0/share/wasi-sysroot/lib/wasm32-wasi"
9 | LIBCLANG_RT_PATH = "/opt/wasi-sdk/wasi-sdk-12.0/lib/clang/11.0.0/lib/wasi"
10 |
--------------------------------------------------------------------------------
/crates/spidermonkey-wasm-sys/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "spidermonkey-wasm-sys"
3 | version = "0.1.0"
4 | edition = "2018"
5 | include = [
6 | "spidermonkey-wasm-build",
7 | "src/**/*",
8 | "build.rs",
9 | "vendor/**/*",
10 | ".cargo/**/*",
11 | ]
12 | license = "MPL-2.0"
13 |
14 | [profile.release]
15 | opt-level = 3
16 |
17 | [dependencies]
18 | cxx = "1.0"
19 | link-cplusplus = { version = "1.0", features = ["nothing"] }
20 |
21 | [build-dependencies]
22 | fs_extra = "1.2.0"
23 | walkdir = "2"
24 | cxx-build = "1.0"
25 |
26 | [features]
27 | moz_debug = []
--------------------------------------------------------------------------------
/crates/spidermonkey-wasm-sys/LICENSE:
--------------------------------------------------------------------------------
1 | Mozilla Public License Version 2.0
2 | ==================================
3 |
4 | 1. Definitions
5 | --------------
6 |
7 | 1.1. "Contributor"
8 | means each individual or legal entity that creates, contributes to
9 | the creation of, or owns Covered Software.
10 |
11 | 1.2. "Contributor Version"
12 | means the combination of the Contributions of others (if any) used
13 | by a Contributor and that particular Contributor's Contribution.
14 |
15 | 1.3. "Contribution"
16 | means Covered Software of a particular Contributor.
17 |
18 | 1.4. "Covered Software"
19 | means Source Code Form to which the initial Contributor has attached
20 | the notice in Exhibit A, the Executable Form of such Source Code
21 | Form, and Modifications of such Source Code Form, in each case
22 | including portions thereof.
23 |
24 | 1.5. "Incompatible With Secondary Licenses"
25 | means
26 |
27 | (a) that the initial Contributor has attached the notice described
28 | in Exhibit B to the Covered Software; or
29 |
30 | (b) that the Covered Software was made available under the terms of
31 | version 1.1 or earlier of the License, but not also under the
32 | terms of a Secondary License.
33 |
34 | 1.6. "Executable Form"
35 | means any form of the work other than Source Code Form.
36 |
37 | 1.7. "Larger Work"
38 | means a work that combines Covered Software with other material, in
39 | a separate file or files, that is not Covered Software.
40 |
41 | 1.8. "License"
42 | means this document.
43 |
44 | 1.9. "Licensable"
45 | means having the right to grant, to the maximum extent possible,
46 | whether at the time of the initial grant or subsequently, any and
47 | all of the rights conveyed by this License.
48 |
49 | 1.10. "Modifications"
50 | means any of the following:
51 |
52 | (a) any file in Source Code Form that results from an addition to,
53 | deletion from, or modification of the contents of Covered
54 | Software; or
55 |
56 | (b) any new file in Source Code Form that contains any Covered
57 | Software.
58 |
59 | 1.11. "Patent Claims" of a Contributor
60 | means any patent claim(s), including without limitation, method,
61 | process, and apparatus claims, in any patent Licensable by such
62 | Contributor that would be infringed, but for the grant of the
63 | License, by the making, using, selling, offering for sale, having
64 | made, import, or transfer of either its Contributions or its
65 | Contributor Version.
66 |
67 | 1.12. "Secondary License"
68 | means either the GNU General Public License, Version 2.0, the GNU
69 | Lesser General Public License, Version 2.1, the GNU Affero General
70 | Public License, Version 3.0, or any later versions of those
71 | licenses.
72 |
73 | 1.13. "Source Code Form"
74 | means the form of the work preferred for making modifications.
75 |
76 | 1.14. "You" (or "Your")
77 | means an individual or a legal entity exercising rights under this
78 | License. For legal entities, "You" includes any entity that
79 | controls, is controlled by, or is under common control with You. For
80 | purposes of this definition, "control" means (a) the power, direct
81 | or indirect, to cause the direction or management of such entity,
82 | whether by contract or otherwise, or (b) ownership of more than
83 | fifty percent (50%) of the outstanding shares or beneficial
84 | ownership of such entity.
85 |
86 | 2. License Grants and Conditions
87 | --------------------------------
88 |
89 | 2.1. Grants
90 |
91 | Each Contributor hereby grants You a world-wide, royalty-free,
92 | non-exclusive license:
93 |
94 | (a) under intellectual property rights (other than patent or trademark)
95 | Licensable by such Contributor to use, reproduce, make available,
96 | modify, display, perform, distribute, and otherwise exploit its
97 | Contributions, either on an unmodified basis, with Modifications, or
98 | as part of a Larger Work; and
99 |
100 | (b) under Patent Claims of such Contributor to make, use, sell, offer
101 | for sale, have made, import, and otherwise transfer either its
102 | Contributions or its Contributor Version.
103 |
104 | 2.2. Effective Date
105 |
106 | The licenses granted in Section 2.1 with respect to any Contribution
107 | become effective for each Contribution on the date the Contributor first
108 | distributes such Contribution.
109 |
110 | 2.3. Limitations on Grant Scope
111 |
112 | The licenses granted in this Section 2 are the only rights granted under
113 | this License. No additional rights or licenses will be implied from the
114 | distribution or licensing of Covered Software under this License.
115 | Notwithstanding Section 2.1(b) above, no patent license is granted by a
116 | Contributor:
117 |
118 | (a) for any code that a Contributor has removed from Covered Software;
119 | or
120 |
121 | (b) for infringements caused by: (i) Your and any other third party's
122 | modifications of Covered Software, or (ii) the combination of its
123 | Contributions with other software (except as part of its Contributor
124 | Version); or
125 |
126 | (c) under Patent Claims infringed by Covered Software in the absence of
127 | its Contributions.
128 |
129 | This License does not grant any rights in the trademarks, service marks,
130 | or logos of any Contributor (except as may be necessary to comply with
131 | the notice requirements in Section 3.4).
132 |
133 | 2.4. Subsequent Licenses
134 |
135 | No Contributor makes additional grants as a result of Your choice to
136 | distribute the Covered Software under a subsequent version of this
137 | License (see Section 10.2) or under the terms of a Secondary License (if
138 | permitted under the terms of Section 3.3).
139 |
140 | 2.5. Representation
141 |
142 | Each Contributor represents that the Contributor believes its
143 | Contributions are its original creation(s) or it has sufficient rights
144 | to grant the rights to its Contributions conveyed by this License.
145 |
146 | 2.6. Fair Use
147 |
148 | This License is not intended to limit any rights You have under
149 | applicable copyright doctrines of fair use, fair dealing, or other
150 | equivalents.
151 |
152 | 2.7. Conditions
153 |
154 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
155 | in Section 2.1.
156 |
157 | 3. Responsibilities
158 | -------------------
159 |
160 | 3.1. Distribution of Source Form
161 |
162 | All distribution of Covered Software in Source Code Form, including any
163 | Modifications that You create or to which You contribute, must be under
164 | the terms of this License. You must inform recipients that the Source
165 | Code Form of the Covered Software is governed by the terms of this
166 | License, and how they can obtain a copy of this License. You may not
167 | attempt to alter or restrict the recipients' rights in the Source Code
168 | Form.
169 |
170 | 3.2. Distribution of Executable Form
171 |
172 | If You distribute Covered Software in Executable Form then:
173 |
174 | (a) such Covered Software must also be made available in Source Code
175 | Form, as described in Section 3.1, and You must inform recipients of
176 | the Executable Form how they can obtain a copy of such Source Code
177 | Form by reasonable means in a timely manner, at a charge no more
178 | than the cost of distribution to the recipient; and
179 |
180 | (b) You may distribute such Executable Form under the terms of this
181 | License, or sublicense it under different terms, provided that the
182 | license for the Executable Form does not attempt to limit or alter
183 | the recipients' rights in the Source Code Form under this License.
184 |
185 | 3.3. Distribution of a Larger Work
186 |
187 | You may create and distribute a Larger Work under terms of Your choice,
188 | provided that You also comply with the requirements of this License for
189 | the Covered Software. If the Larger Work is a combination of Covered
190 | Software with a work governed by one or more Secondary Licenses, and the
191 | Covered Software is not Incompatible With Secondary Licenses, this
192 | License permits You to additionally distribute such Covered Software
193 | under the terms of such Secondary License(s), so that the recipient of
194 | the Larger Work may, at their option, further distribute the Covered
195 | Software under the terms of either this License or such Secondary
196 | License(s).
197 |
198 | 3.4. Notices
199 |
200 | You may not remove or alter the substance of any license notices
201 | (including copyright notices, patent notices, disclaimers of warranty,
202 | or limitations of liability) contained within the Source Code Form of
203 | the Covered Software, except that You may alter any license notices to
204 | the extent required to remedy known factual inaccuracies.
205 |
206 | 3.5. Application of Additional Terms
207 |
208 | You may choose to offer, and to charge a fee for, warranty, support,
209 | indemnity or liability obligations to one or more recipients of Covered
210 | Software. However, You may do so only on Your own behalf, and not on
211 | behalf of any Contributor. You must make it absolutely clear that any
212 | such warranty, support, indemnity, or liability obligation is offered by
213 | You alone, and You hereby agree to indemnify every Contributor for any
214 | liability incurred by such Contributor as a result of warranty, support,
215 | indemnity or liability terms You offer. You may include additional
216 | disclaimers of warranty and limitations of liability specific to any
217 | jurisdiction.
218 |
219 | 4. Inability to Comply Due to Statute or Regulation
220 | ---------------------------------------------------
221 |
222 | If it is impossible for You to comply with any of the terms of this
223 | License with respect to some or all of the Covered Software due to
224 | statute, judicial order, or regulation then You must: (a) comply with
225 | the terms of this License to the maximum extent possible; and (b)
226 | describe the limitations and the code they affect. Such description must
227 | be placed in a text file included with all distributions of the Covered
228 | Software under this License. Except to the extent prohibited by statute
229 | or regulation, such description must be sufficiently detailed for a
230 | recipient of ordinary skill to be able to understand it.
231 |
232 | 5. Termination
233 | --------------
234 |
235 | 5.1. The rights granted under this License will terminate automatically
236 | if You fail to comply with any of its terms. However, if You become
237 | compliant, then the rights granted under this License from a particular
238 | Contributor are reinstated (a) provisionally, unless and until such
239 | Contributor explicitly and finally terminates Your grants, and (b) on an
240 | ongoing basis, if such Contributor fails to notify You of the
241 | non-compliance by some reasonable means prior to 60 days after You have
242 | come back into compliance. Moreover, Your grants from a particular
243 | Contributor are reinstated on an ongoing basis if such Contributor
244 | notifies You of the non-compliance by some reasonable means, this is the
245 | first time You have received notice of non-compliance with this License
246 | from such Contributor, and You become compliant prior to 30 days after
247 | Your receipt of the notice.
248 |
249 | 5.2. If You initiate litigation against any entity by asserting a patent
250 | infringement claim (excluding declaratory judgment actions,
251 | counter-claims, and cross-claims) alleging that a Contributor Version
252 | directly or indirectly infringes any patent, then the rights granted to
253 | You by any and all Contributors for the Covered Software under Section
254 | 2.1 of this License shall terminate.
255 |
256 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all
257 | end user license agreements (excluding distributors and resellers) which
258 | have been validly granted by You or Your distributors under this License
259 | prior to termination shall survive termination.
260 |
261 | ************************************************************************
262 | * *
263 | * 6. Disclaimer of Warranty *
264 | * ------------------------- *
265 | * *
266 | * Covered Software is provided under this License on an "as is" *
267 | * basis, without warranty of any kind, either expressed, implied, or *
268 | * statutory, including, without limitation, warranties that the *
269 | * Covered Software is free of defects, merchantable, fit for a *
270 | * particular purpose or non-infringing. The entire risk as to the *
271 | * quality and performance of the Covered Software is with You. *
272 | * Should any Covered Software prove defective in any respect, You *
273 | * (not any Contributor) assume the cost of any necessary servicing, *
274 | * repair, or correction. This disclaimer of warranty constitutes an *
275 | * essential part of this License. No use of any Covered Software is *
276 | * authorized under this License except under this disclaimer. *
277 | * *
278 | ************************************************************************
279 |
280 | ************************************************************************
281 | * *
282 | * 7. Limitation of Liability *
283 | * -------------------------- *
284 | * *
285 | * Under no circumstances and under no legal theory, whether tort *
286 | * (including negligence), contract, or otherwise, shall any *
287 | * Contributor, or anyone who distributes Covered Software as *
288 | * permitted above, be liable to You for any direct, indirect, *
289 | * special, incidental, or consequential damages of any character *
290 | * including, without limitation, damages for lost profits, loss of *
291 | * goodwill, work stoppage, computer failure or malfunction, or any *
292 | * and all other commercial damages or losses, even if such party *
293 | * shall have been informed of the possibility of such damages. This *
294 | * limitation of liability shall not apply to liability for death or *
295 | * personal injury resulting from such party's negligence to the *
296 | * extent applicable law prohibits such limitation. Some *
297 | * jurisdictions do not allow the exclusion or limitation of *
298 | * incidental or consequential damages, so this exclusion and *
299 | * limitation may not apply to You. *
300 | * *
301 | ************************************************************************
302 |
303 | 8. Litigation
304 | -------------
305 |
306 | Any litigation relating to this License may be brought only in the
307 | courts of a jurisdiction where the defendant maintains its principal
308 | place of business and such litigation shall be governed by laws of that
309 | jurisdiction, without reference to its conflict-of-law provisions.
310 | Nothing in this Section shall prevent a party's ability to bring
311 | cross-claims or counter-claims.
312 |
313 | 9. Miscellaneous
314 | ----------------
315 |
316 | This License represents the complete agreement concerning the subject
317 | matter hereof. If any provision of this License is held to be
318 | unenforceable, such provision shall be reformed only to the extent
319 | necessary to make it enforceable. Any law or regulation which provides
320 | that the language of a contract shall be construed against the drafter
321 | shall not be used to construe this License against a Contributor.
322 |
323 | 10. Versions of the License
324 | ---------------------------
325 |
326 | 10.1. New Versions
327 |
328 | Mozilla Foundation is the license steward. Except as provided in Section
329 | 10.3, no one other than the license steward has the right to modify or
330 | publish new versions of this License. Each version will be given a
331 | distinguishing version number.
332 |
333 | 10.2. Effect of New Versions
334 |
335 | You may distribute the Covered Software under the terms of the version
336 | of the License under which You originally received the Covered Software,
337 | or under the terms of any subsequent version published by the license
338 | steward.
339 |
340 | 10.3. Modified Versions
341 |
342 | If you create software not governed by this License, and you want to
343 | create a new license for such software, you may create and use a
344 | modified version of this License if you rename the license and remove
345 | any references to the name of the license steward (except to note that
346 | such modified license differs from this License).
347 |
348 | 10.4. Distributing Source Code Form that is Incompatible With Secondary
349 | Licenses
350 |
351 | If You choose to distribute Source Code Form that is Incompatible With
352 | Secondary Licenses under the terms of this version of the License, the
353 | notice described in Exhibit B of this License must be attached.
354 |
355 | Exhibit A - Source Code Form License Notice
356 | -------------------------------------------
357 |
358 | This Source Code Form is subject to the terms of the Mozilla Public
359 | License, v. 2.0. If a copy of the MPL was not distributed with this
360 | file, You can obtain one at http://mozilla.org/MPL/2.0/.
361 |
362 | If it is not possible or desirable to put the notice in a particular
363 | file, then You may include the notice in a location (such as a LICENSE
364 | file in a relevant directory) where a recipient would be likely to look
365 | for such a notice.
366 |
367 | You may add additional accurate notices of copyright ownership.
368 |
369 | Exhibit B - "Incompatible With Secondary Licenses" Notice
370 | ---------------------------------------------------------
371 |
372 | This Source Code Form is "Incompatible With Secondary Licenses", as
373 | defined by the Mozilla Public License, v. 2.0.
--------------------------------------------------------------------------------
/crates/spidermonkey-wasm-sys/build.rs:
--------------------------------------------------------------------------------
1 | use cxx_build::bridge as cxxbridge;
2 | use fs_extra::dir;
3 | use std::{
4 | env,
5 | path::{Path, PathBuf},
6 | };
7 | use walkdir::WalkDir;
8 |
9 | static SPIDERMONKEY_BUILD_DIR: &str = "spidermonkey-wasm-build";
10 |
11 | fn main() {
12 | let out_dir = env::var_os("OUT_DIR")
13 | .map(PathBuf::from)
14 | .expect("could not find OUT_DIR");
15 |
16 | let profile = derive_profile();
17 |
18 | let source_dir = PathBuf::from(SPIDERMONKEY_BUILD_DIR).join(profile);
19 | let source_include_dir = source_dir.join("include");
20 | let source_lib_dir = source_dir.join("lib");
21 |
22 | let out_include_dir = out_dir.join("include");
23 | let out_lib_dir = out_dir.join("lib");
24 |
25 | if !source_dir.exists() {
26 | panic!("SpiderMonkey build directory not found. Try updating git submodules via git submodule update --recursive --init");
27 | }
28 |
29 | if !source_include_dir.exists() || !source_lib_dir.exists() {
30 | panic!("SpiderMonkey build artifacts not found.");
31 | }
32 |
33 | if !out_include_dir.exists() {
34 | let copy_options = dir::CopyOptions::new();
35 | dir::copy(source_include_dir, &out_dir, ©_options)
36 | .expect("Could not copy header files to OUT directory");
37 | }
38 |
39 | if !out_lib_dir.exists() {
40 | let copy_options = dir::CopyOptions::new();
41 | dir::copy(source_lib_dir, &out_dir, ©_options)
42 | .expect("Could not copy lib directory to OUT directory");
43 | }
44 |
45 | println!("cargo:rustc-link-search={}", out_lib_dir.display());
46 |
47 | let libclang_path = env::var("LIBCLANG_PATH").expect("LIBCLANG_PATH to be defined");
48 | let libclang_rt_path = env::var("LIBCLANG_RT_PATH").expect("LIBCLANG_RT_PATH to be defined");
49 |
50 | println!("cargo:rustc-link-search=native={}", libclang_path);
51 | println!("cargo:rustc-link-search=native={}", libclang_rt_path);
52 |
53 | println!("cargo:rustc-link-lib=static=jsrust");
54 | println!("cargo:rustc-link-lib=static=js_static");
55 | println!("cargo:rustc-link-lib=static=c++abi");
56 | println!("cargo:rustc-link-lib=static=clang_rt.builtins-wasm32");
57 | bridge(&out_lib_dir, &out_include_dir, &profile);
58 |
59 | println!("cargo:rerun-if-changed=build.rs");
60 | println!("cargo:rerun-if-changed=src/api.h");
61 | println!("cargo:rerun-if-changed=src/api.cpp");
62 | println!("cargo:rerun-if-changed=src/lib.rs");
63 | }
64 |
65 | fn bridge(lib_dir: impl AsRef, include_dir: impl AsRef, profile: &str) {
66 | let mut builder = cxxbridge("src/lib.rs");
67 |
68 | if profile == "debug-build" {
69 | builder.define("DEBUG", None);
70 | }
71 |
72 | builder
73 | .cpp(true)
74 | .cpp_link_stdlib("c++")
75 | .file("src/api.cpp")
76 | .include(include_dir)
77 | .include("src")
78 | .target("wasm32-wasi")
79 | .flag_if_supported("-Wall")
80 | .flag_if_supported("-Werror")
81 | .flag_if_supported("-Qunused-arguments")
82 | .flag_if_supported("-fno-sized-deallocation")
83 | .flag_if_supported("-fno-exceptions")
84 | .flag_if_supported("-fno-aligned-new")
85 | .flag_if_supported("-mthread-model")
86 | .flag_if_supported("single")
87 | .flag_if_supported("-fPIC")
88 | .flag_if_supported("-fno-rtti")
89 | .flag_if_supported("-fno-math-errno")
90 | .flag_if_supported("-pipe")
91 | .flag_if_supported("-fno-omit-frame-pointer")
92 | .flag_if_supported("-funwind-tables")
93 | .flag_if_supported("-Wno-invalid-offsetof")
94 | .flag_if_supported("-std=gnu++17");
95 |
96 | for entry in WalkDir::new(lib_dir)
97 | .sort_by_file_name()
98 | .into_iter()
99 | .filter_map(|e| e.ok())
100 | {
101 | let entry_path = entry.path();
102 | if entry_path.is_file() && entry_path.extension().unwrap() == "o" {
103 | builder.object(entry_path);
104 | }
105 | }
106 |
107 | builder.opt_level(2).compile("spidermonkey-wasm");
108 | }
109 |
110 | fn derive_profile() -> &'static str {
111 | let mut profile = env::var("PROFILE").unwrap_or_else(|_| "debug".into());
112 |
113 | if cfg!(feature = "moz_debug") {
114 | profile = "debug".into();
115 | }
116 |
117 | match profile.as_str() {
118 | "debug" => "debug-build",
119 | "release" => "release-build",
120 | _ => panic!("Unsupported profile: {}", profile),
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/crates/spidermonkey-wasm-sys/src/api.cpp:
--------------------------------------------------------------------------------
1 | #include "api.h"
2 |
3 | std::unique_ptr MakeDefaultGlobalClass() {
4 | const JSClass defaultGlobal = {
5 | "Global",
6 | JSCLASS_GLOBAL_FLAGS,
7 | &JS::DefaultGlobalClassOps
8 | };
9 |
10 | return std::make_unique(defaultGlobal);
11 | }
12 |
13 | std::unique_ptr MakeDefaultRealmOptions() {
14 | return std::make_unique();
15 | }
16 |
17 | std::unique_ptr MakeOwningCompileOptions(JSContext* context, const CompileOptionsParams &opts) {
18 | JS::CompileOptions jsOpts(context);
19 |
20 | if (opts.force_full_parse) {
21 | jsOpts.setForceFullParse();
22 | }
23 |
24 | jsOpts.setFileAndLine(opts.file.data(), opts.lineno);
25 |
26 | auto owningOpts = std::make_unique(context);
27 |
28 | if (!owningOpts->copy(context, jsOpts)) {
29 | owningOpts.reset(nullptr);
30 | }
31 |
32 | return owningOpts;
33 | }
34 |
35 | bool InitDefaultSelfHostedCode(JSContext* context) {
36 | return JS::InitSelfHostedCode(context);
37 | }
38 |
39 | std::unique_ptr MakeUtf8UnitSourceText(JSContext* context, rust::Str units, size_t length, JS::SourceOwnership ownership) {
40 | auto src = std::make_unique();
41 | if (!src->init(context, units.data(), length, ownership)) {
42 | src.reset(nullptr);
43 | }
44 |
45 | return src;
46 | }
47 |
48 | bool Utf8SourceEvaluate(JSContext* context, const JS::OwningCompileOptions& opts, Utf8UnitSourceText& src, JS::MutableHandle rval) {
49 | return JS::Evaluate(context, opts, src, rval);
50 | }
51 |
52 | JSScript* Utf8SourceCompile(JSContext* context, const JS::OwningCompileOptions& opts, Utf8UnitSourceText& src) {
53 | return JS::Compile(context, opts, src);
54 | }
55 |
56 | std::unique_ptr MakeUninitPersistentRootedObject() {
57 | return std::make_unique();
58 | }
59 |
60 | void InitPersistentRootedObject(JS::PersistentRootedObject& obj, JSContext* context, JSObject* initial) {
61 | obj.init(context, initial);
62 | }
63 |
64 | uint32_t DefaultHeapMaxBytes() {
65 | return JS::DefaultHeapMaxBytes;
66 | }
67 |
68 | bool Utf8IsCompilableUnit(JSContext* context, JS::HandleObject global, rust::Str source) {
69 | return JS_Utf8BufferIsCompilableUnit(context, global, source.data(), source.length());
70 | }
71 |
72 | rust::String JSStringToRustString(JSContext* context, JS::HandleString str) {
73 | JS::UniqueChars chars = JS_EncodeStringToUTF8(context, str);
74 | return rust::String(chars.get());
75 | }
76 |
77 | bool ReportException(JSContext* context) {
78 | JS::ExceptionStack stack(context);
79 |
80 | if (!JS::StealPendingExceptionStack(context, &stack)) {
81 | return false;
82 | }
83 |
84 | JS::ErrorReportBuilder report(context);
85 | if (!report.init(context, stack, JS::ErrorReportBuilder::WithSideEffects)) {
86 | return false;
87 | }
88 |
89 | JS::PrintError(stderr, report, false);
90 |
91 | return true;
92 | }
93 |
94 | void JS_SetGCCallbackWrapper(JSContext* context, JSGCCallback callback) {
95 | JS_SetGCCallback(context, callback, nullptr);
96 | }
97 |
98 | const JSClassOps* DefaultGlobalClassOps() {
99 | return &JS::DefaultGlobalClassOps;
100 | }
101 |
102 | uint32_t JSClassGlobalFlags() {
103 | return JSCLASS_GLOBAL_FLAGS;
104 | }
105 |
106 |
107 |
--------------------------------------------------------------------------------
/crates/spidermonkey-wasm-sys/src/api.h:
--------------------------------------------------------------------------------
1 | #include "js-confdefs.h"
2 | #include "jsapi.h"
3 | #include "jsfriendapi.h"
4 | #include "js/ArrayBuffer.h"
5 | #include "js/BuildId.h"
6 | #include "js/CompilationAndEvaluation.h"
7 | #include "js/ContextOptions.h"
8 | #include "js/Conversions.h"
9 | #include "js/Date.h"
10 | #include "js/Equality.h"
11 | #include "js/ForOfIterator.h"
12 | #include "js/Id.h"
13 | #include "js/Initialization.h"
14 | #include "js/JSON.h"
15 | #include "js/MemoryMetrics.h"
16 | #include "js/Modules.h"
17 | #include "js/Object.h"
18 | #include "js/Promise.h"
19 | #include "js/PropertySpec.h"
20 | #include "js/Proxy.h"
21 | #include "js/Realm.h"
22 | #include "js/RegExp.h"
23 | #include "js/SavedFrameAPI.h"
24 | #include "js/ScalarType.h"
25 | #include "js/SourceText.h"
26 | #include "js/Stream.h"
27 | #include "js/String.h"
28 | #include "js/StructuredClone.h"
29 | #include "js/Symbol.h"
30 | #include "js/Utility.h"
31 | #include "js/Warnings.h"
32 | #include "js/WasmModule.h"
33 | #include "js/shadow/Object.h"
34 | #include "js/shadow/Shape.h"
35 | #include "js/friend/DOMProxy.h"
36 | #include "js/friend/ErrorMessages.h"
37 | #include "js/friend/WindowProxy.h"
38 | #include "js/experimental/JitInfo.h"
39 | #include "js/experimental/TypedData.h"
40 | #include "spidermonkey-wasm-sys/src/lib.rs.h"
41 | #include "rust/cxx.h"
42 |
43 | struct CompileOptionsParams;
44 |
45 | typedef JS::SourceText Utf8UnitSourceText;
46 |
47 | uint32_t DefaultHeapMaxBytes();
48 |
49 | std::unique_ptr MakeDefaultGlobalClass();
50 | std::unique_ptr MakeDefaultRealmOptions();
51 | std::unique_ptr MakeOwningCompileOptions(JSContext* context, const CompileOptionsParams &opts);
52 | std::unique_ptr MakeUtf8UnitSourceText(JSContext* context, rust::Str units, size_t length, JS::SourceOwnership ownership);
53 |
54 | bool InitDefaultSelfHostedCode(JSContext* context);
55 | bool InitUtf8UnitSourceText(JSContext* context, Utf8UnitSourceText& src, rust::Str units, size_t length, JS::SourceOwnership ownership);
56 | bool Utf8SourceEvaluate(JSContext* context, const JS::OwningCompileOptions& opts, Utf8UnitSourceText& src, JS::MutableHandle rval);
57 | JSScript* Utf8SourceCompile(JSContext* context, const JS::OwningCompileOptions& opts, Utf8UnitSourceText& src);
58 |
59 | std::unique_ptr MakeUninitPersistentRootedObject();
60 | void InitPersistentRootedObject(JS::PersistentRootedObject& obj, JSContext* context, JSObject* initial);
61 |
62 | bool Utf8IsCompilableUnit(JSContext* context, JS::HandleObject global, rust::Str source);
63 |
64 | rust::String JSStringToRustString(JSContext* context, JS::HandleString str);
65 |
66 | bool ReportException(JSContext* context);
67 |
68 | void JS_SetGCCallbackWrapper(JSContext* context, JSGCCallback callback);
69 |
70 | const JSClassOps* DefaultGlobalClassOps();
71 |
72 | uint32_t JSClassGlobalFlags();
73 |
--------------------------------------------------------------------------------
/crates/spidermonkey-wasm-sys/src/jsclass.rs:
--------------------------------------------------------------------------------
1 | #[repr(C)]
2 | #[repr(align(8))]
3 | pub struct JSClass {
4 | pub name: *const ::std::os::raw::c_char,
5 | pub flags: u32,
6 | pub c_ops: *const crate::jsffi::JSClassOps,
7 | pub spec: *const crate::jsffi::ClassSpec,
8 | pub ext: *const crate::jsffi::ClassExtension,
9 | pub o_ops: *const crate::jsffi::ObjectOps,
10 | }
11 |
--------------------------------------------------------------------------------
/crates/spidermonkey-wasm-sys/src/jsgc.rs:
--------------------------------------------------------------------------------
1 | use crate::jsffi::{
2 | AutoRooterListHeads, GeckoProfilerThread, JSContext, JSObject, JSScript, JSString, Realm,
3 | Value, Zone,
4 | };
5 | use std::{cell::UnsafeCell, marker::PhantomData};
6 | use std::{ffi::c_void, ptr};
7 |
8 | // -- ROOTING
9 |
10 | #[allow(non_snake_case)]
11 | #[repr(C)]
12 | pub struct RootingContext {
13 | pub stackRoots_: [u32; 15usize],
14 | pub autoGCRooters_: AutoRooterListHeads,
15 | pub geckoProfiler_: GeckoProfilerThread,
16 | pub realm_: *mut Realm,
17 | pub zone_: *mut Zone,
18 | pub nativeStackLimit: [usize; 3usize],
19 | pub wasiRecursionDepth: u32,
20 | }
21 |
22 | #[repr(C)]
23 | #[derive(Debug)]
24 | pub struct Rooted {
25 | pub stack: *mut *mut Rooted<*mut c_void>,
26 | pub prev: *mut Rooted<*mut c_void>,
27 | pub ptr: T,
28 | }
29 |
30 | impl Default for Rooted {
31 | fn default() -> Self {
32 | Self {
33 | stack: ptr::null_mut(),
34 | prev: ptr::null_mut(),
35 | ptr: unsafe { std::mem::zeroed() },
36 | }
37 | }
38 | }
39 |
40 | impl Rooted {
41 | pub unsafe fn init(&mut self, context: *mut JSContext, initial: T)
42 | where
43 | T: JSRootKind,
44 | {
45 | self.ptr = initial;
46 | let kind = T::root_kind() as usize;
47 | let rooting_context = context as *mut RootingContext;
48 | let stack: *mut *mut Rooted<*mut c_void> =
49 | &mut (*rooting_context).stackRoots_[kind] as *mut _ as *mut _;
50 |
51 | self.stack = stack;
52 | self.prev = *stack;
53 | *stack = self as *mut _ as usize as _;
54 | }
55 |
56 | pub unsafe fn remove_from_root_stack(&mut self) {
57 | assert!(*self.stack == self as *mut _ as usize as _);
58 | *self.stack = self.prev;
59 | }
60 | }
61 |
62 | #[repr(i8)]
63 | #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
64 | pub enum RootKind {
65 | BaseShape = 0,
66 | JitCode = 1,
67 | Scope = 2,
68 | Object = 3,
69 | Script = 4,
70 | Shape = 5,
71 | String = 6,
72 | Symbol = 7,
73 | BigInt = 8,
74 | RegExpShared = 9,
75 | GetterSetter = 10,
76 | PropMap = 11,
77 | Id = 12,
78 | Value = 13,
79 | Traceable = 14,
80 | Limit = 15,
81 | }
82 |
83 | pub trait JSRootKind {
84 | fn root_kind() -> RootKind;
85 | }
86 |
87 | impl JSRootKind for *mut JSObject {
88 | fn root_kind() -> RootKind {
89 | RootKind::Object
90 | }
91 | }
92 |
93 | impl JSRootKind for Value {
94 | fn root_kind() -> RootKind {
95 | RootKind::Value
96 | }
97 | }
98 |
99 | impl JSRootKind for *mut JSString {
100 | fn root_kind() -> RootKind {
101 | RootKind::String
102 | }
103 | }
104 |
105 | impl JSRootKind for *mut JSScript {
106 | fn root_kind() -> RootKind {
107 | RootKind::Script
108 | }
109 | }
110 |
111 | // HANDLE
112 |
113 | #[repr(C)]
114 | #[derive(Debug)]
115 | pub struct Handle {
116 | pub ptr: *const T,
117 | pub _marker: PhantomData>,
118 | }
119 |
120 | #[repr(C)]
121 | #[derive(Debug)]
122 | pub struct MutableHandle {
123 | pub ptr: *mut T,
124 | pub _marker: PhantomData>,
125 | }
126 |
127 | #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
128 | #[repr(u32)]
129 | pub enum JSGCParamKey {
130 | JsgcMaxBytes = 0,
131 | JsgcMaxNurseryBytes = 2,
132 | JsgcBytes = 3,
133 | JsgcNumber = 4,
134 | JsgcIncrementalGcEnabled = 5,
135 | JsgcPerZoneGcEnabled = 6,
136 | JsgcUnusedChunks = 7,
137 | JsgcTotalChunks = 8,
138 | JsgcSliceTimeBudgetMs = 9,
139 | JsgcMarkStackLimit = 10,
140 | JsgcHighFrequencyTimeLimit = 11,
141 | JsgcSmallHeapSizeMax = 12,
142 | JsgcLargeHeapSizeMin = 13,
143 | JsgcHighFrequencySmallHeapGrowth = 14,
144 | JsgcHighFrequencyLargeHeapGrowth = 15,
145 | JsgcLowFrequencyHeapGrowth = 16,
146 | JsgcAllocationThreshold = 19,
147 | JsgcMinEmptyChunkCount = 21,
148 | JsgcMaxEmptyChunkCount = 22,
149 | JsgcCompactingEnabled = 23,
150 | JsgcSmallHeapIncrementalLimit = 25,
151 | JsgcLargeHeapIncrementalLimit = 26,
152 | JsgcNurseryFreeThresholdForIdleCollection = 27,
153 | JsgcPretenureThreshold = 28,
154 | JsgcPretenureGroupThreshold = 29,
155 | JsgcNurseryFreeThresholdForIdleCollectionPercent = 30,
156 | JsgcMinNurseryBytes = 31,
157 | JsgcMinLastDitchGcPeriod = 32,
158 | JsgcZoneAllocDelayKb = 33,
159 | JsgcNurseryBytes = 34,
160 | JsgcMallocThresholdBase = 35,
161 | JsgcIncrementalWeakmapEnabled = 37,
162 | JsgcChunkBytes = 38,
163 | JsgcHelperThreadRatio = 39,
164 | JsgcMaxHelperThreads = 40,
165 | JsgcHelperThreadCount = 41,
166 | JsgcPretenureStringThreshold = 42,
167 | JsgcStopPretenureStringThreshold = 43,
168 | JsgcMajorGcNumber = 44,
169 | JsgcMinorGcNumber = 45,
170 | JsgcNurseryTimeoutForIdleCollectionMs = 46,
171 | JsgcSystemPageSizeKb = 47,
172 | JsgcUrgentThresholdMb = 48,
173 | }
174 |
175 | #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
176 | #[repr(u32)]
177 | pub enum JSGCOptions {
178 | Normal = 0,
179 | Shrink = 1,
180 | Shutdown = 2,
181 | }
182 |
183 | #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
184 | #[repr(i8)]
185 | pub enum JSGCReason {
186 | FirstFirefoxReason = 33,
187 | FirstReservedReason = 90,
188 | Api = 0,
189 | EagerAllocTrigger = 1,
190 | DestroyRuntime = 2,
191 | RootsRemoved = 3,
192 | LastDitch = 4,
193 | TooMuchMalloc = 5,
194 | AllocTrigger = 6,
195 | DebugGc = 7,
196 | CompartmentRevived = 8,
197 | Reset = 9,
198 | OutOfNursery = 10,
199 | EvictNursery = 11,
200 | DelayedAtomsGc = 12,
201 | SharedMemoryLimit = 13,
202 | IdleTimeCollection = 14,
203 | BgTaskFinished = 15,
204 | AbortGc = 16,
205 | FullWholeCellBuffer = 17,
206 | FullGenericBuffer = 18,
207 | FullValueBuffer = 19,
208 | FullCellPtrObjBuffer = 20,
209 | FullSlotBuffer = 21,
210 | FullShapeBuffer = 22,
211 | TooMuchWasmMemory = 23,
212 | DisableGenerationalGc = 24,
213 | FinishGc = 25,
214 | PrepareForTracing = 26,
215 | Unused4 = 27,
216 | FullCellPtrStrBuffer = 28,
217 | TooMuchJitCode = 29,
218 | FullCellPtrBigintBuffer = 30,
219 | Unused5 = 31,
220 | NurseryMallocBuffers = 32,
221 | ComponentUtils = 34,
222 | MemPressure = 35,
223 | CcFinished = 36,
224 | CcForced = 37,
225 | LoadEnd = 38,
226 | Unused3 = 39,
227 | PageHide = 40,
228 | NsjscontextDestroy = 41,
229 | WorkerShutdown = 42,
230 | SetDocShell = 43,
231 | DomUtils = 44,
232 | DomIpc = 45,
233 | DomWorker = 46,
234 | InterSliceGc = 47,
235 | Unused1 = 48,
236 | FullGcTimer = 49,
237 | ShutdownCc = 50,
238 | Unused2 = 51,
239 | UserInactive = 52,
240 | XpconnectShutdown = 53,
241 | Docshell = 54,
242 | HtmlParser = 55,
243 | Reserved2 = 91,
244 | Reserved3 = 92,
245 | Reserved4 = 93,
246 | Reserved5 = 94,
247 | Reserved6 = 95,
248 | Reserved7 = 96,
249 | Reserved8 = 97,
250 | Reserved9 = 98,
251 | NoReason = 99,
252 | NumReasons = 100,
253 | }
254 |
255 | #[repr(u32)]
256 | #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
257 | pub enum JSGCStatus {
258 | JsgcBegin = 0,
259 | JsgcEnd = 1,
260 | }
261 |
262 | #[repr(transparent)]
263 | pub struct OnJSGCCallback(
264 | pub extern "C" fn(
265 | context: *mut JSContext,
266 | status: JSGCStatus,
267 | reason: JSGCReason,
268 | data: *mut std::os::raw::c_void,
269 | ),
270 | );
271 |
--------------------------------------------------------------------------------
/crates/spidermonkey-wasm-sys/src/jsrealm.rs:
--------------------------------------------------------------------------------
1 | use crate::jsffi::{self, EnterRealm, JSContext, JSObject, LeaveRealm};
2 |
3 | #[repr(C)]
4 | #[derive(Debug)]
5 | #[allow(non_snake_case)]
6 | pub struct JSAutoRealm {
7 | pub cx_: *mut jsffi::JSContext,
8 | pub oldRealm_: *mut jsffi::Realm,
9 | }
10 |
11 | impl JSAutoRealm {
12 | pub fn new(context: *mut JSContext, target: *mut JSObject) -> Self {
13 | Self {
14 | cx_: context,
15 | oldRealm_: unsafe { EnterRealm(context, target) },
16 | }
17 | }
18 | }
19 |
20 | impl Drop for JSAutoRealm {
21 | fn drop(&mut self) {
22 | unsafe {
23 | LeaveRealm(self.cx_, self.oldRealm_);
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/crates/spidermonkey-wasm-sys/src/jsval.rs:
--------------------------------------------------------------------------------
1 | #[repr(C)]
2 | #[allow(non_snake_case)]
3 | #[derive(Copy, Clone)]
4 | pub struct Value {
5 | pub asBits_: u64,
6 | }
7 |
--------------------------------------------------------------------------------
/crates/spidermonkey-wasm-sys/src/lib.rs:
--------------------------------------------------------------------------------
1 | extern crate link_cplusplus;
2 |
3 | pub mod jsclass;
4 | pub mod jsgc;
5 | pub mod jsrealm;
6 | pub mod jsval;
7 | pub use cxx::UniquePtr;
8 |
9 | use cxx::{type_id, ExternType};
10 |
11 | macro_rules! impl_extern_type {
12 | ($type:ty, $id:expr, $kind:ty) => {
13 | unsafe impl ExternType for $type {
14 | type Id = type_id!($id);
15 | type Kind = $kind;
16 | }
17 | };
18 | }
19 |
20 | impl_extern_type!(jsffi::JSAutoRealm, "JSAutoRealm", cxx::kind::Opaque);
21 | impl_extern_type!(jsffi::JSClass, "JSClass", cxx::kind::Opaque);
22 | impl_extern_type!(jsffi::RootingContext, "RootingContext", cxx::kind::Opaque);
23 | impl_extern_type!(jsffi::RootKind, "JS::RootKind", cxx::kind::Trivial);
24 | impl_extern_type!(jsffi::Value, "JS::Value", cxx::kind::Trivial);
25 |
26 | impl_extern_type!(jsffi::RootedObject, "JS::RootedObject", cxx::kind::Opaque);
27 | impl_extern_type!(jsffi::RootedValue, "JS::RootedValue", cxx::kind::Opaque);
28 | impl_extern_type!(jsffi::RootedString, "JS::RootedString", cxx::kind::Opaque);
29 | impl_extern_type!(jsffi::RootedScript, "JS::RootedScript", cxx::kind::Opaque);
30 |
31 | impl_extern_type!(jsffi::HandleValue, "JS::HandleValue", cxx::kind::Trivial);
32 | impl_extern_type!(jsffi::HandleString, "JS::HandleString", cxx::kind::Trivial);
33 | impl_extern_type!(jsffi::HandleObject, "JS::HandleObject", cxx::kind::Trivial);
34 | impl_extern_type!(jsffi::HandleScript, "JS::HandleScript", cxx::kind::Trivial);
35 |
36 | impl_extern_type!(
37 | jsffi::MutableHandleObject,
38 | "JS::MutableHandleObject",
39 | cxx::kind::Opaque
40 | );
41 | impl_extern_type!(
42 | jsffi::MutableHandleValue,
43 | "JS::MutableHandleValue",
44 | cxx::kind::Trivial
45 | );
46 |
47 | impl_extern_type!(jsffi::JSGCParamKey, "JSGCParamKey", cxx::kind::Trivial);
48 | impl_extern_type!(jsffi::GCOptions, "JS::GCOptions", cxx::kind::Trivial);
49 | impl_extern_type!(jsffi::GCReason, "JS::GCReason", cxx::kind::Trivial);
50 | impl_extern_type!(jsffi::JSGCCallback, "JSGCCallback", cxx::kind::Trivial);
51 | impl_extern_type!(jsffi::JSGCStatus, "JSGCStatus", cxx::kind::Trivial);
52 |
53 | #[cxx::bridge]
54 | pub mod jsffi {
55 |
56 | #[repr(u32)]
57 | #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
58 | #[namespace = "JS"]
59 | enum OnNewGlobalHookOption {
60 | FireOnNewGlobalHook = 0,
61 | DontFireOnNewGlobalHook = 1,
62 | }
63 |
64 | #[repr(u32)]
65 | #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
66 | #[namespace = "JS"]
67 | enum SourceOwnership {
68 | Borrowed = 0,
69 | TakeOwnership = 1,
70 | }
71 |
72 | #[repr(i32)]
73 | #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
74 | #[namespace = "JS"]
75 | pub enum WeakRefSpecifier {
76 | Disabled = 0,
77 | EnabledWithCleanupSome = 1,
78 | EnabledWithoutCleanupSome = 2,
79 | }
80 |
81 | struct CompileOptionsParams {
82 | force_full_parse: bool,
83 | file: String,
84 | lineno: usize,
85 | }
86 |
87 | unsafe extern "C++" {
88 | include!("api.h");
89 |
90 | type JSAutoRealm = crate::jsrealm::JSAutoRealm;
91 | type JSObject;
92 | type JSRuntime;
93 | type JSContext;
94 | type JSClass = crate::jsclass::JSClass;
95 | type JSScript;
96 | type JSPrincipals;
97 | type RootingContext = crate::jsgc::RootingContext;
98 | type Utf8UnitSourceText;
99 | type JSString;
100 | type JSClassOps;
101 | #[namespace = "js"]
102 | type ClassSpec;
103 | #[namespace = "js"]
104 | type ClassExtension;
105 | #[namespace = "js"]
106 | type ObjectOps;
107 |
108 | type JSGCStatus = crate::jsgc::JSGCStatus;
109 | #[namespace = "JS"]
110 | type ContextOptions;
111 | #[namespace = "JS"]
112 | type RealmCreationOptions;
113 | #[namespace = "JS"]
114 | type SourceOwnership;
115 | #[namespace = "JS"]
116 | type WeakRefSpecifier;
117 | #[namespace = "JS"]
118 | type Value = crate::jsval::Value;
119 | #[namespace = "JS"]
120 | type PersistentRootedObject;
121 | #[namespace = "JS"]
122 | type RootedObject = crate::jsgc::Rooted<*mut JSObject>;
123 | #[namespace = "JS"]
124 | type RootedValue = crate::jsgc::Rooted;
125 | #[namespace = "JS"]
126 | type RootedString = crate::jsgc::Rooted<*mut JSString>;
127 | #[namespace = "JS"]
128 | type RootedScript = crate::jsgc::Rooted<*mut JSScript>;
129 | #[namespace = "JS"]
130 | type HandleObject = crate::jsgc::Handle<*mut JSObject>;
131 | #[namespace = "JS"]
132 | type HandleScript = crate::jsgc::Handle<*mut JSScript>;
133 | #[namespace = "JS"]
134 | type HandleValue = crate::jsgc::Handle;
135 | #[namespace = "JS"]
136 | type HandleString = crate::jsgc::Handle<*mut JSString>;
137 | #[namespace = "JS"]
138 | type MutableHandleObject = crate::jsgc::MutableHandle<*mut JSObject>;
139 | #[namespace = "JS"]
140 | type MutableHandleValue = crate::jsgc::MutableHandle;
141 | #[namespace = "JS"]
142 | type RootKind = crate::jsgc::RootKind;
143 | #[namespace = "JS"]
144 | type AutoRooterListHeads;
145 | #[namespace = "js"]
146 | type GeckoProfilerThread;
147 | #[namespace = "JS"]
148 | type Realm;
149 | #[namespace = "JS"]
150 | type Zone;
151 | #[namespace = "JS"]
152 | type RealmOptions;
153 | #[namespace = "JS"]
154 | type OnNewGlobalHookOption;
155 | #[namespace = "JS"]
156 | type OwningCompileOptions;
157 | #[namespace = "JS"]
158 | type ReadOnlyCompileOptions;
159 | #[namespace = "JS"]
160 | unsafe fn DisableIncrementalGC(context: *mut JSContext);
161 | #[namespace = "JS"]
162 | unsafe fn PrepareForFullGC(context: *mut JSContext);
163 | #[namespace = "JS"]
164 | type GCOptions = crate::jsgc::JSGCOptions;
165 | #[namespace = "JS"]
166 | type GCReason = crate::jsgc::JSGCReason;
167 | #[namespace = "JS"]
168 | unsafe fn NonIncrementalGC(context: *mut JSContext, options: GCOptions, reason: GCReason);
169 | type JSGCParamKey = crate::jsgc::JSGCParamKey;
170 |
171 | type JSGCCallback = crate::jsgc::OnJSGCCallback;
172 |
173 | unsafe fn JS_SetGCParameter(context: *mut JSContext, param_key: JSGCParamKey, value: u32);
174 | unsafe fn JS_MaybeGC(context: *mut JSContext);
175 |
176 | // TODO: Support *mut c_void so that we can drop the wrapper definition and use the
177 | // real function type exposed by SpiderMonkey
178 | unsafe fn JS_SetGCCallbackWrapper(context: *mut JSContext, callback: JSGCCallback);
179 |
180 | unsafe fn JS_GetRuntime(context: *mut JSContext) -> *mut JSRuntime;
181 | unsafe fn JS_NewContext(max_bytes: u32, parent: *mut JSRuntime) -> *mut JSContext;
182 | unsafe fn JS_DestroyContext(context: *mut JSContext);
183 | fn DefaultHeapMaxBytes() -> u32;
184 |
185 | fn JS_Init() -> bool;
186 | fn JS_ShutDown();
187 |
188 | #[rust_name = "default_global_class_ops"]
189 | fn DefaultGlobalClassOps() -> *const JSClassOps;
190 |
191 | #[rust_name = "js_class_global_flags"]
192 | fn JSClassGlobalFlags() -> u32;
193 |
194 | #[rust_name = "make_default_realm_options"]
195 | fn MakeDefaultRealmOptions() -> UniquePtr;
196 |
197 | unsafe fn MakeOwningCompileOptions(
198 | context: *mut JSContext,
199 | opts: &CompileOptionsParams,
200 | ) -> UniquePtr;
201 |
202 | unsafe fn InitDefaultSelfHostedCode(context: *mut JSContext) -> bool;
203 | #[namespace = "js"]
204 | unsafe fn UseInternalJobQueues(context: *mut JSContext) -> bool;
205 |
206 | unsafe fn JS_NewPlainObject(context: *mut JSContext) -> *mut JSObject;
207 | unsafe fn JS_NewGlobalObject(
208 | context: *mut JSContext,
209 | klass: *const JSClass,
210 | principals: *mut JSPrincipals,
211 | hook: OnNewGlobalHookOption,
212 | realm_opts: &RealmOptions,
213 | ) -> *mut JSObject;
214 |
215 | #[namespace = "JS"]
216 | unsafe fn EnterRealm(context: *mut JSContext, target: *mut JSObject) -> *mut Realm;
217 | #[namespace = "JS"]
218 | unsafe fn LeaveRealm(context: *mut JSContext, old_realm: *mut Realm);
219 |
220 | #[namespace = "JS"]
221 | #[rust_name = "undefined_value"]
222 | fn UndefinedValue() -> Value;
223 |
224 | #[rust_name = "to_int32"]
225 | fn toInt32(self: &Value) -> i32;
226 |
227 | #[rust_name = "is_string"]
228 | fn isString(self: &Value) -> bool;
229 |
230 | #[rust_name = "to_string"]
231 | fn toString(self: &Value) -> *mut JSString;
232 |
233 | unsafe fn MakeUtf8UnitSourceText(
234 | context: *mut JSContext,
235 | units: &str,
236 | length: usize,
237 | ownership: SourceOwnership,
238 | ) -> UniquePtr;
239 |
240 | unsafe fn Utf8SourceEvaluate(
241 | context: *mut JSContext,
242 | compile_opts: &OwningCompileOptions,
243 | source: Pin<&mut Utf8UnitSourceText>,
244 | rval: MutableHandleValue,
245 | ) -> bool;
246 |
247 | unsafe fn Utf8SourceCompile(
248 | context: *mut JSContext,
249 | options: &OwningCompileOptions,
250 | source: Pin<&mut Utf8UnitSourceText>,
251 | ) -> *mut JSScript;
252 |
253 | unsafe fn JS_ExecuteScript(
254 | context: *mut JSContext,
255 | scriptArg: HandleScript,
256 | rval: MutableHandleValue,
257 | ) -> bool;
258 |
259 | fn MakeUninitPersistentRootedObject() -> UniquePtr;
260 | unsafe fn InitPersistentRootedObject(
261 | root: Pin<&mut PersistentRootedObject>,
262 | context: *mut JSContext,
263 | initial: *mut JSObject,
264 | );
265 | fn initialized(self: &PersistentRootedObject) -> bool;
266 |
267 | unsafe fn Utf8IsCompilableUnit(
268 | context: *mut JSContext,
269 | global: HandleObject,
270 | source: &str,
271 | ) -> bool;
272 |
273 | #[namespace = "JS"]
274 | unsafe fn ToString(context: *mut JSContext, value: HandleValue) -> *mut JSString;
275 |
276 | unsafe fn JSStringToRustString(context: *mut JSContext, string: HandleString) -> String;
277 |
278 | unsafe fn ReportException(context: *mut JSContext) -> bool;
279 |
280 | #[namespace = "js"]
281 | unsafe fn RunJobs(context: *mut JSContext);
282 |
283 | #[namespace = "JS"]
284 | #[rust_name = "context_options_ref"]
285 | unsafe fn ContextOptionsRef<'a>(context: *mut JSContext) -> Pin<&'a mut ContextOptions>;
286 | #[rust_name = "set_private_class_fields"]
287 | fn setPrivateClassFields<'a>(
288 | self: Pin<&mut ContextOptions>,
289 | private_class_fields: bool,
290 | ) -> Pin<&'a mut ContextOptions>;
291 | #[rust_name = "private_class_fields"]
292 | fn privateClassFields(self: &ContextOptions) -> bool;
293 | #[rust_name = "set_private_class_methods"]
294 | fn setPrivateClassMethods<'a>(
295 | self: Pin<&mut ContextOptions>,
296 | private_class_methods: bool,
297 | ) -> Pin<&'a mut ContextOptions>;
298 | #[rust_name = "private_class_methods"]
299 | fn privateClassMethods(self: &ContextOptions) -> bool;
300 | #[rust_name = "set_class_static_blocks"]
301 | fn setClassStaticBlocks<'a>(
302 | self: Pin<&mut ContextOptions>,
303 | class_static_blocks: bool,
304 | ) -> Pin<&'a mut ContextOptions>;
305 | #[rust_name = "class_static_blocks"]
306 | fn classStaticBlocks(self: &ContextOptions) -> bool;
307 | #[rust_name = "set_ergnomic_brand_checks"]
308 | fn setErgnomicBrandChecks<'a>(
309 | self: Pin<&mut ContextOptions>,
310 | ergnomic_brand_checks: bool,
311 | ) -> Pin<&'a mut ContextOptions>;
312 |
313 | #[rust_name = "creation_options"]
314 | fn creationOptions(self: Pin<&mut RealmOptions>) -> Pin<&mut RealmCreationOptions>;
315 |
316 | #[rust_name = "set_streams_enabled"]
317 | fn setStreamsEnabled(
318 | self: Pin<&mut RealmCreationOptions>,
319 | enabled: bool,
320 | ) -> Pin<&mut RealmCreationOptions>;
321 | #[rust_name = "streams_enabled"]
322 | fn getStreamsEnabled(self: &RealmCreationOptions) -> bool;
323 |
324 | #[rust_name = "set_readable_byte_streams_enabled"]
325 | fn setReadableByteStreamsEnabled(
326 | self: Pin<&mut RealmCreationOptions>,
327 | enabled: bool,
328 | ) -> Pin<&mut RealmCreationOptions>;
329 | #[rust_name = "get_readable_byte_streams_enabled"]
330 | fn getReadableByteStreamsEnabled(self: &RealmCreationOptions) -> bool;
331 |
332 | #[rust_name = "set_byob_stream_readers_enabled"]
333 | fn setBYOBStreamReadersEnabled(
334 | self: Pin<&mut RealmCreationOptions>,
335 | enabled: bool,
336 | ) -> Pin<&mut RealmCreationOptions>;
337 | #[rust_name = "get_byob_stream_readers_enabled"]
338 | fn getBYOBStreamReadersEnabled(self: &RealmCreationOptions) -> bool;
339 |
340 | #[rust_name = "set_readable_stream_pipe_to_enabled"]
341 | fn setReadableStreamPipeToEnabled(
342 | self: Pin<&mut RealmCreationOptions>,
343 | enabled: bool,
344 | ) -> Pin<&mut RealmCreationOptions>;
345 | #[rust_name = "get_readable_stream_pipe_to_enabled"]
346 | fn getReadableStreamPipeToEnabled(self: &RealmCreationOptions) -> bool;
347 |
348 | #[rust_name = "set_writable_streams_enabled"]
349 | fn setWritableStreamsEnabled(
350 | self: Pin<&mut RealmCreationOptions>,
351 | enabled: bool,
352 | ) -> Pin<&mut RealmCreationOptions>;
353 | #[rust_name = "get_writable_streams_enabled"]
354 | fn getWritableStreamsEnabled(self: &RealmCreationOptions) -> bool;
355 |
356 | #[rust_name = "set_iterator_helpers_enabled"]
357 | fn setIteratorHelpersEnabled(
358 | self: Pin<&mut RealmCreationOptions>,
359 | enabled: bool,
360 | ) -> Pin<&mut RealmCreationOptions>;
361 | #[rust_name = "get_iterator_helpers_enabled"]
362 | fn getIteratorHelpersEnabled(self: &RealmCreationOptions) -> bool;
363 |
364 | #[rust_name = "set_weak_refs_enabled"]
365 | fn setWeakRefsEnabled(
366 | self: Pin<&mut RealmCreationOptions>,
367 | specifier: WeakRefSpecifier,
368 | ) -> Pin<&mut RealmCreationOptions>;
369 | #[rust_name = "get_weak_refs_enabled"]
370 | fn getWeakRefsEnabled(self: &RealmCreationOptions) -> WeakRefSpecifier;
371 |
372 | #[cfg(feature = "moz_debug")]
373 | #[rust_name = "js_set_gc_zeal"]
374 | unsafe fn JS_SetGCZeal(context: *mut JSContext, zeal: u8, freq: u32);
375 | }
376 | }
377 |
--------------------------------------------------------------------------------
/crates/spidermonkey-wasm-sys/tests/integration.rs:
--------------------------------------------------------------------------------
1 | mod integration {
2 | use spidermonkey_wasm_sys::jsffi::JS_NewPlainObject;
3 | use spidermonkey_wasm_sys::{jsclass, jsffi, jsgc, jsrealm};
4 | use std::marker::PhantomData;
5 | use std::ptr;
6 |
7 | fn init_engine() -> *mut jsffi::JSContext {
8 | assert!(jsffi::JS_Init());
9 | unsafe {
10 | let context = jsffi::JS_NewContext(jsffi::DefaultHeapMaxBytes(), ptr::null_mut());
11 | assert!(!context.is_null());
12 | context
13 | }
14 | }
15 |
16 | fn shutdown_engine(context: *mut jsffi::JSContext) {
17 | unsafe {
18 | jsffi::JS_DestroyContext(context);
19 | }
20 | jsffi::JS_ShutDown();
21 | }
22 |
23 | #[test]
24 | fn eval() {
25 | let global_class: jsclass::JSClass = jsclass::JSClass {
26 | name: "global\0".as_ptr() as *const i8,
27 | flags: jsffi::js_class_global_flags(),
28 | c_ops: jsffi::default_global_class_ops(),
29 | spec: std::ptr::null(),
30 | ext: std::ptr::null(),
31 | o_ops: std::ptr::null(),
32 | };
33 |
34 | let context = init_engine();
35 |
36 | unsafe {
37 | assert!(jsffi::InitDefaultSelfHostedCode(context));
38 |
39 | let realm_opts = jsffi::make_default_realm_options();
40 | let mut global_object = jsgc::Rooted::default();
41 | global_object.init(
42 | context,
43 | jsffi::JS_NewGlobalObject(
44 | context,
45 | &global_class,
46 | ptr::null_mut(),
47 | jsffi::OnNewGlobalHookOption::FireOnNewGlobalHook,
48 | &realm_opts,
49 | ),
50 | );
51 |
52 | let _ar = jsrealm::JSAutoRealm::new(context, global_object.ptr);
53 | let owning_compile_options = jsffi::MakeOwningCompileOptions(
54 | context,
55 | &jsffi::CompileOptionsParams {
56 | force_full_parse: false,
57 | lineno: 1,
58 | file: "eval.js".into(),
59 | },
60 | );
61 |
62 | let mut undefined_value = jsffi::undefined_value();
63 | let rval = jsgc::MutableHandle {
64 | ptr: &mut undefined_value,
65 | _marker: PhantomData,
66 | };
67 |
68 | let script = "41 + 1";
69 | let mut source = jsffi::MakeUtf8UnitSourceText(
70 | context,
71 | &script,
72 | script.len(),
73 | jsffi::SourceOwnership::Borrowed,
74 | );
75 |
76 | jsffi::Utf8SourceEvaluate(context, &owning_compile_options, source.pin_mut(), rval);
77 |
78 | let result = undefined_value.to_int32();
79 | assert_eq!(result, 42);
80 | global_object.remove_from_root_stack();
81 |
82 | let mut persistent = jsffi::MakeUninitPersistentRootedObject();
83 | assert!(!persistent.initialized());
84 | jsffi::InitPersistentRootedObject(
85 | persistent.pin_mut(),
86 | context,
87 | JS_NewPlainObject(context),
88 | );
89 | assert!(persistent.initialized());
90 | }
91 |
92 | shutdown_engine(context);
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/crates/spidermonkey-wasm/.cargo/config:
--------------------------------------------------------------------------------
1 | [build]
2 | target = "wasm32-wasi"
3 |
4 | [env]
5 | CXX = "/opt/wasi-sdk/wasi-sdk-12.0/bin/clang++ --sysroot=/opt/wasi-sdk/wasi-sdk-12.0/share/wasi-sysroot"
6 | CXXFLAGS = "-fno-exceptions -DRUST_CXX_NO_EXCEPTIONS"
7 | AR = "/opt/wasi-sdk/wasi-sdk-12.0/bin/ar"
8 | LIBCLANG_PATH = "/opt/wasi-sdk/wasi-sdk-12.0/share/wasi-sysroot/lib/wasm32-wasi"
9 | LIBCLANG_RT_PATH = "/opt/wasi-sdk/wasi-sdk-12.0/lib/clang/11.0.0/lib/wasi"
10 |
11 |
--------------------------------------------------------------------------------
/crates/spidermonkey-wasm/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "spidermonkey-wasm"
3 | version = "0.1.0"
4 | edition = "2021"
5 | license = "MPL-2.0"
6 |
7 | [dependencies]
8 | spidermonkey-wasm-sys = { path = "../spidermonkey-wasm-sys" }
9 | anyhow = "1.0.55"
10 |
11 | [dev-dependencies]
12 | spidermonkey-wasm-sys = { path = "../spidermonkey-wasm-sys", features = ["moz_debug"] }
--------------------------------------------------------------------------------
/crates/spidermonkey-wasm/LICENSE:
--------------------------------------------------------------------------------
1 | Mozilla Public License Version 2.0
2 | ==================================
3 |
4 | 1. Definitions
5 | --------------
6 |
7 | 1.1. "Contributor"
8 | means each individual or legal entity that creates, contributes to
9 | the creation of, or owns Covered Software.
10 |
11 | 1.2. "Contributor Version"
12 | means the combination of the Contributions of others (if any) used
13 | by a Contributor and that particular Contributor's Contribution.
14 |
15 | 1.3. "Contribution"
16 | means Covered Software of a particular Contributor.
17 |
18 | 1.4. "Covered Software"
19 | means Source Code Form to which the initial Contributor has attached
20 | the notice in Exhibit A, the Executable Form of such Source Code
21 | Form, and Modifications of such Source Code Form, in each case
22 | including portions thereof.
23 |
24 | 1.5. "Incompatible With Secondary Licenses"
25 | means
26 |
27 | (a) that the initial Contributor has attached the notice described
28 | in Exhibit B to the Covered Software; or
29 |
30 | (b) that the Covered Software was made available under the terms of
31 | version 1.1 or earlier of the License, but not also under the
32 | terms of a Secondary License.
33 |
34 | 1.6. "Executable Form"
35 | means any form of the work other than Source Code Form.
36 |
37 | 1.7. "Larger Work"
38 | means a work that combines Covered Software with other material, in
39 | a separate file or files, that is not Covered Software.
40 |
41 | 1.8. "License"
42 | means this document.
43 |
44 | 1.9. "Licensable"
45 | means having the right to grant, to the maximum extent possible,
46 | whether at the time of the initial grant or subsequently, any and
47 | all of the rights conveyed by this License.
48 |
49 | 1.10. "Modifications"
50 | means any of the following:
51 |
52 | (a) any file in Source Code Form that results from an addition to,
53 | deletion from, or modification of the contents of Covered
54 | Software; or
55 |
56 | (b) any new file in Source Code Form that contains any Covered
57 | Software.
58 |
59 | 1.11. "Patent Claims" of a Contributor
60 | means any patent claim(s), including without limitation, method,
61 | process, and apparatus claims, in any patent Licensable by such
62 | Contributor that would be infringed, but for the grant of the
63 | License, by the making, using, selling, offering for sale, having
64 | made, import, or transfer of either its Contributions or its
65 | Contributor Version.
66 |
67 | 1.12. "Secondary License"
68 | means either the GNU General Public License, Version 2.0, the GNU
69 | Lesser General Public License, Version 2.1, the GNU Affero General
70 | Public License, Version 3.0, or any later versions of those
71 | licenses.
72 |
73 | 1.13. "Source Code Form"
74 | means the form of the work preferred for making modifications.
75 |
76 | 1.14. "You" (or "Your")
77 | means an individual or a legal entity exercising rights under this
78 | License. For legal entities, "You" includes any entity that
79 | controls, is controlled by, or is under common control with You. For
80 | purposes of this definition, "control" means (a) the power, direct
81 | or indirect, to cause the direction or management of such entity,
82 | whether by contract or otherwise, or (b) ownership of more than
83 | fifty percent (50%) of the outstanding shares or beneficial
84 | ownership of such entity.
85 |
86 | 2. License Grants and Conditions
87 | --------------------------------
88 |
89 | 2.1. Grants
90 |
91 | Each Contributor hereby grants You a world-wide, royalty-free,
92 | non-exclusive license:
93 |
94 | (a) under intellectual property rights (other than patent or trademark)
95 | Licensable by such Contributor to use, reproduce, make available,
96 | modify, display, perform, distribute, and otherwise exploit its
97 | Contributions, either on an unmodified basis, with Modifications, or
98 | as part of a Larger Work; and
99 |
100 | (b) under Patent Claims of such Contributor to make, use, sell, offer
101 | for sale, have made, import, and otherwise transfer either its
102 | Contributions or its Contributor Version.
103 |
104 | 2.2. Effective Date
105 |
106 | The licenses granted in Section 2.1 with respect to any Contribution
107 | become effective for each Contribution on the date the Contributor first
108 | distributes such Contribution.
109 |
110 | 2.3. Limitations on Grant Scope
111 |
112 | The licenses granted in this Section 2 are the only rights granted under
113 | this License. No additional rights or licenses will be implied from the
114 | distribution or licensing of Covered Software under this License.
115 | Notwithstanding Section 2.1(b) above, no patent license is granted by a
116 | Contributor:
117 |
118 | (a) for any code that a Contributor has removed from Covered Software;
119 | or
120 |
121 | (b) for infringements caused by: (i) Your and any other third party's
122 | modifications of Covered Software, or (ii) the combination of its
123 | Contributions with other software (except as part of its Contributor
124 | Version); or
125 |
126 | (c) under Patent Claims infringed by Covered Software in the absence of
127 | its Contributions.
128 |
129 | This License does not grant any rights in the trademarks, service marks,
130 | or logos of any Contributor (except as may be necessary to comply with
131 | the notice requirements in Section 3.4).
132 |
133 | 2.4. Subsequent Licenses
134 |
135 | No Contributor makes additional grants as a result of Your choice to
136 | distribute the Covered Software under a subsequent version of this
137 | License (see Section 10.2) or under the terms of a Secondary License (if
138 | permitted under the terms of Section 3.3).
139 |
140 | 2.5. Representation
141 |
142 | Each Contributor represents that the Contributor believes its
143 | Contributions are its original creation(s) or it has sufficient rights
144 | to grant the rights to its Contributions conveyed by this License.
145 |
146 | 2.6. Fair Use
147 |
148 | This License is not intended to limit any rights You have under
149 | applicable copyright doctrines of fair use, fair dealing, or other
150 | equivalents.
151 |
152 | 2.7. Conditions
153 |
154 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
155 | in Section 2.1.
156 |
157 | 3. Responsibilities
158 | -------------------
159 |
160 | 3.1. Distribution of Source Form
161 |
162 | All distribution of Covered Software in Source Code Form, including any
163 | Modifications that You create or to which You contribute, must be under
164 | the terms of this License. You must inform recipients that the Source
165 | Code Form of the Covered Software is governed by the terms of this
166 | License, and how they can obtain a copy of this License. You may not
167 | attempt to alter or restrict the recipients' rights in the Source Code
168 | Form.
169 |
170 | 3.2. Distribution of Executable Form
171 |
172 | If You distribute Covered Software in Executable Form then:
173 |
174 | (a) such Covered Software must also be made available in Source Code
175 | Form, as described in Section 3.1, and You must inform recipients of
176 | the Executable Form how they can obtain a copy of such Source Code
177 | Form by reasonable means in a timely manner, at a charge no more
178 | than the cost of distribution to the recipient; and
179 |
180 | (b) You may distribute such Executable Form under the terms of this
181 | License, or sublicense it under different terms, provided that the
182 | license for the Executable Form does not attempt to limit or alter
183 | the recipients' rights in the Source Code Form under this License.
184 |
185 | 3.3. Distribution of a Larger Work
186 |
187 | You may create and distribute a Larger Work under terms of Your choice,
188 | provided that You also comply with the requirements of this License for
189 | the Covered Software. If the Larger Work is a combination of Covered
190 | Software with a work governed by one or more Secondary Licenses, and the
191 | Covered Software is not Incompatible With Secondary Licenses, this
192 | License permits You to additionally distribute such Covered Software
193 | under the terms of such Secondary License(s), so that the recipient of
194 | the Larger Work may, at their option, further distribute the Covered
195 | Software under the terms of either this License or such Secondary
196 | License(s).
197 |
198 | 3.4. Notices
199 |
200 | You may not remove or alter the substance of any license notices
201 | (including copyright notices, patent notices, disclaimers of warranty,
202 | or limitations of liability) contained within the Source Code Form of
203 | the Covered Software, except that You may alter any license notices to
204 | the extent required to remedy known factual inaccuracies.
205 |
206 | 3.5. Application of Additional Terms
207 |
208 | You may choose to offer, and to charge a fee for, warranty, support,
209 | indemnity or liability obligations to one or more recipients of Covered
210 | Software. However, You may do so only on Your own behalf, and not on
211 | behalf of any Contributor. You must make it absolutely clear that any
212 | such warranty, support, indemnity, or liability obligation is offered by
213 | You alone, and You hereby agree to indemnify every Contributor for any
214 | liability incurred by such Contributor as a result of warranty, support,
215 | indemnity or liability terms You offer. You may include additional
216 | disclaimers of warranty and limitations of liability specific to any
217 | jurisdiction.
218 |
219 | 4. Inability to Comply Due to Statute or Regulation
220 | ---------------------------------------------------
221 |
222 | If it is impossible for You to comply with any of the terms of this
223 | License with respect to some or all of the Covered Software due to
224 | statute, judicial order, or regulation then You must: (a) comply with
225 | the terms of this License to the maximum extent possible; and (b)
226 | describe the limitations and the code they affect. Such description must
227 | be placed in a text file included with all distributions of the Covered
228 | Software under this License. Except to the extent prohibited by statute
229 | or regulation, such description must be sufficiently detailed for a
230 | recipient of ordinary skill to be able to understand it.
231 |
232 | 5. Termination
233 | --------------
234 |
235 | 5.1. The rights granted under this License will terminate automatically
236 | if You fail to comply with any of its terms. However, if You become
237 | compliant, then the rights granted under this License from a particular
238 | Contributor are reinstated (a) provisionally, unless and until such
239 | Contributor explicitly and finally terminates Your grants, and (b) on an
240 | ongoing basis, if such Contributor fails to notify You of the
241 | non-compliance by some reasonable means prior to 60 days after You have
242 | come back into compliance. Moreover, Your grants from a particular
243 | Contributor are reinstated on an ongoing basis if such Contributor
244 | notifies You of the non-compliance by some reasonable means, this is the
245 | first time You have received notice of non-compliance with this License
246 | from such Contributor, and You become compliant prior to 30 days after
247 | Your receipt of the notice.
248 |
249 | 5.2. If You initiate litigation against any entity by asserting a patent
250 | infringement claim (excluding declaratory judgment actions,
251 | counter-claims, and cross-claims) alleging that a Contributor Version
252 | directly or indirectly infringes any patent, then the rights granted to
253 | You by any and all Contributors for the Covered Software under Section
254 | 2.1 of this License shall terminate.
255 |
256 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all
257 | end user license agreements (excluding distributors and resellers) which
258 | have been validly granted by You or Your distributors under this License
259 | prior to termination shall survive termination.
260 |
261 | ************************************************************************
262 | * *
263 | * 6. Disclaimer of Warranty *
264 | * ------------------------- *
265 | * *
266 | * Covered Software is provided under this License on an "as is" *
267 | * basis, without warranty of any kind, either expressed, implied, or *
268 | * statutory, including, without limitation, warranties that the *
269 | * Covered Software is free of defects, merchantable, fit for a *
270 | * particular purpose or non-infringing. The entire risk as to the *
271 | * quality and performance of the Covered Software is with You. *
272 | * Should any Covered Software prove defective in any respect, You *
273 | * (not any Contributor) assume the cost of any necessary servicing, *
274 | * repair, or correction. This disclaimer of warranty constitutes an *
275 | * essential part of this License. No use of any Covered Software is *
276 | * authorized under this License except under this disclaimer. *
277 | * *
278 | ************************************************************************
279 |
280 | ************************************************************************
281 | * *
282 | * 7. Limitation of Liability *
283 | * -------------------------- *
284 | * *
285 | * Under no circumstances and under no legal theory, whether tort *
286 | * (including negligence), contract, or otherwise, shall any *
287 | * Contributor, or anyone who distributes Covered Software as *
288 | * permitted above, be liable to You for any direct, indirect, *
289 | * special, incidental, or consequential damages of any character *
290 | * including, without limitation, damages for lost profits, loss of *
291 | * goodwill, work stoppage, computer failure or malfunction, or any *
292 | * and all other commercial damages or losses, even if such party *
293 | * shall have been informed of the possibility of such damages. This *
294 | * limitation of liability shall not apply to liability for death or *
295 | * personal injury resulting from such party's negligence to the *
296 | * extent applicable law prohibits such limitation. Some *
297 | * jurisdictions do not allow the exclusion or limitation of *
298 | * incidental or consequential damages, so this exclusion and *
299 | * limitation may not apply to You. *
300 | * *
301 | ************************************************************************
302 |
303 | 8. Litigation
304 | -------------
305 |
306 | Any litigation relating to this License may be brought only in the
307 | courts of a jurisdiction where the defendant maintains its principal
308 | place of business and such litigation shall be governed by laws of that
309 | jurisdiction, without reference to its conflict-of-law provisions.
310 | Nothing in this Section shall prevent a party's ability to bring
311 | cross-claims or counter-claims.
312 |
313 | 9. Miscellaneous
314 | ----------------
315 |
316 | This License represents the complete agreement concerning the subject
317 | matter hereof. If any provision of this License is held to be
318 | unenforceable, such provision shall be reformed only to the extent
319 | necessary to make it enforceable. Any law or regulation which provides
320 | that the language of a contract shall be construed against the drafter
321 | shall not be used to construe this License against a Contributor.
322 |
323 | 10. Versions of the License
324 | ---------------------------
325 |
326 | 10.1. New Versions
327 |
328 | Mozilla Foundation is the license steward. Except as provided in Section
329 | 10.3, no one other than the license steward has the right to modify or
330 | publish new versions of this License. Each version will be given a
331 | distinguishing version number.
332 |
333 | 10.2. Effect of New Versions
334 |
335 | You may distribute the Covered Software under the terms of the version
336 | of the License under which You originally received the Covered Software,
337 | or under the terms of any subsequent version published by the license
338 | steward.
339 |
340 | 10.3. Modified Versions
341 |
342 | If you create software not governed by this License, and you want to
343 | create a new license for such software, you may create and use a
344 | modified version of this License if you rename the license and remove
345 | any references to the name of the license steward (except to note that
346 | such modified license differs from this License).
347 |
348 | 10.4. Distributing Source Code Form that is Incompatible With Secondary
349 | Licenses
350 |
351 | If You choose to distribute Source Code Form that is Incompatible With
352 | Secondary Licenses under the terms of this version of the License, the
353 | notice described in Exhibit B of this License must be attached.
354 |
355 | Exhibit A - Source Code Form License Notice
356 | -------------------------------------------
357 |
358 | This Source Code Form is subject to the terms of the Mozilla Public
359 | License, v. 2.0. If a copy of the MPL was not distributed with this
360 | file, You can obtain one at http://mozilla.org/MPL/2.0/.
361 |
362 | If it is not possible or desirable to put the notice in a particular
363 | file, then You may include the notice in a location (such as a LICENSE
364 | file in a relevant directory) where a recipient would be likely to look
365 | for such a notice.
366 |
367 | You may add additional accurate notices of copyright ownership.
368 |
369 | Exhibit B - "Incompatible With Secondary Licenses" Notice
370 | ---------------------------------------------------------
371 |
372 | This Source Code Form is "Incompatible With Secondary Licenses", as
373 | defined by the Mozilla Public License, v. 2.0.
--------------------------------------------------------------------------------
/crates/spidermonkey-wasm/src/compilation_options.rs:
--------------------------------------------------------------------------------
1 | use anyhow::{bail, Result};
2 | use spidermonkey_wasm_sys::{
3 | jsffi::{CompileOptionsParams, JSContext, MakeOwningCompileOptions, OwningCompileOptions},
4 | UniquePtr,
5 | };
6 | use std::ops::Deref;
7 |
8 | pub struct CompilationOptions {
9 | inner: UniquePtr,
10 | }
11 |
12 | impl CompilationOptions {
13 | pub fn new(
14 | context: *mut JSContext,
15 | lineno: usize,
16 | force_full_parse: bool,
17 | file: String,
18 | ) -> Result {
19 | let opts = CompileOptionsParams {
20 | lineno,
21 | force_full_parse,
22 | file,
23 | };
24 |
25 | let inner = unsafe { MakeOwningCompileOptions(context, &opts) };
26 |
27 | if inner.is_null() {
28 | bail!("Couldn't create compilation options")
29 | }
30 |
31 | Ok(Self { inner })
32 | }
33 | }
34 |
35 | impl Deref for CompilationOptions {
36 | type Target = OwningCompileOptions;
37 |
38 | fn deref(&self) -> &Self::Target {
39 | &self.inner
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/crates/spidermonkey-wasm/src/handle.rs:
--------------------------------------------------------------------------------
1 | use std::marker::PhantomData;
2 |
3 | use spidermonkey_wasm_sys::{
4 | jsffi::{JSObject, JSScript, JSString, Value},
5 | jsgc::{Handle as RawHandle, MutableHandle as RawMutableHandle},
6 | };
7 |
8 | pub type HandleString<'a> = Handle<'a, *mut JSString>;
9 | pub type HandleObject<'a> = Handle<'a, *mut JSObject>;
10 | pub type HandleScript<'a> = Handle<'a, *mut JSScript>;
11 | pub type HandleValue<'a> = Handle<'a, Value>;
12 | pub type MutableHandleString<'a> = MutableHandle<'a, *mut JSString>;
13 | pub type MutableHandleObject<'a> = MutableHandle<'a, *mut JSObject>;
14 | pub type MutableHandleScript<'a> = MutableHandle<'a, *mut JSScript>;
15 | pub type MutableHandleValue<'a> = MutableHandle<'a, Value>;
16 |
17 | #[derive(Clone, Copy)]
18 | pub struct Handle<'a, T: 'a> {
19 | ptr: &'a T,
20 | }
21 |
22 | impl<'a, T: 'a> Handle<'a, T> {
23 | pub fn new(ptr: &T) -> Handle {
24 | Handle { ptr }
25 | }
26 |
27 | pub fn get(&self) -> T
28 | where
29 | T: Copy,
30 | {
31 | *self.ptr
32 | }
33 |
34 | pub fn into_raw(self) -> RawHandle {
35 | RawHandle {
36 | ptr: self.ptr as *const T,
37 | _marker: PhantomData,
38 | }
39 | }
40 | }
41 |
42 | pub struct MutableHandle<'a, T: 'a> {
43 | ptr: &'a mut T,
44 | }
45 |
46 | impl<'a, T: 'a> MutableHandle<'a, T> {
47 | pub fn new(ptr: &'a mut T) -> Self {
48 | MutableHandle { ptr }
49 | }
50 |
51 | pub fn into_raw(self) -> RawMutableHandle {
52 | RawMutableHandle {
53 | ptr: self.ptr as *mut T,
54 | _marker: PhantomData,
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/crates/spidermonkey-wasm/src/js.rs:
--------------------------------------------------------------------------------
1 | use anyhow::{bail, Result};
2 | use spidermonkey_wasm_sys::jsffi::{
3 | context_options_ref as raw_context_options_ref, default_global_class_ops,
4 | js_class_global_flags, ContextOptions, JSContext, JSObject, JSString, JSStringToRustString,
5 | JS_NewGlobalObject, OnNewGlobalHookOption, RealmOptions, ReportException, RunJobs, ToString,
6 | Utf8IsCompilableUnit,
7 | };
8 |
9 | use spidermonkey_wasm_sys::jsclass::JSClass;
10 |
11 | // Re-exports of safe bindings from the sys crate
12 | pub use spidermonkey_wasm_sys::jsffi::{make_default_realm_options, undefined_value};
13 |
14 | use crate::handle::{HandleObject, HandleString, HandleValue};
15 |
16 | use std::{pin::Pin, ptr};
17 |
18 | pub fn new_global_object(
19 | cx: *mut JSContext,
20 | class: &JSClass,
21 | opts: &RealmOptions,
22 | ) -> *mut JSObject {
23 | unsafe {
24 | JS_NewGlobalObject(
25 | cx,
26 | class,
27 | ptr::null_mut(),
28 | OnNewGlobalHookOption::FireOnNewGlobalHook,
29 | opts,
30 | )
31 | }
32 | }
33 |
34 | pub fn make_default_global_class() -> JSClass {
35 | JSClass {
36 | name: b"global\0".as_ptr() as *const i8,
37 | flags: js_class_global_flags(),
38 | c_ops: default_global_class_ops(),
39 | spec: std::ptr::null(),
40 | ext: std::ptr::null(),
41 | o_ops: std::ptr::null(),
42 | }
43 | }
44 |
45 | pub fn run_jobs(cx: *mut JSContext) {
46 | unsafe {
47 | RunJobs(cx);
48 | }
49 | }
50 |
51 | pub fn report_exception(cx: *mut JSContext) -> Result<()> {
52 | if !unsafe { ReportException(cx) } {
53 | bail!("Exception thrown while reporting exception");
54 | }
55 |
56 | Ok(())
57 | }
58 |
59 | pub fn to_string(cx: *mut JSContext, val: HandleValue) -> *mut JSString {
60 | unsafe { ToString(cx, val.into_raw()) }
61 | }
62 |
63 | pub fn to_rust_string(cx: *mut JSContext, val: HandleString) -> String {
64 | unsafe { JSStringToRustString(cx, val.into_raw()) }
65 | }
66 |
67 | pub fn is_compilable_unit(cx: *mut JSContext, handle: HandleObject, buffer: &str) -> bool {
68 | unsafe { Utf8IsCompilableUnit(cx, handle.into_raw(), buffer) }
69 | }
70 |
71 | pub fn context_options_ref<'a>(cx: *mut JSContext) -> Pin<&'a mut ContextOptions> {
72 | unsafe { raw_context_options_ref(cx) }
73 | }
74 |
--------------------------------------------------------------------------------
/crates/spidermonkey-wasm/src/lib.rs:
--------------------------------------------------------------------------------
1 | pub mod compilation_options;
2 | pub mod handle;
3 | pub mod js;
4 | pub mod rooted;
5 | pub mod runtime;
6 | pub mod utf8_source;
7 |
8 | pub use spidermonkey_wasm_sys::jsffi::{
9 | JSClass, JSContext, JSObject, OnNewGlobalHookOption, RealmOptions, WeakRefSpecifier,
10 | };
11 |
12 | pub use spidermonkey_wasm_sys::jsrealm::JSAutoRealm;
13 |
14 | // Re-export low-level Rooted types for macro convenience
15 | // and for GC callback definition
16 | pub use spidermonkey_wasm_sys::jsgc::{
17 | JSGCReason, JSGCStatus, OnJSGCCallback, Rooted as RawRooted,
18 | };
19 |
--------------------------------------------------------------------------------
/crates/spidermonkey-wasm/src/rooted.rs:
--------------------------------------------------------------------------------
1 | use crate::handle::{Handle, MutableHandle};
2 | use spidermonkey_wasm_sys::{
3 | jsffi::{JSContext, JSObject, JSScript, JSString, Value},
4 | jsgc::{JSRootKind, Rooted as RawRooted},
5 | };
6 | use std::pin::Pin;
7 |
8 | pub type RootedValue<'a> = Rooted<'a, Value>;
9 | pub type RootedObject<'a> = Rooted<'a, *mut JSObject>;
10 | pub type RootedScript<'a> = Rooted<'a, *mut JSScript>;
11 | pub type RootedString<'a> = Rooted<'a, *mut JSString>;
12 |
13 | /// Helper to root values on the stack.
14 | ///
15 | /// Inspired by: https://github.com/servo/rust-mozjs/blob/master/src/rust.rs#L546;
16 | /// with the difference that this implementation allows rooting multiple values at once.
17 | ///
18 | /// # Usage
19 | ///
20 | /// root!(with(context);
21 | /// let undefined_value = jsapi::UndefinedValue();
22 | /// let other_vaue = jsapi::UndefinedValue();
23 | /// );
24 | ///
25 | #[macro_export]
26 | macro_rules! root {
27 | (with($cx:expr); $(let $v:ident = $init:expr;)*) => { $(
28 | let mut $v = $crate::RawRooted::default();
29 | let $v = $crate::rooted::Rooted::new($cx, &mut $v, $init);
30 | )*};
31 |
32 | (with($cx:expr); $(let mut $v:ident = $init:expr;)*) => { $(
33 | let mut $v = $crate::RawRooted::default();
34 | let mut $v = $crate::rooted::Rooted::new($cx, &mut $v, $init);
35 | )*};
36 | }
37 |
38 | pub struct Rooted<'a, T: 'a + JSRootKind> {
39 | root: Pin<&'a mut RawRooted>,
40 | }
41 |
42 | impl<'a, T: 'a + JSRootKind> Rooted<'a, T> {
43 | pub fn new(context: *mut JSContext, root: &'a mut RawRooted, initial: T) -> Self {
44 | unsafe { root.init(context, initial) };
45 |
46 | Self {
47 | root: unsafe { Pin::new_unchecked(root) },
48 | }
49 | }
50 |
51 | pub fn handle(&self) -> Handle {
52 | Handle::new(&self.root.ptr)
53 | }
54 |
55 | pub fn mut_handle(&mut self) -> MutableHandle {
56 | let mut_pin = self.root.as_mut();
57 | let raw_rooted = unsafe { mut_pin.get_unchecked_mut() };
58 |
59 | MutableHandle::new(&mut raw_rooted.ptr)
60 | }
61 |
62 | pub fn get(&self) -> T
63 | where
64 | T: Copy,
65 | {
66 | self.root.ptr
67 | }
68 | }
69 |
70 | impl<'a, T: 'a + JSRootKind> Drop for Rooted<'a, T> {
71 | fn drop(&mut self) {
72 | inner_drop(self.root.as_mut());
73 |
74 | fn inner_drop<'a, T: 'a + JSRootKind>(this: Pin<&'a mut RawRooted>) {
75 | let raw_root = unsafe { this.get_unchecked_mut() };
76 | unsafe { raw_root.remove_from_root_stack() };
77 | }
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/crates/spidermonkey-wasm/src/runtime.rs:
--------------------------------------------------------------------------------
1 | use crate::{
2 | handle::{HandleScript, MutableHandleValue},
3 | utf8_source::Utf8Source,
4 | };
5 | use spidermonkey_wasm_sys::{
6 | jsffi::{
7 | DefaultHeapMaxBytes, DisableIncrementalGC, InitDefaultSelfHostedCode, JSContext, JSRuntime,
8 | JSScript, JS_DestroyContext, JS_ExecuteScript, JS_GetRuntime, JS_Init, JS_MaybeGC,
9 | JS_NewContext, JS_SetGCCallbackWrapper, JS_SetGCParameter, JS_ShutDown, NonIncrementalGC,
10 | OwningCompileOptions, PrepareForFullGC, UseInternalJobQueues, Utf8SourceCompile,
11 | Utf8SourceEvaluate,
12 | },
13 | jsgc::{JSGCOptions, JSGCParamKey, JSGCReason, OnJSGCCallback},
14 | };
15 |
16 | use anyhow::{bail, Result};
17 | use std::ptr;
18 |
19 | pub struct Runtime {
20 | context: *mut JSContext,
21 | }
22 |
23 | // This implementation doesn't reflect the entire parent-child
24 | // relationship between runtimes. It assumes a single, top level runtime
25 | // and a single context. This should be enough for the Wasm
26 | // use case. This implementation can be expanded if necessary.
27 |
28 | impl Runtime {
29 | pub fn new() -> Result {
30 | if !JS_Init() {
31 | bail!("Couldn't initialize runtime. Call to JS_Init failed");
32 | }
33 |
34 | let context: *mut JSContext =
35 | unsafe { JS_NewContext(DefaultHeapMaxBytes(), ptr::null_mut()) };
36 |
37 | if context.is_null() {
38 | bail!("Couldn't create context");
39 | }
40 |
41 | unsafe {
42 | if !UseInternalJobQueues(context) {
43 | bail!("Couldn't initialize runtime. Call to UseInternalJobQueues failed");
44 | }
45 |
46 | if !InitDefaultSelfHostedCode(context) {
47 | bail!("Couldn't initialize runtime. Call to InitDefaultSelfHostedCode failed");
48 | }
49 | }
50 |
51 | Ok(Self { context })
52 | }
53 |
54 | pub fn cx(&self) -> *mut JSContext {
55 | self.context
56 | }
57 |
58 | pub fn rt(&self) -> *const JSRuntime {
59 | unsafe { JS_GetRuntime(self.context) }
60 | }
61 |
62 | // TODO: Investigate if there's a need for
63 | // `AutoDisableGenerationalGC`; according to
64 | // the class' documentation, generational
65 | // GC is disabled by default (ref
66 | // https://searchfox.org/mozilla-central/source/js/public/GCAPI.h#964). Unless `--enable-gcgenerational`
67 | // is passed. which is not the case (ref
68 | // https://github.com/bytecodealliance/spidermonkey-wasm-build/blob/main/mozconfigs/release)
69 | pub fn disable_incremental_gc(&self) {
70 | unsafe { DisableIncrementalGC(self.context) }
71 | }
72 |
73 | pub fn set_gc_parameter(&self, key: JSGCParamKey, val: u32) {
74 | unsafe {
75 | JS_SetGCParameter(self.context, key, val);
76 | }
77 | }
78 |
79 | pub fn prepare_for_full_gc(&self) {
80 | unsafe { PrepareForFullGC(self.context) };
81 | }
82 |
83 | pub fn non_incremental_gc(&self, opts: JSGCOptions, reason: JSGCReason) {
84 | unsafe { NonIncrementalGC(self.context, opts, reason) };
85 | }
86 |
87 | pub fn set_gc_callback(&self, callback: OnJSGCCallback) {
88 | unsafe {
89 | JS_SetGCCallbackWrapper(self.context, callback);
90 | }
91 | }
92 |
93 | pub fn maybe_gc(&self) {
94 | unsafe { JS_MaybeGC(self.context) };
95 | }
96 |
97 | pub fn compile(
98 | &self,
99 | opts: &OwningCompileOptions,
100 | src: &mut Utf8Source,
101 | ) -> Result<*mut JSScript> {
102 | let ptr = unsafe { Utf8SourceCompile(self.context, opts, src.pin_mut()) };
103 |
104 | if ptr.is_null() {
105 | bail!("Script compilation failed");
106 | }
107 |
108 | Ok(ptr)
109 | }
110 |
111 | pub fn execute(&self, script_handle: HandleScript, rval: MutableHandleValue) -> Result<()> {
112 | let success =
113 | unsafe { JS_ExecuteScript(self.context, script_handle.into_raw(), rval.into_raw()) };
114 |
115 | if !success {
116 | bail!("Script execution failed");
117 | }
118 |
119 | Ok(())
120 | }
121 |
122 | pub fn eval(
123 | &self,
124 | opts: &OwningCompileOptions,
125 | src: &mut Utf8Source,
126 | rval: MutableHandleValue,
127 | ) -> Result<()> {
128 | let success =
129 | unsafe { Utf8SourceEvaluate(self.context, opts, src.pin_mut(), rval.into_raw()) };
130 | if !success {
131 | bail!("Eval failed");
132 | }
133 | Ok(())
134 | }
135 | }
136 |
137 | impl Drop for Runtime {
138 | fn drop(&mut self) {
139 | unsafe {
140 | JS_DestroyContext(self.context);
141 | }
142 | JS_ShutDown();
143 | }
144 | }
145 |
146 | #[cfg(test)]
147 | mod tests {
148 |
149 | use super::Runtime;
150 |
151 | #[test]
152 | fn cx() {
153 | let rt = Runtime::new().unwrap();
154 | assert!(!rt.cx().is_null());
155 | assert!(!rt.rt().is_null());
156 | }
157 | }
158 |
--------------------------------------------------------------------------------
/crates/spidermonkey-wasm/src/utf8_source.rs:
--------------------------------------------------------------------------------
1 | use anyhow::{bail, Result};
2 | use spidermonkey_wasm_sys::{
3 | jsffi::{JSContext, MakeUtf8UnitSourceText, SourceOwnership, Utf8UnitSourceText},
4 | UniquePtr,
5 | };
6 | use std::pin::Pin;
7 |
8 | pub struct Utf8Source {
9 | inner: UniquePtr,
10 | }
11 |
12 | impl Utf8Source {
13 | pub fn new(context: *mut JSContext, src: &str) -> Result {
14 | let inner =
15 | unsafe { MakeUtf8UnitSourceText(context, src, src.len(), SourceOwnership::Borrowed) };
16 |
17 | if inner.is_null() {
18 | bail!("Could not initialize Utf8Source");
19 | }
20 |
21 | Ok(Self { inner })
22 | }
23 |
24 | pub fn pin_mut(&mut self) -> Pin<&mut Utf8UnitSourceText> {
25 | self.inner.pin_mut()
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/crates/spidermonkey-wasm/tests/compile.rs:
--------------------------------------------------------------------------------
1 | mod compile {
2 | use spidermonkey_wasm::{
3 | compilation_options::CompilationOptions, js, root, runtime::Runtime,
4 | utf8_source::Utf8Source, JSAutoRealm,
5 | };
6 |
7 | #[test]
8 | fn compile_and_compile_fail() {
9 | let runtime = Runtime::new().unwrap();
10 | let global_class = js::make_default_global_class();
11 | let context = runtime.cx();
12 |
13 | let realm_opts = js::make_default_realm_options();
14 | root!(with(context);
15 | let global_object = js::new_global_object(runtime.cx(), &global_class, &realm_opts);
16 | );
17 |
18 | let global_object_handle = global_object.handle();
19 | let _ar = JSAutoRealm::new(context, global_object_handle.get());
20 |
21 | root!(with(context);
22 | let mut return_value = js::undefined_value();
23 | );
24 |
25 | let return_value_handle = return_value.mut_handle();
26 | let mut script = Utf8Source::new(context, "41 + 1").unwrap();
27 | let compile_opts = CompilationOptions::new(context, 1, false, "eval.js".into()).unwrap();
28 |
29 | root!(with(context);
30 | let js_script = runtime.compile(&compile_opts, &mut script).unwrap();
31 | );
32 | runtime
33 | .execute(js_script.handle(), return_value_handle)
34 | .unwrap();
35 | let result = return_value.get().to_int32();
36 | assert_eq!(result, 42);
37 |
38 | let mut script = Utf8Source::new(context, "invalid syntax").unwrap();
39 | let compile_opts = CompilationOptions::new(context, 1, false, "eval.js".into()).unwrap();
40 |
41 | assert!(runtime.compile(&compile_opts, &mut script).is_err());
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/crates/spidermonkey-wasm/tests/context_opts.rs:
--------------------------------------------------------------------------------
1 | mod context_opts {
2 | use spidermonkey_wasm::{js, runtime::Runtime};
3 |
4 | #[test]
5 | fn set_unset() {
6 | let runtime = Runtime::new().unwrap();
7 | let context = runtime.cx();
8 | let mut opts_ref = js::context_options_ref(context);
9 |
10 | opts_ref = opts_ref
11 | .set_private_class_fields(true)
12 | .set_class_static_blocks(true)
13 | .set_private_class_methods(true)
14 | .set_ergnomic_brand_checks(true);
15 |
16 | assert!(opts_ref.private_class_fields());
17 | assert!(opts_ref.private_class_methods());
18 | assert!(opts_ref.class_static_blocks());
19 |
20 | opts_ref = opts_ref
21 | .set_private_class_fields(false)
22 | .set_class_static_blocks(false)
23 | .set_private_class_methods(false)
24 | .set_ergnomic_brand_checks(false);
25 |
26 | assert!(!opts_ref.private_class_fields());
27 | assert!(!opts_ref.private_class_methods());
28 | assert!(!opts_ref.class_static_blocks());
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/crates/spidermonkey-wasm/tests/eval.rs:
--------------------------------------------------------------------------------
1 | mod eval {
2 | use spidermonkey_wasm::{
3 | compilation_options::CompilationOptions, js, root, runtime::Runtime,
4 | utf8_source::Utf8Source, JSAutoRealm,
5 | };
6 |
7 | use spidermonkey_wasm_sys::jsffi::js_set_gc_zeal;
8 |
9 | #[test]
10 | fn eval() {
11 | let runtime = Runtime::new().unwrap();
12 | let global_class = js::make_default_global_class();
13 | let context = runtime.cx();
14 |
15 | unsafe {
16 | js_set_gc_zeal(context, 2, 1);
17 | }
18 |
19 | let realm_opts = js::make_default_realm_options();
20 | root!(with(context);
21 | let global_object = js::new_global_object(context, &global_class, &realm_opts);
22 | );
23 |
24 | let global_object_handle = global_object.handle();
25 | let _ar = JSAutoRealm::new(context, global_object_handle.get());
26 |
27 | root!(with(context);
28 | let mut return_value = js::undefined_value();
29 | );
30 |
31 | let return_value_handle = return_value.mut_handle();
32 | let mut script = Utf8Source::new(context, "41 + 1").unwrap();
33 | let compile_opts = CompilationOptions::new(context, 1, false, "eval.js".into()).unwrap();
34 |
35 | runtime
36 | .eval(&compile_opts, &mut script, return_value_handle)
37 | .unwrap();
38 | let result = return_value.get().to_int32();
39 | assert_eq!(result, 42);
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/crates/spidermonkey-wasm/tests/realm_opts.rs:
--------------------------------------------------------------------------------
1 | mod realm_opts {
2 | use spidermonkey_wasm::{js, runtime::Runtime, WeakRefSpecifier};
3 |
4 | #[test]
5 | fn set() {
6 | let _runtime = Runtime::new().unwrap();
7 | let mut realm_opts = js::make_default_realm_options();
8 | let mut realm_creation_opts = realm_opts.pin_mut().creation_options();
9 |
10 | realm_creation_opts = realm_creation_opts
11 | .set_streams_enabled(true)
12 | .set_readable_byte_streams_enabled(true)
13 | .set_byob_stream_readers_enabled(true)
14 | .set_readable_stream_pipe_to_enabled(true)
15 | .set_writable_streams_enabled(true)
16 | .set_iterator_helpers_enabled(true)
17 | .set_weak_refs_enabled(WeakRefSpecifier::EnabledWithCleanupSome);
18 |
19 | assert!(realm_creation_opts.get_writable_streams_enabled());
20 | assert!(realm_creation_opts.get_readable_byte_streams_enabled());
21 | assert!(realm_creation_opts.get_byob_stream_readers_enabled());
22 | assert!(realm_creation_opts.get_readable_stream_pipe_to_enabled());
23 | assert!(realm_creation_opts.get_writable_streams_enabled());
24 | assert!(realm_creation_opts.get_iterator_helpers_enabled());
25 | assert_eq!(
26 | realm_creation_opts.get_weak_refs_enabled(),
27 | WeakRefSpecifier::EnabledWithCleanupSome
28 | );
29 |
30 | realm_creation_opts = realm_creation_opts
31 | .set_streams_enabled(false)
32 | .set_readable_byte_streams_enabled(false)
33 | .set_byob_stream_readers_enabled(false)
34 | .set_readable_stream_pipe_to_enabled(false)
35 | .set_writable_streams_enabled(false)
36 | .set_iterator_helpers_enabled(false);
37 |
38 | assert!(!realm_creation_opts.get_writable_streams_enabled());
39 | assert!(!realm_creation_opts.get_readable_byte_streams_enabled());
40 | assert!(!realm_creation_opts.get_byob_stream_readers_enabled());
41 | assert!(!realm_creation_opts.get_readable_stream_pipe_to_enabled());
42 | assert!(!realm_creation_opts.get_writable_streams_enabled());
43 | assert!(!realm_creation_opts.get_iterator_helpers_enabled());
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/update-wasi-sdk.sh:
--------------------------------------------------------------------------------
1 | !# /usr/bin/env bash
2 |
3 | set -ex
4 |
5 | version=$(cat WASI_SDK)
6 | mkdir -p /opt/wasi-sdk
7 |
8 | cd /opt/wasi-sdk
9 |
10 | name=wasi-sdk-$version
11 | qualified_name=$name.0
12 |
13 | if [[ -d $qualified_name ]]; then
14 | echo "$qualified_name exists";
15 | exit 0;
16 | fi
17 |
18 | asset=$qualified_name
19 | unamestr=$(uname)
20 |
21 | if [ $unamestr = "Linux" ]; then
22 | asset=$asset-linux.tar.gz
23 | elif [ $unamestr = "Darwin" ]; then
24 | asset=$asset-macos.tar.gz
25 | else
26 | echo "Unsupported platform $unamestr";
27 | exit 1;
28 | fi
29 |
30 | curl --fail -L -O \
31 | https://github.com/WebAssembly/wasi-sdk/releases/download/$name/$asset
32 |
33 | tar xf $asset
34 | rm -rf $asset
35 |
--------------------------------------------------------------------------------