module.cc 17.9 KB
Newer Older
1 2 3 4
// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5 6
#include "src/objects/module.h"

7 8 9
#include <unordered_map>
#include <unordered_set>

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 26 27

namespace v8 {
namespace internal {

28
namespace {
29
#ifdef DEBUG
30 31
void PrintModuleName(Module module, std::ostream& os) {
  if (module.IsSourceTextModule()) {
32
    SourceTextModule::cast(module).GetScript().GetNameOrSourceURL().Print(os);
33 34 35
  } else {
    SyntheticModule::cast(module).name().Print(os);
  }
36
#ifndef OBJECT_PRINT
37
  os << "\n";
38
#endif  // OBJECT_PRINT
39 40
}

41
void PrintStatusTransition(Module module, Module::Status old_status) {
42 43
  if (!FLAG_trace_module_status) return;
  StdoutStream os;
44 45
  os << "Changing module status from " << old_status << " to "
     << module.status() << " for ";
46 47 48 49 50 51 52 53
  PrintModuleName(module, os);
}

void PrintStatusMessage(Module module, const char* message) {
  if (!FLAG_trace_module_status) return;
  StdoutStream os;
  os << "Instantiating module ";
  PrintModuleName(module, os);
54 55 56
}
#endif  // DEBUG

57
void SetStatusInternal(Module module, Module::Status new_status) {
58
  DisallowGarbageCollection no_gc;
59
#ifdef DEBUG
60 61 62 63
  Module::Status old_status = static_cast<Module::Status>(module.status());
  module.set_status(new_status);
  PrintStatusTransition(module, old_status);
#else
64
  module.set_status(new_status);
65
#endif  // DEBUG
66 67 68 69
}

}  // end namespace

70
void Module::SetStatus(Status new_status) {
71
  DisallowGarbageCollection no_gc;
72 73
  DCHECK_LE(status(), new_status);
  DCHECK_NE(new_status, Module::kErrored);
74
  SetStatusInternal(*this, new_status);
75 76
}

77 78 79
// static
void Module::RecordErrorUsingPendingException(Isolate* isolate,
                                              Handle<Module> module) {
80
  Handle<Object> the_exception(isolate->pending_exception(), isolate);
81
  RecordError(isolate, module, the_exception);
82
}
83

84 85 86
// static
void Module::RecordError(Isolate* isolate, Handle<Module> module,
                         Handle<Object> error) {
87
  DisallowGarbageCollection no_gc;
88
  DCHECK(module->exception().IsTheHole(isolate));
89
  DCHECK(!error->IsTheHole(isolate));
90
  if (module->IsSourceTextModule()) {
91 92 93 94
    // Revert to minmal SFI in case we have already been instantiating or
    // evaluating.
    auto self = SourceTextModule::cast(*module);
    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 100
  } else {
    // v8::TryCatch uses `null` for termination exceptions.
101
    module->set_exception(*isolate->factory()->null_value());
102
  }
103 104
}

105
void Module::ResetGraph(Isolate* isolate, Handle<Module> module) {
106
  DCHECK_NE(module->status(), kEvaluating);
107
  if (module->status() != kPreLinking && module->status() != kLinking) {
108 109
    return;
  }
110 111 112 113

  Handle<FixedArray> requested_modules =
      module->IsSourceTextModule()
          ? Handle<FixedArray>(
114
                SourceTextModule::cast(*module).requested_modules(), isolate)
115 116
          : Handle<FixedArray>();
  Reset(isolate, module);
117 118

  if (!module->IsSourceTextModule()) {
119
    DCHECK(module->IsSyntheticModule());
120 121 122 123 124 125 126 127 128
    return;
  }
  for (int i = 0; i < requested_modules->length(); ++i) {
    Handle<Object> descendant(requested_modules->get(i), isolate);
    if (descendant->IsModule()) {
      ResetGraph(isolate, Handle<Module>::cast(descendant));
    } else {
      DCHECK(descendant->IsUndefined(isolate));
    }
129 130 131
  }
}

132
void Module::Reset(Isolate* isolate, Handle<Module> module) {
133
  DCHECK(module->status() == kPreLinking || module->status() == kLinking);
134
  DCHECK(module->exception().IsTheHole(isolate));
135 136 137
  // The namespace object cannot exist, because it would have been created
  // by RunInitializationCode, which is called only after this module's SCC
  // succeeds instantiation.
138
  DCHECK(!module->module_namespace().IsJSModuleNamespace());
139 140 141 142 143
  const int export_count =
      module->IsSourceTextModule()
          ? SourceTextModule::cast(*module).regular_exports().length()
          : SyntheticModule::cast(*module).export_names().length();
  Handle<ObjectHashTable> exports = ObjectHashTable::New(isolate, export_count);
144 145 146 147 148

  if (module->IsSourceTextModule()) {
    SourceTextModule::Reset(isolate, Handle<SourceTextModule>::cast(module));
  }

149
  module->set_exports(*exports);
150
  SetStatusInternal(*module, kUnlinked);
151 152
}

153
Object Module::GetException() {
154
  DisallowGarbageCollection no_gc;
155
  DCHECK_EQ(status(), Module::kErrored);
156
  DCHECK(!exception().IsTheHole());
157
  return exception();
158 159
}

160
MaybeHandle<Cell> Module::ResolveExport(Isolate* isolate, Handle<Module> module,
161 162
                                        Handle<String> module_specifier,
                                        Handle<String> export_name,
163 164
                                        MessageLocation loc, bool must_resolve,
                                        Module::ResolveSet* resolve_set) {
165
  DCHECK_GE(module->status(), kPreLinking);
166 167
  DCHECK_NE(module->status(), kEvaluating);

168 169 170 171 172
  if (module->IsSourceTextModule()) {
    return SourceTextModule::ResolveExport(
        isolate, Handle<SourceTextModule>::cast(module), module_specifier,
        export_name, loc, must_resolve, resolve_set);
  } else {
173 174
    return SyntheticModule::ResolveExport(
        isolate, Handle<SyntheticModule>::cast(module), module_specifier,
175
        export_name, loc, must_resolve);
176 177 178
  }
}

179 180 181 182
bool Module::Instantiate(
    Isolate* isolate, Handle<Module> module, v8::Local<v8::Context> context,
    v8::Module::ResolveModuleCallback callback,
    DeprecatedResolveCallback callback_without_import_assertions) {
183
#ifdef DEBUG
184
  PrintStatusMessage(*module, "Instantiating module ");
185 186
#endif  // DEBUG

187 188
  if (!PrepareInstantiate(isolate, module, context, callback,
                          callback_without_import_assertions)) {
189
    ResetGraph(isolate, module);
190
    DCHECK_EQ(module->status(), kUnlinked);
191 192 193
    return false;
  }
  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 200
    return false;
  }
201
  DCHECK(module->status() == kLinked || module->status() == kEvaluated ||
202
         module->status() == kErrored);
203 204 205 206
  DCHECK(stack.empty());
  return true;
}

207 208 209 210
bool Module::PrepareInstantiate(
    Isolate* isolate, Handle<Module> module, v8::Local<v8::Context> context,
    v8::Module::ResolveModuleCallback callback,
    DeprecatedResolveCallback callback_without_import_assertions) {
211
  DCHECK_NE(module->status(), kEvaluating);
212 213 214
  DCHECK_NE(module->status(), kLinking);
  if (module->status() >= kPreLinking) return true;
  module->SetStatus(kPreLinking);
215 216
  STACK_CHECK(isolate, false);

217 218
  if (module->IsSourceTextModule()) {
    return SourceTextModule::PrepareInstantiate(
219 220
        isolate, Handle<SourceTextModule>::cast(module), context, callback,
        callback_without_import_assertions);
221
  } else {
222
    return SyntheticModule::PrepareInstantiate(
223
        isolate, Handle<SyntheticModule>::cast(module), context);
224 225 226
  }
}

227
bool Module::FinishInstantiate(Isolate* isolate, Handle<Module> module,
228
                               ZoneForwardList<Handle<SourceTextModule>>* stack,
229 230
                               unsigned* dfs_index, Zone* zone) {
  DCHECK_NE(module->status(), kEvaluating);
231 232
  if (module->status() >= kLinking) return true;
  DCHECK_EQ(module->status(), kPreLinking);
233 234
  STACK_CHECK(isolate, false);

235 236 237 238 239
  if (module->IsSourceTextModule()) {
    return SourceTextModule::FinishInstantiate(
        isolate, Handle<SourceTextModule>::cast(module), stack, dfs_index,
        zone);
  } else {
240 241
    return SyntheticModule::FinishInstantiate(
        isolate, Handle<SyntheticModule>::cast(module));
242 243 244
  }
}

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 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269

