Revision: 9335
Author:   [email protected]
Date:     Tue Sep 20 03:06:23 2011
Log:      Initialize pre-allocated fields of JSObject with undefined.

BUG=94873

Review URL: http://codereview.chromium.org/7929001
http://code.google.com/p/v8/source/detail?r=9335

Added:
 /branches/bleeding_edge/test/mjsunit/regress/regress-94873.js
Modified:
 /branches/bleeding_edge/src/arm/builtins-arm.cc
 /branches/bleeding_edge/src/arm/macro-assembler-arm.cc
 /branches/bleeding_edge/src/arm/macro-assembler-arm.h
 /branches/bleeding_edge/src/heap.cc
 /branches/bleeding_edge/src/ia32/builtins-ia32.cc
 /branches/bleeding_edge/src/ia32/macro-assembler-ia32.cc
 /branches/bleeding_edge/src/ia32/macro-assembler-ia32.h
 /branches/bleeding_edge/src/objects-inl.h
 /branches/bleeding_edge/src/objects.h
 /branches/bleeding_edge/src/x64/builtins-x64.cc
 /branches/bleeding_edge/src/x64/macro-assembler-x64.cc
 /branches/bleeding_edge/src/x64/macro-assembler-x64.h

=======================================
--- /dev/null
+++ /branches/bleeding_edge/test/mjsunit/regress/regress-94873.js Tue Sep 20 03:06:23 2011
@@ -0,0 +1,78 @@
+// Copyright 2011 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.
+
+// Flags: --expose-debug-as debug
+// Get the Debug object exposed from the debug context global object.
+Debug = debug.Debug;
+
+function sendCommand(state, cmd) {
+  // Get the debug command processor in paused state.
+  var dcp = state.debugCommandProcessor(false);
+  var request = JSON.stringify(cmd);
+  var response = dcp.processDebugJSONRequest(request);
+  return JSON.parse(response);
+}
+
+function listener(event, exec_state, event_data, data) {
+  try {
+    if (event == Debug.DebugEvent.Break) {
+      var line = event_data.sourceLineText();
+      print('break: ' + line);
+
+      var frame = sendCommand(exec_state, {
+        seq: 0,
+        type: "request",
+        command: "frame"
+      });
+
+      sendCommand(exec_state, {
+        seq: 0,
+        type: "request",
+        command: "evaluate",
+        arguments: {
+          expression: "obj.x.toString()",
+          additional_context: [{
+            name: "obj",
+            handle: frame.body.receiver.ref
+          }]
+        }
+      });
+    }
+  } catch (e) {
+    print(e);
+  }
+}
+
+Debug.setListener(listener);
+
+function a(x, y) {
+  this.x = x;
+  this.y = y;
+}
+
+Debug.setBreakPoint(a, 0, 0);
+new a(1, 2);
=======================================
--- /branches/bleeding_edge/src/arm/builtins-arm.cc     Fri Sep 16 05:26:29 2011
+++ /branches/bleeding_edge/src/arm/builtins-arm.cc     Tue Sep 20 03:06:23 2011
@@ -750,20 +750,22 @@
       // r5: First in-object property of JSObject (not tagged)
__ add(r6, r4, Operand(r3, LSL, kPointerSizeLog2)); // End of object.
       ASSERT_EQ(3 * kPointerSize, JSObject::kHeaderSize);
-      { Label loop, entry;
-        if (count_constructions) {
-          // To allow for truncation.
-          __ LoadRoot(r7, Heap::kOnePointerFillerMapRootIndex);
-        } else {
-          __ LoadRoot(r7, Heap::kUndefinedValueRootIndex);
-        }
-        __ b(&entry);
-        __ bind(&loop);
-        __ str(r7, MemOperand(r5, kPointerSize, PostIndex));
-        __ bind(&entry);
-        __ cmp(r5, r6);
-        __ b(lt, &loop);
-      }
+      __ LoadRoot(r7, Heap::kUndefinedValueRootIndex);
+      if (count_constructions) {
+        __ ldr(r0, FieldMemOperand(r2, Map::kInstanceSizesOffset));
+ __ Ubfx(r0, r0, Map::kPreAllocatedPropertyFieldsByte * kBitsPerByte,
+                kBitsPerByte);
+        __ add(r0, r5, Operand(r0, LSL, kPointerSizeLog2));
+        // r0: offset of first field after pre-allocated fields
+        if (FLAG_debug_code) {
+          __ cmp(r0, r6);
+ __ Assert(le, "Unexpected number of pre-allocated property fields.");
+        }
+        __ InitializeFieldsWithFiller(r5, r0, r7);
+        // To allow for truncation.
+        __ LoadRoot(r7, Heap::kOnePointerFillerMapRootIndex);
+      }
+      __ InitializeFieldsWithFiller(r5, r6, r7);

