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/v8.h" 6 7 #include "src/bootstrapper.h" 8 #include "src/debug.h" 9 #include "src/scopeinfo.h" 10 11 namespace v8 { 12 namespace internal { 13 14 Context* Context::declaration_context() { 15 Context* current = this; 16 while (!current->IsFunctionContext() && !current->IsNativeContext()) { 17 current = current->previous(); 18 DCHECK(current->closure() == closure()); 19 } 20 return current; 21 } 22 23 24 JSBuiltinsObject* Context::builtins() { 25 GlobalObject* object = global_object(); 26 if (object->IsJSGlobalObject()) { 27 return JSGlobalObject::cast(object)->builtins(); 28 } else { 29 DCHECK(object->IsJSBuiltinsObject()); 30 return JSBuiltinsObject::cast(object); 31 } 32 } 33 34 35 Context* Context::global_context() { 36 Context* current = this; 37 while (!current->IsGlobalContext()) { 38 current = current->previous(); 39 } 40 return current; 41 } 42 43 44 Context* Context::native_context() { 45 // Fast case: the global object for this context has been set. In 46 // that case, the global object has a direct pointer to the global 47 // context. 48 if (global_object()->IsGlobalObject()) { 49 return global_object()->native_context(); 50 } 51 52 // During bootstrapping, the global object might not be set and we 53 // have to search the context chain to find the native context. 54 DCHECK(this->GetIsolate()->bootstrapper()->IsActive()); 55 Context* current = this; 56 while (!current->IsNativeContext()) { 57 JSFunction* closure = JSFunction::cast(current->closure()); 58 current = Context::cast(closure->context()); 59 } 60 return current; 61 } 62 63 64 JSObject* Context::global_proxy() { 65 return native_context()->global_proxy_object(); 66 } 67 68 69 void Context::set_global_proxy(JSObject* object) { 70 native_context()->set_global_proxy_object(object); 71 } 72 73 74 /** 75 * Lookups a property in an object environment, taking the unscopables into 76 * account. This is used For HasBinding spec algorithms for ObjectEnvironment. 77 */ 78 static Maybe<PropertyAttributes> UnscopableLookup(LookupIterator* it) { 79 Isolate* isolate = it->isolate(); 80 81 Maybe<PropertyAttributes> attrs = JSReceiver::GetPropertyAttributes(it); 82 DCHECK(attrs.has_value || isolate->has_pending_exception()); 83 if (!attrs.has_value || attrs.value == ABSENT) return attrs; 84 85 Handle<Symbol> unscopables_symbol( 86 isolate->native_context()->unscopables_symbol(), isolate); 87 Handle<Object> receiver = it->GetReceiver(); 88 Handle<Object> unscopables; 89 MaybeHandle<Object> maybe_unscopables = 90 Object::GetProperty(receiver, unscopables_symbol); 91 if (!maybe_unscopables.ToHandle(&unscopables)) { 92 return Maybe<PropertyAttributes>(); 93 } 94 if (!unscopables->IsSpecObject()) return attrs; 95 Maybe<bool> blacklist = JSReceiver::HasProperty( 96 Handle<JSReceiver>::cast(unscopables), it->name()); 97 if (!blacklist.has_value) { 98 DCHECK(isolate->has_pending_exception()); 99 return Maybe<PropertyAttributes>(); 100 } 101 if (blacklist.value) return maybe(ABSENT); 102 return attrs; 103 } 104 105 106 Handle<Object> Context::Lookup(Handle<String> name, 107 ContextLookupFlags flags, 108 int* index, 109 PropertyAttributes* attributes, 110 BindingFlags* binding_flags) { 111 Isolate* isolate = GetIsolate(); 112 Handle<Context> context(this, isolate); 113 114 bool follow_context_chain = (flags & FOLLOW_CONTEXT_CHAIN) != 0; 115 *index = -1; 116 *attributes = ABSENT; 117 *binding_flags = MISSING_BINDING; 118 119 if (FLAG_trace_contexts) { 120 PrintF("Context::Lookup("); 121 name->ShortPrint(); 122 PrintF(")\n"); 123 } 124 125 do { 126 if (FLAG_trace_contexts) { 127 PrintF(" - looking in context %p", reinterpret_cast<void*>(*context)); 128 if (context->IsNativeContext()) PrintF(" (native context)"); 129 PrintF("\n"); 130 } 131 132 // 1. Check global objects, subjects of with, and extension objects. 133 if (context->IsNativeContext() || 134 context->IsWithContext() || 135 (context->IsFunctionContext() && context->has_extension())) { 136 Handle<JSReceiver> object( 137 JSReceiver::cast(context->extension()), isolate); 138 // Context extension objects needs to behave as if they have no 139 // prototype. So even if we want to follow prototype chains, we need 140 // to only do a local lookup for context extension objects. 141 Maybe<PropertyAttributes> maybe; 142 if ((flags & FOLLOW_PROTOTYPE_CHAIN) == 0 || 143 object->IsJSContextExtensionObject()) { 144 maybe = JSReceiver::GetOwnPropertyAttributes(object, name); 145 } else if (context->IsWithContext()) { 146 LookupIterator it(object, name); 147 maybe = UnscopableLookup(&it); 148 } else { 149 maybe = JSReceiver::GetPropertyAttributes(object, name); 150 } 151 152 if (!maybe.has_value) return Handle<Object>(); 153 DCHECK(!isolate->has_pending_exception()); 154 *attributes = maybe.value; 155 156 if (maybe.value != ABSENT) { 157 if (FLAG_trace_contexts) { 158 PrintF("=> found property in context object %p\n", 159 reinterpret_cast<void*>(*object)); 160 } 161 return object; 162 } 163 } 164 165 // 2. Check the context proper if it has slots. 166 if (context->IsFunctionContext() || context->IsBlockContext()) { 167 // Use serialized scope information of functions and blocks to search 168 // for the context index. 169 Handle<ScopeInfo> scope_info; 170 if (context->IsFunctionContext()) { 171 scope_info = Handle<ScopeInfo>( 172 context->closure()->shared()->scope_info(), isolate); 173 } else { 174 scope_info = Handle<ScopeInfo>( 175 ScopeInfo::cast(context->extension()), isolate); 176 } 177 VariableMode mode; 178 InitializationFlag init_flag; 179 // TODO(sigurds) Figure out whether maybe_assigned_flag should 180 // be used to compute binding_flags. 181 MaybeAssignedFlag maybe_assigned_flag; 182 int slot_index = ScopeInfo::ContextSlotIndex( 183 scope_info, name, &mode, &init_flag, &maybe_assigned_flag); 184 DCHECK(slot_index < 0 || slot_index >= MIN_CONTEXT_SLOTS); 185 if (slot_index >= 0) { 186 if (FLAG_trace_contexts) { 187 PrintF("=> found local in context slot %d (mode = %d)\n", 188 slot_index, mode); 189 } 190 *index = slot_index; 191 // Note: Fixed context slots are statically allocated by the compiler. 192 // Statically allocated variables always have a statically known mode, 193 // which is the mode with which they were declared when added to the 194 // scope. Thus, the DYNAMIC mode (which corresponds to dynamically 195 // declared variables that were introduced through declaration nodes) 196 // must not appear here. 197 switch (mode) { 198 case INTERNAL: // Fall through. 199 case VAR: 200 *attributes = NONE; 201 *binding_flags = MUTABLE_IS_INITIALIZED; 202 break; 203 case LET: 204 *attributes = NONE; 205 *binding_flags = (init_flag == kNeedsInitialization) 206 ? MUTABLE_CHECK_INITIALIZED : MUTABLE_IS_INITIALIZED; 207 break; 208 case CONST_LEGACY: 209 *attributes = READ_ONLY; 210 *binding_flags = (init_flag == kNeedsInitialization) 211 ? IMMUTABLE_CHECK_INITIALIZED : IMMUTABLE_IS_INITIALIZED; 212 break; 213 case CONST: 214 *attributes = READ_ONLY; 215 *binding_flags = (init_flag == kNeedsInitialization) 216 ? IMMUTABLE_CHECK_INITIALIZED_HARMONY : 217 IMMUTABLE_IS_INITIALIZED_HARMONY; 218 break; 219 case MODULE: 220 *attributes = READ_ONLY; 221 *binding_flags = IMMUTABLE_IS_INITIALIZED_HARMONY; 222 break; 223 case DYNAMIC: 224 case DYNAMIC_GLOBAL: 225 case DYNAMIC_LOCAL: 226 case TEMPORARY: 227 UNREACHABLE(); 228 break; 229 } 230 return context; 231 } 232 233 // Check the slot corresponding to the intermediate context holding 234 // only the function name variable. 235 if (follow_context_chain && context->IsFunctionContext()) { 236 VariableMode mode; 237 int function_index = scope_info->FunctionContextSlotIndex(*name, &mode); 238 if (function_index >= 0) { 239 if (FLAG_trace_contexts) { 240 PrintF("=> found intermediate function in context slot %d\n", 241 function_index); 242 } 243 *index = function_index; 244 *attributes = READ_ONLY; 245 DCHECK(mode == CONST_LEGACY || mode == CONST); 246 *binding_flags = (mode == CONST_LEGACY) 247 ? IMMUTABLE_IS_INITIALIZED : IMMUTABLE_IS_INITIALIZED_HARMONY; 248 return context; 249 } 250 } 251 252 } else if (context->IsCatchContext()) { 253 // Catch contexts have the variable name in the extension slot. 254 if (String::Equals(name, handle(String::cast(context->extension())))) { 255 if (FLAG_trace_contexts) { 256 PrintF("=> found in catch context\n"); 257 } 258 *index = Context::THROWN_OBJECT_INDEX; 259 *attributes = NONE; 260 *binding_flags = MUTABLE_IS_INITIALIZED; 261 return context; 262 } 263 } 264 265 // 3. Prepare to continue with the previous (next outermost) context. 266 if (context->IsNativeContext()) { 267 follow_context_chain = false; 268 } else { 269 context = Handle<Context>(context->previous(), isolate); 270 } 271 } while (follow_context_chain); 272 273 if (FLAG_trace_contexts) { 274 PrintF("=> no property/slot found\n"); 275 } 276 return Handle<Object>::null(); 277 } 278 279 280 void Context::AddOptimizedFunction(JSFunction* function) { 281 DCHECK(IsNativeContext()); 282 #ifdef ENABLE_SLOW_DCHECKS 283 if (FLAG_enable_slow_asserts) { 284 Object* element = get(OPTIMIZED_FUNCTIONS_LIST); 285 while (!element->IsUndefined()) { 286 CHECK(element != function); 287 element = JSFunction::cast(element)->next_function_link(); 288 } 289 } 290 291 // Check that the context belongs to the weak native contexts list. 292 bool found = false; 293 Object* context = GetHeap()->native_contexts_list(); 294 while (!context->IsUndefined()) { 295 if (context == this) { 296 found = true; 297 break; 298 } 299 context = Context::cast(context)->get(Context::NEXT_CONTEXT_LINK); 300 } 301 CHECK(found); 302 #endif 303 304 // If the function link field is already used then the function was 305 // enqueued as a code flushing candidate and we remove it now. 306 if (!function->next_function_link()->IsUndefined()) { 307 CodeFlusher* flusher = GetHeap()->mark_compact_collector()->code_flusher(); 308 flusher->EvictCandidate(function); 309 } 310 311 DCHECK(function->next_function_link()->IsUndefined()); 312 313 function->set_next_function_link(get(OPTIMIZED_FUNCTIONS_LIST)); 314 set(OPTIMIZED_FUNCTIONS_LIST, function); 315 } 316 317 318 void Context::RemoveOptimizedFunction(JSFunction* function) { 319 DCHECK(IsNativeContext()); 320 Object* element = get(OPTIMIZED_FUNCTIONS_LIST); 321 JSFunction* prev = NULL; 322 while (!element->IsUndefined()) { 323 JSFunction* element_function = JSFunction::cast(element); 324 DCHECK(element_function->next_function_link()->IsUndefined() || 325 element_function->next_function_link()->IsJSFunction()); 326 if (element_function == function) { 327 if (prev == NULL) { 328 set(OPTIMIZED_FUNCTIONS_LIST, element_function->next_function_link()); 329 } else { 330 prev->set_next_function_link(element_function->next_function_link()); 331 } 332 element_function->set_next_function_link(GetHeap()->undefined_value()); 333 return; 334 } 335 prev = element_function; 336 element = element_function->next_function_link(); 337 } 338 UNREACHABLE(); 339 } 340 341 342 void Context::SetOptimizedFunctionsListHead(Object* head) { 343 DCHECK(IsNativeContext()); 344 set(OPTIMIZED_FUNCTIONS_LIST, head); 345 } 346 347 348 Object* Context::OptimizedFunctionsListHead() { 349 DCHECK(IsNativeContext()); 350 return get(OPTIMIZED_FUNCTIONS_LIST); 351 } 352 353 354 void Context::AddOptimizedCode(Code* code) { 355 DCHECK(IsNativeContext()); 356 DCHECK(code->kind() == Code::OPTIMIZED_FUNCTION); 357 DCHECK(code->next_code_link()->IsUndefined()); 358 code->set_next_code_link(get(OPTIMIZED_CODE_LIST)); 359 set(OPTIMIZED_CODE_LIST, code); 360 } 361 362 363 void Context::SetOptimizedCodeListHead(Object* head) { 364 DCHECK(IsNativeContext()); 365 set(OPTIMIZED_CODE_LIST, head); 366 } 367 368 369 Object* Context::OptimizedCodeListHead() { 370 DCHECK(IsNativeContext()); 371 return get(OPTIMIZED_CODE_LIST); 372 } 373 374 375 void Context::SetDeoptimizedCodeListHead(Object* head) { 376 DCHECK(IsNativeContext()); 377 set(DEOPTIMIZED_CODE_LIST, head); 378 } 379 380 381 Object* Context::DeoptimizedCodeListHead() { 382 DCHECK(IsNativeContext()); 383 return get(DEOPTIMIZED_CODE_LIST); 384 } 385 386 387 Handle<Object> Context::ErrorMessageForCodeGenerationFromStrings() { 388 Isolate* isolate = GetIsolate(); 389 Handle<Object> result(error_message_for_code_gen_from_strings(), isolate); 390 if (!result->IsUndefined()) return result; 391 return isolate->factory()->NewStringFromStaticChars( 392 "Code generation from strings disallowed for this context"); 393 } 394 395 396 #ifdef DEBUG 397 bool Context::IsBootstrappingOrValidParentContext( 398 Object* object, Context* child) { 399 // During bootstrapping we allow all objects to pass as 400 // contexts. This is necessary to fix circular dependencies. 401 if (child->GetIsolate()->bootstrapper()->IsActive()) return true; 402 if (!object->IsContext()) return false; 403 Context* context = Context::cast(object); 404 return context->IsNativeContext() || context->IsGlobalContext() || 405 context->IsModuleContext() || !child->IsModuleContext(); 406 } 407 408 409 bool Context::IsBootstrappingOrGlobalObject(Isolate* isolate, Object* object) { 410 // During bootstrapping we allow all objects to pass as global 411 // objects. This is necessary to fix circular dependencies. 412 return isolate->heap()->gc_state() != Heap::NOT_IN_GC || 413 isolate->bootstrapper()->IsActive() || 414 object->IsGlobalObject(); 415 } 416 #endif 417 418 } } // namespace v8::internal 419