| // Copyright 2019 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "runtime.h" |
| |
| #include <lib/syslog/cpp/macros.h> |
| #include <string.h> |
| |
| #include "src/developer/shell/josh/lib/fdio.h" |
| #include "src/developer/shell/josh/lib/fidl.h" |
| #include "src/developer/shell/josh/lib/zx.h" |
| #include "third_party/quickjs/quickjs-libc.h" |
| #include "third_party/quickjs/quickjs.h" |
| |
| // This is present and needs to be called in the most recent version of quickjs. |
| // It's only here so that we can run against both the old and new version until |
| // we integrate the new version. |
| __attribute__((weak)) void js_std_init_handlers(JSRuntime* rt) {} |
| |
| namespace shell { |
| |
| Runtime::Runtime() { |
| rt_ = JS_NewRuntime(); |
| js_std_init_handlers(rt_); |
| is_valid_ = (rt_ == nullptr); |
| |
| // The correct loader for ES6 modules. Not sure why this has to be done by hand. |
| JS_SetModuleLoaderFunc(rt_, nullptr, js_module_loader, nullptr); |
| } |
| |
| Runtime::~Runtime() { |
| if (is_valid_) { |
| js_std_free_handlers(rt_); |
| JS_FreeRuntime(rt_); |
| } |
| } |
| |
| Context::Context(const Runtime* rt) : ctx_(JS_NewContext(rt->Get())) { |
| is_valid_ = (ctx_ == nullptr); |
| |
| #if __has_feature(address_sanitizer) |
| // ASan tends to exceed the max stack size of 256K. |
| JS_SetMaxStackSize(rt->Get(), 1024 * 1024); |
| #endif // __has_feature(address_sanitizer) |
| } |
| |
| Context::~Context() { |
| if (is_valid_) { |
| JS_FreeContext(ctx_); |
| } |
| } |
| |
| bool Context::Export(const std::string& lib, const std::string& js_path) { |
| std::string path; |
| int flags = JS_EVAL_TYPE_MODULE; |
| if (!js_path.empty()) { |
| path = js_path; |
| if (path[path.length() - 1] != '/') { |
| path.append("/"); |
| } |
| path.append(lib); |
| path.append(".js"); |
| } else { |
| path = lib; |
| flags |= JS_EVAL_FLAG_COMPILE_ONLY; |
| } |
| std::string init_str = "import * as " + lib + " from '" + path + |
| "';\n" |
| "globalThis." + |
| lib + " = " + lib + ";\n"; |
| |
| JSValue init_compile = JS_Eval(ctx_, init_str.c_str(), init_str.length(), "<input>", flags); |
| |
| if (JS_IsException(init_compile)) { |
| return false; |
| } |
| |
| if (js_path.empty()) { |
| js_module_set_import_meta(ctx_, init_compile, 1, 1); |
| JSValue init_run = JS_EvalFunction(ctx_, init_compile); |
| |
| if (JS_IsException(init_run)) { |
| js_std_dump_error(ctx_); |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| bool Context::InitStd() { |
| // System modules |
| js_init_module_std(ctx_, "std"); |
| if (!Export("std")) { |
| return false; |
| } |
| |
| js_init_module_os(ctx_, "os"); |
| if (!Export("os")) { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| extern "C" const uint8_t qjsc_fidl[]; |
| extern "C" const uint32_t qjsc_fidl_size; |
| extern "C" const uint8_t qjsc_fdio[]; |
| extern "C" const uint32_t qjsc_fdio_size; |
| extern "C" const uint8_t qjsc_zx[]; |
| extern "C" const uint32_t qjsc_zx_size; |
| |
| bool Context::InitBuiltins(const std::string& fidl_path, const std::string& boot_js_path) { |
| if (fdio::FdioModuleInit(ctx_, "fdio") == nullptr) { |
| return false; |
| } |
| if (!Export("fdio")) { |
| return false; |
| } |
| |
| if (fidl::FidlModuleInit(ctx_, "fidl_internal", fidl_path) == nullptr) { |
| return false; |
| } |
| js_std_eval_binary(ctx_, qjsc_fidl, qjsc_fidl_size, 0); |
| |
| if (zx::ZxModuleInit(ctx_, "zx_internal") == nullptr) { |
| return false; |
| } |
| |
| js_std_eval_binary(ctx_, qjsc_zx, qjsc_zx_size, 0); |
| |
| if (!boot_js_path.empty()) { |
| if (!Export("pp", boot_js_path)) { |
| return false; |
| } |
| if (!Export("ns", boot_js_path)) { |
| return false; |
| } |
| if (!Export("task", boot_js_path)) { |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| } // namespace shell |