Analyze functions for assignment to this properties.

During parsing functions are analyzed for statements of the form this.x = ...;. These assignments are categorized in two types: simple and non simple. The simple ones are where the right hand side is known to be either a constant or an argument to the function. If a function only contains statements of this type the property names are collected and for the simple assignments the index of the argument or the constant value assigned are stored as well.

When the initial map for a function is created and the function consists of only this type of assignemnts the initial map is created with a descriptor array describing these properties which will be known to always exist in an object created from the function.

The information on this property assignments is not collected during pre-parsing so if compiling using pre-parse data these optimization hints are not available.

Next step will be to use the information collected for the simple assignments to generate constructor code which will create and initialize the object from this information without calling the code for the function.
Review URL: http://codereview.chromium.org/172088

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@2710 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
sgjesse@chromium.org 2009-08-19 07:30:20 +00:00
parent 03a39152b9
commit 4e03645dc4
13 changed files with 547 additions and 40 deletions

View File

@ -111,6 +111,7 @@ AST_NODE_LIST(DEF_FORWARD_DECLARATION)
// Typedef only introduced to avoid unreadable code.
// Please do appreciate the required space in "> >".
typedef ZoneList<Handle<String> > ZoneStringList;
typedef ZoneList<Handle<Object> > ZoneObjectList;
class AstNode: public ZoneObject {
@ -1226,6 +1227,9 @@ class FunctionLiteral: public Expression {
int materialized_literal_count,
bool contains_array_literal,
int expected_property_count,
bool has_only_this_property_assignments,
bool has_only_simple_this_property_assignments,
Handle<FixedArray> this_property_assignments,
int num_parameters,
int start_position,
int end_position,
@ -1236,6 +1240,10 @@ class FunctionLiteral: public Expression {
materialized_literal_count_(materialized_literal_count),
contains_array_literal_(contains_array_literal),
expected_property_count_(expected_property_count),
has_only_this_property_assignments_(has_only_this_property_assignments),
has_only_simple_this_property_assignments_(
has_only_simple_this_property_assignments),
this_property_assignments_(this_property_assignments),
num_parameters_(num_parameters),
start_position_(start_position),
end_position_(end_position),
@ -1265,6 +1273,15 @@ class FunctionLiteral: public Expression {
int materialized_literal_count() { return materialized_literal_count_; }
bool contains_array_literal() { return contains_array_literal_; }
int expected_property_count() { return expected_property_count_; }
bool has_only_this_property_assignments() {
return has_only_this_property_assignments_;
}
bool has_only_simple_this_property_assignments() {
return has_only_simple_this_property_assignments_;
}
Handle<FixedArray> this_property_assignments() {
return this_property_assignments_;
}
int num_parameters() { return num_parameters_; }
bool AllowsLazyCompilation();
@ -1291,6 +1308,9 @@ class FunctionLiteral: public Expression {
int materialized_literal_count_;
bool contains_array_literal_;
int expected_property_count_;
bool has_only_this_property_assignments_;
bool has_only_simple_this_property_assignments_;
Handle<FixedArray> this_property_assignments_;
int num_parameters_;
int start_position_;
int end_position_;

View File

@ -255,6 +255,10 @@ void CodeGenerator::SetFunctionInfo(Handle<JSFunction> fun,
fun->shared()->set_is_expression(lit->is_expression());
fun->shared()->set_is_toplevel(is_toplevel);
fun->shared()->set_inferred_name(*lit->inferred_name());
fun->shared()->SetThisPropertyAssignmentsInfo(
lit->has_only_this_property_assignments(),
lit->has_only_simple_this_property_assignments(),
*lit->this_property_assignments());
}

View File

@ -1054,6 +1054,7 @@ Object* Heap::AllocateMap(InstanceType instance_type, int instance_size) {
map->set_constructor(null_value());
map->set_instance_size(instance_size);
map->set_inobject_properties(0);
map->set_pre_allocated_property_fields(0);
map->set_instance_descriptors(empty_descriptor_array());
map->set_code_cache(empty_fixed_array());
map->set_unused_property_fields(0);
@ -1611,6 +1612,9 @@ Object* Heap::AllocateSharedFunctionInfo(Object* name) {
share->set_start_position_and_type(0);
share->set_debug_info(undefined_value());
share->set_inferred_name(empty_string());
share->set_compiler_hints(0);
share->set_this_property_assignments_count(0);
share->set_this_property_assignments(undefined_value());
return result;
}
@ -2050,16 +2054,10 @@ Object* Heap::AllocateArgumentsObject(Object* callee, int length) {
Object* Heap::AllocateInitialMap(JSFunction* fun) {
ASSERT(!fun->has_initial_map());
// First create a new map with the expected number of properties being
// allocated in-object.
int expected_nof_properties = fun->shared()->expected_nof_properties();
int instance_size = JSObject::kHeaderSize +
expected_nof_properties * kPointerSize;
if (instance_size > JSObject::kMaxInstanceSize) {
instance_size = JSObject::kMaxInstanceSize;
expected_nof_properties = (instance_size - JSObject::kHeaderSize) /
kPointerSize;
}
// First create a new map with the size and number of in-object properties
// suggested by the function.
int instance_size = fun->shared()->CalculateInstanceSize();
int in_object_properties = fun->shared()->CalculateInObjectProperties();
Object* map_obj = Heap::AllocateMap(JS_OBJECT_TYPE, instance_size);
if (map_obj->IsFailure()) return map_obj;
@ -2072,9 +2070,33 @@ Object* Heap::AllocateInitialMap(JSFunction* fun) {
if (prototype->IsFailure()) return prototype;
}
Map* map = Map::cast(map_obj);
map->set_inobject_properties(expected_nof_properties);
map->set_unused_property_fields(expected_nof_properties);
map->set_inobject_properties(in_object_properties);
map->set_unused_property_fields(in_object_properties);
map->set_prototype(prototype);
// If the function has only simple this property assignments add field
// descriptors for these to the initial map as the object cannot be
// constructed without having these properties.
ASSERT(in_object_properties <= Map::kMaxPreAllocatedPropertyFields);
if (fun->shared()->has_only_this_property_assignments() &&
fun->shared()->this_property_assignments_count() > 0) {
int count = fun->shared()->this_property_assignments_count();
if (count > in_object_properties) {
count = in_object_properties;
}
DescriptorArray* descriptors = *Factory::NewDescriptorArray(count);
if (descriptors->IsFailure()) return descriptors;
for (int i = 0; i < count; i++) {
String* name = fun->shared()->GetThisPropertyAssignmentName(i);
ASSERT(name->IsSymbol());
FieldDescriptor field(name, i, NONE);
descriptors->Set(i, &field);
}
descriptors->Sort();
map->set_instance_descriptors(descriptors);
map->set_pre_allocated_property_fields(count);
map->set_unused_property_fields(in_object_properties - count);
}
return map;
}
@ -2106,7 +2128,11 @@ Object* Heap::AllocateJSObjectFromMap(Map* map, PretenureFlag pretenure) {
ASSERT(map->instance_type() != JS_BUILTINS_OBJECT_TYPE);
// Allocate the backing storage for the properties.
int prop_size = map->unused_property_fields() - map->inobject_properties();
int prop_size =
map->pre_allocated_property_fields() +
map->unused_property_fields() -
map->inobject_properties();
ASSERT(prop_size >= 0);
Object* properties = AllocateFixedArray(prop_size, pretenure);
if (properties->IsFailure()) return properties;
@ -2599,6 +2625,7 @@ Object* Heap::CopyFixedArray(FixedArray* src) {
Object* Heap::AllocateFixedArray(int length) {
ASSERT(length >= 0);
if (length == 0) return empty_fixed_array();
Object* result = AllocateRawFixedArray(length);
if (!result->IsFailure()) {

View File

@ -180,12 +180,16 @@ void Builtins::Generate_JSConstructStubGeneric(MacroAssembler* masm) {
// eax: initial map
// ebx: JSObject
// edi: start of next object
// Calculate the total number of properties described by the map.
__ movzx_b(edx, FieldOperand(eax, Map::kUnusedPropertyFieldsOffset));
__ movzx_b(ecx, FieldOperand(eax, Map::kInObjectPropertiesOffset));
__ movzx_b(ecx, FieldOperand(eax, Map::kPreAllocatedPropertyFieldsOffset));
__ add(edx, Operand(ecx));
// Calculate unused properties past the end of the in-object properties.
__ movzx_b(ecx, FieldOperand(eax, Map::kInObjectPropertiesOffset));
__ sub(edx, Operand(ecx));
// Done if no extra properties are to be allocated.
__ j(zero, &allocated);
__ Assert(positive, "Property allocation count failed.");
// Scale the number of elements by pointer size and add the header for
// FixedArrays to the start of the next object calculation from above.
@ -321,6 +325,7 @@ void Builtins::Generate_JSConstructStubGeneric(MacroAssembler* masm) {
__ pop(ecx);
__ lea(esp, Operand(esp, ebx, times_2, 1 * kPointerSize)); // 1 ~ receiver
__ push(ecx);
__ IncrementCounter(&Counters::constructed_objects, 1);
__ ret(0);
}

View File

@ -371,9 +371,9 @@ void JSObject::JSObjectVerify() {
VerifyHeapPointer(properties());
VerifyHeapPointer(elements());
if (HasFastProperties()) {
CHECK(map()->unused_property_fields() ==
(map()->inobject_properties() + properties()->length() -
map()->NextFreePropertyIndex()));
CHECK_EQ(map()->unused_property_fields(),
(map()->inobject_properties() + properties()->length() -
map()->NextFreePropertyIndex()));
}
}
@ -462,6 +462,7 @@ void Map::MapPrint() {
HeapObject::PrintHeader("Map");
PrintF(" - type: %s\n", TypeToString(instance_type()));
PrintF(" - instance size: %d\n", instance_size());
PrintF(" - inobject properties: %d\n", inobject_properties());
PrintF(" - unused property fields: %d\n", unused_property_fields());
if (is_hidden_prototype()) {
PrintF(" - hidden_prototype\n");
@ -619,6 +620,12 @@ void SharedFunctionInfo::SharedFunctionInfoPrint() {
PrintF("\n - debug info = ");
debug_info()->ShortPrint();
PrintF("\n - length = %d", length());
PrintF("\n - has_only_this_property_assignments = %d",
has_only_this_property_assignments());
PrintF("\n - has_only_simple_this_property_assignments = %d",
has_only_simple_this_property_assignments());
PrintF("\n - this_property_assignments = ");
this_property_assignments()->ShortPrint();
PrintF("\n");
}

View File

@ -91,7 +91,13 @@ PropertyDetails PropertyDetails::AsDeleted() {
}
#define BOOL_ACCESSORS(holder, field, name, offset) \
#define BOOL_GETTER(holder, field, name, offset) \
bool holder::name() { \
return BooleanBit::get(field(), offset); \
} \
#define BOOL_ACCESSORS(holder, field, name, offset) \
bool holder::name() { \
return BooleanBit::get(field(), offset); \
} \
@ -1937,6 +1943,11 @@ int Map::inobject_properties() {
}
int Map::pre_allocated_property_fields() {
return READ_BYTE_FIELD(this, kPreAllocatedPropertyFieldsOffset);
}
int HeapObject::SizeFromMap(Map* map) {
InstanceType instance_type = map->instance_type();
// Only inline the most frequent cases.
@ -1969,6 +1980,14 @@ void Map::set_inobject_properties(int value) {
}
void Map::set_pre_allocated_property_fields(int value) {
ASSERT(0 <= value && value < 256);
WRITE_BYTE_FIELD(this,
kPreAllocatedPropertyFieldsOffset,
static_cast<byte>(value));
}
InstanceType Map::instance_type() {
return static_cast<InstanceType>(READ_BYTE_FIELD(this, kInstanceTypeOffset));
}
@ -2298,6 +2317,8 @@ ACCESSORS(SharedFunctionInfo, function_data, Object,
ACCESSORS(SharedFunctionInfo, script, Object, kScriptOffset)
ACCESSORS(SharedFunctionInfo, debug_info, Object, kDebugInfoOffset)
ACCESSORS(SharedFunctionInfo, inferred_name, String, kInferredNameOffset)
ACCESSORS(SharedFunctionInfo, this_property_assignments, Object,
kThisPropertyAssignmentsOffset)
BOOL_ACCESSORS(FunctionTemplateInfo, flag, hidden_prototype,
kHiddenPrototypeBit)
@ -2308,6 +2329,13 @@ BOOL_ACCESSORS(SharedFunctionInfo, start_position_and_type, is_expression,
kIsExpressionBit)
BOOL_ACCESSORS(SharedFunctionInfo, start_position_and_type, is_toplevel,
kIsTopLevelBit)
BOOL_GETTER(SharedFunctionInfo, compiler_hints,
has_only_this_property_assignments,
kHasOnlyThisPropertyAssignments)
BOOL_GETTER(SharedFunctionInfo, compiler_hints,
has_only_simple_this_property_assignments,
kHasOnlySimpleThisPropertyAssignments)
INT_ACCESSORS(SharedFunctionInfo, length, kLengthOffset)
INT_ACCESSORS(SharedFunctionInfo, formal_parameter_count,
@ -2319,6 +2347,10 @@ INT_ACCESSORS(SharedFunctionInfo, start_position_and_type,
INT_ACCESSORS(SharedFunctionInfo, end_position, kEndPositionOffset)
INT_ACCESSORS(SharedFunctionInfo, function_token_position,
kFunctionTokenPositionOffset)
INT_ACCESSORS(SharedFunctionInfo, compiler_hints,
kCompilerHintsOffset)
INT_ACCESSORS(SharedFunctionInfo, this_property_assignments_count,
kThisPropertyAssignmentsCountOffset)
void SharedFunctionInfo::DontAdaptArguments() {

View File

@ -4780,6 +4780,48 @@ Object* SharedFunctionInfo::GetSourceCode() {
}
int SharedFunctionInfo::CalculateInstanceSize() {
int instance_size =
JSObject::kHeaderSize +
expected_nof_properties() * kPointerSize;
if (instance_size > JSObject::kMaxInstanceSize) {
instance_size = JSObject::kMaxInstanceSize;
}
return instance_size;
}
int SharedFunctionInfo::CalculateInObjectProperties() {
return (CalculateInstanceSize() - JSObject::kHeaderSize) / kPointerSize;
}
void SharedFunctionInfo::SetThisPropertyAssignmentsInfo(
bool only_this_property_assignments,
bool only_simple_this_property_assignments,
FixedArray* assignments) {
ASSERT(this_property_assignments()->IsUndefined());
set_compiler_hints(BooleanBit::set(compiler_hints(),
kHasOnlyThisPropertyAssignments,
only_this_property_assignments));
set_compiler_hints(BooleanBit::set(compiler_hints(),
kHasOnlySimpleThisPropertyAssignments,
only_simple_this_property_assignments));
set_this_property_assignments(assignments);
set_this_property_assignments_count(assignments->length() / 3);
}
String* SharedFunctionInfo::GetThisPropertyAssignmentName(int index) {
Object* obj = this_property_assignments();
ASSERT(obj->IsFixedArray());
ASSERT(index < this_property_assignments_count());
obj = FixedArray::cast(obj)->get(index * 3);
ASSERT(obj->IsString());
return String::cast(obj);
}
// Support function for printing the source code to a StringStream
// without any allocation in the heap.
void SharedFunctionInfo::SourceCodePrint(StringStream* accumulator,
@ -4826,6 +4868,8 @@ void SharedFunctionInfo::SharedFunctionInfoIterateBody(ObjectVisitor* v) {
IteratePointers(v, kNameOffset, kConstructStubOffset + kPointerSize);
IteratePointers(v, kInstanceClassNameOffset, kScriptOffset + kPointerSize);
IteratePointers(v, kDebugInfoOffset, kInferredNameOffset + kPointerSize);
IteratePointers(v, kThisPropertyAssignmentsOffset,
kThisPropertyAssignmentsOffset + kPointerSize);
}

View File

@ -2704,6 +2704,10 @@ class Map: public HeapObject {
inline int inobject_properties();
inline void set_inobject_properties(int value);
// Count of property fields pre-allocated in the object when first allocated.
inline int pre_allocated_property_fields();
inline void set_pre_allocated_property_fields(int value);
// Instance type.
inline InstanceType instance_type();
inline void set_instance_type(InstanceType value);
@ -2869,6 +2873,8 @@ class Map: public HeapObject {
void MapVerify();
#endif
static const int kMaxPreAllocatedPropertyFields = 255;
// Layout description.
static const int kInstanceSizesOffset = HeapObject::kHeaderSize;
static const int kInstanceAttributesOffset = kInstanceSizesOffset + kIntSize;
@ -2882,7 +2888,8 @@ class Map: public HeapObject {
// Byte offsets within kInstanceSizesOffset.
static const int kInstanceSizeOffset = kInstanceSizesOffset + 0;
static const int kInObjectPropertiesOffset = kInstanceSizesOffset + 1;
// The bytes at positions 2 and 3 are not in use at the moment.
static const int kPreAllocatedPropertyFieldsOffset = kInstanceSizesOffset + 2;
// The byte at position 3 is not in use at the moment.
// Byte offsets within kInstanceAttributesOffset attributes.
static const int kInstanceTypeOffset = kInstanceAttributesOffset + 0;
@ -3090,10 +3097,42 @@ class SharedFunctionInfo: public HeapObject {
inline bool is_toplevel();
inline void set_is_toplevel(bool value);
// Bit field containing various information collected by the compiler to
// drive optimization.
inline int compiler_hints();
inline void set_compiler_hints(int value);
// Add information on assignments of the form this.x = ...;
void SetThisPropertyAssignmentsInfo(
bool has_only_this_property_assignments,
bool has_only_simple_this_property_assignments,
FixedArray* this_property_assignments);
// Indicate that this function only consists of assignments of the form
// this.x = ...;.
inline bool has_only_this_property_assignments();
// Indicate that this function only consists of assignments of the form
// this.x = y; where y is either a constant or refers to an argument.
inline bool has_only_simple_this_property_assignments();
// For functions which only contains this property assignments this provides
// access to the names for the properties assigned.
DECL_ACCESSORS(this_property_assignments, Object)
inline int this_property_assignments_count();
inline void set_this_property_assignments_count(int value);
String* GetThisPropertyAssignmentName(int index);
// [source code]: Source code for the function.
bool HasSourceCode();
Object* GetSourceCode();
// Calculate the instance size.
int CalculateInstanceSize();
// Calculate the number of in-object properties.
int CalculateInObjectProperties();
// Dispatched behavior.
void SharedFunctionInfoIterateBody(ObjectVisitor* v);
// Set max_length to -1 for unlimited length.
@ -3129,7 +3168,12 @@ class SharedFunctionInfo: public HeapObject {
static const int kScriptOffset = kExternalReferenceDataOffset + kPointerSize;
static const int kDebugInfoOffset = kScriptOffset + kPointerSize;
static const int kInferredNameOffset = kDebugInfoOffset + kPointerSize;
static const int kSize = kInferredNameOffset + kPointerSize;
static const int kCompilerHintsOffset = kInferredNameOffset + kPointerSize;
static const int kThisPropertyAssignmentsOffset =
kCompilerHintsOffset + kPointerSize;
static const int kThisPropertyAssignmentsCountOffset =
kThisPropertyAssignmentsOffset + kPointerSize;
static const int kSize = kThisPropertyAssignmentsCountOffset + kPointerSize;
private:
// Bit positions in length_and_flg.
@ -3146,6 +3190,10 @@ class SharedFunctionInfo: public HeapObject {
static const int kStartPositionShift = 2;
static const int kStartPositionMask = ~((1 << kStartPositionShift) - 1);
// Bit positions in compiler_hints.
static const int kHasOnlyThisPropertyAssignments = 0;
static const int kHasOnlySimpleThisPropertyAssignments = 1;
DISALLOW_IMPLICIT_CONSTRUCTORS(SharedFunctionInfo);
};

View File

@ -678,6 +678,25 @@ class TemporaryScope BASE_EMBEDDED {
void set_contains_array_literal() { contains_array_literal_ = true; }
bool contains_array_literal() { return contains_array_literal_; }
void SetThisPropertyAssignmentInfo(
bool only_this_property_assignments,
bool only_simple_this_property_assignments,
Handle<FixedArray> this_property_assignments) {
only_this_property_assignments_ = only_this_property_assignments;
only_simple_this_property_assignments_ =
only_simple_this_property_assignments;
this_property_assignments_ = this_property_assignments;
}
bool only_this_property_assignments() {
return only_this_property_assignments_;
}
bool only_simple_this_property_assignments() {
return only_simple_this_property_assignments_;
}
Handle<FixedArray> this_property_assignments() {
return this_property_assignments_;
}
void AddProperty() { expected_property_count_++; }
int expected_property_count() { return expected_property_count_; }
private:
@ -695,6 +714,10 @@ class TemporaryScope BASE_EMBEDDED {
// Properties count estimation.
int expected_property_count_;
bool only_this_property_assignments_;
bool only_simple_this_property_assignments_;
Handle<FixedArray> this_property_assignments_;
// Bookkeeping
Parser* parser_;
TemporaryScope* parent_;
@ -707,6 +730,9 @@ TemporaryScope::TemporaryScope(Parser* parser)
: materialized_literal_count_(0),
contains_array_literal_(false),
expected_property_count_(0),
only_this_property_assignments_(false),
only_simple_this_property_assignments_(false),
this_property_assignments_(Factory::empty_fixed_array()),
parser_(parser),
parent_(parser->temp_scope_) {
parser->temp_scope_ = this;
@ -1218,12 +1244,20 @@ FunctionLiteral* Parser::ParseProgram(Handle<String> source,
bool ok = true;
ParseSourceElements(&body, Token::EOS, &ok);
if (ok) {
result = NEW(FunctionLiteral(no_name, top_scope_,
body.elements(),
temp_scope.materialized_literal_count(),
temp_scope.contains_array_literal(),
temp_scope.expected_property_count(),
0, 0, source->length(), false));
result = NEW(FunctionLiteral(
no_name,
top_scope_,
body.elements(),
temp_scope.materialized_literal_count(),
temp_scope.contains_array_literal(),
temp_scope.expected_property_count(),
temp_scope.only_this_property_assignments(),
temp_scope.only_simple_this_property_assignments(),
temp_scope.this_property_assignments(),
0,
0,
source->length(),
false));
} else if (scanner().stack_overflow()) {
Top::StackOverflow();
}
@ -1314,9 +1348,23 @@ void PreParser::ReportMessageAt(Scanner::Location source_location,
}
// Base class containing common code for the different finder classes used by
// the parser.
class ParserFinder {
protected:
ParserFinder() {}
static Assignment* AsAssignment(Statement* stat) {
if (stat == NULL) return NULL;
ExpressionStatement* exp_stat = stat->AsExpressionStatement();
if (exp_stat == NULL) return NULL;
return exp_stat->expression()->AsAssignment();
}
};
// An InitializationBlockFinder finds and marks sequences of statements of the
// form x.y.z.a = ...; x.y.z.b = ...; etc.
class InitializationBlockFinder {
class InitializationBlockFinder : public ParserFinder {
public:
InitializationBlockFinder()
: first_in_block_(NULL), last_in_block_(NULL), block_size_(0) {}
@ -1341,13 +1389,6 @@ class InitializationBlockFinder {
}
private:
static Assignment* AsAssignment(Statement* stat) {
if (stat == NULL) return NULL;
ExpressionStatement* exp_stat = stat->AsExpressionStatement();
if (exp_stat == NULL) return NULL;
return exp_stat->expression()->AsAssignment();
}
// Returns true if the expressions appear to denote the same object.
// In the context of initialization blocks, we only consider expressions
// of the form 'x.y.z'.
@ -1418,6 +1459,161 @@ class InitializationBlockFinder {
};
// A ThisNamedPropertyAssigmentFinder finds and marks statements of the form
// this.x = ...;, where x is a named property. It also determines whether a
// function contains only assignments of this type.
class ThisNamedPropertyAssigmentFinder : public ParserFinder {
public:
ThisNamedPropertyAssigmentFinder()
: only_this_property_assignments_(true),
only_simple_this_property_assignments_(true),
names_(NULL),
assigned_arguments_(NULL),
assigned_constants_(NULL) {}
void Update(Scope* scope, Statement* stat) {
// Bail out if function already has non this property assignment
// statements.
if (!only_this_property_assignments_) {
return;
}
// Check whether this statement is of the form this.x = ...;
Assignment* assignment = AsAssignment(stat);
if (IsThisPropertyAssignment(assignment)) {
HandleThisPropertyAssignment(scope, assignment);
} else {
only_this_property_assignments_ = false;
only_simple_this_property_assignments_ = false;
}
}
// Returns whether only statements of the form this.x = ...; was encountered.
bool only_this_property_assignments() {
return only_this_property_assignments_;
}
// Returns whether only statements of the form this.x = y; where y is either a
// constant or a function argument was encountered.
bool only_simple_this_property_assignments() {
return only_simple_this_property_assignments_;
}
// Returns a fixed array containing three elements for each assignment of the
// form this.x = y;
Handle<FixedArray> GetThisPropertyAssignments() {
if (names_ == NULL) {
return Factory::empty_fixed_array();
}
ASSERT(names_ != NULL);
ASSERT(assigned_arguments_ != NULL);
ASSERT_EQ(names_->length(), assigned_arguments_->length());
ASSERT_EQ(names_->length(), assigned_constants_->length());
Handle<FixedArray> assignments =
Factory::NewFixedArray(names_->length() * 3);
for (int i = 0; i < names_->length(); i++) {
assignments->set(i * 3, *names_->at(i));
assignments->set(i * 3 + 1, Smi::FromInt(assigned_arguments_->at(i)));
assignments->set(i * 3 + 2, *assigned_constants_->at(i));
}
return assignments;
}
private:
bool IsThisPropertyAssignment(Assignment* assignment) {
if (assignment != NULL) {
Property* property = assignment->target()->AsProperty();
return assignment->op() == Token::ASSIGN
&& property != NULL
&& property->obj()->AsVariableProxy() != NULL
&& property->obj()->AsVariableProxy()->is_this();
}
return false;
}
void HandleThisPropertyAssignment(Scope* scope, Assignment* assignment) {
// Check that the property assigned to is a named property.
Property* property = assignment->target()->AsProperty();
ASSERT(property != NULL);
Literal* literal = property->key()->AsLiteral();
uint32_t dummy;
if (literal != NULL &&
literal->handle()->IsString() &&
!String::cast(*(literal->handle()))->AsArrayIndex(&dummy)) {
Handle<String> key = Handle<String>::cast(literal->handle());
// Check whether the value assigned is either a constant or matches the
// name of one of the arguments to the function.
if (assignment->value()->AsLiteral() != NULL) {
// Constant assigned.
Literal* literal = assignment->value()->AsLiteral();
AssignmentFromConstant(key, literal->handle());
} else if (assignment->value()->AsVariableProxy() != NULL) {
// Variable assigned.
Handle<String> name =
assignment->value()->AsVariableProxy()->name();
// Check whether the variable assigned matches an argument name.
int index = -1;
for (int i = 0; i < scope->num_parameters(); i++) {
if (*scope->parameter(i)->name() == *name) {
// Assigned from function argument.
index = i;
break;
}
}
if (index != -1) {
AssignmentFromParameter(key, index);
} else {
AssignmentFromSomethingElse(key);
}
} else {
AssignmentFromSomethingElse(key);
}
}
}
void AssignmentFromParameter(Handle<String> name, int index) {
EnsureAllocation();
names_->Add(name);
assigned_arguments_->Add(index);
assigned_constants_->Add(Factory::undefined_value());
}
void AssignmentFromConstant(Handle<String> name, Handle<Object> value) {
EnsureAllocation();
names_->Add(name);
assigned_arguments_->Add(-1);
assigned_constants_->Add(value);
}
void AssignmentFromSomethingElse(Handle<String> name) {
EnsureAllocation();
names_->Add(name);
assigned_arguments_->Add(-1);
assigned_constants_->Add(Factory::undefined_value());
// The this assignment is not a simple one.
only_simple_this_property_assignments_ = false;
}
void EnsureAllocation() {
if (names_ == NULL) {
ASSERT(assigned_arguments_ == NULL);
ASSERT(assigned_constants_ == NULL);
names_ = new ZoneStringList(4);
assigned_arguments_ = new ZoneList<int>(4);
assigned_constants_ = new ZoneObjectList(4);
}
}
bool only_this_property_assignments_;
bool only_simple_this_property_assignments_;
ZoneStringList* names_;
ZoneList<int>* assigned_arguments_;
ZoneObjectList* assigned_constants_;
};
void* Parser::ParseSourceElements(ZoneListWrapper<Statement>* processor,
int end_token,
bool* ok) {
@ -1432,15 +1628,33 @@ void* Parser::ParseSourceElements(ZoneListWrapper<Statement>* processor,
ASSERT(processor != NULL);
InitializationBlockFinder block_finder;
ThisNamedPropertyAssigmentFinder this_property_assignment_finder;
while (peek() != end_token) {
Statement* stat = ParseStatement(NULL, CHECK_OK);
if (stat == NULL || stat->IsEmpty()) continue;
// We find and mark the initialization blocks on top level code only.
// This is because the optimization prevents reuse of the map transitions,
// so it should be used only for code that will only be run once.
if (top_scope_->is_global_scope()) block_finder.Update(stat);
if (top_scope_->is_global_scope()) {
block_finder.Update(stat);
}
// Find and mark all assignments to named properties in this (this.x =)
if (top_scope_->is_function_scope()) {
this_property_assignment_finder.Update(top_scope_, stat);
}
processor->Add(stat);
}
// Propagate the collected information on this property assignments.
if (top_scope_->is_function_scope()) {
if (this_property_assignment_finder.only_this_property_assignments()) {
temp_scope_->SetThisPropertyAssignmentInfo(
this_property_assignment_finder.only_this_property_assignments(),
this_property_assignment_finder.
only_simple_this_property_assignments(),
this_property_assignment_finder.GetThisPropertyAssignments());
}
}
return 0;
}
@ -3507,6 +3721,9 @@ FunctionLiteral* Parser::ParseFunctionLiteral(Handle<String> var_name,
int materialized_literal_count;
int expected_property_count;
bool contains_array_literal;
bool only_this_property_assignments;
bool only_simple_this_property_assignments;
Handle<FixedArray> this_property_assignments;
if (is_lazily_compiled && pre_data() != NULL) {
FunctionEntry entry = pre_data()->GetFunctionEnd(start_pos);
int end_pos = entry.end_pos();
@ -3514,12 +3731,20 @@ FunctionLiteral* Parser::ParseFunctionLiteral(Handle<String> var_name,
scanner_.SeekForward(end_pos);
materialized_literal_count = entry.literal_count();
expected_property_count = entry.property_count();
only_this_property_assignments = false;
only_simple_this_property_assignments = false;
this_property_assignments = Factory::empty_fixed_array();
contains_array_literal = entry.contains_array_literal();
} else {
ParseSourceElements(&body, Token::RBRACE, CHECK_OK);
materialized_literal_count = temp_scope.materialized_literal_count();
expected_property_count = temp_scope.expected_property_count();
contains_array_literal = temp_scope.contains_array_literal();
only_this_property_assignments =
temp_scope.only_this_property_assignments();
only_simple_this_property_assignments =
temp_scope.only_simple_this_property_assignments();
this_property_assignments = temp_scope.this_property_assignments();
}
Expect(Token::RBRACE, CHECK_OK);
@ -3534,10 +3759,18 @@ FunctionLiteral* Parser::ParseFunctionLiteral(Handle<String> var_name,
}
FunctionLiteral* function_literal =
NEW(FunctionLiteral(name, top_scope_,
body.elements(), materialized_literal_count,
contains_array_literal, expected_property_count,
num_parameters, start_pos, end_pos,
NEW(FunctionLiteral(name,
top_scope_,
body.elements(),
materialized_literal_count,
contains_array_literal,
expected_property_count,
only_this_property_assignments,
only_simple_this_property_assignments,
this_property_assignments,
num_parameters,
start_pos,
end_pos,
function_name->length() > 0));
if (!is_pre_parsing_) {
function_literal->set_function_token_position(function_token_position);

View File

@ -4380,6 +4380,8 @@ static Object* Runtime_NewObject(Arguments args) {
Handle<Code> stub = ComputeConstructStub(map);
function->shared()->set_construct_stub(*stub);
}
Counters::constructed_objects.Increment();
Counters::constructed_objects_runtime.Increment();
return *result;
}

View File

@ -139,6 +139,8 @@ namespace internal {
SC(named_store_global_inline_miss, V8.NamedStoreGlobalInlineMiss) \
SC(call_global_inline, V8.CallGlobalInline) \
SC(call_global_inline_miss, V8.CallGlobalInlineMiss) \
SC(constructed_objects, V8.ConstructedObjects) \
SC(constructed_objects_runtime, V8.ConstructedObjectsRuntime) \
SC(for_in, V8.ForIn) \
SC(enum_cache_hits, V8.EnumCacheHits) \
SC(enum_cache_misses, V8.EnumCacheMisses) \

View File

@ -585,12 +585,16 @@ void Builtins::Generate_JSConstructStubGeneric(MacroAssembler* masm) {
// rax: initial map
// rbx: JSObject
// rdi: start of next object
// Calculate total properties described map.
__ movzxbq(rdx, FieldOperand(rax, Map::kUnusedPropertyFieldsOffset));
__ movzxbq(rcx, FieldOperand(rax, Map::kInObjectPropertiesOffset));
__ movzxbq(rcx, FieldOperand(rax, Map::kPreAllocatedPropertyFieldsOffset));
__ addq(rdx, rcx);
// Calculate unused properties past the end of the in-object properties.
__ movzxbq(rcx, FieldOperand(rax, Map::kInObjectPropertiesOffset));
__ subq(rdx, rcx);
// Done if no extra properties are to be allocated.
__ j(zero, &allocated);
__ Assert(positive, "Property allocation count failed.");
// Scale the number of elements by pointer size and add the header for
// FixedArrays to the start of the next object calculation from above.
@ -726,6 +730,7 @@ void Builtins::Generate_JSConstructStubGeneric(MacroAssembler* masm) {
__ pop(rcx);
__ lea(rsp, Operand(rsp, rbx, times_4, 1 * kPointerSize)); // 1 ~ receiver
__ push(rcx);
__ IncrementCounter(&Counters::constructed_objects, 1);
__ ret(0);
}

View File

@ -0,0 +1,78 @@
// Copyright 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.
function props(x) {
var array = [];
for (var p in x) array.push(p);
return array.sort();
}
function f1() {
this.x = 1;
}
function f2(x) {
this.x = x;
}
function f3(x) {
this.x = x;
this.y = 1;
this.z = f1;
}
function f4(x) {
this.x = x;
this.y = 1;
if (x == 1) return;
this.z = f1;
}
o1_1 = new f1();
o1_2 = new f1();
assertArrayEquals(["x"], props(o1_1));
assertArrayEquals(["x"], props(o1_2));
o2_1 = new f2(0);
o2_2 = new f2(0);
assertArrayEquals(["x"], props(o2_1));
assertArrayEquals(["x"], props(o2_2));
o3_1 = new f3(0);
o3_2 = new f3(0);
assertArrayEquals(["x", "y", "z"], props(o3_1));
assertArrayEquals(["x", "y", "z"], props(o3_2));
o4_0_1 = new f4(0);
o4_0_2 = new f4(0);
assertArrayEquals(["x", "y", "z"], props(o4_0_1));
assertArrayEquals(["x", "y", "z"], props(o4_0_2));
o4_1_1 = new f4(1);
o4_1_2 = new f4(1);
assertArrayEquals(["x", "y"], props(o4_1_1));
assertArrayEquals(["x", "y"], props(o4_1_2));