• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2017 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "src/objects/module.h"
6 
7 #include <unordered_map>
8 #include <unordered_set>
9 
10 #include "src/api/api-inl.h"
11 #include "src/ast/modules.h"
12 #include "src/builtins/accessors.h"
13 #include "src/heap/heap-inl.h"
14 #include "src/objects/cell-inl.h"
15 #include "src/objects/hash-table-inl.h"
16 #include "src/objects/js-generator-inl.h"
17 #include "src/objects/module-inl.h"
18 #include "src/objects/objects-inl.h"
19 #include "src/objects/synthetic-module-inl.h"
20 #include "src/utils/ostreams.h"
21 
22 namespace v8 {
23 namespace internal {
24 
25 namespace {
26 #ifdef DEBUG
PrintModuleName(Module module,std::ostream & os)27 void PrintModuleName(Module module, std::ostream& os) {
28   if (module.IsSourceTextModule()) {
29     SourceTextModule::cast(module).script().GetNameOrSourceURL().Print(os);
30   } else {
31     SyntheticModule::cast(module).name().Print(os);
32   }
33 #ifndef OBJECT_PRINT
34   os << "\n";
35 #endif  // OBJECT_PRINT
36 }
37 
PrintStatusTransition(Module module,Module::Status new_status)38 void PrintStatusTransition(Module module, Module::Status new_status) {
39   if (!FLAG_trace_module_status) return;
40   StdoutStream os;
41   os << "Changing module status from " << module.status() << " to "
42      << new_status << " for ";
43   PrintModuleName(module, os);
44 }
45 
PrintStatusMessage(Module module,const char * message)46 void PrintStatusMessage(Module module, const char* message) {
47   if (!FLAG_trace_module_status) return;
48   StdoutStream os;
49   os << "Instantiating module ";
50   PrintModuleName(module, os);
51 }
52 #endif  // DEBUG
53 
SetStatusInternal(Module module,Module::Status new_status)54 void SetStatusInternal(Module module, Module::Status new_status) {
55   DisallowHeapAllocation no_alloc;
56 #ifdef DEBUG
57   PrintStatusTransition(module, new_status);
58 #endif  // DEBUG
59   module.set_status(new_status);
60 }
61 
62 }  // end namespace
63 
SetStatus(Status new_status)64 void Module::SetStatus(Status new_status) {
65   DisallowHeapAllocation no_alloc;
66   DCHECK_LE(status(), new_status);
67   DCHECK_NE(new_status, Module::kErrored);
68   SetStatusInternal(*this, new_status);
69 }
70 
71 // static
RecordErrorUsingPendingException(Isolate * isolate,Handle<Module> module)72 void Module::RecordErrorUsingPendingException(Isolate* isolate,
73                                               Handle<Module> module) {
74   Handle<Object> the_exception(isolate->pending_exception(), isolate);
75   RecordError(isolate, module, the_exception);
76 }
77 
78 // static
RecordError(Isolate * isolate,Handle<Module> module,Handle<Object> error)79 void Module::RecordError(Isolate* isolate, Handle<Module> module,
80                          Handle<Object> error) {
81   DCHECK(module->exception().IsTheHole(isolate));
82   DCHECK(!error->IsTheHole(isolate));
83   if (module->IsSourceTextModule()) {
84     Handle<SourceTextModule> self(SourceTextModule::cast(*module), isolate);
85     self->set_code(self->info());
86   }
87   SetStatusInternal(*module, Module::kErrored);
88   if (isolate->is_catchable_by_javascript(*error)) {
89     module->set_exception(*error);
90   } else {
91     // v8::TryCatch uses `null` for termination exceptions.
92     module->set_exception(*isolate->factory()->null_value());
93   }
94 }
95 
ResetGraph(Isolate * isolate,Handle<Module> module)96 void Module::ResetGraph(Isolate* isolate, Handle<Module> module) {
97   DCHECK_NE(module->status(), kEvaluating);
98   if (module->status() != kPreInstantiating &&
99       module->status() != kInstantiating) {
100     return;
101   }
102 
103   Handle<FixedArray> requested_modules =
104       module->IsSourceTextModule()
105           ? Handle<FixedArray>(
106                 SourceTextModule::cast(*module).requested_modules(), isolate)
107           : Handle<FixedArray>();
108   Reset(isolate, module);
109 
110   if (!module->IsSourceTextModule()) {
111     DCHECK(module->IsSyntheticModule());
112     return;
113   }
114   for (int i = 0; i < requested_modules->length(); ++i) {
115     Handle<Object> descendant(requested_modules->get(i), isolate);
116     if (descendant->IsModule()) {
117       ResetGraph(isolate, Handle<Module>::cast(descendant));
118     } else {
119       DCHECK(descendant->IsUndefined(isolate));
120     }
121   }
122 }
123 
Reset(Isolate * isolate,Handle<Module> module)124 void Module::Reset(Isolate* isolate, Handle<Module> module) {
125   DCHECK(module->status() == kPreInstantiating ||
126          module->status() == kInstantiating);
127   DCHECK(module->exception().IsTheHole(isolate));
128   // The namespace object cannot exist, because it would have been created
129   // by RunInitializationCode, which is called only after this module's SCC
130   // succeeds instantiation.
131   DCHECK(!module->module_namespace().IsJSModuleNamespace());
132   const int export_count =
133       module->IsSourceTextModule()
134           ? SourceTextModule::cast(*module).regular_exports().length()
135           : SyntheticModule::cast(*module).export_names().length();
136   Handle<ObjectHashTable> exports = ObjectHashTable::New(isolate, export_count);
137 
138   if (module->IsSourceTextModule()) {
139     SourceTextModule::Reset(isolate, Handle<SourceTextModule>::cast(module));
140   }
141 
142   module->set_exports(*exports);
143   SetStatusInternal(*module, kUninstantiated);
144 }
145 
GetException()146 Object Module::GetException() {
147   DisallowHeapAllocation no_gc;
148   DCHECK_EQ(status(), Module::kErrored);
149   DCHECK(!exception().IsTheHole());
150   return exception();
151 }
152 
ResolveExport(Isolate * isolate,Handle<Module> module,Handle<String> module_specifier,Handle<String> export_name,MessageLocation loc,bool must_resolve,Module::ResolveSet * resolve_set)153 MaybeHandle<Cell> Module::ResolveExport(Isolate* isolate, Handle<Module> module,
154                                         Handle<String> module_specifier,
155                                         Handle<String> export_name,
156                                         MessageLocation loc, bool must_resolve,
157                                         Module::ResolveSet* resolve_set) {
158   DCHECK_GE(module->status(), kPreInstantiating);
159   DCHECK_NE(module->status(), kEvaluating);
160 
161   if (module->IsSourceTextModule()) {
162     return SourceTextModule::ResolveExport(
163         isolate, Handle<SourceTextModule>::cast(module), module_specifier,
164         export_name, loc, must_resolve, resolve_set);
165   } else {
166     return SyntheticModule::ResolveExport(
167         isolate, Handle<SyntheticModule>::cast(module), module_specifier,
168         export_name, loc, must_resolve);
169   }
170 }
171 
Instantiate(Isolate * isolate,Handle<Module> module,v8::Local<v8::Context> context,v8::Module::ResolveCallback callback)172 bool Module::Instantiate(Isolate* isolate, Handle<Module> module,
173                          v8::Local<v8::Context> context,
174                          v8::Module::ResolveCallback callback) {
175 #ifdef DEBUG
176   PrintStatusMessage(*module, "Instantiating module ");
177 #endif  // DEBUG
178 
179   if (!PrepareInstantiate(isolate, module, context, callback)) {
180     ResetGraph(isolate, module);
181     DCHECK_EQ(module->status(), kUninstantiated);
182     return false;
183   }
184   Zone zone(isolate->allocator(), ZONE_NAME);
185   ZoneForwardList<Handle<SourceTextModule>> stack(&zone);
186   unsigned dfs_index = 0;
187   if (!FinishInstantiate(isolate, module, &stack, &dfs_index, &zone)) {
188     ResetGraph(isolate, module);
189     DCHECK_EQ(module->status(), kUninstantiated);
190     return false;
191   }
192   DCHECK(module->status() == kInstantiated || module->status() == kEvaluated ||
193          module->status() == kErrored);
194   DCHECK(stack.empty());
195   return true;
196 }
197 
PrepareInstantiate(Isolate * isolate,Handle<Module> module,v8::Local<v8::Context> context,v8::Module::ResolveCallback callback)198 bool Module::PrepareInstantiate(Isolate* isolate, Handle<Module> module,
199                                 v8::Local<v8::Context> context,
200                                 v8::Module::ResolveCallback callback) {
201   DCHECK_NE(module->status(), kEvaluating);
202   DCHECK_NE(module->status(), kInstantiating);
203   if (module->status() >= kPreInstantiating) return true;
204   module->SetStatus(kPreInstantiating);
205   STACK_CHECK(isolate, false);
206 
207   if (module->IsSourceTextModule()) {
208     return SourceTextModule::PrepareInstantiate(
209         isolate, Handle<SourceTextModule>::cast(module), context, callback);
210   } else {
211     return SyntheticModule::PrepareInstantiate(
212         isolate, Handle<SyntheticModule>::cast(module), context, callback);
213   }
214 }
215 
FinishInstantiate(Isolate * isolate,Handle<Module> module,ZoneForwardList<Handle<SourceTextModule>> * stack,unsigned * dfs_index,Zone * zone)216 bool Module::FinishInstantiate(Isolate* isolate, Handle<Module> module,
217                                ZoneForwardList<Handle<SourceTextModule>>* stack,
218                                unsigned* dfs_index, Zone* zone) {
219   DCHECK_NE(module->status(), kEvaluating);
220   if (module->status() >= kInstantiating) return true;
221   DCHECK_EQ(module->status(), kPreInstantiating);
222   STACK_CHECK(isolate, false);
223 
224   if (module->IsSourceTextModule()) {
225     return SourceTextModule::FinishInstantiate(
226         isolate, Handle<SourceTextModule>::cast(module), stack, dfs_index,
227         zone);
228   } else {
229     return SyntheticModule::FinishInstantiate(
230         isolate, Handle<SyntheticModule>::cast(module));
231   }
232 }
233 
Evaluate(Isolate * isolate,Handle<Module> module)234 MaybeHandle<Object> Module::Evaluate(Isolate* isolate, Handle<Module> module) {
235 #ifdef DEBUG
236   PrintStatusMessage(*module, "Evaluating module ");
237 #endif  // DEBUG
238   STACK_CHECK(isolate, MaybeHandle<Object>());
239   if (FLAG_harmony_top_level_await && module->IsSourceTextModule()) {
240     return SourceTextModule::EvaluateMaybeAsync(
241         isolate, Handle<SourceTextModule>::cast(module));
242   } else {
243     return Module::InnerEvaluate(isolate, module);
244   }
245 }
246 
InnerEvaluate(Isolate * isolate,Handle<Module> module)247 MaybeHandle<Object> Module::InnerEvaluate(Isolate* isolate,
248                                           Handle<Module> module) {
249   if (module->status() == kErrored) {
250     isolate->Throw(module->GetException());
251     return MaybeHandle<Object>();
252   } else if (module->status() == kEvaluated) {
253     return isolate->factory()->undefined_value();
254   }
255 
256   // InnerEvaluate can be called both to evaluate top level modules without
257   // the harmony_top_level_await flag and recursively to evaluate
258   // SyntheticModules in the dependency graphs of SourceTextModules.
259   //
260   // However, SyntheticModules transition directly to 'Evaluated,' so we should
261   // never see an 'Evaluating' module at this point.
262   CHECK_EQ(module->status(), kInstantiated);
263 
264   if (module->IsSourceTextModule()) {
265     return SourceTextModule::Evaluate(isolate,
266                                       Handle<SourceTextModule>::cast(module));
267   } else {
268     return SyntheticModule::Evaluate(isolate,
269                                      Handle<SyntheticModule>::cast(module));
270   }
271 }
272 
GetModuleNamespace(Isolate * isolate,Handle<Module> module)273 Handle<JSModuleNamespace> Module::GetModuleNamespace(Isolate* isolate,
274                                                      Handle<Module> module) {
275   Handle<HeapObject> object(module->module_namespace(), isolate);
276   ReadOnlyRoots roots(isolate);
277   if (!object->IsUndefined(roots)) {
278     // Namespace object already exists.
279     return Handle<JSModuleNamespace>::cast(object);
280   }
281 
282   // Collect the export names.
283   Zone zone(isolate->allocator(), ZONE_NAME);
284   UnorderedModuleSet visited(&zone);
285 
286   if (module->IsSourceTextModule()) {
287     SourceTextModule::FetchStarExports(
288         isolate, Handle<SourceTextModule>::cast(module), &zone, &visited);
289   }
290 
291   Handle<ObjectHashTable> exports(module->exports(), isolate);
292   ZoneVector<Handle<String>> names(&zone);
293   names.reserve(exports->NumberOfElements());
294   for (InternalIndex i : exports->IterateEntries()) {
295     Object key;
296     if (!exports->ToKey(roots, i, &key)) continue;
297     names.push_back(handle(String::cast(key), isolate));
298   }
299   DCHECK_EQ(static_cast<int>(names.size()), exports->NumberOfElements());
300 
301   // Sort them alphabetically.
302   std::sort(names.begin(), names.end(),
303             [&isolate](Handle<String> a, Handle<String> b) {
304               return String::Compare(isolate, a, b) ==
305                      ComparisonResult::kLessThan;
306             });
307 
308   // Create the namespace object (initially empty).
309   Handle<JSModuleNamespace> ns = isolate->factory()->NewJSModuleNamespace();
310   ns->set_module(*module);
311   module->set_module_namespace(*ns);
312 
313   // Create the properties in the namespace object. Transition the object
314   // to dictionary mode so that property addition is faster.
315   PropertyAttributes attr = DONT_DELETE;
316   JSObject::NormalizeProperties(isolate, ns, CLEAR_INOBJECT_PROPERTIES,
317                                 static_cast<int>(names.size()),
318                                 "JSModuleNamespace");
319   for (const auto& name : names) {
320     JSObject::SetNormalizedProperty(
321         ns, name, Accessors::MakeModuleNamespaceEntryInfo(isolate, name),
322         PropertyDetails(kAccessor, attr, PropertyCellType::kMutable));
323   }
324   JSObject::PreventExtensions(ns, kThrowOnError).ToChecked();
325 
326   // Optimize the namespace object as a prototype, for two reasons:
327   // - The object's map is guaranteed not to be shared. ICs rely on this.
328   // - We can store a pointer from the map back to the namespace object.
329   //   Turbofan can use this for inlining the access.
330   JSObject::OptimizeAsPrototype(ns);
331 
332   Handle<PrototypeInfo> proto_info =
333       Map::GetOrCreatePrototypeInfo(Handle<JSObject>::cast(ns), isolate);
334   proto_info->set_module_namespace(*ns);
335   return ns;
336 }
337 
GetExport(Isolate * isolate,Handle<String> name)338 MaybeHandle<Object> JSModuleNamespace::GetExport(Isolate* isolate,
339                                                  Handle<String> name) {
340   Handle<Object> object(module().exports().Lookup(name), isolate);
341   if (object->IsTheHole(isolate)) {
342     return isolate->factory()->undefined_value();
343   }
344 
345   Handle<Object> value(Cell::cast(*object).value(), isolate);
346   if (value->IsTheHole(isolate)) {
347     THROW_NEW_ERROR(
348         isolate, NewReferenceError(MessageTemplate::kNotDefined, name), Object);
349   }
350 
351   return value;
352 }
353 
GetPropertyAttributes(LookupIterator * it)354 Maybe<PropertyAttributes> JSModuleNamespace::GetPropertyAttributes(
355     LookupIterator* it) {
356   Handle<JSModuleNamespace> object = it->GetHolder<JSModuleNamespace>();
357   Handle<String> name = Handle<String>::cast(it->GetName());
358   DCHECK_EQ(it->state(), LookupIterator::ACCESSOR);
359 
360   Isolate* isolate = it->isolate();
361 
362   Handle<Object> lookup(object->module().exports().Lookup(name), isolate);
363   if (lookup->IsTheHole(isolate)) return Just(ABSENT);
364 
365   Handle<Object> value(Handle<Cell>::cast(lookup)->value(), isolate);
366   if (value->IsTheHole(isolate)) {
367     isolate->Throw(*isolate->factory()->NewReferenceError(
368         MessageTemplate::kNotDefined, name));
369     return Nothing<PropertyAttributes>();
370   }
371 
372   return Just(it->property_attributes());
373 }
374 
IsGraphAsync(Isolate * isolate) const375 bool Module::IsGraphAsync(Isolate* isolate) const {
376   DisallowGarbageCollection no_gc;
377 
378   // Only SourceTextModules may be async.
379   if (!IsSourceTextModule()) return false;
380   SourceTextModule root = SourceTextModule::cast(*this);
381 
382   Zone zone(isolate->allocator(), ZONE_NAME);
383   const size_t bucket_count = 2;
384   ZoneUnorderedSet<Module, Module::Hash> visited(&zone, bucket_count);
385   ZoneVector<SourceTextModule> worklist(&zone);
386   visited.insert(root);
387   worklist.push_back(root);
388 
389   do {
390     SourceTextModule current = worklist.back();
391     worklist.pop_back();
392     DCHECK_GE(current.status(), kInstantiated);
393 
394     if (current.async()) return true;
395     FixedArray requested_modules = current.requested_modules();
396     for (int i = 0, length = requested_modules.length(); i < length; ++i) {
397       Module descendant = Module::cast(requested_modules.get(i));
398       if (descendant.IsSourceTextModule()) {
399         const bool cycle = !visited.insert(descendant).second;
400         if (!cycle) worklist.push_back(SourceTextModule::cast(descendant));
401       }
402     }
403   } while (!worklist.empty());
404 
405   return false;
406 }
407 
408 }  // namespace internal
409 }  // namespace v8
410