Home | History | Annotate | Download | only in src
      1 // Copyright 2011 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/contexts.h"
      6 
      7 #include "src/ast/scopeinfo.h"
      8 #include "src/bootstrapper.h"
      9 #include "src/debug/debug.h"
     10 #include "src/isolate-inl.h"
     11 
     12 namespace v8 {
     13 namespace internal {
     14 
     15 
     16 Handle<ScriptContextTable> ScriptContextTable::Extend(
     17     Handle<ScriptContextTable> table, Handle<Context> script_context) {
     18   Handle<ScriptContextTable> result;
     19   int used = table->used();
     20   int length = table->length();
     21   CHECK(used >= 0 && length > 0 && used < length);
     22   if (used + kFirstContextSlot == length) {
     23     CHECK(length < Smi::kMaxValue / 2);
     24     Isolate* isolate = table->GetIsolate();
     25     Handle<FixedArray> copy =
     26         isolate->factory()->CopyFixedArrayAndGrow(table, length);
     27     copy->set_map(isolate->heap()->script_context_table_map());
     28     result = Handle<ScriptContextTable>::cast(copy);
     29   } else {
     30     result = table;
     31   }
     32   result->set_used(used + 1);
     33 
     34   DCHECK(script_context->IsScriptContext());
     35   result->set(used + kFirstContextSlot, *script_context);
     36   return result;
     37 }
     38 
     39 
     40 bool ScriptContextTable::Lookup(Handle<ScriptContextTable> table,
     41                                 Handle<String> name, LookupResult* result) {
     42   for (int i = 0; i < table->used(); i++) {
     43     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->init_flag,
     48         &result->maybe_assigned_flag);
     49 
     50     if (slot_index >= 0) {
     51       result->context_index = i;
     52       result->slot_index = slot_index;
     53       return true;
     54     }
     55   }
     56   return false;
     57 }
     58 
     59 
     60 bool Context::is_declaration_context() {
     61   if (IsFunctionContext() || IsNativeContext() || IsScriptContext()) {
     62     return true;
     63   }
     64   if (!IsBlockContext()) return false;
     65   Object* ext = extension();
     66   // If we have the special extension, we immediately know it must be a
     67   // declaration scope. That's just a small performance shortcut.
     68   return ext->IsSloppyBlockWithEvalContextExtension()
     69       || ScopeInfo::cast(ext)->is_declaration_scope();
     70 }
     71 
     72 
     73 Context* Context::declaration_context() {
     74   Context* current = this;
     75   while (!current->is_declaration_context()) {
     76     current = current->previous();
     77     DCHECK(current->closure() == closure());
     78   }
     79   return current;
     80 }
     81 
     82 
     83 JSObject* Context::extension_object() {
     84   DCHECK(IsNativeContext() || IsFunctionContext() || IsBlockContext());
     85   HeapObject* object = extension();
     86   if (object->IsTheHole()) return nullptr;
     87   if (IsBlockContext()) {
     88     if (!object->IsSloppyBlockWithEvalContextExtension()) return nullptr;
     89     object = SloppyBlockWithEvalContextExtension::cast(object)->extension();
     90   }
     91   DCHECK(object->IsJSContextExtensionObject() ||
     92          (IsNativeContext() && object->IsJSGlobalObject()));
     93   return JSObject::cast(object);
     94 }
     95 
     96 
     97 JSReceiver* Context::extension_receiver() {
     98   DCHECK(IsNativeContext() || IsWithContext() ||
     99          IsFunctionContext() || IsBlockContext());
    100   return IsWithContext() ? JSReceiver::cast(extension()) : extension_object();
    101 }
    102 
    103 
    104 ScopeInfo* Context::scope_info() {
    105   DCHECK(IsModuleContext() || IsScriptContext() || IsBlockContext());
    106   HeapObject* object = extension();
    107   if (object->IsSloppyBlockWithEvalContextExtension()) {
    108     DCHECK(IsBlockContext());
    109     object = SloppyBlockWithEvalContextExtension::cast(object)->scope_info();
    110   }
    111   return ScopeInfo::cast(object);
    112 }
    113 
    114 
    115 String* Context::catch_name() {
    116   DCHECK(IsCatchContext());
    117   return String::cast(extension());
    118 }
    119 
    120 
    121 JSGlobalObject* Context::global_object() {
    122   return JSGlobalObject::cast(native_context()->extension());
    123 }
    124 
    125 
    126 Context* Context::script_context() {
    127   Context* current = this;
    128   while (!current->IsScriptContext()) {
    129     current = current->previous();
    130   }
    131   return current;
    132 }
    133 
    134 
    135 JSObject* Context::global_proxy() {
    136   return native_context()->global_proxy_object();
    137 }
    138 
    139 
    140 void Context::set_global_proxy(JSObject* object) {
    141   native_context()->set_global_proxy_object(object);
    142 }
    143 
    144 
    145 /**
    146  * Lookups a property in an object environment, taking the unscopables into
    147  * account. This is used For HasBinding spec algorithms for ObjectEnvironment.
    148  */
    149 static Maybe<bool> UnscopableLookup(LookupIterator* it) {
    150   Isolate* isolate = it->isolate();
    151 
    152   Maybe<bool> found = JSReceiver::HasProperty(it);
    153   if (!found.IsJust() || !found.FromJust()) return found;
    154 
    155   Handle<Object> unscopables;
    156   ASSIGN_RETURN_ON_EXCEPTION_VALUE(
    157       isolate, unscopables,
    158       Object::GetProperty(it->GetReceiver(),
    159                           isolate->factory()->unscopables_symbol()),
    160       Nothing<bool>());
    161   if (!unscopables->IsJSReceiver()) return Just(true);
    162   Handle<Object> blacklist;
    163   ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, blacklist,
    164                                    Object::GetProperty(unscopables, it->name()),
    165                                    Nothing<bool>());
    166   return Just(!blacklist->BooleanValue());
    167 }
    168 
    169 static void GetAttributesAndBindingFlags(VariableMode mode,
    170                                          InitializationFlag init_flag,
    171                                          PropertyAttributes* attributes,
    172                                          BindingFlags* binding_flags) {
    173   switch (mode) {
    174     case VAR:
    175       *attributes = NONE;
    176       *binding_flags = MUTABLE_IS_INITIALIZED;
    177       break;
    178     case LET:
    179       *attributes = NONE;
    180       *binding_flags = (init_flag == kNeedsInitialization)
    181                            ? MUTABLE_CHECK_INITIALIZED
    182                            : MUTABLE_IS_INITIALIZED;
    183       break;
    184     case CONST_LEGACY:
    185       *attributes = READ_ONLY;
    186       *binding_flags = (init_flag == kNeedsInitialization)
    187                            ? IMMUTABLE_CHECK_INITIALIZED
    188                            : IMMUTABLE_IS_INITIALIZED;
    189       break;
    190     case CONST:
    191       *attributes = READ_ONLY;
    192       *binding_flags = (init_flag == kNeedsInitialization)
    193                            ? IMMUTABLE_CHECK_INITIALIZED_HARMONY
    194                            : IMMUTABLE_IS_INITIALIZED_HARMONY;
    195       break;
    196     case IMPORT:
    197       // TODO(ES6)
    198       UNREACHABLE();
    199       break;
    200     case DYNAMIC:
    201     case DYNAMIC_GLOBAL:
    202     case DYNAMIC_LOCAL:
    203     case TEMPORARY:
    204       // Note: Fixed context slots are statically allocated by the compiler.
    205       // Statically allocated variables always have a statically known mode,
    206       // which is the mode with which they were declared when added to the
    207       // scope. Thus, the DYNAMIC mode (which corresponds to dynamically
    208       // declared variables that were introduced through declaration nodes)
    209       // must not appear here.
    210       UNREACHABLE();
    211       break;
    212   }
    213 }
    214 
    215 
    216 Handle<Object> Context::Lookup(Handle<String> name,
    217                                ContextLookupFlags flags,
    218                                int* index,
    219                                PropertyAttributes* attributes,
    220                                BindingFlags* binding_flags) {
    221   Isolate* isolate = GetIsolate();
    222   Handle<Context> context(this, isolate);
    223 
    224   bool follow_context_chain = (flags & FOLLOW_CONTEXT_CHAIN) != 0;
    225   *index = kNotFound;
    226   *attributes = ABSENT;
    227   *binding_flags = MISSING_BINDING;
    228 
    229   if (FLAG_trace_contexts) {
    230     PrintF("Context::Lookup(");
    231     name->ShortPrint();
    232     PrintF(")\n");
    233   }
    234 
    235   do {
    236     if (FLAG_trace_contexts) {
    237       PrintF(" - looking in context %p", reinterpret_cast<void*>(*context));
    238       if (context->IsScriptContext()) PrintF(" (script context)");
    239       if (context->IsNativeContext()) PrintF(" (native context)");
    240       PrintF("\n");
    241     }
    242 
    243     // 1. Check global objects, subjects of with, and extension objects.
    244     if ((context->IsNativeContext() ||
    245          (context->IsWithContext() && ((flags & SKIP_WITH_CONTEXT) == 0)) ||
    246          context->IsFunctionContext() || context->IsBlockContext()) &&
    247         context->extension_receiver() != nullptr) {
    248       Handle<JSReceiver> object(context->extension_receiver());
    249 
    250       if (context->IsNativeContext()) {
    251         if (FLAG_trace_contexts) {
    252           PrintF(" - trying other script contexts\n");
    253         }
    254         // Try other script contexts.
    255         Handle<ScriptContextTable> script_contexts(
    256             context->global_object()->native_context()->script_context_table());
    257         ScriptContextTable::LookupResult r;
    258         if (ScriptContextTable::Lookup(script_contexts, name, &r)) {
    259           if (FLAG_trace_contexts) {
    260             Handle<Context> c = ScriptContextTable::GetContext(script_contexts,
    261                                                                r.context_index);
    262             PrintF("=> found property in script context %d: %p\n",
    263                    r.context_index, reinterpret_cast<void*>(*c));
    264           }
    265           *index = r.slot_index;
    266           GetAttributesAndBindingFlags(r.mode, r.init_flag, attributes,
    267                                        binding_flags);
    268           return ScriptContextTable::GetContext(script_contexts,
    269                                                 r.context_index);
    270         }
    271       }
    272 
    273       // Context extension objects needs to behave as if they have no
    274       // prototype.  So even if we want to follow prototype chains, we need
    275       // to only do a local lookup for context extension objects.
    276       Maybe<PropertyAttributes> maybe = Nothing<PropertyAttributes>();
    277       if ((flags & FOLLOW_PROTOTYPE_CHAIN) == 0 ||
    278           object->IsJSContextExtensionObject()) {
    279         maybe = JSReceiver::GetOwnPropertyAttributes(object, name);
    280       } else if (context->IsWithContext()) {
    281         // A with context will never bind "this".
    282         if (name->Equals(*isolate->factory()->this_string())) {
    283           maybe = Just(ABSENT);
    284         } else {
    285           LookupIterator it(object, name);
    286           Maybe<bool> found = UnscopableLookup(&it);
    287           if (found.IsNothing()) {
    288             maybe = Nothing<PropertyAttributes>();
    289           } else {
    290             // Luckily, consumers of |maybe| only care whether the property
    291             // was absent or not, so we can return a dummy |NONE| value
    292             // for its attributes when it was present.
    293             maybe = Just(found.FromJust() ? NONE : ABSENT);
    294           }
    295         }
    296       } else {
    297         maybe = JSReceiver::GetPropertyAttributes(object, name);
    298       }
    299 
    300       if (!maybe.IsJust()) return Handle<Object>();
    301       DCHECK(!isolate->has_pending_exception());
    302       *attributes = maybe.FromJust();
    303 
    304       if (maybe.FromJust() != ABSENT) {
    305         if (FLAG_trace_contexts) {
    306           PrintF("=> found property in context object %p\n",
    307                  reinterpret_cast<void*>(*object));
    308         }
    309         return object;
    310       }
    311     }
    312 
    313     // 2. Check the context proper if it has slots.
    314     if (context->IsFunctionContext() || context->IsBlockContext() ||
    315         context->IsScriptContext()) {
    316       // Use serialized scope information of functions and blocks to search
    317       // for the context index.
    318       Handle<ScopeInfo> scope_info(context->IsFunctionContext()
    319           ? context->closure()->shared()->scope_info()
    320           : context->scope_info());
    321       VariableMode mode;
    322       InitializationFlag init_flag;
    323       // TODO(sigurds) Figure out whether maybe_assigned_flag should
    324       // be used to compute binding_flags.
    325       MaybeAssignedFlag maybe_assigned_flag;
    326       int slot_index = ScopeInfo::ContextSlotIndex(
    327           scope_info, name, &mode, &init_flag, &maybe_assigned_flag);
    328       DCHECK(slot_index < 0 || slot_index >= MIN_CONTEXT_SLOTS);
    329       if (slot_index >= 0) {
    330         if (FLAG_trace_contexts) {
    331           PrintF("=> found local in context slot %d (mode = %d)\n",
    332                  slot_index, mode);
    333         }
    334         *index = slot_index;
    335         GetAttributesAndBindingFlags(mode, init_flag, attributes,
    336                                      binding_flags);
    337         return context;
    338       }
    339 
    340       // Check the slot corresponding to the intermediate context holding
    341       // only the function name variable.
    342       if (follow_context_chain && context->IsFunctionContext()) {
    343         VariableMode mode;
    344         int function_index = scope_info->FunctionContextSlotIndex(*name, &mode);
    345         if (function_index >= 0) {
    346           if (FLAG_trace_contexts) {
    347             PrintF("=> found intermediate function in context slot %d\n",
    348                    function_index);
    349           }
    350           *index = function_index;
    351           *attributes = READ_ONLY;
    352           DCHECK(mode == CONST_LEGACY || mode == CONST);
    353           *binding_flags = (mode == CONST_LEGACY)
    354               ? IMMUTABLE_IS_INITIALIZED : IMMUTABLE_IS_INITIALIZED_HARMONY;
    355           return context;
    356         }
    357       }
    358 
    359     } else if (context->IsCatchContext()) {
    360       // Catch contexts have the variable name in the extension slot.
    361       if (String::Equals(name, handle(context->catch_name()))) {
    362         if (FLAG_trace_contexts) {
    363           PrintF("=> found in catch context\n");
    364         }
    365         *index = Context::THROWN_OBJECT_INDEX;
    366         *attributes = NONE;
    367         *binding_flags = MUTABLE_IS_INITIALIZED;
    368         return context;
    369       }
    370     }
    371 
    372     // 3. Prepare to continue with the previous (next outermost) context.
    373     if (context->IsNativeContext() ||
    374         ((flags & STOP_AT_DECLARATION_SCOPE) != 0 &&
    375          context->is_declaration_context())) {
    376       follow_context_chain = false;
    377     } else {
    378       context = Handle<Context>(context->previous(), isolate);
    379     }
    380   } while (follow_context_chain);
    381 
    382   if (FLAG_trace_contexts) {
    383     PrintF("=> no property/slot found\n");
    384   }
    385   return Handle<Object>::null();
    386 }
    387 
    388 
    389 void Context::InitializeGlobalSlots() {
    390   DCHECK(IsScriptContext());
    391   DisallowHeapAllocation no_gc;
    392 
    393   ScopeInfo* scope_info = this->scope_info();
    394 
    395   int context_globals = scope_info->ContextGlobalCount();
    396   if (context_globals > 0) {
    397     PropertyCell* empty_cell = GetHeap()->empty_property_cell();
    398 
    399     int context_locals = scope_info->ContextLocalCount();
    400     int index = Context::MIN_CONTEXT_SLOTS + context_locals;
    401     for (int i = 0; i < context_globals; i++) {
    402       set(index++, empty_cell);
    403     }
    404   }
    405 }
    406 
    407 
    408 void Context::AddOptimizedFunction(JSFunction* function) {
    409   DCHECK(IsNativeContext());
    410 #ifdef ENABLE_SLOW_DCHECKS
    411   if (FLAG_enable_slow_asserts) {
    412     Object* element = get(OPTIMIZED_FUNCTIONS_LIST);
    413     while (!element->IsUndefined()) {
    414       CHECK(element != function);
    415       element = JSFunction::cast(element)->next_function_link();
    416     }
    417   }
    418 
    419   // Check that the context belongs to the weak native contexts list.
    420   bool found = false;
    421   Object* context = GetHeap()->native_contexts_list();
    422   while (!context->IsUndefined()) {
    423     if (context == this) {
    424       found = true;
    425       break;
    426     }
    427     context = Context::cast(context)->get(Context::NEXT_CONTEXT_LINK);
    428   }
    429   CHECK(found);
    430 #endif
    431 
    432   // If the function link field is already used then the function was
    433   // enqueued as a code flushing candidate and we remove it now.
    434   if (!function->next_function_link()->IsUndefined()) {
    435     CodeFlusher* flusher = GetHeap()->mark_compact_collector()->code_flusher();
    436     flusher->EvictCandidate(function);
    437   }
    438 
    439   DCHECK(function->next_function_link()->IsUndefined());
    440 
    441   function->set_next_function_link(get(OPTIMIZED_FUNCTIONS_LIST),
    442                                    UPDATE_WEAK_WRITE_BARRIER);
    443   set(OPTIMIZED_FUNCTIONS_LIST, function, UPDATE_WEAK_WRITE_BARRIER);
    444 }
    445 
    446 
    447 void Context::RemoveOptimizedFunction(JSFunction* function) {
    448   DCHECK(IsNativeContext());
    449   Object* element = get(OPTIMIZED_FUNCTIONS_LIST);
    450   JSFunction* prev = NULL;
    451   while (!element->IsUndefined()) {
    452     JSFunction* element_function = JSFunction::cast(element);
    453     DCHECK(element_function->next_function_link()->IsUndefined() ||
    454            element_function->next_function_link()->IsJSFunction());
    455     if (element_function == function) {
    456       if (prev == NULL) {
    457         set(OPTIMIZED_FUNCTIONS_LIST, element_function->next_function_link(),
    458             UPDATE_WEAK_WRITE_BARRIER);
    459       } else {
    460         prev->set_next_function_link(element_function->next_function_link(),
    461                                      UPDATE_WEAK_WRITE_BARRIER);
    462       }
    463       element_function->set_next_function_link(GetHeap()->undefined_value(),
    464                                                UPDATE_WEAK_WRITE_BARRIER);
    465       return;
    466     }
    467     prev = element_function;
    468     element = element_function->next_function_link();
    469   }
    470   UNREACHABLE();
    471 }
    472 
    473 
    474 void Context::SetOptimizedFunctionsListHead(Object* head) {
    475   DCHECK(IsNativeContext());
    476   set(OPTIMIZED_FUNCTIONS_LIST, head, UPDATE_WEAK_WRITE_BARRIER);
    477 }
    478 
    479 
    480 Object* Context::OptimizedFunctionsListHead() {
    481   DCHECK(IsNativeContext());
    482   return get(OPTIMIZED_FUNCTIONS_LIST);
    483 }
    484 
    485 
    486 void Context::AddOptimizedCode(Code* code) {
    487   DCHECK(IsNativeContext());
    488   DCHECK(code->kind() == Code::OPTIMIZED_FUNCTION);
    489   DCHECK(code->next_code_link()->IsUndefined());
    490   code->set_next_code_link(get(OPTIMIZED_CODE_LIST));
    491   set(OPTIMIZED_CODE_LIST, code, UPDATE_WEAK_WRITE_BARRIER);
    492 }
    493 
    494 
    495 void Context::SetOptimizedCodeListHead(Object* head) {
    496   DCHECK(IsNativeContext());
    497   set(OPTIMIZED_CODE_LIST, head, UPDATE_WEAK_WRITE_BARRIER);
    498 }
    499 
    500 
    501 Object* Context::OptimizedCodeListHead() {
    502   DCHECK(IsNativeContext());
    503   return get(OPTIMIZED_CODE_LIST);
    504 }
    505 
    506 
    507 void Context::SetDeoptimizedCodeListHead(Object* head) {
    508   DCHECK(IsNativeContext());
    509   set(DEOPTIMIZED_CODE_LIST, head, UPDATE_WEAK_WRITE_BARRIER);
    510 }
    511 
    512 
    513 Object* Context::DeoptimizedCodeListHead() {
    514   DCHECK(IsNativeContext());
    515   return get(DEOPTIMIZED_CODE_LIST);
    516 }
    517 
    518 
    519 Handle<Object> Context::ErrorMessageForCodeGenerationFromStrings() {
    520   Isolate* isolate = GetIsolate();
    521   Handle<Object> result(error_message_for_code_gen_from_strings(), isolate);
    522   if (!result->IsUndefined()) return result;
    523   return isolate->factory()->NewStringFromStaticChars(
    524       "Code generation from strings disallowed for this context");
    525 }
    526 
    527 
    528 #define COMPARE_NAME(index, type, name) \
    529   if (string->IsOneByteEqualTo(STATIC_CHAR_VECTOR(#name))) return index;
    530 
    531 int Context::ImportedFieldIndexForName(Handle<String> string) {
    532   NATIVE_CONTEXT_IMPORTED_FIELDS(COMPARE_NAME)
    533   return kNotFound;
    534 }
    535 
    536 
    537 int Context::IntrinsicIndexForName(Handle<String> string) {
    538   NATIVE_CONTEXT_INTRINSIC_FUNCTIONS(COMPARE_NAME);
    539   return kNotFound;
    540 }
    541 
    542 #undef COMPARE_NAME
    543 
    544 
    545 bool Context::IsJSBuiltin(Handle<Context> native_context,
    546                           Handle<JSFunction> function) {
    547 #define COMPARE_FUNCTION(index, type, name) \
    548   if (*function == native_context->get(index)) return true;
    549   NATIVE_CONTEXT_JS_BUILTINS(COMPARE_FUNCTION);
    550 #undef COMPARE_FUNCTION
    551   return false;
    552 }
    553 
    554 
    555 #ifdef DEBUG
    556 
    557 bool Context::IsBootstrappingOrNativeContext(Isolate* isolate, Object* object) {
    558   // During bootstrapping we allow all objects to pass as global
    559   // objects. This is necessary to fix circular dependencies.
    560   return isolate->heap()->gc_state() != Heap::NOT_IN_GC ||
    561          isolate->bootstrapper()->IsActive() || object->IsNativeContext();
    562 }
    563 
    564 
    565 bool Context::IsBootstrappingOrValidParentContext(
    566     Object* object, Context* child) {
    567   // During bootstrapping we allow all objects to pass as
    568   // contexts. This is necessary to fix circular dependencies.
    569   if (child->GetIsolate()->bootstrapper()->IsActive()) return true;
    570   if (!object->IsContext()) return false;
    571   Context* context = Context::cast(object);
    572   return context->IsNativeContext() || context->IsScriptContext() ||
    573          context->IsModuleContext() || !child->IsModuleContext();
    574 }
    575 
    576 #endif
    577 
    578 
    579 void Context::IncrementErrorsThrown() {
    580   DCHECK(IsNativeContext());
    581 
    582   int previous_value = errors_thrown()->value();
    583   set_errors_thrown(Smi::FromInt(previous_value + 1));
    584 }
    585 
    586 
    587 int Context::GetErrorsThrown() { return errors_thrown()->value(); }
    588 
    589 }  // namespace internal
    590 }  // namespace v8
    591