  // In the event of errored evaluation, return a rejected promise.
  if (module->status() == kErrored) {
    // If we have a top level capability we assume it has already been
    // rejected, and return it here. Otherwise create a new promise and
    // reject it with the module's exception.
    if (module->top_level_capability().IsJSPromise()) {
      Handle<JSPromise> top_level_capability(
          JSPromise::cast(module->top_level_capability()), isolate);
      DCHECK(top_level_capability->status() == Promise::kRejected &&
             top_level_capability->result() == module->exception());
      return top_level_capability;
    }
    Handle<JSPromise> capability = isolate->factory()->NewJSPromise();
    JSPromise::Reject(capability, handle(module->exception(), isolate));
    return capability;
  }

  // Start of Evaluate () Concrete Method
  // 2. Assert: module.[[Status]] is "linked" or "evaluated".
270
  CHECK(module->status() == kLinked || module->status() == kEvaluated);
271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286

  // 3. If module.[[Status]] is "evaluated", set module to
  //    module.[[CycleRoot]].
  // A Synthetic Module has no children so it is its own cycle root.
  if (module->status() == kEvaluated && module->IsSourceTextModule()) {
    module = Handle<SourceTextModule>::cast(module)->GetCycleRoot(isolate);
  }

  // 4. If module.[[TopLevelCapability]] is not undefined, then
  //    a. Return module.[[TopLevelCapability]].[[Promise]].
  if (module->top_level_capability().IsJSPromise()) {
    return handle(JSPromise::cast(module->top_level_capability()), isolate);
  }
  DCHECK(module->top_level_capability().IsUndefined());

