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 ASSERT(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 ASSERT(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 ASSERT(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 Handle<Object> Context::Lookup(Handle<String> name, 75 ContextLookupFlags flags, 76 int* index, 77 PropertyAttributes* attributes, 78 BindingFlags* binding_flags) { 79 Isolate* isolate = GetIsolate(); 80 Handle<Context> context(this, isolate); 81 82 bool follow_context_chain = (flags & FOLLOW_CONTEXT_CHAIN) != 0; 83 *index = -1; 84 *attributes = ABSENT; 85 *binding_flags = MISSING_BINDING; 86 87 if (FLAG_trace_contexts) { 88 PrintF("Context::Lookup("); 89 name->ShortPrint(); 90 PrintF(")\n"); 91 } 92 93 do { 94 if (FLAG_trace_contexts) { 95 PrintF(" - looking in context %p", reinterpret_cast<void*>(*context)); 96 if (context->IsNativeContext()) PrintF(" (native context)"); 97 PrintF("\n"); 98 } 99 100 // 1. Check global objects, subjects of with, and extension objects. 101 if (context->IsNativeContext() || 102 context->IsWithContext() || 103 (context->IsFunctionContext() && context->has_extension())) { 104 Handle<JSReceiver> object( 105 JSReceiver::cast(context->extension()), isolate); 106 // Context extension objects needs to behave as if they have no 107 // prototype. So even if we want to follow prototype chains, we need 108 // to only do a local lookup for context extension objects. 109 if ((flags & FOLLOW_PROTOTYPE_CHAIN) == 0 || 110 object->IsJSContextExtensionObject()) { 111 *attributes = JSReceiver::GetOwnPropertyAttributes(object, name); 112 } else { 113 *attributes = JSReceiver::GetPropertyAttributes(object, name); 114 } 115 if (isolate->has_pending_exception()) return Handle<Object>(); 116 117 if (*attributes != ABSENT) { 118 if (FLAG_trace_contexts) { 119 PrintF("=> found property in context object %p\n", 120 reinterpret_cast<void*>(*object)); 121 } 122 return object; 123 } 124 } 125 126 // 2. Check the context proper if it has slots. 127 if (context->IsFunctionContext() || context->IsBlockContext()) { 128 // Use serialized scope information of functions and blocks to search 129 // for the context index. 130 Handle<ScopeInfo> scope_info; 131 if (context->IsFunctionContext()) { 132 scope_info = Handle<ScopeInfo>( 133 context->closure()->shared()->scope_info(), isolate); 134 } else { 135 scope_info = Handle<ScopeInfo>( 136 ScopeInfo::cast(context->extension()), isolate); 137 } 138 VariableMode mode; 139 InitializationFlag init_flag; 140 int slot_index = 141 ScopeInfo::ContextSlotIndex(scope_info, name, &mode, &init_flag); 142 ASSERT(slot_index < 0 || slot_index >= MIN_CONTEXT_SLOTS); 143 if (slot_index >= 0) { 144 if (FLAG_trace_contexts) { 145 PrintF("=> found local in context slot %d (mode = %d)\n", 146 slot_index, mode); 147 } 148 *index = slot_index; 149 // Note: Fixed context slots are statically allocated by the compiler. 150 // Statically allocated variables always have a statically known mode, 151 // which is the mode with which they were declared when added to the 152 // scope. Thus, the DYNAMIC mode (which corresponds to dynamically 153 // declared variables that were introduced through declaration nodes) 154 // must not appear here. 155 switch (mode) { 156 case INTERNAL: // Fall through. 157 case VAR: 158 *attributes = NONE; 159 *binding_flags = MUTABLE_IS_INITIALIZED; 160 break; 161 case LET: 162 *attributes = NONE; 163 *binding_flags = (init_flag == kNeedsInitialization) 164 ? MUTABLE_CHECK_INITIALIZED : MUTABLE_IS_INITIALIZED; 165 break; 166 case CONST_LEGACY: 167 *attributes = READ_ONLY; 168 *binding_flags = (init_flag == kNeedsInitialization) 169 ? IMMUTABLE_CHECK_INITIALIZED : IMMUTABLE_IS_INITIALIZED; 170 break; 171 case CONST: 172 *attributes = READ_ONLY; 173 *binding_flags = (init_flag == kNeedsInitialization) 174 ? IMMUTABLE_CHECK_INITIALIZED_HARMONY : 175 IMMUTABLE_IS_INITIALIZED_HARMONY; 176 break; 177 case MODULE: 178 *attributes = READ_ONLY; 179 *binding_flags = IMMUTABLE_IS_INITIALIZED_HARMONY; 180 break; 181 case DYNAMIC: 182 case DYNAMIC_GLOBAL: 183 case DYNAMIC_LOCAL: 184 case TEMPORARY: 185 UNREACHABLE(); 186 break; 187 } 188 return context; 189 } 190 191 // Check the slot corresponding to the intermediate context holding 192 // only the function name variable. 193 if (follow_context_chain && context->IsFunctionContext()) { 194 VariableMode mode; 195 int function_index = scope_info->FunctionContextSlotIndex(*name, &mode); 196 if (function_index >= 0) { 197 if (FLAG_trace_contexts) { 198 PrintF("=> found intermediate function in context slot %d\n", 199 function_index); 200 } 201 *index = function_index; 202 *attributes = READ_ONLY; 203 ASSERT(mode == CONST_LEGACY || mode == CONST); 204 *binding_flags = (mode == CONST_LEGACY) 205 ? IMMUTABLE_IS_INITIALIZED : IMMUTABLE_IS_INITIALIZED_HARMONY; 206 return context; 207 } 208 } 209 210 } else if (context->IsCatchContext()) { 211 // Catch contexts have the variable name in the extension slot. 212 if (String::Equals(name, handle(String::cast(context->extension())))) { 213 if (FLAG_trace_contexts) { 214 PrintF("=> found in catch context\n"); 215 } 216 *index = Context::THROWN_OBJECT_INDEX; 217 *attributes = NONE; 218 *binding_flags = MUTABLE_IS_INITIALIZED; 219 return context; 220 } 221 } 222 223 // 3. Prepare to continue with the previous (next outermost) context. 224 if (context->IsNativeContext()) { 225 follow_context_chain = false; 226 } else { 227 context = Handle<Context>(context->previous(), isolate); 228 } 229 } while (follow_context_chain); 230 231 if (FLAG_trace_contexts) { 232 PrintF("=> no property/slot found\n"); 233 } 234 return Handle<Object>::null(); 235 } 236 237 238 void Context::AddOptimizedFunction(JSFunction* function) { 239 ASSERT(IsNativeContext()); 240 #ifdef ENABLE_SLOW_ASSERTS 241 if (FLAG_enable_slow_asserts) { 242 Object* element = get(OPTIMIZED_FUNCTIONS_LIST); 243 while (!element->IsUndefined()) { 244 CHECK(element != function); 245 element = JSFunction::cast(element)->next_function_link(); 246 } 247 } 248 249 // Check that the context belongs to the weak native contexts list. 250 bool found = false; 251 Object* context = GetHeap()->native_contexts_list(); 252 while (!context->IsUndefined()) { 253 if (context == this) { 254 found = true; 255 break; 256 } 257 context = Context::cast(context)->get(Context::NEXT_CONTEXT_LINK); 258 } 259 CHECK(found); 260 #endif 261 262 // If the function link field is already used then the function was 263 // enqueued as a code flushing candidate and we remove it now. 264 if (!function->next_function_link()->IsUndefined()) { 265 CodeFlusher* flusher = GetHeap()->mark_compact_collector()->code_flusher(); 266 flusher->EvictCandidate(function); 267 } 268 269 ASSERT(function->next_function_link()->IsUndefined()); 270 271 function->set_next_function_link(get(OPTIMIZED_FUNCTIONS_LIST)); 272 set(OPTIMIZED_FUNCTIONS_LIST, function); 273 } 274 275 276 void Context::RemoveOptimizedFunction(JSFunction* function) { 277 ASSERT(IsNativeContext()); 278 Object* element = get(OPTIMIZED_FUNCTIONS_LIST); 279 JSFunction* prev = NULL; 280 while (!element->IsUndefined()) { 281 JSFunction* element_function = JSFunction::cast(element); 282 ASSERT(element_function->next_function_link()->IsUndefined() || 283 element_function->next_function_link()->IsJSFunction()); 284 if (element_function == function) { 285 if (prev == NULL) { 286 set(OPTIMIZED_FUNCTIONS_LIST, element_function->next_function_link()); 287 } else { 288 prev->set_next_function_link(element_function->next_function_link()); 289 } 290 element_function->set_next_function_link(GetHeap()->undefined_value()); 291 return; 292 } 293 prev = element_function; 294 element = element_function->next_function_link(); 295 } 296 UNREACHABLE(); 297 } 298 299 300 void Context::SetOptimizedFunctionsListHead(Object* head) { 301 ASSERT(IsNativeContext()); 302 set(OPTIMIZED_FUNCTIONS_LIST, head); 303 } 304 305 306 Object* Context::OptimizedFunctionsListHead() { 307 ASSERT(IsNativeContext()); 308 return get(OPTIMIZED_FUNCTIONS_LIST); 309 } 310 311 312 void Context::AddOptimizedCode(Code* code) { 313 ASSERT(IsNativeContext()); 314 ASSERT(code->kind() == Code::OPTIMIZED_FUNCTION); 315 ASSERT(code->next_code_link()->IsUndefined()); 316 code->set_next_code_link(get(OPTIMIZED_CODE_LIST)); 317 set(OPTIMIZED_CODE_LIST, code); 318 } 319 320 321 void Context::SetOptimizedCodeListHead(Object* head) { 322 ASSERT(IsNativeContext()); 323 set(OPTIMIZED_CODE_LIST, head); 324 } 325 326 327 Object* Context::OptimizedCodeListHead() { 328 ASSERT(IsNativeContext()); 329 return get(OPTIMIZED_CODE_LIST); 330 } 331 332 333 void Context::SetDeoptimizedCodeListHead(Object* head) { 334 ASSERT(IsNativeContext()); 335 set(DEOPTIMIZED_CODE_LIST, head); 336 } 337 338 339 Object* Context::DeoptimizedCodeListHead() { 340 ASSERT(IsNativeContext()); 341 return get(DEOPTIMIZED_CODE_LIST); 342 } 343 344 345 Handle<Object> Context::ErrorMessageForCodeGenerationFromStrings() { 346 Isolate* isolate = GetIsolate(); 347 Handle<Object> result(error_message_for_code_gen_from_strings(), isolate); 348 if (!result->IsUndefined()) return result; 349 return isolate->factory()->NewStringFromStaticAscii( 350 "Code generation from strings disallowed for this context"); 351 } 352 353 354 #ifdef DEBUG 355 bool Context::IsBootstrappingOrValidParentContext( 356 Object* object, Context* child) { 357 // During bootstrapping we allow all objects to pass as 358 // contexts. This is necessary to fix circular dependencies. 359 if (child->GetIsolate()->bootstrapper()->IsActive()) return true; 360 if (!object->IsContext()) return false; 361 Context* context = Context::cast(object); 362 return context->IsNativeContext() || context->IsGlobalContext() || 363 context->IsModuleContext() || !child->IsModuleContext(); 364 } 365 366 367 bool Context::IsBootstrappingOrGlobalObject(Isolate* isolate, Object* object) { 368 // During bootstrapping we allow all objects to pass as global 369 // objects. This is necessary to fix circular dependencies. 370 return isolate->heap()->gc_state() != Heap::NOT_IN_GC || 371 isolate->bootstrapper()->IsActive() || 372 object->IsGlobalObject(); 373 } 374 #endif 375 376 } } // namespace v8::internal 377