зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1637529 - Rewrite the shell module loader in C++ r=jandem
Sorry for the big patch. This is a straight rewrite of shell/ModuleLoader.js in C++. It's mostly straigtforward but there were a couple of clunky parts: using promises/closures from C++ was rather verbose and I had to write some string utilities. Differential Revision: https://phabricator.services.mozilla.com/D75271
This commit is contained in:
Родитель
74a8ec946b
Коммит
7bc7b5ed18
|
@ -0,0 +1,533 @@
|
|||
/* -*- Mode: javascript; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4
|
||||
* -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "shell/ModuleLoader.h"
|
||||
|
||||
#include "mozilla/ArrayUtils.h"
|
||||
#include "mozilla/DebugOnly.h"
|
||||
#include "mozilla/TextUtils.h"
|
||||
|
||||
#include "NamespaceImports.h"
|
||||
|
||||
#include "js/Modules.h"
|
||||
#include "js/SourceText.h"
|
||||
#include "js/StableStringChars.h"
|
||||
#include "shell/jsshell.h"
|
||||
#include "shell/OSObject.h"
|
||||
#include "shell/StringUtils.h"
|
||||
#include "vm/JSAtom.h"
|
||||
#include "vm/JSContext.h"
|
||||
#include "vm/StringType.h"
|
||||
|
||||
using namespace js;
|
||||
using namespace js::shell;
|
||||
|
||||
static constexpr char16_t JavaScriptScheme[] = u"javascript:";
|
||||
|
||||
static bool IsJavaScriptURL(HandleLinearString path) {
|
||||
return StringStartsWith(path, JavaScriptScheme);
|
||||
}
|
||||
|
||||
static JSString* ExtractJavaScriptURLSource(JSContext* cx,
|
||||
HandleLinearString path) {
|
||||
MOZ_ASSERT(IsJavaScriptURL(path));
|
||||
|
||||
const size_t schemeLength = mozilla::ArrayLength(JavaScriptScheme) - 1;
|
||||
return SubString(cx, path, schemeLength);
|
||||
}
|
||||
|
||||
bool ModuleLoader::init(JSContext* cx, HandleString loadPath) {
|
||||
loadPathStr = AtomizeString(cx, loadPath, PinAtom);
|
||||
if (!loadPathStr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(IsAbsolutePath(loadPathStr));
|
||||
|
||||
char16_t sep = PathSeparator;
|
||||
pathSeparatorStr = AtomizeChars(cx, &sep, 1);
|
||||
if (!pathSeparatorStr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
JSRuntime* rt = cx->runtime();
|
||||
JS::SetModuleResolveHook(rt, ModuleLoader::ResolveImportedModule);
|
||||
JS::SetModuleMetadataHook(rt, ModuleLoader::GetImportMetaProperties);
|
||||
JS::SetModuleDynamicImportHook(rt, ModuleLoader::ImportModuleDynamically);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// static
|
||||
JSObject* ModuleLoader::ResolveImportedModule(
|
||||
JSContext* cx, JS::HandleValue referencingPrivate,
|
||||
JS::HandleString specifier) {
|
||||
ShellContext* scx = GetShellContext(cx);
|
||||
return scx->moduleLoader->resolveImportedModule(cx, referencingPrivate,
|
||||
specifier);
|
||||
}
|
||||
|
||||
// static
|
||||
bool ModuleLoader::GetImportMetaProperties(JSContext* cx,
|
||||
JS::HandleValue privateValue,
|
||||
JS::HandleObject metaObject) {
|
||||
ShellContext* scx = GetShellContext(cx);
|
||||
return scx->moduleLoader->populateImportMeta(cx, privateValue, metaObject);
|
||||
}
|
||||
|
||||
// static
|
||||
bool ModuleLoader::ImportModuleDynamically(JSContext* cx,
|
||||
JS::HandleValue referencingPrivate,
|
||||
JS::HandleString specifier,
|
||||
JS::HandleObject promise) {
|
||||
ShellContext* scx = GetShellContext(cx);
|
||||
return scx->moduleLoader->dynamicImport(cx, referencingPrivate, specifier,
|
||||
promise);
|
||||
}
|
||||
|
||||
bool ModuleLoader::loadRootModule(JSContext* cx, HandleString path) {
|
||||
return loadAndExecute(cx, path);
|
||||
}
|
||||
|
||||
bool ModuleLoader::loadAndExecute(JSContext* cx, HandleString path) {
|
||||
RootedObject module(cx, loadAndParse(cx, path));
|
||||
if (!module) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return JS::ModuleInstantiate(cx, module) && JS::ModuleEvaluate(cx, module);
|
||||
}
|
||||
|
||||
JSObject* ModuleLoader::resolveImportedModule(
|
||||
JSContext* cx, JS::HandleValue referencingPrivate,
|
||||
JS::HandleString specifier) {
|
||||
RootedLinearString path(cx, resolve(cx, specifier, referencingPrivate));
|
||||
if (!path) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return loadAndParse(cx, path);
|
||||
}
|
||||
|
||||
bool ModuleLoader::populateImportMeta(JSContext* cx,
|
||||
JS::HandleValue privateValue,
|
||||
JS::HandleObject metaObject) {
|
||||
RootedLinearString path(cx);
|
||||
if (!privateValue.isUndefined()) {
|
||||
if (!getScriptPath(cx, privateValue, &path)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!path) {
|
||||
path = NewStringCopyZ<CanGC>(cx, "(unknown)");
|
||||
if (!path) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
RootedValue pathValue(cx, StringValue(path));
|
||||
return JS_DefineProperty(cx, metaObject, "url", pathValue, JSPROP_ENUMERATE);
|
||||
}
|
||||
|
||||
bool ModuleLoader::dynamicImport(JSContext* cx,
|
||||
JS::HandleValue referencingPrivate,
|
||||
JS::HandleString specifier,
|
||||
JS::HandleObject promise) {
|
||||
// To make this more realistic, use a promise to delay the import and make it
|
||||
// happen asynchronously. This method packages up the arguments and creates a
|
||||
// resolved promise, which on fullfillment calls doDynamicImport with the
|
||||
// original arguments.
|
||||
|
||||
MOZ_ASSERT(promise);
|
||||
RootedValue specifierValue(cx, StringValue(specifier));
|
||||
RootedValue promiseValue(cx, ObjectValue(*promise));
|
||||
RootedObject closure(cx, JS_NewPlainObject(cx));
|
||||
if (!closure ||
|
||||
!JS_DefineProperty(cx, closure, "referencingPrivate", referencingPrivate,
|
||||
JSPROP_ENUMERATE) ||
|
||||
!JS_DefineProperty(cx, closure, "specifier", specifierValue,
|
||||
JSPROP_ENUMERATE) ||
|
||||
!JS_DefineProperty(cx, closure, "promise", promiseValue,
|
||||
JSPROP_ENUMERATE)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RootedFunction onResolved(
|
||||
cx, NewNativeFunction(cx, DynamicImportDelayFulfilled, 1, nullptr));
|
||||
if (!onResolved) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RootedFunction onRejected(
|
||||
cx, NewNativeFunction(cx, DynamicImportDelayRejected, 1, nullptr));
|
||||
if (!onRejected) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RootedObject delayPromise(cx);
|
||||
RootedValue closureValue(cx, ObjectValue(*closure));
|
||||
delayPromise = PromiseObject::unforgeableResolve(cx, closureValue);
|
||||
if (!delayPromise) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return JS::AddPromiseReactions(cx, delayPromise, onResolved, onRejected);
|
||||
}
|
||||
|
||||
bool ModuleLoader::DynamicImportDelayFulfilled(JSContext* cx, unsigned argc,
|
||||
Value* vp) {
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
RootedObject closure(cx, &args[0].toObject());
|
||||
|
||||
RootedValue referencingPrivate(cx);
|
||||
RootedValue specifierValue(cx);
|
||||
RootedValue promiseValue(cx);
|
||||
if (!JS_GetProperty(cx, closure, "referencingPrivate", &referencingPrivate) ||
|
||||
!JS_GetProperty(cx, closure, "specifier", &specifierValue) ||
|
||||
!JS_GetProperty(cx, closure, "promise", &promiseValue)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RootedString specifier(cx, specifierValue.toString());
|
||||
RootedObject promise(cx, &promiseValue.toObject());
|
||||
|
||||
ShellContext* scx = GetShellContext(cx);
|
||||
return scx->moduleLoader->doDynamicImport(cx, referencingPrivate, specifier,
|
||||
promise);
|
||||
}
|
||||
|
||||
bool ModuleLoader::DynamicImportDelayRejected(JSContext* cx, unsigned argc,
|
||||
Value* vp) {
|
||||
MOZ_CRASH("This promise should never be rejected");
|
||||
}
|
||||
|
||||
bool ModuleLoader::doDynamicImport(JSContext* cx,
|
||||
JS::HandleValue referencingPrivate,
|
||||
JS::HandleString specifier,
|
||||
JS::HandleObject promise) {
|
||||
// Exceptions during dynamic import are handled by calling
|
||||
// FinishDynamicModuleImport with a pending exception on the context.
|
||||
mozilla::DebugOnly<bool> ok =
|
||||
tryDynamicImport(cx, referencingPrivate, specifier, promise);
|
||||
MOZ_ASSERT_IF(!ok, JS_IsExceptionPending(cx));
|
||||
return JS::FinishDynamicModuleImport(cx, referencingPrivate, specifier,
|
||||
promise);
|
||||
}
|
||||
|
||||
bool ModuleLoader::tryDynamicImport(JSContext* cx,
|
||||
JS::HandleValue referencingPrivate,
|
||||
JS::HandleString specifier,
|
||||
JS::HandleObject promise) {
|
||||
RootedLinearString path(cx, resolve(cx, specifier, referencingPrivate));
|
||||
if (!path) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return loadAndExecute(cx, path);
|
||||
}
|
||||
|
||||
JSLinearString* ModuleLoader::resolve(JSContext* cx, HandleString nameArg,
|
||||
HandleValue referencingInfo) {
|
||||
if (nameArg->length() == 0) {
|
||||
JS_ReportErrorASCII(cx, "Invalid module specifier");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RootedLinearString name(cx, JS_EnsureLinearString(cx, nameArg));
|
||||
if (!name) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (IsJavaScriptURL(name) || IsAbsolutePath(name)) {
|
||||
return name;
|
||||
}
|
||||
|
||||
// Treat |name| as a relative path if it starts with either "./" or "../".
|
||||
bool isRelative =
|
||||
StringStartsWith(name, u"./") || StringStartsWith(name, u"../")
|
||||
#ifdef XP_WIN
|
||||
|| StringStartsWith(name, u".\\") || StringStartsWith(name, u"..\\")
|
||||
#endif
|
||||
;
|
||||
|
||||
RootedString path(cx, loadPathStr);
|
||||
|
||||
if (isRelative) {
|
||||
if (referencingInfo.isUndefined()) {
|
||||
JS_ReportErrorASCII(cx, "No referencing module for relative import");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RootedLinearString refPath(cx);
|
||||
if (!getScriptPath(cx, referencingInfo, &refPath)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!refPath) {
|
||||
JS_ReportErrorASCII(cx, "No path set for referencing module");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int32_t sepIndex = LastIndexOf(refPath, u'/');
|
||||
#ifdef XP_WIN
|
||||
sepIndex = std::max(sepIndex, LastIndexOf(refPath, u'\\'));
|
||||
#endif
|
||||
if (sepIndex >= 0) {
|
||||
path = SubString(cx, refPath, 0, sepIndex);
|
||||
if (!path) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RootedString result(cx);
|
||||
RootedString pathSep(cx, pathSeparatorStr);
|
||||
result = JS_ConcatStrings(cx, path, pathSep);
|
||||
if (!result) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
result = JS_ConcatStrings(cx, result, name);
|
||||
if (!result) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return JS_EnsureLinearString(cx, result);
|
||||
}
|
||||
|
||||
JSObject* ModuleLoader::loadAndParse(JSContext* cx, HandleString pathArg) {
|
||||
RootedLinearString path(cx, JS_EnsureLinearString(cx, pathArg));
|
||||
if (!path) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
path = normalizePath(cx, path);
|
||||
if (!path) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RootedObject module(cx);
|
||||
if (!lookupModuleInRegistry(cx, path, &module)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (module) {
|
||||
return module;
|
||||
}
|
||||
|
||||
UniqueChars filename = JS_EncodeStringToLatin1(cx, path);
|
||||
if (!filename) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
JS::CompileOptions options(cx);
|
||||
options.setFileAndLine(filename.get(), 1);
|
||||
|
||||
RootedString source(cx, fetchSource(cx, path));
|
||||
if (!source) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
JS::AutoStableStringChars stableChars(cx);
|
||||
if (!stableChars.initTwoByte(cx, source)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const char16_t* chars = stableChars.twoByteRange().begin().get();
|
||||
JS::SourceText<char16_t> srcBuf;
|
||||
if (!srcBuf.init(cx, chars, source->length(),
|
||||
JS::SourceOwnership::Borrowed)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
module = JS::CompileModule(cx, options, srcBuf);
|
||||
if (!module) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RootedObject info(cx, CreateScriptPrivate(cx, path));
|
||||
if (!info) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
JS::SetModulePrivate(module, ObjectValue(*info));
|
||||
|
||||
if (!addModuleToRegistry(cx, path, module)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return module;
|
||||
}
|
||||
|
||||
bool ModuleLoader::lookupModuleInRegistry(JSContext* cx, HandleString path,
|
||||
MutableHandleObject moduleOut) {
|
||||
moduleOut.set(nullptr);
|
||||
|
||||
RootedObject registry(cx, getOrCreateModuleRegistry(cx));
|
||||
if (!registry) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RootedValue pathValue(cx, StringValue(path));
|
||||
RootedValue moduleValue(cx);
|
||||
if (!JS::MapGet(cx, registry, pathValue, &moduleValue)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!moduleValue.isUndefined()) {
|
||||
moduleOut.set(&moduleValue.toObject());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ModuleLoader::addModuleToRegistry(JSContext* cx, HandleString path,
|
||||
HandleObject module) {
|
||||
RootedObject registry(cx, getOrCreateModuleRegistry(cx));
|
||||
if (!registry) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RootedValue pathValue(cx, StringValue(path));
|
||||
RootedValue moduleValue(cx, ObjectValue(*module));
|
||||
return JS::MapSet(cx, registry, pathValue, moduleValue);
|
||||
}
|
||||
|
||||
JSObject* ModuleLoader::getOrCreateModuleRegistry(JSContext* cx) {
|
||||
Handle<GlobalObject*> global = cx->global();
|
||||
RootedValue value(cx, global->getReservedSlot(GlobalAppSlotModuleRegistry));
|
||||
if (!value.isUndefined()) {
|
||||
return &value.toObject();
|
||||
}
|
||||
|
||||
JSObject* registry = JS::NewMapObject(cx);
|
||||
if (!registry) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
global->setReservedSlot(GlobalAppSlotModuleRegistry, ObjectValue(*registry));
|
||||
return registry;
|
||||
}
|
||||
|
||||
bool ModuleLoader::getScriptPath(JSContext* cx, HandleValue privateValue,
|
||||
MutableHandle<JSLinearString*> pathOut) {
|
||||
pathOut.set(nullptr);
|
||||
|
||||
RootedObject infoObj(cx, &privateValue.toObject());
|
||||
RootedValue pathValue(cx);
|
||||
if (!JS_GetProperty(cx, infoObj, "path", &pathValue)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pathValue.isUndefined()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
RootedString path(cx, pathValue.toString());
|
||||
pathOut.set(JS_EnsureLinearString(cx, path));
|
||||
return pathOut;
|
||||
}
|
||||
|
||||
JSLinearString* ModuleLoader::normalizePath(JSContext* cx,
|
||||
HandleLinearString pathArg) {
|
||||
RootedLinearString path(cx, pathArg);
|
||||
|
||||
if (IsJavaScriptURL(path)) {
|
||||
return path;
|
||||
}
|
||||
|
||||
#ifdef XP_WIN
|
||||
// Replace all forward slashes with backward slashes.
|
||||
path = ReplaceCharGlobally(cx, path, u'/', PathSeparator);
|
||||
if (!path) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Remove the drive letter, if present.
|
||||
RootedLinearString drive(cx);
|
||||
if (path->length() > 2 && mozilla::IsAsciiAlpha(CharAt(path, 0)) &&
|
||||
CharAt(path, 1) == u':' && CharAt(path, 2) == u'\\') {
|
||||
drive = SubString(cx, path, 0, 2);
|
||||
path = SubString(cx, path, 2);
|
||||
if (!drive || !path) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
#endif // XP_WIN
|
||||
|
||||
// Normalize the path by removing redundant path components.
|
||||
Rooted<GCVector<JSLinearString*>> components(cx);
|
||||
size_t lastSep = 0;
|
||||
while (lastSep < path->length()) {
|
||||
int32_t i = IndexOf(path, PathSeparator, lastSep);
|
||||
if (i < 0) {
|
||||
i = path->length();
|
||||
}
|
||||
|
||||
RootedLinearString part(cx, SubString(cx, path, lastSep, i));
|
||||
if (!part) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
lastSep = i + 1;
|
||||
|
||||
// Remove "." when preceded by a path component.
|
||||
if (StringEquals(part, u".") && !components.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (StringEquals(part, u"..") && !components.empty()) {
|
||||
// Replace "./.." with "..".
|
||||
if (StringEquals(components.back(), u".")) {
|
||||
components.back() = part;
|
||||
continue;
|
||||
}
|
||||
|
||||
// When preceded by a non-empty path component, remove ".." and the
|
||||
// preceding component, unless the preceding component is also "..".
|
||||
if (!StringEquals(components.back(), u"") &&
|
||||
!StringEquals(components.back(), u"..")) {
|
||||
components.popBack();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (!components.append(part)) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
RootedLinearString pathSep(cx, pathSeparatorStr);
|
||||
RootedString normalized(cx, JoinStrings(cx, components, pathSep));
|
||||
if (!normalized) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#ifdef XP_WIN
|
||||
if (drive) {
|
||||
normalized = JS_ConcatStrings(cx, drive, normalized);
|
||||
if (!normalized) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return JS_EnsureLinearString(cx, normalized);
|
||||
}
|
||||
|
||||
JSString* ModuleLoader::fetchSource(JSContext* cx, HandleLinearString path) {
|
||||
if (IsJavaScriptURL(path)) {
|
||||
return ExtractJavaScriptURLSource(cx, path);
|
||||
}
|
||||
|
||||
RootedString resolvedPath(cx, ResolvePath(cx, path, RootRelative));
|
||||
if (!resolvedPath) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return FileAsString(cx, resolvedPath);
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* vim: set ts=8 sts=2 et sw=2 tw=80:
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef shell_ModuleLoader_h
|
||||
#define shell_ModuleLoader_h
|
||||
|
||||
#include "gc/Rooting.h"
|
||||
#include "js/RootingAPI.h"
|
||||
|
||||
namespace js {
|
||||
namespace shell {
|
||||
|
||||
class ModuleLoader {
|
||||
public:
|
||||
bool init(JSContext* cx, HandleString loadPath);
|
||||
bool loadRootModule(JSContext* cx, HandleString path);
|
||||
|
||||
private:
|
||||
static JSObject* ResolveImportedModule(JSContext* cx,
|
||||
HandleValue referencingPrivate,
|
||||
HandleString specifier);
|
||||
static bool GetImportMetaProperties(JSContext* cx, HandleValue privateValue,
|
||||
HandleObject metaObject);
|
||||
static bool ImportModuleDynamically(JSContext* cx,
|
||||
HandleValue referencingPrivate,
|
||||
HandleString specifier,
|
||||
HandleObject promise);
|
||||
|
||||
static bool DynamicImportDelayFulfilled(JSContext* cx, unsigned argc,
|
||||
Value* vp);
|
||||
static bool DynamicImportDelayRejected(JSContext* cx, unsigned argc,
|
||||
Value* vp);
|
||||
|
||||
bool loadAndExecute(JSContext* cx, HandleString path);
|
||||
JSObject* resolveImportedModule(JSContext* cx, HandleValue referencingPrivate,
|
||||
HandleString specifier);
|
||||
bool populateImportMeta(JSContext* cx, HandleValue privateValue,
|
||||
HandleObject metaObject);
|
||||
bool dynamicImport(JSContext* cx, HandleValue referencingPrivate,
|
||||
HandleString specifier, HandleObject promise);
|
||||
bool doDynamicImport(JSContext* cx, HandleValue referencingPrivate,
|
||||
HandleString specifier, HandleObject promise);
|
||||
bool tryDynamicImport(JSContext* cx, HandleValue referencingPrivate,
|
||||
HandleString specifier, HandleObject promise);
|
||||
JSObject* loadAndParse(JSContext* cx, HandleString path);
|
||||
bool lookupModuleInRegistry(JSContext* cx, HandleString path,
|
||||
MutableHandleObject moduleOut);
|
||||
bool addModuleToRegistry(JSContext* cx, HandleString path,
|
||||
HandleObject module);
|
||||
JSLinearString* resolve(JSContext* cx, HandleString name,
|
||||
HandleValue referencingInfo);
|
||||
bool getScriptPath(JSContext* cx, HandleValue privateValue,
|
||||
MutableHandle<JSLinearString*> pathOut);
|
||||
JSLinearString* normalizePath(JSContext* cx, HandleLinearString path);
|
||||
JSObject* getOrCreateModuleRegistry(JSContext* cx);
|
||||
JSString* fetchSource(JSContext* cx, HandleLinearString path);
|
||||
|
||||
// The following are used for pinned atoms which do not need rooting.
|
||||
JSAtom* loadPathStr = nullptr;
|
||||
JSAtom* pathSeparatorStr = nullptr;
|
||||
} JS_HAZ_NON_GC_POINTER;
|
||||
|
||||
} // namespace shell
|
||||
} // namespace js
|
||||
|
||||
#endif // shell_ModuleLoader_h
|
|
@ -1,246 +0,0 @@
|
|||
/* -*- Mode: javascript; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/* global getModuleLoadPath setModuleLoadHook setModuleResolveHook setModuleMetadataHook */
|
||||
/* global getModulePrivate setModulePrivate parseModule os */
|
||||
/* global setModuleDynamicImportHook finishDynamicModuleImport abortDynamicModuleImport */
|
||||
|
||||
// A basic synchronous module loader for testing the shell.
|
||||
//
|
||||
// Supports loading files and 'javascript:' URLs that embed JS source text.
|
||||
|
||||
{
|
||||
// Save standard built-ins before scripts can modify them.
|
||||
const ArrayPrototypeJoin = Array.prototype.join;
|
||||
const MapPrototypeGet = Map.prototype.get;
|
||||
const MapPrototypeHas = Map.prototype.has;
|
||||
const MapPrototypeSet = Map.prototype.set;
|
||||
const ObjectDefineProperty = Object.defineProperty;
|
||||
const ReflectApply = Reflect.apply;
|
||||
const StringPrototypeIndexOf = String.prototype.indexOf;
|
||||
const StringPrototypeLastIndexOf = String.prototype.lastIndexOf;
|
||||
const StringPrototypeStartsWith = String.prototype.startsWith;
|
||||
const StringPrototypeSubstring = String.prototype.substring;
|
||||
const ErrorClass = Error;
|
||||
const PromiseClass = Promise;
|
||||
const PromiseResolve = Promise.resolve;
|
||||
|
||||
const JAVASCRIPT_SCHEME = "javascript:";
|
||||
|
||||
const ReflectLoader = new class {
|
||||
constructor() {
|
||||
this.registry = new Map();
|
||||
this.loadPath = getModuleLoadPath();
|
||||
}
|
||||
|
||||
isJavascriptURL(name) {
|
||||
return ReflectApply(StringPrototypeStartsWith, name, [JAVASCRIPT_SCHEME]);
|
||||
}
|
||||
|
||||
resolve(name, referencingInfo) {
|
||||
if (name === "") {
|
||||
throw new ErrorClass("Invalid module specifier");
|
||||
}
|
||||
|
||||
if (this.isJavascriptURL(name) || os.path.isAbsolute(name)) {
|
||||
return name;
|
||||
}
|
||||
|
||||
let loadPath = this.loadPath;
|
||||
|
||||
// Treat |name| as a relative path if it starts with either "./"
|
||||
// or "../".
|
||||
let isRelative = ReflectApply(StringPrototypeStartsWith, name, ["./"])
|
||||
|| ReflectApply(StringPrototypeStartsWith, name, ["../"])
|
||||
#ifdef XP_WIN
|
||||
|| ReflectApply(StringPrototypeStartsWith, name, [".\\"])
|
||||
|| ReflectApply(StringPrototypeStartsWith, name, ["..\\"])
|
||||
#endif
|
||||
;
|
||||
|
||||
// If |name| is a relative path and the referencing module's path is
|
||||
// available, load |name| relative to the that path.
|
||||
if (isRelative) {
|
||||
if (!referencingInfo) {
|
||||
throw new ErrorClass("No referencing module for relative import");
|
||||
}
|
||||
|
||||
let path = referencingInfo.path;
|
||||
|
||||
let sepIndex = ReflectApply(StringPrototypeLastIndexOf, path, ["/"]);
|
||||
#ifdef XP_WIN
|
||||
let otherSepIndex = ReflectApply(StringPrototypeLastIndexOf, path, ["\\"]);
|
||||
if (otherSepIndex > sepIndex)
|
||||
sepIndex = otherSepIndex;
|
||||
#endif
|
||||
if (sepIndex >= 0)
|
||||
loadPath = ReflectApply(StringPrototypeSubstring, path, [0, sepIndex]);
|
||||
}
|
||||
|
||||
return os.path.join(loadPath, name);
|
||||
}
|
||||
|
||||
normalize(path) {
|
||||
if (this.isJavascriptURL(path)) {
|
||||
return path;
|
||||
}
|
||||
|
||||
#ifdef XP_WIN
|
||||
// Replace all forward slashes with backward slashes.
|
||||
// NB: It may be tempting to replace this loop with a call to
|
||||
// String.prototype.replace, but user scripts may have modified
|
||||
// String.prototype or RegExp.prototype built-in functions, which makes
|
||||
// it unsafe to call String.prototype.replace.
|
||||
let newPath = "";
|
||||
let lastSlash = 0;
|
||||
while (true) {
|
||||
let i = ReflectApply(StringPrototypeIndexOf, path, ["/", lastSlash]);
|
||||
if (i < 0) {
|
||||
newPath += ReflectApply(StringPrototypeSubstring, path, [lastSlash]);
|
||||
break;
|
||||
}
|
||||
newPath += ReflectApply(StringPrototypeSubstring, path, [lastSlash, i]) + "\\";
|
||||
lastSlash = i + 1;
|
||||
}
|
||||
path = newPath;
|
||||
|
||||
// Remove the drive letter, if present.
|
||||
let isalpha = c => ("A" <= c && c <= "Z") || ("a" <= c && c <= "z");
|
||||
let drive = "";
|
||||
if (path.length > 2 && isalpha(path[0]) && path[1] === ":" && path[2] === "\\") {
|
||||
drive = ReflectApply(StringPrototypeSubstring, path, [0, 2]);
|
||||
path = ReflectApply(StringPrototypeSubstring, path, [2]);
|
||||
}
|
||||
#endif
|
||||
|
||||
const pathsep =
|
||||
#ifdef XP_WIN
|
||||
"\\";
|
||||
#else
|
||||
"/";
|
||||
#endif
|
||||
|
||||
let n = 0;
|
||||
let components = [];
|
||||
|
||||
// Normalize the path by removing redundant path components.
|
||||
// NB: See above for why we don't call String.prototype.split here.
|
||||
let lastSep = 0;
|
||||
while (lastSep < path.length) {
|
||||
let i = ReflectApply(StringPrototypeIndexOf, path, [pathsep, lastSep]);
|
||||
if (i < 0)
|
||||
i = path.length;
|
||||
let part = ReflectApply(StringPrototypeSubstring, path, [lastSep, i]);
|
||||
lastSep = i + 1;
|
||||
|
||||
// Remove "." when preceded by a path component.
|
||||
if (part === "." && n > 0)
|
||||
continue;
|
||||
|
||||
if (part === ".." && n > 0) {
|
||||
// Replace "./.." with "..".
|
||||
if (components[n - 1] === ".") {
|
||||
components[n - 1] = "..";
|
||||
continue;
|
||||
}
|
||||
|
||||
// When preceded by a non-empty path component, remove ".." and
|
||||
// the preceding component, unless the preceding component is also
|
||||
// "..".
|
||||
if (components[n - 1] !== "" && components[n - 1] !== "..") {
|
||||
components.length = --n;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
ObjectDefineProperty(components, n++, {
|
||||
__proto__: null,
|
||||
value: part,
|
||||
writable: true, enumerable: true, configurable: true,
|
||||
});
|
||||
}
|
||||
|
||||
let normalized = ReflectApply(ArrayPrototypeJoin, components, [pathsep]);
|
||||
#ifdef XP_WIN
|
||||
normalized = drive + normalized;
|
||||
#endif
|
||||
return normalized;
|
||||
}
|
||||
|
||||
fetch(path) {
|
||||
if (this.isJavascriptURL(path)) {
|
||||
return ReflectApply(StringPrototypeSubstring, path, [JAVASCRIPT_SCHEME.length]);
|
||||
}
|
||||
|
||||
return os.file.readFile(path);
|
||||
}
|
||||
|
||||
loadAndParse(path) {
|
||||
let normalized = this.normalize(path);
|
||||
if (ReflectApply(MapPrototypeHas, this.registry, [normalized]))
|
||||
return ReflectApply(MapPrototypeGet, this.registry, [normalized]);
|
||||
|
||||
let source = this.fetch(path);
|
||||
let module = parseModule(source, path);
|
||||
let moduleInfo = { path: normalized };
|
||||
setModulePrivate(module, moduleInfo);
|
||||
ReflectApply(MapPrototypeSet, this.registry, [normalized, module]);
|
||||
return module;
|
||||
}
|
||||
|
||||
loadAndExecute(path) {
|
||||
let module = this.loadAndParse(path);
|
||||
module.declarationInstantiation();
|
||||
return module.evaluation();
|
||||
}
|
||||
|
||||
importRoot(path) {
|
||||
return this.loadAndExecute(path);
|
||||
}
|
||||
|
||||
["import"](name, referencingInfo) {
|
||||
let path = this.resolve(name, null);
|
||||
return this.loadAndExecute(path);
|
||||
}
|
||||
|
||||
populateImportMeta(moduleInfo, metaObject) {
|
||||
// For the shell, use the module's normalized path as the base URL.
|
||||
|
||||
let path;
|
||||
if (moduleInfo) {
|
||||
path = moduleInfo.path;
|
||||
} else {
|
||||
path = "(unknown)";
|
||||
}
|
||||
metaObject.url = path;
|
||||
}
|
||||
|
||||
dynamicImport(referencingInfo, specifier, promise) {
|
||||
ReflectApply(PromiseResolve, PromiseClass, [])
|
||||
.then(_ => {
|
||||
let path = ReflectLoader.resolve(specifier, referencingInfo);
|
||||
ReflectLoader.loadAndExecute(path);
|
||||
finishDynamicModuleImport(referencingInfo, specifier, promise);
|
||||
}).catch(err => {
|
||||
abortDynamicModuleImport(referencingInfo, specifier, promise, err);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
setModuleLoadHook((path) => ReflectLoader.importRoot(path));
|
||||
|
||||
setModuleResolveHook((referencingInfo, requestName) => {
|
||||
let path = ReflectLoader.resolve(requestName, referencingInfo);
|
||||
return ReflectLoader.loadAndParse(path);
|
||||
});
|
||||
|
||||
setModuleMetadataHook((module, metaObject) => {
|
||||
ReflectLoader.populateImportMeta(module, metaObject);
|
||||
});
|
||||
|
||||
setModuleDynamicImportHook((referencingInfo, specifier, promise) => {
|
||||
ReflectLoader.dynamicImport(referencingInfo, specifier, promise);
|
||||
});
|
||||
}
|
|
@ -35,6 +35,7 @@
|
|||
#include "js/PropertySpec.h"
|
||||
#include "js/Wrapper.h"
|
||||
#include "shell/jsshell.h"
|
||||
#include "shell/StringUtils.h"
|
||||
#include "util/StringBuffer.h"
|
||||
#include "util/Text.h"
|
||||
#include "util/Windows.h"
|
||||
|
@ -57,18 +58,8 @@ using js::shell::RCFile;
|
|||
namespace js {
|
||||
namespace shell {
|
||||
|
||||
#ifdef XP_WIN
|
||||
const char PathSeparator = '\\';
|
||||
#else
|
||||
const char PathSeparator = '/';
|
||||
#endif
|
||||
|
||||
static bool IsAbsolutePath(const UniqueChars& filename) {
|
||||
const char* pathname = filename.get();
|
||||
|
||||
if (pathname[0] == PathSeparator) {
|
||||
return true;
|
||||
}
|
||||
bool IsAbsolutePath(JSLinearString* filename) {
|
||||
size_t length = filename->length();
|
||||
|
||||
#ifdef XP_WIN
|
||||
// On Windows there are various forms of absolute paths (see
|
||||
|
@ -79,16 +70,16 @@ static bool IsAbsolutePath(const UniqueChars& filename) {
|
|||
// "\\..."
|
||||
// "C:\..."
|
||||
//
|
||||
// The first two cases are handled by the test above so we only need a test
|
||||
// for the last one here.
|
||||
// The first two cases are handled by the common test below so we only need a
|
||||
// specific test for the last one here.
|
||||
|
||||
if ((strlen(pathname) > 3 && mozilla::IsAsciiAlpha(pathname[0]) &&
|
||||
pathname[1] == ':' && pathname[2] == '\\')) {
|
||||
if (length > 3 && mozilla::IsAsciiAlpha(CharAt(filename, 0)) &&
|
||||
CharAt(filename, 1) == u':' && CharAt(filename, 2) == u'\\') {
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
return false;
|
||||
return length > 0 && CharAt(filename, 0) == PathSeparator;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -109,13 +100,18 @@ JSString* ResolvePath(JSContext* cx, HandleString filenameStr,
|
|||
#endif
|
||||
}
|
||||
|
||||
UniqueChars filename = JS_EncodeStringToLatin1(cx, filenameStr);
|
||||
if (!filename) {
|
||||
RootedLinearString str(cx, JS_EnsureLinearString(cx, filenameStr));
|
||||
if (!str) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (IsAbsolutePath(filename)) {
|
||||
return filenameStr;
|
||||
if (IsAbsolutePath(str)) {
|
||||
return str;
|
||||
}
|
||||
|
||||
UniqueChars filename = JS_EncodeStringToLatin1(cx, str);
|
||||
if (!filename) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
JS::AutoFilename scriptFilename;
|
||||
|
@ -757,12 +753,12 @@ static bool ospath_isAbsolute(JSContext* cx, unsigned argc, Value* vp) {
|
|||
return false;
|
||||
}
|
||||
|
||||
UniqueChars path = JS_EncodeStringToLatin1(cx, args[0].toString());
|
||||
if (!path) {
|
||||
RootedLinearString str(cx, JS_EnsureLinearString(cx, args[0].toString()));
|
||||
if (!str) {
|
||||
return false;
|
||||
}
|
||||
|
||||
args.rval().setBoolean(IsAbsolutePath(path));
|
||||
args.rval().setBoolean(IsAbsolutePath(str));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -786,14 +782,19 @@ static bool ospath_join(JSContext* cx, unsigned argc, Value* vp) {
|
|||
return false;
|
||||
}
|
||||
|
||||
UniqueChars path = JS_EncodeStringToLatin1(cx, args[i].toString());
|
||||
if (!path) {
|
||||
RootedLinearString str(cx, JS_EnsureLinearString(cx, args[i].toString()));
|
||||
if (!str) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (IsAbsolutePath(path)) {
|
||||
if (IsAbsolutePath(str)) {
|
||||
MOZ_ALWAYS_TRUE(buffer.resize(0));
|
||||
} else if (i != 0) {
|
||||
UniqueChars path = JS_EncodeStringToLatin1(cx, str);
|
||||
if (!path) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!buffer.append(PathSeparator)) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -14,6 +14,12 @@
|
|||
namespace js {
|
||||
namespace shell {
|
||||
|
||||
#ifdef XP_WIN
|
||||
constexpr char PathSeparator = '\\';
|
||||
#else
|
||||
constexpr char PathSeparator = '/';
|
||||
#endif
|
||||
|
||||
struct RCFile;
|
||||
|
||||
/* Define an os object on the given global object. */
|
||||
|
@ -22,6 +28,8 @@ bool DefineOS(JSContext* cx, JS::HandleObject global, bool fuzzingSafe,
|
|||
|
||||
enum PathResolutionMode { RootRelative, ScriptRelative };
|
||||
|
||||
bool IsAbsolutePath(JSLinearString* filename);
|
||||
|
||||
JSString* ResolvePath(JSContext* cx, JS::HandleString filenameStr,
|
||||
PathResolutionMode resolveMode);
|
||||
|
||||
|
|
|
@ -0,0 +1,147 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* vim: set ts=8 sts=2 et sw=2 tw=80:
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/* String utility functions used by the module loader. */
|
||||
|
||||
#ifndef shell_StringUtils_h
|
||||
#define shell_StringUtils_h
|
||||
|
||||
#include "jsapi.h"
|
||||
|
||||
#include "js/StableStringChars.h"
|
||||
|
||||
namespace js {
|
||||
namespace shell {
|
||||
|
||||
inline char16_t CharAt(JSLinearString* str, size_t index) {
|
||||
return str->latin1OrTwoByteChar(index);
|
||||
}
|
||||
|
||||
inline JSLinearString* SubString(JSContext* cx, JSLinearString* str,
|
||||
size_t start, size_t end) {
|
||||
MOZ_ASSERT(start <= str->length());
|
||||
MOZ_ASSERT(end >= start && end <= str->length());
|
||||
return NewDependentString(cx, str, start, end - start);
|
||||
}
|
||||
|
||||
inline JSLinearString* SubString(JSContext* cx, JSLinearString* str,
|
||||
size_t start) {
|
||||
return SubString(cx, str, start, str->length());
|
||||
}
|
||||
|
||||
template <size_t NullTerminatedLength>
|
||||
bool StringStartsWith(JSLinearString* str,
|
||||
const char16_t (&chars)[NullTerminatedLength]) {
|
||||
MOZ_ASSERT(NullTerminatedLength > 0);
|
||||
const size_t length = NullTerminatedLength - 1;
|
||||
MOZ_ASSERT(chars[length] == '\0');
|
||||
|
||||
if (str->length() < length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
if (CharAt(str, i) != chars[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <size_t NullTerminatedLength>
|
||||
bool StringEquals(JSLinearString* str,
|
||||
const char16_t (&chars)[NullTerminatedLength]) {
|
||||
MOZ_ASSERT(NullTerminatedLength > 0);
|
||||
const size_t length = NullTerminatedLength - 1;
|
||||
MOZ_ASSERT(chars[length] == '\0');
|
||||
|
||||
return str->length() == length && StringStartsWith(str, chars);
|
||||
}
|
||||
|
||||
inline int32_t IndexOf(HandleLinearString str, char16_t target,
|
||||
size_t start = 0) {
|
||||
int32_t length = str->length();
|
||||
for (int32_t i = start; i < length; i++) {
|
||||
if (CharAt(str, i) == target) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
inline int32_t LastIndexOf(HandleLinearString str, char16_t target) {
|
||||
int32_t length = str->length();
|
||||
for (int32_t i = length - 1; i >= 0; i--) {
|
||||
if (CharAt(str, i) == target) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
inline JSLinearString* ReplaceCharGlobally(JSContext* cx,
|
||||
HandleLinearString str,
|
||||
char16_t target,
|
||||
char16_t replacement) {
|
||||
int32_t i = IndexOf(str, target);
|
||||
if (i == -1) {
|
||||
return str;
|
||||
}
|
||||
|
||||
JS::AutoStableStringChars chars(cx);
|
||||
if (!chars.initTwoByte(cx, str)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Vector<char16_t> buf(cx);
|
||||
if (!buf.append(chars.twoByteChars(), str->length())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
for (; i < int32_t(buf.length()); i++) {
|
||||
if (buf[i] == target) {
|
||||
buf[i] = replacement;
|
||||
}
|
||||
}
|
||||
|
||||
RootedString result(cx, JS_NewUCStringCopyN(cx, buf.begin(), buf.length()));
|
||||
if (!result) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return JS_EnsureLinearString(cx, result);
|
||||
}
|
||||
|
||||
inline JSString* JoinStrings(JSContext* cx,
|
||||
Handle<GCVector<JSLinearString*>> strings,
|
||||
HandleLinearString separator) {
|
||||
RootedString result(cx, JS_GetEmptyString(cx));
|
||||
|
||||
for (size_t i = 0; i < strings.length(); i++) {
|
||||
HandleString str = strings[i];
|
||||
if (i != 0) {
|
||||
result = JS_ConcatStrings(cx, result, separator);
|
||||
if (!result) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
result = JS_ConcatStrings(cx, result, str);
|
||||
if (!result) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace shell
|
||||
} // namespace js
|
||||
|
||||
#endif // shell_StringUtils_h
|
|
@ -64,7 +64,6 @@
|
|||
# include "prerror.h"
|
||||
# include "prlink.h"
|
||||
#endif
|
||||
#include "shellmoduleloader.out.h"
|
||||
|
||||
#include "builtin/Array.h"
|
||||
#include "builtin/MapObject.h"
|
||||
|
@ -193,17 +192,6 @@ enum JSShellExitCode {
|
|||
EXITCODE_TIMEOUT = 6
|
||||
};
|
||||
|
||||
// Define use of application-specific slots on the shell's global object.
|
||||
enum GlobalAppSlot {
|
||||
GlobalAppSlotModuleLoadHook, // Shell-specific; load a module graph
|
||||
GlobalAppSlotModuleResolveHook, // HostResolveImportedModule
|
||||
GlobalAppSlotModuleMetadataHook, // HostPopulateImportMeta
|
||||
GlobalAppSlotModuleDynamicImportHook, // HostImportModuleDynamically
|
||||
GlobalAppSlotCount
|
||||
};
|
||||
static_assert(GlobalAppSlotCount <= JSCLASS_GLOBAL_APPLICATION_SLOTS,
|
||||
"Too many applications slots defined for shell global");
|
||||
|
||||
/*
|
||||
* Note: This limit should match the stack limit set by the browser in
|
||||
* js/xpconnect/src/XPCJSContext.cpp
|
||||
|
@ -861,6 +849,22 @@ void EnvironmentPreparer::invoke(HandleObject global, Closure& closure) {
|
|||
}
|
||||
}
|
||||
|
||||
JSObject* js::shell::CreateScriptPrivate(JSContext* cx, HandleString path) {
|
||||
RootedObject info(cx, JS_NewPlainObject(cx));
|
||||
if (!info) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (path) {
|
||||
RootedValue pathValue(cx, StringValue(path));
|
||||
if (!JS_DefineProperty(cx, info, "path", pathValue, JSPROP_ENUMERATE)) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
static bool RegisterScriptPathWithModuleLoader(JSContext* cx,
|
||||
HandleScript script,
|
||||
const char* filename) {
|
||||
|
@ -873,20 +877,9 @@ static bool RegisterScriptPathWithModuleLoader(JSContext* cx,
|
|||
return false;
|
||||
}
|
||||
|
||||
RootedObject infoObject(cx);
|
||||
|
||||
Value val = JS::GetScriptPrivate(script);
|
||||
if (val.isUndefined()) {
|
||||
infoObject = JS_NewPlainObject(cx);
|
||||
if (!infoObject) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
infoObject = val.toObjectOrNull();
|
||||
}
|
||||
|
||||
RootedValue pathValue(cx, StringValue(path));
|
||||
if (!JS_DefineProperty(cx, infoObject, "path", pathValue, 0)) {
|
||||
MOZ_ASSERT(JS::GetScriptPrivate(script).isUndefined());
|
||||
RootedObject infoObject(cx, CreateScriptPrivate(cx, path));
|
||||
if (!infoObject) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1002,63 +995,9 @@ static MOZ_MUST_USE bool RunBinAST(JSContext* cx, const char* filename,
|
|||
|
||||
#endif // JS_BUILD_BINAST
|
||||
|
||||
static bool InitModuleLoader(JSContext* cx) {
|
||||
// Decompress and evaluate the embedded module loader source to initialize
|
||||
// the module loader for the current compartment.
|
||||
|
||||
uint32_t srcLen = moduleloader::GetRawScriptsSize();
|
||||
auto src = cx->make_pod_array<char>(srcLen);
|
||||
if (!src ||
|
||||
!DecompressString(moduleloader::compressedSources,
|
||||
moduleloader::GetCompressedSize(),
|
||||
reinterpret_cast<unsigned char*>(src.get()), srcLen)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CompileOptions options(cx);
|
||||
options.setIntroductionType("shell module loader");
|
||||
options.setFileAndLine("shell/ModuleLoader.js", 1);
|
||||
options.setSelfHostingMode(false);
|
||||
options.setForceFullParse();
|
||||
options.setForceStrictMode();
|
||||
|
||||
JS::SourceText<Utf8Unit> srcBuf;
|
||||
if (!srcBuf.init(cx, std::move(src), srcLen)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RootedValue rv(cx);
|
||||
return JS::Evaluate(cx, options, srcBuf, &rv);
|
||||
}
|
||||
|
||||
static bool GetModuleImportHook(JSContext* cx,
|
||||
MutableHandleFunction resultOut) {
|
||||
Handle<GlobalObject*> global = cx->global();
|
||||
RootedValue hookValue(cx,
|
||||
global->getReservedSlot(GlobalAppSlotModuleLoadHook));
|
||||
if (hookValue.isUndefined()) {
|
||||
JS_ReportErrorASCII(cx, "Module load hook not set");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!hookValue.isObject() || !hookValue.toObject().is<JSFunction>()) {
|
||||
JS_ReportErrorASCII(cx, "Module load hook is not a function");
|
||||
return false;
|
||||
}
|
||||
|
||||
resultOut.set(&hookValue.toObject().as<JSFunction>());
|
||||
return true;
|
||||
}
|
||||
|
||||
static MOZ_MUST_USE bool RunModule(JSContext* cx, const char* filename,
|
||||
FILE* file, bool compileOnly) {
|
||||
// Execute a module by calling the module loader's import hook on the
|
||||
// resolved filename.
|
||||
|
||||
RootedFunction importFun(cx);
|
||||
if (!GetModuleImportHook(cx, &importFun)) {
|
||||
return false;
|
||||
}
|
||||
bool compileOnly) {
|
||||
ShellContext* sc = GetShellContext(cx);
|
||||
|
||||
RootedString path(cx, JS_NewStringCopyZ(cx, filename));
|
||||
if (!path) {
|
||||
|
@ -1070,11 +1009,7 @@ static MOZ_MUST_USE bool RunModule(JSContext* cx, const char* filename,
|
|||
return false;
|
||||
}
|
||||
|
||||
JS::RootedValueArray<1> args(cx);
|
||||
args[0].setString(path);
|
||||
|
||||
RootedValue value(cx);
|
||||
return JS_CallFunction(cx, nullptr, importFun, args, &value);
|
||||
return sc->moduleLoader->loadRootModule(cx, path);
|
||||
}
|
||||
|
||||
static void ShellCleanupFinalizationRegistryCallback(JSObject* registry,
|
||||
|
@ -1584,7 +1519,7 @@ static MOZ_MUST_USE bool Process(JSContext* cx, const char* filename,
|
|||
}
|
||||
break;
|
||||
case FileModule:
|
||||
if (!RunModule(cx, filename, file, compileOnly)) {
|
||||
if (!RunModule(cx, filename, compileOnly)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
@ -1931,7 +1866,7 @@ static bool ParseCompileOptions(JSContext* cx, CompileOptions& options,
|
|||
return false;
|
||||
}
|
||||
if (v.isObject()) {
|
||||
RootedObject infoObject(cx, JS_NewPlainObject(cx));
|
||||
RootedObject infoObject(cx, CreateScriptPrivate(cx));
|
||||
RootedValue elementValue(cx, v);
|
||||
if (!JS_WrapValue(cx, &elementValue)) {
|
||||
return false;
|
||||
|
@ -5122,44 +5057,6 @@ static bool DecodeModule(JSContext* cx, unsigned argc, Value* vp) {
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool SetModuleLoadHook(JSContext* cx, unsigned argc, Value* vp) {
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
if (!args.requireAtLeast(cx, "setModuleLoadHook", 1)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!args[0].isObject() || !args[0].toObject().is<JSFunction>()) {
|
||||
const char* typeName = InformalValueTypeName(args[0]);
|
||||
JS_ReportErrorASCII(cx, "expected hook function, got %s", typeName);
|
||||
return false;
|
||||
}
|
||||
|
||||
Handle<GlobalObject*> global = cx->global();
|
||||
global->setReservedSlot(GlobalAppSlotModuleLoadHook, args[0]);
|
||||
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool SetModuleResolveHook(JSContext* cx, unsigned argc, Value* vp) {
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
if (!args.requireAtLeast(cx, "setModuleResolveHook", 1)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!args[0].isObject() || !args[0].toObject().is<JSFunction>()) {
|
||||
const char* typeName = InformalValueTypeName(args[0]);
|
||||
JS_ReportErrorASCII(cx, "expected hook function, got %s", typeName);
|
||||
return false;
|
||||
}
|
||||
|
||||
Handle<GlobalObject*> global = cx->global();
|
||||
global->setReservedSlot(GlobalAppSlotModuleResolveHook, args[0]);
|
||||
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
static JSObject* ShellModuleResolveHook(JSContext* cx,
|
||||
HandleValue referencingPrivate,
|
||||
HandleString specifier) {
|
||||
|
@ -5189,9 +5086,9 @@ static JSObject* ShellModuleResolveHook(JSContext* cx,
|
|||
return &result.toObject();
|
||||
}
|
||||
|
||||
static bool SetModuleMetadataHook(JSContext* cx, unsigned argc, Value* vp) {
|
||||
static bool SetModuleResolveHook(JSContext* cx, unsigned argc, Value* vp) {
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
if (!args.requireAtLeast(cx, "setModuleMetadataHook", 1)) {
|
||||
if (!args.requireAtLeast(cx, "setModuleResolveHook", 1)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -5202,169 +5099,14 @@ static bool SetModuleMetadataHook(JSContext* cx, unsigned argc, Value* vp) {
|
|||
}
|
||||
|
||||
Handle<GlobalObject*> global = cx->global();
|
||||
global->setReservedSlot(GlobalAppSlotModuleMetadataHook, args[0]);
|
||||
global->setReservedSlot(GlobalAppSlotModuleResolveHook, args[0]);
|
||||
|
||||
JS::SetModuleResolveHook(cx->runtime(), ShellModuleResolveHook);
|
||||
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool CallModuleMetadataHook(JSContext* cx, HandleValue modulePrivate,
|
||||
HandleObject metaObject) {
|
||||
Handle<GlobalObject*> global = cx->global();
|
||||
RootedValue hookValue(
|
||||
cx, global->getReservedSlot(GlobalAppSlotModuleMetadataHook));
|
||||
if (hookValue.isUndefined()) {
|
||||
JS_ReportErrorASCII(cx, "Module metadata hook not set");
|
||||
return false;
|
||||
}
|
||||
MOZ_ASSERT(hookValue.toObject().is<JSFunction>());
|
||||
|
||||
JS::RootedValueArray<2> args(cx);
|
||||
args[0].set(modulePrivate);
|
||||
args[1].setObject(*metaObject);
|
||||
|
||||
RootedValue dummy(cx);
|
||||
return JS_CallFunctionValue(cx, nullptr, hookValue, args, &dummy);
|
||||
}
|
||||
|
||||
static bool ReportArgumentTypeError(JSContext* cx, HandleValue value,
|
||||
const char* expected) {
|
||||
const char* typeName = InformalValueTypeName(value);
|
||||
JS_ReportErrorASCII(cx, "Expected %s, got %s", expected, typeName);
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool ShellSetModulePrivate(JSContext* cx, unsigned argc, Value* vp) {
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
||||
if (!args.requireAtLeast(cx, "setModulePrivate", 2)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!args[0].isObject() || !args[0].toObject().is<ModuleObject>()) {
|
||||
return ReportArgumentTypeError(cx, args[0], "module object");
|
||||
}
|
||||
|
||||
JS::SetModulePrivate(&args[0].toObject(), args[1]);
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ShellGetModulePrivate(JSContext* cx, unsigned argc, Value* vp) {
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
||||
if (!args.requireAtLeast(cx, "getModulePrivate", 1)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!args[0].isObject() || !args[0].toObject().is<ModuleObject>()) {
|
||||
return ReportArgumentTypeError(cx, args[0], "module object");
|
||||
}
|
||||
|
||||
args.rval().set(JS::GetModulePrivate(&args[0].toObject()));
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool SetModuleDynamicImportHook(JSContext* cx, unsigned argc,
|
||||
Value* vp) {
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
if (!args.requireAtLeast(cx, "setModuleDynamicImportHook", 1)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!args[0].isObject() || !args[0].toObject().is<JSFunction>()) {
|
||||
const char* typeName = InformalValueTypeName(args[0]);
|
||||
JS_ReportErrorASCII(cx, "expected hook function, got %s", typeName);
|
||||
return false;
|
||||
}
|
||||
|
||||
Handle<GlobalObject*> global = cx->global();
|
||||
global->setReservedSlot(GlobalAppSlotModuleDynamicImportHook, args[0]);
|
||||
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool FinishDynamicModuleImport(JSContext* cx, unsigned argc, Value* vp) {
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
if (!args.requireAtLeast(cx, "finishDynamicModuleImport", 3)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!args[1].isString()) {
|
||||
return ReportArgumentTypeError(cx, args[1], "String");
|
||||
}
|
||||
|
||||
if (!args[2].isObject() || !args[2].toObject().is<PromiseObject>()) {
|
||||
return ReportArgumentTypeError(cx, args[2], "PromiseObject");
|
||||
}
|
||||
|
||||
RootedString specifier(cx, args[1].toString());
|
||||
Rooted<PromiseObject*> promise(cx, &args[2].toObject().as<PromiseObject>());
|
||||
|
||||
return js::FinishDynamicModuleImport(cx, args[0], specifier, promise);
|
||||
}
|
||||
|
||||
static bool AbortDynamicModuleImport(JSContext* cx, unsigned argc, Value* vp) {
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
if (!args.requireAtLeast(cx, "abortDynamicModuleImport", 4)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!args[1].isString()) {
|
||||
return ReportArgumentTypeError(cx, args[1], "String");
|
||||
}
|
||||
|
||||
if (!args[2].isObject() || !args[2].toObject().is<PromiseObject>()) {
|
||||
return ReportArgumentTypeError(cx, args[2], "PromiseObject");
|
||||
}
|
||||
|
||||
RootedString specifier(cx, args[1].toString());
|
||||
Rooted<PromiseObject*> promise(cx, &args[2].toObject().as<PromiseObject>());
|
||||
|
||||
cx->setPendingExceptionAndCaptureStack(args[3]);
|
||||
return js::FinishDynamicModuleImport(cx, args[0], specifier, promise);
|
||||
}
|
||||
|
||||
static bool ShellModuleDynamicImportHook(JSContext* cx,
|
||||
HandleValue referencingPrivate,
|
||||
HandleString specifier,
|
||||
HandleObject promise) {
|
||||
Handle<GlobalObject*> global = cx->global();
|
||||
RootedValue hookValue(
|
||||
cx, global->getReservedSlot(GlobalAppSlotModuleDynamicImportHook));
|
||||
if (hookValue.isUndefined()) {
|
||||
JS_ReportErrorASCII(cx, "Module resolve hook not set");
|
||||
return false;
|
||||
}
|
||||
MOZ_ASSERT(hookValue.toObject().is<JSFunction>());
|
||||
|
||||
JS::RootedValueArray<3> args(cx);
|
||||
args[0].set(referencingPrivate);
|
||||
args[1].setString(specifier);
|
||||
args[2].setObject(*promise);
|
||||
|
||||
RootedValue result(cx);
|
||||
return JS_CallFunctionValue(cx, nullptr, hookValue, args, &result);
|
||||
}
|
||||
|
||||
static bool GetModuleLoadPath(JSContext* cx, unsigned argc, Value* vp) {
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
||||
ShellContext* sc = GetShellContext(cx);
|
||||
if (sc->moduleLoadPath) {
|
||||
JSString* str = JS_NewStringCopyZ(cx, sc->moduleLoadPath.get());
|
||||
if (!str) {
|
||||
return false;
|
||||
}
|
||||
|
||||
args.rval().setString(str);
|
||||
} else {
|
||||
args.rval().setNull();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#if defined(JS_BUILD_BINAST)
|
||||
|
||||
template <typename Tok>
|
||||
|
@ -8924,52 +8666,11 @@ static const JSFunctionSpecWithHelp shell_functions[] = {
|
|||
"decodeModule(code)",
|
||||
" Takes a XDR bytecode representation of an uninstantiated ModuleObject and returns a ModuleObject."),
|
||||
|
||||
JS_FN_HELP("setModuleLoadHook", SetModuleLoadHook, 1, 0,
|
||||
"setModuleLoadHook(function(path))",
|
||||
" Set the shell specific module load hook to |function|.\n"
|
||||
" This hook is used to load a module graph. It should be implemented by the\n"
|
||||
" module loader."),
|
||||
|
||||
JS_FN_HELP("setModuleResolveHook", SetModuleResolveHook, 1, 0,
|
||||
"setModuleResolveHook(function(referrer, specifier))",
|
||||
" Set the HostResolveImportedModule hook to |function|.\n"
|
||||
" This hook is used to look up a previously loaded module object. It should\n"
|
||||
" be implemented by the module loader."),
|
||||
|
||||
JS_FN_HELP("setModuleMetadataHook", SetModuleMetadataHook, 1, 0,
|
||||
"setModuleMetadataHook(function(module) {})",
|
||||
" Set the HostPopulateImportMeta hook to |function|.\n"
|
||||
" This hook is used to create the metadata object returned by import.meta for\n"
|
||||
" a module. It should be implemented by the module loader."),
|
||||
|
||||
JS_FN_HELP("setModuleDynamicImportHook", SetModuleDynamicImportHook, 1, 0,
|
||||
"setModuleDynamicImportHook(function(referrer, specifier, promise))",
|
||||
" Set the HostImportModuleDynamically hook to |function|.\n"
|
||||
" This hook is used to dynamically import a module. It should\n"
|
||||
" be implemented by the module loader."),
|
||||
|
||||
JS_FN_HELP("finishDynamicModuleImport", FinishDynamicModuleImport, 3, 0,
|
||||
"finishDynamicModuleImport(referrer, specifier, promise)",
|
||||
" The module loader's dynamic import hook should call this when the module has"
|
||||
" been loaded successfully."),
|
||||
|
||||
JS_FN_HELP("abortDynamicModuleImport", AbortDynamicModuleImport, 4, 0,
|
||||
"abortDynamicModuleImport(referrer, specifier, promise, error)",
|
||||
" The module loader's dynamic import hook should call this when the module "
|
||||
" import has failed."),
|
||||
|
||||
JS_FN_HELP("setModulePrivate", ShellSetModulePrivate, 2, 0,
|
||||
"setModulePrivate(scriptObject, privateValue)",
|
||||
" Associate a private value with a module object.\n"),
|
||||
|
||||
JS_FN_HELP("getModulePrivate", ShellGetModulePrivate, 2, 0,
|
||||
"getModulePrivate(scriptObject)",
|
||||
" Get the private value associated with a module object.\n"),
|
||||
|
||||
JS_FN_HELP("getModuleLoadPath", GetModuleLoadPath, 0, 0,
|
||||
"getModuleLoadPath()",
|
||||
" Return any --module-load-path argument passed to the shell. Used by the\n"
|
||||
" module loader.\n"),
|
||||
" Set the HostResolveImportedModule hook to |function|, overriding the shell\n"
|
||||
" module loader for testing purposes. This hook is used to look up a\n"
|
||||
" previously loaded module object."),
|
||||
|
||||
#if defined(JS_BUILD_BINAST)
|
||||
|
||||
|
@ -10367,27 +10068,25 @@ static MOZ_MUST_USE bool ProcessArgs(JSContext* cx, OptionParser* op) {
|
|||
return Process(cx, nullptr, forceTTY, FileScript);
|
||||
}
|
||||
|
||||
if (const char* path = op->getStringOption("module-load-path")) {
|
||||
RootedString jspath(cx, JS_NewStringCopyZ(cx, path));
|
||||
RootedString moduleLoadPath(cx);
|
||||
if (const char* option = op->getStringOption("module-load-path")) {
|
||||
RootedString jspath(cx, JS_NewStringCopyZ(cx, option));
|
||||
if (!jspath) {
|
||||
return false;
|
||||
}
|
||||
|
||||
JSString* absolutePath = js::shell::ResolvePath(cx, jspath, RootRelative);
|
||||
if (!absolutePath) {
|
||||
return false;
|
||||
}
|
||||
|
||||
sc->moduleLoadPath = JS_EncodeStringToLatin1(cx, absolutePath);
|
||||
moduleLoadPath = js::shell::ResolvePath(cx, jspath, RootRelative);
|
||||
} else {
|
||||
sc->moduleLoadPath = js::shell::GetCWD();
|
||||
UniqueChars cwd = js::shell::GetCWD();
|
||||
moduleLoadPath = JS_NewStringCopyZ(cx, cwd.get());
|
||||
}
|
||||
|
||||
if (!sc->moduleLoadPath) {
|
||||
if (!moduleLoadPath) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!InitModuleLoader(cx)) {
|
||||
sc->moduleLoader = js::MakeUnique<ModuleLoader>();
|
||||
if (!sc->moduleLoader || !sc->moduleLoader->init(cx, moduleLoadPath)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -11866,10 +11565,6 @@ int main(int argc, char** argv, char** envp) {
|
|||
|
||||
js::SetPreserveWrapperCallback(cx, DummyPreserveWrapperCallback);
|
||||
|
||||
JS::SetModuleResolveHook(cx->runtime(), ShellModuleResolveHook);
|
||||
JS::SetModuleDynamicImportHook(cx->runtime(), ShellModuleDynamicImportHook);
|
||||
JS::SetModuleMetadataHook(cx->runtime(), CallModuleMetadataHook);
|
||||
|
||||
if (op.getBoolOption("disable-wasm-huge-memory")) {
|
||||
if (!sCompilerProcessFlags.append("--disable-wasm-huge-memory")) {
|
||||
return EXIT_FAILURE;
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
#include "builtin/MapObject.h"
|
||||
#include "js/GCVector.h"
|
||||
#include "shell/ModuleLoader.h"
|
||||
#include "threading/ConditionVariable.h"
|
||||
#include "threading/LockGuard.h"
|
||||
#include "threading/Mutex.h"
|
||||
|
@ -32,6 +33,15 @@
|
|||
namespace js {
|
||||
namespace shell {
|
||||
|
||||
// Define use of application-specific slots on the shell's global object.
|
||||
enum GlobalAppSlot {
|
||||
GlobalAppSlotModuleRegistry,
|
||||
GlobalAppSlotModuleResolveHook, // HostResolveImportedModule
|
||||
GlobalAppSlotCount
|
||||
};
|
||||
static_assert(GlobalAppSlotCount <= JSCLASS_GLOBAL_APPLICATION_SLOTS,
|
||||
"Too many applications slots defined for shell global");
|
||||
|
||||
enum JSShellErrNum {
|
||||
#define MSG_DEF(name, count, exception, format) name,
|
||||
#include "jsshell.msg"
|
||||
|
@ -232,7 +242,7 @@ struct ShellContext {
|
|||
|
||||
UniquePtr<ProfilingStack> geckoProfilingStack;
|
||||
|
||||
JS::UniqueChars moduleLoadPath;
|
||||
UniquePtr<ModuleLoader> moduleLoader;
|
||||
|
||||
UniquePtr<MarkBitObservers> markObservers;
|
||||
|
||||
|
@ -250,6 +260,9 @@ extern ShellContext* GetShellContext(JSContext* cx);
|
|||
extern MOZ_MUST_USE bool PrintStackTrace(JSContext* cx,
|
||||
JS::Handle<JSObject*> stackObj);
|
||||
|
||||
extern JSObject* CreateScriptPrivate(JSContext* cx,
|
||||
HandleString path = nullptr);
|
||||
|
||||
} /* namespace shell */
|
||||
} /* namespace js */
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ UNIFIED_SOURCES += [
|
|||
'js.cpp',
|
||||
'jsoptparse.cpp',
|
||||
'jsshell.cpp',
|
||||
'ModuleLoader.cpp',
|
||||
'OSObject.cpp',
|
||||
'WasmTesting.cpp'
|
||||
]
|
||||
|
@ -38,15 +39,6 @@ LOCAL_INCLUDES += [
|
|||
|
||||
OS_LIBS += CONFIG['EDITLINE_LIBS']
|
||||
|
||||
# Prepare module loader JS code for embedding
|
||||
GeneratedFile('shellmoduleloader.out.h', 'shellmoduleloader.js',
|
||||
script='../builtin/embedjs.py',
|
||||
entry_point='generate_shellmoduleloader',
|
||||
inputs=[
|
||||
'../js.msg',
|
||||
'ModuleLoader.js',
|
||||
])
|
||||
|
||||
# Place a GDB Python auto-load file next to the shell executable, both in
|
||||
# the build directory and in the dist/bin directory.
|
||||
DEFINES['topsrcdir'] = '%s/js/src' % TOPSRCDIR
|
||||
|
|
Загрузка…
Ссылка в новой задаче