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.
7 #include "src/bootstrapper.h"
9 #include "src/scopeinfo.h"
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 + 1 == length) {
22 CHECK(length < Smi::kMaxValue / 2);
23 result = Handle<ScriptContextTable>::cast(
24 FixedArray::CopySize(table, length * 2));
28 result->set_used(used + 1);
30 DCHECK(script_context->IsScriptContext());
31 result->set(used + 1, *script_context);
36 bool ScriptContextTable::Lookup(Handle<ScriptContextTable> table,
37 Handle<String> name, LookupResult* result) {
38 for (int i = 0; i < table->used(); i++) {
39 Handle<Context> context = GetContext(table, i);
40 DCHECK(context->IsScriptContext());
41 Handle<ScopeInfo> scope_info(ScopeInfo::cast(context->extension()));
42 int slot_index = ScopeInfo::ContextSlotIndex(
43 scope_info, name, &result->mode, &result->init_flag,
44 &result->maybe_assigned_flag);
46 if (slot_index >= 0) {
47 result->context_index = i;
48 result->slot_index = slot_index;
56 Context* Context::declaration_context() {
57 Context* current = this;
58 while (!current->IsFunctionContext() && !current->IsNativeContext() &&
59 !current->IsScriptContext()) {
60 current = current->previous();
61 DCHECK(current->closure() == closure());
67 JSBuiltinsObject* Context::builtins() {
68 GlobalObject* object = global_object();
69 if (object->IsJSGlobalObject()) {
70 return JSGlobalObject::cast(object)->builtins();
72 DCHECK(object->IsJSBuiltinsObject());
73 return JSBuiltinsObject::cast(object);
78 Context* Context::script_context() {
79 Context* current = this;
80 while (!current->IsScriptContext()) {
81 current = current->previous();
87 Context* Context::native_context() {
88 // Fast case: the global object for this context has been set. In
89 // that case, the global object has a direct pointer to the global
91 if (global_object()->IsGlobalObject()) {
92 return global_object()->native_context();
95 // During bootstrapping, the global object might not be set and we
96 // have to search the context chain to find the native context.
97 DCHECK(this->GetIsolate()->bootstrapper()->IsActive());
98 Context* current = this;
99 while (!current->IsNativeContext()) {
100 JSFunction* closure = JSFunction::cast(current->closure());
101 current = Context::cast(closure->context());
107 JSObject* Context::global_proxy() {
108 return native_context()->global_proxy_object();
112 void Context::set_global_proxy(JSObject* object) {
113 native_context()->set_global_proxy_object(object);
118 * Lookups a property in an object environment, taking the unscopables into
119 * account. This is used For HasBinding spec algorithms for ObjectEnvironment.
121 static Maybe<PropertyAttributes> UnscopableLookup(LookupIterator* it) {
122 Isolate* isolate = it->isolate();
124 Maybe<PropertyAttributes> attrs = JSReceiver::GetPropertyAttributes(it);
125 DCHECK(attrs.IsJust() || isolate->has_pending_exception());
126 if (!attrs.IsJust() || attrs.FromJust() == ABSENT) return attrs;
128 Handle<Symbol> unscopables_symbol = isolate->factory()->unscopables_symbol();
129 Handle<Object> receiver = it->GetReceiver();
130 Handle<Object> unscopables;
131 MaybeHandle<Object> maybe_unscopables =
132 Object::GetProperty(receiver, unscopables_symbol);
133 if (!maybe_unscopables.ToHandle(&unscopables)) {
134 return Nothing<PropertyAttributes>();
136 if (!unscopables->IsSpecObject()) return attrs;
137 Handle<Object> blacklist;
138 MaybeHandle<Object> maybe_blacklist =
139 Object::GetProperty(unscopables, it->name());
140 if (!maybe_blacklist.ToHandle(&blacklist)) {
141 DCHECK(isolate->has_pending_exception());
142 return Nothing<PropertyAttributes>();
144 return blacklist->IsUndefined() ? attrs : Just(ABSENT);
147 static void GetAttributesAndBindingFlags(VariableMode mode,
148 InitializationFlag init_flag,
149 PropertyAttributes* attributes,
150 BindingFlags* binding_flags) {
152 case INTERNAL: // Fall through.
155 *binding_flags = MUTABLE_IS_INITIALIZED;
159 *binding_flags = (init_flag == kNeedsInitialization)
160 ? MUTABLE_CHECK_INITIALIZED
161 : MUTABLE_IS_INITIALIZED;
164 *attributes = READ_ONLY;
165 *binding_flags = (init_flag == kNeedsInitialization)
166 ? IMMUTABLE_CHECK_INITIALIZED
167 : IMMUTABLE_IS_INITIALIZED;
170 *attributes = READ_ONLY;
171 *binding_flags = (init_flag == kNeedsInitialization)
172 ? IMMUTABLE_CHECK_INITIALIZED_HARMONY
173 : IMMUTABLE_IS_INITIALIZED_HARMONY;
183 // Note: Fixed context slots are statically allocated by the compiler.
184 // Statically allocated variables always have a statically known mode,
185 // which is the mode with which they were declared when added to the
186 // scope. Thus, the DYNAMIC mode (which corresponds to dynamically
187 // declared variables that were introduced through declaration nodes)
188 // must not appear here.
195 Handle<Object> Context::Lookup(Handle<String> name,
196 ContextLookupFlags flags,
198 PropertyAttributes* attributes,
199 BindingFlags* binding_flags) {
200 Isolate* isolate = GetIsolate();
201 Handle<Context> context(this, isolate);
203 bool follow_context_chain = (flags & FOLLOW_CONTEXT_CHAIN) != 0;
205 *attributes = ABSENT;
206 *binding_flags = MISSING_BINDING;
208 if (FLAG_trace_contexts) {
209 PrintF("Context::Lookup(");
215 if (FLAG_trace_contexts) {
216 PrintF(" - looking in context %p", reinterpret_cast<void*>(*context));
217 if (context->IsScriptContext()) PrintF(" (script context)");
218 if (context->IsNativeContext()) PrintF(" (native context)");
223 // 1. Check global objects, subjects of with, and extension objects.
224 if (context->IsNativeContext() ||
225 context->IsWithContext() ||
226 (context->IsFunctionContext() && context->has_extension())) {
227 Handle<JSReceiver> object(
228 JSReceiver::cast(context->extension()), isolate);
230 if (context->IsNativeContext()) {
231 if (FLAG_trace_contexts) {
232 PrintF(" - trying other script contexts\n");
234 // Try other script contexts.
235 Handle<ScriptContextTable> script_contexts(
236 context->global_object()->native_context()->script_context_table());
237 ScriptContextTable::LookupResult r;
238 if (ScriptContextTable::Lookup(script_contexts, name, &r)) {
239 if (FLAG_trace_contexts) {
240 Handle<Context> c = ScriptContextTable::GetContext(script_contexts,
242 PrintF("=> found property in script context %d: %p\n",
243 r.context_index, reinterpret_cast<void*>(*c));
245 *index = r.slot_index;
246 GetAttributesAndBindingFlags(r.mode, r.init_flag, attributes,
248 return ScriptContextTable::GetContext(script_contexts,
253 // Context extension objects needs to behave as if they have no
254 // prototype. So even if we want to follow prototype chains, we need
255 // to only do a local lookup for context extension objects.
256 Maybe<PropertyAttributes> maybe = Nothing<PropertyAttributes>();
257 if ((flags & FOLLOW_PROTOTYPE_CHAIN) == 0 ||
258 object->IsJSContextExtensionObject()) {
259 maybe = JSReceiver::GetOwnPropertyAttributes(object, name);
260 } else if (context->IsWithContext()) {
261 LookupIterator it(object, name);
262 maybe = UnscopableLookup(&it);
264 maybe = JSReceiver::GetPropertyAttributes(object, name);
267 if (!maybe.IsJust()) return Handle<Object>();
268 DCHECK(!isolate->has_pending_exception());
269 *attributes = maybe.FromJust();
271 if (maybe.FromJust() != ABSENT) {
272 if (FLAG_trace_contexts) {
273 PrintF("=> found property in context object %p\n",
274 reinterpret_cast<void*>(*object));
280 // 2. Check the context proper if it has slots.
281 if (context->IsFunctionContext() || context->IsBlockContext() ||
282 context->IsScriptContext()) {
283 // Use serialized scope information of functions and blocks to search
284 // for the context index.
285 Handle<ScopeInfo> scope_info;
286 if (context->IsFunctionContext()) {
287 scope_info = Handle<ScopeInfo>(
288 context->closure()->shared()->scope_info(), isolate);
290 scope_info = Handle<ScopeInfo>(
291 ScopeInfo::cast(context->extension()), isolate);
294 InitializationFlag init_flag;
295 // TODO(sigurds) Figure out whether maybe_assigned_flag should
296 // be used to compute binding_flags.
297 MaybeAssignedFlag maybe_assigned_flag;
298 int slot_index = ScopeInfo::ContextSlotIndex(
299 scope_info, name, &mode, &init_flag, &maybe_assigned_flag);
300 DCHECK(slot_index < 0 || slot_index >= MIN_CONTEXT_SLOTS);
301 if (slot_index >= 0) {
302 if (FLAG_trace_contexts) {
303 PrintF("=> found local in context slot %d (mode = %d)\n",
307 GetAttributesAndBindingFlags(mode, init_flag, attributes,
312 // Check the slot corresponding to the intermediate context holding
313 // only the function name variable.
314 if (follow_context_chain && context->IsFunctionContext()) {
316 int function_index = scope_info->FunctionContextSlotIndex(*name, &mode);
317 if (function_index >= 0) {
318 if (FLAG_trace_contexts) {
319 PrintF("=> found intermediate function in context slot %d\n",
322 *index = function_index;
323 *attributes = READ_ONLY;
324 DCHECK(mode == CONST_LEGACY || mode == CONST);
325 *binding_flags = (mode == CONST_LEGACY)
326 ? IMMUTABLE_IS_INITIALIZED : IMMUTABLE_IS_INITIALIZED_HARMONY;
331 } else if (context->IsCatchContext()) {
332 // Catch contexts have the variable name in the extension slot.
333 if (String::Equals(name, handle(String::cast(context->extension())))) {
334 if (FLAG_trace_contexts) {
335 PrintF("=> found in catch context\n");
337 *index = Context::THROWN_OBJECT_INDEX;
339 *binding_flags = MUTABLE_IS_INITIALIZED;
344 // 3. Prepare to continue with the previous (next outermost) context.
345 if (context->IsNativeContext()) {
346 follow_context_chain = false;
348 context = Handle<Context>(context->previous(), isolate);
350 } while (follow_context_chain);
352 if (FLAG_trace_contexts) {
353 PrintF("=> no property/slot found\n");
355 return Handle<Object>::null();
359 void Context::AddOptimizedFunction(JSFunction* function) {
360 DCHECK(IsNativeContext());
361 #ifdef ENABLE_SLOW_DCHECKS
362 if (FLAG_enable_slow_asserts) {
363 Object* element = get(OPTIMIZED_FUNCTIONS_LIST);
364 while (!element->IsUndefined()) {
365 CHECK(element != function);
366 element = JSFunction::cast(element)->next_function_link();
370 // Check that the context belongs to the weak native contexts list.
372 Object* context = GetHeap()->native_contexts_list();
373 while (!context->IsUndefined()) {
374 if (context == this) {
378 context = Context::cast(context)->get(Context::NEXT_CONTEXT_LINK);
383 // If the function link field is already used then the function was
384 // enqueued as a code flushing candidate and we remove it now.
385 if (!function->next_function_link()->IsUndefined()) {
386 CodeFlusher* flusher = GetHeap()->mark_compact_collector()->code_flusher();
387 flusher->EvictCandidate(function);
390 DCHECK(function->next_function_link()->IsUndefined());
392 function->set_next_function_link(get(OPTIMIZED_FUNCTIONS_LIST));
393 set(OPTIMIZED_FUNCTIONS_LIST, function);
397 void Context::RemoveOptimizedFunction(JSFunction* function) {
398 DCHECK(IsNativeContext());
399 Object* element = get(OPTIMIZED_FUNCTIONS_LIST);
400 JSFunction* prev = NULL;
401 while (!element->IsUndefined()) {
402 JSFunction* element_function = JSFunction::cast(element);
403 DCHECK(element_function->next_function_link()->IsUndefined() ||
404 element_function->next_function_link()->IsJSFunction());
405 if (element_function == function) {
407 set(OPTIMIZED_FUNCTIONS_LIST, element_function->next_function_link());
409 prev->set_next_function_link(element_function->next_function_link());
411 element_function->set_next_function_link(GetHeap()->undefined_value());
414 prev = element_function;
415 element = element_function->next_function_link();
421 void Context::SetOptimizedFunctionsListHead(Object* head) {
422 DCHECK(IsNativeContext());
423 set(OPTIMIZED_FUNCTIONS_LIST, head);
427 Object* Context::OptimizedFunctionsListHead() {
428 DCHECK(IsNativeContext());
429 return get(OPTIMIZED_FUNCTIONS_LIST);
433 void Context::AddOptimizedCode(Code* code) {
434 DCHECK(IsNativeContext());
435 DCHECK(code->kind() == Code::OPTIMIZED_FUNCTION);
436 DCHECK(code->next_code_link()->IsUndefined());
437 code->set_next_code_link(get(OPTIMIZED_CODE_LIST));
438 set(OPTIMIZED_CODE_LIST, code);
442 void Context::SetOptimizedCodeListHead(Object* head) {
443 DCHECK(IsNativeContext());
444 set(OPTIMIZED_CODE_LIST, head);
448 Object* Context::OptimizedCodeListHead() {
449 DCHECK(IsNativeContext());
450 return get(OPTIMIZED_CODE_LIST);
454 void Context::SetDeoptimizedCodeListHead(Object* head) {
455 DCHECK(IsNativeContext());
456 set(DEOPTIMIZED_CODE_LIST, head);
460 Object* Context::DeoptimizedCodeListHead() {
461 DCHECK(IsNativeContext());
462 return get(DEOPTIMIZED_CODE_LIST);
466 Handle<Object> Context::ErrorMessageForCodeGenerationFromStrings() {
467 Isolate* isolate = GetIsolate();
468 Handle<Object> result(error_message_for_code_gen_from_strings(), isolate);
469 if (!result->IsUndefined()) return result;
470 return isolate->factory()->NewStringFromStaticChars(
471 "Code generation from strings disallowed for this context");
476 bool Context::IsBootstrappingOrValidParentContext(
477 Object* object, Context* child) {
478 // During bootstrapping we allow all objects to pass as
479 // contexts. This is necessary to fix circular dependencies.
480 if (child->GetIsolate()->bootstrapper()->IsActive()) return true;
481 if (!object->IsContext()) return false;
482 Context* context = Context::cast(object);
483 return context->IsNativeContext() || context->IsScriptContext() ||
484 context->IsModuleContext() || !child->IsModuleContext();
488 bool Context::IsBootstrappingOrGlobalObject(Isolate* isolate, Object* object) {
489 // During bootstrapping we allow all objects to pass as global
490 // objects. This is necessary to fix circular dependencies.
491 return isolate->heap()->gc_state() != Heap::NOT_IN_GC ||
492 isolate->bootstrapper()->IsActive() ||
493 object->IsGlobalObject();
497 } } // namespace v8::internal