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