Reapply revisions 1432, 1433, 1469 and 1472 while fixing issue 279.
authorolehougaard <olehougaard@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Mon, 23 Mar 2009 07:27:47 +0000 (07:27 +0000)
committerolehougaard <olehougaard@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Mon, 23 Mar 2009 07:27:47 +0000 (07:27 +0000)
Review URL: http://codereview.chromium.org/48006

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@1571 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

14 files changed:
src/ast.cc
src/ast.h
src/codegen-arm.cc
src/codegen-ia32.cc
src/heap.h
src/objects-inl.h
src/objects.h
src/parser.cc
src/parser.h
src/runtime.cc
src/runtime.h
test/mjsunit/fuzz-natives.js
test/mjsunit/object-literal.js
test/mjsunit/regress/regress-279.js

index 25a2cd4..1a6010a 100644 (file)
@@ -135,8 +135,12 @@ 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_->AsMaterializedLiteral() != NULL) {
+    kind_ = MATERIALIZED_LITERAL;
+  } else if (value_->AsLiteral() != NULL) {
+    kind_ = CONSTANT;
   } else {
-    kind_ = value_->AsLiteral() == NULL ? COMPUTED : CONSTANT;
+    kind_ = COMPUTED;
   }
 }
 
index 35e21f4..1574479 100644 (file)
--- a/src/ast.h
+++ b/src/ast.h
@@ -95,6 +95,7 @@ namespace v8 { namespace internal {
 
 // Forward declarations
 class TargetCollector;
+class MaterializedLiteral;
 
 #define DEF_FORWARD_DECLARATION(type) class type;
 NODE_LIST(DEF_FORWARD_DECLARATION)
@@ -129,6 +130,9 @@ class Node: public ZoneObject {
   virtual BinaryOperation* AsBinaryOperation() { return NULL; }
   virtual Assignment* AsAssignment() { return NULL; }
   virtual FunctionLiteral* AsFunctionLiteral() { return NULL; }
+  virtual MaterializedLiteral* AsMaterializedLiteral() { return NULL; }
+  virtual ObjectLiteral* AsObjectLiteral() { return NULL; }
+  virtual ArrayLiteral* AsArrayLiteral() { return NULL; }
 
   void set_statement_pos(int statement_pos) { statement_pos_ = statement_pos; }
   int statement_pos() const { return statement_pos_; }
@@ -636,11 +640,23 @@ class Literal: public Expression {
 // Base class for literals that needs space in the corresponding JSFunction.
 class MaterializedLiteral: public Expression {
  public:
-  explicit MaterializedLiteral(int literal_index)
-      : literal_index_(literal_index) {}
+  explicit MaterializedLiteral(int literal_index, bool is_simple, int depth)
+      : literal_index_(literal_index), is_simple_(is_simple), depth_(depth) {}
+
+  virtual MaterializedLiteral* AsMaterializedLiteral() { return this; }
+
   int literal_index() { return literal_index_; }
+
+  // A materialized literal is simple if the values consist of only
+  // constants and simple object and array literals.
+  bool is_simple() const { return is_simple_; }
+
+  int depth() const { return depth_; }
+
  private:
   int literal_index_;
+  bool is_simple_;
+  int depth_;
 };
 
 
@@ -655,10 +671,11 @@ class ObjectLiteral: public MaterializedLiteral {
    public:
 
     enum Kind {
-      CONSTANT,       // Property with constant value (at compile time).
-      COMPUTED,       // Property with computed value (at execution time).
-      GETTER, SETTER,  // Property is an accessor function.
-      PROTOTYPE       // Property is __proto__.
+      CONSTANT,              // Property with constant value (compile time).
+      COMPUTED,              // Property with computed value (execution time).
+      MATERIALIZED_LITERAL,  // Property value is a materialized literal.
+      GETTER, SETTER,        // Property is an accessor function.
+      PROTOTYPE              // Property is __proto__.
     };
 
     Property(Literal* key, Expression* value);
@@ -676,12 +693,14 @@ class ObjectLiteral: public MaterializedLiteral {
 
   ObjectLiteral(Handle<FixedArray> constant_properties,
                 ZoneList<Property*>* properties,
-                int literal_index)
-      : MaterializedLiteral(literal_index),
+                int literal_index,
+                bool is_simple,
+                int depth)
+      : MaterializedLiteral(literal_index, is_simple, depth),
         constant_properties_(constant_properties),
-        properties_(properties) {
-  }
+        properties_(properties) {}
 
+  virtual ObjectLiteral* AsObjectLiteral() { return this; }
   virtual void Accept(AstVisitor* v);
 
   Handle<FixedArray> constant_properties() const {
@@ -701,7 +720,7 @@ class RegExpLiteral: public MaterializedLiteral {
   RegExpLiteral(Handle<String> pattern,
                 Handle<String> flags,
                 int literal_index)
-      : MaterializedLiteral(literal_index),
+      : MaterializedLiteral(literal_index, false, 1),
         pattern_(pattern),
         flags_(flags) {}
 
@@ -717,14 +736,19 @@ class RegExpLiteral: public MaterializedLiteral {
 
 // An array literal has a literals object that is used
 // for minimizing the work when constructing it at runtime.
-class ArrayLiteral: public Expression {
+class ArrayLiteral: public MaterializedLiteral {
  public:
   ArrayLiteral(Handle<FixedArray> literals,
-               ZoneList<Expression*>* values)
-      : literals_(literals), values_(values) {
-  }
+               ZoneList<Expression*>* values,
+               int literal_index,
+               bool is_simple,
+               int depth)
+      : MaterializedLiteral(literal_index, is_simple, depth),
+        literals_(literals),
+        values_(values) {}
 
   virtual void Accept(AstVisitor* v);
+  virtual ArrayLiteral* AsArrayLiteral() { return this; }
 
   Handle<FixedArray> literals() const { return literals_; }
   ZoneList<Expression*>* values() const { return values_; }
index 87fdfa0..bc905a4 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright 2006-2008 the V8 project authors. All rights reserved.
+// Copyright 2006-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:
@@ -2662,7 +2662,7 @@ void CodeGenerator::VisitRegExpLiteral(RegExpLiteral* node) {
 
 
 // This deferred code stub will be used for creating the boilerplate
-// by calling Runtime_CreateObjectLiteral.
+// by calling Runtime_CreateObjectLiteralBoilerplate.
 // Each created boilerplate is stored in the JSFunction and they are
 // therefore context dependent.
 class DeferredObjectLiteral: public DeferredCode {
@@ -2684,7 +2684,7 @@ void DeferredObjectLiteral::Generate() {
   enter()->Bind();
   VirtualFrame::SpilledScope spilled_scope(generator());
 
-  // If the entry is undefined we call the runtime system to computed
+  // If the entry is undefined we call the runtime system to compute
   // the literal.
 
   VirtualFrame* frame = generator()->frame();
@@ -2736,7 +2736,11 @@ void CodeGenerator::VisitObjectLiteral(ObjectLiteral* node) {
   frame_->EmitPush(r2);
 
   // Clone the boilerplate object.
-  frame_->CallRuntime(Runtime::kCloneObjectLiteralBoilerplate, 1);
+  Runtime::FunctionId clone_function_id = Runtime::kCloneLiteralBoilerplate;
+  if (node->depth() == 1) {
+    clone_function_id = Runtime::kCloneShallowLiteralBoilerplate;
+  }
+  frame_->CallRuntime(clone_function_id, 1);
   frame_->EmitPush(r0);  // save the result
   // r0: cloned object literal
 
@@ -2745,7 +2749,11 @@ void CodeGenerator::VisitObjectLiteral(ObjectLiteral* node) {
     Literal* key = property->key();
     Expression* value = property->value();
     switch (property->kind()) {
-      case ObjectLiteral::Property::CONSTANT: break;
+      case ObjectLiteral::Property::CONSTANT:
+        break;
+      case ObjectLiteral::Property::MATERIALIZED_LITERAL:
+        if (property->value()->AsMaterializedLiteral()->is_simple()) break;
+        // else fall through
       case ObjectLiteral::Property::COMPUTED:  // fall through
       case ObjectLiteral::Property::PROTOTYPE: {
         frame_->EmitPush(r0);  // dup the result
@@ -2782,6 +2790,49 @@ void CodeGenerator::VisitObjectLiteral(ObjectLiteral* node) {
 }
 
 
+// This deferred code stub will be used for creating the boilerplate
+// by calling Runtime_CreateArrayLiteralBoilerplate.
+// Each created boilerplate is stored in the JSFunction and they are
+// therefore context dependent.
+class DeferredArrayLiteral: public DeferredCode {
+ public:
+  DeferredArrayLiteral(CodeGenerator* generator, ArrayLiteral* node)
+      : DeferredCode(generator), node_(node) {
+    set_comment("[ DeferredArrayLiteral");
+  }
+
+  virtual void Generate();
+
+ private:
+  ArrayLiteral* node_;
+};
+
+
+void DeferredArrayLiteral::Generate() {
+  // Argument is passed in r1.
+  enter()->Bind();
+  VirtualFrame::SpilledScope spilled_scope(generator());
+
+  // If the entry is undefined we call the runtime system to computed
+  // the literal.
+
+  VirtualFrame* frame = generator()->frame();
+  // Literal array (0).
+  frame->EmitPush(r1);
+  // Literal index (1).
+  __ mov(r0, Operand(Smi::FromInt(node_->literal_index())));
+  frame->EmitPush(r0);
+  // Constant properties (2).
+  __ mov(r0, Operand(node_->literals()));
+  frame->EmitPush(r0);
+  Result boilerplate =
+      frame->CallRuntime(Runtime::kCreateArrayLiteralBoilerplate, 3);
+  __ mov(r2, Operand(boilerplate.reg()));
+  // Result is returned in r2.
+  exit_.Jump();
+}
+
+
 void CodeGenerator::VisitArrayLiteral(ArrayLiteral* node) {
 #ifdef DEBUG
   int original_height = frame_->height();
@@ -2789,17 +2840,38 @@ void CodeGenerator::VisitArrayLiteral(ArrayLiteral* node) {
   VirtualFrame::SpilledScope spilled_scope(this);
   Comment cmnt(masm_, "[ ArrayLiteral");
 
-  // Call runtime to create the array literal.
-  __ mov(r0, Operand(node->literals()));
-  frame_->EmitPush(r0);
-  // Load the function of this frame.
-  __ ldr(r0, frame_->Function());
-  __ ldr(r0, FieldMemOperand(r0, JSFunction::kLiteralsOffset));
-  frame_->EmitPush(r0);
-  frame_->CallRuntime(Runtime::kCreateArrayLiteral, 2);
+  DeferredArrayLiteral* deferred = new DeferredArrayLiteral(this, node);
 
-  // Push the resulting array literal on the stack.
-  frame_->EmitPush(r0);
+  // Retrieve the literal array and check the allocated entry.
+
+  // Load the function of this activation.
+  __ ldr(r1, frame_->Function());
+
+  // Load the literals array of the function.
+  __ ldr(r1, FieldMemOperand(r1, JSFunction::kLiteralsOffset));
+
+  // Load the literal at the ast saved index.
+  int literal_offset =
+      FixedArray::kHeaderSize + node->literal_index() * kPointerSize;
+  __ ldr(r2, FieldMemOperand(r1, literal_offset));
+
+  // Check whether we need to materialize the object literal boilerplate.
+  // If so, jump to the deferred code.
+  __ cmp(r2, Operand(Factory::undefined_value()));
+  deferred->enter()->Branch(eq);
+  deferred->BindExit();
+
+  // Push the object literal boilerplate.
+  frame_->EmitPush(r2);
+
+  // Clone the boilerplate object.
+  Runtime::FunctionId clone_function_id = Runtime::kCloneLiteralBoilerplate;
+  if (node->depth() == 1) {
+    clone_function_id = Runtime::kCloneShallowLiteralBoilerplate;
+  }
+  frame_->CallRuntime(clone_function_id, 1);
+  frame_->EmitPush(r0);  // save the result
+  // r0: cloned object literal
 
   // Generate code to set the elements in the array that are not
   // literals.
index a66e548..2b19fe1 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright 2006-2008 the V8 project authors. All rights reserved.
+// Copyright 2006-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:
@@ -33,6 +33,7 @@
 #include "register-allocator-inl.h"
 #include "runtime.h"
 #include "scopes.h"
+#include "parser.h"
 
 namespace v8 { namespace internal {
 
@@ -3532,15 +3533,22 @@ void CodeGenerator::VisitObjectLiteral(ObjectLiteral* node) {
   // Push the boilerplate object.
   frame_->Push(&boilerplate);
   // Clone the boilerplate object.
-  Result clone =
-      frame_->CallRuntime(Runtime::kCloneObjectLiteralBoilerplate, 1);
+  Runtime::FunctionId clone_function_id = Runtime::kCloneLiteralBoilerplate;
+  if (node->depth() == 1) {
+    clone_function_id = Runtime::kCloneShallowLiteralBoilerplate;
+  }
+  Result clone = frame_->CallRuntime(clone_function_id, 1);
   // Push the newly cloned literal object as the result.
   frame_->Push(&clone);
 
   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::MATERIALIZED_LITERAL:
+        if (CompileTimeValue::IsCompileTimeValue(property->value())) break;
+        // else fall through.
       case ObjectLiteral::Property::COMPUTED: {
         Handle<Object> key(property->key()->handle());
         Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize));
@@ -3598,59 +3606,126 @@ void CodeGenerator::VisitObjectLiteral(ObjectLiteral* node) {
 }
 
 
+// This deferred code stub will be used for creating the boilerplate
+// by calling Runtime_CreateArrayLiteralBoilerplate.
+// Each created boilerplate is stored in the JSFunction and they are
+// therefore context dependent.
+class DeferredArrayLiteral: public DeferredCode {
+ public:
+  DeferredArrayLiteral(CodeGenerator* generator,
+                       ArrayLiteral* node)
+      : DeferredCode(generator), node_(node) {
+    set_comment("[ DeferredArrayLiteral");
+  }
+
+  virtual void Generate();
+
+ private:
+  ArrayLiteral* node_;
+};
+
+
+void DeferredArrayLiteral::Generate() {
+  Result literals(generator());
+  enter()->Bind(&literals);
+  // Since the entry is undefined we call the runtime system to
+  // compute the literal.
+
+  VirtualFrame* frame = generator()->frame();
+  // Literal array (0).
+  frame->Push(&literals);
+  // Literal index (1).
+  frame->Push(Smi::FromInt(node_->literal_index()));
+  // Constant properties (2).
+  frame->Push(node_->literals());
+  Result boilerplate =
+      frame->CallRuntime(Runtime::kCreateArrayLiteralBoilerplate, 3);
+  exit_.Jump(&boilerplate);
+}
+
+
 void CodeGenerator::VisitArrayLiteral(ArrayLiteral* node) {
   Comment cmnt(masm_, "[ ArrayLiteral");
+  DeferredArrayLiteral* deferred = new DeferredArrayLiteral(this, node);
 
-  // Call the runtime to create the array literal.
-  frame_->Push(node->literals());
-  // Load the literals array of the current function.
+  // Retrieve the literals array and check the allocated entry.  Begin
+  // with a writable copy of the function of this activation in a
+  // register.
   frame_->PushFunction();
   Result literals = frame_->Pop();
   literals.ToRegister();
-  frame_->Spill(literals.reg());  // Make it writable.
+  frame_->Spill(literals.reg());
+
+  // Load the literals array of the function.
   __ mov(literals.reg(),
          FieldOperand(literals.reg(), JSFunction::kLiteralsOffset));
-  frame_->Push(&literals);
-  Result array = frame_->CallRuntime(Runtime::kCreateArrayLiteral, 2);
+
+  // Load the literal at the ast saved index.
+  int literal_offset =
+      FixedArray::kHeaderSize + node->literal_index() * kPointerSize;
+  Result boilerplate = allocator_->Allocate();
+  ASSERT(boilerplate.is_valid());
+  __ mov(boilerplate.reg(), FieldOperand(literals.reg(), literal_offset));
+
+  // Check whether we need to materialize the object literal boilerplate.
+  // If so, jump to the deferred code passing the literals array.
+  __ cmp(boilerplate.reg(), Factory::undefined_value());
+  deferred->enter()->Branch(equal, &literals, not_taken);
+
+  literals.Unuse();
+  // The deferred code returns the boilerplate object.
+  deferred->BindExit(&boilerplate);
 
   // Push the resulting array literal on the stack.
-  frame_->Push(&array);
+  frame_->Push(&boilerplate);
+
+  // Clone the boilerplate object.
+  Runtime::FunctionId clone_function_id = Runtime::kCloneLiteralBoilerplate;
+  if (node->depth() == 1) {
+    clone_function_id = Runtime::kCloneShallowLiteralBoilerplate;
+  }
+  Result clone = frame_->CallRuntime(clone_function_id, 1);
+  // Push the newly cloned literal object as the result.
+  frame_->Push(&clone);
 
   // Generate code to set the elements in the array that are not
   // literals.
   for (int i = 0; i < node->values()->length(); i++) {
     Expression* value = node->values()->at(i);
 
-    // If value is literal the property value is already set in the
+    // If value is literal the property value is already set in the
     // boilerplate object.
-    if (value->AsLiteral() == NULL) {
-      // The property must be set by generated code.
-      Load(value);
+    if (value->AsLiteral() != NULL) continue;
+    // If value is a materialized literal the property value is already set
+    // in the boilerplate object if it is simple.
+    if (CompileTimeValue::IsCompileTimeValue(value)) continue;
 
-      // Get the property value off the stack.
-      Result prop_value = frame_->Pop();
-      prop_value.ToRegister();
+    // The property must be set by generated code.
+    Load(value);
 
-      // Fetch the array literal while leaving a copy on the stack and
-      // use it to get the elements array.
-      frame_->Dup();
-      Result elements = frame_->Pop();
-      elements.ToRegister();
-      frame_->Spill(elements.reg());
-      // Get the elements array.
-      __ mov(elements.reg(),
-             FieldOperand(elements.reg(), JSObject::kElementsOffset));
-
-      // Write to the indexed properties array.
-      int offset = i * kPointerSize + Array::kHeaderSize;
-      __ mov(FieldOperand(elements.reg(), offset), prop_value.reg());
-
-      // Update the write barrier for the array address.
-      frame_->Spill(prop_value.reg());  // Overwritten by the write barrier.
-      Result scratch = allocator_->Allocate();
-      ASSERT(scratch.is_valid());
-      __ RecordWrite(elements.reg(), offset, prop_value.reg(), scratch.reg());
-    }
+    // Get the property value off the stack.
+    Result prop_value = frame_->Pop();
+    prop_value.ToRegister();
+
+    // Fetch the array literal while leaving a copy on the stack and
+    // use it to get the elements array.
+    frame_->Dup();
+    Result elements = frame_->Pop();
+    elements.ToRegister();
+    frame_->Spill(elements.reg());
+    // Get the elements array.
+    __ mov(elements.reg(),
+           FieldOperand(elements.reg(), JSObject::kElementsOffset));
+
+    // Write to the indexed properties array.
+    int offset = i * kPointerSize + Array::kHeaderSize;
+    __ mov(FieldOperand(elements.reg(), offset), prop_value.reg());
+
+    // Update the write barrier for the array address.
+    frame_->Spill(prop_value.reg());  // Overwritten by the write barrier.
+    Result scratch = allocator_->Allocate();
+    ASSERT(scratch.is_valid());
+    __ RecordWrite(elements.reg(), offset, prop_value.reg(), scratch.reg());
   }
 }
 
index 9b5085d..c83c2e8 100644 (file)
@@ -167,8 +167,6 @@ namespace v8 { namespace internal {
   V(char_at_symbol, "CharAt")                                            \
   V(undefined_symbol, "undefined")                                       \
   V(value_of_symbol, "valueOf")                                          \
-  V(CreateObjectLiteralBoilerplate_symbol, "CreateObjectLiteralBoilerplate") \
-  V(CreateArrayLiteral_symbol, "CreateArrayLiteral")                     \
   V(InitializeVarGlobal_symbol, "InitializeVarGlobal")                   \
   V(InitializeConstGlobal_symbol, "InitializeConstGlobal")               \
   V(stack_overflow_symbol, "kStackOverflowBoilerplate")                  \
index 1e1f359..831a248 100644 (file)
@@ -1094,6 +1094,15 @@ Object* JSObject::FastPropertyAtPut(int index, Object* value) {
 }
 
 
+Object* JSObject::InObjectPropertyAt(int index) {
+  // Adjust for the number of properties stored in the object.
+  index -= map()->inobject_properties();
+  ASSERT(index < 0);
+  int offset = map()->instance_size() + (index * kPointerSize);
+  return READ_FIELD(this, offset);
+}
+
+
 Object* JSObject::InObjectPropertyAtPut(int index,
                                         Object* value,
                                         WriteBarrierMode mode) {
index 6f78f65..63480eb 100644 (file)
@@ -1396,7 +1396,8 @@ class JSObject: public HeapObject {
   inline Object* FastPropertyAt(int index);
   inline Object* FastPropertyAtPut(int index, Object* value);
 
-  // Access to set in object properties.
+  // Access to in object properties.
+  inline Object* InObjectPropertyAt(int index);
   inline Object* InObjectPropertyAtPut(int index,
                                        Object* value,
                                        WriteBarrierMode mode
index a760772..fc0ca4d 100644 (file)
@@ -158,10 +158,12 @@ 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 expression is a literal, return the literal value;
+  // if the expression is a materialized literal and is simple return a
+  // compile time value as encoded by CompileTimeValue::GetValue().
+  // Otherwise, return undefined literal as the placeholder
   // in the object literal boilerplate.
-  Literal* GetBoilerplateValue(ObjectLiteral::Property* property);
+  Handle<Object> GetBoilerplateValue(Expression* expression);
 
   enum FunctionLiteralType {
     EXPRESSION,
@@ -3054,6 +3056,7 @@ Expression* Parser::ParseArrayLiteral(bool* ok) {
 
   // Update the scope information before the pre-parsing bailout.
   temp_scope_->set_contains_array_literal();
+  int literal_index = temp_scope_->NextMaterializedLiteralIndex();
 
   if (is_pre_parsing_) return NULL;
 
@@ -3062,16 +3065,24 @@ Expression* Parser::ParseArrayLiteral(bool* ok) {
       Factory::NewFixedArray(values.length(), TENURED);
 
   // Fill in the literals.
+  bool is_simple = true;
+  int depth = 1;
   for (int i = 0; i < values.length(); i++) {
-    Literal* literal = values.at(i)->AsLiteral();
-    if (literal == NULL) {
+    MaterializedLiteral* m_literal = values.at(i)->AsMaterializedLiteral();
+    if (m_literal != NULL && m_literal->depth() + 1 > depth) {
+      depth = m_literal->depth() + 1;
+    }
+    Handle<Object> boilerplate_value = GetBoilerplateValue(values.at(i));
+    if (boilerplate_value->IsUndefined()) {
       literals->set_the_hole(i);
+      is_simple = false;
     } else {
-      literals->set(i, *literal->handle());
+      literals->set(i, *boilerplate_value);
     }
   }
 
-  return NEW(ArrayLiteral(literals, values.elements()));
+  return NEW(ArrayLiteral(literals, values.elements(),
+                          literal_index, is_simple, depth));
 }
 
 
@@ -3081,10 +3092,48 @@ bool Parser::IsBoilerplateProperty(ObjectLiteral::Property* property) {
 }
 
 
-Literal* Parser::GetBoilerplateValue(ObjectLiteral::Property* property) {
-  if (property->kind() == ObjectLiteral::Property::CONSTANT)
-    return property->value()->AsLiteral();
-  return GetLiteralUndefined();
+bool CompileTimeValue::IsCompileTimeValue(Expression* expression) {
+  MaterializedLiteral* lit = expression->AsMaterializedLiteral();
+  return lit != NULL && lit->is_simple();
+}
+
+Handle<FixedArray> CompileTimeValue::GetValue(Expression* expression) {
+  ASSERT(IsCompileTimeValue(expression));
+  Handle<FixedArray> result = Factory::NewFixedArray(2, TENURED);
+  ObjectLiteral* object_literal = expression->AsObjectLiteral();
+  if (object_literal != NULL) {
+    ASSERT(object_literal->is_simple());
+    result->set(kTypeSlot, Smi::FromInt(OBJECT_LITERAL));
+    result->set(kElementsSlot, *object_literal->constant_properties());
+  } else {
+    ArrayLiteral* array_literal = expression->AsArrayLiteral();
+    ASSERT(array_literal != NULL && array_literal->is_simple());
+    result->set(kTypeSlot, Smi::FromInt(ARRAY_LITERAL));
+    result->set(kElementsSlot, *array_literal->literals());
+  }
+  return result;
+}
+
+
+CompileTimeValue::Type CompileTimeValue::GetType(Handle<FixedArray> value) {
+  Smi* type_value = Smi::cast(value->get(kTypeSlot));
+  return static_cast<Type>(type_value->value());
+}
+
+
+Handle<FixedArray> CompileTimeValue::GetElements(Handle<FixedArray> value) {
+  return Handle<FixedArray>(FixedArray::cast(value->get(kElementsSlot)));
+}
+
+
+Handle<Object> Parser::GetBoilerplateValue(Expression* expression) {
+  if (expression->AsLiteral() != NULL) {
+    return expression->AsLiteral()->handle();
+  }
+  if (CompileTimeValue::IsCompileTimeValue(expression)) {
+    return CompileTimeValue::GetValue(expression);
+  }
+  return Factory::undefined_value();
 }
 
 
@@ -3179,24 +3228,36 @@ Expression* Parser::ParseObjectLiteral(bool* ok) {
   Handle<FixedArray> constant_properties =
       Factory::NewFixedArray(number_of_boilerplate_properties * 2, TENURED);
   int position = 0;
+  bool is_simple = true;
+  int depth = 1;
   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;
+    }
+    MaterializedLiteral* m_literal = property->value()->AsMaterializedLiteral();
+    if (m_literal != NULL && m_literal->depth() + 1 > depth) {
+      depth = m_literal->depth() + 1;
+    }
 
     // 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->value());
+    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,
+                           depth);
 }
 
 
index ee303b4..4c1401c 100644 (file)
@@ -29,6 +29,7 @@
 #define V8_PARSER_H_
 
 #include "scanner.h"
+#include "allocation.h"
 
 namespace v8 { namespace internal {
 
@@ -165,6 +166,35 @@ FunctionLiteral* MakeLazyAST(Handle<Script> script,
                              int end_position,
                              bool is_expression);
 
+
+// Support for handling complex values (array and object literals) that
+// can be fully handled at compile time.
+class CompileTimeValue: public AllStatic {
+ public:
+  enum Type {
+    OBJECT_LITERAL,
+    ARRAY_LITERAL
+  };
+
+  static bool IsCompileTimeValue(Expression* expression);
+
+  // Get the value as a compile time value.
+  static Handle<FixedArray> GetValue(Expression* expression);
+
+  // Get the type of a compile time value returned by GetValue().
+  static Type GetType(Handle<FixedArray> value);
+
+  // Get the elements array of a compile time value returned by GetValue().
+  static Handle<FixedArray> GetElements(Handle<FixedArray> value);
+
+ private:
+  static const int kTypeSlot = 0;
+  static const int kElementsSlot = 1;
+
+  DISALLOW_IMPLICIT_CONSTRUCTORS(CompileTimeValue);
+};
+
+
 } }  // namespace v8::internal
 
 #endif  // V8_PARSER_H_
index 3cf2439..2bf1f08 100644 (file)
@@ -44,6 +44,7 @@
 #include "scopeinfo.h"
 #include "v8threads.h"
 #include "smart-pointer.h"
+#include "parser.h"
 
 namespace v8 { namespace internal {
 
@@ -70,6 +71,13 @@ namespace v8 { namespace internal {
   RUNTIME_ASSERT(obj->IsBoolean());                                   \
   bool name = (obj)->IsTrue();
 
+// Cast the given object to a Smi and store its value in an int variable
+// with the given name.  If the object is not a Smi call IllegalOperation
+// and return.
+#define CONVERT_SMI_CHECKED(name, obj)                            \
+  RUNTIME_ASSERT(obj->IsSmi());                                   \
+  int name = Smi::cast(obj)->value();
+
 // Cast the given object to a double and store it in a variable with
 // the given name.  If the object is not a number (as opposed to
 // the number not-a-number) call IllegalOperation and return.
@@ -93,7 +101,103 @@ static Object* IllegalOperation() {
 }
 
 
-static Object* Runtime_CloneObjectLiteralBoilerplate(Arguments args) {
+static Object* DeepCopyBoilerplate(JSObject* boilerplate) {
+  StackLimitCheck check;
+  if (check.HasOverflowed()) return Top::StackOverflow();
+
+  Object* result = Heap::CopyJSObject(boilerplate);
+  if (result->IsFailure()) return result;
+  JSObject* copy = JSObject::cast(result);
+
+  // Deep copy local properties.
+  if (copy->HasFastProperties()) {
+    FixedArray* properties = copy->properties();
+    WriteBarrierMode mode = properties->GetWriteBarrierMode();
+    for (int i = 0; i < properties->length(); i++) {
+      Object* value = properties->get(i);
+      if (value->IsJSObject()) {
+        JSObject* jsObject = JSObject::cast(value);
+        result = DeepCopyBoilerplate(jsObject);
+        if (result->IsFailure()) return result;
+        properties->set(i, result, mode);
+      }
+    }
+    mode = copy->GetWriteBarrierMode();
+    for (int i = 0; i < copy->map()->inobject_properties(); i++) {
+      Object* value = copy->InObjectPropertyAt(i);
+      if (value->IsJSObject()) {
+        JSObject* jsObject = JSObject::cast(value);
+        result = DeepCopyBoilerplate(jsObject);
+        if (result->IsFailure()) return result;
+        copy->InObjectPropertyAtPut(i, result, mode);
+      }
+    }
+  } else {
+    result = Heap::AllocateFixedArray(copy->NumberOfLocalProperties(NONE));
+    if (result->IsFailure()) return result;
+    FixedArray* names = FixedArray::cast(result);
+    copy->GetLocalPropertyNames(names, 0);
+    for (int i = 0; i < names->length(); i++) {
+      ASSERT(names->get(i)->IsString());
+      String* keyString = String::cast(names->get(i));
+      PropertyAttributes attributes =
+        copy->GetLocalPropertyAttribute(keyString);
+      // Only deep copy fields from the object literal expression.
+      // In particular, don't try to copy the length attribute of
+      // an array.
+      if (attributes != NONE) continue;
+      Object* value = copy->GetProperty(keyString, &attributes);
+      ASSERT(!value->IsFailure());
+      if (value->IsJSObject()) {
+        JSObject* jsObject = JSObject::cast(value);
+        result = DeepCopyBoilerplate(jsObject);
+        if (result->IsFailure()) return result;
+        result = copy->SetProperty(keyString, result, NONE);
+        if (result->IsFailure()) return result;
+      }
+    }
+  }
+
+  // Deep copy local elements.
+  if (copy->HasFastElements()) {
+    FixedArray* elements = copy->elements();
+    WriteBarrierMode mode = elements->GetWriteBarrierMode();
+    for (int i = 0; i < elements->length(); i++) {
+      Object* value = elements->get(i);
+      if (value->IsJSObject()) {
+        JSObject* jsObject = JSObject::cast(value);
+        result = DeepCopyBoilerplate(jsObject);
+        if (result->IsFailure()) return result;
+        elements->set(i, result, mode);
+      }
+    }
+  } else {
+    Dictionary* element_dictionary = copy->element_dictionary();
+    int capacity = element_dictionary->Capacity();
+    for (int i = 0; i < capacity; i++) {
+      Object* k = element_dictionary->KeyAt(i);
+      if (element_dictionary->IsKey(k)) {
+        Object* value = element_dictionary->ValueAt(i);
+        if (value->IsJSObject()) {
+          JSObject* jsObject = JSObject::cast(value);
+          result = DeepCopyBoilerplate(jsObject);
+          if (result->IsFailure()) return result;
+          element_dictionary->ValueAtPut(i, result);
+        }
+      }
+    }
+  }
+  return copy;
+}
+
+
+static Object* Runtime_CloneLiteralBoilerplate(Arguments args) {
+  CONVERT_CHECKED(JSObject, boilerplate, args[0]);
+  return DeepCopyBoilerplate(boilerplate);
+}
+
+
+static Object* Runtime_CloneShallowLiteralBoilerplate(Arguments args) {
   CONVERT_CHECKED(JSObject, boilerplate, args[0]);
   return Heap::CopyJSObject(boilerplate);
 }
@@ -132,14 +236,14 @@ 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> CreateLiteralBoilerplate(
+    Handle<FixedArray> literals,
+    Handle<FixedArray> constant_properties);
+
 
+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
@@ -162,6 +266,13 @@ 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 = CreateLiteralBoilerplate(literals, array);
+        if (value.is_null()) return value;
+      }
       Handle<Object> result;
       uint32_t element_index = 0;
       if (key->IsSymbol()) {
@@ -186,39 +297,97 @@ 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 Handle<Object> CreateArrayLiteralBoilerplate(
+    Handle<FixedArray> literals,
+    Handle<FixedArray> elements) {
+  // Create the JSArray.
+  Handle<JSFunction> constructor(
+      JSFunction::GlobalContextFromLiterals(*literals)->array_function());
+  Handle<Object> object = Factory::NewJSObject(constructor);
+
+  Handle<Object> copied_elements = Factory::CopyFixedArray(elements);
+
+  Handle<FixedArray> content = Handle<FixedArray>::cast(copied_elements);
+  for (int i = 0; i < content->length(); i++) {
+    if (content->get(i)->IsFixedArray()) {
+      // The value contains the constant_properties of a
+      // simple object literal.
+      Handle<FixedArray> fa(FixedArray::cast(content->get(i)));
+      Handle<Object> result =
+        CreateLiteralBoilerplate(literals, fa);
+      if (result.is_null()) return result;
+      content->set(i, *result);
+    }
+  }
+
+  // Set the elements.
+  Handle<JSArray>::cast(object)->SetContent(*content);
+  return object;
+}
+
+
+static Handle<Object> CreateLiteralBoilerplate(
+    Handle<FixedArray> literals,
+    Handle<FixedArray> array) {
+  Handle<FixedArray> elements = CompileTimeValue::GetElements(array);
+  switch (CompileTimeValue::GetType(array)) {
+    case CompileTimeValue::OBJECT_LITERAL:
+      return CreateObjectLiteralBoilerplate(literals, elements);
+    case CompileTimeValue::ARRAY_LITERAL:
+      return CreateArrayLiteralBoilerplate(literals, elements);
+    default:
+      UNREACHABLE();
+      return Handle<Object>::null();
+  }
+}
+
+
+static Object* Runtime_CreateObjectLiteralBoilerplate(Arguments args) {
+  HandleScope scope;
+  ASSERT(args.length() == 3);
+  // Copy the arguments.
+  CONVERT_ARG_CHECKED(FixedArray, literals, 0);
+  CONVERT_SMI_CHECKED(literals_index, args[1]);
+  CONVERT_ARG_CHECKED(FixedArray, constant_properties, 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;
 }
 
 
-static Object* Runtime_CreateArrayLiteral(Arguments args) {
+static Object* Runtime_CreateArrayLiteralBoilerplate(Arguments args) {
   // Takes a FixedArray of elements containing the literal elements of
   // the array literal and produces JSArray with those elements.
   // Additionally takes the literals array of the surrounding function
   // which contains the context from which to get the Array function
   // to use for creating the array literal.
-  ASSERT(args.length() == 2);
-  CONVERT_CHECKED(FixedArray, elements, args[0]);
-  CONVERT_CHECKED(FixedArray, literals, args[1]);
-  JSFunction* constructor =
-      JSFunction::GlobalContextFromLiterals(literals)->array_function();
-  // Create the JSArray.
-  Object* object = Heap::AllocateJSObject(constructor);
-  if (object->IsFailure()) return object;
+  HandleScope scope;
+  ASSERT(args.length() == 3);
+  CONVERT_ARG_CHECKED(FixedArray, literals, 0);
+  CONVERT_SMI_CHECKED(literals_index, args[1]);
+  CONVERT_ARG_CHECKED(FixedArray, elements, 2);
 
-  // Copy the elements.
-  Object* content = elements->Copy();
-  if (content->IsFailure()) return content;
+  Handle<Object> object = CreateArrayLiteralBoilerplate(literals, elements);
+  if (object.is_null()) return Failure::Exception();
 
-  // Set the elements.
-  JSArray::cast(object)->SetContent(FixedArray::cast(content));
-  return object;
+  // Update the functions literal and return the boilerplate.
+  literals->set(literals_index, *object);
+  return *object;
 }
 
 
index 0a4795d..3bed31b 100644 (file)
@@ -253,9 +253,10 @@ namespace v8 { namespace internal {
   \
   /* Literals */ \
   F(MaterializeRegExpLiteral, 4)\
-  F(CreateArrayLiteral, 2) \
+  F(CreateArrayLiteralBoilerplate, 3) \
   F(CreateObjectLiteralBoilerplate, 3) \
-  F(CloneObjectLiteralBoilerplate, 1) \
+  F(CloneLiteralBoilerplate, 1) \
+  F(CloneShallowLiteralBoilerplate, 1) \
   \
   /* Catch context extension objects */ \
   F(CreateCatchExtensionObject, 2) \
@@ -328,7 +329,6 @@ class Runtime : public AllStatic {
     kNofFunctions
 #undef F
   };
-  static Object* CreateArrayLiteral(Arguments args);
 
   // Runtime function descriptor.
   struct Function {
index 64331d1..debcc9a 100644 (file)
@@ -121,7 +121,9 @@ var knownProblems = {
   "PushContext": true,
   "LazyCompile": true,
   "CreateObjectLiteralBoilerplate": true,
-  "CloneObjectLiteralBoilerplate": true,
+  "CloneLiteralBoilerplate": true,
+  "CloneShallowLiteralBoilerplate": true,
+  "CreateArrayLiteralBoilerplate": true,
   "IS_VAR": true,
   "ResolvePossiblyDirectEval": true,
   "Log": true
index e2da84b..cc6f59d 100644 (file)
@@ -58,6 +58,48 @@ for (var i = 0; i < 2; i++) {
   }
 }
 
-arr[0].a = 2;
-assertEquals(2, arr[0].a);
-assertEquals(7, arr[1].a);
+arr[0].b.x = 2;
+assertEquals(2, arr[0].b.x);
+assertEquals(12, arr[1].b.x);
+
+
+function makeSparseArray() {
+  return {
+    '0': { x: 12, y: 24 },
+    '1000000': { x: 0, y: 0 }
+  };
+}
+
+var sa1 = makeSparseArray();
+sa1[0].x = 0;
+var sa2 = makeSparseArray();
+assertEquals(12, sa2[0].x);
+
+// Test that non-constant literals work.
+var n = new Object();
+
+function makeNonConstantArray() { return [ [ n ] ]; }
+
+var a = makeNonConstantArray();
+a[0][0].foo = "bar";
+assertEquals("bar", n.foo);
+
+function makeNonConstantObject() { return { a: { b: n } }; }
+
+a = makeNonConstantObject();
+a.a.b.bar = "foo";
+assertEquals("foo", n.bar);
+
+// Test that exceptions for regexps still hold.
+function makeRegexpInArray() { return [ [ /a*/, {} ] ]; }
+
+a = makeRegexpInArray();
+var b = makeRegexpInArray();
+assertTrue(a[0][0] === b[0][0]);
+assertFalse(a[0][1] === b[0][1]);
+
+function makeRegexpInObject() { return { a: { b: /b*/, c: {} } }; }
+a = makeRegexpInObject();
+b = makeRegexpInObject();
+assertTrue(a.a.b === b.a.b);
+assertFalse(a.a.c === b.a.c);
index 7f95b16..e500dd6 100644 (file)
@@ -50,4 +50,13 @@ function makeObjectInArray() {
 a = makeObjectInArray();
 a[0].bar = 1;
 b = makeObjectInArray();
-assertEquals('undefined', typeof(b[0].bar), "Object in object");
+assertEquals('undefined', typeof(b[0].bar), "Object in array");
+
+function makeArrayInArray() {
+  return [ [] ];
+}
+
+a = makeArrayInArray();
+a[0].push(5);
+b = makeArrayInArray();
+assertEquals(0, b[0].length, "Array in array");