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:
parent
e2ccac2279
commit
d46a9900ee
@ -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;
|
||||||
}
|
}
|
||||||
|
20
src/ast.h
20
src/ast.h
@ -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_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -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));
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
63
test/mjsunit/object-literal.js
Normal file
63
test/mjsunit/object-literal.js
Normal 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);
|
Loading…
Reference in New Issue
Block a user