Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  ModuleLoader.cpp   Sprache: C

 

/* 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/TextUtils.h"

#include "jsapi.h"
#include "NamespaceImports.h"

#include "builtin/TestingUtility.h"  // js::CreateScriptPrivate
#include "js/Conversions.h"
#include "js/MapAndSet.h"
#include "js/Modules.h"
#include "js/Prefs.h"
#include "js/PropertyAndElement.h"  // JS_DefineProperty, JS_GetProperty
#include "js/SourceText.h"
#include "js/StableStringChars.h"
#include "shell/jsshell.h"
#include "shell/OSObject.h"
#include "shell/StringUtils.h"
#include "util/Text.h"
#include "vm/JSAtomUtils.h"  // AtomizeString, PinAtom
#include "vm/JSContext.h"
#include "vm/StringType.h"

#include "vm/NativeObject-inl.h"

using namespace js;
using namespace js::shell;

static constexpr char16_t JavaScriptScheme[] = u"javascript:";

static bool IsJavaScriptURL(Handle<JSLinearString*> path) {
  return StringStartsWith(path, JavaScriptScheme);
}

static JSString* ExtractJavaScriptURLSource(JSContext* cx,
                                            Handle<JSLinearString*> path) {
  MOZ_ASSERT(IsJavaScriptURL(path));

  const size_t schemeLength = js_strlen(JavaScriptScheme);
  return SubString(cx, path, schemeLength);
}

bool ModuleLoader::init(JSContext* cx, HandleString loadPath) {
  loadPathStr = AtomizeString(cx, loadPath);
  if (!loadPathStr || !PinAtom(cx, loadPathStr)) {
    return false;
  }

  MOZ_ASSERT(IsAbsolutePath(loadPathStr));

  char16_t sep = PathSeparator;
  pathSeparatorStr = AtomizeChars(cx, &sep, 1);
  if (!pathSeparatorStr || !PinAtom(cx, pathSeparatorStr)) {
    return false;
  }

  JSRuntime* rt = cx->runtime();
  JS::SetModuleLoadHook(rt, ModuleLoader::LoadImportedModule);
  JS::SetModuleMetadataHook(rt, ModuleLoader::GetImportMetaProperties);
  return true;
}

// static
bool ModuleLoader::LoadImportedModule(JSContext* cx,
                                      JS::Handle<JSScript*> referrer,
                                      JS::Handle<JSObject*> moduleRequest,
                                      JS::HandleValue hostDefined,
                                      JS::HandleValue payload,
                                      uint32_t lineNumber,
                                      JS::ColumnNumberOneOrigin columnNumber) {
  ShellContext* scx = GetShellContext(cx);
  return scx->moduleLoader->loadImportedModule(cx, referrer, moduleRequest,
                                               payload);
}

// static
bool ModuleLoader::GetImportMetaProperties(JSContext* cx,
                                           JS::HandleObject moduleRecord,
                                           JS::HandleObject metaObject) {
  ShellContext* scx = GetShellContext(cx);
  return scx->moduleLoader->populateImportMeta(cx, moduleRecord, metaObject);
}

bool ModuleLoader::ImportMetaResolve(JSContext* cx, unsigned argc, Value* vp) {
  CallArgs args = CallArgsFromVp(argc, vp);
  RootedValue modulePrivate(
      cx, js::GetFunctionNativeReserved(&args.callee(), ModulePrivateSlot));

  // https://html.spec.whatwg.org/#hostgetimportmetaproperties
  // Step 4.1. Set specifier to ? ToString(specifier).
  //
  // https://tc39.es/ecma262/#sec-tostring
  RootedValue v(cx, args.get(ImportMetaResolveSpecifierArg));
  RootedString specifier(cx, JS::ToString(cx, v));
  if (!specifier) {
    return false;
  }

  // Step 4.2, 4.3 are implemented in importMetaResolve.
  ShellContext* scx = GetShellContext(cx);
  RootedString url(cx);
  if (!scx->moduleLoader->importMetaResolve(cx, modulePrivate, specifier,
                                            &url)) {
    return false;
  }

  // Step 4.4. Return the serialization of url.
  args.rval().setString(url);
  return true;
}

bool ModuleLoader::loadRootModule(JSContext* cx, HandleString path) {
  Rooted<JSAtom*> specifier(cx, AtomizeString(cx, path));
  if (!specifier) {
    return false;
  }
  RootedObject moduleRequest(
      cx, ModuleRequestObject::create(cx, specifier,
                                      JS::ModuleType::JavaScriptOrWasm,
                                      ImportPhase::Evaluation));
  if (!moduleRequest) {
    return false;
  }

  RootedValue rval(cx);
  if (!loadAndExecute(cx, path, moduleRequest, &rval)) {
    return false;
  }

  RootedObject evaluationPromise(cx, &rval.toObject());
  if (evaluationPromise == nullptr) {
    return false;
  }

  return JS::ThrowOnModuleEvaluationFailure(cx, evaluationPromise);
}

bool ModuleLoader::registerTestModule(JSContext* cx, HandleObject moduleRequest,
                                      Handle<ModuleObject*> module) {
  Rooted<JSLinearString*> path(cx, resolve(cx, moduleRequest, nullptr));
  if (!path) {
    return false;
  }

  path = normalizePath(cx, path);
  if (!path) {
    return false;
  }

  JS::ModuleType moduleType =
      moduleRequest->as<ModuleRequestObject>().moduleType();

  return addModuleToRegistry(cx, moduleType, path, module);
}

void ModuleLoader::clearModules(JSContext* cx) {
  Handle<GlobalObject*> global = cx->global();
  global->setReservedSlot(GlobalAppSlotModuleRegistry, UndefinedValue());
}

bool ModuleLoader::loadAndExecute(JSContext* cx, HandleString path,
                                  HandleObject moduleRequestArg,
                                  MutableHandleValue rval) {
  RootedObject module(cx, loadAndParse(cx, path, moduleRequestArg));
  if (!module) {
    return false;
  }

  return loadAndExecute(cx, module, rval);
}

bool ModuleLoader::loadAndExecute(JSContext* cx, HandleObject module,
                                  MutableHandleValue rval) {
  MOZ_ASSERT(module);

  RootedValue hostDefined(cx, ObjectValue(*module));
  if (!JS::LoadRequestedModules(cx, module, hostDefined, LoadResolved,
                                LoadRejected)) {
    return false;
  }

  if (JS_IsExceptionPending(cx)) {
    return false;
  }

  return JS::ModuleEvaluate(cx, module, rval);
}

/* static */
bool ModuleLoader::LoadResolved(JSContext* cx, HandleValue hostDefined) {
  RootedObject module(cx, &hostDefined.toObject());
  return JS::ModuleLink(cx, module);
}

