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

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

7
#include "src/ast/modules.h"
8
#include "src/debug/debug.h"
9
#include "src/execution/isolate-inl.h"
10
#include "src/init/bootstrapper.h"
11
#include "src/objects/module-inl.h"
12
#include "src/objects/string-set-inl.h"
13

14 15
namespace v8 {
namespace internal {
16

17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
void ScriptContextTable::AddLocalNamesFromContext(
    Isolate* isolate, Handle<ScriptContextTable> script_context_table,
    Handle<Context> script_context, bool ignore_duplicates,
    int script_context_index) {
  ReadOnlyRoots roots(isolate);
  PtrComprCageBase cage_base(isolate);
  Handle<NameToIndexHashTable> names_table(
      script_context_table->names_to_context_index(cage_base), isolate);
  Handle<ScopeInfo> scope_info(script_context->scope_info(cage_base), isolate);
  int local_count = scope_info->ContextLocalCount();
  names_table = names_table->EnsureCapacity(isolate, names_table, local_count);
  for (auto it : ScopeInfo::IterateLocalNames(scope_info)) {
    Handle<Name> name(it->name(cage_base), isolate);
    if (ignore_duplicates) {
      int32_t hash = NameToIndexShape::Hash(roots, name);
      if (names_table->FindEntry(cage_base, roots, name, hash).is_found()) {
        continue;
      }
    }
    names_table = NameToIndexHashTable::Add(isolate, names_table, name,
                                            script_context_index);
  }
  script_context_table->set_names_to_context_index(*names_table);
}

42
Handle<ScriptContextTable> ScriptContextTable::Extend(
43 44
    Isolate* isolate, Handle<ScriptContextTable> table,
    Handle<Context> script_context, bool ignore_duplicates) {
45
  Handle<ScriptContextTable> result;
46
  int used = table->used(kAcquireLoad);
47 48
  int length = table->length();
  CHECK(used >= 0 && length > 0 && used < length);
49
  if (used + kFirstContextSlotIndex == length) {
50
    CHECK(length < Smi::kMaxValue / 2);
51
    Handle<FixedArray> copy =
52
        isolate->factory()->CopyFixedArrayAndGrow(table, length);
53
    copy->set_map(ReadOnlyRoots(isolate).script_context_table_map());
54
    result = Handle<ScriptContextTable>::cast(copy);
55 56 57
  } else {
    result = table;
  }
58
  DCHECK(script_context->IsScriptContext());
59 60
  ScriptContextTable::AddLocalNamesFromContext(isolate, result, script_context,
                                               ignore_duplicates, used);
61
  result->set(used + kFirstContextSlotIndex, *script_context, kReleaseStore);
62
  result->set_used(used + 1, kReleaseStore);
63 64 65
  return result;
}

66 67 68 69 70 71 72 73 74 75
void Context::Initialize(Isolate* isolate) {
  ScopeInfo scope_info = this->scope_info();
  int header = scope_info.ContextHeaderLength();
  for (int var = 0; var < scope_info.ContextLocalCount(); var++) {
    if (scope_info.ContextLocalInitFlag(var) == kNeedsInitialization) {
      set(header + var, ReadOnlyRoots(isolate).the_hole_value());
    }
  }
}

76 77
bool ScriptContextTable::Lookup(Handle<String> name,
                                VariableLookupResult* result) {
78
  DisallowGarbageCollection no_gc;
79 80 81 82 83 84 85 86 87 88 89
  int index = names_to_context_index().Lookup(name);
  if (index == -1) return false;
  DCHECK_LE(0, index);
  DCHECK_LT(index, used(kAcquireLoad));
  Context context = get_context(index);
  DCHECK(context.IsScriptContext());
  int slot_index = context.scope_info().ContextSlotIndex(name, result);
  if (slot_index >= 0) {
    result->context_index = index;
    result->slot_index = slot_index;
    return true;
90 91 92 93
  }
  return false;
}

94
bool Context::is_declaration_context() const {
95 96
  if (IsFunctionContext() || IsNativeContext() || IsScriptContext() ||
      IsModuleContext()) {
97 98
    return true;
  }
99
  if (IsEvalContext()) {
100
    return scope_info().language_mode() == LanguageMode::kStrict;
101
  }
102
  if (!IsBlockContext()) return false;
103
  return scope_info().is_declaration_scope();
104 105
}

106
Context Context::declaration_context() const {
107
  Context current = *this;
108 109
  while (!current.is_declaration_context()) {
    current = current.previous();
110 111 112 113
  }
  return current;
}

114
Context Context::closure_context() const {
115
  Context current = *this;
116 117 118 119
  while (!current.IsFunctionContext() && !current.IsScriptContext() &&
         !current.IsModuleContext() && !current.IsNativeContext() &&
         !current.IsEvalContext()) {
    current = current.previous();
120 121 122
  }
  return current;
}
123

124
JSObject Context::extension_object() const {
125
  DCHECK(IsNativeContext() || IsFunctionContext() || IsBlockContext() ||
126
         IsEvalContext() || IsCatchContext());
127
  HeapObject object = extension();
128
  if (object.IsUndefined()) return JSObject();
129 130
  DCHECK(object.IsJSContextExtensionObject() ||
         (IsNativeContext() && object.IsJSGlobalObject()));
131 132 133
  return JSObject::cast(object);
}

134
JSReceiver Context::extension_receiver() const {
135
  DCHECK(IsNativeContext() || IsWithContext() || IsEvalContext() ||
136
         IsFunctionContext() || IsBlockContext());
137
  return IsWithContext() ? JSReceiver::cast(extension()) : extension_object();
138 139
}

140
SourceTextModule Context::module() const {
141
  Context current = *this;
142 143
  while (!current.IsModuleContext()) {
    current = current.previous();
144
  }
145
  return SourceTextModule::cast(current.extension());
146
}
147

148
JSGlobalObject Context::global_object() const {
149
  return JSGlobalObject::cast(native_context().extension());
150 151
}

152
Context Context::script_context() const {
153
  Context current = *this;
154 155
  while (!current.IsScriptContext()) {
    current = current.previous();
156 157 158 159
  }
  return current;
}

160
JSGlobalProxy Context::global_proxy() const {
161
  return native_context().global_proxy_object();
162 163
}

164 165 166 167
/**
 * Lookups a property in an object environment, taking the unscopables into
 * account. This is used For HasBinding spec algorithms for ObjectEnvironment.
 */
168
static Maybe<bool> UnscopableLookup(LookupIterator* it, bool is_with_context) {
169 170
  Isolate* isolate = it->isolate();

171
  Maybe<bool> found = JSReceiver::HasProperty(it);
172
  if (!is_with_context || found.IsNothing() || !found.FromJust()) return found;
173 174

  Handle<Object> unscopables;
175 176
  ASSIGN_RETURN_ON_EXCEPTION_VALUE(
      isolate, unscopables,
177 178
      JSReceiver::GetProperty(isolate,
                              Handle<JSReceiver>::cast(it->GetReceiver()),
179
                              isolate->factory()->unscopables_symbol()),
180 181
      Nothing<bool>());
  if (!unscopables->IsJSReceiver()) return Just(true);
Dan Elphick's avatar
Dan Elphick committed
182
  Handle<Object> blocklist;
183
  ASSIGN_RETURN_ON_EXCEPTION_VALUE(
Dan Elphick's avatar
Dan Elphick committed
184
      isolate, blocklist,
185
      JSReceiver::GetProperty(isolate, Handle<JSReceiver>::cast(unscopables),
186 187
                              it->name()),
      Nothing<bool>());
Dan Elphick's avatar
Dan Elphick committed
188
  return Just(!blocklist->BooleanValue(isolate));
189 190
}

191
static PropertyAttributes GetAttributesForMode(VariableMode mode) {
192 193
  DCHECK(IsSerializableVariableMode(mode));
  return IsConstVariableMode(mode) ? READ_ONLY : NONE;
194 195
}

196 197 198 199
// static
Handle<Object> Context::Lookup(Handle<Context> context, Handle<String> name,
                               ContextLookupFlags flags, int* index,
                               PropertyAttributes* attributes,
200
                               InitializationFlag* init_flag,
201 202
                               VariableMode* variable_mode,
                               bool* is_sloppy_function_name) {
203
  Isolate* isolate = context->GetIsolate();
204 205

  bool follow_context_chain = (flags & FOLLOW_CONTEXT_CHAIN) != 0;
206
  *index = kNotFound;
207
  *attributes = ABSENT;
208
  *init_flag = kCreatedInitialized;
209
  *variable_mode = VariableMode::kVar;
210 211 212
  if (is_sloppy_function_name != nullptr) {
    *is_sloppy_function_name = false;
  }
213 214 215 216 217 218 219 220 221

  if (FLAG_trace_contexts) {
    PrintF("Context::Lookup(");
    name->ShortPrint();
    PrintF(")\n");
  }

  do {
    if (FLAG_trace_contexts) {
222 223
      PrintF(" - looking in context %p",
             reinterpret_cast<void*>(context->ptr()));
224
      if (context->IsScriptContext()) PrintF(" (script context)");
225
      if (context->IsNativeContext()) PrintF(" (native context)");
226 227 228
      PrintF("\n");
    }

229
    // 1. Check global objects, subjects of with, and extension objects.
230
    DCHECK_IMPLIES(context->IsEvalContext() && context->has_extension(),
231
                   context->extension().IsTheHole(isolate));
232
    if ((context->IsNativeContext() || context->IsWithContext() ||
233
         context->IsFunctionContext() || context->IsBlockContext()) &&
234
        context->has_extension() && !context->extension_receiver().is_null()) {
235
      Handle<JSReceiver> object(context->extension_receiver(), isolate);
236 237

      if (context->IsNativeContext()) {
238
        DisallowGarbageCollection no_gc;
239
        if (FLAG_trace_contexts) {
240
          PrintF(" - trying other script contexts\n");
241
        }
242
        // Try other script contexts.
243
        ScriptContextTable script_contexts =
244
            context->global_object().native_context().script_context_table();
245
        VariableLookupResult r;
246
        if (script_contexts.Lookup(name, &r)) {
247
          Context script_context = script_contexts.get_context(r.context_index);
248
          if (FLAG_trace_contexts) {
249
            PrintF("=> found property in script context %d: %p\n",
250 251
                   r.context_index,
                   reinterpret_cast<void*>(script_context.ptr()));
252 253
          }
          *index = r.slot_index;
254
          *variable_mode = r.mode;
255
          *init_flag = r.init_flag;
256
          *attributes = GetAttributesForMode(r.mode);
257
          return handle(script_context, isolate);
258 259 260
        }
      }

261 262 263
      // Context extension objects needs to behave as if they have no
      // prototype.  So even if we want to follow prototype chains, we need
      // to only do a local lookup for context extension objects.
264
      Maybe<PropertyAttributes> maybe = Nothing<PropertyAttributes>();
265 266
      if ((flags & FOLLOW_PROTOTYPE_CHAIN) == 0 ||
          object->IsJSContextExtensionObject()) {
267
        maybe = JSReceiver::GetOwnPropertyAttributes(object, name);
268
      } else {
269 270
        // A with context will never bind "this", but debug-eval may look into
        // a with context when resolving "this". Other synthetic variables such
271 272 273
        // as new.target may be resolved as VariableMode::kDynamicLocal due to
        // bug v8:5405 , skipping them here serves as a workaround until a more
        // thorough fix can be applied.
274 275 276
        // TODO(v8:5405): Replace this check with a DCHECK when resolution of
        // of synthetic variables does not go through this code path.
        if (ScopeInfo::VariableIsSynthetic(*name)) {
277
          DCHECK(context->IsWithContext());
278 279
          maybe = Just(ABSENT);
        } else {
280
          LookupIterator it(isolate, object, name, object);
281
          Maybe<bool> found = UnscopableLookup(&it, context->IsWithContext());
282 283 284 285 286 287 288 289
          if (found.IsNothing()) {
            maybe = Nothing<PropertyAttributes>();
          } else {
            // Luckily, consumers of |maybe| only care whether the property
            // was absent or not, so we can return a dummy |NONE| value
            // for its attributes when it was present.
            maybe = Just(found.FromJust() ? NONE : ABSENT);
          }
290
        }
291
      }
292

293
      if (maybe.IsNothing()) return Handle<Object>();
294
      DCHECK(!isolate->has_pending_exception());
295
      *attributes = maybe.FromJust();
296

297
      if (maybe.FromJust() != ABSENT) {
298 299
        if (FLAG_trace_contexts) {
          PrintF("=> found property in context object %p\n",
300
                 reinterpret_cast<void*>(object->ptr()));
301
        }
302
        return object;
303 304 305
      }
    }

306
    // 2. Check the context proper if it has slots.
307
    if (context->IsFunctionContext() || context->IsBlockContext() ||
308
        context->IsScriptContext() || context->IsEvalContext() ||
309
        context->IsModuleContext() || context->IsCatchContext()) {
310
      DisallowGarbageCollection no_gc;
311 312
      // Use serialized scope information of functions and blocks to search
      // for the context index.
313
      ScopeInfo scope_info = context->scope_info();
314
      VariableLookupResult lookup_result;
315
      int slot_index = scope_info.ContextSlotIndex(name, &lookup_result);
316
      DCHECK(slot_index < 0 || slot_index >= MIN_CONTEXT_SLOTS);
317
      if (slot_index >= 0) {
Simon Zünd's avatar
Simon Zünd committed
318 319 320 321 322 323 324 325 326 327 328
        // Re-direct lookup to the ScriptContextTable in case we find a hole in
        // a REPL script context. REPL scripts allow re-declaration of
        // script-level let bindings. The value itself is stored in the script
        // context of the first script that declared a variable, all other
        // script contexts will contain 'the hole' for that particular name.
        if (scope_info.IsReplModeScope() &&
            context->get(slot_index).IsTheHole(isolate)) {
          context = Handle<Context>(context->previous(), isolate);
          continue;
        }

329
        if (FLAG_trace_contexts) {
330
          PrintF("=> found local in context slot %d (mode = %hhu)\n",
331
                 slot_index, static_cast<uint8_t>(lookup_result.mode));
332
        }
333
        *index = slot_index;
334 335 336
        *variable_mode = lookup_result.mode;
        *init_flag = lookup_result.init_flag;
        *attributes = GetAttributesForMode(lookup_result.mode);
337 338 339
        return context;
      }

340
      // Check the slot corresponding to the intermediate context holding
341 342
      // only the function name variable. It's conceptually (and spec-wise)
      // in an outer scope of the function's declaration scope.
343
      if (follow_context_chain && context->IsFunctionContext()) {
344
        int function_index = scope_info.FunctionContextSlotIndex(*name);
345
        if (function_index >= 0) {
346 347
          if (FLAG_trace_contexts) {
            PrintF("=> found intermediate function in context slot %d\n",
348
                   function_index);
349
          }
350
          *index = function_index;
351
          *attributes = READ_ONLY;
352
          *init_flag = kCreatedInitialized;
353
          *variable_mode = VariableMode::kConst;
354
          if (is_sloppy_function_name != nullptr &&
355
              is_sloppy(scope_info.language_mode())) {
356 357
            *is_sloppy_function_name = true;
          }
358 359 360
          return context;
        }
      }
361

362 363 364 365 366 367
      // Lookup variable in module imports and exports.
      if (context->IsModuleContext()) {
        VariableMode mode;
        InitializationFlag flag;
        MaybeAssignedFlag maybe_assigned_flag;
        int cell_index =
368
            scope_info.ModuleIndex(*name, &mode, &flag, &maybe_assigned_flag);
369 370 371 372 373 374 375
        if (cell_index != 0) {
          if (FLAG_trace_contexts) {
            PrintF("=> found in module imports or exports\n");
          }
          *index = cell_index;
          *variable_mode = mode;
          *init_flag = flag;
376 377
          *attributes = SourceTextModuleDescriptor::GetCellIndexKind(
                            cell_index) == SourceTextModuleDescriptor::kExport
378 379 380 381 382
                            ? GetAttributesForMode(mode)
                            : READ_ONLY;
          return handle(context->module(), isolate);
        }
      }
383 384
    } else if (context->IsDebugEvaluateContext()) {
      // Check materialized locals.
385
      Object ext = context->get(EXTENSION_INDEX);
386
      if (ext.IsJSReceiver()) {
387
        Handle<JSReceiver> extension(JSReceiver::cast(ext), isolate);
388
        LookupIterator it(isolate, extension, name, extension);
389 390 391 392
        Maybe<bool> found = JSReceiver::HasProperty(&it);
        if (found.FromMaybe(false)) {
          *attributes = NONE;
          return extension;
393 394
        }
      }
395

Dan Elphick's avatar
Dan Elphick committed
396
      // Check blocklist. Names that are listed, cannot be resolved further.
397 398 399
      ScopeInfo scope_info = context->scope_info();
      if (scope_info.HasLocalsBlockList() &&
          scope_info.LocalsBlockList().Has(isolate, name)) {
400
        if (FLAG_trace_contexts) {
Dan Elphick's avatar
Dan Elphick committed
401
          PrintF(" - name is blocklisted. Aborting.\n");
402 403 404 405
        }
        break;
      }

406
      // Check the original context, but do not follow its context chain.
407
      Object obj = context->get(WRAPPED_CONTEXT_INDEX);
408
      if (obj.IsContext()) {
409
        Handle<Context> wrapped_context(Context::cast(obj), isolate);
410
        Handle<Object> result =
411
            Context::Lookup(wrapped_context, name, DONT_FOLLOW_CHAINS, index,
412
                            attributes, init_flag, variable_mode);
413 414
        if (!result.is_null()) return result;
      }
415 416
    }

417
    // 3. Prepare to continue with the previous (next outermost) context.
418 419
    if (context->IsNativeContext()) break;

420
    context = Handle<Context>(context->previous(), isolate);
421 422 423 424 425
  } while (follow_context_chain);

  if (FLAG_trace_contexts) {
    PrintF("=> no property/slot found\n");
  }
426
  return Handle<Object>::null();
427 428
}

429
void NativeContext::AddOptimizedCode(CodeT code) {
430
  DCHECK(CodeKindCanDeoptimize(code.kind()));
431
  DCHECK(code.next_code_link().IsUndefined());
432
  code.set_next_code_link(OptimizedCodeListHead());
433
  set(OPTIMIZED_CODE_LIST, code, UPDATE_WEAK_WRITE_BARRIER, kReleaseStore);
434 435
}

436
Handle<Object> Context::ErrorMessageForCodeGenerationFromStrings() {
437 438
  Isolate* isolate = GetIsolate();
  Handle<Object> result(error_message_for_code_gen_from_strings(), isolate);
439
  if (!result->IsUndefined(isolate)) return result;
440
  return isolate->factory()->NewStringFromStaticChars(
441
      "Code generation from strings disallowed for this context");
442 443
}

444
#define COMPARE_NAME(index, type, name) \
445
  if (string->IsOneByteEqualTo(base::StaticCharVector(#name))) return index;
446 447 448 449 450 451 452 453

int Context::IntrinsicIndexForName(Handle<String> string) {
  NATIVE_CONTEXT_INTRINSIC_FUNCTIONS(COMPARE_NAME);
  return kNotFound;
}

#undef COMPARE_NAME

454 455 456 457 458 459
#define COMPARE_NAME(index, type, name)                                      \
  {                                                                          \
    const int name_length = static_cast<int>(arraysize(#name)) - 1;          \
    if ((length == name_length) && strncmp(string, #name, name_length) == 0) \
      return index;                                                          \
  }
460 461 462 463 464 465 466 467 468

int Context::IntrinsicIndexForName(const unsigned char* unsigned_string,
                                   int length) {
  const char* string = reinterpret_cast<const char*>(unsigned_string);
  NATIVE_CONTEXT_INTRINSIC_FUNCTIONS(COMPARE_NAME);
  return kNotFound;
}

#undef COMPARE_NAME
469

470
#ifdef VERIFY_HEAP
471 472 473 474 475 476 477 478 479 480
namespace {
// TODO(v8:12298): Fix js-context-specialization cctests to set up full
// native contexts instead of using dummy internalized strings as
// extensions.
bool IsContexExtensionTestObject(HeapObject extension) {
  return extension.IsInternalizedString() &&
         String::cast(extension).length() == 1;
}
}  // namespace

481 482
void Context::VerifyExtensionSlot(HeapObject extension) {
  CHECK(scope_info().HasContextExtensionSlot());
483
  // Early exit for potentially uninitialized contexfts.
484
  if (extension.IsUndefined()) return;
485 486 487 488 489
  if (extension.IsJSContextExtensionObject()) {
    CHECK((IsBlockContext() && scope_info().is_declaration_scope()) ||
          IsFunctionContext());
  } else if (IsModuleContext()) {
    CHECK(extension.IsSourceTextModule());
490
  } else if (IsDebugEvaluateContext() || IsWithContext()) {
491 492
    CHECK(extension.IsJSReceiver() ||
          (IsWithContext() && IsContexExtensionTestObject(extension)));
493
  } else if (IsNativeContext()) {
494 495 496 497 498
    CHECK(extension.IsJSGlobalObject() ||
          IsContexExtensionTestObject(extension));
  } else if (IsScriptContext()) {
    // Host-defined options can be stored on the context for classic scripts.
    CHECK(extension.IsFixedArray());
499 500 501 502
  }
}
#endif  // VERIFY_HEAP

503 504 505 506 507 508 509 510
void Context::set_extension(HeapObject object, WriteBarrierMode mode) {
  DCHECK(scope_info().HasContextExtensionSlot());
#ifdef VERIFY_HEAP
  VerifyExtensionSlot(object);
#endif
  set(EXTENSION_INDEX, object, mode);
}

511
#ifdef DEBUG
512

513
bool Context::IsBootstrappingOrValidParentContext(Object object,
514
                                                  Context child) {
515 516
  // During bootstrapping we allow all objects to pass as
  // contexts. This is necessary to fix circular dependencies.
517 518
  if (child.GetIsolate()->bootstrapper()->IsActive()) return true;
  if (!object.IsContext()) return false;
519
  Context context = Context::cast(object);
520 521
  return context.IsNativeContext() || context.IsScriptContext() ||
         context.IsModuleContext() || !child.IsModuleContext();
522 523 524 525
}

#endif

526
void NativeContext::ResetErrorsThrown() { set_errors_thrown(Smi::FromInt(0)); }
527

528
void NativeContext::IncrementErrorsThrown() {
529
  int previous_value = errors_thrown().value();
530 531 532
  set_errors_thrown(Smi::FromInt(previous_value + 1));
}

533
int NativeContext::GetErrorsThrown() { return errors_thrown().value(); }
534

535 536
STATIC_ASSERT(Context::MIN_CONTEXT_SLOTS == 2);
STATIC_ASSERT(Context::MIN_CONTEXT_EXTENDED_SLOTS == 3);
537 538 539 540 541 542 543
STATIC_ASSERT(NativeContext::kScopeInfoOffset ==
              Context::OffsetOfElementAt(NativeContext::SCOPE_INFO_INDEX));
STATIC_ASSERT(NativeContext::kPreviousOffset ==
              Context::OffsetOfElementAt(NativeContext::PREVIOUS_INDEX));
STATIC_ASSERT(NativeContext::kExtensionOffset ==
              Context::OffsetOfElementAt(NativeContext::EXTENSION_INDEX));

544
STATIC_ASSERT(NativeContext::kStartOfStrongFieldsOffset ==
545
              Context::OffsetOfElementAt(-1));
546 547 548 549 550 551 552 553
STATIC_ASSERT(NativeContext::kStartOfWeakFieldsOffset ==
              Context::OffsetOfElementAt(NativeContext::FIRST_WEAK_SLOT));
STATIC_ASSERT(NativeContext::kMicrotaskQueueOffset ==
              Context::SizeFor(NativeContext::NATIVE_CONTEXT_SLOTS));
STATIC_ASSERT(NativeContext::kSize ==
              (Context::SizeFor(NativeContext::NATIVE_CONTEXT_SLOTS) +
               kSystemPointerSize));

554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588
void NativeContext::RunPromiseHook(PromiseHookType type,
                                   Handle<JSPromise> promise,
                                   Handle<Object> parent) {
  Isolate* isolate = promise->GetIsolate();
  DCHECK(isolate->HasContextPromiseHooks());
  int contextSlot;

  switch (type) {
    case PromiseHookType::kInit:
      contextSlot = PROMISE_HOOK_INIT_FUNCTION_INDEX;
      break;
    case PromiseHookType::kResolve:
      contextSlot = PROMISE_HOOK_RESOLVE_FUNCTION_INDEX;
      break;
    case PromiseHookType::kBefore:
      contextSlot = PROMISE_HOOK_BEFORE_FUNCTION_INDEX;
      break;
    case PromiseHookType::kAfter:
      contextSlot = PROMISE_HOOK_AFTER_FUNCTION_INDEX;
      break;
    default:
      UNREACHABLE();
  }

  Handle<Object> hook(isolate->native_context()->get(contextSlot), isolate);
  if (hook->IsUndefined()) return;

  int argc = type == PromiseHookType::kInit ? 2 : 1;
  Handle<Object> argv[2] = {
    Handle<Object>::cast(promise),
    parent
  };

  Handle<Object> receiver = isolate->global_proxy();

589 590 591 592 593 594 595 596 597
  StackLimitCheck check(isolate);
  bool failed = false;
  if (check.HasOverflowed()) {
    isolate->StackOverflow();
    failed = true;
  } else {
    failed = Execution::Call(isolate, hook, receiver, argc, argv).is_null();
  }
  if (failed) {
598 599 600 601 602 603 604 605 606 607 608 609
    DCHECK(isolate->has_pending_exception());
    Handle<Object> exception(isolate->pending_exception(), isolate);

    MessageLocation* no_location = nullptr;
    Handle<JSMessageObject> message =
        isolate->CreateMessageOrAbort(exception, no_location);
    MessageHandler::ReportMessage(isolate, no_location, message);

    isolate->clear_pending_exception();
  }
}

610 611
}  // namespace internal
}  // namespace v8