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