1 /* -*- Mode: javascript; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4
2 * -*- */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #include "shell/ShellModuleObjectWrapper.h"
8
9 #include "jsapi.h" // JS_GetProperty, JS::Call, JS_NewPlainObject, JS_DefineProperty
10
11 #include "builtin/ModuleObject.h" // js::ModuleObject
12 #include "js/CallArgs.h" // JS::CallArgs
13 #include "js/CallNonGenericMethod.h" // CallNonGenericMethod
14 #include "js/Class.h" // JSClass, JSCLASS_*
15 #include "js/ErrorReport.h" // JS_ReportErrorASCII
16 #include "js/PropertySpec.h" // JSPropertySpec, JS_PSG, JS_PS_END, JSFunctionSpec, JS_FN, JS_FN_END
17 #include "js/RootingAPI.h" // JS::Rooted, JS::Handle, JS::MutableHandle
18 #include "js/Value.h" // JS::Value
19 #include "vm/ArrayObject.h" // ArrayObject, NewDenseFullyAllocatedArray
20 #include "vm/GlobalObject.h" // DefinePropertiesAndFunctions
21 #include "vm/JSFunction.h" // JSFunction
22 #include "vm/JSObject.h" // JSObject
23 #include "vm/List.h" // ListObject
24 #include "vm/NativeObject.h" // NativeObject
25 #include "vm/Stack.h" // FixedInvokeArgs
26
27 #include "vm/NativeObject-inl.h" // NativeObject::ensureDenseInitializedLength
28
29 using namespace js;
30 using namespace js::shell;
31
32 #define DEFINE_CLASS_IMPL(CLASS) \
33 CLASS* Shell##CLASS##Wrapper::get() { \
34 return &getReservedSlot(TargetSlot).toObject().as<CLASS>(); \
35 } \
36 /* static */ const JSClass Shell##CLASS##Wrapper::class_ = { \
37 "Shell" #CLASS "Wrapper", \
38 JSCLASS_HAS_RESERVED_SLOTS(Shell##CLASS##Wrapper::SlotCount)}; \
39 MOZ_ALWAYS_INLINE bool IsShell##CLASS##Wrapper(JS::Handle<JS::Value> v) { \
40 return v.isObject() && v.toObject().is<Shell##CLASS##Wrapper>(); \
41 }
42
43 #define DEFINE_CLASS(CLASS) \
44 class Shell##CLASS##Wrapper : public js::NativeObject { \
45 public: \
46 using Target = CLASS; \
47 enum ModuleSlot { TargetSlot = 0, SlotCount }; \
48 static const JSClass class_; \
49 static Shell##CLASS##Wrapper* create(JSContext* cx, \
50 JS::Handle<CLASS*> obj); \
51 CLASS* get(); \
52 }; \
53 DEFINE_CLASS_IMPL(CLASS)
54
55 DEFINE_CLASS(ModuleRequestObject)
DEFINE_CLASS(ImportEntryObject)56 DEFINE_CLASS(ImportEntryObject)
57 DEFINE_CLASS(ExportEntryObject)
58 DEFINE_CLASS(RequestedModuleObject)
59 // NOTE: We don't need wrapper for IndirectBindingMap and ModuleNamespaceObject
60 DEFINE_CLASS_IMPL(ModuleObject)
61
62 #undef DEFINE_CLASS
63 #undef DEFINE_CLASS_IMPL
64
65 bool IdentFilter(JSContext* cx, JS::Handle<JS::Value> from,
66 JS::MutableHandle<JS::Value> to) {
67 to.set(from);
68 return true;
69 }
70
71 template <class T>
SingleFilter(JSContext * cx,JS::Handle<JS::Value> from,JS::MutableHandle<JS::Value> to)72 bool SingleFilter(JSContext* cx, JS::Handle<JS::Value> from,
73 JS::MutableHandle<JS::Value> to) {
74 using TargetT = typename T::Target;
75
76 if (!from.isObject() || !from.toObject().is<TargetT>()) {
77 to.set(from);
78 return true;
79 }
80
81 JS::Rooted<TargetT*> obj(cx, &from.toObject().as<TargetT>());
82 JS::Rooted<T*> filtered(cx, T::create(cx, obj));
83 if (!filtered) {
84 return false;
85 }
86 to.setObject(*filtered);
87 return true;
88 }
89
90 template <class T>
ArrayFilter(JSContext * cx,JS::Handle<JS::Value> from,JS::MutableHandle<JS::Value> to)91 bool ArrayFilter(JSContext* cx, JS::Handle<JS::Value> from,
92 JS::MutableHandle<JS::Value> to) {
93 using TargetT = typename T::Target;
94
95 if (!from.isObject() || !from.toObject().is<ArrayObject>()) {
96 to.set(from);
97 return true;
98 }
99
100 JS::Rooted<ArrayObject*> fromArray(cx, &from.toObject().as<ArrayObject>());
101 uint32_t length = fromArray->length();
102 JS::Rooted<ArrayObject*> toArray(cx, NewDenseFullyAllocatedArray(cx, length));
103 if (!toArray) {
104 return false;
105 }
106
107 toArray->ensureDenseInitializedLength(0, length);
108
109 for (uint32_t i = 0; i < length; i++) {
110 JS::Rooted<JS::Value> item(cx, fromArray->getDenseElement(i));
111 JS::Rooted<TargetT*> req(cx, &item.toObject().as<TargetT>());
112 JS::Rooted<T*> filtered(cx, T::create(cx, req));
113 if (!filtered) {
114 return false;
115 }
116 toArray->initDenseElement(i, ObjectValue(*filtered));
117 }
118 to.setObject(*toArray);
119 return true;
120 }
121
122 template <class T>
ListToArrayFilter(JSContext * cx,JS::Handle<JS::Value> from,JS::MutableHandle<JS::Value> to)123 bool ListToArrayFilter(JSContext* cx, JS::Handle<JS::Value> from,
124 JS::MutableHandle<JS::Value> to) {
125 using TargetT = typename T::Target;
126
127 if (!from.isObject() || !from.toObject().is<ListObject>()) {
128 to.set(from);
129 return true;
130 }
131
132 JS::Rooted<ListObject*> fromList(cx, &from.toObject().as<ListObject>());
133 uint32_t length = fromList->length();
134 JS::Rooted<ArrayObject*> toArray(cx, NewDenseFullyAllocatedArray(cx, length));
135 if (!toArray) {
136 return false;
137 }
138
139 toArray->ensureDenseInitializedLength(0, length);
140
141 for (uint32_t i = 0; i < length; i++) {
142 JS::Rooted<JS::Value> item(cx, fromList->get(i));
143 JS::Rooted<TargetT*> req(cx, &item.toObject().as<TargetT>());
144 JS::Rooted<T*> filtered(cx, T::create(cx, req));
145 if (!filtered) {
146 return false;
147 }
148 toArray->initDenseElement(i, ObjectValue(*filtered));
149 }
150 to.setObject(*toArray);
151 return true;
152 }
153
154 template <class T, typename FilterT>
ShellModuleWrapperGetter(JSContext * cx,const JS::CallArgs & args,const char * prop,FilterT filter)155 bool ShellModuleWrapperGetter(JSContext* cx, const JS::CallArgs& args,
156 const char* prop, FilterT filter) {
157 using TargetT = typename T::Target;
158
159 JS::Rooted<TargetT*> obj(cx, args.thisv().toObject().as<T>().get());
160 JS::Rooted<JS::Value> rval(cx);
161 if (!JS_GetProperty(cx, obj, prop, &rval)) {
162 return false;
163 }
164
165 JS::Rooted<JS::Value> filtered(cx);
166 if (!filter(cx, rval, &filtered)) {
167 return false;
168 }
169 args.rval().set(filtered);
170 return true;
171 }
172
173 #define DEFINE_GETTER_FUNCTIONS(CLASS, PROP, FILTER) \
174 static bool Shell##CLASS##Wrapper_##PROP##Getter_impl( \
175 JSContext* cx, const JS::CallArgs& args) { \
176 return ShellModuleWrapperGetter<Shell##CLASS##Wrapper>(cx, args, #PROP, \
177 FILTER); \
178 } \
179 static bool Shell##CLASS##Wrapper_##PROP##Getter(JSContext* cx, \
180 unsigned argc, Value* vp) { \
181 JS::CallArgs args = JS::CallArgsFromVp(argc, vp); \
182 return CallNonGenericMethod<IsShell##CLASS##Wrapper, \
183 Shell##CLASS##Wrapper_##PROP##Getter_impl>( \
184 cx, args); \
185 }
186
187 DEFINE_GETTER_FUNCTIONS(ModuleRequestObject, specifier, IdentFilter)
188
189 static const JSPropertySpec ShellModuleRequestObjectWrapper_accessors[] = {
190 JS_PSG("specifier", ShellModuleRequestObjectWrapper_specifierGetter, 0),
191 JS_PS_END};
192
193 DEFINE_GETTER_FUNCTIONS(ImportEntryObject, moduleRequest,
194 SingleFilter<ShellModuleRequestObjectWrapper>)
195 DEFINE_GETTER_FUNCTIONS(ImportEntryObject, importName, IdentFilter)
196 DEFINE_GETTER_FUNCTIONS(ImportEntryObject, localName, IdentFilter)
197 DEFINE_GETTER_FUNCTIONS(ImportEntryObject, lineNumber, IdentFilter)
198 DEFINE_GETTER_FUNCTIONS(ImportEntryObject, columnNumber, IdentFilter)
199
200 static const JSPropertySpec ShellImportEntryObjectWrapper_accessors[] = {
201 JS_PSG("moduleRequest", ShellImportEntryObjectWrapper_moduleRequestGetter,
202 0),
203 JS_PSG("importName", ShellImportEntryObjectWrapper_importNameGetter, 0),
204 JS_PSG("localName", ShellImportEntryObjectWrapper_localNameGetter, 0),
205 JS_PSG("lineNumber", ShellImportEntryObjectWrapper_lineNumberGetter, 0),
206 JS_PSG("columnNumber", ShellImportEntryObjectWrapper_columnNumberGetter, 0),
207 JS_PS_END};
208
209 DEFINE_GETTER_FUNCTIONS(ExportEntryObject, exportName, IdentFilter)
210 DEFINE_GETTER_FUNCTIONS(ExportEntryObject, moduleRequest,
211 SingleFilter<ShellModuleRequestObjectWrapper>)
212 DEFINE_GETTER_FUNCTIONS(ExportEntryObject, importName, IdentFilter)
213 DEFINE_GETTER_FUNCTIONS(ExportEntryObject, localName, IdentFilter)
214 DEFINE_GETTER_FUNCTIONS(ExportEntryObject, lineNumber, IdentFilter)
215 DEFINE_GETTER_FUNCTIONS(ExportEntryObject, columnNumber, IdentFilter)
216
217 static const JSPropertySpec ShellExportEntryObjectWrapper_accessors[] = {
218 JS_PSG("exportName", ShellExportEntryObjectWrapper_exportNameGetter, 0),
219 JS_PSG("moduleRequest", ShellExportEntryObjectWrapper_moduleRequestGetter,
220 0),
221 JS_PSG("importName", ShellExportEntryObjectWrapper_importNameGetter, 0),
222 JS_PSG("localName", ShellExportEntryObjectWrapper_localNameGetter, 0),
223 JS_PSG("lineNumber", ShellExportEntryObjectWrapper_lineNumberGetter, 0),
224 JS_PSG("columnNumber", ShellExportEntryObjectWrapper_columnNumberGetter, 0),
225 JS_PS_END};
226
227 DEFINE_GETTER_FUNCTIONS(RequestedModuleObject, moduleRequest,
228 SingleFilter<ShellModuleRequestObjectWrapper>)
229 DEFINE_GETTER_FUNCTIONS(RequestedModuleObject, lineNumber, IdentFilter)
230 DEFINE_GETTER_FUNCTIONS(RequestedModuleObject, columnNumber, IdentFilter)
231
232 static const JSPropertySpec ShellRequestedModuleObjectWrapper_accessors[] = {
233 JS_PSG("moduleRequest",
234 ShellRequestedModuleObjectWrapper_moduleRequestGetter, 0),
235 JS_PSG("lineNumber", ShellRequestedModuleObjectWrapper_lineNumberGetter, 0),
236 JS_PSG("columnNumber", ShellRequestedModuleObjectWrapper_columnNumberGetter,
237 0),
238 JS_PS_END};
239
240 DEFINE_GETTER_FUNCTIONS(ModuleObject, namespace, IdentFilter)
241 DEFINE_GETTER_FUNCTIONS(ModuleObject, status, IdentFilter)
242 DEFINE_GETTER_FUNCTIONS(ModuleObject, evaluationError, IdentFilter)
243 DEFINE_GETTER_FUNCTIONS(ModuleObject, requestedModules,
244 ArrayFilter<ShellRequestedModuleObjectWrapper>)
245 DEFINE_GETTER_FUNCTIONS(ModuleObject, importEntries,
246 ArrayFilter<ShellImportEntryObjectWrapper>)
247 DEFINE_GETTER_FUNCTIONS(ModuleObject, localExportEntries,
248 ArrayFilter<ShellExportEntryObjectWrapper>)
249 DEFINE_GETTER_FUNCTIONS(ModuleObject, indirectExportEntries,
250 ArrayFilter<ShellExportEntryObjectWrapper>)
251 DEFINE_GETTER_FUNCTIONS(ModuleObject, starExportEntries,
252 ArrayFilter<ShellExportEntryObjectWrapper>)
253 DEFINE_GETTER_FUNCTIONS(ModuleObject, dfsIndex, IdentFilter)
254 DEFINE_GETTER_FUNCTIONS(ModuleObject, dfsAncestorIndex, IdentFilter)
255 DEFINE_GETTER_FUNCTIONS(ModuleObject, async, IdentFilter)
256 DEFINE_GETTER_FUNCTIONS(ModuleObject, topLevelCapability, IdentFilter)
257 DEFINE_GETTER_FUNCTIONS(ModuleObject, asyncEvaluatingPostOrder, IdentFilter)
258 DEFINE_GETTER_FUNCTIONS(ModuleObject, asyncParentModules,
259 ListToArrayFilter<ShellModuleObjectWrapper>)
260 DEFINE_GETTER_FUNCTIONS(ModuleObject, pendingAsyncDependencies, IdentFilter)
261
262 static const JSPropertySpec ShellModuleObjectWrapper_accessors[] = {
263 JS_PSG("namespace", ShellModuleObjectWrapper_namespaceGetter, 0),
264 JS_PSG("status", ShellModuleObjectWrapper_statusGetter, 0),
265 JS_PSG("evaluationError", ShellModuleObjectWrapper_evaluationErrorGetter,
266 0),
267 JS_PSG("requestedModules", ShellModuleObjectWrapper_requestedModulesGetter,
268 0),
269 JS_PSG("importEntries", ShellModuleObjectWrapper_importEntriesGetter, 0),
270 JS_PSG("localExportEntries",
271 ShellModuleObjectWrapper_localExportEntriesGetter, 0),
272 JS_PSG("indirectExportEntries",
273 ShellModuleObjectWrapper_indirectExportEntriesGetter, 0),
274 JS_PSG("starExportEntries",
275 ShellModuleObjectWrapper_starExportEntriesGetter, 0),
276 JS_PSG("dfsIndex", ShellModuleObjectWrapper_dfsIndexGetter, 0),
277 JS_PSG("dfsAncestorIndex", ShellModuleObjectWrapper_dfsAncestorIndexGetter,
278 0),
279 JS_PSG("async", ShellModuleObjectWrapper_asyncGetter, 0),
280 JS_PSG("topLevelCapability",
281 ShellModuleObjectWrapper_topLevelCapabilityGetter, 0),
282 JS_PSG("asyncEvaluatingPostOrder",
283 ShellModuleObjectWrapper_asyncEvaluatingPostOrderGetter, 0),
284 JS_PSG("asyncParentModules",
285 ShellModuleObjectWrapper_asyncParentModulesGetter, 0),
286 JS_PSG("pendingAsyncDependencies",
287 ShellModuleObjectWrapper_pendingAsyncDependenciesGetter, 0),
288 JS_PS_END};
289
290 #undef DEFINE_GETTER_FUNCTIONS
291
292 template <class T, size_t ARGC, typename CheckArgsT, typename FilterT>
ShellModuleWrapperMethod(JSContext * cx,const JS::CallArgs & args,const char * prop,CheckArgsT checkArgs,FilterT filter)293 bool ShellModuleWrapperMethod(JSContext* cx, const JS::CallArgs& args,
294 const char* prop, CheckArgsT checkArgs,
295 FilterT filter) {
296 using TargetT = typename T::Target;
297
298 JS::Rooted<TargetT*> obj(cx, args.thisv().toObject().as<T>().get());
299 JS::Rooted<JS::Value> methodVal(cx);
300 if (!JS_GetProperty(cx, obj, prop, &methodVal)) {
301 return false;
302 }
303 if (!methodVal.isObject() || !methodVal.toObject().is<JSFunction>()) {
304 JS_ReportErrorASCII(cx, "method property is not function");
305 return false;
306 }
307
308 JS::Rooted<JSFunction*> method(cx, &methodVal.toObject().as<JSFunction>());
309 if (!checkArgs(cx, args)) {
310 return false;
311 }
312
313 FixedInvokeArgs<ARGC> invokeArgs(cx);
314 for (size_t i = 0; i < ARGC; i++) {
315 invokeArgs[i].set(args.get(i));
316 }
317
318 JS::Rooted<JS::Value> rval(cx);
319 if (!JS::Call(cx, obj, method, invokeArgs, &rval)) {
320 return false;
321 }
322
323 JS::Rooted<JS::Value> filtered(cx);
324 if (!filter(cx, rval, &filtered)) {
325 return false;
326 }
327 args.rval().set(filtered);
328 return true;
329 }
330
331 #define DEFINE_METHOD_FUNCTIONS(CLASS, METHOD, ARGC, CHECK, FILTER) \
332 static bool Shell##CLASS##Wrapper_##METHOD##_impl( \
333 JSContext* cx, const JS::CallArgs& args) { \
334 return ShellModuleWrapperMethod<Shell##CLASS##Wrapper, ARGC>( \
335 cx, args, #METHOD, CHECK, FILTER); \
336 } \
337 static bool Shell##CLASS##Wrapper##_##METHOD(JSContext* cx, unsigned argc, \
338 Value* vp) { \
339 JS::CallArgs args = JS::CallArgsFromVp(argc, vp); \
340 return CallNonGenericMethod<Is##Shell##CLASS##Wrapper, \
341 Shell##CLASS##Wrapper_##METHOD##_impl>(cx, \
342 args); \
343 }
344
CheckNoArgs(JSContext * cx,const CallArgs & args)345 static bool CheckNoArgs(JSContext* cx, const CallArgs& args) { return true; }
346
347 DEFINE_METHOD_FUNCTIONS(ModuleObject, declarationInstantiation, 0, CheckNoArgs,
348 IdentFilter)
349 DEFINE_METHOD_FUNCTIONS(ModuleObject, evaluation, 0, CheckNoArgs, IdentFilter)
350
351 static const JSFunctionSpec ShellModuleObjectWrapper_functions[] = {
352 JS_FN("declarationInstantiation",
353 ShellModuleObjectWrapper_declarationInstantiation, 0, 0),
354 JS_FN("evaluation", ShellModuleObjectWrapper_evaluation, 0, 0), JS_FS_END};
355
356 #undef DEFINE_METHOD_FUNCTIONS
357
358 #define DEFINE_CREATE(CLASS, ACCESSORS, FUNCTIONS) \
359 /* static */ \
360 Shell##CLASS##Wrapper* Shell##CLASS##Wrapper::create( \
361 JSContext* cx, JS::Handle<CLASS*> obj) { \
362 JS::Rooted<JSObject*> wrapper(cx, JS_NewObject(cx, &class_)); \
363 if (!wrapper) { \
364 return nullptr; \
365 } \
366 if (!DefinePropertiesAndFunctions(cx, wrapper, ACCESSORS, FUNCTIONS)) { \
367 return nullptr; \
368 } \
369 wrapper->as<Shell##CLASS##Wrapper>().initReservedSlot(TargetSlot, \
370 ObjectValue(*obj)); \
371 return &wrapper->as<Shell##CLASS##Wrapper>(); \
372 }
373
374 DEFINE_CREATE(ModuleRequestObject, ShellModuleRequestObjectWrapper_accessors,
375 nullptr)
376 DEFINE_CREATE(ImportEntryObject, ShellImportEntryObjectWrapper_accessors,
377 nullptr)
378 DEFINE_CREATE(ExportEntryObject, ShellExportEntryObjectWrapper_accessors,
379 nullptr)
380 DEFINE_CREATE(RequestedModuleObject,
381 ShellRequestedModuleObjectWrapper_accessors, nullptr)
382 DEFINE_CREATE(ModuleObject, ShellModuleObjectWrapper_accessors,
383 ShellModuleObjectWrapper_functions)
384
385 #undef DEFINE_CREATE
386