1 // Copyright 2011 the V8 project authors. All rights reserved. 2 // Redistribution and use in source and binary forms, with or without 3 // modification, are permitted provided that the following conditions are 4 // met: 5 // 6 // * Redistributions of source code must retain the above copyright 7 // notice, this list of conditions and the following disclaimer. 8 // * Redistributions in binary form must reproduce the above 9 // copyright notice, this list of conditions and the following 10 // disclaimer in the documentation and/or other materials provided 11 // with the distribution. 12 // * Neither the name of Google Inc. nor the names of its 13 // contributors may be used to endorse or promote products derived 14 // from this software without specific prior written permission. 15 // 16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 28 #include "v8.h" 29 30 #include "bootstrapper.h" 31 #include "debug.h" 32 #include "scopeinfo.h" 33 34 namespace v8 { 35 namespace internal { 36 37 Context* Context::declaration_context() { 38 Context* current = this; 39 while (!current->IsFunctionContext() && !current->IsNativeContext()) { 40 current = current->previous(); 41 ASSERT(current->closure() == closure()); 42 } 43 return current; 44 } 45 46 47 JSBuiltinsObject* Context::builtins() { 48 GlobalObject* object = global_object(); 49 if (object->IsJSGlobalObject()) { 50 return JSGlobalObject::cast(object)->builtins(); 51 } else { 52 ASSERT(object->IsJSBuiltinsObject()); 53 return JSBuiltinsObject::cast(object); 54 } 55 } 56 57 58 Context* Context::global_context() { 59 Context* current = this; 60 while (!current->IsGlobalContext()) { 61 current = current->previous(); 62 } 63 return current; 64 } 65 66 67 Context* Context::native_context() { 68 // Fast case: the global object for this context has been set. In 69 // that case, the global object has a direct pointer to the global 70 // context. 71 if (global_object()->IsGlobalObject()) { 72 return global_object()->native_context(); 73 } 74 75 // During bootstrapping, the global object might not be set and we 76 // have to search the context chain to find the native context. 77 ASSERT(this->GetIsolate()->bootstrapper()->IsActive()); 78 Context* current = this; 79 while (!current->IsNativeContext()) { 80 JSFunction* closure = JSFunction::cast(current->closure()); 81 current = Context::cast(closure->context()); 82 } 83 return current; 84 } 85 86 87 JSObject* Context::global_proxy() { 88 return native_context()->global_proxy_object(); 89 } 90 91 92 void Context::set_global_proxy(JSObject* object) { 93 native_context()->set_global_proxy_object(object); 94 } 95 96 97 Handle<Object> Context::Lookup(Handle<String> name, 98 ContextLookupFlags flags, 99 int* index, 100 PropertyAttributes* attributes, 101 BindingFlags* binding_flags) { 102 Isolate* isolate = GetIsolate(); 103 Handle<Context> context(this, isolate); 104 105 bool follow_context_chain = (flags & FOLLOW_CONTEXT_CHAIN) != 0; 106 *index = -1; 107 *attributes = ABSENT; 108 *binding_flags = MISSING_BINDING; 109 110 if (FLAG_trace_contexts) { 111 PrintF("Context::Lookup("); 112 name->ShortPrint(); 113 PrintF(")\n"); 114 } 115 116 do { 117 if (FLAG_trace_contexts) { 118 PrintF(" - looking in context %p", reinterpret_cast<void*>(*context)); 119 if (context->IsNativeContext()) PrintF(" (native context)"); 120 PrintF("\n"); 121 } 122 123 // 1. Check global objects, subjects of with, and extension objects. 124 if (context->IsNativeContext() || 125 context->IsWithContext() || 126 (context->IsFunctionContext() && context->has_extension())) { 127 Handle<JSReceiver> object( 128 JSReceiver::cast(context->extension()), isolate); 129 // Context extension objects needs to behave as if they have no 130 // prototype. So even if we want to follow prototype chains, we need 131 // to only do a local lookup for context extension objects. 132 if ((flags & FOLLOW_PROTOTYPE_CHAIN) == 0 || 133 object->IsJSContextExtensionObject()) { 134 *attributes = object->GetLocalPropertyAttribute(*name); 135 } else { 136 *attributes = object->GetPropertyAttribute(*name); 137 } 138 if (isolate->has_pending_exception()) return Handle<Object>(); 139 140 if (*attributes != ABSENT) { 141 if (FLAG_trace_contexts) { 142 PrintF("=> found property in context object %p\n", 143 reinterpret_cast<void*>(*object)); 144 } 145 return object; 146 } 147 } 148 149 // 2. Check the context proper if it has slots. 150 if (context->IsFunctionContext() || context->IsBlockContext()) { 151 // Use serialized scope information of functions and blocks to search 152 // for the context index. 153 Handle<ScopeInfo> scope_info; 154 if (context->IsFunctionContext()) { 155 scope_info = Handle<ScopeInfo>( 156 context->closure()->shared()->scope_info(), isolate); 157 } else { 158 scope_info = Handle<ScopeInfo>( 159 ScopeInfo::cast(context->extension()), isolate); 160 } 161 VariableMode mode; 162 InitializationFlag init_flag; 163 int slot_index = scope_info->ContextSlotIndex(*name, &mode, &init_flag); 164 ASSERT(slot_index < 0 || slot_index >= MIN_CONTEXT_SLOTS); 165 if (slot_index >= 0) { 166 if (FLAG_trace_contexts) { 167 PrintF("=> found local in context slot %d (mode = %d)\n", 168 slot_index, mode); 169 } 170 *index = slot_index; 171 // Note: Fixed context slots are statically allocated by the compiler. 172 // Statically allocated variables always have a statically known mode, 173 // which is the mode with which they were declared when added to the 174 // scope. Thus, the DYNAMIC mode (which corresponds to dynamically 175 // declared variables that were introduced through declaration nodes) 176 // must not appear here. 177 switch (mode) { 178 case INTERNAL: // Fall through. 179 case VAR: 180 *attributes = NONE; 181 *binding_flags = MUTABLE_IS_INITIALIZED; 182 break; 183 case LET: 184 *attributes = NONE; 185 *binding_flags = (init_flag == kNeedsInitialization) 186 ? MUTABLE_CHECK_INITIALIZED : MUTABLE_IS_INITIALIZED; 187 break; 188 case CONST: 189 *attributes = READ_ONLY; 190 *binding_flags = (init_flag == kNeedsInitialization) 191 ? IMMUTABLE_CHECK_INITIALIZED : IMMUTABLE_IS_INITIALIZED; 192 break; 193 case CONST_HARMONY: 194 *attributes = READ_ONLY; 195 *binding_flags = (init_flag == kNeedsInitialization) 196 ? IMMUTABLE_CHECK_INITIALIZED_HARMONY : 197 IMMUTABLE_IS_INITIALIZED_HARMONY; 198 break; 199 case MODULE: 200 *attributes = READ_ONLY; 201 *binding_flags = IMMUTABLE_IS_INITIALIZED_HARMONY; 202 break; 203 case DYNAMIC: 204 case DYNAMIC_GLOBAL: 205 case DYNAMIC_LOCAL: 206 case TEMPORARY: 207 UNREACHABLE(); 208 break; 209 } 210 return context; 211 } 212 213 // Check the slot corresponding to the intermediate context holding 214 // only the function name variable. 215 if (follow_context_chain && context->IsFunctionContext()) { 216 VariableMode mode; 217 int function_index = scope_info->FunctionContextSlotIndex(*name, &mode); 218 if (function_index >= 0) { 219 if (FLAG_trace_contexts) { 220 PrintF("=> found intermediate function in context slot %d\n", 221 function_index); 222 } 223 *index = function_index; 224 *attributes = READ_ONLY; 225 ASSERT(mode == CONST || mode == CONST_HARMONY); 226 *binding_flags = (mode == CONST) 227 ? IMMUTABLE_IS_INITIALIZED : IMMUTABLE_IS_INITIALIZED_HARMONY; 228 return context; 229 } 230 } 231 232 } else if (context->IsCatchContext()) { 233 // Catch contexts have the variable name in the extension slot. 234 if (name->Equals(String::cast(context->extension()))) { 235 if (FLAG_trace_contexts) { 236 PrintF("=> found in catch context\n"); 237 } 238 *index = Context::THROWN_OBJECT_INDEX; 239 *attributes = NONE; 240 *binding_flags = MUTABLE_IS_INITIALIZED; 241 return context; 242 } 243 } 244 245 // 3. Prepare to continue with the previous (next outermost) context. 246 if (context->IsNativeContext()) { 247 follow_context_chain = false; 248 } else { 249 context = Handle<Context>(context->previous(), isolate); 250 } 251 } while (follow_context_chain); 252 253 if (FLAG_trace_contexts) { 254 PrintF("=> no property/slot found\n"); 255 } 256 return Handle<Object>::null(); 257 } 258 259 260 void Context::AddOptimizedFunction(JSFunction* function) { 261 ASSERT(IsNativeContext()); 262 #ifdef ENABLE_SLOW_ASSERTS 263 if (FLAG_enable_slow_asserts) { 264 Object* element = get(OPTIMIZED_FUNCTIONS_LIST); 265 while (!element->IsUndefined()) { 266 CHECK(element != function); 267 element = JSFunction::cast(element)->next_function_link(); 268 } 269 } 270 271 // Check that the context belongs to the weak native contexts list. 272 bool found = false; 273 Object* context = GetHeap()->native_contexts_list(); 274 while (!context->IsUndefined()) { 275 if (context == this) { 276 found = true; 277 break; 278 } 279 context = Context::cast(context)->get(Context::NEXT_CONTEXT_LINK); 280 } 281 CHECK(found); 282 #endif 283 284 // If the function link field is already used then the function was 285 // enqueued as a code flushing candidate and we remove it now. 286 if (!function->next_function_link()->IsUndefined()) { 287 CodeFlusher* flusher = GetHeap()->mark_compact_collector()->code_flusher(); 288 flusher->EvictCandidate(function); 289 } 290 291 ASSERT(function->next_function_link()->IsUndefined()); 292 293 function->set_next_function_link(get(OPTIMIZED_FUNCTIONS_LIST)); 294 set(OPTIMIZED_FUNCTIONS_LIST, function); 295 } 296 297 298 void Context::RemoveOptimizedFunction(JSFunction* function) { 299 ASSERT(IsNativeContext()); 300 Object* element = get(OPTIMIZED_FUNCTIONS_LIST); 301 JSFunction* prev = NULL; 302 while (!element->IsUndefined()) { 303 JSFunction* element_function = JSFunction::cast(element); 304 ASSERT(element_function->next_function_link()->IsUndefined() || 305 element_function->next_function_link()->IsJSFunction()); 306 if (element_function == function) { 307 if (prev == NULL) { 308 set(OPTIMIZED_FUNCTIONS_LIST, element_function->next_function_link()); 309 } else { 310 prev->set_next_function_link(element_function->next_function_link()); 311 } 312 element_function->set_next_function_link(GetHeap()->undefined_value()); 313 return; 314 } 315 prev = element_function; 316 element = element_function->next_function_link(); 317 } 318 UNREACHABLE(); 319 } 320 321 322 void Context::SetOptimizedFunctionsListHead(Object* head) { 323 ASSERT(IsNativeContext()); 324 set(OPTIMIZED_FUNCTIONS_LIST, head); 325 } 326 327 328 Object* Context::OptimizedFunctionsListHead() { 329 ASSERT(IsNativeContext()); 330 return get(OPTIMIZED_FUNCTIONS_LIST); 331 } 332 333 334 void Context::AddOptimizedCode(Code* code) { 335 ASSERT(IsNativeContext()); 336 ASSERT(code->kind() == Code::OPTIMIZED_FUNCTION); 337 ASSERT(code->next_code_link()->IsUndefined()); 338 code->set_next_code_link(get(OPTIMIZED_CODE_LIST)); 339 set(OPTIMIZED_CODE_LIST, code); 340 } 341 342 343 void Context::SetOptimizedCodeListHead(Object* head) { 344 ASSERT(IsNativeContext()); 345 set(OPTIMIZED_CODE_LIST, head); 346 } 347 348 349 Object* Context::OptimizedCodeListHead() { 350 ASSERT(IsNativeContext()); 351 return get(OPTIMIZED_CODE_LIST); 352 } 353 354 355 void Context::SetDeoptimizedCodeListHead(Object* head) { 356 ASSERT(IsNativeContext()); 357 set(DEOPTIMIZED_CODE_LIST, head); 358 } 359 360 361 Object* Context::DeoptimizedCodeListHead() { 362 ASSERT(IsNativeContext()); 363 return get(DEOPTIMIZED_CODE_LIST); 364 } 365 366 367 Handle<Object> Context::ErrorMessageForCodeGenerationFromStrings() { 368 Handle<Object> result(error_message_for_code_gen_from_strings(), 369 GetIsolate()); 370 if (!result->IsUndefined()) return result; 371 return GetIsolate()->factory()->NewStringFromAscii(i::CStrVector( 372 "Code generation from strings disallowed for this context")); 373 } 374 375 376 #ifdef DEBUG 377 bool Context::IsBootstrappingOrValidParentContext( 378 Object* object, Context* child) { 379 // During bootstrapping we allow all objects to pass as 380 // contexts. This is necessary to fix circular dependencies. 381 if (child->GetIsolate()->bootstrapper()->IsActive()) return true; 382 if (!object->IsContext()) return false; 383 Context* context = Context::cast(object); 384 return context->IsNativeContext() || context->IsGlobalContext() || 385 context->IsModuleContext() || !child->IsModuleContext(); 386 } 387 388 389 bool Context::IsBootstrappingOrGlobalObject(Isolate* isolate, Object* object) { 390 // During bootstrapping we allow all objects to pass as global 391 // objects. This is necessary to fix circular dependencies. 392 return isolate->heap()->gc_state() != Heap::NOT_IN_GC || 393 isolate->bootstrapper()->IsActive() || 394 object->IsGlobalObject(); 395 } 396 #endif 397 398 } } // namespace v8::internal 399