Source code

Revision control

Other Tools

1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2
* vim: set ts=8 sts=2 et sw=2 tw=80:
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 "builtin/ModuleObject.h"
8
9
#include "mozilla/DebugOnly.h"
10
#include "mozilla/EnumSet.h"
11
#include "mozilla/ScopeExit.h"
12
13
#include "builtin/Promise.h"
14
#include "builtin/SelfHostingDefines.h"
15
#include "frontend/ParseNode.h"
16
#include "frontend/SharedContext.h"
17
#include "gc/FreeOp.h"
18
#include "gc/Policy.h"
19
#include "gc/Tracer.h"
20
#include "js/Modules.h" // JS::GetModulePrivate, JS::ModuleDynamicImportHook
21
#include "js/PropertySpec.h"
22
#include "vm/AsyncFunction.h"
23
#include "vm/AsyncIteration.h"
24
#include "vm/EqualityOperations.h" // js::SameValue
25
#include "vm/ModuleBuilder.h" // js::ModuleBuilder
26
#include "vm/SelfHosting.h"
27
28
#include "vm/JSObject-inl.h"
29
#include "vm/JSScript-inl.h"
30
31
using namespace js;
32
33
static_assert(MODULE_STATUS_UNINSTANTIATED < MODULE_STATUS_INSTANTIATING &&
34
MODULE_STATUS_INSTANTIATING < MODULE_STATUS_INSTANTIATED &&
35
MODULE_STATUS_INSTANTIATED < MODULE_STATUS_EVALUATED &&
36
MODULE_STATUS_EVALUATED < MODULE_STATUS_EVALUATED_ERROR,
37
"Module statuses are ordered incorrectly");
38
39
template <typename T, Value ValueGetter(const T* obj)>
40
static bool ModuleValueGetterImpl(JSContext* cx, const CallArgs& args) {
41
args.rval().set(ValueGetter(&args.thisv().toObject().as<T>()));
42
return true;
43
}
44
45
template <typename T, Value ValueGetter(const T* obj)>
46
static bool ModuleValueGetter(JSContext* cx, unsigned argc, Value* vp) {
47
CallArgs args = CallArgsFromVp(argc, vp);
48
return CallNonGenericMethod<T::isInstance,
49
ModuleValueGetterImpl<T, ValueGetter>>(cx, args);
50
}
51
52
#define DEFINE_GETTER_FUNCTIONS(cls, name, slot) \
53
static Value cls##_##name##Value(const cls* obj) { \
54
return obj->getReservedSlot(cls::slot); \
55
} \
56
\
57
static bool cls##_##name##Getter(JSContext* cx, unsigned argc, Value* vp) { \
58
return ModuleValueGetter<cls, cls##_##name##Value>(cx, argc, vp); \
59
}
60
61
#define DEFINE_ATOM_ACCESSOR_METHOD(cls, name) \
62
JSAtom* cls::name() const { \
63
Value value = cls##_##name##Value(this); \
64
return &value.toString()->asAtom(); \
65
}
66
67
#define DEFINE_ATOM_OR_NULL_ACCESSOR_METHOD(cls, name) \
68
JSAtom* cls::name() const { \
69
Value value = cls##_##name##Value(this); \
70
if (value.isNull()) return nullptr; \
71
return &value.toString()->asAtom(); \
72
}
73
74
#define DEFINE_UINT32_ACCESSOR_METHOD(cls, name) \
75
uint32_t cls::name() const { \
76
Value value = cls##_##name##Value(this); \
77
MOZ_ASSERT(value.toNumber() >= 0); \
78
if (value.isInt32()) return value.toInt32(); \
79
return JS::ToUint32(value.toDouble()); \
80
}
81
82
///////////////////////////////////////////////////////////////////////////
83
// ImportEntryObject
84
85
/* static */ const JSClass ImportEntryObject::class_ = {
86
"ImportEntry", JSCLASS_HAS_RESERVED_SLOTS(ImportEntryObject::SlotCount)};
87
88
DEFINE_GETTER_FUNCTIONS(ImportEntryObject, moduleRequest, ModuleRequestSlot)
89
DEFINE_GETTER_FUNCTIONS(ImportEntryObject, importName, ImportNameSlot)
90
DEFINE_GETTER_FUNCTIONS(ImportEntryObject, localName, LocalNameSlot)
91
DEFINE_GETTER_FUNCTIONS(ImportEntryObject, lineNumber, LineNumberSlot)
92
DEFINE_GETTER_FUNCTIONS(ImportEntryObject, columnNumber, ColumnNumberSlot)
93
94
DEFINE_ATOM_ACCESSOR_METHOD(ImportEntryObject, moduleRequest)
95
DEFINE_ATOM_ACCESSOR_METHOD(ImportEntryObject, importName)
96
DEFINE_ATOM_ACCESSOR_METHOD(ImportEntryObject, localName)
97
DEFINE_UINT32_ACCESSOR_METHOD(ImportEntryObject, lineNumber)
98
DEFINE_UINT32_ACCESSOR_METHOD(ImportEntryObject, columnNumber)
99
100
/* static */
101
bool ImportEntryObject::isInstance(HandleValue value) {
102
return value.isObject() && value.toObject().is<ImportEntryObject>();
103
}
104
105
/* static */
106
bool GlobalObject::initImportEntryProto(JSContext* cx,
107
Handle<GlobalObject*> global) {
108
static const JSPropertySpec protoAccessors[] = {
109
JS_PSG("moduleRequest", ImportEntryObject_moduleRequestGetter, 0),
110
JS_PSG("importName", ImportEntryObject_importNameGetter, 0),
111
JS_PSG("localName", ImportEntryObject_localNameGetter, 0),
112
JS_PSG("lineNumber", ImportEntryObject_lineNumberGetter, 0),
113
JS_PSG("columnNumber", ImportEntryObject_columnNumberGetter, 0),
114
JS_PS_END};
115
116
RootedObject proto(
117
cx, GlobalObject::createBlankPrototype<PlainObject>(cx, global));
118
if (!proto) {
119
return false;
120
}
121
122
if (!DefinePropertiesAndFunctions(cx, proto, protoAccessors, nullptr)) {
123
return false;
124
}
125
126
global->initReservedSlot(IMPORT_ENTRY_PROTO, ObjectValue(*proto));
127
return true;
128
}
129
130
/* static */
131
ImportEntryObject* ImportEntryObject::create(
132
JSContext* cx, HandleAtom moduleRequest, HandleAtom importName,
133
HandleAtom localName, uint32_t lineNumber, uint32_t columnNumber) {
134
RootedObject proto(
135
cx, GlobalObject::getOrCreateImportEntryPrototype(cx, cx->global()));
136
if (!proto) {
137
return nullptr;
138
}
139
140
ImportEntryObject* self =
141
NewObjectWithGivenProto<ImportEntryObject>(cx, proto);
142
if (!self) {
143
return nullptr;
144
}
145
146
self->initReservedSlot(ModuleRequestSlot, StringValue(moduleRequest));
147
self->initReservedSlot(ImportNameSlot, StringValue(importName));
148
self->initReservedSlot(LocalNameSlot, StringValue(localName));
149
self->initReservedSlot(LineNumberSlot, NumberValue(lineNumber));
150
self->initReservedSlot(ColumnNumberSlot, NumberValue(columnNumber));
151
return self;
152
}
153
154
///////////////////////////////////////////////////////////////////////////
155
// ExportEntryObject
156
157
/* static */ const JSClass ExportEntryObject::class_ = {
158
"ExportEntry", JSCLASS_HAS_RESERVED_SLOTS(ExportEntryObject::SlotCount)};
159
160
DEFINE_GETTER_FUNCTIONS(ExportEntryObject, exportName, ExportNameSlot)
161
DEFINE_GETTER_FUNCTIONS(ExportEntryObject, moduleRequest, ModuleRequestSlot)
162
DEFINE_GETTER_FUNCTIONS(ExportEntryObject, importName, ImportNameSlot)
163
DEFINE_GETTER_FUNCTIONS(ExportEntryObject, localName, LocalNameSlot)
164
DEFINE_GETTER_FUNCTIONS(ExportEntryObject, lineNumber, LineNumberSlot)
165
DEFINE_GETTER_FUNCTIONS(ExportEntryObject, columnNumber, ColumnNumberSlot)
166
167
DEFINE_ATOM_OR_NULL_ACCESSOR_METHOD(ExportEntryObject, exportName)
168
DEFINE_ATOM_OR_NULL_ACCESSOR_METHOD(ExportEntryObject, moduleRequest)
169
DEFINE_ATOM_OR_NULL_ACCESSOR_METHOD(ExportEntryObject, importName)
170
DEFINE_ATOM_OR_NULL_ACCESSOR_METHOD(ExportEntryObject, localName)
171
DEFINE_UINT32_ACCESSOR_METHOD(ExportEntryObject, lineNumber)
172
DEFINE_UINT32_ACCESSOR_METHOD(ExportEntryObject, columnNumber)
173
174
/* static */
175
bool ExportEntryObject::isInstance(HandleValue value) {
176
return value.isObject() && value.toObject().is<ExportEntryObject>();
177
}
178
179
/* static */
180
bool GlobalObject::initExportEntryProto(JSContext* cx,
181
Handle<GlobalObject*> global) {
182
static const JSPropertySpec protoAccessors[] = {
183
JS_PSG("exportName", ExportEntryObject_exportNameGetter, 0),
184
JS_PSG("moduleRequest", ExportEntryObject_moduleRequestGetter, 0),
185
JS_PSG("importName", ExportEntryObject_importNameGetter, 0),
186
JS_PSG("localName", ExportEntryObject_localNameGetter, 0),
187
JS_PSG("lineNumber", ExportEntryObject_lineNumberGetter, 0),
188
JS_PSG("columnNumber", ExportEntryObject_columnNumberGetter, 0),
189
JS_PS_END};
190
191
RootedObject proto(
192
cx, GlobalObject::createBlankPrototype<PlainObject>(cx, global));
193
if (!proto) {
194
return false;
195
}
196
197
if (!DefinePropertiesAndFunctions(cx, proto, protoAccessors, nullptr)) {
198
return false;
199
}
200
201
global->initReservedSlot(EXPORT_ENTRY_PROTO, ObjectValue(*proto));
202
return true;
203
}
204
205
static Value StringOrNullValue(JSString* maybeString) {
206
return maybeString ? StringValue(maybeString) : NullValue();
207
}
208
209
/* static */
210
ExportEntryObject* ExportEntryObject::create(
211
JSContext* cx, HandleAtom maybeExportName, HandleAtom maybeModuleRequest,
212
HandleAtom maybeImportName, HandleAtom maybeLocalName, uint32_t lineNumber,
213
uint32_t columnNumber) {
214
// Line and column numbers are optional for export entries since direct
215
// entries are checked at parse time.
216
217
RootedObject proto(
218
cx, GlobalObject::getOrCreateExportEntryPrototype(cx, cx->global()));
219
if (!proto) {
220
return nullptr;
221
}
222
223
ExportEntryObject* self =
224
NewObjectWithGivenProto<ExportEntryObject>(cx, proto);
225
if (!self) {
226
return nullptr;
227
}
228
229
self->initReservedSlot(ExportNameSlot, StringOrNullValue(maybeExportName));
230
self->initReservedSlot(ModuleRequestSlot,
231
StringOrNullValue(maybeModuleRequest));
232
self->initReservedSlot(ImportNameSlot, StringOrNullValue(maybeImportName));
233
self->initReservedSlot(LocalNameSlot, StringOrNullValue(maybeLocalName));
234
self->initReservedSlot(LineNumberSlot, NumberValue(lineNumber));
235
self->initReservedSlot(ColumnNumberSlot, NumberValue(columnNumber));
236
return self;
237
}
238
239
///////////////////////////////////////////////////////////////////////////
240
// RequestedModuleObject
241
242
/* static */ const JSClass RequestedModuleObject::class_ = {
243
"RequestedModule",
244
JSCLASS_HAS_RESERVED_SLOTS(RequestedModuleObject::SlotCount)};
245
246
DEFINE_GETTER_FUNCTIONS(RequestedModuleObject, moduleSpecifier,
247
ModuleSpecifierSlot)
248
DEFINE_GETTER_FUNCTIONS(RequestedModuleObject, lineNumber, LineNumberSlot)
249
DEFINE_GETTER_FUNCTIONS(RequestedModuleObject, columnNumber, ColumnNumberSlot)
250
251
DEFINE_ATOM_ACCESSOR_METHOD(RequestedModuleObject, moduleSpecifier)
252
DEFINE_UINT32_ACCESSOR_METHOD(RequestedModuleObject, lineNumber)
253
DEFINE_UINT32_ACCESSOR_METHOD(RequestedModuleObject, columnNumber)
254
255
/* static */
256
bool RequestedModuleObject::isInstance(HandleValue value) {
257
return value.isObject() && value.toObject().is<RequestedModuleObject>();
258
}
259
260
/* static */
261
bool GlobalObject::initRequestedModuleProto(JSContext* cx,
262
Handle<GlobalObject*> global) {
263
static const JSPropertySpec protoAccessors[] = {
264
JS_PSG("moduleSpecifier", RequestedModuleObject_moduleSpecifierGetter, 0),
265
JS_PSG("lineNumber", RequestedModuleObject_lineNumberGetter, 0),
266
JS_PSG("columnNumber", RequestedModuleObject_columnNumberGetter, 0),
267
JS_PS_END};
268
269
RootedObject proto(
270
cx, GlobalObject::createBlankPrototype<PlainObject>(cx, global));
271
if (!proto) {
272
return false;
273
}
274
275
if (!DefinePropertiesAndFunctions(cx, proto, protoAccessors, nullptr)) {
276
return false;
277
}
278
279
global->initReservedSlot(REQUESTED_MODULE_PROTO, ObjectValue(*proto));
280
return true;
281
}
282
283
/* static */
284
RequestedModuleObject* RequestedModuleObject::create(JSContext* cx,
285
HandleAtom moduleSpecifier,
286
uint32_t lineNumber,
287
uint32_t columnNumber) {
288
RootedObject proto(
289
cx, GlobalObject::getOrCreateRequestedModulePrototype(cx, cx->global()));
290
if (!proto) {
291
return nullptr;
292
}
293
294
RequestedModuleObject* self =
295
NewObjectWithGivenProto<RequestedModuleObject>(cx, proto);
296
if (!self) {
297
return nullptr;
298
}
299
300
self->initReservedSlot(ModuleSpecifierSlot, StringValue(moduleSpecifier));
301
self->initReservedSlot(LineNumberSlot, NumberValue(lineNumber));
302
self->initReservedSlot(ColumnNumberSlot, NumberValue(columnNumber));
303
return self;
304
}
305
306
///////////////////////////////////////////////////////////////////////////
307
// IndirectBindingMap
308
309
IndirectBindingMap::Binding::Binding(ModuleEnvironmentObject* environment,
310
Shape* shape)
311
: environment(environment), shape(shape) {}
312
313
void IndirectBindingMap::trace(JSTracer* trc) {
314
if (!map_) {
315
return;
316
}
317
318
for (Map::Enum e(*map_); !e.empty(); e.popFront()) {
319
Binding& b = e.front().value();
320
TraceEdge(trc, &b.environment, "module bindings environment");
321
TraceEdge(trc, &b.shape, "module bindings shape");
322
mozilla::DebugOnly<jsid> prev(e.front().key());
323
TraceEdge(trc, &e.front().mutableKey(), "module bindings binding name");
324
MOZ_ASSERT(e.front().key() == prev);
325
}
326
}
327
328
bool IndirectBindingMap::put(JSContext* cx, HandleId name,
329
HandleModuleEnvironmentObject environment,
330
HandleId localName) {
331
// This object might have been allocated on the background parsing thread in
332
// different zone to the final module. Lazily allocate the map so we don't
333
// have to switch its zone when merging realms.
334
if (!map_) {
335
MOZ_ASSERT(!cx->zone()->createdForHelperThread());
336
map_.emplace(cx->zone());
337
}
338
339
RootedShape shape(cx, environment->lookup(cx, localName));
340
MOZ_ASSERT(shape);
341
if (!map_->put(name, Binding(environment, shape))) {
342
ReportOutOfMemory(cx);
343
return false;
344
}
345
346
return true;
347
}
348
349
bool IndirectBindingMap::lookup(jsid name, ModuleEnvironmentObject** envOut,
350
Shape** shapeOut) const {
351
if (!map_) {
352
return false;
353
}
354
355
auto ptr = map_->lookup(name);
356
if (!ptr) {
357
return false;
358
}
359
360
const Binding& binding = ptr->value();
361
MOZ_ASSERT(binding.environment);
362
MOZ_ASSERT(!binding.environment->inDictionaryMode());
363
MOZ_ASSERT(binding.environment->containsPure(binding.shape));
364
*envOut = binding.environment;
365
*shapeOut = binding.shape;
366
return true;
367
}
368
369
///////////////////////////////////////////////////////////////////////////
370
// ModuleNamespaceObject
371
372
/* static */
373
const ModuleNamespaceObject::ProxyHandler ModuleNamespaceObject::proxyHandler;
374
375
/* static */
376
bool ModuleNamespaceObject::isInstance(HandleValue value) {
377
return value.isObject() && value.toObject().is<ModuleNamespaceObject>();
378
}
379
380
/* static */
381
ModuleNamespaceObject* ModuleNamespaceObject::create(
382
JSContext* cx, HandleModuleObject module, HandleObject exports,
383
UniquePtr<IndirectBindingMap> bindings) {
384
RootedValue priv(cx, ObjectValue(*module));
385
ProxyOptions options;
386
options.setLazyProto(true);
387
options.setSingleton(true);
388
Rooted<UniquePtr<IndirectBindingMap>> rootedBindings(cx, std::move(bindings));
389
RootedObject object(
390
cx, NewProxyObject(cx, &proxyHandler, priv, nullptr, options));
391
if (!object) {
392
return nullptr;
393
}
394
395
SetProxyReservedSlot(object, ExportsSlot, ObjectValue(*exports));
396
SetProxyReservedSlot(object, BindingsSlot,
397
PrivateValue(rootedBindings.release()));
398
AddCellMemory(object, sizeof(IndirectBindingMap),
399
MemoryUse::ModuleBindingMap);
400
401
return &object->as<ModuleNamespaceObject>();
402
}
403
404
ModuleObject& ModuleNamespaceObject::module() {
405
return GetProxyPrivate(this).toObject().as<ModuleObject>();
406
}
407
408
JSObject& ModuleNamespaceObject::exports() {
409
return GetProxyReservedSlot(this, ExportsSlot).toObject();
410
}
411
412
IndirectBindingMap& ModuleNamespaceObject::bindings() {
413
Value value = GetProxyReservedSlot(this, BindingsSlot);
414
auto bindings = static_cast<IndirectBindingMap*>(value.toPrivate());
415
MOZ_ASSERT(bindings);
416
return *bindings;
417
}
418
419
bool ModuleNamespaceObject::hasBindings() const {
420
// Import bindings may not be present if we hit OOM in initialization.
421
return !GetProxyReservedSlot(this, BindingsSlot).isUndefined();
422
}
423
424
bool ModuleNamespaceObject::addBinding(JSContext* cx, HandleAtom exportedName,
425
HandleModuleObject targetModule,
426
HandleAtom localName) {
427
RootedModuleEnvironmentObject environment(
428
cx, &targetModule->initialEnvironment());
429
RootedId exportedNameId(cx, AtomToId(exportedName));
430
RootedId localNameId(cx, AtomToId(localName));
431
return bindings().put(cx, exportedNameId, environment, localNameId);
432
}
433
434
const char ModuleNamespaceObject::ProxyHandler::family = 0;
435
436
ModuleNamespaceObject::ProxyHandler::ProxyHandler()
437
: BaseProxyHandler(&family, false) {}
438
439
bool ModuleNamespaceObject::ProxyHandler::getPrototype(
440
JSContext* cx, HandleObject proxy, MutableHandleObject protop) const {
441
protop.set(nullptr);
442
return true;
443
}
444
445
bool ModuleNamespaceObject::ProxyHandler::setPrototype(
446
JSContext* cx, HandleObject proxy, HandleObject proto,
447
ObjectOpResult& result) const {
448
if (!proto) {
449
return result.succeed();
450
}
451
return result.failCantSetProto();
452
}
453
454
bool ModuleNamespaceObject::ProxyHandler::getPrototypeIfOrdinary(
455
JSContext* cx, HandleObject proxy, bool* isOrdinary,
456
MutableHandleObject protop) const {
457
*isOrdinary = false;
458
return true;
459
}
460
461
bool ModuleNamespaceObject::ProxyHandler::setImmutablePrototype(
462
JSContext* cx, HandleObject proxy, bool* succeeded) const {
463
*succeeded = true;
464
return true;
465
}
466
467
bool ModuleNamespaceObject::ProxyHandler::isExtensible(JSContext* cx,
468
HandleObject proxy,
469
bool* extensible) const {
470
*extensible = false;
471
return true;
472
}
473
474
bool ModuleNamespaceObject::ProxyHandler::preventExtensions(
475
JSContext* cx, HandleObject proxy, ObjectOpResult& result) const {
476
result.succeed();
477
return true;
478
}
479
480
bool ModuleNamespaceObject::ProxyHandler::getOwnPropertyDescriptor(
481
JSContext* cx, HandleObject proxy, HandleId id,
482
MutableHandle<PropertyDescriptor> desc) const {
483
Rooted<ModuleNamespaceObject*> ns(cx, &proxy->as<ModuleNamespaceObject>());
484
if (JSID_IS_SYMBOL(id)) {
485
if (JSID_TO_SYMBOL(id) == cx->wellKnownSymbols().toStringTag) {
486
RootedValue value(cx, StringValue(cx->names().Module));
487
desc.object().set(proxy);
488
desc.setWritable(false);
489
desc.setEnumerable(false);
490
desc.setConfigurable(false);
491
desc.setValue(value);
492
return true;
493
}
494
495
return true;
496
}
497
498
const IndirectBindingMap& bindings = ns->bindings();
499
ModuleEnvironmentObject* env;
500
Shape* shape;
501
if (!bindings.lookup(id, &env, &shape)) {
502
return true;
503
}
504
505
RootedValue value(cx, env->getSlot(shape->slot()));
506
if (value.isMagic(JS_UNINITIALIZED_LEXICAL)) {
507
ReportRuntimeLexicalError(cx, JSMSG_UNINITIALIZED_LEXICAL, id);
508
return false;
509
}
510
511
desc.object().set(env);
512
desc.setConfigurable(false);
513
desc.setEnumerable(true);
514
desc.setValue(value);
515
return true;
516
}
517
518
static bool ValidatePropertyDescriptor(
519
JSContext* cx, Handle<PropertyDescriptor> desc, bool expectedWritable,
520
bool expectedEnumerable, bool expectedConfigurable,
521
HandleValue expectedValue, ObjectOpResult& result) {
522
if (desc.isAccessorDescriptor()) {
523
return result.fail(JSMSG_CANT_REDEFINE_PROP);
524
}
525
526
if (desc.hasWritable() && desc.writable() != expectedWritable) {
527
return result.fail(JSMSG_CANT_REDEFINE_PROP);
528
}
529
530
if (desc.hasEnumerable() && desc.enumerable() != expectedEnumerable) {
531
return result.fail(JSMSG_CANT_REDEFINE_PROP);
532
}
533
534
if (desc.hasConfigurable() && desc.configurable() != expectedConfigurable) {
535
return result.fail(JSMSG_CANT_REDEFINE_PROP);
536
}
537
538
if (desc.hasValue()) {
539
bool same;
540
if (!SameValue(cx, desc.value(), expectedValue, &same)) {
541
return false;
542
}
543
if (!same) {
544
return result.fail(JSMSG_CANT_REDEFINE_PROP);
545
}
546
}
547
548
return result.succeed();
549
}
550
551
bool ModuleNamespaceObject::ProxyHandler::defineProperty(
552
JSContext* cx, HandleObject proxy, HandleId id,
553
Handle<PropertyDescriptor> desc, ObjectOpResult& result) const {
554
if (JSID_IS_SYMBOL(id)) {
555
if (JSID_TO_SYMBOL(id) == cx->wellKnownSymbols().toStringTag) {
556
RootedValue value(cx, StringValue(cx->names().Module));
557
return ValidatePropertyDescriptor(cx, desc, false, false, false, value,
558
result);
559
}
560
return result.fail(JSMSG_CANT_DEFINE_PROP_OBJECT_NOT_EXTENSIBLE);
561
}
562
563
const IndirectBindingMap& bindings =
564
proxy->as<ModuleNamespaceObject>().bindings();
565
ModuleEnvironmentObject* env;
566
Shape* shape;
567
if (!bindings.lookup(id, &env, &shape)) {
568
return result.fail(JSMSG_CANT_DEFINE_PROP_OBJECT_NOT_EXTENSIBLE);
569
}
570
571
RootedValue value(cx, env->getSlot(shape->slot()));
572
if (value.isMagic(JS_UNINITIALIZED_LEXICAL)) {
573
ReportRuntimeLexicalError(cx, JSMSG_UNINITIALIZED_LEXICAL, id);
574
return false;
575
}
576
577
return ValidatePropertyDescriptor(cx, desc, true, true, false, value, result);
578
}
579
580
bool ModuleNamespaceObject::ProxyHandler::has(JSContext* cx, HandleObject proxy,
581
HandleId id, bool* bp) const {
582
Rooted<ModuleNamespaceObject*> ns(cx, &proxy->as<ModuleNamespaceObject>());
583
if (JSID_IS_SYMBOL(id)) {
584
*bp = JSID_TO_SYMBOL(id) == cx->wellKnownSymbols().toStringTag;
585
return true;
586
}
587
588
*bp = ns->bindings().has(id);
589
return true;
590
}
591
592
bool ModuleNamespaceObject::ProxyHandler::get(JSContext* cx, HandleObject proxy,
593
HandleValue receiver, HandleId id,
594
MutableHandleValue vp) const {
595
Rooted<ModuleNamespaceObject*> ns(cx, &proxy->as<ModuleNamespaceObject>());
596
if (JSID_IS_SYMBOL(id)) {
597
if (JSID_TO_SYMBOL(id) == cx->wellKnownSymbols().toStringTag) {
598
vp.setString(cx->names().Module);
599
return true;
600
}
601
602
vp.setUndefined();
603
return true;
604
}
605
606
ModuleEnvironmentObject* env;
607
Shape* shape;
608
if (!ns->bindings().lookup(id, &env, &shape)) {
609
vp.setUndefined();
610
return true;
611
}
612
613
RootedValue value(cx, env->getSlot(shape->slot()));
614
if (value.isMagic(JS_UNINITIALIZED_LEXICAL)) {
615
ReportRuntimeLexicalError(cx, JSMSG_UNINITIALIZED_LEXICAL, id);
616
return false;
617
}
618
619
vp.set(value);
620
return true;
621
}
622
623
bool ModuleNamespaceObject::ProxyHandler::set(JSContext* cx, HandleObject proxy,
624
HandleId id, HandleValue v,
625
HandleValue receiver,
626
ObjectOpResult& result) const {
627
return result.failReadOnly();
628
}
629
630
bool ModuleNamespaceObject::ProxyHandler::delete_(
631
JSContext* cx, HandleObject proxy, HandleId id,
632
ObjectOpResult& result) const {
633
Rooted<ModuleNamespaceObject*> ns(cx, &proxy->as<ModuleNamespaceObject>());
634
if (JSID_IS_SYMBOL(id)) {
635
if (JSID_TO_SYMBOL(id) == cx->wellKnownSymbols().toStringTag) {
636
return result.failCantDelete();
637
}
638
639
return result.succeed();
640
}
641
642
if (ns->bindings().has(id)) {
643
return result.failCantDelete();
644
}
645
646
return result.succeed();
647
}
648
649
bool ModuleNamespaceObject::ProxyHandler::ownPropertyKeys(
650
JSContext* cx, HandleObject proxy, MutableHandleIdVector props) const {
651
Rooted<ModuleNamespaceObject*> ns(cx, &proxy->as<ModuleNamespaceObject>());
652
RootedObject exports(cx, &ns->exports());
653
uint32_t count;
654
if (!GetLengthProperty(cx, exports, &count) ||
655
!props.reserve(props.length() + count + 1)) {
656
return false;
657
}
658
659
Rooted<ValueVector> names(cx, ValueVector(cx));
660
if (!names.resize(count) || !GetElements(cx, exports, count, names.begin())) {
661
return false;
662
}
663
664
for (uint32_t i = 0; i < count; i++) {
665
props.infallibleAppend(AtomToId(&names[i].toString()->asAtom()));
666
}
667
668
props.infallibleAppend(SYMBOL_TO_JSID(cx->wellKnownSymbols().toStringTag));
669
670
return true;
671
}
672
673
void ModuleNamespaceObject::ProxyHandler::trace(JSTracer* trc,
674
JSObject* proxy) const {
675
auto& self = proxy->as<ModuleNamespaceObject>();
676
677
if (self.hasBindings()) {
678
self.bindings().trace(trc);
679
}
680
}
681
682
void ModuleNamespaceObject::ProxyHandler::finalize(JSFreeOp* fop,
683
JSObject* proxy) const {
684
auto& self = proxy->as<ModuleNamespaceObject>();
685
686
if (self.hasBindings()) {
687
fop->delete_(proxy, &self.bindings(), MemoryUse::ModuleBindingMap);
688
}
689
}
690
691
///////////////////////////////////////////////////////////////////////////
692
// FunctionDeclaration
693
694
FunctionDeclaration::FunctionDeclaration(HandleAtom name, HandleFunction fun)
695
: name(name), fun(fun) {}
696
697
void FunctionDeclaration::trace(JSTracer* trc) {
698
TraceEdge(trc, &name, "FunctionDeclaration name");
699
TraceEdge(trc, &fun, "FunctionDeclaration fun");
700
}
701
702
///////////////////////////////////////////////////////////////////////////
703
// ModuleObject
704
705
/* static */ const JSClassOps ModuleObject::classOps_ = {
706
nullptr, /* addProperty */
707
nullptr, /* delProperty */
708
nullptr, /* enumerate */
709
nullptr, /* newEnumerate */
710
nullptr, /* resolve */
711
nullptr, /* mayResolve */
712
ModuleObject::finalize,
713
nullptr, /* call */
714
nullptr, /* hasInstance */
715
nullptr, /* construct */
716
ModuleObject::trace};
717
718
/* static */ const JSClass ModuleObject::class_ = {
719
"Module",
720
JSCLASS_HAS_RESERVED_SLOTS(ModuleObject::SlotCount) |
721
JSCLASS_BACKGROUND_FINALIZE,
722
&ModuleObject::classOps_};
723
724
#define DEFINE_ARRAY_SLOT_ACCESSOR(cls, name, slot) \
725
ArrayObject& cls::name() const { \
726
return getReservedSlot(cls::slot).toObject().as<ArrayObject>(); \
727
}
728
729
DEFINE_ARRAY_SLOT_ACCESSOR(ModuleObject, requestedModules, RequestedModulesSlot)
730
DEFINE_ARRAY_SLOT_ACCESSOR(ModuleObject, importEntries, ImportEntriesSlot)
731
DEFINE_ARRAY_SLOT_ACCESSOR(ModuleObject, localExportEntries,
732
LocalExportEntriesSlot)
733
DEFINE_ARRAY_SLOT_ACCESSOR(ModuleObject, indirectExportEntries,
734
IndirectExportEntriesSlot)
735
DEFINE_ARRAY_SLOT_ACCESSOR(ModuleObject, starExportEntries,
736
StarExportEntriesSlot)
737
738
/* static */
739
bool ModuleObject::isInstance(HandleValue value) {
740
return value.isObject() && value.toObject().is<ModuleObject>();
741
}
742
743
/* static */
744
ModuleObject* ModuleObject::create(JSContext* cx) {
745
RootedObject proto(
746
cx, GlobalObject::getOrCreateModulePrototype(cx, cx->global()));
747
if (!proto) {
748
return nullptr;
749
}
750
751
RootedModuleObject self(cx, NewObjectWithGivenProto<ModuleObject>(cx, proto));
752
if (!self) {
753
return nullptr;
754
}
755
756
IndirectBindingMap* bindings = cx->new_<IndirectBindingMap>();
757
if (!bindings) {
758
return nullptr;
759
}
760
761
InitReservedSlot(self, ImportBindingsSlot, bindings,
762
MemoryUse::ModuleBindingMap);
763
764
FunctionDeclarationVector* funDecls = cx->new_<FunctionDeclarationVector>();
765
if (!funDecls) {
766
return nullptr;
767
}
768
769
self->initReservedSlot(FunctionDeclarationsSlot, PrivateValue(funDecls));
770
return self;
771
}
772
773
/* static */
774
void ModuleObject::finalize(JSFreeOp* fop, JSObject* obj) {
775
MOZ_ASSERT(fop->maybeOnHelperThread());
776
ModuleObject* self = &obj->as<ModuleObject>();
777
if (self->hasImportBindings()) {
778
fop->delete_(obj, &self->importBindings(), MemoryUse::ModuleBindingMap);
779
}
780
if (FunctionDeclarationVector* funDecls = self->functionDeclarations()) {
781
// Not tracked as these may move between zones on merge.
782
fop->deleteUntracked(funDecls);
783
}
784
}
785
786
ModuleEnvironmentObject& ModuleObject::initialEnvironment() const {
787
Value value = getReservedSlot(EnvironmentSlot);
788
return value.toObject().as<ModuleEnvironmentObject>();
789
}
790
791
ModuleEnvironmentObject* ModuleObject::environment() const {
792
// Note that this it's valid to call this even if there was an error
793
// evaluating the module.
794
795
// According to the spec the environment record is created during
796
// instantiation, but we create it earlier than that.
797
if (status() < MODULE_STATUS_INSTANTIATED) {
798
return nullptr;
799
}
800
801
return &initialEnvironment();
802
}
803
804
bool ModuleObject::hasImportBindings() const {
805
// Import bindings may not be present if we hit OOM in initialization.
806
return !getReservedSlot(ImportBindingsSlot).isUndefined();
807
}
808
809
IndirectBindingMap& ModuleObject::importBindings() {
810
return *static_cast<IndirectBindingMap*>(
811
getReservedSlot(ImportBindingsSlot).toPrivate());
812
}
813
814
ModuleNamespaceObject* ModuleObject::namespace_() {
815
Value value = getReservedSlot(NamespaceSlot);
816
if (value.isUndefined()) {
817
return nullptr;
818
}
819
return &value.toObject().as<ModuleNamespaceObject>();
820
}
821
822
ScriptSourceObject* ModuleObject::scriptSourceObject() const {
823
return &getReservedSlot(ScriptSourceObjectSlot)
824
.toObject()
825
.as<ScriptSourceObject>();
826
}
827
828
FunctionDeclarationVector* ModuleObject::functionDeclarations() {
829
Value value = getReservedSlot(FunctionDeclarationsSlot);
830
if (value.isUndefined()) {
831
return nullptr;
832
}
833
834
return static_cast<FunctionDeclarationVector*>(value.toPrivate());
835
}
836
837
void ModuleObject::init(HandleScript script) {
838
MOZ_ASSERT(script);
839
initReservedSlot(ScriptSlot, PrivateGCThingValue(script));
840
initReservedSlot(StatusSlot, Int32Value(MODULE_STATUS_UNINSTANTIATED));
841
initReservedSlot(ScriptSourceObjectSlot,
842
ObjectValue(*script->sourceObject()));
843
}
844
845
void ModuleObject::setInitialEnvironment(
846
HandleModuleEnvironmentObject initialEnvironment) {
847
initReservedSlot(EnvironmentSlot, ObjectValue(*initialEnvironment));
848
}
849
850
void ModuleObject::initImportExportData(HandleArrayObject requestedModules,
851
HandleArrayObject importEntries,
852
HandleArrayObject localExportEntries,
853
HandleArrayObject indirectExportEntries,
854
HandleArrayObject starExportEntries) {
855
initReservedSlot(RequestedModulesSlot, ObjectValue(*requestedModules));
856
initReservedSlot(ImportEntriesSlot, ObjectValue(*importEntries));
857
initReservedSlot(LocalExportEntriesSlot, ObjectValue(*localExportEntries));
858
initReservedSlot(IndirectExportEntriesSlot,
859
ObjectValue(*indirectExportEntries));
860
initReservedSlot(StarExportEntriesSlot, ObjectValue(*starExportEntries));
861
setReservedSlot(StatusSlot, Int32Value(MODULE_STATUS_UNINSTANTIATED));
862
}
863
864
static bool FreezeObjectProperty(JSContext* cx, HandleNativeObject obj,
865
uint32_t slot) {
866
RootedObject property(cx, &obj->getSlot(slot).toObject());
867
return FreezeObject(cx, property);
868
}
869
870
/* static */
871
bool ModuleObject::Freeze(JSContext* cx, HandleModuleObject self) {
872
return FreezeObjectProperty(cx, self, RequestedModulesSlot) &&
873
FreezeObjectProperty(cx, self, ImportEntriesSlot) &&
874
FreezeObjectProperty(cx, self, LocalExportEntriesSlot) &&
875
FreezeObjectProperty(cx, self, IndirectExportEntriesSlot) &&
876
FreezeObjectProperty(cx, self, StarExportEntriesSlot) &&
877
FreezeObject(cx, self);
878
}
879
880
#ifdef DEBUG
881
882
static inline bool CheckObjectFrozen(JSContext* cx, HandleObject obj,
883
bool* result) {
884
return TestIntegrityLevel(cx, obj, IntegrityLevel::Frozen, result);
885
}
886
887
static inline bool CheckObjectPropertyFrozen(JSContext* cx,
888
HandleNativeObject obj,
889
uint32_t slot, bool* result) {
890
RootedObject property(cx, &obj->getSlot(slot).toObject());
891
return CheckObjectFrozen(cx, property, result);
892
}
893
894
/* static */ inline bool ModuleObject::AssertFrozen(JSContext* cx,
895
HandleModuleObject self) {
896
static const mozilla::EnumSet<ModuleSlot> slotsToCheck = {
897
RequestedModulesSlot, ImportEntriesSlot, LocalExportEntriesSlot,
898
IndirectExportEntriesSlot, StarExportEntriesSlot};
899
900
bool frozen = false;
901
for (auto slot : slotsToCheck) {
902
if (!CheckObjectPropertyFrozen(cx, self, slot, &frozen)) {
903
return false;
904
}
905
MOZ_ASSERT(frozen);
906
}
907
908
if (!CheckObjectFrozen(cx, self, &frozen)) {
909
return false;
910
}
911
MOZ_ASSERT(frozen);
912
913
return true;
914
}
915
916
#endif
917
918
inline static void AssertModuleScopesMatch(ModuleObject* module) {
919
MOZ_ASSERT(module->enclosingScope()->is<GlobalScope>());
920
MOZ_ASSERT(IsGlobalLexicalEnvironment(
921
&module->initialEnvironment().enclosingEnvironment()));
922
}
923
924
void ModuleObject::fixEnvironmentsAfterRealmMerge() {
925
AssertModuleScopesMatch(this);
926
initialEnvironment().fixEnclosingEnvironmentAfterRealmMerge(
927
script()->global());
928
AssertModuleScopesMatch(this);
929
}
930
931
JSScript* ModuleObject::maybeScript() const {
932
Value value = getReservedSlot(ScriptSlot);
933
if (value.isUndefined()) return nullptr;
934
935
return value.toGCThing()->as<JSScript>();
936
}
937
938
JSScript* ModuleObject::script() const {
939
JSScript* ptr = maybeScript();
940
MOZ_RELEASE_ASSERT(ptr);
941
return ptr;
942
}
943
944
static inline void AssertValidModuleStatus(ModuleStatus status) {
945
MOZ_ASSERT(status >= MODULE_STATUS_UNINSTANTIATED &&
946
status <= MODULE_STATUS_EVALUATED_ERROR);
947
}
948
949
ModuleStatus ModuleObject::status() const {
950
ModuleStatus status = getReservedSlot(StatusSlot).toInt32();
951
AssertValidModuleStatus(status);
952
return status;
953
}
954
955
bool ModuleObject::hadEvaluationError() const {
956
return status() == MODULE_STATUS_EVALUATED_ERROR;
957
}
958
959
Value ModuleObject::evaluationError() const {
960
MOZ_ASSERT(hadEvaluationError());
961
return getReservedSlot(EvaluationErrorSlot);
962
}
963
964
JSObject* ModuleObject::metaObject() const {
965
Value value = getReservedSlot(MetaObjectSlot);
966
if (value.isObject()) {
967
return &value.toObject();
968
}
969
970
MOZ_ASSERT(value.isUndefined());
971
return nullptr;
972
}
973
974
void ModuleObject::setMetaObject(JSObject* obj) {
975
MOZ_ASSERT(obj);
976
MOZ_ASSERT(!metaObject());
977
setReservedSlot(MetaObjectSlot, ObjectValue(*obj));
978
}
979
980
Scope* ModuleObject::enclosingScope() const {
981
return script()->enclosingScope();
982
}
983
984
/* static */
985
void ModuleObject::trace(JSTracer* trc, JSObject* obj) {
986
ModuleObject& module = obj->as<ModuleObject>();
987
988
if (module.hasImportBindings()) {
989
module.importBindings().trace(trc);
990
}
991
992
if (FunctionDeclarationVector* funDecls = module.functionDeclarations()) {
993
funDecls->trace(trc);
994
}
995
}
996
997
bool ModuleObject::noteFunctionDeclaration(JSContext* cx, HandleAtom name,
998
HandleFunction fun) {
999
FunctionDeclarationVector* funDecls = functionDeclarations();
1000
if (!funDecls->emplaceBack(name, fun)) {
1001
ReportOutOfMemory(cx);
1002
return false;
1003
}
1004
1005
return true;
1006
}
1007
1008
/* static */
1009
bool ModuleObject::instantiateFunctionDeclarations(JSContext* cx,
1010
HandleModuleObject self) {
1011
#ifdef DEBUG
1012
MOZ_ASSERT(self->status() == MODULE_STATUS_INSTANTIATING);
1013
if (!AssertFrozen(cx, self)) {
1014
return false;
1015
}
1016
#endif
1017
1018
FunctionDeclarationVector* funDecls = self->functionDeclarations();
1019
if (!funDecls) {
1020
JS_ReportErrorASCII(
1021
cx, "Module function declarations have already been instantiated");
1022
return false;
1023
}
1024
1025
RootedModuleEnvironmentObject env(cx, &self->initialEnvironment());
1026
RootedFunction fun(cx);
1027
RootedObject obj(cx);
1028
RootedValue value(cx);
1029
1030
for (const auto& funDecl : *funDecls) {
1031
fun = funDecl.fun;
1032
obj = Lambda(cx, fun, env);
1033
if (!obj) {
1034
return false;
1035
}
1036
1037
value = ObjectValue(*obj);
1038
if (!SetProperty(cx, env, funDecl.name->asPropertyName(), value)) {
1039
return false;
1040
}
1041
}
1042
1043
js_delete(funDecls);
1044
self->setReservedSlot(FunctionDeclarationsSlot, UndefinedValue());
1045
return true;
1046
}
1047
1048
/* static */
1049
bool ModuleObject::execute(JSContext* cx, HandleModuleObject self,
1050
MutableHandleValue rval) {
1051
#ifdef DEBUG
1052
MOZ_ASSERT(self->status() == MODULE_STATUS_EVALUATING);
1053
if (!AssertFrozen(cx, self)) {
1054
return false;
1055
}
1056
#endif
1057
1058
RootedScript script(cx, self->script());
1059
1060
// The top-level script if a module is only ever executed once. Clear the
1061
// reference at exit to prevent us keeping this alive unnecessarily. This is
1062
// kept while executing so it is available to the debugger.
1063
auto guardA = mozilla::MakeScopeExit(
1064
[&] { self->setReservedSlot(ScriptSlot, UndefinedValue()); });
1065
1066
RootedModuleEnvironmentObject scope(cx, self->environment());
1067
if (!scope) {
1068
JS_ReportErrorASCII(cx,
1069
"Module declarations have not yet been instantiated");
1070
return false;
1071
}
1072
1073
return Execute(cx, script, *scope, rval.address());
1074
}
1075
1076
/* static */
1077
ModuleNamespaceObject* ModuleObject::createNamespace(JSContext* cx,
1078
HandleModuleObject self,
1079
HandleObject exports) {
1080
MOZ_ASSERT(!self->namespace_());
1081
MOZ_ASSERT(exports->is<ArrayObject>());
1082
1083
auto bindings = cx->make_unique<IndirectBindingMap>();
1084
if (!bindings) {
1085
return nullptr;
1086
}
1087
1088
auto ns =
1089
ModuleNamespaceObject::create(cx, self, exports, std::move(bindings));
1090
if (!ns) {
1091
return nullptr;
1092
}
1093
1094
self->initReservedSlot(NamespaceSlot, ObjectValue(*ns));
1095
return ns;
1096
}
1097
1098
static bool InvokeSelfHostedMethod(JSContext* cx, HandleModuleObject self,
1099
HandlePropertyName name) {
1100
RootedValue thisv(cx, ObjectValue(*self));
1101
FixedInvokeArgs<0> args(cx);
1102
1103
RootedValue ignored(cx);
1104
return CallSelfHostedFunction(cx, name, thisv, args, &ignored);
1105
}
1106
1107
/* static */
1108
bool ModuleObject::Instantiate(JSContext* cx, HandleModuleObject self) {
1109
return InvokeSelfHostedMethod(cx, self, cx->names().ModuleInstantiate);
1110
}
1111
1112
/* static */
1113
bool ModuleObject::Evaluate(JSContext* cx, HandleModuleObject self) {
1114
return InvokeSelfHostedMethod(cx, self, cx->names().ModuleEvaluate);
1115
}
1116
1117
/* static */
1118
ModuleNamespaceObject* ModuleObject::GetOrCreateModuleNamespace(
1119
JSContext* cx, HandleModuleObject self) {
1120
FixedInvokeArgs<1> args(cx);
1121
args[0].setObject(*self);
1122
1123
RootedValue result(cx);
1124
if (!CallSelfHostedFunction(cx, cx->names().GetModuleNamespace,
1125
UndefinedHandleValue, args, &result)) {
1126
return nullptr;
1127
}
1128
1129
return &result.toObject().as<ModuleNamespaceObject>();
1130
}
1131
1132
DEFINE_GETTER_FUNCTIONS(ModuleObject, namespace_, NamespaceSlot)
1133
DEFINE_GETTER_FUNCTIONS(ModuleObject, status, StatusSlot)
1134
DEFINE_GETTER_FUNCTIONS(ModuleObject, evaluationError, EvaluationErrorSlot)
1135
DEFINE_GETTER_FUNCTIONS(ModuleObject, requestedModules, RequestedModulesSlot)
1136
DEFINE_GETTER_FUNCTIONS(ModuleObject, importEntries, ImportEntriesSlot)
1137
DEFINE_GETTER_FUNCTIONS(ModuleObject, localExportEntries,
1138
LocalExportEntriesSlot)
1139
DEFINE_GETTER_FUNCTIONS(ModuleObject, indirectExportEntries,
1140
IndirectExportEntriesSlot)
1141
DEFINE_GETTER_FUNCTIONS(ModuleObject, starExportEntries, StarExportEntriesSlot)
1142
DEFINE_GETTER_FUNCTIONS(ModuleObject, dfsIndex, DFSIndexSlot)
1143
DEFINE_GETTER_FUNCTIONS(ModuleObject, dfsAncestorIndex, DFSAncestorIndexSlot)
1144
1145
/* static */
1146
bool GlobalObject::initModuleProto(JSContext* cx,
1147
Handle<GlobalObject*> global) {
1148
static const JSPropertySpec protoAccessors[] = {
1149
JS_PSG("namespace", ModuleObject_namespace_Getter, 0),
1150
JS_PSG("status", ModuleObject_statusGetter, 0),
1151
JS_PSG("evaluationError", ModuleObject_evaluationErrorGetter, 0),
1152
JS_PSG("requestedModules", ModuleObject_requestedModulesGetter, 0),
1153
JS_PSG("importEntries", ModuleObject_importEntriesGetter, 0),
1154
JS_PSG("localExportEntries", ModuleObject_localExportEntriesGetter, 0),
1155
JS_PSG("indirectExportEntries", ModuleObject_indirectExportEntriesGetter,
1156
0),
1157
JS_PSG("starExportEntries", ModuleObject_starExportEntriesGetter, 0),
1158
JS_PSG("dfsIndex", ModuleObject_dfsIndexGetter, 0),
1159
JS_PSG("dfsAncestorIndex", ModuleObject_dfsAncestorIndexGetter, 0),
1160
JS_PS_END};
1161
1162
static const JSFunctionSpec protoFunctions[] = {
1163
JS_SELF_HOSTED_FN("getExportedNames", "ModuleGetExportedNames", 1, 0),
1164
JS_SELF_HOSTED_FN("resolveExport", "ModuleResolveExport", 2, 0),
1165
JS_SELF_HOSTED_FN("declarationInstantiation", "ModuleInstantiate", 0, 0),
1166
JS_SELF_HOSTED_FN("evaluation", "ModuleEvaluate", 0, 0), JS_FS_END};
1167
1168
RootedObject proto(
1169
cx, GlobalObject::createBlankPrototype<PlainObject>(cx, global));
1170
if (!proto) {
1171
return false;
1172
}
1173
1174
if (!DefinePropertiesAndFunctions(cx, proto, protoAccessors,
1175
protoFunctions)) {
1176
return false;
1177
}
1178
1179
global->setReservedSlot(MODULE_PROTO, ObjectValue(*proto));
1180
return true;
1181
}
1182
1183
#undef DEFINE_GETTER_FUNCTIONS
1184
#undef DEFINE_STRING_ACCESSOR_METHOD
1185
#undef DEFINE_ARRAY_SLOT_ACCESSOR
1186
1187
///////////////////////////////////////////////////////////////////////////
1188
// ModuleBuilder
1189
1190
ModuleBuilder::ModuleBuilder(JSContext* cx, HandleModuleObject module,
1191
const frontend::EitherParser& eitherParser)
1192
: cx_(cx),
1193
module_(cx, module),
1194
eitherParser_(eitherParser),
1195
requestedModuleSpecifiers_(cx, AtomSet(cx)),
1196
requestedModules_(cx, RequestedModuleVector(cx)),
1197
importEntries_(cx, ImportEntryMap(cx)),
1198
exportEntries_(cx, ExportEntryVector(cx)),
1199
exportNames_(cx, AtomSet(cx)),
1200
localExportEntries_(cx, ExportEntryVector(cx)),
1201
indirectExportEntries_(cx, ExportEntryVector(cx)),
1202
starExportEntries_(cx, ExportEntryVector(cx)) {}
1203
1204
bool ModuleBuilder::buildTables() {
1205
for (const auto& e : exportEntries_) {
1206
RootedExportEntryObject exp(cx_, e);
1207
if (!exp->moduleRequest()) {
1208
RootedImportEntryObject importEntry(cx_,
1209
importEntryFor(exp->localName()));
1210
if (!importEntry) {
1211
if (!localExportEntries_.append(exp)) {
1212
return false;
1213
}
1214
} else {
1215
if (importEntry->importName() == cx_->names().star) {
1216
if (!localExportEntries_.append(exp)) {
1217
return false;
1218
}
1219
} else {
1220
RootedAtom exportName(cx_, exp->exportName());
1221
RootedAtom moduleRequest(cx_, importEntry->moduleRequest());
1222
RootedAtom importName(cx_, importEntry->importName());
1223
RootedExportEntryObject exportEntry(cx_);
1224
exportEntry = ExportEntryObject::create(
1225
cx_, exportName, moduleRequest, importName, nullptr,
1226
exp->lineNumber(), exp->columnNumber());
1227
if (!exportEntry || !indirectExportEntries_.append(exportEntry)) {
1228
return false;
1229
}
1230
}
1231
}
1232
} else if (exp->importName() == cx_->names().star) {
1233
if (!starExportEntries_.append(exp)) {
1234
return false;
1235
}
1236
} else {
1237
if (!indirectExportEntries_.append(exp)) {
1238
return false;
1239
}
1240
}
1241
}
1242
1243
return true;
1244
}
1245
1246
bool ModuleBuilder::initModule() {
1247
RootedArrayObject requestedModules(cx_, createArray(requestedModules_));
1248
if (!requestedModules) {
1249
return false;
1250
}
1251
1252
RootedArrayObject importEntries(cx_, createArray(importEntries_));
1253
if (!importEntries) {
1254
return false;
1255
}
1256
1257
RootedArrayObject localExportEntries(cx_, createArray(localExportEntries_));
1258
if (!localExportEntries) {
1259
return false;
1260
}
1261
1262
RootedArrayObject indirectExportEntries(cx_,
1263
createArray(indirectExportEntries_));
1264
if (!indirectExportEntries) {
1265
return false;
1266
}
1267
1268
RootedArrayObject starExportEntries(cx_, createArray(starExportEntries_));
1269
if (!starExportEntries) {
1270
return false;
1271
}
1272
1273
module_->initImportExportData(requestedModules, importEntries,
1274
localExportEntries, indirectExportEntries,
1275
starExportEntries);
1276
1277
return true;
1278
}
1279
1280
bool ModuleBuilder::processImport(frontend::BinaryNode* importNode) {
1281
using namespace js::frontend;
1282
1283
MOZ_ASSERT(importNode->isKind(ParseNodeKind::ImportDecl));
1284
1285
ListNode* specList = &importNode->left()->as<ListNode>();
1286
MOZ_ASSERT(specList->isKind(ParseNodeKind::ImportSpecList));
1287
1288
NameNode* moduleSpec = &importNode->right()->as<NameNode>();
1289
MOZ_ASSERT(moduleSpec->isKind(ParseNodeKind::StringExpr));
1290
1291
RootedAtom module(cx_, moduleSpec->atom());
1292
if (!maybeAppendRequestedModule(module, moduleSpec)) {
1293
return false;
1294
}
1295
1296
RootedAtom importName(cx_);
1297
RootedAtom localName(cx_);
1298
for (ParseNode* item : specList->contents()) {
1299
BinaryNode* spec = &item->as<BinaryNode>();
1300
MOZ_ASSERT(spec->isKind(ParseNodeKind::ImportSpec));
1301
1302
NameNode* importNameNode = &spec->left()->as<NameNode>();
1303
1304
NameNode* localNameNode = &spec->right()->as<NameNode>();
1305
1306
importName = importNameNode->atom();
1307
localName = localNameNode->atom();
1308
1309
uint32_t line;
1310
uint32_t column;
1311
eitherParser_.computeLineAndColumn(importNameNode->pn_pos.begin, &line,
1312
&column);
1313
1314
RootedImportEntryObject importEntry(cx_);
1315
importEntry = ImportEntryObject::create(cx_, module, importName, localName,
1316
line, column);
1317
if (!importEntry || !appendImportEntryObject(importEntry)) {
1318
return false;
1319
}
1320
}
1321
1322
return true;
1323
}
1324
1325
bool ModuleBuilder::appendImportEntryObject(
1326
HandleImportEntryObject importEntry) {
1327
MOZ_ASSERT(importEntry->localName());
1328
return importEntries_.put(importEntry->localName(), importEntry);
1329
}
1330
1331
bool ModuleBuilder::processExport(frontend::ParseNode* exportNode) {
1332
using namespace js::frontend;
1333
1334
MOZ_ASSERT(exportNode->isKind(ParseNodeKind::ExportStmt) ||
1335
exportNode->isKind(ParseNodeKind::ExportDefaultStmt));
1336
1337
bool isDefault = exportNode->isKind(ParseNodeKind::ExportDefaultStmt);
1338
ParseNode* kid = isDefault ? exportNode->as<BinaryNode>().left()
1339
: exportNode->as<UnaryNode>().kid();
1340
1341
if (isDefault && exportNode->as<BinaryNode>().right()) {
1342
// This is an export default containing an expression.
1343
HandlePropertyName localName = cx_->names().default_;
1344
HandlePropertyName exportName = cx_->names().default_;
1345
return appendExportEntry(exportName, localName);
1346
}
1347
1348
switch (kid->getKind()) {
1349
case ParseNodeKind::ExportSpecList: {
1350
MOZ_ASSERT(!isDefault);
1351
RootedAtom localName(cx_);
1352
RootedAtom exportName(cx_);
1353
for (ParseNode* item : kid->as<ListNode>().contents()) {
1354
BinaryNode* spec = &item->as<BinaryNode>();
1355
MOZ_ASSERT(spec->isKind(ParseNodeKind::ExportSpec));
1356
1357
NameNode* localNameNode = &spec->left()->as<NameNode>();
1358
NameNode* exportNameNode = &spec->right()->as<NameNode>();
1359
localName = localNameNode->atom();
1360
exportName = exportNameNode->atom();
1361
if (!appendExportEntry(exportName, localName, spec)) {
1362
return false;
1363
}
1364
}
1365
break;
1366
}
1367
1368
case ParseNodeKind::ClassDecl: {
1369
const ClassNode& cls = kid->as<ClassNode>();
1370
MOZ_ASSERT(cls.names());
1371
RootedAtom localName(cx_, cls.names()->innerBinding()->atom());
1372
RootedAtom exportName(
1373
cx_, isDefault ? cx_->names().default_ : localName.get());
1374
if (!appendExportEntry(exportName, localName)) {
1375
return false;
1376
}
1377
break;
1378
}
1379
1380
case ParseNodeKind::VarStmt:
1381
case ParseNodeKind::ConstDecl:
1382
case ParseNodeKind::LetDecl: {
1383
RootedAtom localName(cx_);
1384
RootedAtom exportName(cx_);
1385
for (ParseNode* binding : kid->as<ListNode>().contents()) {
1386
if (binding->isKind(ParseNodeKind::AssignExpr)) {
1387
binding = binding->as<AssignmentNode>().left();
1388
} else {
1389
MOZ_ASSERT(binding->isKind(ParseNodeKind::Name));
1390
}
1391
1392
if (binding->isKind(ParseNodeKind::Name)) {
1393
localName = binding->as<NameNode>().atom();
1394
exportName = isDefault ? cx_->names().default_ : localName.get();
1395
if (!appendExportEntry(exportName, localName)) {
1396
return false;
1397
}
1398
} else if (binding->isKind(ParseNodeKind::ArrayExpr)) {
1399
if (!processExportArrayBinding(&binding->as<ListNode>())) {
1400
return false;
1401
}
1402
} else {
1403
MOZ_ASSERT(binding->isKind(ParseNodeKind::ObjectExpr));
1404
if (!processExportObjectBinding(&binding->as<ListNode>())) {
1405
return false;
1406
}
1407
}
1408
}
1409
break;
1410
}
1411
1412
case ParseNodeKind::Function: {
1413
FunctionBox* box = kid->as<FunctionNode>().funbox();
1414
MOZ_ASSERT(!box->isArrow());
1415
RootedAtom localName(cx_, box->explicitName());
1416
RootedAtom exportName(
1417
cx_, isDefault ? cx_->names().default_ : localName.get());
1418
MOZ_ASSERT_IF(isDefault, localName);
1419
if (!appendExportEntry(exportName, localName)) {
1420
return false;
1421
}
1422
break;
1423
}
1424
1425
default:
1426
MOZ_CRASH("Unexpected parse node");
1427
}
1428
1429
return true;
1430
}
1431
1432
bool ModuleBuilder::processExportBinding(frontend::ParseNode* binding) {
1433
using namespace js::frontend;
1434
1435
if (binding->isKind(ParseNodeKind::Name)) {
1436
RootedAtom name(cx_, binding->as<NameNode>().atom());
1437
return appendExportEntry(name, name);
1438
}
1439
1440
if (binding->isKind(ParseNodeKind::ArrayExpr)) {
1441
return processExportArrayBinding(&binding->as<ListNode>());
1442
}
1443
1444
MOZ_ASSERT(binding->isKind(ParseNodeKind::ObjectExpr));
1445
return processExportObjectBinding(&binding->as<ListNode>());
1446
}
1447
1448
bool ModuleBuilder::processExportArrayBinding(frontend::ListNode* array) {
1449
using namespace js::frontend;
1450
1451
MOZ_ASSERT(array->isKind(ParseNodeKind::ArrayExpr));
1452
1453
for (ParseNode* node : array->contents()) {
1454
if (node->isKind(ParseNodeKind::Elision)) {
1455
continue;
1456
}
1457
1458
if (node->isKind(ParseNodeKind::Spread)) {
1459
node = node->as<UnaryNode>().kid();
1460
} else if (node->isKind(ParseNodeKind::AssignExpr)) {
1461
node = node->as<AssignmentNode>().left();
1462
}
1463
1464
if (!processExportBinding(node)) {
1465
return false;
1466
}
1467
}
1468
1469
return true;
1470
}
1471
1472
bool ModuleBuilder::processExportObjectBinding(frontend::ListNode* obj) {
1473
using namespace js::frontend;
1474
1475
MOZ_ASSERT(obj->isKind(ParseNodeKind::ObjectExpr));
1476
1477
for (ParseNode* node : obj->contents()) {
1478
MOZ_ASSERT(node->isKind(ParseNodeKind::MutateProto) ||
1479
node->isKind(ParseNodeKind::PropertyDefinition) ||
1480
node->isKind(ParseNodeKind::Shorthand) ||
1481
node->isKind(ParseNodeKind::Spread));
1482
1483
ParseNode* target;
1484
if (node->isKind(ParseNodeKind::Spread)) {
1485
target = node->as<UnaryNode>().kid();
1486
} else {
1487
if (node->isKind(ParseNodeKind::MutateProto)) {
1488
target = node->as<UnaryNode>().kid();
1489
} else {
1490
target = node->as<BinaryNode>().right();
1491
}
1492
1493
if (target->isKind(ParseNodeKind::AssignExpr)) {
1494
target = target->as<AssignmentNode>().left();
1495
}
1496
}
1497
1498
if (!processExportBinding(target)) {
1499
return false;
1500
}
1501
}
1502
1503
return true;
1504
}
1505
1506
bool ModuleBuilder::processExportFrom(frontend::BinaryNode* exportNode) {
1507
using namespace js::frontend;
1508
1509
MOZ_ASSERT(exportNode->isKind(ParseNodeKind::ExportFromStmt));
1510
1511
ListNode* specList = &exportNode->left()->as<ListNode>();
1512
MOZ_ASSERT(specList->isKind(ParseNodeKind::ExportSpecList));
1513
1514
NameNode* moduleSpec = &exportNode->right()->as<NameNode>();
1515
MOZ_ASSERT(moduleSpec->isKind(ParseNodeKind::StringExpr));
1516
1517
RootedAtom module(cx_, moduleSpec->atom());
1518
if (!maybeAppendRequestedModule(module, moduleSpec)) {
1519
return false;
1520
}
1521
1522
RootedAtom bindingName(cx_);
1523
RootedAtom exportName(cx_);
1524
for (ParseNode* spec : specList->contents()) {
1525
if (spec->isKind(ParseNodeKind::ExportSpec)) {
1526
NameNode* localNameNode = &spec->as<BinaryNode>().left()->as<NameNode>();
1527
NameNode* exportNameNode =
1528
&spec->as<BinaryNode>().right()->as<NameNode>();
1529
bindingName = localNameNode->atom();
1530
exportName = exportNameNode->atom();
1531
if (!appendExportFromEntry(exportName, module, bindingName,
1532
localNameNode)) {
1533
return false;
1534
}
1535
} else {
1536
MOZ_ASSERT(spec->isKind(ParseNodeKind::ExportBatchSpecStmt));
1537
exportName = cx_->names().star;
1538
if (!appendExportFromEntry(nullptr, module, exportName, spec)) {
1539
return false;
1540
}
1541
}
1542
}
1543
1544
return true;
1545
}
1546
1547
ImportEntryObject* ModuleBuilder::importEntryFor(JSAtom* localName) const {
1548
MOZ_ASSERT(localName);
1549
auto ptr = importEntries_.lookup(localName);
1550
if (!ptr) {
1551
return nullptr;
1552
}
1553
1554
return ptr->value();
1555
}
1556
1557
bool ModuleBuilder::hasExportedName(JSAtom* name) const {
1558
MOZ_ASSERT(name);
1559
return exportNames_.has(name);
1560
}
1561
1562
bool ModuleBuilder::appendExportEntry(HandleAtom exportName,
1563
HandleAtom localName,
1564
frontend::ParseNode* node) {
1565
uint32_t line = 0;
1566
uint32_t column = 0;
1567
if (node) {
1568
eitherParser_.computeLineAndColumn(node->pn_pos.begin, &line, &column);
1569
}
1570
1571
Rooted<ExportEntryObject*> exportEntry(cx_);
1572
exportEntry = ExportEntryObject::create(cx_, exportName, nullptr, nullptr,
1573
localName, line, column);
1574
return exportEntry && appendExportEntryObject(exportEntry);
1575
}
1576
1577
bool ModuleBuilder::appendExportFromEntry(HandleAtom exportName,
1578
HandleAtom moduleRequest,
1579
HandleAtom importName,
1580
frontend::ParseNode* node) {
1581
uint32_t line;
1582
uint32_t column;
1583
eitherParser_.computeLineAndColumn(node->pn_pos.begin, &line, &column);
1584
1585
Rooted<ExportEntryObject*> exportEntry(cx_);
1586
exportEntry = ExportEntryObject::create(cx_, exportName, moduleRequest,
1587
importName, nullptr, line, column);
1588
return exportEntry && appendExportEntryObject(exportEntry);
1589
}
1590
1591
bool ModuleBuilder::appendExportEntryObject(
1592
HandleExportEntryObject exportEntry) {
1593
if (!exportEntries_.append(exportEntry)) {
1594
return false;
1595
}
1596
1597
JSAtom* exportName = exportEntry->exportName();
1598
return !exportName || exportNames_.put(exportName);
1599
}
1600
1601
bool ModuleBuilder::maybeAppendRequestedModule(HandleAtom specifier,
1602
frontend::ParseNode* node) {
1603
if (requestedModuleSpecifiers_.has(specifier)) {
1604
return true;
1605
}
1606
1607
uint32_t line;
1608
uint32_t column;
1609
eitherParser_.computeLineAndColumn(node->pn_pos.begin, &line, &column);
1610
1611
JSContext* cx = cx_;
1612
RootedRequestedModuleObject req(
1613
cx, RequestedModuleObject::create(cx, specifier, line, column));
1614
if (!req) {
1615
return false;
1616
}
1617
1618
return FreezeObject(cx, req) && requestedModules_.append(req) &&
1619
requestedModuleSpecifiers_.put(specifier);
1620
}
1621
1622
template <typename T>
1623
ArrayObject* ModuleBuilder::createArray(const JS::Rooted<GCVector<T>>& vector) {
1624
uint32_t length = vector.length();
1625
RootedArrayObject array(cx_, NewDenseFullyAllocatedArray(cx_, length));
1626
if (!array) {
1627
return nullptr;
1628
}
1629
1630
array->setDenseInitializedLength(length);
1631
for (uint32_t i = 0; i < length; i++) {
1632
array->initDenseElement(i, ObjectValue(*vector[i]));
1633
}
1634
1635
return array;
1636
}
1637
1638
template <typename K, typename V>
1639
ArrayObject* ModuleBuilder::createArray(
1640
const JS::Rooted<GCHashMap<K, V>>& map) {