contexts.cc 19.5 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/contexts.h"
6

7
#include "src/bootstrapper.h"
8
#include "src/debug/debug.h"
9
#include "src/isolate-inl.h"
10
#include "src/scopeinfo.h"
11

12 13
namespace v8 {
namespace internal {
14

15

16 17 18
Handle<ScriptContextTable> ScriptContextTable::Extend(
    Handle<ScriptContextTable> table, Handle<Context> script_context) {
  Handle<ScriptContextTable> result;
19 20 21
  int used = table->used();
  int length = table->length();
  CHECK(used >= 0 && length > 0 && used < length);
22
  if (used + kFirstContextSlot == length) {
23
    CHECK(length < Smi::kMaxValue / 2);
24 25 26 27 28
    Isolate* isolate = table->GetIsolate();
    Handle<FixedArray> copy =
        isolate->factory()->CopyFixedArrayAndGrow(table, length);
    copy->set_map(isolate->heap()->script_context_table_map());
    result = Handle<ScriptContextTable>::cast(copy);
29 30 31 32 33
  } else {
    result = table;
  }
  result->set_used(used + 1);

34
  DCHECK(script_context->IsScriptContext());
35
  result->set(used + kFirstContextSlot, *script_context);
36 37 38 39
  return result;
}


40
bool ScriptContextTable::Lookup(Handle<ScriptContextTable> table,
41 42 43
                                Handle<String> name, LookupResult* result) {
  for (int i = 0; i < table->used(); i++) {
    Handle<Context> context = GetContext(table, i);
44
    DCHECK(context->IsScriptContext());
45
    Handle<ScopeInfo> scope_info(context->scope_info());
46
    int slot_index = ScopeInfo::ContextSlotIndex(
47
        scope_info, name, &result->mode, &result->location, &result->init_flag,
48 49
        &result->maybe_assigned_flag);

50
    if (slot_index >= 0 && result->location == VariableLocation::CONTEXT) {
51 52 53 54 55 56 57 58 59
      result->context_index = i;
      result->slot_index = slot_index;
      return true;
    }
  }
  return false;
}


60 61 62 63 64 65 66 67 68 69 70 71 72
bool Context::is_declaration_context() {
  if (IsFunctionContext() || IsNativeContext() || IsScriptContext()) {
    return true;
  }
  if (!IsBlockContext()) return false;
  Object* ext = extension();
  // If we have the special extension, we immediately know it must be a
  // declaration scope. That's just a small performance shortcut.
  return ext->IsSloppyBlockWithEvalContextExtension()
      || ScopeInfo::cast(ext)->is_declaration_scope();
}


73 74
Context* Context::declaration_context() {
  Context* current = this;
75
  while (!current->is_declaration_context()) {
76
    current = current->previous();
77
    DCHECK(current->closure() == closure());
78 79 80 81 82
  }
  return current;
}


83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120
JSObject* Context::extension_object() {
  DCHECK(IsNativeContext() || IsFunctionContext() || IsBlockContext());
  Object* object = extension();
  if (object == nullptr) return nullptr;
  if (IsBlockContext()) {
    if (!object->IsSloppyBlockWithEvalContextExtension()) return nullptr;
    object = SloppyBlockWithEvalContextExtension::cast(object)->extension();
  }
  DCHECK(object->IsJSContextExtensionObject() ||
         (IsNativeContext() && object->IsJSGlobalObject()));
  return JSObject::cast(object);
}


JSReceiver* Context::extension_receiver() {
  DCHECK(IsNativeContext() || IsWithContext() ||
         IsFunctionContext() || IsBlockContext());
  return IsWithContext() ? JSReceiver::cast(extension()) : extension_object();
}


ScopeInfo* Context::scope_info() {
  DCHECK(IsModuleContext() || IsScriptContext() || IsBlockContext());
  Object* object = extension();
  if (object->IsSloppyBlockWithEvalContextExtension()) {
    DCHECK(IsBlockContext());
    object = SloppyBlockWithEvalContextExtension::cast(object)->scope_info();
  }
  return ScopeInfo::cast(object);
}


String* Context::catch_name() {
  DCHECK(IsCatchContext());
  return String::cast(extension());
}


121
JSBuiltinsObject* Context::builtins() {
122
  GlobalObject* object = global_object();
123 124 125
  if (object->IsJSGlobalObject()) {
    return JSGlobalObject::cast(object)->builtins();
  } else {
126
    DCHECK(object->IsJSBuiltinsObject());
127 128 129 130 131
    return JSBuiltinsObject::cast(object);
  }
}


132
Context* Context::script_context() {
133
  Context* current = this;
134
  while (!current->IsScriptContext()) {
135 136 137 138 139 140
    current = current->previous();
  }
  return current;
}


141
Context* Context::native_context() {
142 143 144 145 146 147 148
  // Fast case: the receiver context is already a native context.
  if (IsNativeContext()) return this;
  // The global object has a direct pointer to the native context. If the
  // following DCHECK fails, the native context is probably being accessed
  // indirectly during bootstrapping. This is unsupported.
  DCHECK(global_object()->IsGlobalObject());
  return global_object()->native_context();
149 150 151
}


152
JSObject* Context::global_proxy() {
153
  return native_context()->global_proxy_object();
154 155
}

156

157
void Context::set_global_proxy(JSObject* object) {
158
  native_context()->set_global_proxy_object(object);
159 160 161
}


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

