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