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