Optimizing nested, constant object literals (like JSON objects) by building one large...
authorolehougaard <olehougaard@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Fri, 6 Mar 2009 09:38:17 +0000 (09:38 +0000)
committerolehougaard <olehougaard@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Fri, 6 Mar 2009 09:38:17 +0000 (09:38 +0000)
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
src/ast.h
src/codegen-ia32.cc
src/parser.cc
src/runtime.cc
test/mjsunit/object-literal.js [new file with mode: 0644]

index a577d38..c41c0d4 100644 (file)
@@ -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;
   }
index e54ea63..70314a1 100644 (file)
--- 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<FixedArray> constant_properties,
                 ZoneList<Property*>* 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<FixedArray> constant_properties() const {
@@ -685,9 +690,14 @@ class ObjectLiteral: public MaterializedLiteral {
   }
   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:
   Handle<FixedArray> constant_properties_;
   ZoneList<Property*>* properties_;
+  bool is_simple_;
 };
 
 
index c0f1b12..b5ac200 100644 (file)
@@ -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<Object> key(property->key()->handle());
         Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize));
index 115e181..31fbf9d 100644 (file)
@@ -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<Object> GetBoilerplateValue(ObjectLiteral::Property* property);
 
   enum FunctionLiteralType {
     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)
-    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<FixedArray> 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<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.
     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);
 }
 
 
index 0a99141..701c43b 100644 (file)
@@ -131,14 +131,9 @@ static Handle<Map> ComputeObjectLiteralMap(
 }
 
 
-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);
-
+static Handle<Object> CreateObjectLiteralBoilerplate(
+    Handle<FixedArray> literals,
+    Handle<FixedArray> 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<Object> key(constant_properties->get(index+0));
       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;
       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<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, *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 (file)
index 0000000..e2da84b
--- /dev/null
@@ -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);