  Maybe<PropertyAttributes> attrs = JSReceiver::GetPropertyAttributes(it);
170 171
  DCHECK(attrs.IsJust() || isolate->has_pending_exception());
  if (!attrs.IsJust() || attrs.FromJust() == ABSENT) return attrs;
172

173
  Handle<Symbol> unscopables_symbol = isolate->factory()->unscopables_symbol();
174 175 176 177 178
  Handle<Object> receiver = it->GetReceiver();
  Handle<Object> unscopables;
  MaybeHandle<Object> maybe_unscopables =
      Object::GetProperty(receiver, unscopables_symbol);
  if (!maybe_unscopables.ToHandle(&unscopables)) {
179
    return Nothing<PropertyAttributes>();
180 181
  }
  if (!unscopables->IsSpecObject()) return attrs;
182 183 184 185
  Handle<Object> blacklist;
  MaybeHandle<Object> maybe_blacklist =
      Object::GetProperty(unscopables, it->name());
  if (!maybe_blacklist.ToHandle(&blacklist)) {
186
    DCHECK(isolate->has_pending_exception());
187
    return Nothing<PropertyAttributes>();
188
  }
189
  return blacklist->BooleanValue() ? Just(ABSENT) : attrs;
190 191
}

192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218
static void GetAttributesAndBindingFlags(VariableMode mode,
                                         InitializationFlag init_flag,
                                         PropertyAttributes* attributes,
                                         BindingFlags* binding_flags) {
  switch (mode) {
    case VAR:
      *attributes = NONE;
      *binding_flags = MUTABLE_IS_INITIALIZED;
      break;
    case LET:
      *attributes = NONE;
      *binding_flags = (init_flag == kNeedsInitialization)
                           ? MUTABLE_CHECK_INITIALIZED
                           : MUTABLE_IS_INITIALIZED;
      break;
    case CONST_LEGACY:
      *attributes = READ_ONLY;
      *binding_flags = (init_flag == kNeedsInitialization)
                           ? IMMUTABLE_CHECK_INITIALIZED
                           : IMMUTABLE_IS_INITIALIZED;
      break;
    case CONST:
      *attributes = READ_ONLY;
      *binding_flags = (init_flag == kNeedsInitialization)
                           ? IMMUTABLE_CHECK_INITIALIZED_HARMONY
                           : IMMUTABLE_IS_INITIALIZED_HARMONY;
      break;
219 220 221 222
    case IMPORT:
      // TODO(ES6)
      UNREACHABLE();
      break;
223 224 225 226 227 228 229 230 231 232 233 234 235 236 237
    case DYNAMIC:
    case DYNAMIC_GLOBAL:
    case DYNAMIC_LOCAL:
    case TEMPORARY:
      // Note: Fixed context slots are statically allocated by the compiler.
      // Statically allocated variables always have a statically known mode,
      // which is the mode with which they were declared when added to the
      // scope. Thus, the DYNAMIC mode (which corresponds to dynamically
      // declared variables that were introduced through declaration nodes)
      // must not appear here.
      UNREACHABLE();
      break;
  }
}

238

239 240
Handle<Object> Context::Lookup(Handle<String> name,
                               ContextLookupFlags flags,
241
                               int* index,
242 243
                               PropertyAttributes* attributes,
                               BindingFlags* binding_flags) {
244 245
  Isolate* isolate = GetIsolate();
  Handle<Context> context(this, isolate);
246 247

  bool follow_context_chain = (flags & FOLLOW_CONTEXT_CHAIN) != 0;
248
  *index = kNotFound;
249
  *attributes = ABSENT;
250
  *binding_flags = MISSING_BINDING;
251 252 253 254 255 256 257 258 259

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

  do {
    if (FLAG_trace_contexts) {
260
      PrintF(" - looking in context %p", reinterpret_cast<void*>(*context));
261
      if (context->IsScriptContext()) PrintF(" (script context)");
262
      if (context->IsNativeContext()) PrintF(" (native context)");
263 264 265
      PrintF("\n");
    }

266
    // 1. Check global objects, subjects of with, and extension objects.
267 268 269 270
    if ((context->IsNativeContext() || context->IsWithContext() ||
         context->IsFunctionContext() || context->IsBlockContext()) &&
        context->extension_receiver() != nullptr) {
      Handle<JSReceiver> object(context->extension_receiver());
271 272 273

      if (context->IsNativeContext()) {
        if (FLAG_trace_contexts) {
274
          PrintF(" - trying other script contexts\n");
275
        }
276 277 278 279 280
        // Try other script contexts.
        Handle<ScriptContextTable> script_contexts(
            context->global_object()->native_context()->script_context_table());
        ScriptContextTable::LookupResult r;
        if (ScriptContextTable::Lookup(script_contexts, name, &r)) {
281
          if (FLAG_trace_contexts) {
282
            Handle<Context> c = ScriptContextTable::GetContext(script_contexts,
283
                                                               r.context_index);
284
            PrintF("=> found property in script context %d: %p\n",
285 286 287 288 289
                   r.context_index, reinterpret_cast<void*>(*c));
          }
          *index = r.slot_index;
          GetAttributesAndBindingFlags(r.mode, r.init_flag, attributes,
                                       binding_flags);
290
          return ScriptContextTable::GetContext(script_contexts,
291 292 293 294
                                                r.context_index);
        }
      }

295 296 297
      // 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.
298
      Maybe<PropertyAttributes> maybe = Nothing<PropertyAttributes>();
299 300
      if ((flags & FOLLOW_PROTOTYPE_CHAIN) == 0 ||
          object->IsJSContextExtensionObject()) {
301
        maybe = JSReceiver::GetOwnPropertyAttributes(object, name);
302
      } else if (context->IsWithContext()) {
303 304 305 306 307 308 309
        // A with context will never bind "this".
        if (name->Equals(*isolate->factory()->this_string())) {
          maybe = Just(ABSENT);
        } else {
          LookupIterator it(object, name);
          maybe = UnscopableLookup(&it);
        }
310
      } else {
311
        maybe = JSReceiver::GetPropertyAttributes(object, name);
312
      }
313

314
      if (!maybe.IsJust()) return Handle<Object>();
315
      DCHECK(!isolate->has_pending_exception());
316
      *attributes = maybe.FromJust();
317

318
      if (maybe.FromJust() != ABSENT) {
319 320 321
        if (FLAG_trace_contexts) {
          PrintF("=> found property in context object %p\n",
                 reinterpret_cast<void*>(*object));
322
        }
323
        return object;
324 325 326
      }
    }

327
    // 2. Check the context proper if it has slots.
328
    if (context->IsFunctionContext() || context->IsBlockContext() ||
329
        context->IsScriptContext()) {
330 331
      // Use serialized scope information of functions and blocks to search
      // for the context index.
332 333 334
      Handle<ScopeInfo> scope_info(context->IsFunctionContext()
          ? context->closure()->shared()->scope_info()
          : context->scope_info());
335
      VariableMode mode;
336
      VariableLocation location;
337
      InitializationFlag init_flag;
338 339 340 341
      // TODO(sigurds) Figure out whether maybe_assigned_flag should
      // be used to compute binding_flags.
      MaybeAssignedFlag maybe_assigned_flag;
      int slot_index = ScopeInfo::ContextSlotIndex(
342
          scope_info, name, &mode, &location, &init_flag, &maybe_assigned_flag);
343
      DCHECK(slot_index < 0 || slot_index >= MIN_CONTEXT_SLOTS);
344
      if (slot_index >= 0 && location == VariableLocation::CONTEXT) {
345 346
        if (FLAG_trace_contexts) {
          PrintF("=> found local in context slot %d (mode = %d)\n",
347
                 slot_index, mode);
348
        }
349
        *index = slot_index;
350 351
        GetAttributesAndBindingFlags(mode, init_flag, attributes,
                                     binding_flags);
352 353 354
        return context;
      }

355 356
      // Check the slot corresponding to the intermediate context holding
      // only the function name variable.
357
      if (follow_context_chain && context->IsFunctionContext()) {
358 359
        VariableMode mode;
        int function_index = scope_info->FunctionContextSlotIndex(*name, &mode);
360
        if (function_index >= 0) {
361 362
          if (FLAG_trace_contexts) {
            PrintF("=> found intermediate function in context slot %d\n",
363
                   function_index);
364
          }
365
          *index = function_index;
366
          *attributes = READ_ONLY;
367
          DCHECK(mode == CONST_LEGACY || mode == CONST);
368
          *binding_flags = (mode == CONST_LEGACY)
369
              ? IMMUTABLE_IS_INITIALIZED : IMMUTABLE_IS_INITIALIZED_HARMONY;
370 371 372
          return context;
        }
      }
373 374 375

    } else if (context->IsCatchContext()) {
      // Catch contexts have the variable name in the extension slot.
376
      if (String::Equals(name, handle(context->catch_name()))) {
377 378 379 380 381 382 383 384
        if (FLAG_trace_contexts) {
          PrintF("=> found in catch context\n");
        }
        *index = Context::THROWN_OBJECT_INDEX;
        *attributes = NONE;
        *binding_flags = MUTABLE_IS_INITIALIZED;
        return context;
      }
385 386
    }

387
    // 3. Prepare to continue with the previous (next outermost) context.
388
    if (context->IsNativeContext()) {
389
      follow_context_chain = false;
390
    } else {
391
      context = Handle<Context>(context->previous(), isolate);
392 393 394 395 396 397
    }
  } while (follow_context_chain);

  if (FLAG_trace_contexts) {
    PrintF("=> no property/slot found\n");
  }
398
  return Handle<Object>::null();
399 400 401
}


402 403 404 405
void Context::InitializeGlobalSlots() {
  DCHECK(IsScriptContext());
  DisallowHeapAllocation no_gc;

406
  ScopeInfo* scope_info = this->scope_info();
407 408 409 410 411 412 413 414 415 416 417 418 419 420

  int context_globals = scope_info->ContextGlobalCount();
  if (context_globals > 0) {
    PropertyCell* empty_cell = GetHeap()->empty_property_cell();

    int context_locals = scope_info->ContextLocalCount();
    int index = Context::MIN_CONTEXT_SLOTS + context_locals;
    for (int i = 0; i < context_globals; i++) {
      set(index++, empty_cell);
    }
  }
}


421
void Context::AddOptimizedFunction(JSFunction* function) {
422 423
  DCHECK(IsNativeContext());
#ifdef ENABLE_SLOW_DCHECKS
424 425 426 427 428 429
  if (FLAG_enable_slow_asserts) {
    Object* element = get(OPTIMIZED_FUNCTIONS_LIST);
    while (!element->IsUndefined()) {
      CHECK(element != function);
      element = JSFunction::cast(element)->next_function_link();
    }
430 431
  }

432
  // Check that the context belongs to the weak native contexts list.
433
  bool found = false;
434
  Object* context = GetHeap()->native_contexts_list();
435 436 437 438 439 440 441 442 443
  while (!context->IsUndefined()) {
    if (context == this) {
      found = true;
      break;
    }
    context = Context::cast(context)->get(Context::NEXT_CONTEXT_LINK);
  }
  CHECK(found);
#endif
444 445 446 447 448 449 450 451

  // If the function link field is already used then the function was
  // enqueued as a code flushing candidate and we remove it now.
  if (!function->next_function_link()->IsUndefined()) {
    CodeFlusher* flusher = GetHeap()->mark_compact_collector()->code_flusher();
    flusher->EvictCandidate(function);
  }

452
  DCHECK(function->next_function_link()->IsUndefined());
453

454 455
  function->set_next_function_link(get(OPTIMIZED_FUNCTIONS_LIST),
                                   UPDATE_WEAK_WRITE_BARRIER);
456
  set(OPTIMIZED_FUNCTIONS_LIST, function, UPDATE_WEAK_WRITE_BARRIER);
457 458 459 460
}


void Context::RemoveOptimizedFunction(JSFunction* function) {
461
  DCHECK(IsNativeContext());
462 463 464 465
  Object* element = get(OPTIMIZED_FUNCTIONS_LIST);
  JSFunction* prev = NULL;
  while (!element->IsUndefined()) {
    JSFunction* element_function = JSFunction::cast(element);
466
    DCHECK(element_function->next_function_link()->IsUndefined() ||
467 468 469
           element_function->next_function_link()->IsJSFunction());
    if (element_function == function) {
      if (prev == NULL) {
470 471
        set(OPTIMIZED_FUNCTIONS_LIST, element_function->next_function_link(),
            UPDATE_WEAK_WRITE_BARRIER);
472
      } else {
473 474
        prev->set_next_function_link(element_function->next_function_link(),
                                     UPDATE_WEAK_WRITE_BARRIER);
475
      }
476 477
      element_function->set_next_function_link(GetHeap()->undefined_value(),
                                               UPDATE_WEAK_WRITE_BARRIER);
478 479 480 481 482 483 484 485 486
      return;
    }
    prev = element_function;
    element = element_function->next_function_link();
  }
  UNREACHABLE();
}


487
void Context::SetOptimizedFunctionsListHead(Object* head) {
488
  DCHECK(IsNativeContext());
489
  set(OPTIMIZED_FUNCTIONS_LIST, head, UPDATE_WEAK_WRITE_BARRIER);
490 491 492
}


493
Object* Context::OptimizedFunctionsListHead() {
494
  DCHECK(IsNativeContext());
495 496 497 498
  return get(OPTIMIZED_FUNCTIONS_LIST);
}


499
void Context::AddOptimizedCode(Code* code) {
500 501 502
  DCHECK(IsNativeContext());
  DCHECK(code->kind() == Code::OPTIMIZED_FUNCTION);
  DCHECK(code->next_code_link()->IsUndefined());
503
  code->set_next_code_link(get(OPTIMIZED_CODE_LIST));
504
  set(OPTIMIZED_CODE_LIST, code, UPDATE_WEAK_WRITE_BARRIER);
505 506 507 508
}


void Context::SetOptimizedCodeListHead(Object* head) {
509
  DCHECK(IsNativeContext());
510
  set(OPTIMIZED_CODE_LIST, head, UPDATE_WEAK_WRITE_BARRIER);
511 512 513 514
}


Object* Context::OptimizedCodeListHead() {
515
  DCHECK(IsNativeContext());
516 517 518 519 520
  return get(OPTIMIZED_CODE_LIST);
}


void Context::SetDeoptimizedCodeListHead(Object* head) {
521
  DCHECK(IsNativeContext());
522
  set(DEOPTIMIZED_CODE_LIST, head, UPDATE_WEAK_WRITE_BARRIER);
523 524 525 526
}


Object* Context::DeoptimizedCodeListHead() {
527
  DCHECK(IsNativeContext());
528
  return get(DEOPTIMIZED_CODE_LIST);
529 530 531
}


532
Handle<Object> Context::ErrorMessageForCodeGenerationFromStrings() {
533 534
  Isolate* isolate = GetIsolate();
  Handle<Object> result(error_message_for_code_gen_from_strings(), isolate);
535
  if (!result->IsUndefined()) return result;
536
  return isolate->factory()->NewStringFromStaticChars(
537
      "Code generation from strings disallowed for this context");
538 539 540
}


541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557
#define COMPARE_NAME(index, type, name) \
  if (string->IsOneByteEqualTo(STATIC_CHAR_VECTOR(#name))) return index;

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


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

#undef COMPARE_NAME


558 559 560 561 562 563 564 565 566 567
bool Context::IsJSBuiltin(Handle<Context> native_context,
                          Handle<JSFunction> function) {
#define COMPARE_FUNCTION(index, type, name) \
  if (*function == native_context->get(index)) return true;
  NATIVE_CONTEXT_JS_BUILTINS(COMPARE_FUNCTION);
#undef COMPARE_FUNCTION
  return false;
}


568
#ifdef DEBUG
569 570
bool Context::IsBootstrappingOrValidParentContext(
    Object* object, Context* child) {
571 572
  // During bootstrapping we allow all objects to pass as
  // contexts. This is necessary to fix circular dependencies.
573
  if (child->GetIsolate()->bootstrapper()->IsActive()) return true;
574 575
  if (!object->IsContext()) return false;
  Context* context = Context::cast(object);
576
  return context->IsNativeContext() || context->IsScriptContext() ||
577
         context->IsModuleContext() || !child->IsModuleContext();
578 579 580
}


581
bool Context::IsBootstrappingOrGlobalObject(Isolate* isolate, Object* object) {
582 583
  // During bootstrapping we allow all objects to pass as global
  // objects. This is necessary to fix circular dependencies.
584 585 586
  return isolate->heap()->gc_state() != Heap::NOT_IN_GC ||
      isolate->bootstrapper()->IsActive() ||
      object->IsGlobalObject();
587 588 589
}
#endif

590 591
}  // namespace internal
}  // namespace v8