  if (module->IsSourceTextModule()) {
287 288
    return SourceTextModule::Evaluate(isolate,
                                      Handle<SourceTextModule>::cast(module));
289
  } else {
290 291
    return SyntheticModule::Evaluate(isolate,
                                     Handle<SyntheticModule>::cast(module));
292 293 294
  }
}

295 296
Handle<JSModuleNamespace> Module::GetModuleNamespace(Isolate* isolate,
                                                     Handle<Module> module) {
297
  Handle<HeapObject> object(module->module_namespace(), isolate);
298 299
  ReadOnlyRoots roots(isolate);
  if (!object->IsUndefined(roots)) {
300 301 302 303 304 305 306
    // Namespace object already exists.
    return Handle<JSModuleNamespace>::cast(object);
  }

  // Collect the export names.
  Zone zone(isolate->allocator(), ZONE_NAME);
  UnorderedModuleSet visited(&zone);
307 308 309 310 311 312

  if (module->IsSourceTextModule()) {
    SourceTextModule::FetchStarExports(
        isolate, Handle<SourceTextModule>::cast(module), &zone, &visited);
  }

313 314 315
  Handle<ObjectHashTable> exports(module->exports(), isolate);
  ZoneVector<Handle<String>> names(&zone);
  names.reserve(exports->NumberOfElements());
316
  for (InternalIndex i : exports->IterateEntries()) {
317
    Object key;
318
    if (!exports->ToKey(roots, i, &key)) continue;
319 320 321 322 323
    names.push_back(handle(String::cast(key), isolate));
  }
  DCHECK_EQ(static_cast<int>(names.size()), exports->NumberOfElements());

  // Sort them alphabetically.
324 325 326 327 328
  std::sort(names.begin(), names.end(),
            [&isolate](Handle<String> a, Handle<String> b) {
              return String::Compare(isolate, a, b) ==
                     ComparisonResult::kLessThan;
            });
329 330 331 332 333 334

  // Create the namespace object (initially empty).
  Handle<JSModuleNamespace> ns = isolate->factory()->NewJSModuleNamespace();
  ns->set_module(*module);
  module->set_module_namespace(*ns);

335 336
  // Create the properties in the namespace object. Transition the object
  // to dictionary mode so that property addition is faster.
337
  PropertyAttributes attr = DONT_DELETE;
338
  JSObject::NormalizeProperties(isolate, ns, CLEAR_INOBJECT_PROPERTIES,
339 340
                                static_cast<int>(names.size()),
                                "JSModuleNamespace");
341
  JSObject::NormalizeElements(ns);
342
  for (const auto& name : names) {
343 344 345 346
    uint32_t index = 0;
    if (name->AsArrayIndex(&index)) {
      JSObject::SetNormalizedElement(
          ns, index, Accessors::MakeModuleNamespaceEntryInfo(isolate, name),
347 348
          PropertyDetails(PropertyKind::kAccessor, attr,
                          PropertyCellType::kMutable));
349 350 351
    } else {
      JSObject::SetNormalizedProperty(
          ns, name, Accessors::MakeModuleNamespaceEntryInfo(isolate, name),
352 353
          PropertyDetails(PropertyKind::kAccessor, attr,
                          PropertyCellType::kMutable));
354
    }
355
  }
356
  JSObject::PreventExtensions(ns, kThrowOnError).ToChecked();
357

358 359 360 361 362
  // Optimize the namespace object as a prototype, for two reasons:
  // - The object's map is guaranteed not to be shared. ICs rely on this.
  // - We can store a pointer from the map back to the namespace object.
  //   Turbofan can use this for inlining the access.
  JSObject::OptimizeAsPrototype(ns);
363 364 365 366

  Handle<PrototypeInfo> proto_info =
      Map::GetOrCreatePrototypeInfo(Handle<JSObject>::cast(ns), isolate);
  proto_info->set_module_namespace(*ns);
367 368 369
  return ns;
}

