|
|
|
// 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.
|
|
|
|
|
|
|
|
#ifndef V8_PROPERTY_H_
|
|
|
|
#define V8_PROPERTY_H_
|
|
|
|
|
|
|
|
namespace v8 {
|
|
|
|
namespace internal {
|
|
|
|
|
|
|
|
|
|
|
|
// Abstraction for elements in instance-descriptor arrays.
|
|
|
|
//
|
|
|
|
// Each descriptor has a key, property attributes, property type,
|
|
|
|
// property index (in the actual instance-descriptor array) and
|
|
|
|
// optionally a piece of data.
|
|
|
|
//
|
|
|
|
|
|
|
|
class Descriptor BASE_EMBEDDED {
|
|
|
|
public:
|
|
|
|
static int IndexFromValue(Object* value) {
|
|
|
|
return Smi::cast(value)->value();
|
|
|
|
}
|
|
|
|
|
|
|
|
Object* KeyToSymbol() {
|
|
|
|
if (!StringShape(key_).IsSymbol()) {
|
|
|
|
Object* result = Heap::LookupSymbol(key_);
|
|
|
|
if (result->IsFailure()) return result;
|
|
|
|
key_ = String::cast(result);
|
|
|
|
}
|
|
|
|
return key_;
|
|
|
|
}
|
|
|
|
|
|
|
|
String* GetKey() { return key_; }
|
|
|
|
Object* GetValue() { return value_; }
|
|
|
|
PropertyDetails GetDetails() { return details_; }
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
void Print();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
void SetEnumerationIndex(int index) {
|
|
|
|
ASSERT(PropertyDetails::IsValidIndex(index));
|
|
|
|
details_ = PropertyDetails(details_.attributes(), details_.type(), index);
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
String* key_;
|
|
|
|
Object* value_;
|
|
|
|
PropertyDetails details_;
|
|
|
|
|
|
|
|
protected:
|
|
|
|
Descriptor() : details_(Smi::FromInt(0)) {}
|
|
|
|
|
|
|
|
void Init(String* key, Object* value, PropertyDetails details) {
|
|
|
|
key_ = key;
|
|
|
|
value_ = value;
|
|
|
|
details_ = details;
|
|
|
|
}
|
|
|
|
|
|
|
|
Descriptor(String* key, Object* value, PropertyDetails details)
|
|
|
|
: key_(key),
|
|
|
|
value_(value),
|
|
|
|
details_(details) { }
|
|
|
|
|
|
|
|
Descriptor(String* key,
|
|
|
|
Object* value,
|
|
|
|
PropertyAttributes attributes,
|
|
|
|
PropertyType type,
|
|
|
|
int index = 0)
|
|
|
|
: key_(key),
|
|
|
|
value_(value),
|
|
|
|
details_(attributes, type, index) { }
|
|
|
|
|
|
|
|
friend class DescriptorWriter;
|
|
|
|
friend class DescriptorReader;
|
|
|
|
friend class DescriptorArray;
|
|
|
|
};
|
|
|
|
|
|
|
|
// A pointer from a map to the new map that is created by adding
|
|
|
|
// a named property. These are key to the speed and functioning of V8.
|
|
|
|
// The two maps should always have the same prototype, since
|
|
|
|
// MapSpace::CreateBackPointers depends on this.
|
|
|
|
class MapTransitionDescriptor: public Descriptor {
|
|
|
|
public:
|
|
|
|
MapTransitionDescriptor(String* key, Map* map, PropertyAttributes attributes)
|
|
|
|
: Descriptor(key, map, attributes, MAP_TRANSITION) { }
|
|
|
|
};
|
|
|
|
|
|
|
|
// Marks a field name in a map so that adding the field is guaranteed
|
|
|
|
// to create a FIELD descriptor in the new map. Used after adding
|
|
|
|
// a constant function the first time, creating a CONSTANT_FUNCTION
|
|
|
|
// descriptor in the new map. This avoids creating multiple maps with
|
|
|
|
// the same CONSTANT_FUNCTION field.
|
|
|
|
class ConstTransitionDescriptor: public Descriptor {
|
|
|
|
public:
|
|
|
|
explicit ConstTransitionDescriptor(String* key)
|
|
|
|
: Descriptor(key, Smi::FromInt(0), NONE, CONSTANT_TRANSITION) { }
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
class FieldDescriptor: public Descriptor {
|
|
|
|
public:
|
|
|
|
FieldDescriptor(String* key,
|
|
|
|
int field_index,
|
|
|
|
PropertyAttributes attributes,
|
|
|
|
int index = 0)
|
|
|
|
: Descriptor(key, Smi::FromInt(field_index), attributes, FIELD, index) {}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
class ConstantFunctionDescriptor: public Descriptor {
|
|
|
|
public:
|
|
|
|
ConstantFunctionDescriptor(String* key,
|
|
|
|
JSFunction* function,
|
|
|
|
PropertyAttributes attributes,
|
|
|
|
int index = 0)
|
|
|
|
: Descriptor(key, function, attributes, CONSTANT_FUNCTION, index) {}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
class CallbacksDescriptor: public Descriptor {
|
|
|
|
public:
|
|
|
|
CallbacksDescriptor(String* key,
|
|
|
|
Object* proxy,
|
|
|
|
PropertyAttributes attributes,
|
|
|
|
int index = 0)
|
|
|
|
: Descriptor(key, proxy, attributes, CALLBACKS, index) {}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
class LookupResult BASE_EMBEDDED {
|
|
|
|
public:
|
|
|
|
// Where did we find the result;
|
|
|
|
enum {
|
|
|
|
NOT_FOUND,
|
|
|
|
DESCRIPTOR_TYPE,
|
|
|
|
DICTIONARY_TYPE,
|
|
|
|
INTERCEPTOR_TYPE,
|
|
|
|
CONSTANT_TYPE
|
|
|
|
} lookup_type_;
|
|
|
|
|
|
|
|
LookupResult()
|
|
|
|
: lookup_type_(NOT_FOUND),
|
|
|
|
cacheable_(true),
|
|
|
|
details_(NONE, NORMAL) {}
|
|
|
|
|
|
|
|
void DescriptorResult(JSObject* holder, PropertyDetails details, int number) {
|
|
|
|
lookup_type_ = DESCRIPTOR_TYPE;
|
|
|
|
holder_ = holder;
|
|
|
|
details_ = details;
|
|
|
|
number_ = number;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ConstantResult(JSObject* holder) {
|
|
|
|
lookup_type_ = CONSTANT_TYPE;
|
|
|
|
holder_ = holder;
|
|
|
|
details_ =
|
|
|
|
PropertyDetails(static_cast<PropertyAttributes>(DONT_ENUM |
|
|
|
|
DONT_DELETE),
|
|
|
|
CALLBACKS);
|
|
|
|
number_ = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void DictionaryResult(JSObject* holder, int entry) {
|
|
|
|
lookup_type_ = DICTIONARY_TYPE;
|
|
|
|
holder_ = holder;
|
|
|
|
details_ = holder->property_dictionary()->DetailsAt(entry);
|
|
|
|
number_ = entry;
|
|
|
|
}
|
|
|
|
|
|
|
|
void InterceptorResult(JSObject* holder) {
|
|
|
|
lookup_type_ = INTERCEPTOR_TYPE;
|
|
|
|
holder_ = holder;
|
|
|
|
details_ = PropertyDetails(NONE, INTERCEPTOR);
|
|
|
|
}
|
|
|
|
|
|
|
|
void NotFound() {
|
|
|
|
lookup_type_ = NOT_FOUND;
|
|
|
|
}
|
|
|
|
|
|
|
|
JSObject* holder() {
|
|
|
|
ASSERT(IsValid());
|
|
|
|
return holder_;
|
|
|
|
}
|
|
|
|
|
|
|
|
PropertyType type() {
|
|
|
|
ASSERT(IsValid());
|
|
|
|
return details_.type();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool IsTransitionType() {
|
|
|
|
PropertyType t = type();
|
|
|
|
if (t == MAP_TRANSITION || t == CONSTANT_TRANSITION) return true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
PropertyAttributes GetAttributes() {
|
|
|
|
ASSERT(IsValid());
|
|
|
|
return details_.attributes();
|
|
|
|
}
|
|
|
|
|
|
|
|
PropertyDetails GetPropertyDetails() {
|
|
|
|
return details_;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool IsReadOnly() { return details_.IsReadOnly(); }
|
|
|
|
bool IsDontDelete() { return details_.IsDontDelete(); }
|
|
|
|
bool IsDontEnum() { return details_.IsDontEnum(); }
|
|
|
|
|
|
|
|
bool IsValid() { return lookup_type_ != NOT_FOUND; }
|
|
|
|
bool IsNotFound() { return lookup_type_ == NOT_FOUND; }
|
|
|
|
|
|
|
|
// Tells whether the result is a property.
|
|
|
|
// Excluding transitions and the null descriptor.
|
|
|
|
bool IsProperty() {
|
|
|
|
return IsValid() && type() < FIRST_PHANTOM_PROPERTY_TYPE;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool IsCacheable() { return cacheable_; }
|
|
|
|
void DisallowCaching() { cacheable_ = false; }
|
|
|
|
|
|
|
|
// Tells whether the value needs to be loaded.
|
|
|
|
bool IsLoaded() {
|
|
|
|
if (lookup_type_ == DESCRIPTOR_TYPE || lookup_type_ == DICTIONARY_TYPE) {
|
|
|
|
Object* target = GetLazyValue();
|
|
|
|
return !target->IsJSObject() || JSObject::cast(target)->IsLoaded();
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
Object* GetLazyValue() {
|
|
|
|
switch (type()) {
|
|
|
|
case FIELD:
|
|
|
|
return holder()->FastPropertyAt(GetFieldIndex());
|
|
|
|
case NORMAL:
|
|
|
|
return holder()->property_dictionary()->ValueAt(GetDictionaryEntry());
|
|
|
|
case CONSTANT_FUNCTION:
|
|
|
|
return GetConstantFunction();
|
|
|
|
default:
|
|
|
|
return Smi::FromInt(0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Map* GetTransitionMap() {
|
|
|
|
ASSERT(lookup_type_ == DESCRIPTOR_TYPE);
|
|
|
|
ASSERT(type() == MAP_TRANSITION);
|
|
|
|
return Map::cast(GetValue());
|
|
|
|
}
|
|
|
|
|
|
|
|
int GetFieldIndex() {
|
|
|
|
ASSERT(lookup_type_ == DESCRIPTOR_TYPE);
|
|
|
|
ASSERT(type() == FIELD);
|
|
|
|
return Descriptor::IndexFromValue(GetValue());
|
|
|
|
}
|
|
|
|
|
|
|
|
int GetDictionaryEntry() {
|
|
|
|
ASSERT(lookup_type_ == DICTIONARY_TYPE);
|
|
|
|
return number_;
|
|
|
|
}
|
|
|
|
|
|
|
|
JSFunction* GetConstantFunction() {
|
|
|
|
ASSERT(type() == CONSTANT_FUNCTION);
|
|
|
|
return JSFunction::cast(GetValue());
|
|
|
|
}
|
|
|
|
|
|
|
|
Object* GetCallbackObject() {
|
|
|
|
if (lookup_type_ == CONSTANT_TYPE) {
|
|
|
|
// For now we only have the __proto__ as constant type.
|
|
|
|
return Heap::prototype_accessors();
|
|
|
|
}
|
|
|
|
return GetValue();
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
void Print();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
Object* GetValue() {
|
|
|
|
if (lookup_type_ == DESCRIPTOR_TYPE) {
|
|
|
|
DescriptorArray* descriptors = holder()->map()->instance_descriptors();
|
|
|
|
return descriptors->GetValue(number_);
|
|
|
|
}
|
|
|
|
// In the dictionary case, the data is held in the value field.
|
|
|
|
ASSERT(lookup_type_ == DICTIONARY_TYPE);
|
|
|
|
return holder()->property_dictionary()->ValueAt(GetDictionaryEntry());
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
JSObject* holder_;
|
|
|
|
int number_;
|
|
|
|
bool cacheable_;
|
|
|
|
PropertyDetails details_;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// The DescriptorStream is an abstraction for iterating over a map's
|
|
|
|
// instance descriptors.
|
|
|
|
class DescriptorStream BASE_EMBEDDED {
|
|
|
|
public:
|
|
|
|
explicit DescriptorStream(DescriptorArray* descriptors, int pos) {
|
|
|
|
descriptors_ = descriptors;
|
|
|
|
pos_ = pos;
|
|
|
|
limit_ = descriptors_->number_of_descriptors();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Tells whether we have reached the end of the steam.
|
|
|
|
bool eos() { return pos_ >= limit_; }
|
|
|
|
|
|
|
|
int next_position() { return pos_ + 1; }
|
|
|
|
void advance() { pos_ = next_position(); }
|
|
|
|
|
|
|
|
protected:
|
|
|
|
DescriptorArray* descriptors_;
|
|
|
|
int pos_; // Current position.
|
|
|
|
int limit_; // Limit for position.
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
class DescriptorReader: public DescriptorStream {
|
|
|
|
public:
|
|
|
|
explicit DescriptorReader(DescriptorArray* descriptors, int pos = 0)
|
|
|
|
: DescriptorStream(descriptors, pos) {}
|
|
|
|
|
|
|
|
String* GetKey() { return descriptors_->GetKey(pos_); }
|
|
|
|
Object* GetValue() { return descriptors_->GetValue(pos_); }
|
|
|
|
PropertyDetails GetDetails() {
|
|
|
|
return PropertyDetails(descriptors_->GetDetails(pos_));
|
|
|
|
}
|
|
|
|
|
|
|
|
int GetFieldIndex() { return Descriptor::IndexFromValue(GetValue()); }
|
|
|
|
|
|
|
|
bool IsDontEnum() { return GetDetails().IsDontEnum(); }
|
|
|
|
|
|
|
|
PropertyType type() { return GetDetails().type(); }
|
|
|
|
|
|
|
|
// Tells whether the type is a transition.
|
|
|
|
bool IsTransition() {
|
|
|
|
PropertyType t = type();
|
|
|
|
ASSERT(t != INTERCEPTOR);
|
|
|
|
return t == MAP_TRANSITION || t == CONSTANT_TRANSITION;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool IsNullDescriptor() {
|
|
|
|
return type() == NULL_DESCRIPTOR;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool IsProperty() {
|
|
|
|
return type() < FIRST_PHANTOM_PROPERTY_TYPE;
|
|
|
|
}
|
|
|
|
|
|
|
|
JSFunction* GetConstantFunction() { return JSFunction::cast(GetValue()); }
|
|
|
|
|
|
|
|
AccessorDescriptor* GetCallbacks() {
|
|
|
|
ASSERT(type() == CALLBACKS);
|
|
|
|
Proxy* p = Proxy::cast(GetCallbacksObject());
|
|
|
|
return reinterpret_cast<AccessorDescriptor*>(p->proxy());
|
|
|
|
}
|
|
|
|
|
|
|
|
Object* GetCallbacksObject() {
|
|
|
|
ASSERT(type() == CALLBACKS);
|
|
|
|
return GetValue();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Equals(String* name) { return name->Equals(GetKey()); }
|
|
|
|
|
|
|
|
void Get(Descriptor* desc) {
|
|
|
|
descriptors_->Get(pos_, desc);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
class DescriptorWriter: public DescriptorStream {
|
|
|
|
public:
|
|
|
|
explicit DescriptorWriter(DescriptorArray* descriptors)
|
|
|
|
: DescriptorStream(descriptors, 0) {}
|
|
|
|
|
|
|
|
// Append a descriptor to this stream.
|
|
|
|
void Write(Descriptor* desc);
|
|
|
|
// Read a descriptor from the reader and append it to this stream.
|
|
|
|
void WriteFrom(DescriptorReader* reader);
|
|
|
|
};
|
|
|
|
|
|
|
|
} } // namespace v8::internal
|
|
|
|
|
|
|
|
#endif // V8_PROPERTY_H_
|