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