From d46a9900ee7508d57702c97c361932aaa720cdc8 Mon Sep 17 00:00:00 2001 From: olehougaard Date: Fri, 6 Mar 2009 09:38:17 +0000 Subject: [PATCH] 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 --- src/ast.cc | 2 ++ src/ast.h | 20 ++++++++++---- src/codegen-ia32.cc | 5 +++- src/parser.cc | 35 ++++++++++++++++------- src/runtime.cc | 40 +++++++++++++++++++-------- test/mjsunit/object-literal.js | 63 ++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 138 insertions(+), 27 deletions(-) create mode 100644 test/mjsunit/object-literal.js diff --git a/src/ast.cc b/src/ast.cc index a577d38..c41c0d4 100644 --- a/src/ast.cc +++ b/src/ast.cc @@ -135,6 +135,8 @@ ObjectLiteral::Property::Property(Literal* key, Expression* value) { Object* k = *key->handle(); if (k->IsSymbol() && Heap::Proto_symbol()->Equals(String::cast(k))) { kind_ = PROTOTYPE; + } else if (value_->AsObjectLiteral() != NULL) { + kind_ = OBJECT_LITERAL; } else { kind_ = value_->AsLiteral() == NULL ? COMPUTED : CONSTANT; } diff --git a/src/ast.h b/src/ast.h index e54ea63..70314a1 100644 --- a/src/ast.h +++ b/src/ast.h @@ -129,6 +129,7 @@ class Node: public ZoneObject { virtual BinaryOperation* AsBinaryOperation() { return NULL; } virtual Assignment* AsAssignment() { return NULL; } virtual FunctionLiteral* AsFunctionLiteral() { return NULL; } + virtual ObjectLiteral* AsObjectLiteral() { return NULL; } void set_statement_pos(int statement_pos) { statement_pos_ = statement_pos; } int statement_pos() const { return statement_pos_; } @@ -651,10 +652,11 @@ class ObjectLiteral: public MaterializedLiteral { public: enum Kind { - CONSTANT, // Property with constant value (at compile time). - COMPUTED, // Property with computed value (at execution time). + CONSTANT, // Property with constant value (at compile time). + COMPUTED, // Property with computed value (at execution time). + OBJECT_LITERAL, // Property value is an object literal. GETTER, SETTER, // Property is an accessor function. - PROTOTYPE // Property is __proto__. + PROTOTYPE // Property is __proto__. }; Property(Literal* key, Expression* value); @@ -672,12 +674,15 @@ class ObjectLiteral: public MaterializedLiteral { ObjectLiteral(Handle constant_properties, ZoneList* properties, - int literal_index) + int literal_index, + bool is_simple) : MaterializedLiteral(literal_index), constant_properties_(constant_properties), - properties_(properties) { + properties_(properties), + is_simple_(is_simple) { } + virtual ObjectLiteral* AsObjectLiteral() { return this; } virtual void Accept(AstVisitor* v); Handle constant_properties() const { @@ -685,9 +690,14 @@ class ObjectLiteral: public MaterializedLiteral { } ZoneList* 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: Handle constant_properties_; ZoneList* properties_; + bool is_simple_; }; diff --git a/src/codegen-ia32.cc b/src/codegen-ia32.cc index c0f1b12..b5ac200 100644 --- a/src/codegen-ia32.cc +++ b/src/codegen-ia32.cc @@ -3432,7 +3432,10 @@ void CodeGenerator::VisitObjectLiteral(ObjectLiteral* node) { for (int i = 0; i < node->properties()->length(); i++) { ObjectLiteral::Property* property = node->properties()->at(i); 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: { Handle key(property->key()->handle()); Handle ic(Builtins::builtin(Builtins::StoreIC_Initialize)); diff --git a/src/parser.cc b/src/parser.cc index 115e181..31fbf9d 100644 --- a/src/parser.cc +++ b/src/parser.cc @@ -158,10 +158,13 @@ class Parser { // Decide if a property should be the object boilerplate. bool IsBoilerplateProperty(ObjectLiteral::Property* property); - // If the property is CONSTANT type, it returns the literal value, - // otherwise, it return undefined literal as the placeholder + // If the property is CONSTANT type, return the literal value; + // 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. - Literal* GetBoilerplateValue(ObjectLiteral::Property* property); + Handle GetBoilerplateValue(ObjectLiteral::Property* property); enum FunctionLiteralType { EXPRESSION, @@ -3083,10 +3086,16 @@ bool Parser::IsBoilerplateProperty(ObjectLiteral::Property* property) { } -Literal* Parser::GetBoilerplateValue(ObjectLiteral::Property* property) { +Handle Parser::GetBoilerplateValue(ObjectLiteral::Property* property) { if (property->kind() == ObjectLiteral::Property::CONSTANT) - return property->value()->AsLiteral(); - return GetLiteralUndefined(); + return property->value()->AsLiteral()->handle(); + 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 constant_properties = Factory::NewFixedArray(number_of_boilerplate_properties * 2, TENURED); int position = 0; + bool is_simple = true; for (int i = 0; i < properties.length(); 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 // value for COMPUTED properties, the real value is filled in at // runtime. The enumeration order is maintained. Handle key = property->key()->handle(); - Literal* literal = GetBoilerplateValue(property); + Handle value = GetBoilerplateValue(property); + is_simple = is_simple && !value->IsUndefined(); // Add name, value pair to the fixed array. constant_properties->set(position++, *key); - constant_properties->set(position++, *literal->handle()); + constant_properties->set(position++, *value); } return new ObjectLiteral(constant_properties, properties.elements(), - literal_index); + literal_index, + is_simple); } diff --git a/src/runtime.cc b/src/runtime.cc index 0a99141..701c43b 100644 --- a/src/runtime.cc +++ b/src/runtime.cc @@ -131,14 +131,9 @@ static Handle ComputeObjectLiteralMap( } -static Object* Runtime_CreateObjectLiteralBoilerplate(Arguments args) { - HandleScope scope; - ASSERT(args.length() == 3); - // Copy the arguments. - Handle literals = args.at(0); - int literals_index = Smi::cast(args[1])->value(); - Handle constant_properties = args.at(2); - +static Handle CreateObjectLiteralBoilerplate( + Handle literals, + Handle constant_properties) { // Get the global context from the literals array. This is the // context in which the function was created and we use the object // 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) { Handle key(constant_properties->get(index+0)); Handle value(constant_properties->get(index+1)); + if (value->IsFixedArray()) { + // The value contains the constant_properties of a + // simple object literal. + Handle array = Handle::cast(value); + value = CreateObjectLiteralBoilerplate(literals, array); + } Handle result; uint32_t element_index = 0; if (key->IsSymbol()) { @@ -185,14 +186,31 @@ static Object* Runtime_CreateObjectLiteralBoilerplate(Arguments args) { // exception, the exception is converted to an empty handle in // the handle based operations. In that case, we need to // convert back to an exception. - if (result.is_null()) return Failure::Exception(); + if (result.is_null()) return result; } } + return boilerplate; +} + + +static Object* Runtime_CreateObjectLiteralBoilerplate(Arguments args) { + HandleScope scope; + ASSERT(args.length() == 3); + // Copy the arguments. + Handle literals = args.at(0); + int literals_index = Smi::cast(args[1])->value(); + Handle constant_properties = args.at(2); + + Handle result = + CreateObjectLiteralBoilerplate(literals, constant_properties); + + if (result.is_null()) return Failure::Exception(); + // Update the functions literal and return the boilerplate. - literals->set(literals_index, *boilerplate); + literals->set(literals_index, *result);; - return *boilerplate; + return *result; } diff --git a/test/mjsunit/object-literal.js b/test/mjsunit/object-literal.js new file mode 100644 index 0000000..e2da84b --- /dev/null +++ b/test/mjsunit/object-literal.js @@ -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); -- 2.7.4