// Add the object tag to make the JSObject real, so that we can continue
       // and jump into the continuation code at any time from now on. Any
@@ -780,9 +782,11 @@
// The field instance sizes contains both pre-allocated property fields
       // and in-object properties.
       __ ldr(r0, FieldMemOperand(r2, Map::kInstanceSizesOffset));
-      __ Ubfx(r6, r0, Map::kPreAllocatedPropertyFieldsByte * 8, 8);
+      __ Ubfx(r6, r0, Map::kPreAllocatedPropertyFieldsByte * kBitsPerByte,
+              kBitsPerByte);
       __ add(r3, r3, Operand(r6));
-      __ Ubfx(r6, r0, Map::kInObjectPropertiesByte * 8, 8);
+      __ Ubfx(r6, r0, Map::kInObjectPropertiesByte * kBitsPerByte,
+              kBitsPerByte);
       __ sub(r3, r3, Operand(r6), SetCC);

       // Done if no extra properties are to be allocated.
=======================================
--- /branches/bleeding_edge/src/arm/macro-assembler-arm.cc Mon Sep 19 11:36:47 2011 +++ /branches/bleeding_edge/src/arm/macro-assembler-arm.cc Tue Sep 20 03:06:23 2011
@@ -3000,6 +3000,19 @@
   b(ne, &byte_loop_1);
   bind(&done);
 }
+
+
+void MacroAssembler::InitializeFieldsWithFiller(Register start_offset,
+                                                Register end_offset,
+                                                Register filler) {
+  Label loop, entry;
+  b(&entry);
+  bind(&loop);
+  str(filler, MemOperand(start_offset, kPointerSize, PostIndex));
+  bind(&entry);
+  cmp(start_offset, end_offset);
+  b(lt, &loop);
+}


 void MacroAssembler::CountLeadingZeros(Register zeros,   // Answer.
=======================================
--- /branches/bleeding_edge/src/arm/macro-assembler-arm.h Mon Sep 19 11:36:47 2011 +++ /branches/bleeding_edge/src/arm/macro-assembler-arm.h Tue Sep 20 03:06:23 2011
@@ -653,6 +653,13 @@
                  Register length,
                  Register scratch);

+ // Initialize fields with filler values. Fields starting at | start_offset| + // not including end_offset are overwritten with the value in |filler|. At
+  // the end the loop, |start_offset| takes the value of |end_offset|.
+  void InitializeFieldsWithFiller(Register start_offset,
+                                  Register end_offset,
+                                  Register filler);
+
// ---------------------------------------------------------------------------
   // Support functions.

=======================================
--- /branches/bleeding_edge/src/heap.cc Tue Sep 20 01:32:48 2011
+++ /branches/bleeding_edge/src/heap.cc Tue Sep 20 03:06:23 2011
@@ -3443,6 +3443,9 @@
   // We cannot always fill with one_pointer_filler_map because objects
// created from API functions expect their internal fields to be initialized
   // with undefined_value.
+ // Pre-allocated fields need to be initialized with undefined_value as well
+  // so that object accesses before the constructor completes (e.g. in the
+  // debugger) will not cause a crash.
   if (map->constructor()->IsJSFunction() &&
       JSFunction::cast(map->constructor())->shared()->
           IsInobjectSlackTrackingInProgress()) {
@@ -3452,7 +3455,7 @@
   } else {
     filler = Heap::undefined_value();
   }
-  obj->InitializeBody(map->instance_size(), filler);
+  obj->InitializeBody(map, Heap::undefined_value(), filler);
 }


=======================================
--- /branches/bleeding_edge/src/ia32/builtins-ia32.cc Fri Sep 16 05:26:29 2011 +++ /branches/bleeding_edge/src/ia32/builtins-ia32.cc Tue Sep 20 03:06:23 2011
@@ -204,22 +204,23 @@
       // eax: initial map
       // ebx: JSObject
       // edi: start of next object