/* static */
bool ModuleLoader::LoadRejected(JSContext* cx, HandleValue hostDefined,
                                HandleValue error) {
  JS_SetPendingException(cx, error);
  return true;
}

// See https://github.com/tc39/test262/blob/main/INTERPRETING.md#modules
JSObject* ModuleLoader::getOrCreateTest262ModuleSourceModule(JSContext* cx) {
  RootedString key(cx, JS_NewStringCopyZ(cx, "<module source>"));
  if (!key) {
    return nullptr;
  }

  RootedObject module(cx);
  if (!lookupModuleInRegistry(cx, JS::ModuleType::JavaScriptOrWasm, key,
                              &module)) {
    return nullptr;
  }
  if (module) {
    return module;
  }

  // Empty module: The string \0xasm, followed by version number 1.
  // https://webassembly.github.io/spec/core/binary/modules.html#binary-module
  static const uint8_t emptyWasmModule[] = {0x00, 0x61, 0x73, 0x6d,
                                            0x01, 0x00, 0x00, 0x00};
  js::Vector<uint8_t, 0, js::MallocAllocPolicy> srcBuf;
  if (!srcBuf.append(emptyWasmModule, sizeof(emptyWasmModule))) {
    return nullptr;
  }

  JS::CompileOptions options(cx);
  options.setFileAndLine("<module source>", 1);
  module = JS::CompileWasmModuleAsSource(cx, options, srcBuf);
  if (!module) {
    return nullptr;
  }

  if (!addModuleToRegistry(cx, JS::ModuleType::JavaScriptOrWasm, key, module)) {
    return nullptr;
  }
  return module;
}

