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->IsGlobalContext()) { 40 current = current->previous(); 41 ASSERT(current->closure() == closure()); 42 } 43 return current; 44 } 45 46 47 JSBuiltinsObject* Context::builtins() { 48 GlobalObject* object = global(); 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 // Fast case: the global object for this context has been set. In 60 // that case, the global object has a direct pointer to the global 61 // context. 62 if (global()->IsGlobalObject()) { 63 return global()->global_context(); 64 } 65 66 // During bootstrapping, the global object might not be set and we 67 // have to search the context chain to find the global context. 68 ASSERT(Isolate::Current()->bootstrapper()->IsActive()); 69 Context* current = this; 70 while (!current->IsGlobalContext()) { 71 JSFunction* closure = JSFunction::cast(current->closure()); 72 current = Context::cast(closure->context()); 73 } 74 return current; 75 } 76 77 78 JSObject* Context::global_proxy() { 79 return global_context()->global_proxy_object(); 80 } 81 82 void Context::set_global_proxy(JSObject* object) { 83 global_context()->set_global_proxy_object(object); 84 } 85 86 87 Handle<Object> Context::Lookup(Handle<String> name, 88 ContextLookupFlags flags, 89 int* index, 90 PropertyAttributes* attributes, 91 BindingFlags* binding_flags) { 92 Isolate* isolate = GetIsolate(); 93 Handle<Context> context(this, isolate); 94 95 bool follow_context_chain = (flags & FOLLOW_CONTEXT_CHAIN) != 0; 96 *index = -1; 97 *attributes = ABSENT; 98 *binding_flags = MISSING_BINDING; 99 100 if (FLAG_trace_contexts) { 101 PrintF("Context::Lookup("); 102 name->ShortPrint(); 103 PrintF(")\n"); 104 } 105 106 do { 107 if (FLAG_trace_contexts) { 108 PrintF(" - looking in context %p", reinterpret_cast<void*>(*context)); 109 if (context->IsGlobalContext()) PrintF(" (global context)"); 110 PrintF("\n"); 111 } 112 113 // 1. Check global objects, subjects of with, and extension objects. 114 if (context->IsGlobalContext() || 115 context->IsWithContext() || 116 (context->IsFunctionContext() && context->has_extension())) { 117 Handle<JSObject> object(JSObject::cast(context->extension()), isolate); 118 // Context extension objects needs to behave as if they have no 119 // prototype. So even if we want to follow prototype chains, we need 120 // to only do a local lookup for context extension objects. 121 if ((flags & FOLLOW_PROTOTYPE_CHAIN) == 0 || 122 object->IsJSContextExtensionObject()) { 123 *attributes = object->GetLocalPropertyAttribute(*name); 124 } else { 125 *attributes = object->GetPropertyAttribute(*name); 126 } 127 if (*attributes != ABSENT) { 128 if (FLAG_trace_contexts) { 129 PrintF("=> found property in context object %p\n", 130 reinterpret_cast<void*>(*object)); 131 } 132 return object; 133 } 134 } 135 136 // 2. Check the context proper if it has slots. 137 if (context->IsFunctionContext() || context->IsBlockContext()) { 138 // Use serialized scope information of functions and blocks to search 139 // for the context index. 140 Handle<ScopeInfo> scope_info; 141 if (context->IsFunctionContext()) { 142 scope_info = Handle<ScopeInfo>( 143 context->closure()->shared()->scope_info(), isolate); 144 } else { 145 scope_info = Handle<ScopeInfo>( 146 ScopeInfo::cast(context->extension()), isolate); 147 } 148 VariableMode mode; 149 InitializationFlag init_flag; 150 int slot_index = scope_info->ContextSlotIndex(*name, &mode, &init_flag); 151 ASSERT(slot_index < 0 || slot_index >= MIN_CONTEXT_SLOTS); 152 if (slot_index >= 0) { 153 if (FLAG_trace_contexts) { 154 PrintF("=> found local in context slot %d (mode = %d)\n", 155 slot_index, mode); 156 } 157 *index = slot_index; 158 // Note: Fixed context slots are statically allocated by the compiler. 159 // Statically allocated variables always have a statically known mode, 160 // which is the mode with which they were declared when added to the 161 // scope. Thus, the DYNAMIC mode (which corresponds to dynamically 162 // declared variables that were introduced through declaration nodes) 163 // must not appear here. 164 switch (mode) { 165 case INTERNAL: // Fall through. 166 case VAR: 167 *attributes = NONE; 168 *binding_flags = MUTABLE_IS_INITIALIZED; 169 break; 170 case LET: 171 *attributes = NONE; 172 *binding_flags = (init_flag == kNeedsInitialization) 173 ? MUTABLE_CHECK_INITIALIZED : MUTABLE_IS_INITIALIZED; 174 break; 175 case CONST: 176 *attributes = READ_ONLY; 177 *binding_flags = (init_flag == kNeedsInitialization) 178 ? IMMUTABLE_CHECK_INITIALIZED : IMMUTABLE_IS_INITIALIZED; 179 break; 180 case CONST_HARMONY: 181 *attributes = READ_ONLY; 182 *binding_flags = (init_flag == kNeedsInitialization) 183 ? IMMUTABLE_CHECK_INITIALIZED_HARMONY : 184 IMMUTABLE_IS_INITIALIZED_HARMONY; 185 break; 186 case DYNAMIC: 187 case DYNAMIC_GLOBAL: 188 case DYNAMIC_LOCAL: 189 case TEMPORARY: 190 UNREACHABLE(); 191 break; 192 } 193 return context; 194 } 195 196 // Check the slot corresponding to the intermediate context holding 197 // only the function name variable. 198 if (follow_context_chain && context->IsFunctionContext()) { 199 VariableMode mode; 200 int function_index = scope_info->FunctionContextSlotIndex(*name, &mode); 201 if (function_index >= 0) { 202 if (FLAG_trace_contexts) { 203 PrintF("=> found intermediate function in context slot %d\n", 204 function_index); 205 } 206 *index = function_index; 207 *attributes = READ_ONLY; 208 ASSERT(mode == CONST || mode == CONST_HARMONY); 209 *binding_flags = (mode == CONST) 210 ? IMMUTABLE_IS_INITIALIZED : IMMUTABLE_IS_INITIALIZED_HARMONY; 211 return context; 212 } 213 } 214 215 } else if (context->IsCatchContext()) { 216 // Catch contexts have the variable name in the extension slot. 217 if (name->Equals(String::cast(context->extension()))) { 218 if (FLAG_trace_contexts) { 219 PrintF("=> found in catch context\n"); 220 } 221 *index = Context::THROWN_OBJECT_INDEX; 222 *attributes = NONE; 223 *binding_flags = MUTABLE_IS_INITIALIZED; 224 return context; 225 } 226 } 227 228 // 3. Prepare to continue with the previous (next outermost) context. 229 if (context->IsGlobalContext()) { 230 follow_context_chain = false; 231 } else { 232 context = Handle<Context>(context->previous(), isolate); 233 } 234 } while (follow_context_chain); 235 236 if (FLAG_trace_contexts) { 237 PrintF("=> no property/slot found\n"); 238 } 239 return Handle<Object>::null(); 240 } 241 242 243 void Context::AddOptimizedFunction(JSFunction* function) { 244 ASSERT(IsGlobalContext()); 245 #ifdef DEBUG 246 Object* element = get(OPTIMIZED_FUNCTIONS_LIST); 247 while (!element->IsUndefined()) { 248 CHECK(element != function); 249 element = JSFunction::cast(element)->next_function_link(); 250 } 251 252 CHECK(function->next_function_link()->IsUndefined()); 253 254 // Check that the context belongs to the weak global contexts list. 255 bool found = false; 256 Object* context = GetHeap()->global_contexts_list(); 257 while (!context->IsUndefined()) { 258 if (context == this) { 259 found = true; 260 break; 261 } 262 context = Context::cast(context)->get(Context::NEXT_CONTEXT_LINK); 263 } 264 CHECK(found); 265 #endif 266 function->set_next_function_link(get(OPTIMIZED_FUNCTIONS_LIST)); 267 set(OPTIMIZED_FUNCTIONS_LIST, function); 268 } 269 270 271 void Context::RemoveOptimizedFunction(JSFunction* function) { 272 ASSERT(IsGlobalContext()); 273 Object* element = get(OPTIMIZED_FUNCTIONS_LIST); 274 JSFunction* prev = NULL; 275 while (!element->IsUndefined()) { 276 JSFunction* element_function = JSFunction::cast(element); 277 ASSERT(element_function->next_function_link()->IsUndefined() || 278 element_function->next_function_link()->IsJSFunction()); 279 if (element_function == function) { 280 if (prev == NULL) { 281 set(OPTIMIZED_FUNCTIONS_LIST, element_function->next_function_link()); 282 } else { 283 prev->set_next_function_link(element_function->next_function_link()); 284 } 285 element_function->set_next_function_link(GetHeap()->undefined_value()); 286 return; 287 } 288 prev = element_function; 289 element = element_function->next_function_link(); 290 } 291 UNREACHABLE(); 292 } 293 294 295 Object* Context::OptimizedFunctionsListHead() { 296 ASSERT(IsGlobalContext()); 297 return get(OPTIMIZED_FUNCTIONS_LIST); 298 } 299 300 301 void Context::ClearOptimizedFunctions() { 302 set(OPTIMIZED_FUNCTIONS_LIST, GetHeap()->undefined_value()); 303 } 304 305 306 #ifdef DEBUG 307 bool Context::IsBootstrappingOrContext(Object* object) { 308 // During bootstrapping we allow all objects to pass as 309 // contexts. This is necessary to fix circular dependencies. 310 return Isolate::Current()->bootstrapper()->IsActive() || object->IsContext(); 311 } 312 313 314 bool Context::IsBootstrappingOrGlobalObject(Object* object) { 315 // During bootstrapping we allow all objects to pass as global 316 // objects. This is necessary to fix circular dependencies. 317 Isolate* isolate = Isolate::Current(); 318 return isolate->heap()->gc_state() != Heap::NOT_IN_GC || 319 isolate->bootstrapper()->IsActive() || 320 object->IsGlobalObject(); 321 } 322 #endif 323 324 } } // namespace v8::internal 325