1/*
2 * Copyright (C) 2015-2016 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "AbstractModuleRecord.h"
28
29#include "Error.h"
30#include "Interpreter.h"
31#include "JSCInlines.h"
32#include "JSMap.h"
33#include "JSModuleEnvironment.h"
34#include "JSModuleNamespaceObject.h"
35#include "UnlinkedModuleProgramCodeBlock.h"
36
37namespace JSC {
38
39const ClassInfo AbstractModuleRecord::s_info = { "AbstractModuleRecord", &Base::s_info, 0, CREATE_METHOD_TABLE(AbstractModuleRecord) };
40
41AbstractModuleRecord::AbstractModuleRecord(VM& vm, Structure* structure, const Identifier& moduleKey, const SourceCode& sourceCode, const VariableEnvironment& declaredVariables, const VariableEnvironment& lexicalVariables)
42 : Base(vm, structure)
43 , m_moduleKey(moduleKey)
44 , m_sourceCode(sourceCode)
45 , m_declaredVariables(declaredVariables)
46 , m_lexicalVariables(lexicalVariables)
47{
48}
49
50void AbstractModuleRecord::destroy(JSCell* cell)
51{
52 AbstractModuleRecord* thisObject = jsCast<AbstractModuleRecord*>(cell);
53 thisObject->AbstractModuleRecord::~AbstractModuleRecord();
54}
55
56void AbstractModuleRecord::finishCreation(ExecState* exec, VM& vm)
57{
58 Base::finishCreation(vm);
59 ASSERT(inherits(info()));
60 putDirect(vm, Identifier::fromString(&vm, ASCIILiteral("registryEntry")), jsUndefined());
61 putDirect(vm, Identifier::fromString(&vm, ASCIILiteral("evaluated")), jsBoolean(false));
62
63 auto scope = DECLARE_THROW_SCOPE(vm);
64 JSMap* map = JSMap::create(exec, vm, globalObject()->mapStructure());
65 RELEASE_ASSERT(!scope.exception());
66 m_dependenciesMap.set(vm, this, map);
67 putDirect(vm, Identifier::fromString(&vm, ASCIILiteral("dependenciesMap")), m_dependenciesMap.get());
68}
69
70void AbstractModuleRecord::visitChildren(JSCell* cell, SlotVisitor& visitor)
71{
72 AbstractModuleRecord* thisObject = jsCast<AbstractModuleRecord*>(cell);
73 Base::visitChildren(thisObject, visitor);
74 visitor.append(&thisObject->m_moduleEnvironment);
75 visitor.append(&thisObject->m_moduleNamespaceObject);
76 visitor.append(&thisObject->m_dependenciesMap);
77}
78
79void AbstractModuleRecord::appendRequestedModule(const Identifier& moduleName)
80{
81 m_requestedModules.add(moduleName.impl());
82}
83
84void AbstractModuleRecord::addStarExportEntry(const Identifier& moduleName)
85{
86 m_starExportEntries.add(moduleName.impl());
87}
88
89void AbstractModuleRecord::addImportEntry(const ImportEntry& entry)
90{
91 bool isNewEntry = m_importEntries.add(entry.localName.impl(), entry).isNewEntry;
92 ASSERT_UNUSED(isNewEntry, isNewEntry); // This is guaranteed by the parser.
93}
94
95void AbstractModuleRecord::addExportEntry(const ExportEntry& entry)
96{
97 bool isNewEntry = m_exportEntries.add(entry.exportName.impl(), entry).isNewEntry;
98 ASSERT_UNUSED(isNewEntry, isNewEntry); // This is guaranteed by the parser.
99}
100
101auto AbstractModuleRecord::tryGetImportEntry(UniquedStringImpl* localName) -> std::optional<ImportEntry>
102{
103 const auto iterator = m_importEntries.find(localName);
104 if (iterator == m_importEntries.end())
105 return std::nullopt;
106 return std::optional<ImportEntry>(iterator->value);
107}
108
109auto AbstractModuleRecord::tryGetExportEntry(UniquedStringImpl* exportName) -> std::optional<ExportEntry>
110{
111 const auto iterator = m_exportEntries.find(exportName);
112 if (iterator == m_exportEntries.end())
113 return std::nullopt;
114 return std::optional<ExportEntry>(iterator->value);
115}
116
117auto AbstractModuleRecord::ExportEntry::createLocal(const Identifier& exportName, const Identifier& localName) -> ExportEntry
118{
119 return ExportEntry { Type::Local, exportName, Identifier(), Identifier(), localName };
120}
121
122auto AbstractModuleRecord::ExportEntry::createIndirect(const Identifier& exportName, const Identifier& importName, const Identifier& moduleName) -> ExportEntry
123{
124 return ExportEntry { Type::Indirect, exportName, moduleName, importName, Identifier() };
125}
126
127auto AbstractModuleRecord::Resolution::notFound() -> Resolution
128{
129 return Resolution { Type::NotFound, nullptr, Identifier() };
130}
131
132auto AbstractModuleRecord::Resolution::error() -> Resolution
133{
134 return Resolution { Type::Error, nullptr, Identifier() };
135}
136
137auto AbstractModuleRecord::Resolution::ambiguous() -> Resolution
138{
139 return Resolution { Type::Ambiguous, nullptr, Identifier() };
140}
141
142static JSValue identifierToJSValue(ExecState* exec, const Identifier& identifier)
143{
144 if (identifier.isSymbol())
145 return Symbol::create(exec->vm(), static_cast<SymbolImpl&>(*identifier.impl()));
146 return jsString(&exec->vm(), identifier.impl());
147}
148
149AbstractModuleRecord* AbstractModuleRecord::hostResolveImportedModule(ExecState* exec, const Identifier& moduleName)
150{
151 JSValue moduleNameValue = identifierToJSValue(exec, moduleName);
152 JSValue pair = m_dependenciesMap->JSMap::get(exec, moduleNameValue);
153 return jsCast<AbstractModuleRecord*>(pair.get(exec, Identifier::fromString(exec, "value")));
154}
155
156auto AbstractModuleRecord::resolveImport(ExecState* exec, const Identifier& localName) -> Resolution
157{
158 std::optional<ImportEntry> optionalImportEntry = tryGetImportEntry(localName.impl());
159 if (!optionalImportEntry)
160 return Resolution::notFound();
161
162 const ImportEntry& importEntry = *optionalImportEntry;
163 if (importEntry.isNamespace(exec->vm()))
164 return Resolution::notFound();
165
166 AbstractModuleRecord* importedModule = hostResolveImportedModule(exec, importEntry.moduleRequest);
167 return importedModule->resolveExport(exec, importEntry.importName);
168}
169
170struct AbstractModuleRecord::ResolveQuery {
171 struct Hash {
172 static unsigned hash(const ResolveQuery&);
173 static bool equal(const ResolveQuery&, const ResolveQuery&);
174 static const bool safeToCompareToEmptyOrDeleted = true;
175 };
176
177 ResolveQuery(AbstractModuleRecord* moduleRecord, UniquedStringImpl* exportName)
178 : moduleRecord(moduleRecord)
179 , exportName(exportName)
180 {
181 }
182
183 ResolveQuery(AbstractModuleRecord* moduleRecord, const Identifier& exportName)
184 : ResolveQuery(moduleRecord, exportName.impl())
185 {
186 }
187
188 enum EmptyValueTag { EmptyValue };
189 ResolveQuery(EmptyValueTag)
190 {
191 }
192
193 enum DeletedValueTag { DeletedValue };
194 ResolveQuery(DeletedValueTag)
195 : moduleRecord(nullptr)
196 , exportName(WTF::HashTableDeletedValue)
197 {
198 }
199
200 bool isEmptyValue() const
201 {
202 return !exportName;
203 }
204
205 bool isDeletedValue() const
206 {
207 return exportName.isHashTableDeletedValue();
208 }
209
210 // The module record is not marked from the GC. But these records are reachable from the JSGlobalObject.
211 // So we don't care the reachability to this record.
212 AbstractModuleRecord* moduleRecord;
213 RefPtr<UniquedStringImpl> exportName;
214};
215
216inline unsigned AbstractModuleRecord::ResolveQuery::Hash::hash(const ResolveQuery& query)
217{
218 return WTF::PtrHash<AbstractModuleRecord*>::hash(query.moduleRecord) + IdentifierRepHash::hash(query.exportName);
219}
220
221inline bool AbstractModuleRecord::ResolveQuery::Hash::equal(const ResolveQuery& lhs, const ResolveQuery& rhs)
222{
223 return lhs.moduleRecord == rhs.moduleRecord && lhs.exportName == rhs.exportName;
224}
225
226auto AbstractModuleRecord::tryGetCachedResolution(UniquedStringImpl* exportName) -> std::optional<Resolution>
227{
228 const auto iterator = m_resolutionCache.find(exportName);
229 if (iterator == m_resolutionCache.end())
230 return std::nullopt;
231 return std::optional<Resolution>(iterator->value);
232}
233
234void AbstractModuleRecord::cacheResolution(UniquedStringImpl* exportName, const Resolution& resolution)
235{
236 m_resolutionCache.add(exportName, resolution);
237}
238
239auto AbstractModuleRecord::resolveExportImpl(ExecState* exec, const ResolveQuery& root) -> Resolution
240{
241 // http://www.ecma-international.org/ecma-262/6.0/#sec-resolveexport
242
243 // How to avoid C++ recursion in this function:
244 // This function avoids C++ recursion of the naive ResolveExport implementation.
245 // Flatten the recursion to the loop with the task queue and frames.
246 //
247 // 1. pendingTasks
248 // We enqueue the recursive resolveExport call to this queue to avoid recursive calls in C++.
249 // The task has 3 types. (1) Query, (2) IndirectFallback and (3) GatherStars.
250 // (1) Query
251 // Querying the resolution to the current module.
252 // (2) IndirectFallback
253 // Examine the result of the indirect export resolution. Only when the indirect export resolution fails,
254 // we look into the star exports. (step 5-a-vi).
255 // (3) GatherStars
256 // Examine the result of the star export resolutions.
257 //
258 // 2. frames
259 // When the spec calls the resolveExport recursively, instead we append the frame
260 // (that holds the result resolution) to the frames and enqueue the task to the pendingTasks.
261 // The entry in the frames means the *local* resolution result of the specific recursive resolveExport.
262 //
263 // We should maintain the local resolution result instead of holding the global resolution result only.
264 // For example,
265 //
266 // star
267 // (1) ---> (2) "Resolve"
268 // |
269 // |
270 // +-> (3) "NotFound"
271 // |
272 // | star
273 // +-> (4) ---> (5) "Resolve" [here]
274 // |
275 // |
276 // +-> (6) "Error"
277 //
278 // Consider the above graph. The numbers represents the modules. Now we are [here].
279 // If we only hold the global resolution result during the resolveExport operation, [here],
280 // we decide the entire result of resolveExport is "Ambiguous", because there are multiple
281 // "Resolve" (in module (2) and (5)). However, this should become "Error" because (6) will
282 // propagate "Error" state to the (4), (4) will become "Error" and then, (1) will become
283 // "Error". We should aggregate the results at the star exports point ((4) and (1)).
284 //
285 // Usually, both "Error" and "Ambiguous" states will throw the syntax error. So except for the content of the
286 // error message, there are no difference. (And if we fix the (6) that raises "Error", next, it will produce
287 // the "Ambiguous" error due to (5). Anyway, user need to fix the both. So which error should be raised at first
288 // doesn't matter so much.
289 //
290 // However, this may become the problem under the module namespace creation.
291 // http://www.ecma-international.org/ecma-262/6.0/#sec-getmodulenamespace
292 // section 15.2.1.18, step 3-d-ii
293 // Here, we distinguish "Ambiguous" and "Error". When "Error" state is produced, we need to throw the propagated error.
294 // But if "Ambiguous" state comes, we just ignore the result.
295 // To follow the requirement strictly, in this implementation, we keep the local resolution result to produce the
296 // correct result under the above complex cases.
297
298 // Caching strategy:
299 // The resolveExport operation is frequently called. So caching results is important.
300 // We observe the following aspects and based on them construct the caching strategy.
301 // Here, we attempt to cache the resolution by constructing the map in module records.
302 // That means Module -> ExportName -> Maybe<Resolution>.
303 // Technically, all the AbstractModuleRecords have the Map<ExportName, Resolution> for caching.
304 //
305 // The important observations are that,
306 //
307 // - *cacheable* means that traversing to this node from a path will produce the same results as starting from this node.
308 //
309 // Here, we define the resovling route. We represent [?] as the module that has the local binding.
310 // And (?) as the module without the local binding.
311 //
312 // @ -> (A) -> (B) -> [C]
313 //
314 // We list the resolving route for each node.
315 //
316 // (A): (A) -> (B) -> [C]
317 // (B): (B) -> [C]
318 // [C]: [C]
319 //
320 // In this case, if we start the tracing from (B), the resolving route becomes (B) -> [C].
321 // So this is the same. At that time, we can say (B) is cacheable in the first tracing.
322 //
323 // - The cache ability of a node depends on the resolving route from this node.
324 //
325 // 1. The starting point is always cacheable.
326 //
327 // 2. A module that has resolved a local binding is always cacheable.
328 //
329 // @ -> (A) -> [B]
330 //
331 // In the above case, we can see the [B] as cacheable.
332 // This is because when starting from [B] node, we immediately resolve with the local binding.
333 // So the resolving route from [B] does not depend on the starting point.
334 //
335 // 3. If we don't follow any star links during the resolution, we can see all the traced nodes are cacheable.
336 //
337 // If there are non star links, it means that there is *no branch* in the module dependency graph.
338 // This *no branch* feature makes all the modules cachable.
339 //
340 // I.e, if we traverse one star link (even if we successfully resolve that star link),
341 // we must still traverse all other star links. I would also explain we don't run into
342 // this when resolving a local/indirect link. When resolving a local/indirect link,
343 // we won't traverse any star links.
344 // And since the module can hold only one local/indirect link for the specific export name (if there
345 // are multiple local/indirect links that has the same export name, it should be syntax error in the
346 // parsing phase.), there is no multiple outgoing links from a module.
347 //
348 // @ -> (A) --> (B) -> [C] -> (D) -> (E) -+
349 // ^ |
350 // | |
351 // +------------------------+
352 //
353 // When starting from @, [C] will be found as the module resolving the given binding.
354 // In this case, (B) can cache this resolution. Since the resolving route is the same to the one when
355 // starting from (B). After caching the above result, we attempt to resolve the same binding from (D).
356 //
357 // @
358 // |
359 // v
360 // @ -> (A) --> (B) -> [C] -> (D) -> (E) -+
361 // ^ |
362 // | |
363 // +------------------------+
364 //
365 // In this case, we can use the (B)'s cached result. And (E) can be cached.
366 //
367 // (E): The resolving route is now (E) -> (B) -> [C]. That is the same when starting from (E).
368 //
369 // No branching makes that the problematic *once-visited* node cannot be seen.
370 // The *once-visited* node makes the resolving route changed since when we see the *once-visited* node,
371 // we stop tracing this.
372 //
373 // If there is no star links and if we look *once-visited* node under no branching graph, *once-visited*
374 // node cannot resolve the requested binding. If the *once-visited* node can resolve the binding, we
375 // should have already finished the resolution before reaching this *once-visited* node.
376 //
377 // 4. Once we follow star links, we should not retrieve the result from the cache and should not cache.
378 //
379 // Star links are only the way to introduce branch.
380 // Once we follow the star links during the resolution, we cannot cache naively.
381 // This is because the cacheability depends on the resolving route. And branching produces the problematic *once-visited*
382 // nodes. Since we don't follow the *once-visited* node, the resolving route from the node becomes different from
383 // the resolving route when starting from this node.
384 //
385 // The following example explains when we should not retrieve the cache and cache the result.
386 //
387 // +----> (D) ------+
388 // | |
389 // | v
390 // (A) *----+----> (B) ---> [C]
391 // ^
392 // |
393 // @
394 //
395 // When starting from (B), we find [C]. In this resolving route, we don't find any star link.
396 // And by definition, (B) and [C] are cachable. (B) is the starting point. And [C] has the local binding.
397 //
398 // +----> (D) ------+
399 // | |
400 // | v
401 // @-> (A) *----+----> (B) ---> [C]
402 //
403 // But when starting from (A), we should not get the value from the cache. Because,
404 //
405 // 1. When looking (D), we reach [C] and make both resolved.
406 // 2. When looking (B), if we retrieved the last cache from (B), (B) becomes resolved.
407 // 3. But actually, (B) is not-found in this trial because (C) is already *once-visited*.
408 // 4. If we accidentally make (B) resolved, (A) becomes ambiguous. But the correct answer is resolved.
409 //
410 // Why is this problem caused? This is because the *once-visited* node makes the result not-found.
411 // In the second trial, (B) -> [C] result is changed from resolved to not-found.
412 //
413 // When does this become a problem? If the status of the *once-visited* node group is resolved,
414 // changing the result to not-found makes the result changed.
415 //
416 // This problem does not happen when we don't see any star link yet. Now, consider the minimum case.
417 //
418 // @-> (A) -> [ some graph ]
419 // ^ |
420 // | |
421 // +------------+
422 //
423 // In (A), we don't see any star link yet. So we can say that all the visited nodes does not have any local
424 // resolution. Because if they had a local/indirect resolution, we should have already finished the tracing.
425 //
426 // And even if the some graph will see the *once-visited* node (in this case, (A)), that does not affect the
427 // result of the resolution. Because even if we follow the link to (A) or not follow the link to (A), the status
428 // of the link is always not-found since (A) does not have any local resolution.
429 // In the above case, we can use the result of the [some graph].
430 //
431 // 5. Once we see star links, even if we have not yet traversed that star link path, we should disable caching.
432 //
433 // Here is the reason why:
434 //
435 // +-------------+
436 // | |
437 // v |
438 // (A) -> (B) -> (C) *-> [E]
439 // * ^
440 // | |
441 // v @
442 // [D]
443 //
444 // In the above case, (C) will be resolved with [D].
445 // (C) will see (A) and (A) gives up in (A) -> (B) -> (C) route. So, (A) will fallback to [D].
446 //
447 // +-------------+
448 // | |
449 // v |
450 // @-> (A) -> (B) -> (C) *-> [E]
451 // *
452 // |
453 // v
454 // [D]
455 //
456 // But in this case, (A) will be resolved with [E] (not [D]).
457 // (C) will attempt to follow the link to (A), but it fails.
458 // So (C) will fallback to the star link and found [E]. In this senario,
459 // (C) is now resolved with [E]'s result.
460 //
461 // The cause of this problem is also the same to 4.
462 // In the latter case, when looking (C), we cannot use the cached result in (C).
463 // Because the cached result of (C) depends on the *once-visited* node (A) and
464 // (A) has the fallback system with the star link.
465 // In the latter trial, we now assume that (A)'s status is not-found.
466 // But, actually, in the former trial, (A)'s status becomes resolved due to the fallback to the [D].
467 //
468 // To summarize the observations.
469 //
470 // 1. The starting point is always cacheable.
471 // 2. A module that has resolved a local binding is always cacheable.
472 // 3. If we don't follow any star links during the resolution, we can see all the traced nodes are cacheable.
473 // 4. Once we follow star links, we should not retrieve the result from the cache and should not cache the result.
474 // 5. Once we see star links, even if we have not yet traversed that star link path, we should disable caching.
475
476 typedef WTF::HashSet<ResolveQuery, ResolveQuery::Hash, WTF::CustomHashTraits<ResolveQuery>> ResolveSet;
477 enum class Type { Query, IndirectFallback, GatherStars };
478 struct Task {
479 ResolveQuery query;
480 Type type;
481 };
482
483 Vector<Task, 8> pendingTasks;
484 ResolveSet resolveSet;
485 HashSet<AbstractModuleRecord*> starSet;
486
487 Vector<Resolution, 8> frames;
488
489 bool foundStarLinks = false;
490
491 frames.append(Resolution::notFound());
492
493 // Call when the query is not resolved in the current module.
494 // It will enqueue the star resolution requests. Return "false" if the error occurs.
495 auto resolveNonLocal = [&](const ResolveQuery& query) -> bool {
496 // http://www.ecma-international.org/ecma-262/6.0/#sec-resolveexport
497 // section 15.2.1.16.3, step 6
498 // If the "default" name is not resolved in the current module, we need to throw an error and stop resolution immediately,
499 // Rationale to this error: A default export cannot be provided by an export *.
500 if (query.exportName == exec->propertyNames().defaultKeyword.impl())
501 return false;
502
503 // step 7, If exportStarSet contains module, then return null.
504 if (!starSet.add(query.moduleRecord).isNewEntry)
505 return true;
506
507 // Enqueue the task to gather the results of the stars.
508 // And append the new Resolution frame to gather the local result of the stars.
509 pendingTasks.append(Task { query, Type::GatherStars });
510 foundStarLinks = true;
511 frames.append(Resolution::notFound());
512
513
514 // Enqueue the tasks in reverse order.
515 for (auto iterator = query.moduleRecord->starExportEntries().rbegin(), end = query.moduleRecord->starExportEntries().rend(); iterator != end; ++iterator) {
516 const RefPtr<UniquedStringImpl>& starModuleName = *iterator;
517 AbstractModuleRecord* importedModuleRecord = query.moduleRecord->hostResolveImportedModule(exec, Identifier::fromUid(exec, starModuleName.get()));
518 pendingTasks.append(Task { ResolveQuery(importedModuleRecord, query.exportName.get()), Type::Query });
519 }
520 return true;
521 };
522
523 // Return the current resolution value of the top frame.
524 auto currentTop = [&] () -> Resolution& {
525 ASSERT(!frames.isEmpty());
526 return frames.last();
527 };
528
529 // Merge the given resolution to the current resolution value of the top frame.
530 // If there is ambiguity, return "false". When the "false" is returned, we should make the result "ambiguous".
531 auto mergeToCurrentTop = [&] (const Resolution& resolution) -> bool {
532 if (resolution.type == Resolution::Type::NotFound)
533 return true;
534
535 if (currentTop().type == Resolution::Type::NotFound) {
536 currentTop() = resolution;
537 return true;
538 }
539
540 if (currentTop().moduleRecord != resolution.moduleRecord || currentTop().localName != resolution.localName)
541 return false;
542
543 return true;
544 };
545
546 auto cacheResolutionForQuery = [] (const ResolveQuery& query, const Resolution& resolution) {
547 ASSERT(resolution.type == Resolution::Type::Resolved);
548 query.moduleRecord->cacheResolution(query.exportName.get(), resolution);
549 };
550
551 pendingTasks.append(Task { root, Type::Query });
552 while (!pendingTasks.isEmpty()) {
553 const Task task = pendingTasks.takeLast();
554 const ResolveQuery& query = task.query;
555
556 switch (task.type) {
557 case Type::Query: {
558 AbstractModuleRecord* moduleRecord = query.moduleRecord;
559
560 if (!resolveSet.add(task.query).isNewEntry)
561 continue;
562
563 // 5. Once we see star links, even if we have not yet traversed that star link path, we should disable caching.
564 if (!moduleRecord->starExportEntries().isEmpty())
565 foundStarLinks = true;
566
567 // 4. Once we follow star links, we should not retrieve the result from the cache and should not cache the result.
568 if (!foundStarLinks) {
569 if (std::optional<Resolution> cachedResolution = moduleRecord->tryGetCachedResolution(query.exportName.get())) {
570 if (!mergeToCurrentTop(*cachedResolution))
571 return Resolution::ambiguous();
572 continue;
573 }
574 }
575
576 const std::optional<ExportEntry> optionalExportEntry = moduleRecord->tryGetExportEntry(query.exportName.get());
577 if (!optionalExportEntry) {
578 // If there is no matched exported binding in the current module,
579 // we need to look into the stars.
580 if (!resolveNonLocal(task.query))
581 return Resolution::error();
582 continue;
583 }
584
585 const ExportEntry& exportEntry = *optionalExportEntry;
586 switch (exportEntry.type) {
587 case ExportEntry::Type::Local: {
588 ASSERT(!exportEntry.localName.isNull());
589 Resolution resolution { Resolution::Type::Resolved, moduleRecord, exportEntry.localName };
590 // 2. A module that has resolved a local binding is always cacheable.
591 cacheResolutionForQuery(query, resolution);
592 if (!mergeToCurrentTop(resolution))
593 return Resolution::ambiguous();
594 continue;
595 }
596
597 case ExportEntry::Type::Indirect: {
598 AbstractModuleRecord* importedModuleRecord = moduleRecord->hostResolveImportedModule(exec, exportEntry.moduleName);
599
600 // When the imported module does not produce any resolved binding, we need to look into the stars in the *current*
601 // module. To do this, we append the `IndirectFallback` task to the task queue.
602 pendingTasks.append(Task { query, Type::IndirectFallback });
603 // And append the new Resolution frame to check the indirect export will be resolved or not.
604 frames.append(Resolution::notFound());
605 pendingTasks.append(Task { ResolveQuery(importedModuleRecord, exportEntry.importName), Type::Query });
606 continue;
607 }
608 }
609 break;
610 }
611
612 case Type::IndirectFallback: {
613 Resolution resolution = frames.takeLast();
614
615 if (resolution.type == Resolution::Type::NotFound) {
616 // Indirect export entry does not produce any resolved binding.
617 // So we will investigate the stars.
618 if (!resolveNonLocal(task.query))
619 return Resolution::error();
620 continue;
621 }
622
623 ASSERT_WITH_MESSAGE(resolution.type == Resolution::Type::Resolved, "When we see Error and Ambiguous, we immediately return from this loop. So here, only Resolved comes.");
624
625 // 3. If we don't follow any star links during the resolution, we can see all the traced nodes are cacheable.
626 // 4. Once we follow star links, we should not retrieve the result from the cache and should not cache the result.
627 if (!foundStarLinks)
628 cacheResolutionForQuery(query, resolution);
629
630 // If indirect export entry produces Resolved, we should merge it to the upper frame.
631 // And do not investigate the stars of the current module.
632 if (!mergeToCurrentTop(resolution))
633 return Resolution::ambiguous();
634 break;
635 }
636
637 case Type::GatherStars: {
638 Resolution resolution = frames.takeLast();
639 ASSERT_WITH_MESSAGE(resolution.type == Resolution::Type::Resolved || resolution.type == Resolution::Type::NotFound, "When we see Error and Ambiguous, we immediately return from this loop. So here, only Resolved and NotFound comes.");
640
641 // Merge the star resolution to the upper frame.
642 if (!mergeToCurrentTop(resolution))
643 return Resolution::ambiguous();
644 break;
645 }
646 }
647 }
648
649 ASSERT(frames.size() == 1);
650 // 1. The starting point is always cacheable.
651 if (frames[0].type == Resolution::Type::Resolved)
652 cacheResolutionForQuery(root, frames[0]);
653 return frames[0];
654}
655
656auto AbstractModuleRecord::resolveExport(ExecState* exec, const Identifier& exportName) -> Resolution
657{
658 // Look up the cached resolution first before entering the resolving loop, since the loop setup takes some cost.
659 if (std::optional<Resolution> cachedResolution = tryGetCachedResolution(exportName.impl()))
660 return *cachedResolution;
661 return resolveExportImpl(exec, ResolveQuery(this, exportName.impl()));
662}
663
664static void getExportedNames(ExecState* exec, AbstractModuleRecord* root, IdentifierSet& exportedNames)
665{
666 HashSet<AbstractModuleRecord*> exportStarSet;
667 Vector<AbstractModuleRecord*, 8> pendingModules;
668
669 pendingModules.append(root);
670
671 while (!pendingModules.isEmpty()) {
672 AbstractModuleRecord* moduleRecord = pendingModules.takeLast();
673 if (exportStarSet.contains(moduleRecord))
674 continue;
675 exportStarSet.add(moduleRecord);
676
677 for (const auto& pair : moduleRecord->exportEntries()) {
678 const AbstractModuleRecord::ExportEntry& exportEntry = pair.value;
679 if (moduleRecord == root || exec->propertyNames().defaultKeyword != exportEntry.exportName)
680 exportedNames.add(exportEntry.exportName.impl());
681 }
682
683 for (const auto& starModuleName : moduleRecord->starExportEntries()) {
684 AbstractModuleRecord* requestedModuleRecord = moduleRecord->hostResolveImportedModule(exec, Identifier::fromUid(exec, starModuleName.get()));
685 pendingModules.append(requestedModuleRecord);
686 }
687 }
688}
689
690JSModuleNamespaceObject* AbstractModuleRecord::getModuleNamespace(ExecState* exec)
691{
692 VM& vm = exec->vm();
693 auto scope = DECLARE_THROW_SCOPE(vm);
694
695 // http://www.ecma-international.org/ecma-262/6.0/#sec-getmodulenamespace
696 if (m_moduleNamespaceObject)
697 return m_moduleNamespaceObject.get();
698
699 JSGlobalObject* globalObject = exec->lexicalGlobalObject();
700 IdentifierSet exportedNames;
701 getExportedNames(exec, this, exportedNames);
702
703 IdentifierSet unambiguousNames;
704 for (auto& name : exportedNames) {
705 const AbstractModuleRecord::Resolution resolution = resolveExport(exec, Identifier::fromUid(exec, name.get()));
706 switch (resolution.type) {
707 case Resolution::Type::NotFound:
708 throwSyntaxError(exec, scope, makeString("Exported binding name '", String(name.get()), "' is not found."));
709 return nullptr;
710
711 case Resolution::Type::Error:
712 throwSyntaxError(exec, scope, makeString("Exported binding name 'default' cannot be resolved by star export entries."));
713 return nullptr;
714
715 case Resolution::Type::Ambiguous:
716 break;
717
718 case Resolution::Type::Resolved:
719 unambiguousNames.add(name);
720 break;
721 }
722 }
723
724 m_moduleNamespaceObject.set(vm, this, JSModuleNamespaceObject::create(exec, globalObject, globalObject->moduleNamespaceObjectStructure(), this, unambiguousNames));
725 return m_moduleNamespaceObject.get();
726}
727
728static String printableName(const RefPtr<UniquedStringImpl>& uid)
729{
730 if (uid->isSymbol())
731 return uid.get();
732 return WTF::makeString("'", String(uid.get()), "'");
733}
734
735static String printableName(const Identifier& ident)
736{
737 return printableName(ident.impl());
738}
739
740void AbstractModuleRecord::dump()
741{
742 dataLog("\nAnalyzing ModuleRecord key(", printableName(m_moduleKey), ")\n");
743
744 dataLog(" Dependencies: ", m_requestedModules.size(), " modules\n");
745 for (const auto& moduleName : m_requestedModules)
746 dataLog(" module(", printableName(moduleName), ")\n");
747
748 dataLog(" Import: ", m_importEntries.size(), " entries\n");
749 for (const auto& pair : m_importEntries) {
750 const ImportEntry& importEntry = pair.value;
751 dataLog(" import(", printableName(importEntry.importName), "), local(", printableName(importEntry.localName), "), module(", printableName(importEntry.moduleRequest), ")\n");
752 }
753
754 dataLog(" Export: ", m_exportEntries.size(), " entries\n");
755 for (const auto& pair : m_exportEntries) {
756 const ExportEntry& exportEntry = pair.value;
757 switch (exportEntry.type) {
758 case ExportEntry::Type::Local:
759 dataLog(" [Local] ", "export(", printableName(exportEntry.exportName), "), local(", printableName(exportEntry.localName), ")\n");
760 break;
761
762 case ExportEntry::Type::Indirect:
763 dataLog(" [Indirect] ", "export(", printableName(exportEntry.exportName), "), import(", printableName(exportEntry.importName), "), module(", printableName(exportEntry.moduleName), ")\n");
764 break;
765 }
766 }
767 for (const auto& moduleName : m_starExportEntries)
768 dataLog(" [Star] module(", printableName(moduleName.get()), ")\n");
769}
770
771} // namespace JSC