bool ModuleLoader::loadImportedModule(JSContext* cx,
                                      JS::Handle<JSScript*> referrer,
                                      JS::Handle<JSObject*> moduleRequest,
                                      JS::HandleValue payload) {
  // TODO: Bug 1968904: Update HostLoadImportedModule
  if (payload.isObject() && payload.toObject().is<PromiseObject>()) {
    // This is a dynamic import.
    return dynamicImport(cx, referrer, moduleRequest, payload);
  }

  if (JS::Prefs::experimental_source_phase_imports_test262_module_source()) {
    js::ImportPhase phase = moduleRequest->as<ModuleRequestObject>().phase();
    JSAtom* specifier = moduleRequest->as<ModuleRequestObject>().specifier();
    if (phase == ImportPhase::Source &&
        StringEquals(specifier, u"<module source>")) {
      RootedObject module(cx, getOrCreateTest262ModuleSourceModule(cx));
      if (!module) {
        return false;
      }
      return JS::FinishLoadingImportedModule(cx, referrer, moduleRequest,
                                             payload, module, false);
    }
  }

  Rooted<JSLinearString*> path(cx, resolve(cx, moduleRequest, referrer));
  if (!path) {
    return false;
  }

  RootedObject module(cx, loadAndParse(cx, path, moduleRequest));
  if (!module) {
    return false;
  }

  return JS::FinishLoadingImportedModule(cx, referrer, moduleRequest, payload,
                                         module, false);
}

bool ModuleLoader::populateImportMeta(JSContext* cx,
                                      JS::HandleObject moduleRecord,
                                      JS::HandleObject metaObject) {
  Rooted<JSLinearString*> path(cx);
  Rooted<JS::Value> modulePrivate(cx, JS::GetModulePrivate(moduleRecord));
  if (!modulePrivate.isUndefined()) {
    if (!getScriptPath(cx, modulePrivate, &path)) {
      return false;
    }
  }

  if (!path) {
    path = NewStringCopyZ<CanGC>(cx, "(unknown)");
    if (!path) {
      return false;
    }
  }

  RootedValue pathValue(cx, StringValue(path));
  if (!JS_DefineProperty(cx, metaObject, "url", pathValue, JSPROP_ENUMERATE)) {
    return false;
  }

  JSFunction* resolveFunc = js::DefineFunctionWithReserved(
      cx, metaObject, "resolve", ImportMetaResolve, ImportMetaResolveNumArgs,
      JSPROP_ENUMERATE);
  if (!resolveFunc) {
    return false;
  }

  RootedObject resolveFuncObj(cx, JS_GetFunctionObject(resolveFunc));
  js::SetFunctionNativeReserved(resolveFuncObj, ModulePrivateSlot,
                                modulePrivate);

  return true;
}

bool ModuleLoader::importMetaResolve(JSContext* cx,
                                     JS::Handle<JS::Value> referencingPrivate,
                                     JS::Handle<JSString*> specifier,
                                     JS::MutableHandle<JSString*> urlOut) {
  Rooted<JSLinearString*> path(cx, resolve(cx, specifier, referencingPrivate));
  if (!path) {
    return false;
  }

  urlOut.set(path);
  return true;
}

