[ES6] Cache the resolution result in JSModuleRecord
[WebKit-https.git] / Source / JavaScriptCore / runtime / JSModuleRecord.h
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 #ifndef JSModuleRecord_h
27 #define JSModuleRecord_h
28
29 #include "Identifier.h"
30 #include "JSDestructibleObject.h"
31 #include "SourceCode.h"
32 #include "VariableEnvironment.h"
33 #include <wtf/HashMap.h>
34 #include <wtf/ListHashSet.h>
35 #include <wtf/Optional.h>
36
37 namespace JSC {
38
39 class JSModuleNamespaceObject;
40 class JSModuleEnvironment;
41 class JSMap;
42 class ModuleProgramExecutable;
43
44 // Based on the Source Text Module Record
45 // http://www.ecma-international.org/ecma-262/6.0/#sec-source-text-module-records
46 class JSModuleRecord : public JSDestructibleObject {
47     friend class LLIntOffsetsExtractor;
48 public:
49     typedef JSDestructibleObject Base;
50
51     struct ExportEntry {
52         enum class Type {
53             Local,
54             Namespace,
55             Indirect
56         };
57
58         static ExportEntry createLocal(const Identifier& exportName, const Identifier& localName, const VariableEnvironmentEntry&);
59         static ExportEntry createNamespace(const Identifier& exportName, const Identifier& moduleName);
60         static ExportEntry createIndirect(const Identifier& exportName, const Identifier& importName, const Identifier& moduleName);
61
62         Type type;
63         Identifier exportName;
64         Identifier moduleName;
65         Identifier importName;
66         Identifier localName;
67         VariableEnvironmentEntry variable;
68     };
69
70     struct ImportEntry {
71         Identifier moduleRequest;
72         Identifier importName;
73         Identifier localName;
74
75         bool isNamespace(VM& vm) const
76         {
77             return importName == vm.propertyNames->timesIdentifier;
78         }
79     };
80
81     typedef WTF::ListHashSet<RefPtr<UniquedStringImpl>, IdentifierRepHash> OrderedIdentifierSet;
82     typedef HashMap<RefPtr<UniquedStringImpl>, ImportEntry, IdentifierRepHash, HashTraits<RefPtr<UniquedStringImpl>>> ImportEntries;
83     typedef HashMap<RefPtr<UniquedStringImpl>, ExportEntry, IdentifierRepHash, HashTraits<RefPtr<UniquedStringImpl>>> ExportEntries;
84
85     DECLARE_EXPORT_INFO;
86
87     static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
88     {
89         return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info());
90     }
91
92     static JSModuleRecord* create(VM& vm, Structure* structure, const Identifier& moduleKey, const SourceCode& sourceCode, const VariableEnvironment& declaredVariables, const VariableEnvironment& lexicalVariables)
93     {
94         JSModuleRecord* instance = new (NotNull, allocateCell<JSModuleRecord>(vm.heap)) JSModuleRecord(vm, structure, moduleKey, sourceCode, declaredVariables, lexicalVariables);
95         instance->finishCreation(vm);
96         return instance;
97     }
98
99     void appendRequestedModule(const Identifier&);
100     void addStarExportEntry(const Identifier&);
101     void addImportEntry(const ImportEntry&);
102     void addExportEntry(const ExportEntry&);
103
104     Optional<ImportEntry> tryGetImportEntry(UniquedStringImpl* localName);
105     Optional<ExportEntry> tryGetExportEntry(UniquedStringImpl* exportName);
106
107     const SourceCode& sourceCode() const { return m_sourceCode; }
108     const Identifier& moduleKey() const { return m_moduleKey; }
109     const OrderedIdentifierSet& requestedModules() const { return m_requestedModules; }
110     const ExportEntries& exportEntries() const { return m_exportEntries; }
111     const ImportEntries& importEntries() const { return m_importEntries; }
112     const OrderedIdentifierSet& starExportEntries() const { return m_starExportEntries; }
113
114     const VariableEnvironment& declaredVariables() const { return m_declaredVariables; }
115     const VariableEnvironment& lexicalVariables() const { return m_lexicalVariables; }
116
117     void dump();
118
119     JSModuleEnvironment* moduleEnvironment()
120     {
121         ASSERT(m_moduleEnvironment);
122         return m_moduleEnvironment.get();
123     }
124
125     void link(ExecState*);
126     JSValue execute(ExecState*);
127
128     ModuleProgramExecutable* moduleProgramExecutable() const { return m_moduleProgramExecutable.get(); }
129
130     struct Resolution {
131         enum class Type { Resolved, NotFound, Ambiguous, Error };
132
133         static Resolution notFound();
134         static Resolution error();
135         static Resolution ambiguous();
136
137         Type type;
138         JSModuleRecord* moduleRecord;
139         Identifier localName;
140     };
141
142     Resolution resolveExport(ExecState*, const Identifier& exportName);
143     Resolution resolveImport(ExecState*, const Identifier& localName);
144
145     JSModuleRecord* hostResolveImportedModule(ExecState*, const Identifier& moduleName);
146
147 private:
148     JSModuleRecord(VM& vm, Structure* structure, const Identifier& moduleKey, const SourceCode& sourceCode, const VariableEnvironment& declaredVariables, const VariableEnvironment& lexicalVariables)
149         : Base(vm, structure)
150         , m_moduleKey(moduleKey)
151         , m_sourceCode(sourceCode)
152         , m_declaredVariables(declaredVariables)
153         , m_lexicalVariables(lexicalVariables)
154     {
155     }
156
157     void finishCreation(VM&);
158
159     JSModuleNamespaceObject* getModuleNamespace(ExecState*);
160
161     static void visitChildren(JSCell*, SlotVisitor&);
162     static void destroy(JSCell*);
163
164     void instantiateDeclarations(ExecState*, ModuleProgramExecutable*);
165
166     struct ResolveQuery;
167     static Resolution resolveExportImpl(ExecState*, const ResolveQuery&);
168     Optional<Resolution> tryGetCachedResolution(UniquedStringImpl* exportName);
169     void cacheResolution(UniquedStringImpl* exportName, const Resolution&);
170
171     // The loader resolves the given module name to the module key. The module key is the unique value to represent this module.
172     Identifier m_moduleKey;
173
174     SourceCode m_sourceCode;
175
176     VariableEnvironment m_declaredVariables;
177     VariableEnvironment m_lexicalVariables;
178
179     // Currently, we don't keep the occurrence order of the import / export entries.
180     // So, we does not guarantee the order of the errors.
181     // e.g. The import declaration that occurr later than the another import declaration may
182     //      throw the error even if the former import declaration also has the invalid content.
183     //
184     //      import ... // (1) this has some invalid content.
185     //      import ... // (2) this also has some invalid content.
186     //
187     //      In the above case, (2) may throw the error earlier than (1)
188     //
189     // But, in all the cases, we will throw the syntax error. So except for the content of the syntax error,
190     // there are no difference.
191
192     // Map localName -> ImportEntry.
193     ImportEntries m_importEntries;
194
195     // Map exportName -> ExportEntry.
196     ExportEntries m_exportEntries;
197
198     // Save the occurrence order since resolveExport requires it.
199     OrderedIdentifierSet m_starExportEntries;
200
201     // Save the occurrence order since the module loader loads and runs the modules in this order.
202     // http://www.ecma-international.org/ecma-262/6.0/#sec-moduleevaluation
203     OrderedIdentifierSet m_requestedModules;
204
205     WriteBarrier<JSMap> m_dependenciesMap;
206
207     WriteBarrier<ModuleProgramExecutable> m_moduleProgramExecutable;
208     WriteBarrier<JSModuleEnvironment> m_moduleEnvironment;
209     WriteBarrier<JSModuleNamespaceObject> m_moduleNamespaceObject;
210
211     // We assume that all the JSModuleRecord are retained by ModuleLoaderObject's registry.
212     // So here, we don't visit each object for GC. The resolution cache map caches the once
213     // looked up correctly resolved resolution, since (1) we rarely looked up the non-resolved one,
214     // and (2) if we cache all the attempts the size of the map becomes infinitely large.
215     typedef HashMap<RefPtr<UniquedStringImpl>, Resolution, IdentifierRepHash, HashTraits<RefPtr<UniquedStringImpl>>> Resolutions;
216     Resolutions m_resolutionCache;
217 };
218
219 } // namespace JSC
220
221 #endif // JSModuleRecord_h