blob: e06235af76f712d46b2f87d6cd9508badfe3a3ad [file] [log] [blame]
// 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 <stdlib.h>
#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)->AsSlot();
Slot* t = (*w)->AsSlot();
// 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<class Allocator>
ScopeInfo<Allocator>::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<Variable*, Allocator> locals(32); // 32 is a wild guess
ASSERT(locals.is_empty());
scope->CollectUsedVariables(&locals);
locals.Sort(&CompareLocal);
List<Variable*, Allocator> heap_locals(locals.length());
for (int i = 0; i < locals.length(); i++) {
Variable* var = locals[i];
if (var->is_used()) {
Slot* slot = var->AsSlot();
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]->AsSlot()->index() - Context::MIN_CONTEXT_SLOTS ==
context_slots_.length());
ASSERT(heap_locals[i]->AsSlot()->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->AsSlot()->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->AsSlot()->index() - Context::MIN_CONTEXT_SLOTS ==
context_slots_.length());
ASSERT(var->AsSlot()->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
//
// - calls eval boolean flag
//
// - 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)
//
// - number of parameters (smi)
// - list of parameter names (starting with parameter 0 first)
//
// - number of variables on the stack (smi)
// - list of names of stack-allocated variables (starting with stack slot 0)
// 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<Smi*>(*p++))->value();
return p;
}
static inline Object** ReadBool(Object** p, bool* x) {
*x = (reinterpret_cast<Smi*>(*p++))->value() != 0;
return p;
}
static inline Object** ReadSymbol(Object** p, Handle<String>* s) {
*s = Handle<String>(reinterpret_cast<String*>(*p++));
return p;
}
template <class Allocator>
static Object** ReadList(Object** p, List<Handle<String>, Allocator >* list) {
ASSERT(list->is_empty());
int n;
p = ReadInt(p, &n);
while (n-- > 0) {
Handle<String> s;
p = ReadSymbol(p, &s);
list->Add(s);
}
return p;
}
template <class Allocator>
static Object** ReadList(Object** p,
List<Handle<String>, Allocator>* list,
List<Variable::Mode, Allocator>* modes) {
ASSERT(list->is_empty());
int n;
p = ReadInt(p, &n);
while (n-- > 0) {
Handle<String> s;
int m;
p = ReadSymbol(p, &s);
p = ReadInt(p, &m);
list->Add(s);
modes->Add(static_cast<Variable::Mode>(m));
}
return p;
}
template<class Allocator>
ScopeInfo<Allocator>::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<Allocator>(p, &context_slots_, &context_modes_);
p = ReadList<Allocator>(p, &parameters_);
p = ReadList<Allocator>(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<String> s) {
*p++ = *s;
return p;
}
template <class Allocator>
static Object** WriteList(Object** p, List<Handle<String>, 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 <class Allocator>
static Object** WriteList(Object** p,
List<Handle<String>, Allocator>* list,
List<Variable::Mode, Allocator>* 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<class Allocator>
Handle<SerializedScopeInfo> ScopeInfo<Allocator>::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<SerializedScopeInfo> 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, &parameters_);
p = WriteList(p, &stack_slots_);
ASSERT((p - p0) == length);
return data;
}
template<class Allocator>
Handle<String> ScopeInfo<Allocator>::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<class Allocator>
int ScopeInfo<Allocator>::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> SerializedScopeInfo::Create(Scope* scope) {
ScopeInfo<ZoneListAllocationPolicy> sinfo(scope);
return sinfo.Serialize();
}
SerializedScopeInfo* SerializedScopeInfo::Empty() {
return reinterpret_cast<SerializedScopeInfo*>(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<int>(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<Variable::Mode>(v);
if (mode != NULL) *mode = mode_value;
result = static_cast<int>((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<int>(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<uint32_t>(reinterpret_cast<uintptr_t>(data)) >> 2;
return static_cast<int>((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 <class Allocator>
static void PrintList(const char* list_name,
int nof_internal_slots,
List<Handle<String>, 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<class Allocator>
void ScopeInfo<Allocator>::Print() {
PrintF("ScopeInfo ");
if (function_name_->length() > 0)
function_name_->ShortPrint();
else
PrintF("/* no function name */");
PrintF("{");
PrintList<Allocator>("parameters", 0, parameters_);
PrintList<Allocator>("stack slots", 0, stack_slots_);
PrintList<Allocator>("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<FreeStoreAllocationPolicy>;
template class ScopeInfo<PreallocatedStorage>;
template class ScopeInfo<ZoneListAllocationPolicy>;
} } // namespace v8::internal