mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-29 07:42:04 +00:00
Bug 1719459
- Part 2: Add shell ModuleObject wrapper to avoid unexpected operation. r=jonco
Differential Revision: https://phabricator.services.mozilla.com/D119566
This commit is contained in:
parent
e819448fb3
commit
4ccc6acc5c
@ -1,5 +1,3 @@
|
||||
// |jit-test| error: TypeError
|
||||
|
||||
let a = registerModule('a', parseModule("var x = 1; export { x };"));
|
||||
let b = registerModule('b', parseModule("import { x as y } from 'a';"));
|
||||
a.__proto__ = {15: 1337};
|
||||
|
223
js/src/jit-test/tests/modules/shell-wrapper.js
Normal file
223
js/src/jit-test/tests/modules/shell-wrapper.js
Normal file
@ -0,0 +1,223 @@
|
||||
// Test shell ModuleObject wrapper's accessors and methods.
|
||||
|
||||
load(libdir + "asserts.js");
|
||||
|
||||
function testGetter(obj, name) {
|
||||
// Check the getter is defined on the instance, instead of prototype.
|
||||
// * raw ModuleObject's getters are defined on prototype
|
||||
// * ModuleObject wrapper's getters are defined on instance
|
||||
const desc = Object.getOwnPropertyDescriptor(obj, name);
|
||||
assertEq(typeof desc.get, "function");
|
||||
assertEq(Object.getOwnPropertyDescriptor(Object.getPrototypeOf(obj), name),
|
||||
undefined);
|
||||
|
||||
// Check invalid this value.
|
||||
assertThrowsInstanceOf(() => {
|
||||
desc.get.call({});
|
||||
}, Error);
|
||||
}
|
||||
|
||||
function testMethod(obj, name) {
|
||||
// Check the method is defined on the instance, instead of prototype.
|
||||
// * raw ModuleObject's methods are defined on prototype
|
||||
// * ModuleObject wrapper's methods are defined on instance
|
||||
const desc = Object.getOwnPropertyDescriptor(obj, name);
|
||||
assertEq(typeof desc.value, "function");
|
||||
assertEq(Object.getOwnPropertyDescriptor(Object.getPrototypeOf(obj), name),
|
||||
undefined);
|
||||
|
||||
// Check invalid this value.
|
||||
assertThrowsInstanceOf(() => {
|
||||
desc.value.call({});
|
||||
}, Error);
|
||||
}
|
||||
|
||||
// ==== namespace getter ====
|
||||
const a = registerModule('a', parseModule(`
|
||||
export const v = 10;
|
||||
`));
|
||||
const b = registerModule('b', parseModule(`
|
||||
import * as ns from 'a'
|
||||
`));
|
||||
b.declarationInstantiation();
|
||||
b.evaluation();
|
||||
assertEq(a.namespace.v, 10);
|
||||
testGetter(a, "namespace");
|
||||
|
||||
// ==== status getter ====
|
||||
const MODULE_STATUS_UNLINKED = 0;
|
||||
const MODULE_STATUS_LINKED = 2;
|
||||
const MODULE_STATUS_EVALUATED = 4;
|
||||
|
||||
const c = registerModule('c', parseModule(`
|
||||
`));
|
||||
assertEq(c.status, MODULE_STATUS_UNLINKED);
|
||||
c.declarationInstantiation();
|
||||
assertEq(c.status, MODULE_STATUS_LINKED);
|
||||
c.evaluation();
|
||||
assertEq(c.status, MODULE_STATUS_EVALUATED);
|
||||
testGetter(c, "status");
|
||||
|
||||
// ==== evaluationError getter ====
|
||||
const d = registerModule('d', parseModule(`
|
||||
f();
|
||||
`));
|
||||
d.declarationInstantiation();
|
||||
try {
|
||||
d.evaluation();
|
||||
} catch (e) {
|
||||
}
|
||||
assertEq(d.evaluationError instanceof ReferenceError, true);
|
||||
testGetter(d, "evaluationError");
|
||||
|
||||
// ==== requestedModules getter ====
|
||||
const e = parseModule(`
|
||||
import a from 'b';
|
||||
`);
|
||||
assertEq(e.requestedModules.length, 1);
|
||||
assertEq(e.requestedModules[0].moduleRequest.specifier, 'b');
|
||||
assertEq(e.requestedModules[0].lineNumber, 2);
|
||||
assertEq(e.requestedModules[0].columnNumber, 14);
|
||||
testGetter(e, "requestedModules");
|
||||
testGetter(e.requestedModules[0], "moduleRequest");
|
||||
testGetter(e.requestedModules[0].moduleRequest, "specifier");
|
||||
testGetter(e.requestedModules[0], "lineNumber");
|
||||
testGetter(e.requestedModules[0], "columnNumber");
|
||||
|
||||
// ==== importEntries getter ====
|
||||
const f = parseModule(`
|
||||
import {a as A} from 'b';
|
||||
`);
|
||||
assertEq(f.importEntries.length, 1);
|
||||
assertEq(f.importEntries[0].moduleRequest.specifier, 'b');
|
||||
assertEq(f.importEntries[0].importName, 'a');
|
||||
assertEq(f.importEntries[0].localName, 'A');
|
||||
assertEq(f.importEntries[0].lineNumber, 2);
|
||||
assertEq(f.importEntries[0].columnNumber, 8);
|
||||
testGetter(f, "importEntries");
|
||||
testGetter(f.importEntries[0], "moduleRequest");
|
||||
testGetter(f.importEntries[0].moduleRequest, "specifier");
|
||||
testGetter(f.importEntries[0], "importName");
|
||||
testGetter(f.importEntries[0], "localName");
|
||||
testGetter(f.importEntries[0], "lineNumber");
|
||||
testGetter(f.importEntries[0], "columnNumber");
|
||||
|
||||
// ==== localExportEntries getter ====
|
||||
const g = parseModule(`
|
||||
export const v = 1;
|
||||
`);
|
||||
assertEq(g.localExportEntries.length, 1);
|
||||
assertEq(g.localExportEntries[0].exportName, 'v');
|
||||
assertEq(g.localExportEntries[0].moduleRequest.specifier, null);
|
||||
assertEq(g.localExportEntries[0].importName, null);
|
||||
assertEq(g.localExportEntries[0].localName, 'v');
|
||||
assertEq(g.localExportEntries[0].lineNumber, 0);
|
||||
assertEq(g.localExportEntries[0].columnNumber, 0);
|
||||
testGetter(g, "localExportEntries");
|
||||
testGetter(g.localExportEntries[0], "exportName");
|
||||
testGetter(g.localExportEntries[0], "moduleRequest");
|
||||
testGetter(g.localExportEntries[0].moduleRequest, "specifier");
|
||||
testGetter(g.localExportEntries[0], "importName");
|
||||
testGetter(g.localExportEntries[0], "localName");
|
||||
testGetter(g.localExportEntries[0], "lineNumber");
|
||||
testGetter(g.localExportEntries[0], "columnNumber");
|
||||
|
||||
// ==== indirectExportEntries getter ====
|
||||
const h = parseModule(`
|
||||
export {v} from "b";
|
||||
`);
|
||||
assertEq(h.indirectExportEntries.length, 1);
|
||||
assertEq(h.indirectExportEntries[0].exportName, 'v');
|
||||
assertEq(h.indirectExportEntries[0].moduleRequest.specifier, "b");
|
||||
assertEq(h.indirectExportEntries[0].importName, "v");
|
||||
assertEq(h.indirectExportEntries[0].localName, null);
|
||||
assertEq(h.indirectExportEntries[0].lineNumber, 2);
|
||||
assertEq(h.indirectExportEntries[0].columnNumber, 8);
|
||||
|
||||
// ==== starExportEntries getter ====
|
||||
const i = parseModule(`
|
||||
export * from "b";
|
||||
`);
|
||||
assertEq(i.starExportEntries.length, 1);
|
||||
assertEq(i.starExportEntries[0].exportName, null);
|
||||
assertEq(i.starExportEntries[0].moduleRequest.specifier, "b");
|
||||
assertEq(i.starExportEntries[0].importName, null);
|
||||
assertEq(i.starExportEntries[0].localName, null);
|
||||
assertEq(i.starExportEntries[0].lineNumber, 2);
|
||||
assertEq(i.starExportEntries[0].columnNumber, 7);
|
||||
|
||||
// ==== dfsIndex and dfsAncestorIndex getters ====
|
||||
const j = registerModule('j', parseModule(`
|
||||
export const v1 = 10;
|
||||
import {v2} from 'k'
|
||||
`));
|
||||
const k = registerModule('k', parseModule(`
|
||||
export const v2 = 10;
|
||||
import {v1} from 'j'
|
||||
`));
|
||||
const l = registerModule('l', parseModule(`
|
||||
export const v3 = 10;
|
||||
import {v2} from 'k'
|
||||
import {v1} from 'j'
|
||||
`));
|
||||
assertEq(j.dfsIndex, undefined);
|
||||
assertEq(j.dfsAncestorIndex, undefined);
|
||||
assertEq(k.dfsIndex, undefined);
|
||||
assertEq(k.dfsAncestorIndex, undefined);
|
||||
assertEq(l.dfsIndex, undefined);
|
||||
assertEq(l.dfsAncestorIndex, undefined);
|
||||
l.declarationInstantiation();
|
||||
assertEq(j.dfsIndex, 2);
|
||||
assertEq(j.dfsAncestorIndex, 1);
|
||||
assertEq(k.dfsIndex, 1);
|
||||
assertEq(k.dfsAncestorIndex, 1);
|
||||
assertEq(l.dfsIndex, 0);
|
||||
assertEq(l.dfsAncestorIndex, 0);
|
||||
|
||||
// ==== async and promises getters ====
|
||||
const m = parseModule(`
|
||||
`);
|
||||
assertEq(m.async, false);
|
||||
assertEq(m.topLevelCapability, undefined);
|
||||
assertEq(m.asyncEvaluatingPostOrder, undefined);
|
||||
assertEq(m.asyncParentModules[0], undefined);
|
||||
assertEq(m.pendingAsyncDependencies, undefined);
|
||||
testGetter(m, "async");
|
||||
testGetter(m, "topLevelCapability");
|
||||
testGetter(m, "asyncEvaluatingPostOrder");
|
||||
testGetter(m, "asyncParentModules");
|
||||
testGetter(m, "pendingAsyncDependencies");
|
||||
|
||||
// ==== getExportedNames method shouldn't be exposed ====
|
||||
const n = parseModule(``);
|
||||
assertEq(n.getExportedNames, undefined);
|
||||
|
||||
// ==== resolveExport method shouldn't be exposed ====
|
||||
const o = parseModule(``);
|
||||
assertEq(o.resolveExport, undefined);
|
||||
|
||||
// ==== declarationInstantiation and evaluationmethod methods ====
|
||||
const p = parseModule(``);
|
||||
p.declarationInstantiation();
|
||||
p.evaluation();
|
||||
testMethod(p, "declarationInstantiation");
|
||||
testMethod(p, "evaluation");
|
||||
|
||||
const q = decodeModule(codeModule(parseModule(``)));
|
||||
q.declarationInstantiation();
|
||||
q.evaluation();
|
||||
testMethod(q, "declarationInstantiation");
|
||||
testMethod(q, "evaluation");
|
||||
|
||||
if (helperThreadCount() > 0) {
|
||||
offThreadCompileModule(``);
|
||||
let r = finishOffThreadModule();
|
||||
r.declarationInstantiation();
|
||||
r.evaluation();
|
||||
testMethod(r, "declarationInstantiation");
|
||||
testMethod(r, "evaluation");
|
||||
}
|
||||
|
||||
// ==== gatherAsyncParentCompletions method shouldn't be exposed ====
|
||||
const s = parseModule(``);
|
||||
assertEq(s.gatherAsyncParentCompletions, undefined);
|
385
js/src/shell/ShellModuleObjectWrapper.cpp
Normal file
385
js/src/shell/ShellModuleObjectWrapper.cpp
Normal file
@ -0,0 +1,385 @@
|
||||
/* -*- 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/ShellModuleObjectWrapper.h"
|
||||
|
||||
#include "jsapi.h" // JS_GetProperty, JS::Call, JS_NewPlainObject, JS_DefineProperty
|
||||
|
||||
#include "builtin/ModuleObject.h" // js::ModuleObject
|
||||
#include "js/CallArgs.h" // JS::CallArgs
|
||||
#include "js/CallNonGenericMethod.h" // CallNonGenericMethod
|
||||
#include "js/Class.h" // JSClass, JSCLASS_*
|
||||
#include "js/ErrorReport.h" // JS_ReportErrorASCII
|
||||
#include "js/PropertySpec.h" // JSPropertySpec, JS_PSG, JS_PS_END, JSFunctionSpec, JS_FN, JS_FN_END
|
||||
#include "js/RootingAPI.h" // JS::Rooted, JS::Handle, JS::MutableHandle
|
||||
#include "js/Value.h" // JS::Value
|
||||
#include "vm/ArrayObject.h" // ArrayObject, NewDenseFullyAllocatedArray
|
||||
#include "vm/GlobalObject.h" // DefinePropertiesAndFunctions
|
||||
#include "vm/JSFunction.h" // JSFunction
|
||||
#include "vm/JSObject.h" // JSObject
|
||||
#include "vm/List.h" // ListObject
|
||||
#include "vm/NativeObject.h" // NativeObject
|
||||
#include "vm/Stack.h" // FixedInvokeArgs
|
||||
|
||||
#include "vm/NativeObject-inl.h" // NativeObject::ensureDenseInitializedLength
|
||||
|
||||
using namespace js;
|
||||
using namespace js::shell;
|
||||
|
||||
#define DEFINE_CLASS_IMPL(CLASS) \
|
||||
CLASS* Shell##CLASS##Wrapper::get() { \
|
||||
return &getReservedSlot(TargetSlot).toObject().as<CLASS>(); \
|
||||
} \
|
||||
/* static */ const JSClass Shell##CLASS##Wrapper::class_ = { \
|
||||
"Shell" #CLASS "Wrapper", \
|
||||
JSCLASS_HAS_RESERVED_SLOTS(Shell##CLASS##Wrapper::SlotCount)}; \
|
||||
MOZ_ALWAYS_INLINE bool IsShell##CLASS##Wrapper(JS::Handle<JS::Value> v) { \
|
||||
return v.isObject() && v.toObject().is<Shell##CLASS##Wrapper>(); \
|
||||
}
|
||||
|
||||
#define DEFINE_CLASS(CLASS) \
|
||||
class Shell##CLASS##Wrapper : public js::NativeObject { \
|
||||
public: \
|
||||
using Target = CLASS; \
|
||||
enum ModuleSlot { TargetSlot = 0, SlotCount }; \
|
||||
static const JSClass class_; \
|
||||
static Shell##CLASS##Wrapper* create(JSContext* cx, \
|
||||
JS::Handle<CLASS*> obj); \
|
||||
CLASS* get(); \
|
||||
}; \
|
||||
DEFINE_CLASS_IMPL(CLASS)
|
||||
|
||||
DEFINE_CLASS(ModuleRequestObject)
|
||||
DEFINE_CLASS(ImportEntryObject)
|
||||
DEFINE_CLASS(ExportEntryObject)
|
||||
DEFINE_CLASS(RequestedModuleObject)
|
||||
// NOTE: We don't need wrapper for IndirectBindingMap and ModuleNamespaceObject
|
||||
DEFINE_CLASS_IMPL(ModuleObject)
|
||||
|
||||
#undef DEFINE_CLASS
|
||||
#undef DEFINE_CLASS_IMPL
|
||||
|
||||
bool IdentFilter(JSContext* cx, JS::Handle<JS::Value> from,
|
||||
JS::MutableHandle<JS::Value> to) {
|
||||
to.set(from);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
bool SingleFilter(JSContext* cx, JS::Handle<JS::Value> from,
|
||||
JS::MutableHandle<JS::Value> to) {
|
||||
using TargetT = typename T::Target;
|
||||
|
||||
if (!from.isObject() || !from.toObject().is<TargetT>()) {
|
||||
to.set(from);
|
||||
return true;
|
||||
}
|
||||
|
||||
JS::Rooted<TargetT*> obj(cx, &from.toObject().as<TargetT>());
|
||||
JS::Rooted<T*> filtered(cx, T::create(cx, obj));
|
||||
if (!filtered) {
|
||||
return false;
|
||||
}
|
||||
to.setObject(*filtered);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
bool ArrayFilter(JSContext* cx, JS::Handle<JS::Value> from,
|
||||
JS::MutableHandle<JS::Value> to) {
|
||||
using TargetT = typename T::Target;
|
||||
|
||||
if (!from.isObject() || !from.toObject().is<ArrayObject>()) {
|
||||
to.set(from);
|
||||
return true;
|
||||
}
|
||||
|
||||
JS::Rooted<ArrayObject*> fromArray(cx, &from.toObject().as<ArrayObject>());
|
||||
uint32_t length = fromArray->length();
|
||||
JS::Rooted<ArrayObject*> toArray(cx, NewDenseFullyAllocatedArray(cx, length));
|
||||
if (!toArray) {
|
||||
return false;
|
||||
}
|
||||
|
||||
toArray->ensureDenseInitializedLength(0, length);
|
||||
|
||||
for (uint32_t i = 0; i < length; i++) {
|
||||
JS::Rooted<JS::Value> item(cx, fromArray->getDenseElement(i));
|
||||
JS::Rooted<TargetT*> req(cx, &item.toObject().as<TargetT>());
|
||||
JS::Rooted<T*> filtered(cx, T::create(cx, req));
|
||||
if (!filtered) {
|
||||
return false;
|
||||
}
|
||||
toArray->initDenseElement(i, ObjectValue(*filtered));
|
||||
}
|
||||
to.setObject(*toArray);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
bool ListToArrayFilter(JSContext* cx, JS::Handle<JS::Value> from,
|
||||
JS::MutableHandle<JS::Value> to) {
|
||||
using TargetT = typename T::Target;
|
||||
|
||||
if (!from.isObject() || !from.toObject().is<ListObject>()) {
|
||||
to.set(from);
|
||||
return true;
|
||||
}
|
||||
|
||||
JS::Rooted<ListObject*> fromList(cx, &from.toObject().as<ListObject>());
|
||||
uint32_t length = fromList->length();
|
||||
JS::Rooted<ArrayObject*> toArray(cx, NewDenseFullyAllocatedArray(cx, length));
|
||||
if (!toArray) {
|
||||
return false;
|
||||
}
|
||||
|
||||
toArray->ensureDenseInitializedLength(0, length);
|
||||
|
||||
for (uint32_t i = 0; i < length; i++) {
|
||||
JS::Rooted<JS::Value> item(cx, fromList->get(i));
|
||||
JS::Rooted<TargetT*> req(cx, &item.toObject().as<TargetT>());
|
||||
JS::Rooted<T*> filtered(cx, T::create(cx, req));
|
||||
if (!filtered) {
|
||||
return false;
|
||||
}
|
||||
toArray->initDenseElement(i, ObjectValue(*filtered));
|
||||
}
|
||||
to.setObject(*toArray);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class T, typename FilterT>
|
||||
bool ShellModuleWrapperGetter(JSContext* cx, const JS::CallArgs& args,
|
||||
const char* prop, FilterT filter) {
|
||||
using TargetT = typename T::Target;
|
||||
|
||||
JS::Rooted<TargetT*> obj(cx, args.thisv().toObject().as<T>().get());
|
||||
JS::Rooted<JS::Value> rval(cx);
|
||||
if (!JS_GetProperty(cx, obj, prop, &rval)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
JS::Rooted<JS::Value> filtered(cx);
|
||||
if (!filter(cx, rval, &filtered)) {
|
||||
return false;
|
||||
}
|
||||
args.rval().set(filtered);
|
||||
return true;
|
||||
}
|
||||
|
||||
#define DEFINE_GETTER_FUNCTIONS(CLASS, PROP, FILTER) \
|
||||
static bool Shell##CLASS##Wrapper_##PROP##Getter_impl( \
|
||||
JSContext* cx, const JS::CallArgs& args) { \
|
||||
return ShellModuleWrapperGetter<Shell##CLASS##Wrapper>(cx, args, #PROP, \
|
||||
FILTER); \
|
||||
} \
|
||||
static bool Shell##CLASS##Wrapper_##PROP##Getter(JSContext* cx, \
|
||||
unsigned argc, Value* vp) { \
|
||||
JS::CallArgs args = JS::CallArgsFromVp(argc, vp); \
|
||||
return CallNonGenericMethod<IsShell##CLASS##Wrapper, \
|
||||
Shell##CLASS##Wrapper_##PROP##Getter_impl>( \
|
||||
cx, args); \
|
||||
}
|
||||
|
||||
DEFINE_GETTER_FUNCTIONS(ModuleRequestObject, specifier, IdentFilter)
|
||||
|
||||
static const JSPropertySpec ShellModuleRequestObjectWrapper_accessors[] = {
|
||||
JS_PSG("specifier", ShellModuleRequestObjectWrapper_specifierGetter, 0),
|
||||
JS_PS_END};
|
||||
|
||||
DEFINE_GETTER_FUNCTIONS(ImportEntryObject, moduleRequest,
|
||||
SingleFilter<ShellModuleRequestObjectWrapper>)
|
||||
DEFINE_GETTER_FUNCTIONS(ImportEntryObject, importName, IdentFilter)
|
||||
DEFINE_GETTER_FUNCTIONS(ImportEntryObject, localName, IdentFilter)
|
||||
DEFINE_GETTER_FUNCTIONS(ImportEntryObject, lineNumber, IdentFilter)
|
||||
DEFINE_GETTER_FUNCTIONS(ImportEntryObject, columnNumber, IdentFilter)
|
||||
|
||||
static const JSPropertySpec ShellImportEntryObjectWrapper_accessors[] = {
|
||||
JS_PSG("moduleRequest", ShellImportEntryObjectWrapper_moduleRequestGetter,
|
||||
0),
|
||||
JS_PSG("importName", ShellImportEntryObjectWrapper_importNameGetter, 0),
|
||||
JS_PSG("localName", ShellImportEntryObjectWrapper_localNameGetter, 0),
|
||||
JS_PSG("lineNumber", ShellImportEntryObjectWrapper_lineNumberGetter, 0),
|
||||
JS_PSG("columnNumber", ShellImportEntryObjectWrapper_columnNumberGetter, 0),
|
||||
JS_PS_END};
|
||||
|
||||
DEFINE_GETTER_FUNCTIONS(ExportEntryObject, exportName, IdentFilter)
|
||||
DEFINE_GETTER_FUNCTIONS(ExportEntryObject, moduleRequest,
|
||||
SingleFilter<ShellModuleRequestObjectWrapper>)
|
||||
DEFINE_GETTER_FUNCTIONS(ExportEntryObject, importName, IdentFilter)
|
||||
DEFINE_GETTER_FUNCTIONS(ExportEntryObject, localName, IdentFilter)
|
||||
DEFINE_GETTER_FUNCTIONS(ExportEntryObject, lineNumber, IdentFilter)
|
||||
DEFINE_GETTER_FUNCTIONS(ExportEntryObject, columnNumber, IdentFilter)
|
||||
|
||||
static const JSPropertySpec ShellExportEntryObjectWrapper_accessors[] = {
|
||||
JS_PSG("exportName", ShellExportEntryObjectWrapper_exportNameGetter, 0),
|
||||
JS_PSG("moduleRequest", ShellExportEntryObjectWrapper_moduleRequestGetter,
|
||||
0),
|
||||
JS_PSG("importName", ShellExportEntryObjectWrapper_importNameGetter, 0),
|
||||
JS_PSG("localName", ShellExportEntryObjectWrapper_localNameGetter, 0),
|
||||
JS_PSG("lineNumber", ShellExportEntryObjectWrapper_lineNumberGetter, 0),
|
||||
JS_PSG("columnNumber", ShellExportEntryObjectWrapper_columnNumberGetter, 0),
|
||||
JS_PS_END};
|
||||
|
||||
DEFINE_GETTER_FUNCTIONS(RequestedModuleObject, moduleRequest,
|
||||
SingleFilter<ShellModuleRequestObjectWrapper>)
|
||||
DEFINE_GETTER_FUNCTIONS(RequestedModuleObject, lineNumber, IdentFilter)
|
||||
DEFINE_GETTER_FUNCTIONS(RequestedModuleObject, columnNumber, IdentFilter)
|
||||
|
||||
static const JSPropertySpec ShellRequestedModuleObjectWrapper_accessors[] = {
|
||||
JS_PSG("moduleRequest",
|
||||
ShellRequestedModuleObjectWrapper_moduleRequestGetter, 0),
|
||||
JS_PSG("lineNumber", ShellRequestedModuleObjectWrapper_lineNumberGetter, 0),
|
||||
JS_PSG("columnNumber", ShellRequestedModuleObjectWrapper_columnNumberGetter,
|
||||
0),
|
||||
JS_PS_END};
|
||||
|
||||
DEFINE_GETTER_FUNCTIONS(ModuleObject, namespace, IdentFilter)
|
||||
DEFINE_GETTER_FUNCTIONS(ModuleObject, status, IdentFilter)
|
||||
DEFINE_GETTER_FUNCTIONS(ModuleObject, evaluationError, IdentFilter)
|
||||
DEFINE_GETTER_FUNCTIONS(ModuleObject, requestedModules,
|
||||
ArrayFilter<ShellRequestedModuleObjectWrapper>)
|
||||
DEFINE_GETTER_FUNCTIONS(ModuleObject, importEntries,
|
||||
ArrayFilter<ShellImportEntryObjectWrapper>)
|
||||
DEFINE_GETTER_FUNCTIONS(ModuleObject, localExportEntries,
|
||||
ArrayFilter<ShellExportEntryObjectWrapper>)
|
||||
DEFINE_GETTER_FUNCTIONS(ModuleObject, indirectExportEntries,
|
||||
ArrayFilter<ShellExportEntryObjectWrapper>)
|
||||
DEFINE_GETTER_FUNCTIONS(ModuleObject, starExportEntries,
|
||||
ArrayFilter<ShellExportEntryObjectWrapper>)
|
||||
DEFINE_GETTER_FUNCTIONS(ModuleObject, dfsIndex, IdentFilter)
|
||||
DEFINE_GETTER_FUNCTIONS(ModuleObject, dfsAncestorIndex, IdentFilter)
|
||||
DEFINE_GETTER_FUNCTIONS(ModuleObject, async, IdentFilter)
|
||||
DEFINE_GETTER_FUNCTIONS(ModuleObject, topLevelCapability, IdentFilter)
|
||||
DEFINE_GETTER_FUNCTIONS(ModuleObject, asyncEvaluatingPostOrder, IdentFilter)
|
||||
DEFINE_GETTER_FUNCTIONS(ModuleObject, asyncParentModules,
|
||||
ListToArrayFilter<ShellModuleObjectWrapper>)
|
||||
DEFINE_GETTER_FUNCTIONS(ModuleObject, pendingAsyncDependencies, IdentFilter)
|
||||
|
||||
static const JSPropertySpec ShellModuleObjectWrapper_accessors[] = {
|
||||
JS_PSG("namespace", ShellModuleObjectWrapper_namespaceGetter, 0),
|
||||
JS_PSG("status", ShellModuleObjectWrapper_statusGetter, 0),
|
||||
JS_PSG("evaluationError", ShellModuleObjectWrapper_evaluationErrorGetter,
|
||||
0),
|
||||
JS_PSG("requestedModules", ShellModuleObjectWrapper_requestedModulesGetter,
|
||||
0),
|
||||
JS_PSG("importEntries", ShellModuleObjectWrapper_importEntriesGetter, 0),
|
||||
JS_PSG("localExportEntries",
|
||||
ShellModuleObjectWrapper_localExportEntriesGetter, 0),
|
||||
JS_PSG("indirectExportEntries",
|
||||
ShellModuleObjectWrapper_indirectExportEntriesGetter, 0),
|
||||
JS_PSG("starExportEntries",
|
||||
ShellModuleObjectWrapper_starExportEntriesGetter, 0),
|
||||
JS_PSG("dfsIndex", ShellModuleObjectWrapper_dfsIndexGetter, 0),
|
||||
JS_PSG("dfsAncestorIndex", ShellModuleObjectWrapper_dfsAncestorIndexGetter,
|
||||
0),
|
||||
JS_PSG("async", ShellModuleObjectWrapper_asyncGetter, 0),
|
||||
JS_PSG("topLevelCapability",
|
||||
ShellModuleObjectWrapper_topLevelCapabilityGetter, 0),
|
||||
JS_PSG("asyncEvaluatingPostOrder",
|
||||
ShellModuleObjectWrapper_asyncEvaluatingPostOrderGetter, 0),
|
||||
JS_PSG("asyncParentModules",
|
||||
ShellModuleObjectWrapper_asyncParentModulesGetter, 0),
|
||||
JS_PSG("pendingAsyncDependencies",
|
||||
ShellModuleObjectWrapper_pendingAsyncDependenciesGetter, 0),
|
||||
JS_PS_END};
|
||||
|
||||
#undef DEFINE_GETTER_FUNCTIONS
|
||||
|
||||
template <class T, size_t ARGC, typename CheckArgsT, typename FilterT>
|
||||
bool ShellModuleWrapperMethod(JSContext* cx, const JS::CallArgs& args,
|
||||
const char* prop, CheckArgsT checkArgs,
|
||||
FilterT filter) {
|
||||
using TargetT = typename T::Target;
|
||||
|
||||
JS::Rooted<TargetT*> obj(cx, args.thisv().toObject().as<T>().get());
|
||||
JS::Rooted<JS::Value> methodVal(cx);
|
||||
if (!JS_GetProperty(cx, obj, prop, &methodVal)) {
|
||||
return false;
|
||||
}
|
||||
if (!methodVal.isObject() || !methodVal.toObject().is<JSFunction>()) {
|
||||
JS_ReportErrorASCII(cx, "method property is not function");
|
||||
return false;
|
||||
}
|
||||
|
||||
JS::Rooted<JSFunction*> method(cx, &methodVal.toObject().as<JSFunction>());
|
||||
if (!checkArgs(cx, args)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
FixedInvokeArgs<ARGC> invokeArgs(cx);
|
||||
for (size_t i = 0; i < ARGC; i++) {
|
||||
invokeArgs[i].set(args.get(i));
|
||||
}
|
||||
|
||||
JS::Rooted<JS::Value> rval(cx);
|
||||
if (!JS::Call(cx, obj, method, invokeArgs, &rval)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
JS::Rooted<JS::Value> filtered(cx);
|
||||
if (!filter(cx, rval, &filtered)) {
|
||||
return false;
|
||||
}
|
||||
args.rval().set(filtered);
|
||||
return true;
|
||||
}
|
||||
|
||||
#define DEFINE_METHOD_FUNCTIONS(CLASS, METHOD, ARGC, CHECK, FILTER) \
|
||||
static bool Shell##CLASS##Wrapper_##METHOD##_impl( \
|
||||
JSContext* cx, const JS::CallArgs& args) { \
|
||||
return ShellModuleWrapperMethod<Shell##CLASS##Wrapper, ARGC>( \
|
||||
cx, args, #METHOD, CHECK, FILTER); \
|
||||
} \
|
||||
static bool Shell##CLASS##Wrapper##_##METHOD(JSContext* cx, unsigned argc, \
|
||||
Value* vp) { \
|
||||
JS::CallArgs args = JS::CallArgsFromVp(argc, vp); \
|
||||
return CallNonGenericMethod<Is##Shell##CLASS##Wrapper, \
|
||||
Shell##CLASS##Wrapper_##METHOD##_impl>(cx, \
|
||||
args); \
|
||||
}
|
||||
|
||||
static bool CheckNoArgs(JSContext* cx, const CallArgs& args) { return true; }
|
||||
|
||||
DEFINE_METHOD_FUNCTIONS(ModuleObject, declarationInstantiation, 0, CheckNoArgs,
|
||||
IdentFilter)
|
||||
DEFINE_METHOD_FUNCTIONS(ModuleObject, evaluation, 0, CheckNoArgs, IdentFilter)
|
||||
|
||||
static const JSFunctionSpec ShellModuleObjectWrapper_functions[] = {
|
||||
JS_FN("declarationInstantiation",
|
||||
ShellModuleObjectWrapper_declarationInstantiation, 0, 0),
|
||||
JS_FN("evaluation", ShellModuleObjectWrapper_evaluation, 0, 0), JS_FS_END};
|
||||
|
||||
#undef DEFINE_METHOD_FUNCTIONS
|
||||
|
||||
#define DEFINE_CREATE(CLASS, ACCESSORS, FUNCTIONS) \
|
||||
/* static */ \
|
||||
Shell##CLASS##Wrapper* Shell##CLASS##Wrapper::create( \
|
||||
JSContext* cx, JS::Handle<CLASS*> obj) { \
|
||||
JS::Rooted<JSObject*> wrapper(cx, JS_NewObject(cx, &class_)); \
|
||||
if (!wrapper) { \
|
||||
return nullptr; \
|
||||
} \
|
||||
if (!DefinePropertiesAndFunctions(cx, wrapper, ACCESSORS, FUNCTIONS)) { \
|
||||
return nullptr; \
|
||||
} \
|
||||
wrapper->as<Shell##CLASS##Wrapper>().initReservedSlot(TargetSlot, \
|
||||
ObjectValue(*obj)); \
|
||||
return &wrapper->as<Shell##CLASS##Wrapper>(); \
|
||||
}
|
||||
|
||||
DEFINE_CREATE(ModuleRequestObject, ShellModuleRequestObjectWrapper_accessors,
|
||||
nullptr)
|
||||
DEFINE_CREATE(ImportEntryObject, ShellImportEntryObjectWrapper_accessors,
|
||||
nullptr)
|
||||
DEFINE_CREATE(ExportEntryObject, ShellExportEntryObjectWrapper_accessors,
|
||||
nullptr)
|
||||
DEFINE_CREATE(RequestedModuleObject,
|
||||
ShellRequestedModuleObjectWrapper_accessors, nullptr)
|
||||
DEFINE_CREATE(ModuleObject, ShellModuleObjectWrapper_accessors,
|
||||
ShellModuleObjectWrapper_functions)
|
||||
|
||||
#undef DEFINE_CREATE
|
36
js/src/shell/ShellModuleObjectWrapper.h
Normal file
36
js/src/shell/ShellModuleObjectWrapper.h
Normal file
@ -0,0 +1,36 @@
|
||||
/* -*- 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_ShellModuleObjectWrapper_h
|
||||
#define shell_ShellModuleObjectWrapper_h
|
||||
|
||||
#include "builtin/ModuleObject.h" // js::ModuleObject
|
||||
#include "js/Class.h" // JSClass
|
||||
#include "js/RootingAPI.h" // JS::Handle
|
||||
#include "vm/NativeObject.h" // js::NativeObject
|
||||
|
||||
namespace js {
|
||||
|
||||
namespace shell {
|
||||
|
||||
// ModuleObject's accessors and methods are only for internal usage in
|
||||
// js/src/builtin/Module.js, and they don't check arguments types.
|
||||
//
|
||||
// To use ModuleObject in tests, add a wrapper that checks arguments types.
|
||||
class ShellModuleObjectWrapper : public js::NativeObject {
|
||||
public:
|
||||
using Target = ModuleObject;
|
||||
enum ModuleSlot { TargetSlot = 0, SlotCount };
|
||||
static const JSClass class_;
|
||||
static ShellModuleObjectWrapper* create(JSContext* cx,
|
||||
JS::Handle<ModuleObject*> moduleObj);
|
||||
ModuleObject* get();
|
||||
};
|
||||
|
||||
} // namespace shell
|
||||
} // namespace js
|
||||
|
||||
#endif /* shell_ShellModuleObjectWrapper_h */
|
@ -150,6 +150,7 @@
|
||||
#include "shell/jsoptparse.h"
|
||||
#include "shell/jsshell.h"
|
||||
#include "shell/OSObject.h"
|
||||
#include "shell/ShellModuleObjectWrapper.h"
|
||||
#include "shell/WasmTesting.h"
|
||||
#include "threading/ConditionVariable.h"
|
||||
#include "threading/ExclusiveData.h"
|
||||
@ -3779,8 +3780,11 @@ static bool DisassembleToSprinter(JSContext* cx, unsigned argc, Value* vp,
|
||||
RootedFunction fun(cx);
|
||||
RootedScript script(cx);
|
||||
RootedValue value(cx, p.argv[i]);
|
||||
if (value.isObject() && value.toObject().is<ModuleObject>()) {
|
||||
script = value.toObject().as<ModuleObject>().maybeScript();
|
||||
if (value.isObject() && value.toObject().is<ShellModuleObjectWrapper>()) {
|
||||
script = value.toObject()
|
||||
.as<ShellModuleObjectWrapper>()
|
||||
.get()
|
||||
->maybeScript();
|
||||
} else {
|
||||
script = TestingFunctionArgumentToScript(cx, value, fun.address());
|
||||
}
|
||||
@ -4029,8 +4033,9 @@ static bool CacheIRHealthReport(JSContext* cx, unsigned argc, Value* vp) {
|
||||
} else {
|
||||
RootedValue value(cx, args.get(0));
|
||||
|
||||
if (value.isObject() && value.toObject().is<ModuleObject>()) {
|
||||
script = value.toObject().as<ModuleObject>().maybeScript();
|
||||
if (value.isObject() && value.toObject().is<ShellModuleObjectWrapper>()) {
|
||||
script =
|
||||
value.toObject().as<ShellModuleObjectWrapper>().get()->maybeScript();
|
||||
} else {
|
||||
script = TestingFunctionArgumentToScript(cx, args.get(0));
|
||||
}
|
||||
@ -5520,7 +5525,12 @@ static bool ParseModule(JSContext* cx, unsigned argc, Value* vp) {
|
||||
return false;
|
||||
}
|
||||
|
||||
args.rval().setObject(*module);
|
||||
Rooted<ShellModuleObjectWrapper*> wrapper(
|
||||
cx, ShellModuleObjectWrapper::create(cx, module.as<ModuleObject>()));
|
||||
if (!wrapper) {
|
||||
return false;
|
||||
}
|
||||
args.rval().setObject(*wrapper);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -5605,13 +5615,15 @@ static bool CodeModule(JSContext* cx, unsigned argc, Value* vp) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!args[0].isObject() || !args[0].toObject().is<ModuleObject>()) {
|
||||
if (!args[0].isObject() ||
|
||||
!args[0].toObject().is<ShellModuleObjectWrapper>()) {
|
||||
const char* typeName = InformalValueTypeName(args[0]);
|
||||
JS_ReportErrorASCII(cx, "expected module object, got %s", typeName);
|
||||
return false;
|
||||
}
|
||||
|
||||
RootedModuleObject modObject(cx, &args[0].toObject().as<ModuleObject>());
|
||||
RootedModuleObject modObject(
|
||||
cx, args[0].toObject().as<ShellModuleObjectWrapper>().get());
|
||||
if (modObject->status() >= MODULE_STATUS_LINKING) {
|
||||
JS_ReportErrorASCII(cx, "cannot encode module after instantiation.");
|
||||
return false;
|
||||
@ -5660,7 +5672,12 @@ static bool DecodeModule(JSContext* cx, unsigned argc, Value* vp) {
|
||||
return false;
|
||||
}
|
||||
|
||||
args.rval().setObject(*modObject);
|
||||
Rooted<ShellModuleObjectWrapper*> wrapper(
|
||||
cx, ShellModuleObjectWrapper::create(cx, modObject));
|
||||
if (!wrapper) {
|
||||
return false;
|
||||
}
|
||||
args.rval().setObject(*wrapper);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -5676,14 +5693,16 @@ static bool RegisterModule(JSContext* cx, unsigned argc, Value* vp) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!args[1].isObject() || !args[1].toObject().is<ModuleObject>()) {
|
||||
const char* typeName = InformalValueTypeName(args[0]);
|
||||
if (!args[1].isObject() ||
|
||||
!args[1].toObject().is<ShellModuleObjectWrapper>()) {
|
||||
const char* typeName = InformalValueTypeName(args[1]);
|
||||
JS_ReportErrorASCII(cx, "expected module, got %s", typeName);
|
||||
return false;
|
||||
}
|
||||
|
||||
ShellContext* sc = GetShellContext(cx);
|
||||
RootedModuleObject module(cx, &args[1].toObject().as<ModuleObject>());
|
||||
RootedModuleObject module(
|
||||
cx, args[1].toObject().as<ShellModuleObjectWrapper>().get());
|
||||
|
||||
RootedAtom specifier(cx, AtomizeString(cx, args[0].toString()));
|
||||
if (!specifier) {
|
||||
@ -5699,7 +5718,12 @@ static bool RegisterModule(JSContext* cx, unsigned argc, Value* vp) {
|
||||
return false;
|
||||
}
|
||||
|
||||
args.rval().setObject(*module);
|
||||
Rooted<ShellModuleObjectWrapper*> wrapper(
|
||||
cx, ShellModuleObjectWrapper::create(cx, module));
|
||||
if (!wrapper) {
|
||||
return false;
|
||||
}
|
||||
args.rval().setObject(*wrapper);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -5719,12 +5743,15 @@ static bool GetModuleEnvironmentNames(JSContext* cx, unsigned argc, Value* vp) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!args[0].isObject() || !args[0].toObject().is<ModuleObject>()) {
|
||||
JS_ReportErrorASCII(cx, "First argument should be a ModuleObject");
|
||||
if (!args[0].isObject() ||
|
||||
!args[0].toObject().is<ShellModuleObjectWrapper>()) {
|
||||
JS_ReportErrorASCII(cx,
|
||||
"First argument should be a ShellModuleObjectWrapper");
|
||||
return false;
|
||||
}
|
||||
|
||||
RootedModuleObject module(cx, &args[0].toObject().as<ModuleObject>());
|
||||
RootedModuleObject module(
|
||||
cx, args[0].toObject().as<ShellModuleObjectWrapper>().get());
|
||||
if (module->hadEvaluationError()) {
|
||||
JS_ReportErrorASCII(cx, "Module environment unavailable");
|
||||
return false;
|
||||
@ -5762,8 +5789,10 @@ static bool GetModuleEnvironmentValue(JSContext* cx, unsigned argc, Value* vp) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!args[0].isObject() || !args[0].toObject().is<ModuleObject>()) {
|
||||
JS_ReportErrorASCII(cx, "First argument should be a ModuleObject");
|
||||
if (!args[0].isObject() ||
|
||||
!args[0].toObject().is<ShellModuleObjectWrapper>()) {
|
||||
JS_ReportErrorASCII(cx,
|
||||
"First argument should be a ShellModuleObjectWrapper");
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -5772,7 +5801,8 @@ static bool GetModuleEnvironmentValue(JSContext* cx, unsigned argc, Value* vp) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RootedModuleObject module(cx, &args[0].toObject().as<ModuleObject>());
|
||||
RootedModuleObject module(
|
||||
cx, args[0].toObject().as<ShellModuleObjectWrapper>().get());
|
||||
if (module->hadEvaluationError()) {
|
||||
JS_ReportErrorASCII(cx, "Module environment unavailable");
|
||||
return false;
|
||||
@ -6565,7 +6595,12 @@ static bool FinishOffThreadModule(JSContext* cx, unsigned argc, Value* vp) {
|
||||
return false;
|
||||
}
|
||||
|
||||
args.rval().setObject(*module);
|
||||
Rooted<ShellModuleObjectWrapper*> wrapper(
|
||||
cx, ShellModuleObjectWrapper::create(cx, module.as<ModuleObject>()));
|
||||
if (!wrapper) {
|
||||
return false;
|
||||
}
|
||||
args.rval().setObject(*wrapper);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -8462,8 +8497,9 @@ static bool DumpScopeChain(JSContext* cx, unsigned argc, Value* vp) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!args[0].isObject() || !(args[0].toObject().is<JSFunction>() ||
|
||||
args[0].toObject().is<ModuleObject>())) {
|
||||
if (!args[0].isObject() ||
|
||||
!(args[0].toObject().is<JSFunction>() ||
|
||||
args[0].toObject().is<ShellModuleObjectWrapper>())) {
|
||||
ReportUsageErrorASCII(
|
||||
cx, callee, "Argument must be an interpreted function or a module");
|
||||
return false;
|
||||
@ -8484,7 +8520,7 @@ static bool DumpScopeChain(JSContext* cx, unsigned argc, Value* vp) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
script = obj->as<ModuleObject>().maybeScript();
|
||||
script = obj->as<ShellModuleObjectWrapper>().get()->maybeScript();
|
||||
if (!script) {
|
||||
JS_ReportErrorASCII(cx, "module does not have an associated script");
|
||||
return false;
|
||||
@ -9550,17 +9586,17 @@ static const JSFunctionSpecWithHelp shell_functions[] = {
|
||||
|
||||
JS_FN_HELP("parseModule", ParseModule, 1, 0,
|
||||
"parseModule(code)",
|
||||
" Parses source text as a module and returns a Module object."),
|
||||
" Parses source text as a module and returns a ModuleObject wrapper object."),
|
||||
|
||||
JS_FN_HELP("codeModule", CodeModule, 1, 0,
|
||||
"codeModule(module)",
|
||||
" Takes an uninstantiated ModuleObject and returns a XDR bytecode\n"
|
||||
" Takes an uninstantiated ModuleObject wrapper and returns a XDR bytecode\n"
|
||||
" representation of that ModuleObject."),
|
||||
|
||||
JS_FN_HELP("decodeModule", DecodeModule, 1, 0,
|
||||
"decodeModule(code)",
|
||||
" Takes a XDR bytecode representation of an uninstantiated ModuleObject and\n"
|
||||
" returns a ModuleObject."),
|
||||
" Takes a XDR bytecode representation of an uninstantiated\n"
|
||||
" ModuleObject and returns a ModuleObject wrapper."),
|
||||
|
||||
JS_FN_HELP("registerModule", RegisterModule, 2, 0,
|
||||
"registerModule(specifier, module)",
|
||||
|
@ -21,6 +21,7 @@ UNIFIED_SOURCES += [
|
||||
"jsshell.cpp",
|
||||
"ModuleLoader.cpp",
|
||||
"OSObject.cpp",
|
||||
"ShellModuleObjectWrapper.cpp",
|
||||
"WasmTesting.cpp",
|
||||
]
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user