Skip to content

Commit

Permalink
Now building wasm target of Realm Core
Browse files Browse the repository at this point in the history
  • Loading branch information
kraenhansen committed Aug 16, 2024
1 parent 5d10dd5 commit c07b995
Show file tree
Hide file tree
Showing 10 changed files with 144 additions and 15 deletions.
7 changes: 5 additions & 2 deletions integration-tests/environments/browser/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@
"wireit": {
"test": {
"command": "mocha-remote -- concurrently npm:vite npm:runner",
"dependencies": ["../../../packages/realm:build:ts"]
"dependencies": [
"../../../packages/realm:build:ts",
"../../../packages/realm:prebuild-wasm"
]
}
},
"dependencies": {
Expand All @@ -33,4 +36,4 @@
"puppeteer": "^22.12.0",
"vite": "^5.2.0"
}
}
}
3 changes: 3 additions & 0 deletions integration-tests/tests/src/browser/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ if (typeof navigator.userAgent !== "string") {
throw new Error("This file is only supposed to be imported from a browser environment!");
}

import { ready } from "realm";
await ready;

// Import all the regular tests first
import "./setup-globals";

Expand Down
3 changes: 1 addition & 2 deletions integration-tests/tests/src/setup-globals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,7 @@ describe("Test Harness", function (this: Mocha.Suite) {
Context.prototype.longTimeout = longTimeout;
});

import Realm, { ready } from "realm";
await ready;
import Realm from "realm";

// Disable the logger to avoid console flooding
const { defaultLogLevel = "off" } = environment;
Expand Down
61 changes: 61 additions & 0 deletions packages/realm/bindgen/src/realm_js_wasm_helpers.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
#pragma once

#include <emscripten.h>
#include <realm_helpers.h>

