1 | /* |
2 | * Copyright (C) 2015-2019 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 "JSModuleRecord.h" |
28 | |
29 | #include "BuiltinNames.h" |
30 | #include "Error.h" |
31 | #include "Interpreter.h" |
32 | #include "JSCInlines.h" |
33 | #include "JSModuleEnvironment.h" |
34 | #include "JSModuleLoader.h" |
35 | #include "JSModuleNamespaceObject.h" |
36 | #include "UnlinkedModuleProgramCodeBlock.h" |
37 | |
38 | namespace JSC { |
39 | |
40 | const ClassInfo JSModuleRecord::s_info = { "ModuleRecord" , &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSModuleRecord) }; |
41 | |
42 | |
43 | Structure* JSModuleRecord::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) |
44 | { |
45 | return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); |
46 | } |
47 | |
48 | JSModuleRecord* JSModuleRecord::create(JSGlobalObject* globalObject, VM& vm, Structure* structure, const Identifier& moduleKey, const SourceCode& sourceCode, const VariableEnvironment& declaredVariables, const VariableEnvironment& lexicalVariables) |
49 | { |
50 | JSModuleRecord* instance = new (NotNull, allocateCell<JSModuleRecord>(vm.heap)) JSModuleRecord(vm, structure, moduleKey, sourceCode, declaredVariables, lexicalVariables); |
51 | instance->finishCreation(globalObject, vm); |
52 | return instance; |
53 | } |
54 | JSModuleRecord::JSModuleRecord(VM& vm, Structure* structure, const Identifier& moduleKey, const SourceCode& sourceCode, const VariableEnvironment& declaredVariables, const VariableEnvironment& lexicalVariables) |
55 | : Base(vm, structure, moduleKey) |
56 | , m_sourceCode(sourceCode) |
57 | , m_declaredVariables(declaredVariables) |
58 | , m_lexicalVariables(lexicalVariables) |
59 | { |
60 | } |
61 | |
62 | void JSModuleRecord::destroy(JSCell* cell) |
63 | { |
64 | JSModuleRecord* thisObject = static_cast<JSModuleRecord*>(cell); |
65 | thisObject->JSModuleRecord::~JSModuleRecord(); |
66 | } |
67 | |
68 | void JSModuleRecord::finishCreation(JSGlobalObject* globalObject, VM& vm) |
69 | { |
70 | Base::finishCreation(globalObject, vm); |
71 | ASSERT(inherits(vm, info())); |
72 | } |
73 | |
74 | void JSModuleRecord::visitChildren(JSCell* cell, SlotVisitor& visitor) |
75 | { |
76 | JSModuleRecord* thisObject = jsCast<JSModuleRecord*>(cell); |
77 | ASSERT_GC_OBJECT_INHERITS(thisObject, info()); |
78 | Base::visitChildren(thisObject, visitor); |
79 | visitor.append(thisObject->m_moduleProgramExecutable); |
80 | } |
81 | |
82 | void JSModuleRecord::link(JSGlobalObject* globalObject, JSValue scriptFetcher) |
83 | { |
84 | VM& vm = globalObject->vm(); |
85 | auto scope = DECLARE_THROW_SCOPE(vm); |
86 | |
87 | ModuleProgramExecutable* executable = ModuleProgramExecutable::create(globalObject, sourceCode()); |
88 | EXCEPTION_ASSERT(!!scope.exception() == !executable); |
89 | if (!executable) { |
90 | throwSyntaxError(globalObject, scope); |
91 | return; |
92 | } |
93 | instantiateDeclarations(globalObject, executable, scriptFetcher); |
94 | RETURN_IF_EXCEPTION(scope, void()); |
95 | m_moduleProgramExecutable.set(vm, this, executable); |
96 | } |
97 | |
98 | void JSModuleRecord::instantiateDeclarations(JSGlobalObject* globalObject, ModuleProgramExecutable* moduleProgramExecutable, JSValue scriptFetcher) |
99 | { |
100 | VM& vm = globalObject->vm(); |
101 | auto scope = DECLARE_THROW_SCOPE(vm); |
102 | |
103 | // http://www.ecma-international.org/ecma-262/6.0/#sec-moduledeclarationinstantiation |
104 | |
105 | SymbolTable* symbolTable = moduleProgramExecutable->moduleEnvironmentSymbolTable(); |
106 | JSModuleEnvironment* moduleEnvironment = JSModuleEnvironment::create(vm, globalObject, globalObject, symbolTable, jsTDZValue(), this); |
107 | |
108 | // http://www.ecma-international.org/ecma-262/6.0/#sec-moduledeclarationinstantiation |
109 | // section 15.2.1.16.4 step 9. |
110 | // Ensure all the indirect exports are correctly resolved to unique bindings. |
111 | // Even if we avoided duplicate exports in the parser, still ambiguous exports occur due to the star export (`export * from "mod"`). |
112 | // When we see this type of ambiguity for the indirect exports here, throw a syntax error. |
113 | for (const auto& pair : exportEntries()) { |
114 | const ExportEntry& exportEntry = pair.value; |
115 | if (exportEntry.type == JSModuleRecord::ExportEntry::Type::Indirect) { |
116 | Resolution resolution = resolveExport(globalObject, exportEntry.exportName); |
117 | RETURN_IF_EXCEPTION(scope, void()); |
118 | switch (resolution.type) { |
119 | case Resolution::Type::NotFound: |
120 | throwSyntaxError(globalObject, scope, makeString("Indirectly exported binding name '" , String(exportEntry.exportName.impl()), "' is not found." )); |
121 | return; |
122 | |
123 | case Resolution::Type::Ambiguous: |
124 | throwSyntaxError(globalObject, scope, makeString("Indirectly exported binding name '" , String(exportEntry.exportName.impl()), "' cannot be resolved due to ambiguous multiple bindings." )); |
125 | return; |
126 | |
127 | case Resolution::Type::Error: |
128 | throwSyntaxError(globalObject, scope, makeString("Indirectly exported binding name 'default' cannot be resolved by star export entries." )); |
129 | return; |
130 | |
131 | case Resolution::Type::Resolved: |
132 | break; |
133 | } |
134 | } |
135 | } |
136 | |
137 | // http://www.ecma-international.org/ecma-262/6.0/#sec-moduledeclarationinstantiation |
138 | // section 15.2.1.16.4 step 12. |
139 | // Instantiate namespace objects and initialize the bindings with them if required. |
140 | // And ensure that all the imports correctly resolved to unique bindings. |
141 | for (const auto& pair : importEntries()) { |
142 | const ImportEntry& importEntry = pair.value; |
143 | AbstractModuleRecord* importedModule = hostResolveImportedModule(globalObject, importEntry.moduleRequest); |
144 | RETURN_IF_EXCEPTION(scope, void()); |
145 | if (importEntry.type == AbstractModuleRecord::ImportEntryType::Namespace) { |
146 | JSModuleNamespaceObject* namespaceObject = importedModule->getModuleNamespace(globalObject); |
147 | RETURN_IF_EXCEPTION(scope, void()); |
148 | bool putResult = false; |
149 | symbolTablePutTouchWatchpointSet(moduleEnvironment, globalObject, importEntry.localName, namespaceObject, /* shouldThrowReadOnlyError */ false, /* ignoreReadOnlyErrors */ true, putResult); |
150 | RETURN_IF_EXCEPTION(scope, void()); |
151 | } else { |
152 | Resolution resolution = importedModule->resolveExport(globalObject, importEntry.importName); |
153 | RETURN_IF_EXCEPTION(scope, void()); |
154 | switch (resolution.type) { |
155 | case Resolution::Type::NotFound: |
156 | throwSyntaxError(globalObject, scope, makeString("Importing binding name '" , String(importEntry.importName.impl()), "' is not found." )); |
157 | return; |
158 | |
159 | case Resolution::Type::Ambiguous: |
160 | throwSyntaxError(globalObject, scope, makeString("Importing binding name '" , String(importEntry.importName.impl()), "' cannot be resolved due to ambiguous multiple bindings." )); |
161 | return; |
162 | |
163 | case Resolution::Type::Error: |
164 | throwSyntaxError(globalObject, scope, makeString("Importing binding name 'default' cannot be resolved by star export entries." )); |
165 | return; |
166 | |
167 | case Resolution::Type::Resolved: |
168 | break; |
169 | } |
170 | } |
171 | } |
172 | |
173 | // http://www.ecma-international.org/ecma-262/6.0/#sec-moduledeclarationinstantiation |
174 | // section 15.2.1.16.4 step 14. |
175 | // Module environment contains the heap allocated "var", "function", "let", "const", and "class". |
176 | // When creating the environment, we initialized all the slots with empty, it's ok for lexical values. |
177 | // But for "var" and "function", we should initialize it with undefined. They are contained in the declared variables. |
178 | for (const auto& variable : declaredVariables()) { |
179 | SymbolTableEntry entry = symbolTable->get(variable.key.get()); |
180 | VarOffset offset = entry.varOffset(); |
181 | if (!offset.isStack()) { |
182 | bool putResult = false; |
183 | symbolTablePutTouchWatchpointSet(moduleEnvironment, globalObject, Identifier::fromUid(vm, variable.key.get()), jsUndefined(), /* shouldThrowReadOnlyError */ false, /* ignoreReadOnlyErrors */ true, putResult); |
184 | RETURN_IF_EXCEPTION(scope, void()); |
185 | } |
186 | } |
187 | |
188 | // http://www.ecma-international.org/ecma-262/6.0/#sec-moduledeclarationinstantiation |
189 | // section 15.2.1.16.4 step 16-a-iv. |
190 | // Initialize heap allocated function declarations. |
191 | // They can be called before the body of the module is executed under circular dependencies. |
192 | UnlinkedModuleProgramCodeBlock* unlinkedCodeBlock = moduleProgramExecutable->unlinkedModuleProgramCodeBlock(); |
193 | for (size_t i = 0, numberOfFunctions = unlinkedCodeBlock->numberOfFunctionDecls(); i < numberOfFunctions; ++i) { |
194 | UnlinkedFunctionExecutable* unlinkedFunctionExecutable = unlinkedCodeBlock->functionDecl(i); |
195 | SymbolTableEntry entry = symbolTable->get(unlinkedFunctionExecutable->name().impl()); |
196 | VarOffset offset = entry.varOffset(); |
197 | if (!offset.isStack()) { |
198 | ASSERT(!unlinkedFunctionExecutable->name().isEmpty()); |
199 | if (vm.typeProfiler() || vm.controlFlowProfiler()) { |
200 | vm.functionHasExecutedCache()->insertUnexecutedRange(moduleProgramExecutable->sourceID(), |
201 | unlinkedFunctionExecutable->typeProfilingStartOffset(), |
202 | unlinkedFunctionExecutable->typeProfilingEndOffset()); |
203 | } |
204 | JSFunction* function = JSFunction::create(vm, unlinkedFunctionExecutable->link(vm, moduleProgramExecutable, moduleProgramExecutable->source()), moduleEnvironment); |
205 | bool putResult = false; |
206 | symbolTablePutTouchWatchpointSet(moduleEnvironment, globalObject, unlinkedFunctionExecutable->name(), function, /* shouldThrowReadOnlyError */ false, /* ignoreReadOnlyErrors */ true, putResult); |
207 | RETURN_IF_EXCEPTION(scope, void()); |
208 | } |
209 | } |
210 | |
211 | { |
212 | JSObject* metaProperties = globalObject->moduleLoader()->createImportMetaProperties(globalObject, identifierToJSValue(vm, moduleKey()), this, scriptFetcher); |
213 | RETURN_IF_EXCEPTION(scope, void()); |
214 | bool putResult = false; |
215 | symbolTablePutTouchWatchpointSet(moduleEnvironment, globalObject, vm.propertyNames->builtinNames().metaPrivateName(), metaProperties, /* shouldThrowReadOnlyError */ false, /* ignoreReadOnlyErrors */ true, putResult); |
216 | RETURN_IF_EXCEPTION(scope, void()); |
217 | } |
218 | |
219 | m_moduleEnvironment.set(vm, this, moduleEnvironment); |
220 | } |
221 | |
222 | JSValue JSModuleRecord::evaluate(JSGlobalObject* globalObject) |
223 | { |
224 | if (!m_moduleProgramExecutable) |
225 | return jsUndefined(); |
226 | VM& vm = globalObject->vm(); |
227 | ModuleProgramExecutable* executable = m_moduleProgramExecutable.get(); |
228 | m_moduleProgramExecutable.clear(); |
229 | return vm.interpreter->executeModuleProgram(executable, globalObject, m_moduleEnvironment.get()); |
230 | } |
231 | |
232 | } // namespace JSC |
233 | |