• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/modules.h"
8 #include "src/bootstrapper.h"
9 #include "src/debug/debug.h"
10 #include "src/isolate-inl.h"
11 #include "src/objects/module-inl.h"
12 
13 namespace v8 {
14 namespace internal {
15 
16 
Extend(Handle<ScriptContextTable> table,Handle<Context> script_context)17 Handle<ScriptContextTable> ScriptContextTable::Extend(
18     Handle<ScriptContextTable> table, Handle<Context> script_context) {
19   Handle<ScriptContextTable> result;
20   int used = table->used();
21   int length = table->length();
22   CHECK(used >= 0 && length > 0 && used < length);
23   if (used + kFirstContextSlotIndex == length) {
24     CHECK(length < Smi::kMaxValue / 2);
25     Isolate* isolate = script_context->GetIsolate();
26     Handle<FixedArray> copy =
27         isolate->factory()->CopyFixedArrayAndGrow(table, length);
28     copy->set_map(ReadOnlyRoots(isolate).script_context_table_map());
29     result = Handle<ScriptContextTable>::cast(copy);
30   } else {
31     result = table;
32   }
33   result->set_used(used + 1);
34 
35   DCHECK(script_context->IsScriptContext());
36   result->set(used + kFirstContextSlotIndex, *script_context);
37   return result;
38 }
39 
Lookup(Isolate * isolate,Handle<ScriptContextTable> table,Handle<String> name,LookupResult * result)40 bool ScriptContextTable::Lookup(Isolate* isolate,
41                                 Handle<ScriptContextTable> table,
42                                 Handle<String> name, LookupResult* result) {
43   for (int i = 0; i < table->used(); i++) {
44     Handle<Context> context = GetContext(isolate, table, i);
45     DCHECK(context->IsScriptContext());
46     Handle<ScopeInfo> scope_info(context->scope_info(), context->GetIsolate());
47     int slot_index = ScopeInfo::ContextSlotIndex(
48         scope_info, name, &result->mode, &result->init_flag,
49         &result->maybe_assigned_flag);
50 
51     if (slot_index >= 0) {
52       result->context_index = i;
53       result->slot_index = slot_index;
54       return true;
55     }
56   }
57   return false;
58 }
59 
60 
is_declaration_context()61 bool Context::is_declaration_context() {
62   if (IsFunctionContext() || IsNativeContext() || IsScriptContext() ||
63       IsModuleContext()) {
64     return true;
65   }
66   if (IsEvalContext()) {
67     return scope_info()->language_mode() == LanguageMode::kStrict;
68   }
69   if (!IsBlockContext()) return false;
70   return scope_info()->is_declaration_scope();
71 }
72 
73 
declaration_context()74 Context* Context::declaration_context() {
75   Context* current = this;
76   while (!current->is_declaration_context()) {
77     current = current->previous();
78   }
79   return current;
80 }
81 
closure_context()82 Context* Context::closure_context() {
83   Context* current = this;
84   while (!current->IsFunctionContext() && !current->IsScriptContext() &&
85          !current->IsModuleContext() && !current->IsNativeContext() &&
86          !current->IsEvalContext()) {
87     current = current->previous();
88   }
89   return current;
90 }
91 
extension_object()92 JSObject* Context::extension_object() {
93   DCHECK(IsNativeContext() || IsFunctionContext() || IsBlockContext() ||
94          IsEvalContext() || IsCatchContext());
95   HeapObject* object = extension();
96   if (object->IsTheHole()) return nullptr;
97   DCHECK(object->IsJSContextExtensionObject() ||
98          (IsNativeContext() && object->IsJSGlobalObject()));
99   return JSObject::cast(object);
100 }
101 
extension_receiver()102 JSReceiver* Context::extension_receiver() {
103   DCHECK(IsNativeContext() || IsWithContext() || IsEvalContext() ||
104          IsFunctionContext() || IsBlockContext());
105   return IsWithContext() ? JSReceiver::cast(extension()) : extension_object();
106 }
107 
scope_info()108 ScopeInfo* Context::scope_info() {
109   return ScopeInfo::cast(get(SCOPE_INFO_INDEX));
110 }
111 
module()112 Module* Context::module() {
113   Context* current = this;
114   while (!current->IsModuleContext()) {
115     current = current->previous();
116   }
117   return Module::cast(current->extension());
118 }
119 
global_object()120 JSGlobalObject* Context::global_object() {
121   return JSGlobalObject::cast(native_context()->extension());
122 }
123 
124 
script_context()125 Context* Context::script_context() {
126   Context* current = this;
127   while (!current->IsScriptContext()) {
128     current = current->previous();
129   }
130   return current;
131 }
132 
global_proxy()133 JSGlobalProxy* Context::global_proxy() {
134   return native_context()->global_proxy_object();
135 }
136 
set_global_proxy(JSGlobalProxy * object)137 void Context::set_global_proxy(JSGlobalProxy* object) {
138   native_context()->set_global_proxy_object(object);
139 }
140 
141 
142 /**
143  * Lookups a property in an object environment, taking the unscopables into
144  * account. This is used For HasBinding spec algorithms for ObjectEnvironment.
145  */
UnscopableLookup(LookupIterator * it)146 static Maybe<bool> UnscopableLookup(LookupIterator* it) {
147   Isolate* isolate = it->isolate();
148 
149   Maybe<bool> found = JSReceiver::HasProperty(it);
150   if (found.IsNothing() || !found.FromJust()) return found;
151 
152   Handle<Object> unscopables;
153   ASSIGN_RETURN_ON_EXCEPTION_VALUE(
154       isolate, unscopables,
155       JSReceiver::GetProperty(isolate,
156                               Handle<JSReceiver>::cast(it->GetReceiver()),
157                               isolate->factory()->unscopables_symbol()),
158       Nothing<bool>());
159   if (!unscopables->IsJSReceiver()) return Just(true);
160   Handle<Object> blacklist;
161   ASSIGN_RETURN_ON_EXCEPTION_VALUE(
162       isolate, blacklist,
163       JSReceiver::GetProperty(isolate, Handle<JSReceiver>::cast(unscopables),
164                               it->name()),
165       Nothing<bool>());
166   return Just(!blacklist->BooleanValue(isolate));
167 }
168 
GetAttributesForMode(VariableMode mode)169 static PropertyAttributes GetAttributesForMode(VariableMode mode) {
170   DCHECK(IsDeclaredVariableMode(mode));
171   return mode == VariableMode::kConst ? READ_ONLY : NONE;
172 }
173 
Lookup(Handle<String> name,ContextLookupFlags flags,int * index,PropertyAttributes * attributes,InitializationFlag * init_flag,VariableMode * variable_mode,bool * is_sloppy_function_name)174 Handle<Object> Context::Lookup(Handle<String> name, ContextLookupFlags flags,
175                                int* index, PropertyAttributes* attributes,
176                                InitializationFlag* init_flag,
177                                VariableMode* variable_mode,
178                                bool* is_sloppy_function_name) {
179   Isolate* isolate = GetIsolate();
180   Handle<Context> context(this, isolate);
181 
182   bool follow_context_chain = (flags & FOLLOW_CONTEXT_CHAIN) != 0;
183   bool failed_whitelist = false;
184   *index = kNotFound;
185   *attributes = ABSENT;
186   *init_flag = kCreatedInitialized;
187   *variable_mode = VariableMode::kVar;
188   if (is_sloppy_function_name != nullptr) {
189     *is_sloppy_function_name = false;
190   }
191 
192   if (FLAG_trace_contexts) {
193     PrintF("Context::Lookup(");
194     name->ShortPrint();
195     PrintF(")\n");
196   }
197 
198   do {
199     if (FLAG_trace_contexts) {
200       PrintF(" - looking in context %p", reinterpret_cast<void*>(*context));
201       if (context->IsScriptContext()) PrintF(" (script context)");
202       if (context->IsNativeContext()) PrintF(" (native context)");
203       PrintF("\n");
204     }
205 
206     // 1. Check global objects, subjects of with, and extension objects.
207     DCHECK_IMPLIES(context->IsEvalContext(),
208                    context->extension()->IsTheHole(isolate));
209     if ((context->IsNativeContext() ||
210          (context->IsWithContext() && ((flags & SKIP_WITH_CONTEXT) == 0)) ||
211          context->IsFunctionContext() || context->IsBlockContext()) &&
212         context->extension_receiver() != nullptr) {
213       Handle<JSReceiver> object(context->extension_receiver(), isolate);
214 
215       if (context->IsNativeContext()) {
216         if (FLAG_trace_contexts) {
217           PrintF(" - trying other script contexts\n");
218         }
219         // Try other script contexts.
220         Handle<ScriptContextTable> script_contexts(
221             context->global_object()->native_context()->script_context_table(),
222             isolate);
223         ScriptContextTable::LookupResult r;
224         if (ScriptContextTable::Lookup(isolate, script_contexts, name, &r)) {
225           if (FLAG_trace_contexts) {
226             Handle<Context> c = ScriptContextTable::GetContext(
227                 isolate, script_contexts, r.context_index);
228             PrintF("=> found property in script context %d: %p\n",
229                    r.context_index, reinterpret_cast<void*>(*c));
230           }
231           *index = r.slot_index;
232           *variable_mode = r.mode;
233           *init_flag = r.init_flag;
234           *attributes = GetAttributesForMode(r.mode);
235           return ScriptContextTable::GetContext(isolate, script_contexts,
236                                                 r.context_index);
237         }
238       }
239 
240       // Context extension objects needs to behave as if they have no
241       // prototype.  So even if we want to follow prototype chains, we need
242       // to only do a local lookup for context extension objects.
243       Maybe<PropertyAttributes> maybe = Nothing<PropertyAttributes>();
244       if ((flags & FOLLOW_PROTOTYPE_CHAIN) == 0 ||
245           object->IsJSContextExtensionObject()) {
246         maybe = JSReceiver::GetOwnPropertyAttributes(object, name);
247       } else if (context->IsWithContext()) {
248         // A with context will never bind "this", but debug-eval may look into
249         // a with context when resolving "this". Other synthetic variables such
250         // as new.target may be resolved as VariableMode::kDynamicLocal due to
251         // bug v8:5405 , skipping them here serves as a workaround until a more
252         // thorough fix can be applied.
253         // TODO(v8:5405): Replace this check with a DCHECK when resolution of
254         // of synthetic variables does not go through this code path.
255         if (ScopeInfo::VariableIsSynthetic(*name)) {
256           maybe = Just(ABSENT);
257         } else {
258           LookupIterator it(object, name, object);
259           Maybe<bool> found = UnscopableLookup(&it);
260           if (found.IsNothing()) {
261             maybe = Nothing<PropertyAttributes>();
262           } else {
263             // Luckily, consumers of |maybe| only care whether the property
264             // was absent or not, so we can return a dummy |NONE| value
265             // for its attributes when it was present.
266             maybe = Just(found.FromJust() ? NONE : ABSENT);
267           }
268         }
269       } else {
270         maybe = JSReceiver::GetPropertyAttributes(object, name);
271       }
272 
273       if (maybe.IsNothing()) return Handle<Object>();
274       DCHECK(!isolate->has_pending_exception());
275       *attributes = maybe.FromJust();
276 
277       if (maybe.FromJust() != ABSENT) {
278         if (FLAG_trace_contexts) {
279           PrintF("=> found property in context object %p\n",
280                  reinterpret_cast<void*>(*object));
281         }
282         return object;
283       }
284     }
285 
286     // 2. Check the context proper if it has slots.
287     if (context->IsFunctionContext() || context->IsBlockContext() ||
288         context->IsScriptContext() || context->IsEvalContext() ||
289         context->IsModuleContext() || context->IsCatchContext()) {
290       // Use serialized scope information of functions and blocks to search
291       // for the context index.
292       Handle<ScopeInfo> scope_info(context->scope_info(), isolate);
293       VariableMode mode;
294       InitializationFlag flag;
295       MaybeAssignedFlag maybe_assigned_flag;
296       int slot_index = ScopeInfo::ContextSlotIndex(scope_info, name, &mode,
297                                                    &flag, &maybe_assigned_flag);
298       DCHECK(slot_index < 0 || slot_index >= MIN_CONTEXT_SLOTS);
299       if (slot_index >= 0) {
300         if (FLAG_trace_contexts) {
301           PrintF("=> found local in context slot %d (mode = %hhu)\n",
302                  slot_index, static_cast<uint8_t>(mode));
303         }
304         *index = slot_index;
305         *variable_mode = mode;
306         *init_flag = flag;
307         *attributes = GetAttributesForMode(mode);
308         return context;
309       }
310 
311       // Check the slot corresponding to the intermediate context holding
312       // only the function name variable. It's conceptually (and spec-wise)
313       // in an outer scope of the function's declaration scope.
314       if (follow_context_chain && (flags & STOP_AT_DECLARATION_SCOPE) == 0 &&
315           context->IsFunctionContext()) {
316         int function_index = scope_info->FunctionContextSlotIndex(*name);
317         if (function_index >= 0) {
318           if (FLAG_trace_contexts) {
319             PrintF("=> found intermediate function in context slot %d\n",
320                    function_index);
321           }
322           *index = function_index;
323           *attributes = READ_ONLY;
324           *init_flag = kCreatedInitialized;
325           *variable_mode = VariableMode::kConst;
326           if (is_sloppy_function_name != nullptr &&
327               is_sloppy(scope_info->language_mode())) {
328             *is_sloppy_function_name = true;
329           }
330           return context;
331         }
332       }
333 
334       // Lookup variable in module imports and exports.
335       if (context->IsModuleContext()) {
336         VariableMode mode;
337         InitializationFlag flag;
338         MaybeAssignedFlag maybe_assigned_flag;
339         int cell_index =
340             scope_info->ModuleIndex(name, &mode, &flag, &maybe_assigned_flag);
341         if (cell_index != 0) {
342           if (FLAG_trace_contexts) {
343             PrintF("=> found in module imports or exports\n");
344           }
345           *index = cell_index;
346           *variable_mode = mode;
347           *init_flag = flag;
348           *attributes = ModuleDescriptor::GetCellIndexKind(cell_index) ==
349                                 ModuleDescriptor::kExport
350                             ? GetAttributesForMode(mode)
351                             : READ_ONLY;
352           return handle(context->module(), isolate);
353         }
354       }
355     } else if (context->IsDebugEvaluateContext()) {
356       // Check materialized locals.
357       Object* ext = context->get(EXTENSION_INDEX);
358       if (ext->IsJSReceiver()) {
359         Handle<JSReceiver> extension(JSReceiver::cast(ext), isolate);
360         LookupIterator it(extension, name, extension);
361         Maybe<bool> found = JSReceiver::HasProperty(&it);
362         if (found.FromMaybe(false)) {
363           *attributes = NONE;
364           return extension;
365         }
366       }
367       // Check the original context, but do not follow its context chain.
368       Object* obj = context->get(WRAPPED_CONTEXT_INDEX);
369       if (obj->IsContext()) {
370         Handle<Object> result =
371             Context::cast(obj)->Lookup(name, DONT_FOLLOW_CHAINS, index,
372                                        attributes, init_flag, variable_mode);
373         if (!result.is_null()) return result;
374       }
375       // Check whitelist. Names that do not pass whitelist shall only resolve
376       // to with, script or native contexts up the context chain.
377       obj = context->get(WHITE_LIST_INDEX);
378       if (obj->IsStringSet()) {
379         failed_whitelist =
380             failed_whitelist || !StringSet::cast(obj)->Has(isolate, name);
381       }
382     }
383 
384     // 3. Prepare to continue with the previous (next outermost) context.
385     if (context->IsNativeContext() ||
386         ((flags & STOP_AT_DECLARATION_SCOPE) != 0 &&
387          context->is_declaration_context())) {
388       follow_context_chain = false;
389     } else {
390       do {
391         context = Handle<Context>(context->previous(), isolate);
392         // If we come across a whitelist context, and the name is not
393         // whitelisted, then only consider with, script, module or native
394         // contexts.
395       } while (failed_whitelist && !context->IsScriptContext() &&
396                !context->IsNativeContext() && !context->IsWithContext() &&
397                !context->IsModuleContext());
398     }
399   } while (follow_context_chain);
400 
401   if (FLAG_trace_contexts) {
402     PrintF("=> no property/slot found\n");
403   }
404   return Handle<Object>::null();
405 }
406 
407 
AddOptimizedCode(Code * code)408 void Context::AddOptimizedCode(Code* code) {
409   DCHECK(IsNativeContext());
410   DCHECK(code->kind() == Code::OPTIMIZED_FUNCTION);
411   DCHECK(code->next_code_link()->IsUndefined());
412   code->set_next_code_link(get(OPTIMIZED_CODE_LIST));
413   set(OPTIMIZED_CODE_LIST, code, UPDATE_WEAK_WRITE_BARRIER);
414 }
415 
416 
SetOptimizedCodeListHead(Object * head)417 void Context::SetOptimizedCodeListHead(Object* head) {
418   DCHECK(IsNativeContext());
419   set(OPTIMIZED_CODE_LIST, head, UPDATE_WEAK_WRITE_BARRIER);
420 }
421 
422 
OptimizedCodeListHead()423 Object* Context::OptimizedCodeListHead() {
424   DCHECK(IsNativeContext());
425   return get(OPTIMIZED_CODE_LIST);
426 }
427 
428 
SetDeoptimizedCodeListHead(Object * head)429 void Context::SetDeoptimizedCodeListHead(Object* head) {
430   DCHECK(IsNativeContext());
431   set(DEOPTIMIZED_CODE_LIST, head, UPDATE_WEAK_WRITE_BARRIER);
432 }
433 
434 
DeoptimizedCodeListHead()435 Object* Context::DeoptimizedCodeListHead() {
436   DCHECK(IsNativeContext());
437   return get(DEOPTIMIZED_CODE_LIST);
438 }
439 
440 
ErrorMessageForCodeGenerationFromStrings()441 Handle<Object> Context::ErrorMessageForCodeGenerationFromStrings() {
442   Isolate* isolate = GetIsolate();
443   Handle<Object> result(error_message_for_code_gen_from_strings(), isolate);
444   if (!result->IsUndefined(isolate)) return result;
445   return isolate->factory()->NewStringFromStaticChars(
446       "Code generation from strings disallowed for this context");
447 }
448 
449 
450 #define COMPARE_NAME(index, type, name) \
451   if (string->IsOneByteEqualTo(STATIC_CHAR_VECTOR(#name))) return index;
452 
ImportedFieldIndexForName(Handle<String> string)453 int Context::ImportedFieldIndexForName(Handle<String> string) {
454   NATIVE_CONTEXT_IMPORTED_FIELDS(COMPARE_NAME)
455   return kNotFound;
456 }
457 
458 
IntrinsicIndexForName(Handle<String> string)459 int Context::IntrinsicIndexForName(Handle<String> string) {
460   NATIVE_CONTEXT_INTRINSIC_FUNCTIONS(COMPARE_NAME);
461   return kNotFound;
462 }
463 
464 #undef COMPARE_NAME
465 
466 #define COMPARE_NAME(index, type, name) \
467   if (strncmp(string, #name, length) == 0) return index;
468 
IntrinsicIndexForName(const unsigned char * unsigned_string,int length)469 int Context::IntrinsicIndexForName(const unsigned char* unsigned_string,
470                                    int length) {
471   const char* string = reinterpret_cast<const char*>(unsigned_string);
472   NATIVE_CONTEXT_INTRINSIC_FUNCTIONS(COMPARE_NAME);
473   return kNotFound;
474 }
475 
476 #undef COMPARE_NAME
477 
478 #ifdef DEBUG
479 
IsBootstrappingOrNativeContext(Isolate * isolate,Object * object)480 bool Context::IsBootstrappingOrNativeContext(Isolate* isolate, Object* object) {
481   // During bootstrapping we allow all objects to pass as global
482   // objects. This is necessary to fix circular dependencies.
483   return isolate->heap()->gc_state() != Heap::NOT_IN_GC ||
484          isolate->bootstrapper()->IsActive() || object->IsNativeContext();
485 }
486 
487 
IsBootstrappingOrValidParentContext(Object * object,Context * child)488 bool Context::IsBootstrappingOrValidParentContext(
489     Object* object, Context* child) {
490   // During bootstrapping we allow all objects to pass as
491   // contexts. This is necessary to fix circular dependencies.
492   if (child->GetIsolate()->bootstrapper()->IsActive()) return true;
493   if (!object->IsContext()) return false;
494   Context* context = Context::cast(object);
495   return context->IsNativeContext() || context->IsScriptContext() ||
496          context->IsModuleContext() || !child->IsModuleContext();
497 }
498 
499 #endif
500 
ResetErrorsThrown()501 void Context::ResetErrorsThrown() {
502   DCHECK(IsNativeContext());
503   set_errors_thrown(Smi::FromInt(0));
504 }
505 
IncrementErrorsThrown()506 void Context::IncrementErrorsThrown() {
507   DCHECK(IsNativeContext());
508 
509   int previous_value = errors_thrown()->value();
510   set_errors_thrown(Smi::FromInt(previous_value + 1));
511 }
512 
513 
GetErrorsThrown()514 int Context::GetErrorsThrown() { return errors_thrown()->value(); }
515 
516 }  // namespace internal
517 }  // namespace v8
518