• 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/common/assert-scope.h"
14 #include "src/heap/heap-inl.h"
15 #include "src/objects/cell-inl.h"
16 #include "src/objects/hash-table-inl.h"
17 #include "src/objects/js-generator-inl.h"
18 #include "src/objects/module-inl.h"
19 #include "src/objects/objects-inl.h"
20 #include "src/objects/property-descriptor.h"
21 #include "src/objects/source-text-module.h"
22 #include "src/objects/synthetic-module-inl.h"
23 #include "src/utils/ostreams.h"
24 
25 namespace v8 {
26 namespace internal {
27 
28 namespace {
29 #ifdef DEBUG
PrintModuleName(Module module,std::ostream & os)30 void PrintModuleName(Module module, std::ostream& os) {
31   if (module.IsSourceTextModule()) {
32     SourceTextModule::cast(module).GetScript().GetNameOrSourceURL().Print(os);
33   } else {
34     SyntheticModule::cast(module).name().Print(os);
35   }
36 #ifndef OBJECT_PRINT
37   os << "\n";
38 #endif  // OBJECT_PRINT
39 }
40 
PrintStatusTransition(Module module,Module::Status old_status)41 void PrintStatusTransition(Module module, Module::Status old_status) {
42   if (!FLAG_trace_module_status) return;
43   StdoutStream os;
44   os << "Changing module status from " << old_status << " to "
45      << module.status() << " for ";
46   PrintModuleName(module, os);
47 }
48 
PrintStatusMessage(Module module,const char * message)49 void PrintStatusMessage(Module module, const char* message) {
50   if (!FLAG_trace_module_status) return;
51   StdoutStream os;
52   os << "Instantiating module ";
53   PrintModuleName(module, os);
54 }
55 #endif  // DEBUG
56 
SetStatusInternal(Module module,Module::Status new_status)57 void SetStatusInternal(Module module, Module::Status new_status) {
58   DisallowGarbageCollection no_gc;
59 #ifdef DEBUG
60   Module::Status old_status = static_cast<Module::Status>(module.status());
61   module.set_status(new_status);
62   PrintStatusTransition(module, old_status);
63 #else
64   module.set_status(new_status);
65 #endif  // DEBUG
66 }
67 
68 }  // end namespace
69 
SetStatus(Status new_status)70 void Module::SetStatus(Status new_status) {
71   DisallowGarbageCollection no_gc;
72   DCHECK_LE(status(), new_status);
73   DCHECK_NE(new_status, Module::kErrored);
74   SetStatusInternal(*this, new_status);
75 }
76 
77 // static
RecordErrorUsingPendingException(Isolate * isolate,Handle<Module> module)78 void Module::RecordErrorUsingPendingException(Isolate* isolate,
79                                               Handle<Module> module) {
80   Handle<Object> the_exception(isolate->pending_exception(), isolate);
81   RecordError(isolate, module, the_exception);
82 }
83 
84 // static
RecordError(Isolate * isolate,Handle<Module> module,Handle<Object> error)85 void Module::RecordError(Isolate* isolate, Handle<Module> module,
86                          Handle<Object> error) {
87   DisallowGarbageCollection no_gc;
88   DCHECK(module->exception().IsTheHole(isolate));
89   DCHECK(!error->IsTheHole(isolate));
90   if (module->IsSourceTextModule()) {
91     // Revert to minmal SFI in case we have already been instantiating or
92     // evaluating.
93     auto self = SourceTextModule::cast(*module);
94     self.set_code(self.GetSharedFunctionInfo());
95   }
96   SetStatusInternal(*module, Module::kErrored);
97   if (isolate->is_catchable_by_javascript(*error)) {
98     module->set_exception(*error);
99   } else {
100     // v8::TryCatch uses `null` for termination exceptions.
101     module->set_exception(*isolate->factory()->null_value());
102   }
103 }
104 
ResetGraph(Isolate * isolate,Handle<Module> module)105 void Module::ResetGraph(Isolate* isolate, Handle<Module> module) {
106   DCHECK_NE(module->status(), kEvaluating);
107   if (module->status() != kPreLinking && module->status() != kLinking) {
108     return;
109   }
110 
111   Handle<FixedArray> requested_modules =
112       module->IsSourceTextModule()
113           ? Handle<FixedArray>(
114                 SourceTextModule::cast(*module).requested_modules(), isolate)
115           : Handle<FixedArray>();
116   Reset(isolate, module);
117 
118   if (!module->IsSourceTextModule()) {
119     DCHECK(module->IsSyntheticModule());
120     return;
121   }
122   for (int i = 0; i < requested_modules->length(); ++i) {
123     Handle<Object> descendant(requested_modules->get(i), isolate);
124     if (descendant->IsModule()) {
125       ResetGraph(isolate, Handle<Module>::cast(descendant));
126     } else {
127       DCHECK(descendant->IsUndefined(isolate));
128     }
129   }
130 }
131 
Reset(Isolate * isolate,Handle<Module> module)132 void Module::Reset(Isolate* isolate, Handle<Module> module) {
133   DCHECK(module->status() == kPreLinking || module->status() == kLinking);
134   DCHECK(module->exception().IsTheHole(isolate));
135   // The namespace object cannot exist, because it would have been created
136   // by RunInitializationCode, which is called only after this module's SCC
137   // succeeds instantiation.
138   DCHECK(!module->module_namespace().IsJSModuleNamespace());
139   const int export_count =
140       module->IsSourceTextModule()
141           ? SourceTextModule::cast(*module).regular_exports().length()
142           : SyntheticModule::cast(*module).export_names().length();
143   Handle<ObjectHashTable> exports = ObjectHashTable::New(isolate, export_count);
144 
145   if (module->IsSourceTextModule()) {
146     SourceTextModule::Reset(isolate, Handle<SourceTextModule>::cast(module));
147   }
148 
149   module->set_exports(*exports);
150   SetStatusInternal(*module, kUnlinked);
151 }
152 
GetException()153 Object Module::GetException() {
154   DisallowGarbageCollection no_gc;
155   DCHECK_EQ(status(), Module::kErrored);
156   DCHECK(!exception().IsTheHole());
157   return exception();
158 }
159 
ResolveExport(Isolate * isolate,Handle<Module> module,Handle<String> module_specifier,Handle<String> export_name,MessageLocation loc,bool must_resolve,Module::ResolveSet * resolve_set)160 MaybeHandle<Cell> Module::ResolveExport(Isolate* isolate, Handle<Module> module,
161                                         Handle<String> module_specifier,
162                                         Handle<String> export_name,
163                                         MessageLocation loc, bool must_resolve,
164                                         Module::ResolveSet* resolve_set) {
165   DCHECK_GE(module->status(), kPreLinking);
166   DCHECK_NE(module->status(), kEvaluating);
167 
168   if (module->IsSourceTextModule()) {
169     return SourceTextModule::ResolveExport(
170         isolate, Handle<SourceTextModule>::cast(module), module_specifier,
171         export_name, loc, must_resolve, resolve_set);
172   } else {
173     return SyntheticModule::ResolveExport(
174         isolate, Handle<SyntheticModule>::cast(module), module_specifier,
175         export_name, loc, must_resolve);
176   }
177 }
178 
Instantiate(Isolate * isolate,Handle<Module> module,v8::Local<v8::Context> context,v8::Module::ResolveModuleCallback callback,DeprecatedResolveCallback callback_without_import_assertions)179 bool Module::Instantiate(
180     Isolate* isolate, Handle<Module> module, v8::Local<v8::Context> context,
181     v8::Module::ResolveModuleCallback callback,
182     DeprecatedResolveCallback callback_without_import_assertions) {
183 #ifdef DEBUG
184   PrintStatusMessage(*module, "Instantiating module ");
185 #endif  // DEBUG
186 
187   if (!PrepareInstantiate(isolate, module, context, callback,
188                           callback_without_import_assertions)) {
189     ResetGraph(isolate, module);
190     DCHECK_EQ(module->status(), kUnlinked);
191     return false;
192   }
193   Zone zone(isolate->allocator(), ZONE_NAME);
194   ZoneForwardList<Handle<SourceTextModule>> stack(&zone);
195   unsigned dfs_index = 0;
196   if (!FinishInstantiate(isolate, module, &stack, &dfs_index, &zone)) {
197     ResetGraph(isolate, module);
198     DCHECK_EQ(module->status(), kUnlinked);
199     return false;
200   }
201   DCHECK(module->status() == kLinked || module->status() == kEvaluated ||
202          module->status() == kErrored);
203   DCHECK(stack.empty());
204   return true;
205 }
206 
PrepareInstantiate(Isolate * isolate,Handle<Module> module,v8::Local<v8::Context> context,v8::Module::ResolveModuleCallback callback,DeprecatedResolveCallback callback_without_import_assertions)207 bool Module::PrepareInstantiate(
208     Isolate* isolate, Handle<Module> module, v8::Local<v8::Context> context,
209     v8::Module::ResolveModuleCallback callback,
210     DeprecatedResolveCallback callback_without_import_assertions) {
211   DCHECK_NE(module->status(), kEvaluating);
212   DCHECK_NE(module->status(), kLinking);
213   if (module->status() >= kPreLinking) return true;
214   module->SetStatus(kPreLinking);
215   STACK_CHECK(isolate, false);
216 
217   if (module->IsSourceTextModule()) {
218     return SourceTextModule::PrepareInstantiate(
219         isolate, Handle<SourceTextModule>::cast(module), context, callback,
220         callback_without_import_assertions);
221   } else {
222     return SyntheticModule::PrepareInstantiate(
223         isolate, Handle<SyntheticModule>::cast(module), context);
224   }
225 }
226 
FinishInstantiate(Isolate * isolate,Handle<Module> module,ZoneForwardList<Handle<SourceTextModule>> * stack,unsigned * dfs_index,Zone * zone)227 bool Module::FinishInstantiate(Isolate* isolate, Handle<Module> module,
228                                ZoneForwardList<Handle<SourceTextModule>>* stack,
229                                unsigned* dfs_index, Zone* zone) {
230   DCHECK_NE(module->status(), kEvaluating);
231   if (module->status() >= kLinking) return true;
232   DCHECK_EQ(module->status(), kPreLinking);
233   STACK_CHECK(isolate, false);
234 
235   if (module->IsSourceTextModule()) {
236     return SourceTextModule::FinishInstantiate(
237         isolate, Handle<SourceTextModule>::cast(module), stack, dfs_index,
238         zone);
239   } else {
240     return SyntheticModule::FinishInstantiate(
241         isolate, Handle<SyntheticModule>::cast(module));
242   }
243 }
244 
Evaluate(Isolate * isolate,Handle<Module> module)245 MaybeHandle<Object> Module::Evaluate(Isolate* isolate, Handle<Module> module) {
246 #ifdef DEBUG
247   PrintStatusMessage(*module, "Evaluating module ");
248 #endif  // DEBUG
249   STACK_CHECK(isolate, MaybeHandle<Object>());
250 
251   // In the event of errored evaluation, return a rejected promise.
252   if (module->status() == kErrored) {
253     // If we have a top level capability we assume it has already been
254     // rejected, and return it here. Otherwise create a new promise and
255     // reject it with the module's exception.
256     if (module->top_level_capability().IsJSPromise()) {
257       Handle<JSPromise> top_level_capability(
258           JSPromise::cast(module->top_level_capability()), isolate);
259       DCHECK(top_level_capability->status() == Promise::kRejected &&
260              top_level_capability->result() == module->exception());
261       return top_level_capability;
262     }
263     Handle<JSPromise> capability = isolate->factory()->NewJSPromise();
264     JSPromise::Reject(capability, handle(module->exception(), isolate));
265     return capability;
266   }
267 
268   // Start of Evaluate () Concrete Method
269   // 2. Assert: module.[[Status]] is "linked" or "evaluated".
270   CHECK(module->status() == kLinked || module->status() == kEvaluated);
271 
272   // 3. If module.[[Status]] is "evaluated", set module to
273   //    module.[[CycleRoot]].
274   // A Synthetic Module has no children so it is its own cycle root.
275   if (module->status() == kEvaluated && module->IsSourceTextModule()) {
276     module = Handle<SourceTextModule>::cast(module)->GetCycleRoot(isolate);
277   }
278 
279   // 4. If module.[[TopLevelCapability]] is not undefined, then
280   //    a. Return module.[[TopLevelCapability]].[[Promise]].
281   if (module->top_level_capability().IsJSPromise()) {
282     return handle(JSPromise::cast(module->top_level_capability()), isolate);
283   }
284   DCHECK(module->top_level_capability().IsUndefined());
285 
286   if (module->IsSourceTextModule()) {
287     return SourceTextModule::Evaluate(isolate,
288                                       Handle<SourceTextModule>::cast(module));
289   } else {
290     return SyntheticModule::Evaluate(isolate,
291                                      Handle<SyntheticModule>::cast(module));
292   }
293 }
294 
GetModuleNamespace(Isolate * isolate,Handle<Module> module)295 Handle<JSModuleNamespace> Module::GetModuleNamespace(Isolate* isolate,
296                                                      Handle<Module> module) {
297   Handle<HeapObject> object(module->module_namespace(), isolate);
298   ReadOnlyRoots roots(isolate);
299   if (!object->IsUndefined(roots)) {
300     // Namespace object already exists.
301     return Handle<JSModuleNamespace>::cast(object);
302   }
303 
304   // Collect the export names.
305   Zone zone(isolate->allocator(), ZONE_NAME);
306   UnorderedModuleSet visited(&zone);
307 
308   if (module->IsSourceTextModule()) {
309     SourceTextModule::FetchStarExports(
310         isolate, Handle<SourceTextModule>::cast(module), &zone, &visited);
311   }
312 
313   Handle<ObjectHashTable> exports(module->exports(), isolate);
314   ZoneVector<Handle<String>> names(&zone);
315   names.reserve(exports->NumberOfElements());
316   for (InternalIndex i : exports->IterateEntries()) {
317     Object key;
318     if (!exports->ToKey(roots, i, &key)) continue;
319     names.push_back(handle(String::cast(key), isolate));
320   }
321   DCHECK_EQ(static_cast<int>(names.size()), exports->NumberOfElements());
322 
323   // Sort them alphabetically.
324   std::sort(names.begin(), names.end(),
325             [&isolate](Handle<String> a, Handle<String> b) {
326               return String::Compare(isolate, a, b) ==
327                      ComparisonResult::kLessThan;
328             });
329 
330   // Create the namespace object (initially empty).
331   Handle<JSModuleNamespace> ns = isolate->factory()->NewJSModuleNamespace();
332   ns->set_module(*module);
333   module->set_module_namespace(*ns);
334 
335   // Create the properties in the namespace object. Transition the object
336   // to dictionary mode so that property addition is faster.
337   PropertyAttributes attr = DONT_DELETE;
338   JSObject::NormalizeProperties(isolate, ns, CLEAR_INOBJECT_PROPERTIES,
339                                 static_cast<int>(names.size()),
340                                 "JSModuleNamespace");
341   JSObject::NormalizeElements(ns);
342   for (const auto& name : names) {
343     uint32_t index = 0;
344     if (name->AsArrayIndex(&index)) {
345       JSObject::SetNormalizedElement(
346           ns, index, Accessors::MakeModuleNamespaceEntryInfo(isolate, name),
347           PropertyDetails(PropertyKind::kAccessor, attr,
348                           PropertyCellType::kMutable));
349     } else {
350       JSObject::SetNormalizedProperty(
351           ns, name, Accessors::MakeModuleNamespaceEntryInfo(isolate, name),
352           PropertyDetails(PropertyKind::kAccessor, attr,
353                           PropertyCellType::kMutable));
354     }
355   }
356   JSObject::PreventExtensions(ns, kThrowOnError).ToChecked();
357 
358   // Optimize the namespace object as a prototype, for two reasons:
359   // - The object's map is guaranteed not to be shared. ICs rely on this.
360   // - We can store a pointer from the map back to the namespace object.
361   //   Turbofan can use this for inlining the access.
362   JSObject::OptimizeAsPrototype(ns);
363 
364   Handle<PrototypeInfo> proto_info =
365       Map::GetOrCreatePrototypeInfo(Handle<JSObject>::cast(ns), isolate);
366   proto_info->set_module_namespace(*ns);
367   return ns;
368 }
369 
GetExport(Isolate * isolate,Handle<String> name)370 MaybeHandle<Object> JSModuleNamespace::GetExport(Isolate* isolate,
371                                                  Handle<String> name) {
372   Handle<Object> object(module().exports().Lookup(name), isolate);
373   if (object->IsTheHole(isolate)) {
374     return isolate->factory()->undefined_value();
375   }
376 
377   Handle<Object> value(Cell::cast(*object).value(), isolate);
378   if (value->IsTheHole(isolate)) {
379     // According to https://tc39.es/ecma262/#sec-InnerModuleLinking
380     // step 10 and
381     // https://tc39.es/ecma262/#sec-source-text-module-record-initialize-environment
382     // step 8-25, variables must be declared in Link. And according to
383     // https://tc39.es/ecma262/#sec-module-namespace-exotic-objects-get-p-receiver,
384     // here accessing uninitialized variable error should be throwed.
385     THROW_NEW_ERROR(isolate,
386                     NewReferenceError(
387                         MessageTemplate::kAccessedUninitializedVariable, name),
388                     Object);
389   }
390 
391   return value;
392 }
393 
GetPropertyAttributes(LookupIterator * it)394 Maybe<PropertyAttributes> JSModuleNamespace::GetPropertyAttributes(
395     LookupIterator* it) {
396   Handle<JSModuleNamespace> object = it->GetHolder<JSModuleNamespace>();
397   Handle<String> name = Handle<String>::cast(it->GetName());
398   DCHECK_EQ(it->state(), LookupIterator::ACCESSOR);
399 
400   Isolate* isolate = it->isolate();
401 
402   Handle<Object> lookup(object->module().exports().Lookup(name), isolate);
403   if (lookup->IsTheHole(isolate)) return Just(ABSENT);
404 
405   Handle<Object> value(Handle<Cell>::cast(lookup)->value(), isolate);
406   if (value->IsTheHole(isolate)) {
407     isolate->Throw(*isolate->factory()->NewReferenceError(
408         MessageTemplate::kNotDefined, name));
409     return Nothing<PropertyAttributes>();
410   }
411 
412   return Just(it->property_attributes());
413 }
414 
415 // ES
416 // https://tc39.es/ecma262/#sec-module-namespace-exotic-objects-defineownproperty-p-desc
417 // static
DefineOwnProperty(Isolate * isolate,Handle<JSModuleNamespace> object,Handle<Object> key,PropertyDescriptor * desc,Maybe<ShouldThrow> should_throw)418 Maybe<bool> JSModuleNamespace::DefineOwnProperty(
419     Isolate* isolate, Handle<JSModuleNamespace> object, Handle<Object> key,
420     PropertyDescriptor* desc, Maybe<ShouldThrow> should_throw) {
421   // 1. If Type(P) is Symbol, return OrdinaryDefineOwnProperty(O, P, Desc).
422   if (key->IsSymbol()) {
423     return OrdinaryDefineOwnProperty(isolate, object, key, desc, should_throw);
424   }
425 
426   // 2. Let current be ? O.[[GetOwnProperty]](P).
427   PropertyKey lookup_key(isolate, key);
428   LookupIterator it(isolate, object, lookup_key, LookupIterator::OWN);
429   PropertyDescriptor current;
430   Maybe<bool> has_own = GetOwnPropertyDescriptor(&it, &current);
431   MAYBE_RETURN(has_own, Nothing<bool>());
432 
433   // 3. If current is undefined, return false.
434   // 4. If Desc.[[Configurable]] is present and has value true, return false.
435   // 5. If Desc.[[Enumerable]] is present and has value false, return false.
436   // 6. If ! IsAccessorDescriptor(Desc) is true, return false.
437   // 7. If Desc.[[Writable]] is present and has value false, return false.
438   // 8. If Desc.[[Value]] is present, return
439   //    SameValue(Desc.[[Value]], current.[[Value]]).
440   if (!has_own.FromJust() ||
441       (desc->has_configurable() && desc->configurable()) ||
442       (desc->has_enumerable() && !desc->enumerable()) ||
443       PropertyDescriptor::IsAccessorDescriptor(desc) ||
444       (desc->has_writable() && !desc->writable()) ||
445       (desc->has_value() && !desc->value()->SameValue(*current.value()))) {
446     RETURN_FAILURE(isolate, GetShouldThrow(isolate, should_throw),
447                    NewTypeError(MessageTemplate::kRedefineDisallowed, key));
448   }
449 
450   return Just(true);
451 }
452 
IsGraphAsync(Isolate * isolate) const453 bool Module::IsGraphAsync(Isolate* isolate) const {
454   DisallowGarbageCollection no_gc;
455 
456   // Only SourceTextModules may be async.
457   if (!IsSourceTextModule()) return false;
458   SourceTextModule root = SourceTextModule::cast(*this);
459 
460   Zone zone(isolate->allocator(), ZONE_NAME);
461   const size_t bucket_count = 2;
462   ZoneUnorderedSet<Module, Module::Hash> visited(&zone, bucket_count);
463   ZoneVector<SourceTextModule> worklist(&zone);
464   visited.insert(root);
465   worklist.push_back(root);
466 
467   do {
468     SourceTextModule current = worklist.back();
469     worklist.pop_back();
470     DCHECK_GE(current.status(), kLinked);
471 
472     if (current.async()) return true;
473     FixedArray requested_modules = current.requested_modules();
474     for (int i = 0, length = requested_modules.length(); i < length; ++i) {
475       Module descendant = Module::cast(requested_modules.get(i));
476       if (descendant.IsSourceTextModule()) {
477         const bool cycle = !visited.insert(descendant).second;
478         if (!cycle) worklist.push_back(SourceTextModule::cast(descendant));
479       }
480     }
481   } while (!worklist.empty());
482 
483   return false;
484 }
485 
486 }  // namespace internal
487 }  // namespace v8
488