Optimizing nested, constant object literals (like JSON objects) by building one large object template for the entire object instead of one for each sub-object.

Review URL: http://codereview.chromium.org/39184

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@1432 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
olehougaard 2009-03-06 09:38:17 +00:00
parent e2ccac2279
commit d46a9900ee
6 changed files with 139 additions and 28 deletions

View File

@ -135,6 +135,8 @@ ObjectLiteral::Property::Property(Literal* key, Expression* value) {
Object* k = *key->handle(); Object* k = *key->handle();
if (k->IsSymbol() && Heap::Proto_symbol()->Equals(String::cast(k))) { if (k->IsSymbol() && Heap::Proto_symbol()->Equals(String::cast(k))) {
kind_ = PROTOTYPE; kind_ = PROTOTYPE;
} else if (value_->AsObjectLiteral() != NULL) {
kind_ = OBJECT_LITERAL;
} else { } else {
kind_ = value_->AsLiteral() == NULL ? COMPUTED : CONSTANT; kind_ = value_->AsLiteral() == NULL ? COMPUTED : CONSTANT;
} }

View File

@ -129,6 +129,7 @@ class Node: public ZoneObject {
virtual BinaryOperation* AsBinaryOperation() { return NULL; } virtual BinaryOperation* AsBinaryOperation() { return NULL; }
virtual Assignment* AsAssignment() { return NULL; } virtual Assignment* AsAssignment() { return NULL; }
virtual FunctionLiteral* AsFunctionLiteral() { return NULL; } virtual FunctionLiteral* AsFunctionLiteral() { return NULL; }
virtual ObjectLiteral* AsObjectLiteral() { return NULL; }
void set_statement_pos(int statement_pos) { statement_pos_ = statement_pos; } void set_statement_pos(int statement_pos) { statement_pos_ = statement_pos; }
int statement_pos() const { return statement_pos_; } int statement_pos() const { return statement_pos_; }
@ -651,10 +652,11 @@ class ObjectLiteral: public MaterializedLiteral {
public: public:
enum Kind { enum Kind {
CONSTANT, // Property with constant value (at compile time). CONSTANT, // Property with constant value (at compile time).
COMPUTED, // Property with computed value (at execution time). COMPUTED, // Property with computed value (at execution time).
OBJECT_LITERAL, // Property value is an object literal.
GETTER, SETTER, // Property is an accessor function. GETTER, SETTER, // Property is an accessor function.
PROTOTYPE // Property is __proto__. PROTOTYPE // Property is __proto__.
}; };
Property(Literal* key, Expression* value); Property(Literal* key, Expression* value);
@ -672,12 +674,15 @@ class ObjectLiteral: public MaterializedLiteral {
ObjectLiteral(Handle<FixedArray> constant_properties, ObjectLiteral(Handle<FixedArray> constant_properties,
ZoneList<Property*>* properties, ZoneList<Property*>* properties,
int literal_index) int literal_index,
bool is_simple)
: MaterializedLiteral(literal_index), : MaterializedLiteral(literal_index),
constant_properties_(constant_properties), constant_properties_(constant_properties),
properties_(properties) { properties_(properties),
is_simple_(is_simple) {
} }
virtual ObjectLiteral* AsObjectLiteral() { return this; }
virtual void Accept(AstVisitor* v); virtual void Accept(AstVisitor* v);
Handle<FixedArray> constant_properties() const { Handle<FixedArray> constant_properties() const {
@ -685,9 +690,14 @@ class ObjectLiteral: public MaterializedLiteral {
} }
ZoneList<Property*>* properties() const { return properties_; } ZoneList<Property*>* properties() const { return properties_; }
// An object literal is simple if the values consist of only
// constants and simple object literals.
bool is_simple() const { return is_simple_; }
private: private:
Handle<FixedArray> constant_properties_; Handle<FixedArray> constant_properties_;
ZoneList<Property*>* properties_; ZoneList<Property*>* properties_;
bool is_simple_;
}; };

View File

@ -3432,7 +3432,10 @@ void CodeGenerator::VisitObjectLiteral(ObjectLiteral* node) {
for (int i = 0; i < node->properties()->length(); i++) { for (int i = 0; i < node->properties()->length(); i++) {
ObjectLiteral::Property* property = node->properties()->at(i); ObjectLiteral::Property* property = node->properties()->at(i);
switch (property->kind()) { switch (property->kind()) {
case ObjectLiteral::Property::CONSTANT: break; case ObjectLiteral::Property::CONSTANT:
break;
case ObjectLiteral::Property::OBJECT_LITERAL:
if (property->value()->AsObjectLiteral()->is_simple()) break;
case ObjectLiteral::Property::COMPUTED: { case ObjectLiteral::Property::COMPUTED: {
Handle<Object> key(property->key()->handle()); Handle<Object> key(property->key()->handle());
Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize)); Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize));

View File

@ -158,10 +158,13 @@ class Parser {
// Decide if a property should be the object boilerplate. // Decide if a property should be the object boilerplate.
bool IsBoilerplateProperty(ObjectLiteral::Property* property); bool IsBoilerplateProperty(ObjectLiteral::Property* property);
// If the property is CONSTANT type, it returns the literal value, // If the property is CONSTANT type, return the literal value;
// otherwise, it return undefined literal as the placeholder // if the property is OBJECT_LITERAL and the object literal is
// simple return a fixed array containing the keys and values of the
// object literal.
// Otherwise, return undefined literal as the placeholder
// in the object literal boilerplate. // in the object literal boilerplate.
Literal* GetBoilerplateValue(ObjectLiteral::Property* property); Handle<Object> GetBoilerplateValue(ObjectLiteral::Property* property);
enum FunctionLiteralType { enum FunctionLiteralType {
EXPRESSION, EXPRESSION,
@ -3083,10 +3086,16 @@ bool Parser::IsBoilerplateProperty(ObjectLiteral::Property* property) {
} }
Literal* Parser::GetBoilerplateValue(ObjectLiteral::Property* property) { Handle<Object> Parser::GetBoilerplateValue(ObjectLiteral::Property* property) {
if (property->kind() == ObjectLiteral::Property::CONSTANT) if (property->kind() == ObjectLiteral::Property::CONSTANT)
return property->value()->AsLiteral(); return property->value()->AsLiteral()->handle();
return GetLiteralUndefined(); if (property->kind() == ObjectLiteral::Property::OBJECT_LITERAL) {
ObjectLiteral* object_literal = property->value()->AsObjectLiteral();
if (object_literal->is_simple()) {
return object_literal->constant_properties();
}
}
return Factory::undefined_value();
} }
@ -3181,24 +3190,30 @@ Expression* Parser::ParseObjectLiteral(bool* ok) {
Handle<FixedArray> constant_properties = Handle<FixedArray> constant_properties =
Factory::NewFixedArray(number_of_boilerplate_properties * 2, TENURED); Factory::NewFixedArray(number_of_boilerplate_properties * 2, TENURED);
int position = 0; int position = 0;
bool is_simple = true;
for (int i = 0; i < properties.length(); i++) { for (int i = 0; i < properties.length(); i++) {
ObjectLiteral::Property* property = properties.at(i); ObjectLiteral::Property* property = properties.at(i);
if (!IsBoilerplateProperty(property)) continue; if (!IsBoilerplateProperty(property)) {
is_simple = false;
continue;
}
// Add CONSTANT and COMPUTED properties to boilerplate. Use undefined // Add CONSTANT and COMPUTED properties to boilerplate. Use undefined
// value for COMPUTED properties, the real value is filled in at // value for COMPUTED properties, the real value is filled in at
// runtime. The enumeration order is maintained. // runtime. The enumeration order is maintained.
Handle<Object> key = property->key()->handle(); Handle<Object> key = property->key()->handle();
Literal* literal = GetBoilerplateValue(property); Handle<Object> value = GetBoilerplateValue(property);
is_simple = is_simple && !value->IsUndefined();
// Add name, value pair to the fixed array. // Add name, value pair to the fixed array.
constant_properties->set(position++, *key); constant_properties->set(position++, *key);
constant_properties->set(position++, *literal->handle()); constant_properties->set(position++, *value);
} }
return new ObjectLiteral(constant_properties, return new ObjectLiteral(constant_properties,
properties.elements(), properties.elements(),
literal_index); literal_index,
is_simple);
} }

View File

@ -131,14 +131,9 @@ static Handle<Map> ComputeObjectLiteralMap(
} }
static Object* Runtime_CreateObjectLiteralBoilerplate(Arguments args) { static Handle<Object> CreateObjectLiteralBoilerplate(
HandleScope scope; Handle<FixedArray> literals,
ASSERT(args.length() == 3); Handle<FixedArray> constant_properties) {
// Copy the arguments.
Handle<FixedArray> literals = args.at<FixedArray>(0);
int literals_index = Smi::cast(args[1])->value();
Handle<FixedArray> constant_properties = args.at<FixedArray>(2);
// Get the global context from the literals array. This is the // Get the global context from the literals array. This is the
// context in which the function was created and we use the object // context in which the function was created and we use the object
// function from this context to create the object literal. We do // function from this context to create the object literal. We do
@ -161,6 +156,12 @@ static Object* Runtime_CreateObjectLiteralBoilerplate(Arguments args) {
for (int index = 0; index < length; index +=2) { for (int index = 0; index < length; index +=2) {
Handle<Object> key(constant_properties->get(index+0)); Handle<Object> key(constant_properties->get(index+0));
Handle<Object> value(constant_properties->get(index+1)); Handle<Object> value(constant_properties->get(index+1));
if (value->IsFixedArray()) {
// The value contains the constant_properties of a
// simple object literal.
Handle<FixedArray> array = Handle<FixedArray>::cast(value);
value = CreateObjectLiteralBoilerplate(literals, array);
}
Handle<Object> result; Handle<Object> result;
uint32_t element_index = 0; uint32_t element_index = 0;
if (key->IsSymbol()) { if (key->IsSymbol()) {
@ -185,14 +186,31 @@ static Object* Runtime_CreateObjectLiteralBoilerplate(Arguments args) {
// exception, the exception is converted to an empty handle in // exception, the exception is converted to an empty handle in
// the handle based operations. In that case, we need to // the handle based operations. In that case, we need to
// convert back to an exception. // convert back to an exception.
if (result.is_null()) return Failure::Exception(); if (result.is_null()) return result;
} }
} }
// Update the functions literal and return the boilerplate. return boilerplate;
literals->set(literals_index, *boilerplate); }
return *boilerplate;
static Object* Runtime_CreateObjectLiteralBoilerplate(Arguments args) {
HandleScope scope;
ASSERT(args.length() == 3);
// Copy the arguments.
Handle<FixedArray> literals = args.at<FixedArray>(0);
int literals_index = Smi::cast(args[1])->value();
Handle<FixedArray> constant_properties = args.at<FixedArray>(2);
Handle<Object> result =
CreateObjectLiteralBoilerplate(literals, constant_properties);
if (result.is_null()) return Failure::Exception();
// Update the functions literal and return the boilerplate.
literals->set(literals_index, *result);;
return *result;
} }

View File

@ -0,0 +1,63 @@
// Copyright 2009 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.
var obj = {
a: 7,
b: { x: 12, y: 24 },
c: 'Zebra'
}
assertEquals(7, obj.a);
assertEquals(12, obj.b.x);
assertEquals(24, obj.b.y);
assertEquals('Zebra', obj.c);
var z = 24;
var obj2 = {
a: 7,
b: { x: 12, y: z },
c: 'Zebra'
}
assertEquals(7, obj2.a);
assertEquals(12, obj2.b.x);
assertEquals(24, obj2.b.y);
assertEquals('Zebra', obj2.c);
var arr = [];
for (var i = 0; i < 2; i++) {
arr[i] = {
a: 7,
b: { x: 12, y: 24 },
c: 'Zebra'
}
}
arr[0].a = 2;
assertEquals(2, arr[0].a);
assertEquals(7, arr[1].a);