-      { Label loop, entry;
-        // To allow for truncation.
-        if (count_constructions) {
-          __ mov(edx, factory->one_pointer_filler_map());
-        } else {
-          __ mov(edx, factory->undefined_value());
-        }
-        __ lea(ecx, Operand(ebx, JSObject::kHeaderSize));
-        __ jmp(&entry);
-        __ bind(&loop);
-        __ mov(Operand(ecx, 0), edx);
-        __ add(Operand(ecx), Immediate(kPointerSize));
-        __ bind(&entry);
-        __ cmp(ecx, Operand(edi));
-        __ j(less, &loop);
-      }
+      __ lea(ecx, Operand(ebx, JSObject::kHeaderSize));
+      __ mov(edx, factory->undefined_value());
+      if (count_constructions) {
+        __ movzx_b(esi,
+ FieldOperand(eax, Map::kPreAllocatedPropertyFieldsOffset));
+        __ lea(esi,
+ Operand(ebx, esi, times_pointer_size, JSObject::kHeaderSize));
+        // esi: offset of first field after pre-allocated fields
+        if (FLAG_debug_code) {
+          __ cmp(esi, Operand(edi));
+          __ Assert(less_equal,
+                    "Unexpected number of pre-allocated property fields.");
+        }
+        __ InitializeFieldsWithFiller(ecx, esi, edx);
+        __ mov(edx, factory->one_pointer_filler_map());
+      }
+      __ InitializeFieldsWithFiller(ecx, edi, edx);

// Add the object tag to make the JSObject real, so that we can continue
       // and jump into the continuation code at any time from now on. Any
=======================================
--- /branches/bleeding_edge/src/ia32/macro-assembler-ia32.cc Mon Sep 19 11:36:47 2011 +++ /branches/bleeding_edge/src/ia32/macro-assembler-ia32.cc Tue Sep 20 03:06:23 2011
@@ -1377,6 +1377,20 @@

   bind(&done);
 }
+
+
+void MacroAssembler::InitializeFieldsWithFiller(Register start_offset,
+                                                Register end_offset,
+                                                Register filler) {
+  Label loop, entry;
+  jmp(&entry);
+  bind(&loop);
+  mov(Operand(start_offset, 0), filler);
+  add(Operand(start_offset), Immediate(kPointerSize));
+  bind(&entry);
+  cmp(start_offset, Operand(end_offset));
+  j(less, &loop);
+}


 void MacroAssembler::NegativeZeroTest(Register result,
=======================================
--- /branches/bleeding_edge/src/ia32/macro-assembler-ia32.h Mon Sep 19 11:36:47 2011 +++ /branches/bleeding_edge/src/ia32/macro-assembler-ia32.h Tue Sep 20 03:06:23 2011
@@ -551,6 +551,13 @@
                  Register length,
                  Register scratch);

+ // Initialize fields with filler values. Fields starting at | start_offset| + // not including end_offset are overwritten with the value in |filler|. At
+  // the end the loop, |start_offset| takes the value of |end_offset|.
+  void InitializeFieldsWithFiller(Register start_offset,
+                                  Register end_offset,
+                                  Register filler);
+
// ---------------------------------------------------------------------------
   // Support functions.

=======================================
--- /branches/bleeding_edge/src/objects-inl.h   Mon Sep 19 11:36:47 2011
+++ /branches/bleeding_edge/src/objects-inl.h   Tue Sep 20 03:06:23 2011
@@ -1493,10 +1493,26 @@