370 371 372 373 374
bool JSModuleNamespace::HasExport(Isolate* isolate, Handle<String> name) {
  Handle<Object> object(module().exports().Lookup(name), isolate);
  return !object->IsTheHole(isolate);
}

375 376
MaybeHandle<Object> JSModuleNamespace::GetExport(Isolate* isolate,
                                                 Handle<String> name) {
377
  Handle<Object> object(module().exports().Lookup(name), isolate);
378 379 380 381
  if (object->IsTheHole(isolate)) {
    return isolate->factory()->undefined_value();
  }

382
  Handle<Object> value(Cell::cast(*object).value(), isolate);
383
  if (value->IsTheHole(isolate)) {
384 385 386 387 388 389 390 391 392 393
    // According to https://tc39.es/ecma262/#sec-InnerModuleLinking
    // step 10 and
    // https://tc39.es/ecma262/#sec-source-text-module-record-initialize-environment
    // step 8-25, variables must be declared in Link. And according to
    // https://tc39.es/ecma262/#sec-module-namespace-exotic-objects-get-p-receiver,
    // here accessing uninitialized variable error should be throwed.
    THROW_NEW_ERROR(isolate,
                    NewReferenceError(
                        MessageTemplate::kAccessedUninitializedVariable, name),
                    Object);
394 395 396 397 398
  }

  return value;
}

