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/contexts.h"
6 
7 #include "src/ast/scopeinfo.h"
8 #include "src/bootstrapper.h"
9 #include "src/debug/debug.h"
10 #include "src/isolate-inl.h"
11 
12 namespace v8 {
13 namespace internal {
14 
15 
Extend(Handle<ScriptContextTable> table,Handle<Context> script_context)16 Handle<ScriptContextTable> ScriptContextTable::Extend(
17     Handle<ScriptContextTable> table, Handle<Context> script_context) {
18   Handle<ScriptContextTable> result;
19   int used = table->used();
20   int length = table->length();
21   CHECK(used >= 0 && length > 0 && used < length);
22   if (used + kFirstContextSlot == length) {
23     CHECK(length < Smi::kMaxValue / 2);
24     Isolate* isolate = table->GetIsolate();
25     Handle<FixedArray> copy =
26         isolate->factory()->CopyFixedArrayAndGrow(table, length);
27     copy->set_map(isolate->heap()->script_context_table_map());
28     result = Handle<ScriptContextTable>::cast(copy);
29   } else {
30     result = table;
31   }
32   result->set_used(used + 1);
33 
34   DCHECK(script_context->IsScriptContext());
35   result->set(used + kFirstContextSlot, *script_context);
36   return result;
37 }
38 
39 
Lookup(Handle<ScriptContextTable> table,Handle<String> name,LookupResult * result)40 bool ScriptContextTable::Lookup(Handle<ScriptContextTable> table,
41                                 Handle<String> name, LookupResult* result) {
42   for (int i = 0; i < table->used(); i++) {
43     Handle<Context> context = GetContext(table, i);
44     DCHECK(context->IsScriptContext());
45     Handle<ScopeInfo> scope_info(context->scope_info());
46     int slot_index = ScopeInfo::ContextSlotIndex(
47         scope_info, name, &result->mode, &result->init_flag,
48         &result->maybe_assigned_flag);
49 
50     if (slot_index >= 0) {
51       result->context_index = i;
52       result->slot_index = slot_index;
53       return true;
54     }
55   }
56   return false;
57 }
58 
59 
is_declaration_context()60 bool Context::is_declaration_context() {
61   if (IsFunctionContext() || IsNativeContext() || IsScriptContext()) {
62     return true;
63   }
64   if (!IsBlockContext()) return false;
65   Object* ext = extension();
66   // If we have the special extension, we immediately know it must be a
67   // declaration scope. That's just a small performance shortcut.
68   return ext->IsSloppyBlockWithEvalContextExtension()
69       || ScopeInfo::cast(ext)->is_declaration_scope();
70 }
71 
72 
declaration_context()73 Context* Context::declaration_context() {
74   Context* current = this;
75   while (!current->is_declaration_context()) {
76     current = current->previous();
77     DCHECK(current->closure() == closure());
78   }
79   return current;
80 }
81 
82 
extension_object()83 JSObject* Context::extension_object() {
84   DCHECK(IsNativeContext() || IsFunctionContext() || IsBlockContext());
85   HeapObject* object = extension();
86   if (object->IsTheHole()) return nullptr;
87   if (IsBlockContext()) {
88     if (!object->IsSloppyBlockWithEvalContextExtension()) return nullptr;
89     object = SloppyBlockWithEvalContextExtension::cast(object)->extension();
90   }
91   DCHECK(object->IsJSContextExtensionObject() ||
92          (IsNativeContext() && object->IsJSGlobalObject()));
93   return JSObject::cast(object);
94 }
95 
96 
extension_receiver()97 JSReceiver* Context::extension_receiver() {
98   DCHECK(IsNativeContext() || IsWithContext() ||
99          IsFunctionContext() || IsBlockContext());
100   return IsWithContext() ? JSReceiver::cast(extension()) : extension_object();
101 }
102 
103 
scope_info()104 ScopeInfo* Context::scope_info() {
105   DCHECK(IsModuleContext() || IsScriptContext() || IsBlockContext());
106   HeapObject* object = extension();
107   if (object->IsSloppyBlockWithEvalContextExtension()) {
108     DCHECK(IsBlockContext());
109     object = SloppyBlockWithEvalContextExtension::cast(object)->scope_info();
110   }
111   return ScopeInfo::cast(object);
112 }
113 
114 
catch_name()115 String* Context::catch_name() {
116   DCHECK(IsCatchContext());
117   return String::cast(extension());
118 }
119 
120 
global_object()121 JSGlobalObject* Context::global_object() {
122   return JSGlobalObject::cast(native_context()->extension());
123 }
124 
125 
script_context()126 Context* Context::script_context() {
127   Context* current = this;
128   while (!current->IsScriptContext()) {
129     current = current->previous();
130   }
131   return current;
132 }
133 
134 
global_proxy()135 JSObject* Context::global_proxy() {
136   return native_context()->global_proxy_object();
137 }
138 
139 
set_global_proxy(JSObject * object)140 void Context::set_global_proxy(JSObject* object) {
141   native_context()->set_global_proxy_object(object);
142 }
143 
144 
145 /**
146  * Lookups a property in an object environment, taking the unscopables into
147  * account. This is used For HasBinding spec algorithms for ObjectEnvironment.
148  */
UnscopableLookup(LookupIterator * it)149 static Maybe<bool> UnscopableLookup(LookupIterator* it) {
150   Isolate* isolate = it->isolate();
151 
152   Maybe<bool> found = JSReceiver::HasProperty(it);
153   if (!found.IsJust() || !found.FromJust()) return found;
154 
155   Handle<Object> unscopables;
156   ASSIGN_RETURN_ON_EXCEPTION_VALUE(
157       isolate, unscopables,
158       Object::GetProperty(it->GetReceiver(),
159                           isolate->factory()->unscopables_symbol()),
160       Nothing<bool>());
161   if (!unscopables->IsJSReceiver()) return Just(true);
162   Handle<Object> blacklist;
163   ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, blacklist,
164                                    Object::GetProperty(unscopables, it->name()),
165                                    Nothing<bool>());
166   return Just(!blacklist->BooleanValue());
167 }
168 
GetAttributesAndBindingFlags(VariableMode mode,InitializationFlag init_flag,PropertyAttributes * attributes,BindingFlags * binding_flags)169 static void GetAttributesAndBindingFlags(VariableMode mode,
170                                          InitializationFlag init_flag,
171                                          PropertyAttributes* attributes,
172                                          BindingFlags* binding_flags) {
173   switch (mode) {
174     case VAR:
175       *attributes = NONE;
176       *binding_flags = MUTABLE_IS_INITIALIZED;
177       break;
178     case LET:
179       *attributes = NONE;
180       *binding_flags = (init_flag == kNeedsInitialization)
181                            ? MUTABLE_CHECK_INITIALIZED
182                            : MUTABLE_IS_INITIALIZED;
183       break;
184     case CONST_LEGACY:
185       *attributes = READ_ONLY;
186       *binding_flags = (init_flag == kNeedsInitialization)
187                            ? IMMUTABLE_CHECK_INITIALIZED
188                            : IMMUTABLE_IS_INITIALIZED;
189       break;
190     case CONST:
191       *attributes = READ_ONLY;
192       *binding_flags = (init_flag == kNeedsInitialization)
193                            ? IMMUTABLE_CHECK_INITIALIZED_HARMONY
194                            : IMMUTABLE_IS_INITIALIZED_HARMONY;
195       break;
196     case IMPORT:
197       // TODO(ES6)
198       UNREACHABLE();
199       break;
200     case DYNAMIC:
201     case DYNAMIC_GLOBAL:
202     case DYNAMIC_LOCAL:
203     case TEMPORARY:
204       // Note: Fixed context slots are statically allocated by the compiler.
205       // Statically allocated variables always have a statically known mode,
206       // which is the mode with which they were declared when added to the
207       // scope. Thus, the DYNAMIC mode (which corresponds to dynamically
208       // declared variables that were introduced through declaration nodes)
209       // must not appear here.
210       UNREACHABLE();
211       break;
212   }
213 }
214 
215 
Lookup(Handle<String> name,ContextLookupFlags flags,int * index,PropertyAttributes * attributes,BindingFlags * binding_flags)216 Handle<Object> Context::Lookup(Handle<String> name,
217                                ContextLookupFlags flags,
218                                int* index,
219                                PropertyAttributes* attributes,
220                                BindingFlags* binding_flags) {
221   Isolate* isolate = GetIsolate();
222   Handle<Context> context(this, isolate);
223 
224   bool follow_context_chain = (flags & FOLLOW_CONTEXT_CHAIN) != 0;
225   *index = kNotFound;
226   *attributes = ABSENT;
227   *binding_flags = MISSING_BINDING;
228 
229   if (FLAG_trace_contexts) {
230     PrintF("Context::Lookup(");
231     name->ShortPrint();
232     PrintF(")\n");
233   }
234 
235   do {
236     if (FLAG_trace_contexts) {
237       PrintF(" - looking in context %p", reinterpret_cast<void*>(*context));
238       if (context->IsScriptContext()) PrintF(" (script context)");
239       if (context->IsNativeContext()) PrintF(" (native context)");
240       PrintF("\n");
241     }
242 
243     // 1. Check global objects, subjects of with, and extension objects.
244     if ((context->IsNativeContext() ||
245          (context->IsWithContext() && ((flags & SKIP_WITH_CONTEXT) == 0)) ||
246          context->IsFunctionContext() || context->IsBlockContext()) &&
247         context->extension_receiver() != nullptr) {
248       Handle<JSReceiver> object(context->extension_receiver());
249 
250       if (context->IsNativeContext()) {
251         if (FLAG_trace_contexts) {
252           PrintF(" - trying other script contexts\n");
253         }
254         // Try other script contexts.
255         Handle<ScriptContextTable> script_contexts(
256             context->global_object()->native_context()->script_context_table());
257         ScriptContextTable::LookupResult r;
258         if (ScriptContextTable::Lookup(script_contexts, name, &r)) {
259           if (FLAG_trace_contexts) {
260             Handle<Context> c = ScriptContextTable::GetContext(script_contexts,
261                                                                r.context_index);
262             PrintF("=> found property in script context %d: %p\n",
263                    r.context_index, reinterpret_cast<void*>(*c));
264           }
265           *index = r.slot_index;
266           GetAttributesAndBindingFlags(r.mode, r.init_flag, attributes,
267                                        binding_flags);
268           return ScriptContextTable::GetContext(script_contexts,
269                                                 r.context_index);
270         }
271       }
272 
273       // Context extension objects needs to behave as if they have no
274       // prototype.  So even if we want to follow prototype chains, we need
275       // to only do a local lookup for context extension objects.
276       Maybe<PropertyAttributes> maybe = Nothing<PropertyAttributes>();
277       if ((flags & FOLLOW_PROTOTYPE_CHAIN) == 0 ||
278           object->IsJSContextExtensionObject()) {
279         maybe = JSReceiver::GetOwnPropertyAttributes(object, name);
280       } else if (context->IsWithContext()) {
281         // A with context will never bind "this".
282         if (name->Equals(*isolate->factory()->this_string())) {
283           maybe = Just(ABSENT);
284         } else {
285           LookupIterator it(object, name);
286           Maybe<bool> found = UnscopableLookup(&it);
287           if (found.IsNothing()) {
288             maybe = Nothing<PropertyAttributes>();
289           } else {
290             // Luckily, consumers of |maybe| only care whether the property
291             // was absent or not, so we can return a dummy |NONE| value
292             // for its attributes when it was present.
293             maybe = Just(found.FromJust() ? NONE : ABSENT);
294           }
295         }
296       } else {
297         maybe = JSReceiver::GetPropertyAttributes(object, name);
298       }
299 
300       if (!maybe.IsJust()) return Handle<Object>();
301       DCHECK(!isolate->has_pending_exception());
302       *attributes = maybe.FromJust();
303 
304       if (maybe.FromJust() != ABSENT) {
305         if (FLAG_trace_contexts) {
306           PrintF("=> found property in context object %p\n",
307                  reinterpret_cast<void*>(*object));
308         }
309         return object;
310       }
311     }
312 
313     // 2. Check the context proper if it has slots.
314     if (context->IsFunctionContext() || context->IsBlockContext() ||
315         context->IsScriptContext()) {
316       // Use serialized scope information of functions and blocks to search
317       // for the context index.
318       Handle<ScopeInfo> scope_info(context->IsFunctionContext()
319           ? context->closure()->shared()->scope_info()
320           : context->scope_info());
321       VariableMode mode;
322       InitializationFlag init_flag;
323       // TODO(sigurds) Figure out whether maybe_assigned_flag should
324       // be used to compute binding_flags.
325       MaybeAssignedFlag maybe_assigned_flag;
326       int slot_index = ScopeInfo::ContextSlotIndex(
327           scope_info, name, &mode, &init_flag, &maybe_assigned_flag);
328       DCHECK(slot_index < 0 || slot_index >= MIN_CONTEXT_SLOTS);
329       if (slot_index >= 0) {
330         if (FLAG_trace_contexts) {
331           PrintF("=> found local in context slot %d (mode = %d)\n",
332                  slot_index, mode);
333         }
334         *index = slot_index;
335         GetAttributesAndBindingFlags(mode, init_flag, attributes,
336                                      binding_flags);
337         return context;
338       }
339 
340       // Check the slot corresponding to the intermediate context holding
341       // only the function name variable.
342       if (follow_context_chain && context->IsFunctionContext()) {
343         VariableMode mode;
344         int function_index = scope_info->FunctionContextSlotIndex(*name, &mode);
345         if (function_index >= 0) {
346           if (FLAG_trace_contexts) {
347             PrintF("=> found intermediate function in context slot %d\n",
348                    function_index);
349           }
350           *index = function_index;
351           *attributes = READ_ONLY;
352           DCHECK(mode == CONST_LEGACY || mode == CONST);
353           *binding_flags = (mode == CONST_LEGACY)
354               ? IMMUTABLE_IS_INITIALIZED : IMMUTABLE_IS_INITIALIZED_HARMONY;
355           return context;
356         }
357       }
358 
359     } else if (context->IsCatchContext()) {
360       // Catch contexts have the variable name in the extension slot.
361       if (String::Equals(name, handle(context->catch_name()))) {
362         if (FLAG_trace_contexts) {
363           PrintF("=> found in catch context\n");
364         }
365         *index = Context::THROWN_OBJECT_INDEX;
366         *attributes = NONE;
367         *binding_flags = MUTABLE_IS_INITIALIZED;
368         return context;
369       }
370     }
371 
372     // 3. Prepare to continue with the previous (next outermost) context.
373     if (context->IsNativeContext() ||
374         ((flags & STOP_AT_DECLARATION_SCOPE) != 0 &&
375          context->is_declaration_context())) {
376       follow_context_chain = false;
377     } else {
378       context = Handle<Context>(context->previous(), isolate);
379     }
380   } while (follow_context_chain);
381 
382   if (FLAG_trace_contexts) {
383     PrintF("=> no property/slot found\n");
384   }
385   return Handle<Object>::null();
386 }
387 
388 
InitializeGlobalSlots()389 void Context::InitializeGlobalSlots() {
390   DCHECK(IsScriptContext());
391   DisallowHeapAllocation no_gc;
392 
393   ScopeInfo* scope_info = this->scope_info();
394 
395   int context_globals = scope_info->ContextGlobalCount();
396   if (context_globals > 0) {
397     PropertyCell* empty_cell = GetHeap()->empty_property_cell();
398 
399     int context_locals = scope_info->ContextLocalCount();
400     int index = Context::MIN_CONTEXT_SLOTS + context_locals;
401     for (int i = 0; i < context_globals; i++) {
402       set(index++, empty_cell);
403     }
404   }
405 }
406 
407 
AddOptimizedFunction(JSFunction * function)408 void Context::AddOptimizedFunction(JSFunction* function) {
409   DCHECK(IsNativeContext());
410 #ifdef ENABLE_SLOW_DCHECKS
411   if (FLAG_enable_slow_asserts) {
412     Object* element = get(OPTIMIZED_FUNCTIONS_LIST);
413     while (!element->IsUndefined()) {
414       CHECK(element != function);
415       element = JSFunction::cast(element)->next_function_link();
416     }
417   }
418 
419   // Check that the context belongs to the weak native contexts list.
420   bool found = false;
421   Object* context = GetHeap()->native_contexts_list();
422   while (!context->IsUndefined()) {
423     if (context == this) {
424       found = true;
425       break;
426     }
427     context = Context::cast(context)->get(Context::NEXT_CONTEXT_LINK);
428   }
429   CHECK(found);
430 #endif
431 
432   // If the function link field is already used then the function was
433   // enqueued as a code flushing candidate and we remove it now.
434   if (!function->next_function_link()->IsUndefined()) {
435     CodeFlusher* flusher = GetHeap()->mark_compact_collector()->code_flusher();
436     flusher->EvictCandidate(function);
437   }
438 
439   DCHECK(function->next_function_link()->IsUndefined());
440 
441   function->set_next_function_link(get(OPTIMIZED_FUNCTIONS_LIST),
442                                    UPDATE_WEAK_WRITE_BARRIER);
443   set(OPTIMIZED_FUNCTIONS_LIST, function, UPDATE_WEAK_WRITE_BARRIER);
444 }
445 
446 
RemoveOptimizedFunction(JSFunction * function)447 void Context::RemoveOptimizedFunction(JSFunction* function) {
448   DCHECK(IsNativeContext());
449   Object* element = get(OPTIMIZED_FUNCTIONS_LIST);
450   JSFunction* prev = NULL;
451   while (!element->IsUndefined()) {
452     JSFunction* element_function = JSFunction::cast(element);
453     DCHECK(element_function->next_function_link()->IsUndefined() ||
454            element_function->next_function_link()->IsJSFunction());
455     if (element_function == function) {
456       if (prev == NULL) {
457         set(OPTIMIZED_FUNCTIONS_LIST, element_function->next_function_link(),
458             UPDATE_WEAK_WRITE_BARRIER);
459       } else {
460         prev->set_next_function_link(element_function->next_function_link(),
461                                      UPDATE_WEAK_WRITE_BARRIER);
462       }
463       element_function->set_next_function_link(GetHeap()->undefined_value(),
464                                                UPDATE_WEAK_WRITE_BARRIER);
465       return;
466     }
467     prev = element_function;
468     element = element_function->next_function_link();
469   }
470   UNREACHABLE();
471 }
472 
473 
SetOptimizedFunctionsListHead(Object * head)474 void Context::SetOptimizedFunctionsListHead(Object* head) {
475   DCHECK(IsNativeContext());
476   set(OPTIMIZED_FUNCTIONS_LIST, head, UPDATE_WEAK_WRITE_BARRIER);
477 }
478 
479 
OptimizedFunctionsListHead()480 Object* Context::OptimizedFunctionsListHead() {
481   DCHECK(IsNativeContext());
482   return get(OPTIMIZED_FUNCTIONS_LIST);
483 }
484 
485 
AddOptimizedCode(Code * code)486 void Context::AddOptimizedCode(Code* code) {
487   DCHECK(IsNativeContext());
488   DCHECK(code->kind() == Code::OPTIMIZED_FUNCTION);
489   DCHECK(code->next_code_link()->IsUndefined());
490   code->set_next_code_link(get(OPTIMIZED_CODE_LIST));
491   set(OPTIMIZED_CODE_LIST, code, UPDATE_WEAK_WRITE_BARRIER);
492 }
493 
494 
SetOptimizedCodeListHead(Object * head)495 void Context::SetOptimizedCodeListHead(Object* head) {
496   DCHECK(IsNativeContext());
497   set(OPTIMIZED_CODE_LIST, head, UPDATE_WEAK_WRITE_BARRIER);
498 }
499 
500 
OptimizedCodeListHead()501 Object* Context::OptimizedCodeListHead() {
502   DCHECK(IsNativeContext());
503   return get(OPTIMIZED_CODE_LIST);
504 }
505 
506 
SetDeoptimizedCodeListHead(Object * head)507 void Context::SetDeoptimizedCodeListHead(Object* head) {
508   DCHECK(IsNativeContext());
509   set(DEOPTIMIZED_CODE_LIST, head, UPDATE_WEAK_WRITE_BARRIER);
510 }
511 
512 
DeoptimizedCodeListHead()513 Object* Context::DeoptimizedCodeListHead() {
514   DCHECK(IsNativeContext());
515   return get(DEOPTIMIZED_CODE_LIST);
516 }
517 
518 
ErrorMessageForCodeGenerationFromStrings()519 Handle<Object> Context::ErrorMessageForCodeGenerationFromStrings() {
520   Isolate* isolate = GetIsolate();
521   Handle<Object> result(error_message_for_code_gen_from_strings(), isolate);
522   if (!result->IsUndefined()) return result;
523   return isolate->factory()->NewStringFromStaticChars(
524       "Code generation from strings disallowed for this context");
525 }
526 
527 
528 #define COMPARE_NAME(index, type, name) \
529   if (string->IsOneByteEqualTo(STATIC_CHAR_VECTOR(#name))) return index;
530 
ImportedFieldIndexForName(Handle<String> string)531 int Context::ImportedFieldIndexForName(Handle<String> string) {
532   NATIVE_CONTEXT_IMPORTED_FIELDS(COMPARE_NAME)
533   return kNotFound;
534 }
535 
536 
IntrinsicIndexForName(Handle<String> string)537 int Context::IntrinsicIndexForName(Handle<String> string) {
538   NATIVE_CONTEXT_INTRINSIC_FUNCTIONS(COMPARE_NAME);
539   return kNotFound;
540 }
541 
542 #undef COMPARE_NAME
543 
544 
IsJSBuiltin(Handle<Context> native_context,Handle<JSFunction> function)545 bool Context::IsJSBuiltin(Handle<Context> native_context,
546                           Handle<JSFunction> function) {
547 #define COMPARE_FUNCTION(index, type, name) \
548   if (*function == native_context->get(index)) return true;
549   NATIVE_CONTEXT_JS_BUILTINS(COMPARE_FUNCTION);
550 #undef COMPARE_FUNCTION
551   return false;
552 }
553 
554 
555 #ifdef DEBUG
556 
IsBootstrappingOrNativeContext(Isolate * isolate,Object * object)557 bool Context::IsBootstrappingOrNativeContext(Isolate* isolate, Object* object) {
558   // During bootstrapping we allow all objects to pass as global
559   // objects. This is necessary to fix circular dependencies.
560   return isolate->heap()->gc_state() != Heap::NOT_IN_GC ||
561          isolate->bootstrapper()->IsActive() || object->IsNativeContext();
562 }
563 
564 
IsBootstrappingOrValidParentContext(Object * object,Context * child)565 bool Context::IsBootstrappingOrValidParentContext(
566     Object* object, Context* child) {
567   // During bootstrapping we allow all objects to pass as
568   // contexts. This is necessary to fix circular dependencies.
569   if (child->GetIsolate()->bootstrapper()->IsActive()) return true;
570   if (!object->IsContext()) return false;
571   Context* context = Context::cast(object);
572   return context->IsNativeContext() || context->IsScriptContext() ||
573          context->IsModuleContext() || !child->IsModuleContext();
574 }
575 
576 #endif
577 
578 
IncrementErrorsThrown()579 void Context::IncrementErrorsThrown() {
580   DCHECK(IsNativeContext());
581 
582   int previous_value = errors_thrown()->value();
583   set_errors_thrown(Smi::FromInt(previous_value + 1));
584 }
585 
586 
GetErrorsThrown()587 int Context::GetErrorsThrown() { return errors_thrown()->value(); }
588 
589 }  // namespace internal
590 }  // namespace v8
591