1/*
2 * Copyright (C) 2015 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 "ModuleAnalyzer.h"
28
29#include "ModuleRecord.h"
30
31namespace JSC {
32
33
34ModuleAnalyzer::ModuleAnalyzer(VM& vm, const VariableEnvironment& declaredVariables, const VariableEnvironment& lexicalVariables)
35 : m_vm(&vm)
36 , m_moduleRecord(ModuleRecord::create())
37 , m_declaredVariables(declaredVariables)
38 , m_lexicalVariables(lexicalVariables)
39{
40}
41
42Identifier ModuleAnalyzer::exportedBinding(const RefPtr<UniquedStringImpl>& ident)
43{
44 const auto iterator = m_aliasMap.find(ident);
45 if (iterator != m_aliasMap.end())
46 return iterator->value;
47 return Identifier::fromUid(&vm(), ident.get());
48}
49
50void ModuleAnalyzer::declareExportAlias(const Identifier& localName, const Identifier& exportName)
51{
52 m_aliasMap.add(localName.impl(), exportName);
53}
54
55void ModuleAnalyzer::exportVariable(const RefPtr<UniquedStringImpl>& localName, const VariableEnvironmentEntry& variable)
56{
57 // In the parser, we already marked the variables as Exported and Imported.
58 // By leveraging this information, we collect the information that is needed
59 // to construct the module environment.
60 //
61 // I E
62 // * = exported module local variable
63 // * = imported binding
64 // = non-exported module local variable
65 // * * = indirect exported binding
66 //
67 // One exception is namespace binding (like import * as ns from "mod").
68 // This is annotated as an imported, but the actual binding is locate in the
69 // current module.
70
71 if (!variable.isExported())
72 return;
73
74 const Identifier exportName = exportedBinding(localName);
75
76 // Exported module local variable.
77 if (!variable.isImported()) {
78 moduleRecord().addExportEntry(ModuleRecord::ExportEntry::createLocal(exportName, Identifier::fromUid(m_vm, localName.get()), variable));
79 return;
80 }
81
82 const auto& importEntry = moduleRecord().lookUpImportEntry(localName);
83 if (importEntry.isNamespace(vm())) {
84 // Exported namespace binding.
85 // import * as namespace from "mod"
86 // export { namespace }
87 moduleRecord().addExportEntry(ModuleRecord::ExportEntry::createNamespace(exportName, importEntry.moduleRequest));
88 return;
89 }
90
91 // Indirectly exported binding.
92 // import a from "mod"
93 // export { a }
94 moduleRecord().addExportEntry(ModuleRecord::ExportEntry::createIndirect(exportName, importEntry.importName, importEntry.moduleRequest));
95}
96
97
98
99Ref<ModuleRecord> ModuleAnalyzer::analyze(ModuleProgramNode& moduleProgramNode)
100{
101 // Traverse the module AST and collect
102 // 1. Import entries
103 // 2. Export entries that have FromClause (e.g. export { a } from "mod")
104 // 3. Export entries that have star (e.g. export * from "mod")
105 // 4. Aliased export names (e.g. export { a as b })
106 moduleProgramNode.analyzeModule(*this);
107
108 // Based on the collected information, categorize export entries into 3 types.
109 // 1. Local export entries
110 // This references the local variable in the current module.
111 // This variable should be allocated in the current module environment as a heap variable.
112 //
113 // const variable = 20
114 // export { variable }
115 //
116 // 2. Namespace export entries
117 // This references the namespace object imported by some import entries.
118 // This variable itself should be allocated in the current module environment as a heap variable.
119 // But when the other modules attempt to resolve this export name in this module, this module
120 // should tell the link to the original module.
121 //
122 // import * as namespace from "mod"
123 // export { namespace as mod }
124 //
125 // 3. Indirect export entries
126 // This references the imported binding name from the other module.
127 // This module environment itself should hold the pointer to (1) the original module and
128 // (2) the binding in the original module. The variable itself is allocated in the original
129 // module. This indirect binding is resolved when the CodeBlock resolves the references.
130 //
131 // import mod from "mod"
132 // export { mod }
133 //
134 // export { a } from "mod"
135 //
136 // And separeted from the above 3 types, we also collect the star export entries.
137 //
138 // 4. Star export entries
139 // This exports all the names from the specified external module as the current module's name.
140 //
141 // export * from "mod"
142 for (const auto& pair : m_declaredVariables)
143 exportVariable(pair.key, pair.value);
144
145 for (const auto& pair : m_lexicalVariables)
146 exportVariable(pair.key, pair.value);
147
148 if (Options::dumpModuleRecord())
149 m_moduleRecord->dump();
150
151 return *m_moduleRecord;
152}
153
154} // namespace JSC