399 400 401 402 403 404
Maybe<PropertyAttributes> JSModuleNamespace::GetPropertyAttributes(
    LookupIterator* it) {
  Handle<JSModuleNamespace> object = it->GetHolder<JSModuleNamespace>();
  Handle<String> name = Handle<String>::cast(it->GetName());
  DCHECK_EQ(it->state(), LookupIterator::ACCESSOR);

405
  Isolate* isolate = it->isolate();
406

407
  Handle<Object> lookup(object->module().exports().Lookup(name), isolate);
408
  if (lookup->IsTheHole(isolate)) return Just(ABSENT);
409 410 411 412 413 414 415 416 417 418 419

  Handle<Object> value(Handle<Cell>::cast(lookup)->value(), isolate);
  if (value->IsTheHole(isolate)) {
    isolate->Throw(*isolate->factory()->NewReferenceError(
        MessageTemplate::kNotDefined, name));
    return Nothing<PropertyAttributes>();
  }

  return Just(it->property_attributes());
}

420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457
// ES
// https://tc39.es/ecma262/#sec-module-namespace-exotic-objects-defineownproperty-p-desc
// static
Maybe<bool> JSModuleNamespace::DefineOwnProperty(
    Isolate* isolate, Handle<JSModuleNamespace> object, Handle<Object> key,
    PropertyDescriptor* desc, Maybe<ShouldThrow> should_throw) {
  // 1. If Type(P) is Symbol, return OrdinaryDefineOwnProperty(O, P, Desc).
  if (key->IsSymbol()) {
    return OrdinaryDefineOwnProperty(isolate, object, key, desc, should_throw);
  }

  // 2. Let current be ? O.[[GetOwnProperty]](P).
  PropertyKey lookup_key(isolate, key);
  LookupIterator it(isolate, object, lookup_key, LookupIterator::OWN);
  PropertyDescriptor current;
  Maybe<bool> has_own = GetOwnPropertyDescriptor(&it, &current);
  MAYBE_RETURN(has_own, Nothing<bool>());

  // 3. If current is undefined, return false.
  // 4. If Desc.[[Configurable]] is present and has value true, return false.
  // 5. If Desc.[[Enumerable]] is present and has value false, return false.
  // 6. If ! IsAccessorDescriptor(Desc) is true, return false.
  // 7. If Desc.[[Writable]] is present and has value false, return false.
  // 8. If Desc.[[Value]] is present, return
  //    SameValue(Desc.[[Value]], current.[[Value]]).
  if (!has_own.FromJust() ||
      (desc->has_configurable() && desc->configurable()) ||
      (desc->has_enumerable() && !desc->enumerable()) ||
      PropertyDescriptor::IsAccessorDescriptor(desc) ||
      (desc->has_writable() && !desc->writable()) ||
      (desc->has_value() && !desc->value()->SameValue(*current.value()))) {
    RETURN_FAILURE(isolate, GetShouldThrow(isolate, should_throw),
                   NewTypeError(MessageTemplate::kRedefineDisallowed, key));
  }

  return Just(true);
}

458
bool Module::IsGraphAsync(Isolate* isolate) const {
459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474
  DisallowGarbageCollection no_gc;

  // Only SourceTextModules may be async.
  if (!IsSourceTextModule()) return false;
  SourceTextModule root = SourceTextModule::cast(*this);

  Zone zone(isolate->allocator(), ZONE_NAME);
  const size_t bucket_count = 2;
  ZoneUnorderedSet<Module, Module::Hash> visited(&zone, bucket_count);
  ZoneVector<SourceTextModule> worklist(&zone);
  visited.insert(root);
  worklist.push_back(root);

  do {
    SourceTextModule current = worklist.back();
    worklist.pop_back();
475
    DCHECK_GE(current.status(), kLinked);
476 477 478 479 480 481 482 483 484 485 486 487 488 489 490

    if (current.async()) return true;
    FixedArray requested_modules = current.requested_modules();
    for (int i = 0, length = requested_modules.length(); i < length; ++i) {
      Module descendant = Module::cast(requested_modules.get(i));
      if (descendant.IsSourceTextModule()) {
        const bool cycle = !visited.insert(descendant).second;
        if (!cycle) worklist.push_back(SourceTextModule::cast(descendant));
      }
    }
  } while (!worklist.empty());

  return false;
}

491 492
}  // namespace internal
}  // namespace v8