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