bool ModuleLoader::dynamicImport(JSContext* cx, JS::HandleScript referrer,
                                 JS::HandleObject moduleRequest,
                                 JS::HandleValue payload) {
  // 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.

  RootedValue moduleRequestValue(cx, ObjectValue(*moduleRequest));
  RootedObject closure(cx, JS_NewObjectWithGivenProto(cx, nullptr, nullptr));
  RootedValue referrerValue(cx);
  if (referrer) {
    referrerValue = PrivateGCThingValue(referrer);
  } else {
    RootedScript script(cx);
    const char* filename;
    uint32_t lineno;
    uint32_t pcOffset;
    bool mutedErrors;
    DescribeScriptedCallerForCompilation(cx, &script, &filename, &lineno,
                                         &pcOffset, &mutedErrors);
    MOZ_ASSERT(script);
    referrerValue = PrivateGCThingValue(script);
  }
  MOZ_ASSERT(!referrerValue.isUndefined());

  if (!closure ||
      !JS_DefineProperty(cx, closure, "referrer", referrerValue,
                         JSPROP_ENUMERATE) ||
      !JS_DefineProperty(cx, closure, "moduleRequest", moduleRequestValue,
                         JSPROP_ENUMERATE) ||
      !JS_DefineProperty(cx, closure, "payload", payload, 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 referrerValue(cx);
  RootedValue moduleRequestValue(cx);
  RootedValue payload(cx);
  if (!JS_GetProperty(cx, closure, "referrer", &referrerValue) ||
      !JS_GetProperty(cx, closure, "moduleRequest", &moduleRequestValue) ||
      !JS_GetProperty(cx, closure, "payload", &payload)) {
    return false;
  }

  RootedObject moduleRequest(cx, &moduleRequestValue.toObject());
  RootedScript referrer(cx, static_cast<JSScript*>(referrerValue.toGCThing()));

  ShellContext* scx = GetShellContext(cx);
  return scx->moduleLoader->doDynamicImport(cx, referrer, moduleRequest,
                                            payload);
}

bool ModuleLoader::DynamicImportDelayRejected(JSContext* cx, unsigned argc,
                                              Value* vp) {
  MOZ_CRASH("This promise should never be rejected");
}

bool ModuleLoader::doDynamicImport(JSContext* cx, JS::HandleScript referrer,
                                   JS::HandleObject moduleRequest,
                                   JS::HandleValue payload) {
  // Exceptions during dynamic import are handled by calling
  // FinishLoadingImportedModule with a pending exception on the context.
  js::ImportPhase phase = moduleRequest->as<ModuleRequestObject>().phase();
  if (JS::Prefs::experimental_source_phase_imports() &&
      phase == ImportPhase::Source) {
    if (JS::Prefs::experimental_source_phase_imports_test262_module_source()) {
      JSAtom* specifier = moduleRequest->as<ModuleRequestObject>().specifier();
      if (StringEquals(specifier, u"<module source>")) {
        RootedObject module(cx, getOrCreateTest262ModuleSourceModule(cx));
        if (!module) {
          return JS::FinishLoadingImportedModuleFailedWithPendingException(
              cx, payload);
        }
        return JS::FinishLoadingImportedModule(cx, nullptr, moduleRequest,
                                               payload, module, false);
      }
    }
  }

  Rooted<JSLinearString*> path(cx, resolve(cx, moduleRequest, referrer));
  if (!path) {
    return JS::FinishLoadingImportedModuleFailedWithPendingException(cx,
                                                                     payload);
  }

  RootedObject module(cx, loadAndParse(cx, path, moduleRequest));
  if (!module) {
    return JS::FinishLoadingImportedModuleFailedWithPendingException(cx,
                                                                     payload);
  }

  if (phase != ImportPhase::Source) {
    RootedValue hostDefined(cx, ObjectValue(*module));
    if (!JS::LoadRequestedModules(cx, module, hostDefined, LoadResolved,
                                  LoadRejected)) {
      return JS::FinishLoadingImportedModuleFailedWithPendingException(cx,
                                                                       payload);
    }
  }

  if (JS_IsExceptionPending(cx)) {
    return JS::FinishLoadingImportedModuleFailedWithPendingException(cx,
                                                                     payload);
  }

  return JS::FinishLoadingImportedModule(cx, nullptr, moduleRequest, payload,
                                         module, false);
}

JSLinearString* ModuleLoader::resolve(JSContext* cx,
                                      HandleObject moduleRequestArg,
                                      HandleScript referrer) {
  RootedValue referencingInfo(cx);
  if (referrer) {
    referencingInfo = GetScriptPrivate(referrer);
  }

  ModuleRequestObject* moduleRequest =
      &moduleRequestArg->as<ModuleRequestObject>();
  if (moduleRequest->specifier()->length() == 0) {
    JS_ReportErrorASCII(cx, "Invalid module specifier");
    return nullptr;
  }

  Rooted<JSLinearString*> name(
      cx, JS_EnsureLinearString(cx, moduleRequest->specifier()));
  if (!name) {
    return nullptr;
  }

  return resolve(cx, name, referencingInfo);
}

JSLinearString* ModuleLoader::resolve(JSContext* cx, HandleString specifier,
                                      HandleValue referencingInfo) {
  Rooted<JSLinearString*> name(cx, JS_EnsureLinearString(cx, specifier));
  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;
    }

    Rooted<JSLinearString*> 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;
  }

  Rooted<JSLinearString*> linear(cx, JS_EnsureLinearString(cx, result));
  if (!linear) {
    return nullptr;
  }
  return normalizePath(cx, linear);
}