-void JSObject::InitializeBody(int object_size, Object* value) {
-  ASSERT(!value->IsHeapObject() || !GetHeap()->InNewSpace(value));
- for (int offset = kHeaderSize; offset < object_size; offset += kPointerSize) {
-    WRITE_FIELD(this, offset, value);
+void JSObject::InitializeBody(Map* map,
+                              Object* pre_allocated_value,
+                              Object* filler_value) {
+  ASSERT(!filler_value->IsHeapObject() ||
+         !GetHeap()->InNewSpace(filler_value));
+  ASSERT(!pre_allocated_value->IsHeapObject() ||
+         !GetHeap()->InNewSpace(pre_allocated_value));
+  int size = map->instance_size();
+  int offset = kHeaderSize;
+  if (filler_value != pre_allocated_value) {
+    int pre_allocated = map->pre_allocated_property_fields();
+    ASSERT(pre_allocated * kPointerSize + kHeaderSize <= size);
+    for (int i = 0; i < pre_allocated; i++) {
+      WRITE_FIELD(this, offset, pre_allocated_value);
+      offset += kPointerSize;
+    }
+  }
+  while (offset < size) {
+    WRITE_FIELD(this, offset, filler_value);
+    offset += kPointerSize;
   }
 }

=======================================
--- /branches/bleeding_edge/src/objects.h       Mon Sep 19 11:36:47 2011
+++ /branches/bleeding_edge/src/objects.h       Tue Sep 20 03:06:23 2011
@@ -1815,11 +1815,14 @@
                                        WriteBarrierMode mode
                                        = UPDATE_WRITE_BARRIER);

-  // initializes the body after properties slot, properties slot is
-  // initialized by set_properties
-  // Note: this call does not update write barrier, it is caller's
-  // reponsibility to ensure that *v* can be collected without WB here.
-  inline void InitializeBody(int object_size, Object* value);
+  // Initializes the body after properties slot, properties slot is
+  // initialized by set_properties.  Fill the pre-allocated fields with
+  // pre_allocated_value and the rest with filler_value.
+ // Note: this call does not update write barrier, the caller is responsible
+  // to ensure that |filler_value| can be collected without WB here.
+  inline void InitializeBody(Map* map,
+                             Object* pre_allocated_value,
+                             Object* filler_value);

   // Check whether this object references another object
   bool ReferencesObject(Object* obj);
=======================================
--- /branches/bleeding_edge/src/x64/builtins-x64.cc     Fri Sep 16 05:26:29 2011
+++ /branches/bleeding_edge/src/x64/builtins-x64.cc     Tue Sep 20 03:06:23 2011
@@ -207,22 +207,23 @@
       // rax: initial map
       // rbx: JSObject
       // rdi: start of next object
-      { Label loop, entry;
-        // To allow for truncation.
-        if (count_constructions) {
-          __ LoadRoot(rdx, Heap::kOnePointerFillerMapRootIndex);
-        } else {
-          __ LoadRoot(rdx, Heap::kUndefinedValueRootIndex);
-        }
-        __ lea(rcx, Operand(rbx, JSObject::kHeaderSize));
-        __ jmp(&entry);
-        __ bind(&loop);
-        __ movq(Operand(rcx, 0), rdx);
-        __ addq(rcx, Immediate(kPointerSize));
-        __ bind(&entry);
-        __ cmpq(rcx, rdi);
-        __ j(less, &loop);
-      }
+      __ lea(rcx, Operand(rbx, JSObject::kHeaderSize));
+      __ LoadRoot(rdx, Heap::kUndefinedValueRootIndex);
+      if (count_constructions) {
+        __ movzxbq(rsi,
+ FieldOperand(rax, Map::kPreAllocatedPropertyFieldsOffset));
+        __ lea(rsi,
+ Operand(rbx, rsi, times_pointer_size, JSObject::kHeaderSize));
+        // rsi: offset of first field after pre-allocated fields
+        if (FLAG_debug_code) {
+          __ cmpq(rsi, rdi);
+          __ Assert(less_equal,
+                    "Unexpected number of pre-allocated property fields.");
+        }
+        __ InitializeFieldsWithFiller(rcx, rsi, rdx);
+        __ LoadRoot(rdx, Heap::kOnePointerFillerMapRootIndex);
+      }
+      __ InitializeFieldsWithFiller(rcx, rdi, rdx);

// Add the object tag to make the JSObject real, so that we can continue
       // and jump into the continuation code at any time from now on. Any
=======================================
--- /branches/bleeding_edge/src/x64/macro-assembler-x64.cc Mon Sep 19 11:36:47 2011 +++ /branches/bleeding_edge/src/x64/macro-assembler-x64.cc Tue Sep 20 03:06:23 2011
@@ -3849,6 +3849,20 @@
     bind(&done);
   }
 }
+
+
+void MacroAssembler::InitializeFieldsWithFiller(Register start_offset,
+                                                Register end_offset,
+                                                Register filler) {
+  Label loop, entry;
+  jmp(&entry);
+  bind(&loop);
+  movq(Operand(start_offset, 0), filler);
+  addq(start_offset, Immediate(kPointerSize));
+  bind(&entry);
+  cmpq(start_offset, end_offset);
+  j(less, &loop);
+}


 void MacroAssembler::LoadContext(Register dst, int context_chain_length) {
=======================================
--- /branches/bleeding_edge/src/x64/macro-assembler-x64.h Mon Sep 19 11:36:47 2011 +++ /branches/bleeding_edge/src/x64/macro-assembler-x64.h Tue Sep 20 03:06:23 2011
@@ -1205,6 +1205,13 @@
                  int min_length = 0,
                  Register scratch = kScratchRegister);

+ // Initialize fields with filler values. Fields starting at | start_offset| + // not including end_offset are overwritten with the value in |filler|. At
+  // the end the loop, |start_offset| takes the value of |end_offset|.
+  void InitializeFieldsWithFiller(Register start_offset,
+                                  Register end_offset,
+                                  Register filler);
+

// ---------------------------------------------------------------------------
   // StatsCounter support

--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev

Reply via email to