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