JSObject* ModuleLoader::loadAndParse(JSContext* cx, HandleString pathArg,
                                     JS::HandleObject moduleRequestArg) {
  Rooted<JSLinearString*> path(cx, JS_EnsureLinearString(cx, pathArg));
  if (!path) {
    return nullptr;
  }

  path = normalizePath(cx, path);
  if (!path) {
    return nullptr;
  }

  JS::ModuleType moduleType =
      moduleRequestArg->as<ModuleRequestObject>().moduleType();
  if (moduleType == JS::ModuleType::Unknown ||
      moduleType == JS::ModuleType::CSS) {
    // We don't support CSS modules in the shell because we don't have access
    // to a CSS parser in standalone shell builds.
    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                              JSMSG_BAD_MODULE_TYPE);
    return nullptr;
  }

  RootedObject module(cx);
  if (!lookupModuleInRegistry(cx, moduleType, path, &module)) {
    return nullptr;
  }

  if (module) {
    // TODO: Until we support evaluation phase imports of wasm modules, we need
    // to guard against first importing a wasm module as source, and then
    // subsequently as evaluation phase. The module will be retrieved from the
    // registry, and then we'll attempt to link it, which isn't currently
    // supported. See Bug 2030454.
    if (moduleRequestArg->as<ModuleRequestObject>().phase() ==
            ImportPhase::Evaluation &&
        module->as<ModuleObject>().moduleSource()) {
      JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                                JSMSG_WASM_ESM_EVAL_NOT_SUPPORTED);
      return nullptr;
    }
    return module;
  }

  UniqueChars filename = JS_EncodeStringToUTF8(cx, path);
  if (!filename) {
    return nullptr;
  }

  if (moduleType == JS::ModuleType::Bytes) {
    RootedString resolvedPath(cx, ResolvePath(cx, path, RootRelative));
    if (!resolvedPath) {
      return nullptr;
    }

    auto* typedArray = FileAsImmutableTypedArray(cx, resolvedPath);
    if (!typedArray) {
      return nullptr;
    }
    JS::Rooted<JS::Value> defaultExport(cx, ObjectValue(*typedArray));

    module = JS::CreateDefaultExportSyntheticModule(cx, defaultExport);
    if (!module) {
      return nullptr;
    }

    if (!addModuleToRegistry(cx, moduleType, path, module)) {
      return nullptr;
    }

    return module;
  }

  // Normally the mime type determines whether a module is wasm or not, but
  // this doesn't exist in the shell. Instead, we'll use the file extension.
#ifdef NIGHTLY_BUILD
  if (JS::Prefs::experimental_wasm_esm_integration() &&
      StringEndsWith(path, u".wasm")) {
    js::ImportPhase phase = moduleRequestArg->as<ModuleRequestObject>().phase();
    if (phase != ImportPhase::Source) {
      JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                                JSMSG_WASM_ESM_EVAL_NOT_SUPPORTED);
      return nullptr;
    }
    RootedString resolvedPath(cx, ResolvePath(cx, path, RootRelative));
    if (!resolvedPath) {
      return nullptr;
    }

    UniqueChars resolvedFilename = JS_EncodeStringToUTF8(cx, resolvedPath);
    if (!resolvedFilename) {
      return nullptr;
    }

    FILE* file = OpenFile(cx, resolvedFilename.get(), "rb");
    if (!file) {
      return nullptr;
    }

    size_t fileSize;
    if (!FileSize(cx, resolvedFilename.get(), file, &fileSize)) {
      fclose(file);
      return nullptr;
    }

    js::Vector<uint8_t, 0, js::MallocAllocPolicy> srcBuf;
    if (!srcBuf.growBy(fileSize)) {
      fclose(file);
      ReportOutOfMemory(cx);
      return nullptr;
    }

    if (!ReadFile(cx, resolvedFilename.get(), file,
                  reinterpret_cast<char*>(srcBuf.begin()), fileSize)) {
      fclose(file);
      return nullptr;
    }
    fclose(file);

    JS::CompileOptions options(cx);
    options.setFileAndLine(filename.get(), 1);
    module = JS::CompileWasmModuleAsSource(cx, options, srcBuf);
    if (!module) {
      return nullptr;
    }

    if (!addModuleToRegistry(cx, moduleType, path, module)) {
      return nullptr;
    }

    return module;
  }
