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