// Copyright 2006-2008 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following // disclaimer in the documentation and/or other materials provided // with the distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived // from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include "v8.h" #include "scopeinfo.h" #include "scopes.h" namespace v8 { namespace internal { static int CompareLocal(Variable* const* v, Variable* const* w) { Slot* s = (*v)->slot(); Slot* t = (*w)->slot(); // We may have rewritten parameters (that are in the arguments object) // and which may have a NULL slot... - find a better solution... int x = (s != NULL ? s->index() : 0); int y = (t != NULL ? t->index() : 0); // Consider sorting them according to type as well? return x - y; } template ScopeInfo::ScopeInfo(Scope* scope) : function_name_(Factory::empty_symbol()), calls_eval_(scope->calls_eval()), parameters_(scope->num_parameters()), stack_slots_(scope->num_stack_slots()), context_slots_(scope->num_heap_slots()), context_modes_(scope->num_heap_slots()) { // Add parameters. for (int i = 0; i < scope->num_parameters(); i++) { ASSERT(parameters_.length() == i); parameters_.Add(scope->parameter(i)->name()); } // Add stack locals and collect heap locals. // We are assuming that the locals' slots are allocated in // increasing order, so we can simply add them to the // ScopeInfo lists. However, due to usage analysis, this is // not true for context-allocated locals: Some of them // may be parameters which are allocated before the // non-parameter locals. When the non-parameter locals are // sorted according to usage, the allocated slot indices may // not be in increasing order with the variable list anymore. // Thus, we first collect the context-allocated locals, and then // sort them by context slot index before adding them to the // ScopeInfo list. List locals(32); // 32 is a wild guess ASSERT(locals.is_empty()); scope->CollectUsedVariables(&locals); locals.Sort(&CompareLocal); List heap_locals(locals.length()); for (int i = 0; i < locals.length(); i++) { Variable* var = locals[i]; if (var->is_used()) { Slot* slot = var->slot(); if (slot != NULL) { switch (slot->type()) { case Slot::PARAMETER: // explicitly added to parameters_ above - ignore break; case Slot::LOCAL: ASSERT(stack_slots_.length() == slot->index()); stack_slots_.Add(var->name()); break; case Slot::CONTEXT: heap_locals.Add(var); break; case Slot::LOOKUP: // This is currently not used. UNREACHABLE(); break; } } } } // Add heap locals. if (scope->num_heap_slots() > 0) { // Add user-defined slots. for (int i = 0; i < heap_locals.length(); i++) { ASSERT(heap_locals[i]->slot()->index() - Context::MIN_CONTEXT_SLOTS == context_slots_.length()); ASSERT(heap_locals[i]->slot()->index() - Context::MIN_CONTEXT_SLOTS == context_modes_.length()); context_slots_.Add(heap_locals[i]->name()); context_modes_.Add(heap_locals[i]->mode()); } } else { ASSERT(heap_locals.length() == 0); } // Add the function context slot, if present. // For now, this must happen at the very end because of the // ordering of the scope info slots and the respective slot indices. if (scope->is_function_scope()) { Variable* var = scope->function(); if (var != NULL && var->is_used() && var->slot()->type() == Slot::CONTEXT) { function_name_ = var->name(); // Note that we must not find the function name in the context slot // list - instead it must be handled separately in the // Contexts::Lookup() function. Thus record an empty symbol here so we // get the correct number of context slots. ASSERT(var->slot()->index() - Context::MIN_CONTEXT_SLOTS == context_slots_.length()); ASSERT(var->slot()->index() - Context::MIN_CONTEXT_SLOTS == context_modes_.length()); context_slots_.Add(Factory::empty_symbol()); context_modes_.Add(Variable::INTERNAL); } } } // Encoding format in a FixedArray object: // // - function name // // - number of variables in the context object (smi) (= function context // slot index + 1) // - list of pairs (name, Var mode) of context-allocated variables (starting // with context slot 0) // - NULL (sentinel) // // - number of parameters (smi) // - list of parameter names (starting with parameter 0 first) // - NULL (sentinel) // // - number of variables on the stack (smi) // - list of names of stack-allocated variables (starting with stack slot 0) // - NULL (sentinel) // The ScopeInfo representation could be simplified and the ScopeInfo // re-implemented (with almost the same interface). Here is a // suggestion for the new format: // // - have a single list with all variable names (parameters, stack locals, // context locals), followed by a list of non-Object* values containing // the variables information (what kind, index, attributes) // - searching the linear list of names is fast and yields an index into the // list if the variable name is found // - that list index is then used to find the variable information in the // subsequent list // - the list entries don't have to be in any particular order, so all the // current sorting business can go away // - the ScopeInfo lookup routines can be reduced to perhaps a single lookup // which returns all information at once // - when gathering the information from a Scope, we only need to iterate // through the local variables (parameters and context info is already // present) static inline Object** ReadInt(Object** p, int* x) { *x = (reinterpret_cast(*p++))->value(); return p; } static inline Object** ReadBool(Object** p, bool* x) { *x = (reinterpret_cast(*p++))->value() != 0; return p; } static inline Object** ReadSymbol(Object** p, Handle* s) { *s = Handle(reinterpret_cast(*p++)); return p; } template static Object** ReadList(Object** p, List, Allocator >* list) { ASSERT(list->is_empty()); int n; p = ReadInt(p, &n); while (n-- > 0) { Handle s; p = ReadSymbol(p, &s); list->Add(s); } return p; } template static Object** ReadList(Object** p, List, Allocator>* list, List* modes) { ASSERT(list->is_empty()); int n; p = ReadInt(p, &n); while (n-- > 0) { Handle s; int m; p = ReadSymbol(p, &s); p = ReadInt(p, &m); list->Add(s); modes->Add(static_cast(m)); } return p; } template ScopeInfo::ScopeInfo(SerializedScopeInfo* data) : function_name_(Factory::empty_symbol()), parameters_(4), stack_slots_(8), context_slots_(8), context_modes_(8) { if (data->length() > 0) { Object** p0 = data->data_start(); Object** p = p0; p = ReadSymbol(p, &function_name_); p = ReadBool(p, &calls_eval_); p = ReadList(p, &context_slots_, &context_modes_); p = ReadList(p, ¶meters_); p = ReadList(p, &stack_slots_); ASSERT((p - p0) == FixedArray::cast(data)->length()); } } static inline Object** WriteInt(Object** p, int x) { *p++ = Smi::FromInt(x); return p; } static inline Object** WriteBool(Object** p, bool b) { *p++ = Smi::FromInt(b ? 1 : 0); return p; } static inline Object** WriteSymbol(Object** p, Handle s) { *p++ = *s; return p; } template static Object** WriteList(Object** p, List, Allocator >* list) { const int n = list->length(); p = WriteInt(p, n); for (int i = 0; i < n; i++) { p = WriteSymbol(p, list->at(i)); } return p; } template static Object** WriteList(Object** p, List, Allocator>* list, List* modes) { const int n = list->length(); p = WriteInt(p, n); for (int i = 0; i < n; i++) { p = WriteSymbol(p, list->at(i)); p = WriteInt(p, modes->at(i)); } return p; } template Handle ScopeInfo::Serialize() { // function name, calls eval, length for 3 tables: const int extra_slots = 1 + 1 + 3; int length = extra_slots + context_slots_.length() * 2 + parameters_.length() + stack_slots_.length(); Handle data( SerializedScopeInfo::cast(*Factory::NewFixedArray(length, TENURED))); AssertNoAllocation nogc; Object** p0 = data->data_start(); Object** p = p0; p = WriteSymbol(p, function_name_); p = WriteBool(p, calls_eval_); p = WriteList(p, &context_slots_, &context_modes_); p = WriteList(p, ¶meters_); p = WriteList(p, &stack_slots_); ASSERT((p - p0) == length); return data; } template Handle ScopeInfo::LocalName(int i) const { // A local variable can be allocated either on the stack or in the context. // For variables allocated in the context they are always preceded by // Context::MIN_CONTEXT_SLOTS of fixed allocated slots in the context. if (i < number_of_stack_slots()) { return stack_slot_name(i); } else { return context_slot_name(i - number_of_stack_slots() + Context::MIN_CONTEXT_SLOTS); } } template int ScopeInfo::NumberOfLocals() const { int number_of_locals = number_of_stack_slots(); if (number_of_context_slots() > 0) { ASSERT(number_of_context_slots() >= Context::MIN_CONTEXT_SLOTS); number_of_locals += number_of_context_slots() - Context::MIN_CONTEXT_SLOTS; } return number_of_locals; } Handle SerializedScopeInfo::Create(Scope* scope) { ScopeInfo sinfo(scope); return sinfo.Serialize(); } SerializedScopeInfo* SerializedScopeInfo::Empty() { return reinterpret_cast(Heap::empty_fixed_array()); } Object** SerializedScopeInfo::ContextEntriesAddr() { ASSERT(length() > 0); return data_start() + 2; // +2 for function name and calls eval. } Object** SerializedScopeInfo::ParameterEntriesAddr() { ASSERT(length() > 0); Object** p = ContextEntriesAddr(); int number_of_context_slots; p = ReadInt(p, &number_of_context_slots); return p + number_of_context_slots*2; // *2 for pairs } Object** SerializedScopeInfo::StackSlotEntriesAddr() { ASSERT(length() > 0); Object** p = ParameterEntriesAddr(); int number_of_parameter_slots; p = ReadInt(p, &number_of_parameter_slots); return p + number_of_parameter_slots; } bool SerializedScopeInfo::CallsEval() { if (length() > 0) { Object** p = data_start() + 1; // +1 for function name. bool calls_eval; p = ReadBool(p, &calls_eval); return calls_eval; } return true; } int SerializedScopeInfo::NumberOfStackSlots() { if (length() > 0) { Object** p = StackSlotEntriesAddr(); int number_of_stack_slots; ReadInt(p, &number_of_stack_slots); return number_of_stack_slots; } return 0; } int SerializedScopeInfo::NumberOfContextSlots() { if (length() > 0) { Object** p = ContextEntriesAddr(); int number_of_context_slots; ReadInt(p, &number_of_context_slots); return number_of_context_slots + Context::MIN_CONTEXT_SLOTS; } return 0; } bool SerializedScopeInfo::HasHeapAllocatedLocals() { if (length() > 0) { Object** p = ContextEntriesAddr(); int number_of_context_slots; ReadInt(p, &number_of_context_slots); return number_of_context_slots > 0; } return false; } int SerializedScopeInfo::StackSlotIndex(String* name) { ASSERT(name->IsSymbol()); if (length() > 0) { // Slots start after length entry. Object** p0 = StackSlotEntriesAddr(); int number_of_stack_slots; p0 = ReadInt(p0, &number_of_stack_slots); Object** p = p0; Object** end = p0 + number_of_stack_slots; while (p != end) { if (*p == name) return static_cast(p - p0); p++; } } return -1; } int SerializedScopeInfo::ContextSlotIndex(String* name, Variable::Mode* mode) { ASSERT(name->IsSymbol()); int result = ContextSlotCache::Lookup(this, name, mode); if (result != ContextSlotCache::kNotFound) return result; if (length() > 0) { // Slots start after length entry. Object** p0 = ContextEntriesAddr(); int number_of_context_slots; p0 = ReadInt(p0, &number_of_context_slots); Object** p = p0; Object** end = p0 + number_of_context_slots * 2; while (p != end) { if (*p == name) { ASSERT(((p - p0) & 1) == 0); int v; ReadInt(p + 1, &v); Variable::Mode mode_value = static_cast(v); if (mode != NULL) *mode = mode_value; result = static_cast((p - p0) >> 1) + Context::MIN_CONTEXT_SLOTS; ContextSlotCache::Update(this, name, mode_value, result); return result; } p += 2; } } ContextSlotCache::Update(this, name, Variable::INTERNAL, -1); return -1; } int SerializedScopeInfo::ParameterIndex(String* name) { ASSERT(name->IsSymbol()); if (length() > 0) { // We must read parameters from the end since for // multiply declared parameters the value of the // last declaration of that parameter is used // inside a function (and thus we need to look // at the last index). Was bug# 1110337. // // Eventually, we should only register such parameters // once, with corresponding index. This requires a new // implementation of the ScopeInfo code. See also other // comments in this file regarding this. Object** p = ParameterEntriesAddr(); int number_of_parameter_slots; Object** p0 = ReadInt(p, &number_of_parameter_slots); p = p0 + number_of_parameter_slots; while (p > p0) { p--; if (*p == name) return static_cast(p - p0); } } return -1; } int SerializedScopeInfo::FunctionContextSlotIndex(String* name) { ASSERT(name->IsSymbol()); if (length() > 0) { Object** p = data_start(); if (*p == name) { p = ContextEntriesAddr(); int number_of_context_slots; ReadInt(p, &number_of_context_slots); ASSERT(number_of_context_slots != 0); // The function context slot is the last entry. return number_of_context_slots + Context::MIN_CONTEXT_SLOTS - 1; } } return -1; } int ContextSlotCache::Hash(Object* data, String* name) { // Uses only lower 32 bits if pointers are larger. uintptr_t addr_hash = static_cast(reinterpret_cast(data)) >> 2; return static_cast((addr_hash ^ name->Hash()) % kLength); } int ContextSlotCache::Lookup(Object* data, String* name, Variable::Mode* mode) { int index = Hash(data, name); Key& key = keys_[index]; if ((key.data == data) && key.name->Equals(name)) { Value result(values_[index]); if (mode != NULL) *mode = result.mode(); return result.index() + kNotFound; } return kNotFound; } void ContextSlotCache::Update(Object* data, String* name, Variable::Mode mode, int slot_index) { String* symbol; ASSERT(slot_index > kNotFound); if (Heap::LookupSymbolIfExists(name, &symbol)) { int index = Hash(data, symbol); Key& key = keys_[index]; key.data = data; key.name = symbol; // Please note value only takes a uint as index. values_[index] = Value(mode, slot_index - kNotFound).raw(); #ifdef DEBUG ValidateEntry(data, name, mode, slot_index); #endif } } void ContextSlotCache::Clear() { for (int index = 0; index < kLength; index++) keys_[index].data = NULL; } ContextSlotCache::Key ContextSlotCache::keys_[ContextSlotCache::kLength]; uint32_t ContextSlotCache::values_[ContextSlotCache::kLength]; #ifdef DEBUG void ContextSlotCache::ValidateEntry(Object* data, String* name, Variable::Mode mode, int slot_index) { String* symbol; if (Heap::LookupSymbolIfExists(name, &symbol)) { int index = Hash(data, name); Key& key = keys_[index]; ASSERT(key.data == data); ASSERT(key.name->Equals(name)); Value result(values_[index]); ASSERT(result.mode() == mode); ASSERT(result.index() + kNotFound == slot_index); } } template static void PrintList(const char* list_name, int nof_internal_slots, List, Allocator>& list) { if (list.length() > 0) { PrintF("\n // %s\n", list_name); if (nof_internal_slots > 0) { PrintF(" %2d - %2d [internal slots]\n", 0 , nof_internal_slots - 1); } for (int i = 0; i < list.length(); i++) { PrintF(" %2d ", i + nof_internal_slots); list[i]->ShortPrint(); PrintF("\n"); } } } template void ScopeInfo::Print() { PrintF("ScopeInfo "); if (function_name_->length() > 0) function_name_->ShortPrint(); else PrintF("/* no function name */"); PrintF("{"); PrintList("parameters", 0, parameters_); PrintList("stack slots", 0, stack_slots_); PrintList("context slots", Context::MIN_CONTEXT_SLOTS, context_slots_); PrintF("}\n"); } #endif // DEBUG // Make sure the classes get instantiated by the template system. template class ScopeInfo; template class ScopeInfo; template class ScopeInfo; } } // namespace v8::internal