#endif
  JS::CompileOptions options(cx);
  options.setFileAndLine(filename.get(), 1);

  RootedString source(cx, fetchSource(cx, path));
  if (!source) {
    return nullptr;
  }

  if (moduleType == JS::ModuleType::Text) {
    JS::RootedValue defaultExport(cx, JS::StringValue(source));
    module = JS::CreateDefaultExportSyntheticModule(cx, defaultExport);
    if (!module) {
      return nullptr;
    }

    if (!addModuleToRegistry(cx, moduleType, path, module)) {
      return nullptr;
    }

    return module;
  }

  JS::AutoStableStringChars linearChars(cx);
  if (!linearChars.initTwoByte(cx, source)) {
    return nullptr;
  }

  JS::SourceText<char16_t> srcBuf;
  if (!srcBuf.initMaybeBorrowed(cx, linearChars)) {
    return nullptr;
  }

  if (moduleType == JS::ModuleType::JavaScriptOrWasm) {
    module = JS::CompileModule(cx, options, srcBuf);
    if (!module) {
      return nullptr;
    }

    RootedObject info(cx, js::CreateScriptPrivate(cx, path));
    if (!info) {
      return nullptr;
    }

    JS::SetModulePrivate(module, ObjectValue(*info));
  } else {
    MOZ_ASSERT(moduleType == JS::ModuleType::JSON);
    module = JS::CompileJsonModule(cx, options, srcBuf);
    if (!module) {
      return nullptr;
    }
  }

  if (!addModuleToRegistry(cx, moduleType, path, module)) {
    return nullptr;
  }

  return module;
}

bool ModuleLoader::lookupModuleInRegistry(JSContext* cx,
                                          JS::ModuleType moduleType,
                                          HandleString path,
                                          MutableHandleObject moduleOut) {
  moduleOut.set(nullptr);

  RootedObject registry(cx, getOrCreateModuleRegistry(cx, moduleType));
  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, JS::ModuleType moduleType,
                                       HandleString path, HandleObject module) {
  RootedObject registry(cx, getOrCreateModuleRegistry(cx, moduleType));
  if (!registry) {
    return false;
  }

  RootedValue pathValue(cx, StringValue(path));
  RootedValue moduleValue(cx, ObjectValue(*module));
  return JS::MapSet(cx, registry, pathValue, moduleValue);
}

static ArrayObject* GetOrCreateRootRegistry(JSContext* cx) {
  Handle<GlobalObject*> global = cx->global();
  RootedValue value(cx, global->getReservedSlot(GlobalAppSlotModuleRegistry));
  if (!value.isUndefined()) {
    return &value.toObject().as<ArrayObject>();
  }

  uint32_t numberOfModuleTypes = uint32_t(JS::ModuleType::Limit) + 1;

  Rooted<ArrayObject*> registry(
      cx, NewDenseFullyAllocatedArray(cx, numberOfModuleTypes, TenuredObject));
  if (!registry) {
    return nullptr;
  }
  registry->ensureDenseInitializedLength(0, numberOfModuleTypes);

  Rooted<JSObject*> innerRegistry(cx);
  for (size_t i = 0; i < numberOfModuleTypes; ++i) {
    innerRegistry = JS::NewMapObject(cx);
    if (!innerRegistry) {
      return nullptr;
    }
    registry->initDenseElement(i, ObjectValue(*innerRegistry));
  }

  global->setReservedSlot(GlobalAppSlotModuleRegistry, ObjectValue(*registry));

  return registry;
}

JSObject* ModuleLoader::getOrCreateModuleRegistry(JSContext* cx,
                                                  JS::ModuleType moduleType) {
  Rooted<ArrayObject*> rootRegistry(cx, GetOrCreateRootRegistry(cx));
  if (!rootRegistry) {
    return nullptr;
  }

  uint32_t index = uint32_t(moduleType);
  MOZ_ASSERT(rootRegistry->containsDenseElement(index));
  return &rootRegistry->getDenseElement(index).toObject();
}

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,
                                            Handle<JSLinearString*> pathArg) {
  Rooted<JSLinearString*> 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.
  Rooted<JSLinearString*> 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, cx);
  size_t lastSep = 0;
  while (lastSep < path->length()) {
    int32_t i = IndexOf(path, PathSeparator, lastSep);
    if (i < 0) {
      i = path->length();
    }

    Rooted<JSLinearString*> 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;
    }
  }

  Rooted<JSLinearString*> 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,
                                    Handle<JSLinearString*> path) {
  if (IsJavaScriptURL(path)) {
    return ExtractJavaScriptURLSource(cx, path);
  }

  RootedString resolvedPath(cx, ResolvePath(cx, path, RootRelative));
  if (!resolvedPath) {
    return nullptr;
  }

  return FileAsString(cx, resolvedPath);
}

Messung V0.5 in Prozent
C=96 H=99 G=97

¤ Dauer der Verarbeitung: 0.3 Sekunden  (vorverarbeitet am  2026-09-29) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=1126438
#Domains=1897691