namespace realm::js::wasm {
namespace {

REALM_NOINLINE inline emscripten::val toEmscriptenErrorCode(const std::error_code& e) noexcept
{
REALM_ASSERT_RELEASE(e);
auto jsErr = emscripten::val::global("Error")(emscripten::val(e.message()));
jsErr.set("code", e.value());
jsErr.set("category", e.category().name());

return jsErr;
}
REALM_NOINLINE inline emscripten::val toEmscriptenException(const std::exception& e) noexcept
{
return emscripten::val::global("Error")(std::string(e.what()));
}
REALM_NOINLINE inline emscripten::val toEmscriptenException(const std::exception_ptr& e) noexcept
{
try {
std::rethrow_exception(e);
}
catch (const std::exception& e) {
return toEmscriptenException(e);
}
catch (...) {
return emscripten::val::global("Error")(std::string("Unknown error"));
}
}
// Allocate a new C++ buffer big enough to fit the JS buffer
// Create a JS memory view around the C++ buffer
// Call TypedArray.prototype.set to efficiently copy the JS buffer into the C++ buffer via the view
REALM_NOINLINE inline std::string toBinaryData(const emscripten::val array_buffer) noexcept
{
REALM_ASSERT(array_buffer.instanceof (emscripten::val::global("ArrayBuffer")));
std::string buf;
buf.resize(array_buffer["byteLength"].as<int32_t>());

emscripten::val mv(emscripten::typed_memory_view(buf.length(), buf.data()));
mv.call<void>("set", emscripten::val::global("Uint8Array").new_(array_buffer));

return buf;
}
REALM_NOINLINE inline OwnedBinaryData toOwnedBinaryData(const emscripten::val array_buffer) noexcept
{
REALM_ASSERT(array_buffer.instanceof (emscripten::val::global("ArrayBuffer")));
auto length = array_buffer["byteLength"].as<int32_t>();

std::unique_ptr<char[]> buf(new char[length]);

emscripten::val mv(emscripten::typed_memory_view(length, buf.get()));
mv.call<void>("set", emscripten::val::global("Uint8Array").new_(array_buffer));

return OwnedBinaryData(std::move(buf), length);
}
} // namespace
} // namespace realm::js::wasm
23 changes: 15 additions & 8 deletions packages/realm/bindgen/src/templates/wasm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,9 @@ function convertPrimToEmscripten(addon: BrowserAddon, type: string, expr: string
case "uint64_t":
return `emscripten::val(${expr})`;

case "std::chrono::milliseconds":
return `emscripten::val(std::chrono::milliseconds(${expr}).count())`;

case "StringData":
case "std::string_view":
case "std::string":
Expand Down Expand Up @@ -259,6 +262,9 @@ function convertPrimFromEmscripten(addon: BrowserAddon, type: string, expr: stri
case "uint64_t":
return `${expr}.as<uint64_t>()`;

case "std::chrono::milliseconds":
return `std::chrono::milliseconds(${expr}.as<uint64_t>())`;

case "std::string":
return `${addon.get()}->wrapString((${expr}).as<std::string>())`;

Expand Down Expand Up @@ -341,7 +347,7 @@ function convertToEmscripten(addon: BrowserAddon, type: Type, expr: string): str
case "Nullable": {
return `[&] (auto&& val) { return !val ? emscripten::val::null() : ${c(inner, "FWD(val)")}; }(${expr})`;
}
case "util::Optional":
case "std::optional":
return `[&] (auto&& opt) { return !opt ? emscripten::val::undefined() : ${c(inner, "*FWD(opt)")}; }(${expr})`;
case "std::vector":
return `[&] (auto&& vec) {
Expand Down Expand Up @@ -454,7 +460,7 @@ function convertFromEmscripten(addon: BrowserAddon, type: Type, expr: string): s
inner,
"val",
)}; }(${expr})`;
case "util::Optional":
case "std::optional":
return `[&] (emscripten::val val) {
return val.isUndefined() ? ${type.toCpp()}() : ${c(inner, "val")};
}(${expr})`;
Expand Down Expand Up @@ -498,6 +504,7 @@ function convertFromEmscripten(addon: BrowserAddon, type: Type, expr: string): s
return out;
}(${expr})`;
case "AsyncCallback":
case "util::UniqueFunction":
case "std::function":
return `${type.toCpp()}(${c(inner, expr)})`;
}
Expand Down Expand Up @@ -876,7 +883,7 @@ class BrowserCppDecls extends CppDecls {
outputDefsTo(out: (...parts: string[]) => void) {
super.outputDefsTo(out);
out(`
void browser_init()
void wasm_init()
{
if (!RealmAddon::self) {
RealmAddon::self = std::make_unique<RealmAddon>();
Expand All @@ -900,15 +907,15 @@ class BrowserCppDecls extends CppDecls {
// });

out(`\nfunction("_internal_iterator", &_internal_iterator);`);
out(`\nfunction("browserInit", &browser_init);`);
out(`\nfunction("wasmInit", &wasm_init);`);
out(`\nfunction("injectInjectables", &injectExternalTypes);`);

out("\n}");
}
}

export function generate({ rawSpec, spec, file: makeFile }: TemplateContext): void {
const out = makeFile("browser_init.cpp", clangFormat);
const out = makeFile("wasm_init.cpp", clangFormat);

// HEADER
out(`// This file is generated: Update the spec instead of editing this file directly`);
Expand All @@ -920,16 +927,16 @@ export function generate({ rawSpec, spec, file: makeFile }: TemplateContext): vo
out(`
#include <emscripten/bind.h>
#include <realm_helpers.h>
#include <realm_js_browser_helpers.h>
#include <realm_js_wasm_helpers.h>
namespace realm::js::browser {
namespace realm::js::wasm {
namespace {
`);

new BrowserCppDecls(doJsPasses(spec)).outputDefsTo(out);

out(`
} // namespace
} // namespace realm::js::browser
} // namespace realm::js::wasm
`);
}
2 changes: 1 addition & 1 deletion packages/realm/binding/wasm/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ bindgen(
target_sources(realm-js PRIVATE wasm_init.cpp ${CMAKE_JS_SRC} ${BINDING_DIR}/wasm/platform.cpp)

add_executable(realm-js-wasm)
target_link_options(realm-js-wasm PRIVATE -d -sALLOW_MEMORY_GROWTH=1 -sLLD_REPORT_UNDEFINED -sFETCH=1 -lembind -fwasm-exceptions -sEXPORT_ES6=1 -sWASM_BIGINT=1 -sENVIRONMENT=web -sSTACK_SIZE=131072 --pre-js=../web_polyfill.js)
target_link_options(realm-js-wasm PRIVATE -d -sALLOW_MEMORY_GROWTH=1 -sLLD_REPORT_UNDEFINED -sFETCH=1 -lembind -fwasm-exceptions -sEXPORT_ES6=1 -sWASM_BIGINT=1 -sENVIRONMENT=web -sSTACK_SIZE=131072)
target_link_libraries(realm-js-wasm realm-js)

set_target_properties(realm-js-wasm PROPERTIES
Expand Down
15 changes: 14 additions & 1 deletion packages/realm/binding/wasm/platform.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,27 @@
#include <stdexcept>
#include <stdarg.h>
#include <stdio.h>
#include <string>

#include "../platform.hpp"

static std::string s_default_realm_directory;

namespace realm {

void JsPlatformHelpers::set_default_realm_file_directory(std::string dir)
{
s_default_realm_directory = dir;
}

std::string JsPlatformHelpers::default_realm_file_directory()
{
return std::string("");
if (!s_default_realm_directory.empty()) {
return s_default_realm_directory;
}
else {
return std::string("");
}
}

void JsPlatformHelpers::ensure_directory_exists_for_file(const std::string&)
Expand Down
21 changes: 21 additions & 0 deletions packages/realm/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,27 @@
"src/binding/wrapper.generated.ts"
]
},
"bindgen:generate:wasm-wrapper": {
"command": "realm-bindgen --template bindgen/src/templates/wasm-wrapper.ts --spec bindgen/vendor/realm-core/bindgen/spec.yml --spec bindgen/js_spec.yml --opt-in bindgen/wasm_opt_in_spec.yml --output binding/generated",
"dependencies": [
"bindgen:generate:spec-schema"
],
"files": [
"bindgen/vendor/realm-core/bindgen/spec.yml",
"bindgen/vendor/realm-core/bindgen/src",
"bindgen/js_spec.yml",
"bindgen/wasm_opt_in_spec.yml",
"bindgen/src",
"!bindgen/src/templates",
"bindgen/src/templates/base-wrapper.ts",
"bindgen/src/templates/wasm-wrapper.ts"
],
"output": [
"binding/generated/native.wasm.mjs",
"binding/generated/native.wasm.d.mts",
"binding/generated/native.wasm.d.cts"
]
},
"bindgen:generate:spec-schema": {
"command": "typescript-json-schema bindgen/vendor/realm-core/bindgen/tsconfig.json RelaxedSpec --include bindgen/vendor/realm-core/bindgen/src/spec/relaxed-model.ts --out bindgen/vendor/realm-core/bindgen/generated/spec.schema.json --required --noExtraProps",
"files": [
Expand Down
22 changes: 22 additions & 0 deletions packages/realm/src/platform/browser/binding.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2024 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////

import { injectAndPatch } from "../binding";
const bindingPromise = import("../../../binding/generated/native.wasm.cjs");

bindingPromise.then(injectAndPatch);
2 changes: 1 addition & 1 deletion packages/realm/src/platform/browser/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
//
////////////////////////////////////////////////////////////////////////////

// import "./binding";
import "./binding";
import "./fs";
import "./device-info";
// import "./sync-proxy-config";
Expand Down

0 comments on commit c07b995

Please sign in to comment.