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 <stdlib.h>
6 
7 #include "src/v8.h"
8 
9 #include "src/scopeinfo.h"
10 #include "src/scopes.h"
11 
12 namespace v8 {
13 namespace internal {
14 
15 
Create(Scope * scope,Zone * zone)16 Handle<ScopeInfo> ScopeInfo::Create(Scope* scope, Zone* zone) {
17   // Collect stack and context locals.
18   ZoneList<Variable*> stack_locals(scope->StackLocalCount(), zone);
19   ZoneList<Variable*> context_locals(scope->ContextLocalCount(), zone);
20   scope->CollectStackAndContextLocals(&stack_locals, &context_locals);
21   const int stack_local_count = stack_locals.length();
22   const int context_local_count = context_locals.length();
23   // Make sure we allocate the correct amount.
24   DCHECK(scope->StackLocalCount() == stack_local_count);
25   DCHECK(scope->ContextLocalCount() == context_local_count);
26 
27   // Determine use and location of the function variable if it is present.
28   FunctionVariableInfo function_name_info;
29   VariableMode function_variable_mode;
30   if (scope->is_function_scope() && scope->function() != NULL) {
31     Variable* var = scope->function()->proxy()->var();
32     if (!var->is_used()) {
33       function_name_info = UNUSED;
34     } else if (var->IsContextSlot()) {
35       function_name_info = CONTEXT;
36     } else {
37       DCHECK(var->IsStackLocal());
38       function_name_info = STACK;
39     }
40     function_variable_mode = var->mode();
41   } else {
42     function_name_info = NONE;
43     function_variable_mode = VAR;
44   }
45 
46   const bool has_function_name = function_name_info != NONE;
47   const int parameter_count = scope->num_parameters();
48   const int length = kVariablePartIndex
49       + parameter_count + stack_local_count + 2 * context_local_count
50       + (has_function_name ? 2 : 0);
51 
52   Factory* factory = zone->isolate()->factory();
53   Handle<ScopeInfo> scope_info = factory->NewScopeInfo(length);
54 
55   // Encode the flags.
56   int flags = ScopeTypeField::encode(scope->scope_type()) |
57               CallsEvalField::encode(scope->calls_eval()) |
58               StrictModeField::encode(scope->strict_mode()) |
59               FunctionVariableField::encode(function_name_info) |
60               FunctionVariableMode::encode(function_variable_mode) |
61               AsmModuleField::encode(scope->asm_module()) |
62               AsmFunctionField::encode(scope->asm_function());
63   scope_info->SetFlags(flags);
64   scope_info->SetParameterCount(parameter_count);
65   scope_info->SetStackLocalCount(stack_local_count);
66   scope_info->SetContextLocalCount(context_local_count);
67 
68   int index = kVariablePartIndex;
69   // Add parameters.
70   DCHECK(index == scope_info->ParameterEntriesIndex());
71   for (int i = 0; i < parameter_count; ++i) {
72     scope_info->set(index++, *scope->parameter(i)->name());
73   }
74 
75   // Add stack locals' names. We are assuming that the stack locals'
76   // slots are allocated in increasing order, so we can simply add
77   // them to the ScopeInfo object.
78   DCHECK(index == scope_info->StackLocalEntriesIndex());
79   for (int i = 0; i < stack_local_count; ++i) {
80     DCHECK(stack_locals[i]->index() == i);
81     scope_info->set(index++, *stack_locals[i]->name());
82   }
83 
84   // Due to usage analysis, context-allocated locals are not necessarily in
85   // increasing order: Some of them may be parameters which are allocated before
86   // the non-parameter locals. When the non-parameter locals are sorted
87   // according to usage, the allocated slot indices may not be in increasing
88   // order with the variable list anymore. Thus, we first need to sort them by
89   // context slot index before adding them to the ScopeInfo object.
90   context_locals.Sort(&Variable::CompareIndex);
91 
92   // Add context locals' names.
93   DCHECK(index == scope_info->ContextLocalNameEntriesIndex());
94   for (int i = 0; i < context_local_count; ++i) {
95     scope_info->set(index++, *context_locals[i]->name());
96   }
97 
98   // Add context locals' info.
99   DCHECK(index == scope_info->ContextLocalInfoEntriesIndex());
100   for (int i = 0; i < context_local_count; ++i) {
101     Variable* var = context_locals[i];
102     uint32_t value =
103         ContextLocalMode::encode(var->mode()) |
104         ContextLocalInitFlag::encode(var->initialization_flag()) |
105         ContextLocalMaybeAssignedFlag::encode(var->maybe_assigned());
106     scope_info->set(index++, Smi::FromInt(value));
107   }
108 
109   // If present, add the function variable name and its index.
110   DCHECK(index == scope_info->FunctionNameEntryIndex());
111   if (has_function_name) {
112     int var_index = scope->function()->proxy()->var()->index();
113     scope_info->set(index++, *scope->function()->proxy()->name());
114     scope_info->set(index++, Smi::FromInt(var_index));
115     DCHECK(function_name_info != STACK ||
116            (var_index == scope_info->StackLocalCount() &&
117             var_index == scope_info->StackSlotCount() - 1));
118     DCHECK(function_name_info != CONTEXT ||
119            var_index == scope_info->ContextLength() - 1);
120   }
121 
122   DCHECK(index == scope_info->length());
123   DCHECK(scope->num_parameters() == scope_info->ParameterCount());
124   DCHECK(scope->num_stack_slots() == scope_info->StackSlotCount());
125   DCHECK(scope->num_heap_slots() == scope_info->ContextLength() ||
126          (scope->num_heap_slots() == kVariablePartIndex &&
127           scope_info->ContextLength() == 0));
128   return scope_info;
129 }
130 
131 
Empty(Isolate * isolate)132 ScopeInfo* ScopeInfo::Empty(Isolate* isolate) {
133   return reinterpret_cast<ScopeInfo*>(isolate->heap()->empty_fixed_array());
134 }
135 
136 
scope_type()137 ScopeType ScopeInfo::scope_type() {
138   DCHECK(length() > 0);
139   return ScopeTypeField::decode(Flags());
140 }
141 
142 
CallsEval()143 bool ScopeInfo::CallsEval() {
144   return length() > 0 && CallsEvalField::decode(Flags());
145 }
146 
147 
strict_mode()148 StrictMode ScopeInfo::strict_mode() {
149   return length() > 0 ? StrictModeField::decode(Flags()) : SLOPPY;
150 }
151 
152 
LocalCount()153 int ScopeInfo::LocalCount() {
154   return StackLocalCount() + ContextLocalCount();
155 }
156 
157 
StackSlotCount()158 int ScopeInfo::StackSlotCount() {
159   if (length() > 0) {
160     bool function_name_stack_slot =
161         FunctionVariableField::decode(Flags()) == STACK;
162     return StackLocalCount() + (function_name_stack_slot ? 1 : 0);
163   }
164   return 0;
165 }
166 
167 
ContextLength()168 int ScopeInfo::ContextLength() {
169   if (length() > 0) {
170     int context_locals = ContextLocalCount();
171     bool function_name_context_slot =
172         FunctionVariableField::decode(Flags()) == CONTEXT;
173     bool has_context = context_locals > 0 ||
174         function_name_context_slot ||
175         scope_type() == WITH_SCOPE ||
176         (scope_type() == FUNCTION_SCOPE && CallsEval()) ||
177         scope_type() == MODULE_SCOPE;
178     if (has_context) {
179       return Context::MIN_CONTEXT_SLOTS + context_locals +
180           (function_name_context_slot ? 1 : 0);
181     }
182   }
183   return 0;
184 }
185 
186 
HasFunctionName()187 bool ScopeInfo::HasFunctionName() {
188   if (length() > 0) {
189     return NONE != FunctionVariableField::decode(Flags());
190   } else {
191     return false;
192   }
193 }
194 
195 
HasHeapAllocatedLocals()196 bool ScopeInfo::HasHeapAllocatedLocals() {
197   if (length() > 0) {
198     return ContextLocalCount() > 0;
199   } else {
200     return false;
201   }
202 }
203 
204 
HasContext()205 bool ScopeInfo::HasContext() {
206   return ContextLength() > 0;
207 }
208 
209 
FunctionName()210 String* ScopeInfo::FunctionName() {
211   DCHECK(HasFunctionName());
212   return String::cast(get(FunctionNameEntryIndex()));
213 }
214 
215 
ParameterName(int var)216 String* ScopeInfo::ParameterName(int var) {
217   DCHECK(0 <= var && var < ParameterCount());
218   int info_index = ParameterEntriesIndex() + var;
219   return String::cast(get(info_index));
220 }
221 
222 
LocalName(int var)223 String* ScopeInfo::LocalName(int var) {
224   DCHECK(0 <= var && var < LocalCount());
225   DCHECK(StackLocalEntriesIndex() + StackLocalCount() ==
226          ContextLocalNameEntriesIndex());
227   int info_index = StackLocalEntriesIndex() + var;
228   return String::cast(get(info_index));
229 }
230 
231 
StackLocalName(int var)232 String* ScopeInfo::StackLocalName(int var) {
233   DCHECK(0 <= var && var < StackLocalCount());
234   int info_index = StackLocalEntriesIndex() + var;
235   return String::cast(get(info_index));
236 }
237 
238 
ContextLocalName(int var)239 String* ScopeInfo::ContextLocalName(int var) {
240   DCHECK(0 <= var && var < ContextLocalCount());
241   int info_index = ContextLocalNameEntriesIndex() + var;
242   return String::cast(get(info_index));
243 }
244 
245 
ContextLocalMode(int var)246 VariableMode ScopeInfo::ContextLocalMode(int var) {
247   DCHECK(0 <= var && var < ContextLocalCount());
248   int info_index = ContextLocalInfoEntriesIndex() + var;
249   int value = Smi::cast(get(info_index))->value();
250   return ContextLocalMode::decode(value);
251 }
252 
253 
ContextLocalInitFlag(int var)254 InitializationFlag ScopeInfo::ContextLocalInitFlag(int var) {
255   DCHECK(0 <= var && var < ContextLocalCount());
256   int info_index = ContextLocalInfoEntriesIndex() + var;
257   int value = Smi::cast(get(info_index))->value();
258   return ContextLocalInitFlag::decode(value);
259 }
260 
261 
ContextLocalMaybeAssignedFlag(int var)262 MaybeAssignedFlag ScopeInfo::ContextLocalMaybeAssignedFlag(int var) {
263   DCHECK(0 <= var && var < ContextLocalCount());
264   int info_index = ContextLocalInfoEntriesIndex() + var;
265   int value = Smi::cast(get(info_index))->value();
266   return ContextLocalMaybeAssignedFlag::decode(value);
267 }
268 
269 
LocalIsSynthetic(int var)270 bool ScopeInfo::LocalIsSynthetic(int var) {
271   DCHECK(0 <= var && var < LocalCount());
272   // There's currently no flag stored on the ScopeInfo to indicate that a
273   // variable is a compiler-introduced temporary. However, to avoid conflict
274   // with user declarations, the current temporaries like .generator_object and
275   // .result start with a dot, so we can use that as a flag. It's a hack!
276   Handle<String> name(LocalName(var));
277   return name->length() > 0 && name->Get(0) == '.';
278 }
279 
280 
StackSlotIndex(String * name)281 int ScopeInfo::StackSlotIndex(String* name) {
282   DCHECK(name->IsInternalizedString());
283   if (length() > 0) {
284     int start = StackLocalEntriesIndex();
285     int end = StackLocalEntriesIndex() + StackLocalCount();
286     for (int i = start; i < end; ++i) {
287       if (name == get(i)) {
288         return i - start;
289       }
290     }
291   }
292   return -1;
293 }
294 
295 
ContextSlotIndex(Handle<ScopeInfo> scope_info,Handle<String> name,VariableMode * mode,InitializationFlag * init_flag,MaybeAssignedFlag * maybe_assigned_flag)296 int ScopeInfo::ContextSlotIndex(Handle<ScopeInfo> scope_info,
297                                 Handle<String> name, VariableMode* mode,
298                                 InitializationFlag* init_flag,
299                                 MaybeAssignedFlag* maybe_assigned_flag) {
300   DCHECK(name->IsInternalizedString());
301   DCHECK(mode != NULL);
302   DCHECK(init_flag != NULL);
303   if (scope_info->length() > 0) {
304     ContextSlotCache* context_slot_cache =
305         scope_info->GetIsolate()->context_slot_cache();
306     int result = context_slot_cache->Lookup(*scope_info, *name, mode, init_flag,
307                                             maybe_assigned_flag);
308     if (result != ContextSlotCache::kNotFound) {
309       DCHECK(result < scope_info->ContextLength());
310       return result;
311     }
312 
313     int start = scope_info->ContextLocalNameEntriesIndex();
314     int end = scope_info->ContextLocalNameEntriesIndex() +
315         scope_info->ContextLocalCount();
316     for (int i = start; i < end; ++i) {
317       if (*name == scope_info->get(i)) {
318         int var = i - start;
319         *mode = scope_info->ContextLocalMode(var);
320         *init_flag = scope_info->ContextLocalInitFlag(var);
321         *maybe_assigned_flag = scope_info->ContextLocalMaybeAssignedFlag(var);
322         result = Context::MIN_CONTEXT_SLOTS + var;
323         context_slot_cache->Update(scope_info, name, *mode, *init_flag,
324                                    *maybe_assigned_flag, result);
325         DCHECK(result < scope_info->ContextLength());
326         return result;
327       }
328     }
329     // Cache as not found. Mode, init flag and maybe assigned flag don't matter.
330     context_slot_cache->Update(scope_info, name, INTERNAL, kNeedsInitialization,
331                                kNotAssigned, -1);
332   }
333   return -1;
334 }
335 
336 
ParameterIndex(String * name)337 int ScopeInfo::ParameterIndex(String* name) {
338   DCHECK(name->IsInternalizedString());
339   if (length() > 0) {
340     // We must read parameters from the end since for
341     // multiply declared parameters the value of the
342     // last declaration of that parameter is used
343     // inside a function (and thus we need to look
344     // at the last index). Was bug# 1110337.
345     int start = ParameterEntriesIndex();
346     int end = ParameterEntriesIndex() + ParameterCount();
347     for (int i = end - 1; i >= start; --i) {
348       if (name == get(i)) {
349         return i - start;
350       }
351     }
352   }
353   return -1;
354 }
355 
356 
FunctionContextSlotIndex(String * name,VariableMode * mode)357 int ScopeInfo::FunctionContextSlotIndex(String* name, VariableMode* mode) {
358   DCHECK(name->IsInternalizedString());
359   DCHECK(mode != NULL);
360   if (length() > 0) {
361     if (FunctionVariableField::decode(Flags()) == CONTEXT &&
362         FunctionName() == name) {
363       *mode = FunctionVariableMode::decode(Flags());
364       return Smi::cast(get(FunctionNameEntryIndex() + 1))->value();
365     }
366   }
367   return -1;
368 }
369 
370 
CopyContextLocalsToScopeObject(Handle<ScopeInfo> scope_info,Handle<Context> context,Handle<JSObject> scope_object)371 bool ScopeInfo::CopyContextLocalsToScopeObject(Handle<ScopeInfo> scope_info,
372                                                Handle<Context> context,
373                                                Handle<JSObject> scope_object) {
374   Isolate* isolate = scope_info->GetIsolate();
375   int local_count = scope_info->ContextLocalCount();
376   if (local_count == 0) return true;
377   // Fill all context locals to the context extension.
378   int first_context_var = scope_info->StackLocalCount();
379   int start = scope_info->ContextLocalNameEntriesIndex();
380   for (int i = 0; i < local_count; ++i) {
381     if (scope_info->LocalIsSynthetic(first_context_var + i)) continue;
382     int context_index = Context::MIN_CONTEXT_SLOTS + i;
383     RETURN_ON_EXCEPTION_VALUE(
384         isolate,
385         Runtime::DefineObjectProperty(
386             scope_object,
387             Handle<String>(String::cast(scope_info->get(i + start))),
388             Handle<Object>(context->get(context_index), isolate),
389             ::NONE),
390         false);
391   }
392   return true;
393 }
394 
395 
ParameterEntriesIndex()396 int ScopeInfo::ParameterEntriesIndex() {
397   DCHECK(length() > 0);
398   return kVariablePartIndex;
399 }
400 
401 
StackLocalEntriesIndex()402 int ScopeInfo::StackLocalEntriesIndex() {
403   return ParameterEntriesIndex() + ParameterCount();
404 }
405 
406 
ContextLocalNameEntriesIndex()407 int ScopeInfo::ContextLocalNameEntriesIndex() {
408   return StackLocalEntriesIndex() + StackLocalCount();
409 }
410 
411 
ContextLocalInfoEntriesIndex()412 int ScopeInfo::ContextLocalInfoEntriesIndex() {
413   return ContextLocalNameEntriesIndex() + ContextLocalCount();
414 }
415 
416 
FunctionNameEntryIndex()417 int ScopeInfo::FunctionNameEntryIndex() {
418   return ContextLocalInfoEntriesIndex() + ContextLocalCount();
419 }
420 
421 
Hash(Object * data,String * name)422 int ContextSlotCache::Hash(Object* data, String* name) {
423   // Uses only lower 32 bits if pointers are larger.
424   uintptr_t addr_hash =
425       static_cast<uint32_t>(reinterpret_cast<uintptr_t>(data)) >> 2;
426   return static_cast<int>((addr_hash ^ name->Hash()) % kLength);
427 }
428 
429 
Lookup(Object * data,String * name,VariableMode * mode,InitializationFlag * init_flag,MaybeAssignedFlag * maybe_assigned_flag)430 int ContextSlotCache::Lookup(Object* data, String* name, VariableMode* mode,
431                              InitializationFlag* init_flag,
432                              MaybeAssignedFlag* maybe_assigned_flag) {
433   int index = Hash(data, name);
434   Key& key = keys_[index];
435   if ((key.data == data) && key.name->Equals(name)) {
436     Value result(values_[index]);
437     if (mode != NULL) *mode = result.mode();
438     if (init_flag != NULL) *init_flag = result.initialization_flag();
439     if (maybe_assigned_flag != NULL)
440       *maybe_assigned_flag = result.maybe_assigned_flag();
441     return result.index() + kNotFound;
442   }
443   return kNotFound;
444 }
445 
446 
Update(Handle<Object> data,Handle<String> name,VariableMode mode,InitializationFlag init_flag,MaybeAssignedFlag maybe_assigned_flag,int slot_index)447 void ContextSlotCache::Update(Handle<Object> data, Handle<String> name,
448                               VariableMode mode, InitializationFlag init_flag,
449                               MaybeAssignedFlag maybe_assigned_flag,
450                               int slot_index) {
451   DisallowHeapAllocation no_gc;
452   Handle<String> internalized_name;
453   DCHECK(slot_index > kNotFound);
454   if (StringTable::InternalizeStringIfExists(name->GetIsolate(), name).
455       ToHandle(&internalized_name)) {
456     int index = Hash(*data, *internalized_name);
457     Key& key = keys_[index];
458     key.data = *data;
459     key.name = *internalized_name;
460     // Please note value only takes a uint as index.
461     values_[index] = Value(mode, init_flag, maybe_assigned_flag,
462                            slot_index - kNotFound).raw();
463 #ifdef DEBUG
464     ValidateEntry(data, name, mode, init_flag, maybe_assigned_flag, slot_index);
465 #endif
466   }
467 }
468 
469 
Clear()470 void ContextSlotCache::Clear() {
471   for (int index = 0; index < kLength; index++) keys_[index].data = NULL;
472 }
473 
474 
475 #ifdef DEBUG
476 
ValidateEntry(Handle<Object> data,Handle<String> name,VariableMode mode,InitializationFlag init_flag,MaybeAssignedFlag maybe_assigned_flag,int slot_index)477 void ContextSlotCache::ValidateEntry(Handle<Object> data, Handle<String> name,
478                                      VariableMode mode,
479                                      InitializationFlag init_flag,
480                                      MaybeAssignedFlag maybe_assigned_flag,
481                                      int slot_index) {
482   DisallowHeapAllocation no_gc;
483   Handle<String> internalized_name;
484   if (StringTable::InternalizeStringIfExists(name->GetIsolate(), name).
485       ToHandle(&internalized_name)) {
486     int index = Hash(*data, *name);
487     Key& key = keys_[index];
488     DCHECK(key.data == *data);
489     DCHECK(key.name->Equals(*name));
490     Value result(values_[index]);
491     DCHECK(result.mode() == mode);
492     DCHECK(result.initialization_flag() == init_flag);
493     DCHECK(result.maybe_assigned_flag() == maybe_assigned_flag);
494     DCHECK(result.index() + kNotFound == slot_index);
495   }
496 }
497 
498 
PrintList(const char * list_name,int nof_internal_slots,int start,int end,ScopeInfo * scope_info)499 static void PrintList(const char* list_name,
500                       int nof_internal_slots,
501                       int start,
502                       int end,
503                       ScopeInfo* scope_info) {
504   if (start < end) {
505     PrintF("\n  // %s\n", list_name);
506     if (nof_internal_slots > 0) {
507       PrintF("  %2d - %2d [internal slots]\n", 0 , nof_internal_slots - 1);
508     }
509     for (int i = nof_internal_slots; start < end; ++i, ++start) {
510       PrintF("  %2d ", i);
511       String::cast(scope_info->get(start))->ShortPrint();
512       PrintF("\n");
513     }
514   }
515 }
516 
517 
Print()518 void ScopeInfo::Print() {
519   PrintF("ScopeInfo ");
520   if (HasFunctionName()) {
521     FunctionName()->ShortPrint();
522   } else {
523     PrintF("/* no function name */");
524   }
525   PrintF("{");
526 
527   PrintList("parameters", 0,
528             ParameterEntriesIndex(),
529             ParameterEntriesIndex() + ParameterCount(),
530             this);
531   PrintList("stack slots", 0,
532             StackLocalEntriesIndex(),
533             StackLocalEntriesIndex() + StackLocalCount(),
534             this);
535   PrintList("context slots",
536             Context::MIN_CONTEXT_SLOTS,
537             ContextLocalNameEntriesIndex(),
538             ContextLocalNameEntriesIndex() + ContextLocalCount(),
539             this);
540 
541   PrintF("}\n");
542 }
543 #endif  // DEBUG
544 
545 
546 //---------------------------------------------------------------------------
547 // ModuleInfo.
548 
Create(Isolate * isolate,Interface * interface,Scope * scope)549 Handle<ModuleInfo> ModuleInfo::Create(
550     Isolate* isolate, Interface* interface, Scope* scope) {
551   Handle<ModuleInfo> info = Allocate(isolate, interface->Length());
552   info->set_host_index(interface->Index());
553   int i = 0;
554   for (Interface::Iterator it = interface->iterator();
555        !it.done(); it.Advance(), ++i) {
556     Variable* var = scope->LookupLocal(it.name());
557     info->set_name(i, *(it.name()->string()));
558     info->set_mode(i, var->mode());
559     DCHECK((var->mode() == MODULE) == (it.interface()->IsModule()));
560     if (var->mode() == MODULE) {
561       DCHECK(it.interface()->IsFrozen());
562       DCHECK(it.interface()->Index() >= 0);
563       info->set_index(i, it.interface()->Index());
564     } else {
565       DCHECK(var->index() >= 0);
566       info->set_index(i, var->index());
567     }
568   }
569   DCHECK(i == info->length());
570   return info;
571 }
572 
573 } }  // namespace v8::internal
574