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