blob: e526761b9c8ba51b63034d33acd3d1010a174950 [file] [log] [blame]
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001// Copyright 2012 the V8 project authors. All rights reserved.
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6// * Redistributions of source code must retain the above copyright
7// notice, this list of conditions and the following disclaimer.
8// * Redistributions in binary form must reproduce the above
9// copyright notice, this list of conditions and the following
10// disclaimer in the documentation and/or other materials provided
11// with the distribution.
12// * Neither the name of Google Inc. nor the names of its
13// contributors may be used to endorse or promote products derived
14// from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Steve Blocka7e24c12009-10-30 11:49:00 +000027
28#include <stdlib.h>
Ben Murdochb8a8cc12014-11-26 15:28:44 +000029#include <utility>
Steve Blocka7e24c12009-10-30 11:49:00 +000030
Ben Murdochb8a8cc12014-11-26 15:28:44 +000031#include "src/v8.h"
Steve Blocka7e24c12009-10-30 11:49:00 +000032
Ben Murdochb8a8cc12014-11-26 15:28:44 +000033#include "src/compilation-cache.h"
34#include "src/execution.h"
35#include "src/factory.h"
36#include "src/global-handles.h"
37#include "src/ic/ic.h"
38#include "src/macro-assembler.h"
39#include "test/cctest/cctest.h"
Steve Blocka7e24c12009-10-30 11:49:00 +000040
41using namespace v8::internal;
42
Steve Blocka7e24c12009-10-30 11:49:00 +000043static void CheckMap(Map* map, int type, int instance_size) {
44 CHECK(map->IsHeapObject());
45#ifdef DEBUG
Ben Murdochb8a8cc12014-11-26 15:28:44 +000046 CHECK(CcTest::heap()->Contains(map));
Steve Blocka7e24c12009-10-30 11:49:00 +000047#endif
Ben Murdochb8a8cc12014-11-26 15:28:44 +000048 CHECK_EQ(CcTest::heap()->meta_map(), map->map());
Steve Blocka7e24c12009-10-30 11:49:00 +000049 CHECK_EQ(type, map->instance_type());
50 CHECK_EQ(instance_size, map->instance_size());
51}
52
53
54TEST(HeapMaps) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +000055 CcTest::InitializeVM();
56 Heap* heap = CcTest::heap();
57 CheckMap(heap->meta_map(), MAP_TYPE, Map::kSize);
58 CheckMap(heap->heap_number_map(), HEAP_NUMBER_TYPE, HeapNumber::kSize);
59 CheckMap(heap->fixed_array_map(), FIXED_ARRAY_TYPE, kVariableSizeSentinel);
60 CheckMap(heap->string_map(), STRING_TYPE, kVariableSizeSentinel);
Steve Blocka7e24c12009-10-30 11:49:00 +000061}
62
63
Ben Murdochb8a8cc12014-11-26 15:28:44 +000064static void CheckOddball(Isolate* isolate, Object* obj, const char* string) {
Steve Blocka7e24c12009-10-30 11:49:00 +000065 CHECK(obj->IsOddball());
Ben Murdochb8a8cc12014-11-26 15:28:44 +000066 Handle<Object> handle(obj, isolate);
Steve Blocka7e24c12009-10-30 11:49:00 +000067 Object* print_string =
Ben Murdochb8a8cc12014-11-26 15:28:44 +000068 *Execution::ToString(isolate, handle).ToHandleChecked();
69 CHECK(String::cast(print_string)->IsUtf8EqualTo(CStrVector(string)));
Steve Blocka7e24c12009-10-30 11:49:00 +000070}
71
72
Ben Murdochb8a8cc12014-11-26 15:28:44 +000073static void CheckSmi(Isolate* isolate, int value, const char* string) {
74 Handle<Object> handle(Smi::FromInt(value), isolate);
75 Object* print_string =
76 *Execution::ToString(isolate, handle).ToHandleChecked();
77 CHECK(String::cast(print_string)->IsUtf8EqualTo(CStrVector(string)));
Steve Blocka7e24c12009-10-30 11:49:00 +000078}
79
80
Ben Murdochb8a8cc12014-11-26 15:28:44 +000081static void CheckNumber(Isolate* isolate, double value, const char* string) {
82 Handle<Object> number = isolate->factory()->NewNumber(value);
83 CHECK(number->IsNumber());
84 Handle<Object> print_string =
85 Execution::ToString(isolate, number).ToHandleChecked();
86 CHECK(String::cast(*print_string)->IsUtf8EqualTo(CStrVector(string)));
87}
88
89
90static void CheckFindCodeObject(Isolate* isolate) {
Steve Blocka7e24c12009-10-30 11:49:00 +000091 // Test FindCodeObject
92#define __ assm.
93
Ben Murdochb8a8cc12014-11-26 15:28:44 +000094 Assembler assm(isolate, NULL, 0);
Steve Blocka7e24c12009-10-30 11:49:00 +000095
96 __ nop(); // supported on all architectures
97
98 CodeDesc desc;
99 assm.GetCode(&desc);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000100 Handle<Code> code = isolate->factory()->NewCode(
101 desc, Code::ComputeFlags(Code::STUB), Handle<Code>());
Steve Blocka7e24c12009-10-30 11:49:00 +0000102 CHECK(code->IsCode());
103
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000104 HeapObject* obj = HeapObject::cast(*code);
Steve Blocka7e24c12009-10-30 11:49:00 +0000105 Address obj_addr = obj->address();
106
107 for (int i = 0; i < obj->Size(); i += kPointerSize) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000108 Object* found = isolate->FindCodeObject(obj_addr + i);
109 CHECK_EQ(*code, found);
Steve Blocka7e24c12009-10-30 11:49:00 +0000110 }
111
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000112 Handle<Code> copy = isolate->factory()->NewCode(
113 desc, Code::ComputeFlags(Code::STUB), Handle<Code>());
114 HeapObject* obj_copy = HeapObject::cast(*copy);
115 Object* not_right = isolate->FindCodeObject(obj_copy->address() +
116 obj_copy->Size() / 2);
117 CHECK(not_right != *code);
118}
119
120
121TEST(HandleNull) {
122 CcTest::InitializeVM();
123 Isolate* isolate = CcTest::i_isolate();
124 HandleScope outer_scope(isolate);
125 LocalContext context;
126 Handle<Object> n(reinterpret_cast<Object*>(NULL), isolate);
127 CHECK(!n.is_null());
Steve Blocka7e24c12009-10-30 11:49:00 +0000128}
129
130
131TEST(HeapObjects) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000132 CcTest::InitializeVM();
133 Isolate* isolate = CcTest::i_isolate();
134 Factory* factory = isolate->factory();
135 Heap* heap = isolate->heap();
Steve Blocka7e24c12009-10-30 11:49:00 +0000136
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000137 HandleScope sc(isolate);
138 Handle<Object> value = factory->NewNumber(1.000123);
Steve Blocka7e24c12009-10-30 11:49:00 +0000139 CHECK(value->IsHeapNumber());
140 CHECK(value->IsNumber());
141 CHECK_EQ(1.000123, value->Number());
142
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000143 value = factory->NewNumber(1.0);
Steve Blocka7e24c12009-10-30 11:49:00 +0000144 CHECK(value->IsSmi());
145 CHECK(value->IsNumber());
146 CHECK_EQ(1.0, value->Number());
147
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000148 value = factory->NewNumberFromInt(1024);
Steve Blocka7e24c12009-10-30 11:49:00 +0000149 CHECK(value->IsSmi());
150 CHECK(value->IsNumber());
151 CHECK_EQ(1024.0, value->Number());
152
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000153 value = factory->NewNumberFromInt(Smi::kMinValue);
Steve Blocka7e24c12009-10-30 11:49:00 +0000154 CHECK(value->IsSmi());
155 CHECK(value->IsNumber());
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000156 CHECK_EQ(Smi::kMinValue, Handle<Smi>::cast(value)->value());
Steve Blocka7e24c12009-10-30 11:49:00 +0000157
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000158 value = factory->NewNumberFromInt(Smi::kMaxValue);
Steve Blocka7e24c12009-10-30 11:49:00 +0000159 CHECK(value->IsSmi());
160 CHECK(value->IsNumber());
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000161 CHECK_EQ(Smi::kMaxValue, Handle<Smi>::cast(value)->value());
Steve Blocka7e24c12009-10-30 11:49:00 +0000162
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000163#if !defined(V8_TARGET_ARCH_X64) && !defined(V8_TARGET_ARCH_ARM64) && \
164 !defined(V8_TARGET_ARCH_MIPS64)
Steve Block3ce2e202009-11-05 08:53:23 +0000165 // TODO(lrn): We need a NumberFromIntptr function in order to test this.
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000166 value = factory->NewNumberFromInt(Smi::kMinValue - 1);
Steve Blocka7e24c12009-10-30 11:49:00 +0000167 CHECK(value->IsHeapNumber());
168 CHECK(value->IsNumber());
169 CHECK_EQ(static_cast<double>(Smi::kMinValue - 1), value->Number());
Steve Block3ce2e202009-11-05 08:53:23 +0000170#endif
Steve Blocka7e24c12009-10-30 11:49:00 +0000171
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000172 value = factory->NewNumberFromUint(static_cast<uint32_t>(Smi::kMaxValue) + 1);
Steve Blocka7e24c12009-10-30 11:49:00 +0000173 CHECK(value->IsHeapNumber());
174 CHECK(value->IsNumber());
Steve Block3ce2e202009-11-05 08:53:23 +0000175 CHECK_EQ(static_cast<double>(static_cast<uint32_t>(Smi::kMaxValue) + 1),
176 value->Number());
Steve Blocka7e24c12009-10-30 11:49:00 +0000177
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000178 value = factory->NewNumberFromUint(static_cast<uint32_t>(1) << 31);
179 CHECK(value->IsHeapNumber());
180 CHECK(value->IsNumber());
181 CHECK_EQ(static_cast<double>(static_cast<uint32_t>(1) << 31),
182 value->Number());
Steve Blocka7e24c12009-10-30 11:49:00 +0000183
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000184 // nan oddball checks
185 CHECK(factory->nan_value()->IsNumber());
186 CHECK(std::isnan(factory->nan_value()->Number()));
187
188 Handle<String> s = factory->NewStringFromStaticChars("fisk hest ");
Steve Block6ded16b2010-05-10 14:33:55 +0100189 CHECK(s->IsString());
190 CHECK_EQ(10, s->length());
Steve Blocka7e24c12009-10-30 11:49:00 +0000191
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000192 Handle<String> object_string = Handle<String>::cast(factory->Object_string());
193 Handle<GlobalObject> global(CcTest::i_isolate()->context()->global_object());
194 v8::Maybe<bool> maybe = JSReceiver::HasOwnProperty(global, object_string);
195 CHECK(maybe.has_value);
196 CHECK(maybe.value);
Steve Blocka7e24c12009-10-30 11:49:00 +0000197
198 // Check ToString for oddballs
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000199 CheckOddball(isolate, heap->true_value(), "true");
200 CheckOddball(isolate, heap->false_value(), "false");
201 CheckOddball(isolate, heap->null_value(), "null");
202 CheckOddball(isolate, heap->undefined_value(), "undefined");
Steve Blocka7e24c12009-10-30 11:49:00 +0000203
204 // Check ToString for Smis
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000205 CheckSmi(isolate, 0, "0");
206 CheckSmi(isolate, 42, "42");
207 CheckSmi(isolate, -42, "-42");
Steve Blocka7e24c12009-10-30 11:49:00 +0000208
209 // Check ToString for Numbers
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000210 CheckNumber(isolate, 1.1, "1.1");
Steve Blocka7e24c12009-10-30 11:49:00 +0000211
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000212 CheckFindCodeObject(isolate);
Steve Blocka7e24c12009-10-30 11:49:00 +0000213}
214
215
216TEST(Tagging) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000217 CcTest::InitializeVM();
Steve Blocka7e24c12009-10-30 11:49:00 +0000218 int request = 24;
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100219 CHECK_EQ(request, static_cast<int>(OBJECT_POINTER_ALIGN(request)));
Steve Blocka7e24c12009-10-30 11:49:00 +0000220 CHECK(Smi::FromInt(42)->IsSmi());
Steve Blocka7e24c12009-10-30 11:49:00 +0000221 CHECK(Smi::FromInt(Smi::kMinValue)->IsSmi());
222 CHECK(Smi::FromInt(Smi::kMaxValue)->IsSmi());
223}
224
225
226TEST(GarbageCollection) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000227 CcTest::InitializeVM();
228 Isolate* isolate = CcTest::i_isolate();
229 Heap* heap = isolate->heap();
230 Factory* factory = isolate->factory();
Steve Blocka7e24c12009-10-30 11:49:00 +0000231
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000232 HandleScope sc(isolate);
Steve Block6ded16b2010-05-10 14:33:55 +0100233 // Check GC.
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000234 heap->CollectGarbage(NEW_SPACE);
Steve Blocka7e24c12009-10-30 11:49:00 +0000235
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000236 Handle<GlobalObject> global(CcTest::i_isolate()->context()->global_object());
237 Handle<String> name = factory->InternalizeUtf8String("theFunction");
238 Handle<String> prop_name = factory->InternalizeUtf8String("theSlot");
239 Handle<String> prop_namex = factory->InternalizeUtf8String("theSlotx");
240 Handle<String> obj_name = factory->InternalizeUtf8String("theObject");
241 Handle<Smi> twenty_three(Smi::FromInt(23), isolate);
242 Handle<Smi> twenty_four(Smi::FromInt(24), isolate);
Steve Blocka7e24c12009-10-30 11:49:00 +0000243
Steve Block6ded16b2010-05-10 14:33:55 +0100244 {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000245 HandleScope inner_scope(isolate);
Steve Block6ded16b2010-05-10 14:33:55 +0100246 // Allocate a function and keep it in global object's property.
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000247 Handle<JSFunction> function = factory->NewFunction(name);
248 JSReceiver::SetProperty(global, name, function, SLOPPY).Check();
Steve Block6ded16b2010-05-10 14:33:55 +0100249 // Allocate an object. Unrooted after leaving the scope.
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000250 Handle<JSObject> obj = factory->NewJSObject(function);
251 JSReceiver::SetProperty(obj, prop_name, twenty_three, SLOPPY).Check();
252 JSReceiver::SetProperty(obj, prop_namex, twenty_four, SLOPPY).Check();
Steve Blocka7e24c12009-10-30 11:49:00 +0000253
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000254 CHECK_EQ(Smi::FromInt(23),
255 *Object::GetProperty(obj, prop_name).ToHandleChecked());
256 CHECK_EQ(Smi::FromInt(24),
257 *Object::GetProperty(obj, prop_namex).ToHandleChecked());
Steve Block6ded16b2010-05-10 14:33:55 +0100258 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000259
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000260 heap->CollectGarbage(NEW_SPACE);
Steve Blocka7e24c12009-10-30 11:49:00 +0000261
Steve Block6ded16b2010-05-10 14:33:55 +0100262 // Function should be alive.
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000263 v8::Maybe<bool> maybe = JSReceiver::HasOwnProperty(global, name);
264 CHECK(maybe.has_value);
265 CHECK(maybe.value);
Steve Block6ded16b2010-05-10 14:33:55 +0100266 // Check function is retained.
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000267 Handle<Object> func_value =
268 Object::GetProperty(global, name).ToHandleChecked();
Steve Blocka7e24c12009-10-30 11:49:00 +0000269 CHECK(func_value->IsJSFunction());
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000270 Handle<JSFunction> function = Handle<JSFunction>::cast(func_value);
Steve Blocka7e24c12009-10-30 11:49:00 +0000271
Steve Block6ded16b2010-05-10 14:33:55 +0100272 {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000273 HandleScope inner_scope(isolate);
Steve Block6ded16b2010-05-10 14:33:55 +0100274 // Allocate another object, make it reachable from global.
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000275 Handle<JSObject> obj = factory->NewJSObject(function);
276 JSReceiver::SetProperty(global, obj_name, obj, SLOPPY).Check();
277 JSReceiver::SetProperty(obj, prop_name, twenty_three, SLOPPY).Check();
Steve Block6ded16b2010-05-10 14:33:55 +0100278 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000279
Steve Block6ded16b2010-05-10 14:33:55 +0100280 // After gc, it should survive.
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000281 heap->CollectGarbage(NEW_SPACE);
Steve Blocka7e24c12009-10-30 11:49:00 +0000282
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000283 maybe = JSReceiver::HasOwnProperty(global, obj_name);
284 CHECK(maybe.has_value);
285 CHECK(maybe.value);
286 Handle<Object> obj =
287 Object::GetProperty(global, obj_name).ToHandleChecked();
288 CHECK(obj->IsJSObject());
289 CHECK_EQ(Smi::FromInt(23),
290 *Object::GetProperty(obj, prop_name).ToHandleChecked());
Steve Blocka7e24c12009-10-30 11:49:00 +0000291}
292
293
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000294static void VerifyStringAllocation(Isolate* isolate, const char* string) {
295 HandleScope scope(isolate);
296 Handle<String> s = isolate->factory()->NewStringFromUtf8(
297 CStrVector(string)).ToHandleChecked();
Steve Blockd0582a62009-12-15 09:54:21 +0000298 CHECK_EQ(StrLength(string), s->length());
Steve Blocka7e24c12009-10-30 11:49:00 +0000299 for (int index = 0; index < s->length(); index++) {
Steve Block6ded16b2010-05-10 14:33:55 +0100300 CHECK_EQ(static_cast<uint16_t>(string[index]), s->Get(index));
301 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000302}
303
304
305TEST(String) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000306 CcTest::InitializeVM();
307 Isolate* isolate = reinterpret_cast<Isolate*>(CcTest::isolate());
Steve Blocka7e24c12009-10-30 11:49:00 +0000308
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000309 VerifyStringAllocation(isolate, "a");
310 VerifyStringAllocation(isolate, "ab");
311 VerifyStringAllocation(isolate, "abc");
312 VerifyStringAllocation(isolate, "abcd");
313 VerifyStringAllocation(isolate, "fiskerdrengen er paa havet");
Steve Blocka7e24c12009-10-30 11:49:00 +0000314}
315
316
317TEST(LocalHandles) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000318 CcTest::InitializeVM();
319 Isolate* isolate = CcTest::i_isolate();
320 Factory* factory = isolate->factory();
Steve Blocka7e24c12009-10-30 11:49:00 +0000321
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000322 v8::HandleScope scope(CcTest::isolate());
Steve Blocka7e24c12009-10-30 11:49:00 +0000323 const char* name = "Kasper the spunky";
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000324 Handle<String> string = factory->NewStringFromAsciiChecked(name);
Steve Blockd0582a62009-12-15 09:54:21 +0000325 CHECK_EQ(StrLength(name), string->length());
Steve Blocka7e24c12009-10-30 11:49:00 +0000326}
327
328
329TEST(GlobalHandles) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000330 CcTest::InitializeVM();
331 Isolate* isolate = CcTest::i_isolate();
332 Heap* heap = isolate->heap();
333 Factory* factory = isolate->factory();
334 GlobalHandles* global_handles = isolate->global_handles();
Steve Blocka7e24c12009-10-30 11:49:00 +0000335
Steve Block6ded16b2010-05-10 14:33:55 +0100336 Handle<Object> h1;
337 Handle<Object> h2;
338 Handle<Object> h3;
339 Handle<Object> h4;
Steve Blocka7e24c12009-10-30 11:49:00 +0000340
Steve Block6ded16b2010-05-10 14:33:55 +0100341 {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000342 HandleScope scope(isolate);
Steve Block6ded16b2010-05-10 14:33:55 +0100343
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000344 Handle<Object> i = factory->NewStringFromStaticChars("fisk");
345 Handle<Object> u = factory->NewNumber(1.12344);
Steve Block6ded16b2010-05-10 14:33:55 +0100346
Steve Block44f0eee2011-05-26 01:26:41 +0100347 h1 = global_handles->Create(*i);
348 h2 = global_handles->Create(*u);
349 h3 = global_handles->Create(*i);
350 h4 = global_handles->Create(*u);
Steve Block6ded16b2010-05-10 14:33:55 +0100351 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000352
353 // after gc, it should survive
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000354 heap->CollectGarbage(NEW_SPACE);
Steve Blocka7e24c12009-10-30 11:49:00 +0000355
356 CHECK((*h1)->IsString());
357 CHECK((*h2)->IsHeapNumber());
358 CHECK((*h3)->IsString());
359 CHECK((*h4)->IsHeapNumber());
360
361 CHECK_EQ(*h3, *h1);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000362 GlobalHandles::Destroy(h1.location());
363 GlobalHandles::Destroy(h3.location());
Steve Blocka7e24c12009-10-30 11:49:00 +0000364
365 CHECK_EQ(*h4, *h2);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000366 GlobalHandles::Destroy(h2.location());
367 GlobalHandles::Destroy(h4.location());
Steve Blocka7e24c12009-10-30 11:49:00 +0000368}
369
370
371static bool WeakPointerCleared = false;
372
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000373static void TestWeakGlobalHandleCallback(
374 const v8::WeakCallbackData<v8::Value, void>& data) {
375 std::pair<v8::Persistent<v8::Value>*, int>* p =
376 reinterpret_cast<std::pair<v8::Persistent<v8::Value>*, int>*>(
377 data.GetParameter());
378 if (p->second == 1234) WeakPointerCleared = true;
379 p->first->Reset();
Steve Blocka7e24c12009-10-30 11:49:00 +0000380}
381
382
383TEST(WeakGlobalHandlesScavenge) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000384 i::FLAG_stress_compaction = false;
385 CcTest::InitializeVM();
386 Isolate* isolate = CcTest::i_isolate();
387 Heap* heap = isolate->heap();
388 Factory* factory = isolate->factory();
389 GlobalHandles* global_handles = isolate->global_handles();
Steve Blocka7e24c12009-10-30 11:49:00 +0000390
391 WeakPointerCleared = false;
392
Steve Block6ded16b2010-05-10 14:33:55 +0100393 Handle<Object> h1;
394 Handle<Object> h2;
Steve Blocka7e24c12009-10-30 11:49:00 +0000395
Steve Block6ded16b2010-05-10 14:33:55 +0100396 {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000397 HandleScope scope(isolate);
Steve Block6ded16b2010-05-10 14:33:55 +0100398
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000399 Handle<Object> i = factory->NewStringFromStaticChars("fisk");
400 Handle<Object> u = factory->NewNumber(1.12344);
Steve Block6ded16b2010-05-10 14:33:55 +0100401
Steve Block44f0eee2011-05-26 01:26:41 +0100402 h1 = global_handles->Create(*i);
403 h2 = global_handles->Create(*u);
Steve Block6ded16b2010-05-10 14:33:55 +0100404 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000405
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000406 std::pair<Handle<Object>*, int> handle_and_id(&h2, 1234);
407 GlobalHandles::MakeWeak(h2.location(),
408 reinterpret_cast<void*>(&handle_and_id),
409 &TestWeakGlobalHandleCallback);
Steve Blocka7e24c12009-10-30 11:49:00 +0000410
411 // Scavenge treats weak pointers as normal roots.
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000412 heap->CollectGarbage(NEW_SPACE);
Steve Blocka7e24c12009-10-30 11:49:00 +0000413
414 CHECK((*h1)->IsString());
415 CHECK((*h2)->IsHeapNumber());
416
417 CHECK(!WeakPointerCleared);
Steve Block44f0eee2011-05-26 01:26:41 +0100418 CHECK(!global_handles->IsNearDeath(h2.location()));
419 CHECK(!global_handles->IsNearDeath(h1.location()));
Steve Blocka7e24c12009-10-30 11:49:00 +0000420
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000421 GlobalHandles::Destroy(h1.location());
422 GlobalHandles::Destroy(h2.location());
Steve Blocka7e24c12009-10-30 11:49:00 +0000423}
424
425
426TEST(WeakGlobalHandlesMark) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000427 CcTest::InitializeVM();
428 Isolate* isolate = CcTest::i_isolate();
429 Heap* heap = isolate->heap();
430 Factory* factory = isolate->factory();
431 GlobalHandles* global_handles = isolate->global_handles();
Steve Blocka7e24c12009-10-30 11:49:00 +0000432
433 WeakPointerCleared = false;
434
Steve Block6ded16b2010-05-10 14:33:55 +0100435 Handle<Object> h1;
436 Handle<Object> h2;
Steve Blocka7e24c12009-10-30 11:49:00 +0000437
Steve Block6ded16b2010-05-10 14:33:55 +0100438 {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000439 HandleScope scope(isolate);
Steve Block6ded16b2010-05-10 14:33:55 +0100440
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000441 Handle<Object> i = factory->NewStringFromStaticChars("fisk");
442 Handle<Object> u = factory->NewNumber(1.12344);
Steve Block6ded16b2010-05-10 14:33:55 +0100443
Steve Block44f0eee2011-05-26 01:26:41 +0100444 h1 = global_handles->Create(*i);
445 h2 = global_handles->Create(*u);
Steve Block6ded16b2010-05-10 14:33:55 +0100446 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000447
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000448 // Make sure the objects are promoted.
449 heap->CollectGarbage(OLD_POINTER_SPACE);
450 heap->CollectGarbage(NEW_SPACE);
451 CHECK(!heap->InNewSpace(*h1) && !heap->InNewSpace(*h2));
Steve Blocka7e24c12009-10-30 11:49:00 +0000452
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000453 std::pair<Handle<Object>*, int> handle_and_id(&h2, 1234);
454 GlobalHandles::MakeWeak(h2.location(),
455 reinterpret_cast<void*>(&handle_and_id),
456 &TestWeakGlobalHandleCallback);
Steve Blocka7e24c12009-10-30 11:49:00 +0000457 CHECK(!GlobalHandles::IsNearDeath(h1.location()));
458 CHECK(!GlobalHandles::IsNearDeath(h2.location()));
459
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000460 // Incremental marking potentially marked handles before they turned weak.
461 heap->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask);
Steve Blocka7e24c12009-10-30 11:49:00 +0000462
463 CHECK((*h1)->IsString());
464
465 CHECK(WeakPointerCleared);
466 CHECK(!GlobalHandles::IsNearDeath(h1.location()));
Steve Blocka7e24c12009-10-30 11:49:00 +0000467
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000468 GlobalHandles::Destroy(h1.location());
Steve Blocka7e24c12009-10-30 11:49:00 +0000469}
470
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000471
Steve Blocka7e24c12009-10-30 11:49:00 +0000472TEST(DeleteWeakGlobalHandle) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000473 i::FLAG_stress_compaction = false;
474 CcTest::InitializeVM();
475 Isolate* isolate = CcTest::i_isolate();
476 Heap* heap = isolate->heap();
477 Factory* factory = isolate->factory();
478 GlobalHandles* global_handles = isolate->global_handles();
Steve Blocka7e24c12009-10-30 11:49:00 +0000479
480 WeakPointerCleared = false;
481
Steve Block6ded16b2010-05-10 14:33:55 +0100482 Handle<Object> h;
483
484 {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000485 HandleScope scope(isolate);
Steve Block6ded16b2010-05-10 14:33:55 +0100486
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000487 Handle<Object> i = factory->NewStringFromStaticChars("fisk");
Steve Block44f0eee2011-05-26 01:26:41 +0100488 h = global_handles->Create(*i);
Steve Block6ded16b2010-05-10 14:33:55 +0100489 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000490
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000491 std::pair<Handle<Object>*, int> handle_and_id(&h, 1234);
492 GlobalHandles::MakeWeak(h.location(),
493 reinterpret_cast<void*>(&handle_and_id),
494 &TestWeakGlobalHandleCallback);
Steve Blocka7e24c12009-10-30 11:49:00 +0000495
496 // Scanvenge does not recognize weak reference.
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000497 heap->CollectGarbage(NEW_SPACE);
Steve Blocka7e24c12009-10-30 11:49:00 +0000498
499 CHECK(!WeakPointerCleared);
500
501 // Mark-compact treats weak reference properly.
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000502 heap->CollectGarbage(OLD_POINTER_SPACE);
Steve Blocka7e24c12009-10-30 11:49:00 +0000503
504 CHECK(WeakPointerCleared);
505}
506
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000507
Steve Blocka7e24c12009-10-30 11:49:00 +0000508static const char* not_so_random_string_table[] = {
509 "abstract",
510 "boolean",
511 "break",
512 "byte",
513 "case",
514 "catch",
515 "char",
516 "class",
517 "const",
518 "continue",
519 "debugger",
520 "default",
521 "delete",
522 "do",
523 "double",
524 "else",
525 "enum",
526 "export",
527 "extends",
528 "false",
529 "final",
530 "finally",
531 "float",
532 "for",
533 "function",
534 "goto",
535 "if",
536 "implements",
537 "import",
538 "in",
539 "instanceof",
540 "int",
541 "interface",
542 "long",
543 "native",
544 "new",
545 "null",
546 "package",
547 "private",
548 "protected",
549 "public",
550 "return",
551 "short",
552 "static",
553 "super",
554 "switch",
555 "synchronized",
556 "this",
557 "throw",
558 "throws",
559 "transient",
560 "true",
561 "try",
562 "typeof",
563 "var",
564 "void",
565 "volatile",
566 "while",
567 "with",
568 0
569};
570
571
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000572static void CheckInternalizedStrings(const char** strings) {
573 Isolate* isolate = CcTest::i_isolate();
574 Factory* factory = isolate->factory();
Steve Blocka7e24c12009-10-30 11:49:00 +0000575 for (const char* string = *strings; *strings != 0; string = *strings++) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000576 HandleScope scope(isolate);
577 Handle<String> a =
578 isolate->factory()->InternalizeUtf8String(CStrVector(string));
579 // InternalizeUtf8String may return a failure if a GC is needed.
580 CHECK(a->IsInternalizedString());
581 Handle<String> b = factory->InternalizeUtf8String(string);
582 CHECK_EQ(*b, *a);
583 CHECK(b->IsUtf8EqualTo(CStrVector(string)));
584 b = isolate->factory()->InternalizeUtf8String(CStrVector(string));
585 CHECK_EQ(*b, *a);
586 CHECK(b->IsUtf8EqualTo(CStrVector(string)));
Steve Blocka7e24c12009-10-30 11:49:00 +0000587 }
588}
589
590
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000591TEST(StringTable) {
592 CcTest::InitializeVM();
Steve Blocka7e24c12009-10-30 11:49:00 +0000593
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000594 v8::HandleScope sc(CcTest::isolate());
595 CheckInternalizedStrings(not_so_random_string_table);
596 CheckInternalizedStrings(not_so_random_string_table);
Steve Blocka7e24c12009-10-30 11:49:00 +0000597}
598
599
600TEST(FunctionAllocation) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000601 CcTest::InitializeVM();
602 Isolate* isolate = CcTest::i_isolate();
603 Factory* factory = isolate->factory();
Steve Blocka7e24c12009-10-30 11:49:00 +0000604
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000605 v8::HandleScope sc(CcTest::isolate());
606 Handle<String> name = factory->InternalizeUtf8String("theFunction");
607 Handle<JSFunction> function = factory->NewFunction(name);
Steve Blocka7e24c12009-10-30 11:49:00 +0000608
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000609 Handle<Smi> twenty_three(Smi::FromInt(23), isolate);
610 Handle<Smi> twenty_four(Smi::FromInt(24), isolate);
611
612 Handle<String> prop_name = factory->InternalizeUtf8String("theSlot");
613 Handle<JSObject> obj = factory->NewJSObject(function);
614 JSReceiver::SetProperty(obj, prop_name, twenty_three, SLOPPY).Check();
615 CHECK_EQ(Smi::FromInt(23),
616 *Object::GetProperty(obj, prop_name).ToHandleChecked());
Steve Blocka7e24c12009-10-30 11:49:00 +0000617 // Check that we can add properties to function objects.
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000618 JSReceiver::SetProperty(function, prop_name, twenty_four, SLOPPY).Check();
619 CHECK_EQ(Smi::FromInt(24),
620 *Object::GetProperty(function, prop_name).ToHandleChecked());
Steve Blocka7e24c12009-10-30 11:49:00 +0000621}
622
623
624TEST(ObjectProperties) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000625 CcTest::InitializeVM();
626 Isolate* isolate = CcTest::i_isolate();
627 Factory* factory = isolate->factory();
Steve Blocka7e24c12009-10-30 11:49:00 +0000628
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000629 v8::HandleScope sc(CcTest::isolate());
630 Handle<String> object_string(String::cast(CcTest::heap()->Object_string()));
631 Handle<Object> object = Object::GetProperty(
632 CcTest::i_isolate()->global_object(), object_string).ToHandleChecked();
633 Handle<JSFunction> constructor = Handle<JSFunction>::cast(object);
634 Handle<JSObject> obj = factory->NewJSObject(constructor);
635 Handle<String> first = factory->InternalizeUtf8String("first");
636 Handle<String> second = factory->InternalizeUtf8String("second");
637
638 Handle<Smi> one(Smi::FromInt(1), isolate);
639 Handle<Smi> two(Smi::FromInt(2), isolate);
Steve Blocka7e24c12009-10-30 11:49:00 +0000640
641 // check for empty
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000642 v8::Maybe<bool> maybe = JSReceiver::HasOwnProperty(obj, first);
643 CHECK(maybe.has_value);
644 CHECK(!maybe.value);
Steve Blocka7e24c12009-10-30 11:49:00 +0000645
646 // add first
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000647 JSReceiver::SetProperty(obj, first, one, SLOPPY).Check();
648 maybe = JSReceiver::HasOwnProperty(obj, first);
649 CHECK(maybe.has_value);
650 CHECK(maybe.value);
Steve Blocka7e24c12009-10-30 11:49:00 +0000651
652 // delete first
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000653 JSReceiver::DeleteProperty(obj, first, JSReceiver::NORMAL_DELETION).Check();
654 maybe = JSReceiver::HasOwnProperty(obj, first);
655 CHECK(maybe.has_value);
656 CHECK(!maybe.value);
Steve Blocka7e24c12009-10-30 11:49:00 +0000657
658 // add first and then second
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000659 JSReceiver::SetProperty(obj, first, one, SLOPPY).Check();
660 JSReceiver::SetProperty(obj, second, two, SLOPPY).Check();
661 maybe = JSReceiver::HasOwnProperty(obj, first);
662 CHECK(maybe.has_value);
663 CHECK(maybe.value);
664 maybe = JSReceiver::HasOwnProperty(obj, second);
665 CHECK(maybe.has_value);
666 CHECK(maybe.value);
Steve Blocka7e24c12009-10-30 11:49:00 +0000667
668 // delete first and then second
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000669 JSReceiver::DeleteProperty(obj, first, JSReceiver::NORMAL_DELETION).Check();
670 maybe = JSReceiver::HasOwnProperty(obj, second);
671 CHECK(maybe.has_value);
672 CHECK(maybe.value);
673 JSReceiver::DeleteProperty(obj, second, JSReceiver::NORMAL_DELETION).Check();
674 maybe = JSReceiver::HasOwnProperty(obj, first);
675 CHECK(maybe.has_value);
676 CHECK(!maybe.value);
677 maybe = JSReceiver::HasOwnProperty(obj, second);
678 CHECK(maybe.has_value);
679 CHECK(!maybe.value);
Steve Blocka7e24c12009-10-30 11:49:00 +0000680
681 // add first and then second
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000682 JSReceiver::SetProperty(obj, first, one, SLOPPY).Check();
683 JSReceiver::SetProperty(obj, second, two, SLOPPY).Check();
684 maybe = JSReceiver::HasOwnProperty(obj, first);
685 CHECK(maybe.has_value);
686 CHECK(maybe.value);
687 maybe = JSReceiver::HasOwnProperty(obj, second);
688 CHECK(maybe.has_value);
689 CHECK(maybe.value);
Steve Blocka7e24c12009-10-30 11:49:00 +0000690
691 // delete second and then first
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000692 JSReceiver::DeleteProperty(obj, second, JSReceiver::NORMAL_DELETION).Check();
693 maybe = JSReceiver::HasOwnProperty(obj, first);
694 CHECK(maybe.has_value);
695 CHECK(maybe.value);
696 JSReceiver::DeleteProperty(obj, first, JSReceiver::NORMAL_DELETION).Check();
697 maybe = JSReceiver::HasOwnProperty(obj, first);
698 CHECK(maybe.has_value);
699 CHECK(!maybe.value);
700 maybe = JSReceiver::HasOwnProperty(obj, second);
701 CHECK(maybe.has_value);
702 CHECK(!maybe.value);
Steve Blocka7e24c12009-10-30 11:49:00 +0000703
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000704 // check string and internalized string match
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100705 const char* string1 = "fisk";
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000706 Handle<String> s1 = factory->NewStringFromAsciiChecked(string1);
707 JSReceiver::SetProperty(obj, s1, one, SLOPPY).Check();
708 Handle<String> s1_string = factory->InternalizeUtf8String(string1);
709 maybe = JSReceiver::HasOwnProperty(obj, s1_string);
710 CHECK(maybe.has_value);
711 CHECK(maybe.value);
Steve Blocka7e24c12009-10-30 11:49:00 +0000712
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000713 // check internalized string and string match
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100714 const char* string2 = "fugl";
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000715 Handle<String> s2_string = factory->InternalizeUtf8String(string2);
716 JSReceiver::SetProperty(obj, s2_string, one, SLOPPY).Check();
717 Handle<String> s2 = factory->NewStringFromAsciiChecked(string2);
718 maybe = JSReceiver::HasOwnProperty(obj, s2);
719 CHECK(maybe.has_value);
720 CHECK(maybe.value);
Steve Blocka7e24c12009-10-30 11:49:00 +0000721}
722
723
724TEST(JSObjectMaps) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000725 CcTest::InitializeVM();
726 Isolate* isolate = CcTest::i_isolate();
727 Factory* factory = isolate->factory();
Steve Blocka7e24c12009-10-30 11:49:00 +0000728
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000729 v8::HandleScope sc(CcTest::isolate());
730 Handle<String> name = factory->InternalizeUtf8String("theFunction");
731 Handle<JSFunction> function = factory->NewFunction(name);
Steve Block6ded16b2010-05-10 14:33:55 +0100732
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000733 Handle<String> prop_name = factory->InternalizeUtf8String("theSlot");
734 Handle<JSObject> obj = factory->NewJSObject(function);
735 Handle<Map> initial_map(function->initial_map());
Steve Blocka7e24c12009-10-30 11:49:00 +0000736
737 // Set a propery
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000738 Handle<Smi> twenty_three(Smi::FromInt(23), isolate);
739 JSReceiver::SetProperty(obj, prop_name, twenty_three, SLOPPY).Check();
740 CHECK_EQ(Smi::FromInt(23),
741 *Object::GetProperty(obj, prop_name).ToHandleChecked());
Steve Blocka7e24c12009-10-30 11:49:00 +0000742
743 // Check the map has changed
Steve Block6ded16b2010-05-10 14:33:55 +0100744 CHECK(*initial_map != obj->map());
Steve Blocka7e24c12009-10-30 11:49:00 +0000745}
746
747
748TEST(JSArray) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000749 CcTest::InitializeVM();
750 Isolate* isolate = CcTest::i_isolate();
751 Factory* factory = isolate->factory();
Steve Blocka7e24c12009-10-30 11:49:00 +0000752
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000753 v8::HandleScope sc(CcTest::isolate());
754 Handle<String> name = factory->InternalizeUtf8String("Array");
755 Handle<Object> fun_obj = Object::GetProperty(
756 CcTest::i_isolate()->global_object(), name).ToHandleChecked();
757 Handle<JSFunction> function = Handle<JSFunction>::cast(fun_obj);
Steve Blocka7e24c12009-10-30 11:49:00 +0000758
759 // Allocate the object.
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000760 Handle<Object> element;
761 Handle<JSObject> object = factory->NewJSObject(function);
Steve Block6ded16b2010-05-10 14:33:55 +0100762 Handle<JSArray> array = Handle<JSArray>::cast(object);
Kristian Monsen80d68ea2010-09-08 11:05:35 +0100763 // We just initialized the VM, no heap allocation failure yet.
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000764 JSArray::Initialize(array, 0);
Steve Blocka7e24c12009-10-30 11:49:00 +0000765
766 // Set array length to 0.
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000767 JSArray::SetElementsLength(array, handle(Smi::FromInt(0), isolate)).Check();
Steve Blocka7e24c12009-10-30 11:49:00 +0000768 CHECK_EQ(Smi::FromInt(0), array->length());
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100769 // Must be in fast mode.
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000770 CHECK(array->HasFastSmiOrObjectElements());
Steve Blocka7e24c12009-10-30 11:49:00 +0000771
772 // array[length] = name.
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000773 JSReceiver::SetElement(array, 0, name, NONE, SLOPPY).Check();
Steve Blocka7e24c12009-10-30 11:49:00 +0000774 CHECK_EQ(Smi::FromInt(1), array->length());
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000775 element = i::Object::GetElement(isolate, array, 0).ToHandleChecked();
776 CHECK_EQ(*element, *name);
Steve Blocka7e24c12009-10-30 11:49:00 +0000777
Steve Block6ded16b2010-05-10 14:33:55 +0100778 // Set array length with larger than smi value.
779 Handle<Object> length =
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000780 factory->NewNumberFromUint(static_cast<uint32_t>(Smi::kMaxValue) + 1);
781 JSArray::SetElementsLength(array, length).Check();
Steve Blocka7e24c12009-10-30 11:49:00 +0000782
783 uint32_t int_length = 0;
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100784 CHECK(length->ToArrayIndex(&int_length));
Steve Block6ded16b2010-05-10 14:33:55 +0100785 CHECK_EQ(*length, array->length());
Steve Blocka7e24c12009-10-30 11:49:00 +0000786 CHECK(array->HasDictionaryElements()); // Must be in slow mode.
787
788 // array[length] = name.
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000789 JSReceiver::SetElement(array, int_length, name, NONE, SLOPPY).Check();
Steve Blocka7e24c12009-10-30 11:49:00 +0000790 uint32_t new_int_length = 0;
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100791 CHECK(array->length()->ToArrayIndex(&new_int_length));
Steve Blocka7e24c12009-10-30 11:49:00 +0000792 CHECK_EQ(static_cast<double>(int_length), new_int_length - 1);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000793 element = Object::GetElement(isolate, array, int_length).ToHandleChecked();
794 CHECK_EQ(*element, *name);
795 element = Object::GetElement(isolate, array, 0).ToHandleChecked();
796 CHECK_EQ(*element, *name);
Steve Blocka7e24c12009-10-30 11:49:00 +0000797}
798
799
800TEST(JSObjectCopy) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000801 CcTest::InitializeVM();
802 Isolate* isolate = CcTest::i_isolate();
803 Factory* factory = isolate->factory();
Steve Blocka7e24c12009-10-30 11:49:00 +0000804
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000805 v8::HandleScope sc(CcTest::isolate());
806 Handle<String> object_string(String::cast(CcTest::heap()->Object_string()));
807 Handle<Object> object = Object::GetProperty(
808 CcTest::i_isolate()->global_object(), object_string).ToHandleChecked();
809 Handle<JSFunction> constructor = Handle<JSFunction>::cast(object);
810 Handle<JSObject> obj = factory->NewJSObject(constructor);
811 Handle<String> first = factory->InternalizeUtf8String("first");
812 Handle<String> second = factory->InternalizeUtf8String("second");
Steve Blocka7e24c12009-10-30 11:49:00 +0000813
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000814 Handle<Smi> one(Smi::FromInt(1), isolate);
815 Handle<Smi> two(Smi::FromInt(2), isolate);
Steve Blocka7e24c12009-10-30 11:49:00 +0000816
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000817 JSReceiver::SetProperty(obj, first, one, SLOPPY).Check();
818 JSReceiver::SetProperty(obj, second, two, SLOPPY).Check();
819
820 JSReceiver::SetElement(obj, 0, first, NONE, SLOPPY).Check();
821 JSReceiver::SetElement(obj, 1, second, NONE, SLOPPY).Check();
Steve Blocka7e24c12009-10-30 11:49:00 +0000822
823 // Make the clone.
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000824 Handle<Object> value1, value2;
825 Handle<JSObject> clone = factory->CopyJSObject(obj);
Steve Block6ded16b2010-05-10 14:33:55 +0100826 CHECK(!clone.is_identical_to(obj));
Steve Blocka7e24c12009-10-30 11:49:00 +0000827
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000828 value1 = Object::GetElement(isolate, obj, 0).ToHandleChecked();
829 value2 = Object::GetElement(isolate, clone, 0).ToHandleChecked();
830 CHECK_EQ(*value1, *value2);
831 value1 = Object::GetElement(isolate, obj, 1).ToHandleChecked();
832 value2 = Object::GetElement(isolate, clone, 1).ToHandleChecked();
833 CHECK_EQ(*value1, *value2);
Steve Blocka7e24c12009-10-30 11:49:00 +0000834
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000835 value1 = Object::GetProperty(obj, first).ToHandleChecked();
836 value2 = Object::GetProperty(clone, first).ToHandleChecked();
837 CHECK_EQ(*value1, *value2);
838 value1 = Object::GetProperty(obj, second).ToHandleChecked();
839 value2 = Object::GetProperty(clone, second).ToHandleChecked();
840 CHECK_EQ(*value1, *value2);
Steve Blocka7e24c12009-10-30 11:49:00 +0000841
842 // Flip the values.
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000843 JSReceiver::SetProperty(clone, first, two, SLOPPY).Check();
844 JSReceiver::SetProperty(clone, second, one, SLOPPY).Check();
Steve Blocka7e24c12009-10-30 11:49:00 +0000845
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000846 JSReceiver::SetElement(clone, 0, second, NONE, SLOPPY).Check();
847 JSReceiver::SetElement(clone, 1, first, NONE, SLOPPY).Check();
Steve Blocka7e24c12009-10-30 11:49:00 +0000848
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000849 value1 = Object::GetElement(isolate, obj, 1).ToHandleChecked();
850 value2 = Object::GetElement(isolate, clone, 0).ToHandleChecked();
851 CHECK_EQ(*value1, *value2);
852 value1 = Object::GetElement(isolate, obj, 0).ToHandleChecked();
853 value2 = Object::GetElement(isolate, clone, 1).ToHandleChecked();
854 CHECK_EQ(*value1, *value2);
Steve Blocka7e24c12009-10-30 11:49:00 +0000855
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000856 value1 = Object::GetProperty(obj, second).ToHandleChecked();
857 value2 = Object::GetProperty(clone, first).ToHandleChecked();
858 CHECK_EQ(*value1, *value2);
859 value1 = Object::GetProperty(obj, first).ToHandleChecked();
860 value2 = Object::GetProperty(clone, second).ToHandleChecked();
861 CHECK_EQ(*value1, *value2);
Steve Blocka7e24c12009-10-30 11:49:00 +0000862}
863
864
865TEST(StringAllocation) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000866 CcTest::InitializeVM();
867 Isolate* isolate = CcTest::i_isolate();
868 Factory* factory = isolate->factory();
Steve Blocka7e24c12009-10-30 11:49:00 +0000869
870 const unsigned char chars[] = { 0xe5, 0xa4, 0xa7 };
871 for (int length = 0; length < 100; length++) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000872 v8::HandleScope scope(CcTest::isolate());
873 char* non_one_byte = NewArray<char>(3 * length + 1);
874 char* one_byte = NewArray<char>(length + 1);
875 non_one_byte[3 * length] = 0;
876 one_byte[length] = 0;
Steve Blocka7e24c12009-10-30 11:49:00 +0000877 for (int i = 0; i < length; i++) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000878 one_byte[i] = 'a';
879 non_one_byte[3 * i] = chars[0];
880 non_one_byte[3 * i + 1] = chars[1];
881 non_one_byte[3 * i + 2] = chars[2];
Steve Blocka7e24c12009-10-30 11:49:00 +0000882 }
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000883 Handle<String> non_one_byte_sym = factory->InternalizeUtf8String(
884 Vector<const char>(non_one_byte, 3 * length));
885 CHECK_EQ(length, non_one_byte_sym->length());
886 Handle<String> one_byte_sym =
887 factory->InternalizeOneByteString(OneByteVector(one_byte, length));
888 CHECK_EQ(length, one_byte_sym->length());
889 Handle<String> non_one_byte_str =
890 factory->NewStringFromUtf8(Vector<const char>(non_one_byte, 3 * length))
891 .ToHandleChecked();
892 non_one_byte_str->Hash();
893 CHECK_EQ(length, non_one_byte_str->length());
894 Handle<String> one_byte_str =
895 factory->NewStringFromUtf8(Vector<const char>(one_byte, length))
896 .ToHandleChecked();
897 one_byte_str->Hash();
898 CHECK_EQ(length, one_byte_str->length());
899 DeleteArray(non_one_byte);
900 DeleteArray(one_byte);
Steve Blocka7e24c12009-10-30 11:49:00 +0000901 }
902}
903
904
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000905static int ObjectsFoundInHeap(Heap* heap, Handle<Object> objs[], int size) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000906 // Count the number of objects found in the heap.
907 int found_count = 0;
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000908 HeapIterator iterator(heap);
Leon Clarked91b9f72010-01-27 17:25:45 +0000909 for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000910 for (int i = 0; i < size; i++) {
911 if (*objs[i] == obj) {
912 found_count++;
913 }
914 }
915 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000916 return found_count;
917}
918
919
920TEST(Iteration) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000921 CcTest::InitializeVM();
922 Isolate* isolate = CcTest::i_isolate();
923 Factory* factory = isolate->factory();
924 v8::HandleScope scope(CcTest::isolate());
Steve Blocka7e24c12009-10-30 11:49:00 +0000925
926 // Array of objects to scan haep for.
927 const int objs_count = 6;
928 Handle<Object> objs[objs_count];
929 int next_objs_index = 0;
930
931 // Allocate a JS array to OLD_POINTER_SPACE and NEW_SPACE
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000932 objs[next_objs_index++] = factory->NewJSArray(10);
933 objs[next_objs_index++] = factory->NewJSArray(10,
934 FAST_HOLEY_ELEMENTS,
935 TENURED);
Steve Blocka7e24c12009-10-30 11:49:00 +0000936
937 // Allocate a small string to OLD_DATA_SPACE and NEW_SPACE
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000938 objs[next_objs_index++] = factory->NewStringFromStaticChars("abcdefghij");
Steve Blocka7e24c12009-10-30 11:49:00 +0000939 objs[next_objs_index++] =
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000940 factory->NewStringFromStaticChars("abcdefghij", TENURED);
Steve Blocka7e24c12009-10-30 11:49:00 +0000941
942 // Allocate a large string (for large object space).
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000943 int large_size = Page::kMaxRegularHeapObjectSize + 1;
Steve Blocka7e24c12009-10-30 11:49:00 +0000944 char* str = new char[large_size];
945 for (int i = 0; i < large_size - 1; ++i) str[i] = 'a';
946 str[large_size - 1] = '\0';
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000947 objs[next_objs_index++] = factory->NewStringFromAsciiChecked(str, TENURED);
Steve Blocka7e24c12009-10-30 11:49:00 +0000948 delete[] str;
949
950 // Add a Map object to look for.
951 objs[next_objs_index++] = Handle<Map>(HeapObject::cast(*objs[0])->map());
952
953 CHECK_EQ(objs_count, next_objs_index);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000954 CHECK_EQ(objs_count, ObjectsFoundInHeap(CcTest::heap(), objs, objs_count));
Steve Blocka7e24c12009-10-30 11:49:00 +0000955}
Steve Block6ded16b2010-05-10 14:33:55 +0100956
957
Steve Block6ded16b2010-05-10 14:33:55 +0100958TEST(EmptyHandleEscapeFrom) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000959 CcTest::InitializeVM();
Steve Block6ded16b2010-05-10 14:33:55 +0100960
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000961 v8::HandleScope scope(CcTest::isolate());
Steve Block6ded16b2010-05-10 14:33:55 +0100962 Handle<JSObject> runaway;
963
964 {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000965 v8::EscapableHandleScope nested(CcTest::isolate());
Steve Block6ded16b2010-05-10 14:33:55 +0100966 Handle<JSObject> empty;
967 runaway = empty.EscapeFrom(&nested);
968 }
969
970 CHECK(runaway.is_null());
971}
972
973
974static int LenFromSize(int size) {
975 return (size - FixedArray::kHeaderSize) / kPointerSize;
976}
977
978
979TEST(Regression39128) {
980 // Test case for crbug.com/39128.
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000981 CcTest::InitializeVM();
982 Isolate* isolate = CcTest::i_isolate();
983 TestHeap* heap = CcTest::test_heap();
Steve Block6ded16b2010-05-10 14:33:55 +0100984
985 // Increase the chance of 'bump-the-pointer' allocation in old space.
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000986 heap->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask);
Steve Block6ded16b2010-05-10 14:33:55 +0100987
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000988 v8::HandleScope scope(CcTest::isolate());
Steve Block6ded16b2010-05-10 14:33:55 +0100989
990 // The plan: create JSObject which references objects in new space.
991 // Then clone this object (forcing it to go into old space) and check
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100992 // that region dirty marks are updated correctly.
Steve Block6ded16b2010-05-10 14:33:55 +0100993
994 // Step 1: prepare a map for the object. We add 1 inobject property to it.
Steve Block6ded16b2010-05-10 14:33:55 +0100995 // Create a map with single inobject property.
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000996 Handle<Map> my_map = Map::Create(CcTest::i_isolate(), 1);
Steve Block6ded16b2010-05-10 14:33:55 +0100997 int n_properties = my_map->inobject_properties();
998 CHECK_GT(n_properties, 0);
999
1000 int object_size = my_map->instance_size();
1001
1002 // Step 2: allocate a lot of objects so to almost fill new space: we need
1003 // just enough room to allocate JSObject and thus fill the newspace.
1004
1005 int allocation_amount = Min(FixedArray::kMaxSize,
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001006 Page::kMaxRegularHeapObjectSize + kPointerSize);
Steve Block6ded16b2010-05-10 14:33:55 +01001007 int allocation_len = LenFromSize(allocation_amount);
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001008 NewSpace* new_space = heap->new_space();
Steve Block6ded16b2010-05-10 14:33:55 +01001009 Address* top_addr = new_space->allocation_top_address();
1010 Address* limit_addr = new_space->allocation_limit_address();
1011 while ((*limit_addr - *top_addr) > allocation_amount) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001012 CHECK(!heap->always_allocate());
1013 Object* array = heap->AllocateFixedArray(allocation_len).ToObjectChecked();
Steve Block6ded16b2010-05-10 14:33:55 +01001014 CHECK(new_space->Contains(array));
1015 }
1016
1017 // Step 3: now allocate fixed array and JSObject to fill the whole new space.
1018 int to_fill = static_cast<int>(*limit_addr - *top_addr - object_size);
1019 int fixed_array_len = LenFromSize(to_fill);
1020 CHECK(fixed_array_len < FixedArray::kMaxLength);
1021
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001022 CHECK(!heap->always_allocate());
1023 Object* array = heap->AllocateFixedArray(fixed_array_len).ToObjectChecked();
Steve Block6ded16b2010-05-10 14:33:55 +01001024 CHECK(new_space->Contains(array));
1025
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001026 Object* object = heap->AllocateJSObjectFromMap(*my_map).ToObjectChecked();
Steve Block6ded16b2010-05-10 14:33:55 +01001027 CHECK(new_space->Contains(object));
1028 JSObject* jsobject = JSObject::cast(object);
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001029 CHECK_EQ(0, FixedArray::cast(jsobject->elements())->length());
Steve Block6ded16b2010-05-10 14:33:55 +01001030 CHECK_EQ(0, jsobject->properties()->length());
1031 // Create a reference to object in new space in jsobject.
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001032 FieldIndex index = FieldIndex::ForInObjectOffset(
1033 JSObject::kHeaderSize - kPointerSize);
1034 jsobject->FastPropertyAtPut(index, array);
Steve Block6ded16b2010-05-10 14:33:55 +01001035
1036 CHECK_EQ(0, static_cast<int>(*limit_addr - *top_addr));
1037
1038 // Step 4: clone jsobject, but force always allocate first to create a clone
1039 // in old pointer space.
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001040 Address old_pointer_space_top = heap->old_pointer_space()->top();
1041 AlwaysAllocateScope aa_scope(isolate);
1042 Object* clone_obj = heap->CopyJSObject(jsobject).ToObjectChecked();
Steve Block6ded16b2010-05-10 14:33:55 +01001043 JSObject* clone = JSObject::cast(clone_obj);
1044 if (clone->address() != old_pointer_space_top) {
1045 // Alas, got allocated from free list, we cannot do checks.
1046 return;
1047 }
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001048 CHECK(heap->old_pointer_space()->Contains(clone->address()));
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001049}
1050
Ben Murdochf87a2032010-10-22 12:50:53 +01001051
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001052UNINITIALIZED_TEST(TestCodeFlushing) {
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001053 // If we do not flush code this test is invalid.
1054 if (!FLAG_flush_code) return;
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001055 i::FLAG_allow_natives_syntax = true;
1056 i::FLAG_optimize_for_size = false;
1057 v8::Isolate* isolate = v8::Isolate::New();
1058 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
1059 isolate->Enter();
1060 Factory* factory = i_isolate->factory();
1061 {
1062 v8::HandleScope scope(isolate);
1063 v8::Context::New(isolate)->Enter();
1064 const char* source =
1065 "function foo() {"
1066 " var x = 42;"
1067 " var y = 42;"
1068 " var z = x + y;"
1069 "};"
1070 "foo()";
1071 Handle<String> foo_name = factory->InternalizeUtf8String("foo");
1072
1073 // This compile will add the code to the compilation cache.
1074 {
1075 v8::HandleScope scope(isolate);
1076 CompileRun(source);
1077 }
1078
1079 // Check function is compiled.
1080 Handle<Object> func_value = Object::GetProperty(i_isolate->global_object(),
1081 foo_name).ToHandleChecked();
1082 CHECK(func_value->IsJSFunction());
1083 Handle<JSFunction> function = Handle<JSFunction>::cast(func_value);
1084 CHECK(function->shared()->is_compiled());
1085
1086 // The code will survive at least two GCs.
1087 i_isolate->heap()->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask);
1088 i_isolate->heap()->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask);
1089 CHECK(function->shared()->is_compiled());
1090
1091 // Simulate several GCs that use full marking.
1092 const int kAgingThreshold = 6;
1093 for (int i = 0; i < kAgingThreshold; i++) {
1094 i_isolate->heap()->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask);
1095 }
1096
1097 // foo should no longer be in the compilation cache
1098 CHECK(!function->shared()->is_compiled() || function->IsOptimized());
1099 CHECK(!function->is_compiled() || function->IsOptimized());
1100 // Call foo to get it recompiled.
1101 CompileRun("foo()");
1102 CHECK(function->shared()->is_compiled());
1103 CHECK(function->is_compiled());
1104 }
1105 isolate->Exit();
1106 isolate->Dispose();
1107}
1108
1109
1110TEST(TestCodeFlushingPreAged) {
1111 // If we do not flush code this test is invalid.
1112 if (!FLAG_flush_code) return;
1113 i::FLAG_allow_natives_syntax = true;
1114 i::FLAG_optimize_for_size = true;
1115 CcTest::InitializeVM();
1116 Isolate* isolate = CcTest::i_isolate();
1117 Factory* factory = isolate->factory();
1118 v8::HandleScope scope(CcTest::isolate());
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001119 const char* source = "function foo() {"
1120 " var x = 42;"
1121 " var y = 42;"
1122 " var z = x + y;"
1123 "};"
1124 "foo()";
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001125 Handle<String> foo_name = factory->InternalizeUtf8String("foo");
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001126
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001127 // Compile foo, but don't run it.
1128 { v8::HandleScope scope(CcTest::isolate());
Ben Murdochb0fe1622011-05-05 13:52:32 +01001129 CompileRun(source);
1130 }
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001131
1132 // Check function is compiled.
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001133 Handle<Object> func_value =
1134 Object::GetProperty(isolate->global_object(), foo_name).ToHandleChecked();
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001135 CHECK(func_value->IsJSFunction());
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001136 Handle<JSFunction> function = Handle<JSFunction>::cast(func_value);
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001137 CHECK(function->shared()->is_compiled());
1138
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001139 // The code has been run so will survive at least one GC.
1140 CcTest::heap()->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask);
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001141 CHECK(function->shared()->is_compiled());
Iain Merrick75681382010-08-19 15:07:18 +01001142
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001143 // The code was only run once, so it should be pre-aged and collected on the
1144 // next GC.
1145 CcTest::heap()->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask);
1146 CHECK(!function->shared()->is_compiled() || function->IsOptimized());
1147
1148 // Execute the function again twice, and ensure it is reset to the young age.
1149 { v8::HandleScope scope(CcTest::isolate());
1150 CompileRun("foo();"
1151 "foo();");
1152 }
1153
1154 // The code will survive at least two GC now that it is young again.
1155 CcTest::heap()->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask);
1156 CcTest::heap()->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask);
1157 CHECK(function->shared()->is_compiled());
1158
1159 // Simulate several GCs that use full marking.
1160 const int kAgingThreshold = 6;
1161 for (int i = 0; i < kAgingThreshold; i++) {
1162 CcTest::heap()->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask);
1163 }
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001164
1165 // foo should no longer be in the compilation cache
Ben Murdochb0fe1622011-05-05 13:52:32 +01001166 CHECK(!function->shared()->is_compiled() || function->IsOptimized());
1167 CHECK(!function->is_compiled() || function->IsOptimized());
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001168 // Call foo to get it recompiled.
1169 CompileRun("foo()");
1170 CHECK(function->shared()->is_compiled());
Iain Merrick75681382010-08-19 15:07:18 +01001171 CHECK(function->is_compiled());
Steve Block6ded16b2010-05-10 14:33:55 +01001172}
Ben Murdochf87a2032010-10-22 12:50:53 +01001173
1174
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001175TEST(TestCodeFlushingIncremental) {
1176 // If we do not flush code this test is invalid.
1177 if (!FLAG_flush_code || !FLAG_flush_code_incrementally) return;
1178 i::FLAG_allow_natives_syntax = true;
1179 i::FLAG_optimize_for_size = false;
1180 CcTest::InitializeVM();
1181 Isolate* isolate = CcTest::i_isolate();
1182 Factory* factory = isolate->factory();
1183 v8::HandleScope scope(CcTest::isolate());
1184 const char* source = "function foo() {"
1185 " var x = 42;"
1186 " var y = 42;"
1187 " var z = x + y;"
1188 "};"
1189 "foo()";
1190 Handle<String> foo_name = factory->InternalizeUtf8String("foo");
1191
1192 // This compile will add the code to the compilation cache.
1193 { v8::HandleScope scope(CcTest::isolate());
1194 CompileRun(source);
1195 }
1196
1197 // Check function is compiled.
1198 Handle<Object> func_value =
1199 Object::GetProperty(isolate->global_object(), foo_name).ToHandleChecked();
1200 CHECK(func_value->IsJSFunction());
1201 Handle<JSFunction> function = Handle<JSFunction>::cast(func_value);
1202 CHECK(function->shared()->is_compiled());
1203
1204 // The code will survive at least two GCs.
1205 CcTest::heap()->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask);
1206 CcTest::heap()->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask);
1207 CHECK(function->shared()->is_compiled());
1208
1209 // Simulate several GCs that use incremental marking.
1210 const int kAgingThreshold = 6;
1211 for (int i = 0; i < kAgingThreshold; i++) {
1212 SimulateIncrementalMarking(CcTest::heap());
1213 CcTest::heap()->CollectAllGarbage(Heap::kNoGCFlags);
1214 }
1215 CHECK(!function->shared()->is_compiled() || function->IsOptimized());
1216 CHECK(!function->is_compiled() || function->IsOptimized());
1217
1218 // This compile will compile the function again.
1219 { v8::HandleScope scope(CcTest::isolate());
1220 CompileRun("foo();");
1221 }
1222
1223 // Simulate several GCs that use incremental marking but make sure
1224 // the loop breaks once the function is enqueued as a candidate.
1225 for (int i = 0; i < kAgingThreshold; i++) {
1226 SimulateIncrementalMarking(CcTest::heap());
1227 if (!function->next_function_link()->IsUndefined()) break;
1228 CcTest::heap()->CollectAllGarbage(Heap::kNoGCFlags);
1229 }
1230
1231 // Force optimization while incremental marking is active and while
1232 // the function is enqueued as a candidate.
1233 { v8::HandleScope scope(CcTest::isolate());
1234 CompileRun("%OptimizeFunctionOnNextCall(foo); foo();");
1235 }
1236
1237 // Simulate one final GC to make sure the candidate queue is sane.
1238 CcTest::heap()->CollectAllGarbage(Heap::kNoGCFlags);
1239 CHECK(function->shared()->is_compiled() || !function->IsOptimized());
1240 CHECK(function->is_compiled() || !function->IsOptimized());
1241}
1242
1243
1244TEST(TestCodeFlushingIncrementalScavenge) {
1245 // If we do not flush code this test is invalid.
1246 if (!FLAG_flush_code || !FLAG_flush_code_incrementally) return;
1247 i::FLAG_allow_natives_syntax = true;
1248 i::FLAG_optimize_for_size = false;
1249 CcTest::InitializeVM();
1250 Isolate* isolate = CcTest::i_isolate();
1251 Factory* factory = isolate->factory();
1252 v8::HandleScope scope(CcTest::isolate());
1253 const char* source = "var foo = function() {"
1254 " var x = 42;"
1255 " var y = 42;"
1256 " var z = x + y;"
1257 "};"
1258 "foo();"
1259 "var bar = function() {"
1260 " var x = 23;"
1261 "};"
1262 "bar();";
1263 Handle<String> foo_name = factory->InternalizeUtf8String("foo");
1264 Handle<String> bar_name = factory->InternalizeUtf8String("bar");
1265
1266 // Perfrom one initial GC to enable code flushing.
1267 CcTest::heap()->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask);
1268
1269 // This compile will add the code to the compilation cache.
1270 { v8::HandleScope scope(CcTest::isolate());
1271 CompileRun(source);
1272 }
1273
1274 // Check functions are compiled.
1275 Handle<Object> func_value =
1276 Object::GetProperty(isolate->global_object(), foo_name).ToHandleChecked();
1277 CHECK(func_value->IsJSFunction());
1278 Handle<JSFunction> function = Handle<JSFunction>::cast(func_value);
1279 CHECK(function->shared()->is_compiled());
1280 Handle<Object> func_value2 =
1281 Object::GetProperty(isolate->global_object(), bar_name).ToHandleChecked();
1282 CHECK(func_value2->IsJSFunction());
1283 Handle<JSFunction> function2 = Handle<JSFunction>::cast(func_value2);
1284 CHECK(function2->shared()->is_compiled());
1285
1286 // Clear references to functions so that one of them can die.
1287 { v8::HandleScope scope(CcTest::isolate());
1288 CompileRun("foo = 0; bar = 0;");
1289 }
1290
1291 // Bump the code age so that flushing is triggered while the function
1292 // object is still located in new-space.
1293 const int kAgingThreshold = 6;
1294 for (int i = 0; i < kAgingThreshold; i++) {
1295 function->shared()->code()->MakeOlder(static_cast<MarkingParity>(i % 2));
1296 function2->shared()->code()->MakeOlder(static_cast<MarkingParity>(i % 2));
1297 }
1298
1299 // Simulate incremental marking so that the functions are enqueued as
1300 // code flushing candidates. Then kill one of the functions. Finally
1301 // perform a scavenge while incremental marking is still running.
1302 SimulateIncrementalMarking(CcTest::heap());
1303 *function2.location() = NULL;
1304 CcTest::heap()->CollectGarbage(NEW_SPACE, "test scavenge while marking");
1305
1306 // Simulate one final GC to make sure the candidate queue is sane.
1307 CcTest::heap()->CollectAllGarbage(Heap::kNoGCFlags);
1308 CHECK(!function->shared()->is_compiled() || function->IsOptimized());
1309 CHECK(!function->is_compiled() || function->IsOptimized());
1310}
1311
1312
1313TEST(TestCodeFlushingIncrementalAbort) {
1314 // If we do not flush code this test is invalid.
1315 if (!FLAG_flush_code || !FLAG_flush_code_incrementally) return;
1316 i::FLAG_allow_natives_syntax = true;
1317 i::FLAG_optimize_for_size = false;
1318 CcTest::InitializeVM();
1319 Isolate* isolate = CcTest::i_isolate();
1320 Factory* factory = isolate->factory();
1321 Heap* heap = isolate->heap();
1322 v8::HandleScope scope(CcTest::isolate());
1323 const char* source = "function foo() {"
1324 " var x = 42;"
1325 " var y = 42;"
1326 " var z = x + y;"
1327 "};"
1328 "foo()";
1329 Handle<String> foo_name = factory->InternalizeUtf8String("foo");
1330
1331 // This compile will add the code to the compilation cache.
1332 { v8::HandleScope scope(CcTest::isolate());
1333 CompileRun(source);
1334 }
1335
1336 // Check function is compiled.
1337 Handle<Object> func_value =
1338 Object::GetProperty(isolate->global_object(), foo_name).ToHandleChecked();
1339 CHECK(func_value->IsJSFunction());
1340 Handle<JSFunction> function = Handle<JSFunction>::cast(func_value);
1341 CHECK(function->shared()->is_compiled());
1342
1343 // The code will survive at least two GCs.
1344 heap->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask);
1345 heap->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask);
1346 CHECK(function->shared()->is_compiled());
1347
1348 // Bump the code age so that flushing is triggered.
1349 const int kAgingThreshold = 6;
1350 for (int i = 0; i < kAgingThreshold; i++) {
1351 function->shared()->code()->MakeOlder(static_cast<MarkingParity>(i % 2));
1352 }
1353
1354 // Simulate incremental marking so that the function is enqueued as
1355 // code flushing candidate.
1356 SimulateIncrementalMarking(heap);
1357
1358 // Enable the debugger and add a breakpoint while incremental marking
1359 // is running so that incremental marking aborts and code flushing is
1360 // disabled.
1361 int position = 0;
1362 Handle<Object> breakpoint_object(Smi::FromInt(0), isolate);
1363 isolate->debug()->SetBreakPoint(function, breakpoint_object, &position);
1364 isolate->debug()->ClearAllBreakPoints();
1365
1366 // Force optimization now that code flushing is disabled.
1367 { v8::HandleScope scope(CcTest::isolate());
1368 CompileRun("%OptimizeFunctionOnNextCall(foo); foo();");
1369 }
1370
1371 // Simulate one final GC to make sure the candidate queue is sane.
1372 heap->CollectAllGarbage(Heap::kNoGCFlags);
1373 CHECK(function->shared()->is_compiled() || !function->IsOptimized());
1374 CHECK(function->is_compiled() || !function->IsOptimized());
1375}
1376
1377
1378// Count the number of native contexts in the weak list of native contexts.
1379int CountNativeContexts() {
Ben Murdochf87a2032010-10-22 12:50:53 +01001380 int count = 0;
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001381 Object* object = CcTest::heap()->native_contexts_list();
Ben Murdochf87a2032010-10-22 12:50:53 +01001382 while (!object->IsUndefined()) {
1383 count++;
1384 object = Context::cast(object)->get(Context::NEXT_CONTEXT_LINK);
1385 }
1386 return count;
1387}
1388
1389
Ben Murdochb0fe1622011-05-05 13:52:32 +01001390// Count the number of user functions in the weak list of optimized
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001391// functions attached to a native context.
Ben Murdochb0fe1622011-05-05 13:52:32 +01001392static int CountOptimizedUserFunctions(v8::Handle<v8::Context> context) {
1393 int count = 0;
1394 Handle<Context> icontext = v8::Utils::OpenHandle(*context);
1395 Object* object = icontext->get(Context::OPTIMIZED_FUNCTIONS_LIST);
1396 while (object->IsJSFunction() && !JSFunction::cast(object)->IsBuiltin()) {
1397 count++;
1398 object = JSFunction::cast(object)->next_function_link();
1399 }
1400 return count;
1401}
1402
1403
Ben Murdochf87a2032010-10-22 12:50:53 +01001404TEST(TestInternalWeakLists) {
Steve Block44f0eee2011-05-26 01:26:41 +01001405 v8::V8::Initialize();
1406
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001407 // Some flags turn Scavenge collections into Mark-sweep collections
1408 // and hence are incompatible with this test case.
1409 if (FLAG_gc_global || FLAG_stress_compaction) return;
1410
Ben Murdochf87a2032010-10-22 12:50:53 +01001411 static const int kNumTestContexts = 10;
1412
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001413 Isolate* isolate = CcTest::i_isolate();
1414 Heap* heap = isolate->heap();
1415 HandleScope scope(isolate);
1416 v8::Handle<v8::Context> ctx[kNumTestContexts];
Ben Murdochf87a2032010-10-22 12:50:53 +01001417
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001418 CHECK_EQ(0, CountNativeContexts());
Ben Murdochf87a2032010-10-22 12:50:53 +01001419
1420 // Create a number of global contests which gets linked together.
1421 for (int i = 0; i < kNumTestContexts; i++) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001422 ctx[i] = v8::Context::New(CcTest::isolate());
Ben Murdochb0fe1622011-05-05 13:52:32 +01001423
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001424 // Collect garbage that might have been created by one of the
1425 // installed extensions.
1426 isolate->compilation_cache()->Clear();
1427 heap->CollectAllGarbage(Heap::kNoGCFlags);
Ben Murdochb0fe1622011-05-05 13:52:32 +01001428
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001429 bool opt = (FLAG_always_opt && isolate->use_crankshaft());
1430
1431 CHECK_EQ(i + 1, CountNativeContexts());
Ben Murdochf87a2032010-10-22 12:50:53 +01001432
1433 ctx[i]->Enter();
Ben Murdochb0fe1622011-05-05 13:52:32 +01001434
1435 // Create a handle scope so no function objects get stuch in the outer
1436 // handle scope
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001437 HandleScope scope(isolate);
Ben Murdochb0fe1622011-05-05 13:52:32 +01001438 const char* source = "function f1() { };"
1439 "function f2() { };"
1440 "function f3() { };"
1441 "function f4() { };"
1442 "function f5() { };";
1443 CompileRun(source);
1444 CHECK_EQ(0, CountOptimizedUserFunctions(ctx[i]));
1445 CompileRun("f1()");
1446 CHECK_EQ(opt ? 1 : 0, CountOptimizedUserFunctions(ctx[i]));
1447 CompileRun("f2()");
1448 CHECK_EQ(opt ? 2 : 0, CountOptimizedUserFunctions(ctx[i]));
1449 CompileRun("f3()");
1450 CHECK_EQ(opt ? 3 : 0, CountOptimizedUserFunctions(ctx[i]));
1451 CompileRun("f4()");
1452 CHECK_EQ(opt ? 4 : 0, CountOptimizedUserFunctions(ctx[i]));
1453 CompileRun("f5()");
1454 CHECK_EQ(opt ? 5 : 0, CountOptimizedUserFunctions(ctx[i]));
1455
1456 // Remove function f1, and
1457 CompileRun("f1=null");
1458
1459 // Scavenge treats these references as strong.
1460 for (int j = 0; j < 10; j++) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001461 CcTest::heap()->CollectGarbage(NEW_SPACE);
Ben Murdochb0fe1622011-05-05 13:52:32 +01001462 CHECK_EQ(opt ? 5 : 0, CountOptimizedUserFunctions(ctx[i]));
1463 }
1464
1465 // Mark compact handles the weak references.
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001466 isolate->compilation_cache()->Clear();
1467 heap->CollectAllGarbage(Heap::kNoGCFlags);
Ben Murdochb0fe1622011-05-05 13:52:32 +01001468 CHECK_EQ(opt ? 4 : 0, CountOptimizedUserFunctions(ctx[i]));
1469
1470 // Get rid of f3 and f5 in the same way.
1471 CompileRun("f3=null");
1472 for (int j = 0; j < 10; j++) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001473 CcTest::heap()->CollectGarbage(NEW_SPACE);
Ben Murdochb0fe1622011-05-05 13:52:32 +01001474 CHECK_EQ(opt ? 4 : 0, CountOptimizedUserFunctions(ctx[i]));
1475 }
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001476 CcTest::heap()->CollectAllGarbage(Heap::kNoGCFlags);
Ben Murdochb0fe1622011-05-05 13:52:32 +01001477 CHECK_EQ(opt ? 3 : 0, CountOptimizedUserFunctions(ctx[i]));
1478 CompileRun("f5=null");
1479 for (int j = 0; j < 10; j++) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001480 CcTest::heap()->CollectGarbage(NEW_SPACE);
Ben Murdochb0fe1622011-05-05 13:52:32 +01001481 CHECK_EQ(opt ? 3 : 0, CountOptimizedUserFunctions(ctx[i]));
1482 }
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001483 CcTest::heap()->CollectAllGarbage(Heap::kNoGCFlags);
Ben Murdochb0fe1622011-05-05 13:52:32 +01001484 CHECK_EQ(opt ? 2 : 0, CountOptimizedUserFunctions(ctx[i]));
1485
Ben Murdochf87a2032010-10-22 12:50:53 +01001486 ctx[i]->Exit();
1487 }
1488
1489 // Force compilation cache cleanup.
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001490 CcTest::heap()->NotifyContextDisposed();
1491 CcTest::heap()->CollectAllGarbage(Heap::kNoGCFlags);
Ben Murdochf87a2032010-10-22 12:50:53 +01001492
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001493 // Dispose the native contexts one by one.
Ben Murdochf87a2032010-10-22 12:50:53 +01001494 for (int i = 0; i < kNumTestContexts; i++) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001495 // TODO(dcarney): is there a better way to do this?
1496 i::Object** unsafe = reinterpret_cast<i::Object**>(*ctx[i]);
1497 *unsafe = CcTest::heap()->undefined_value();
Ben Murdochf87a2032010-10-22 12:50:53 +01001498 ctx[i].Clear();
1499
1500 // Scavenge treats these references as strong.
1501 for (int j = 0; j < 10; j++) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001502 CcTest::heap()->CollectGarbage(i::NEW_SPACE);
1503 CHECK_EQ(kNumTestContexts - i, CountNativeContexts());
Ben Murdochf87a2032010-10-22 12:50:53 +01001504 }
1505
1506 // Mark compact handles the weak references.
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001507 CcTest::heap()->CollectAllGarbage(Heap::kNoGCFlags);
1508 CHECK_EQ(kNumTestContexts - i - 1, CountNativeContexts());
Ben Murdochf87a2032010-10-22 12:50:53 +01001509 }
1510
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001511 CHECK_EQ(0, CountNativeContexts());
Ben Murdochf87a2032010-10-22 12:50:53 +01001512}
1513
1514
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001515// Count the number of native contexts in the weak list of native contexts
Ben Murdochf87a2032010-10-22 12:50:53 +01001516// causing a GC after the specified number of elements.
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001517static int CountNativeContextsWithGC(Isolate* isolate, int n) {
1518 Heap* heap = isolate->heap();
Ben Murdochf87a2032010-10-22 12:50:53 +01001519 int count = 0;
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001520 Handle<Object> object(heap->native_contexts_list(), isolate);
Ben Murdochf87a2032010-10-22 12:50:53 +01001521 while (!object->IsUndefined()) {
1522 count++;
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001523 if (count == n) heap->CollectAllGarbage(Heap::kNoGCFlags);
Ben Murdochf87a2032010-10-22 12:50:53 +01001524 object =
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001525 Handle<Object>(Context::cast(*object)->get(Context::NEXT_CONTEXT_LINK),
1526 isolate);
Ben Murdochf87a2032010-10-22 12:50:53 +01001527 }
1528 return count;
1529}
1530
1531
Ben Murdochb0fe1622011-05-05 13:52:32 +01001532// Count the number of user functions in the weak list of optimized
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001533// functions attached to a native context causing a GC after the
Ben Murdochb0fe1622011-05-05 13:52:32 +01001534// specified number of elements.
1535static int CountOptimizedUserFunctionsWithGC(v8::Handle<v8::Context> context,
1536 int n) {
1537 int count = 0;
1538 Handle<Context> icontext = v8::Utils::OpenHandle(*context);
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001539 Isolate* isolate = icontext->GetIsolate();
1540 Handle<Object> object(icontext->get(Context::OPTIMIZED_FUNCTIONS_LIST),
1541 isolate);
Ben Murdochb0fe1622011-05-05 13:52:32 +01001542 while (object->IsJSFunction() &&
1543 !Handle<JSFunction>::cast(object)->IsBuiltin()) {
1544 count++;
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001545 if (count == n) isolate->heap()->CollectAllGarbage(Heap::kNoGCFlags);
Ben Murdochb0fe1622011-05-05 13:52:32 +01001546 object = Handle<Object>(
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001547 Object::cast(JSFunction::cast(*object)->next_function_link()),
1548 isolate);
Ben Murdochb0fe1622011-05-05 13:52:32 +01001549 }
1550 return count;
1551}
1552
1553
Ben Murdochf87a2032010-10-22 12:50:53 +01001554TEST(TestInternalWeakListsTraverseWithGC) {
Steve Block44f0eee2011-05-26 01:26:41 +01001555 v8::V8::Initialize();
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001556 Isolate* isolate = CcTest::i_isolate();
Steve Block44f0eee2011-05-26 01:26:41 +01001557
Ben Murdochf87a2032010-10-22 12:50:53 +01001558 static const int kNumTestContexts = 10;
1559
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001560 HandleScope scope(isolate);
1561 v8::Handle<v8::Context> ctx[kNumTestContexts];
Ben Murdochf87a2032010-10-22 12:50:53 +01001562
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001563 CHECK_EQ(0, CountNativeContexts());
Ben Murdochf87a2032010-10-22 12:50:53 +01001564
1565 // Create an number of contexts and check the length of the weak list both
1566 // with and without GCs while iterating the list.
1567 for (int i = 0; i < kNumTestContexts; i++) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001568 ctx[i] = v8::Context::New(CcTest::isolate());
1569 CHECK_EQ(i + 1, CountNativeContexts());
1570 CHECK_EQ(i + 1, CountNativeContextsWithGC(isolate, i / 2 + 1));
Ben Murdochf87a2032010-10-22 12:50:53 +01001571 }
Ben Murdochb0fe1622011-05-05 13:52:32 +01001572
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001573 bool opt = (FLAG_always_opt && isolate->use_crankshaft());
Ben Murdochb0fe1622011-05-05 13:52:32 +01001574
1575 // Compile a number of functions the length of the weak list of optimized
1576 // functions both with and without GCs while iterating the list.
1577 ctx[0]->Enter();
1578 const char* source = "function f1() { };"
1579 "function f2() { };"
1580 "function f3() { };"
1581 "function f4() { };"
1582 "function f5() { };";
1583 CompileRun(source);
1584 CHECK_EQ(0, CountOptimizedUserFunctions(ctx[0]));
1585 CompileRun("f1()");
1586 CHECK_EQ(opt ? 1 : 0, CountOptimizedUserFunctions(ctx[0]));
1587 CHECK_EQ(opt ? 1 : 0, CountOptimizedUserFunctionsWithGC(ctx[0], 1));
1588 CompileRun("f2()");
1589 CHECK_EQ(opt ? 2 : 0, CountOptimizedUserFunctions(ctx[0]));
1590 CHECK_EQ(opt ? 2 : 0, CountOptimizedUserFunctionsWithGC(ctx[0], 1));
1591 CompileRun("f3()");
1592 CHECK_EQ(opt ? 3 : 0, CountOptimizedUserFunctions(ctx[0]));
1593 CHECK_EQ(opt ? 3 : 0, CountOptimizedUserFunctionsWithGC(ctx[0], 1));
1594 CompileRun("f4()");
1595 CHECK_EQ(opt ? 4 : 0, CountOptimizedUserFunctions(ctx[0]));
1596 CHECK_EQ(opt ? 4 : 0, CountOptimizedUserFunctionsWithGC(ctx[0], 2));
1597 CompileRun("f5()");
1598 CHECK_EQ(opt ? 5 : 0, CountOptimizedUserFunctions(ctx[0]));
1599 CHECK_EQ(opt ? 5 : 0, CountOptimizedUserFunctionsWithGC(ctx[0], 4));
1600
1601 ctx[0]->Exit();
Ben Murdochf87a2032010-10-22 12:50:53 +01001602}
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -08001603
1604
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001605TEST(TestSizeOfObjects) {
1606 v8::V8::Initialize();
1607
1608 // Get initial heap size after several full GCs, which will stabilize
1609 // the heap size and return with sweeping finished completely.
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001610 CcTest::heap()->CollectAllGarbage(Heap::kNoGCFlags);
1611 CcTest::heap()->CollectAllGarbage(Heap::kNoGCFlags);
1612 CcTest::heap()->CollectAllGarbage(Heap::kNoGCFlags);
1613 CcTest::heap()->CollectAllGarbage(Heap::kNoGCFlags);
1614 CcTest::heap()->CollectAllGarbage(Heap::kNoGCFlags);
1615 MarkCompactCollector* collector = CcTest::heap()->mark_compact_collector();
1616 if (collector->sweeping_in_progress()) {
1617 collector->EnsureSweepingCompleted();
1618 }
1619 int initial_size = static_cast<int>(CcTest::heap()->SizeOfObjects());
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001620
1621 {
1622 // Allocate objects on several different old-space pages so that
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001623 // concurrent sweeper threads will be busy sweeping the old space on
1624 // subsequent GC runs.
1625 AlwaysAllocateScope always_allocate(CcTest::i_isolate());
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001626 int filler_size = static_cast<int>(FixedArray::SizeFor(8192));
1627 for (int i = 1; i <= 100; i++) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001628 CcTest::test_heap()->AllocateFixedArray(8192, TENURED).ToObjectChecked();
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001629 CHECK_EQ(initial_size + i * filler_size,
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001630 static_cast<int>(CcTest::heap()->SizeOfObjects()));
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001631 }
1632 }
1633
1634 // The heap size should go back to initial size after a full GC, even
1635 // though sweeping didn't finish yet.
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001636 CcTest::heap()->CollectAllGarbage(Heap::kNoGCFlags);
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001637
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001638 // Normally sweeping would not be complete here, but no guarantees.
1639
1640 CHECK_EQ(initial_size, static_cast<int>(CcTest::heap()->SizeOfObjects()));
1641
1642 // Waiting for sweeper threads should not change heap size.
1643 if (collector->sweeping_in_progress()) {
1644 collector->EnsureSweepingCompleted();
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001645 }
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001646 CHECK_EQ(initial_size, static_cast<int>(CcTest::heap()->SizeOfObjects()));
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001647}
1648
1649
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -08001650TEST(TestSizeOfObjectsVsHeapIteratorPrecision) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001651 CcTest::InitializeVM();
1652 HeapIterator iterator(CcTest::heap());
1653 intptr_t size_of_objects_1 = CcTest::heap()->SizeOfObjects();
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -08001654 intptr_t size_of_objects_2 = 0;
1655 for (HeapObject* obj = iterator.next();
1656 obj != NULL;
1657 obj = iterator.next()) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001658 if (!obj->IsFreeSpace()) {
1659 size_of_objects_2 += obj->Size();
1660 }
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -08001661 }
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001662 // Delta must be within 5% of the larger result.
1663 // TODO(gc): Tighten this up by distinguishing between byte
1664 // arrays that are real and those that merely mark free space
1665 // on the heap.
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -08001666 if (size_of_objects_1 > size_of_objects_2) {
1667 intptr_t delta = size_of_objects_1 - size_of_objects_2;
1668 PrintF("Heap::SizeOfObjects: %" V8_PTR_PREFIX "d, "
1669 "Iterator: %" V8_PTR_PREFIX "d, "
1670 "delta: %" V8_PTR_PREFIX "d\n",
1671 size_of_objects_1, size_of_objects_2, delta);
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001672 CHECK_GT(size_of_objects_1 / 20, delta);
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -08001673 } else {
1674 intptr_t delta = size_of_objects_2 - size_of_objects_1;
1675 PrintF("Heap::SizeOfObjects: %" V8_PTR_PREFIX "d, "
1676 "Iterator: %" V8_PTR_PREFIX "d, "
1677 "delta: %" V8_PTR_PREFIX "d\n",
1678 size_of_objects_1, size_of_objects_2, delta);
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001679 CHECK_GT(size_of_objects_2 / 20, delta);
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -08001680 }
1681}
Ben Murdochb0fe1622011-05-05 13:52:32 +01001682
1683
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001684static void FillUpNewSpace(NewSpace* new_space) {
1685 // Fill up new space to the point that it is completely full. Make sure
1686 // that the scavenger does not undo the filling.
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001687 Heap* heap = new_space->heap();
1688 Isolate* isolate = heap->isolate();
1689 Factory* factory = isolate->factory();
1690 HandleScope scope(isolate);
1691 AlwaysAllocateScope always_allocate(isolate);
1692 intptr_t available = new_space->Capacity() - new_space->Size();
1693 intptr_t number_of_fillers = (available / FixedArray::SizeFor(32)) - 1;
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001694 for (intptr_t i = 0; i < number_of_fillers; i++) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001695 CHECK(heap->InNewSpace(*factory->NewFixedArray(32, NOT_TENURED)));
Ben Murdochb0fe1622011-05-05 13:52:32 +01001696 }
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001697}
Ben Murdochb0fe1622011-05-05 13:52:32 +01001698
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001699
1700TEST(GrowAndShrinkNewSpace) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001701 CcTest::InitializeVM();
1702 Heap* heap = CcTest::heap();
1703 NewSpace* new_space = heap->new_space();
1704
1705 if (heap->ReservedSemiSpaceSize() == heap->InitialSemiSpaceSize() ||
1706 heap->MaxSemiSpaceSize() == heap->InitialSemiSpaceSize()) {
1707 // The max size cannot exceed the reserved size, since semispaces must be
1708 // always within the reserved space. We can't test new space growing and
1709 // shrinking if the reserved size is the same as the minimum (initial) size.
1710 return;
1711 }
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001712
1713 // Explicitly growing should double the space capacity.
1714 intptr_t old_capacity, new_capacity;
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001715 old_capacity = new_space->TotalCapacity();
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001716 new_space->Grow();
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001717 new_capacity = new_space->TotalCapacity();
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001718 CHECK(2 * old_capacity == new_capacity);
1719
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001720 old_capacity = new_space->TotalCapacity();
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001721 FillUpNewSpace(new_space);
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001722 new_capacity = new_space->TotalCapacity();
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001723 CHECK(old_capacity == new_capacity);
1724
1725 // Explicitly shrinking should not affect space capacity.
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001726 old_capacity = new_space->TotalCapacity();
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001727 new_space->Shrink();
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001728 new_capacity = new_space->TotalCapacity();
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001729 CHECK(old_capacity == new_capacity);
1730
1731 // Let the scavenger empty the new space.
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001732 heap->CollectGarbage(NEW_SPACE);
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001733 CHECK_LE(new_space->Size(), old_capacity);
1734
1735 // Explicitly shrinking should halve the space capacity.
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001736 old_capacity = new_space->TotalCapacity();
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001737 new_space->Shrink();
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001738 new_capacity = new_space->TotalCapacity();
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001739 CHECK(old_capacity == 2 * new_capacity);
1740
1741 // Consecutive shrinking should not affect space capacity.
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001742 old_capacity = new_space->TotalCapacity();
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001743 new_space->Shrink();
1744 new_space->Shrink();
1745 new_space->Shrink();
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001746 new_capacity = new_space->TotalCapacity();
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001747 CHECK(old_capacity == new_capacity);
1748}
1749
1750
1751TEST(CollectingAllAvailableGarbageShrinksNewSpace) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001752 CcTest::InitializeVM();
1753 Heap* heap = CcTest::heap();
1754 if (heap->ReservedSemiSpaceSize() == heap->InitialSemiSpaceSize() ||
1755 heap->MaxSemiSpaceSize() == heap->InitialSemiSpaceSize()) {
1756 // The max size cannot exceed the reserved size, since semispaces must be
1757 // always within the reserved space. We can't test new space growing and
1758 // shrinking if the reserved size is the same as the minimum (initial) size.
1759 return;
1760 }
1761
1762 v8::HandleScope scope(CcTest::isolate());
1763 NewSpace* new_space = heap->new_space();
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001764 intptr_t old_capacity, new_capacity;
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001765 old_capacity = new_space->TotalCapacity();
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001766 new_space->Grow();
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001767 new_capacity = new_space->TotalCapacity();
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001768 CHECK(2 * old_capacity == new_capacity);
1769 FillUpNewSpace(new_space);
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001770 heap->CollectAllAvailableGarbage();
1771 new_capacity = new_space->TotalCapacity();
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001772 CHECK(old_capacity == new_capacity);
1773}
1774
1775
1776static int NumberOfGlobalObjects() {
1777 int count = 0;
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001778 HeapIterator iterator(CcTest::heap());
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001779 for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) {
1780 if (obj->IsGlobalObject()) count++;
Ben Murdochb0fe1622011-05-05 13:52:32 +01001781 }
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001782 return count;
1783}
1784
1785
1786// Test that we don't embed maps from foreign contexts into
1787// optimized code.
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001788TEST(LeakNativeContextViaMap) {
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001789 i::FLAG_allow_natives_syntax = true;
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001790 v8::Isolate* isolate = CcTest::isolate();
1791 v8::HandleScope outer_scope(isolate);
1792 v8::Persistent<v8::Context> ctx1p;
1793 v8::Persistent<v8::Context> ctx2p;
1794 {
1795 v8::HandleScope scope(isolate);
1796 ctx1p.Reset(isolate, v8::Context::New(isolate));
1797 ctx2p.Reset(isolate, v8::Context::New(isolate));
1798 v8::Local<v8::Context>::New(isolate, ctx1p)->Enter();
1799 }
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001800
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001801 CcTest::heap()->CollectAllAvailableGarbage();
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001802 CHECK_EQ(4, NumberOfGlobalObjects());
1803
Ben Murdochb0fe1622011-05-05 13:52:32 +01001804 {
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001805 v8::HandleScope inner_scope(isolate);
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001806 CompileRun("var v = {x: 42}");
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001807 v8::Local<v8::Context> ctx1 = v8::Local<v8::Context>::New(isolate, ctx1p);
1808 v8::Local<v8::Context> ctx2 = v8::Local<v8::Context>::New(isolate, ctx2p);
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001809 v8::Local<v8::Value> v = ctx1->Global()->Get(v8_str("v"));
1810 ctx2->Enter();
1811 ctx2->Global()->Set(v8_str("o"), v);
1812 v8::Local<v8::Value> res = CompileRun(
1813 "function f() { return o.x; }"
1814 "for (var i = 0; i < 10; ++i) f();"
1815 "%OptimizeFunctionOnNextCall(f);"
1816 "f();");
1817 CHECK_EQ(42, res->Int32Value());
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001818 ctx2->Global()->Set(v8_str("o"), v8::Int32::New(isolate, 0));
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001819 ctx2->Exit();
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001820 v8::Local<v8::Context>::New(isolate, ctx1)->Exit();
1821 ctx1p.Reset();
1822 isolate->ContextDisposedNotification();
Ben Murdochb0fe1622011-05-05 13:52:32 +01001823 }
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001824 CcTest::heap()->CollectAllAvailableGarbage();
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001825 CHECK_EQ(2, NumberOfGlobalObjects());
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001826 ctx2p.Reset();
1827 CcTest::heap()->CollectAllAvailableGarbage();
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001828 CHECK_EQ(0, NumberOfGlobalObjects());
1829}
1830
1831
1832// Test that we don't embed functions from foreign contexts into
1833// optimized code.
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001834TEST(LeakNativeContextViaFunction) {
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001835 i::FLAG_allow_natives_syntax = true;
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001836 v8::Isolate* isolate = CcTest::isolate();
1837 v8::HandleScope outer_scope(isolate);
1838 v8::Persistent<v8::Context> ctx1p;
1839 v8::Persistent<v8::Context> ctx2p;
1840 {
1841 v8::HandleScope scope(isolate);
1842 ctx1p.Reset(isolate, v8::Context::New(isolate));
1843 ctx2p.Reset(isolate, v8::Context::New(isolate));
1844 v8::Local<v8::Context>::New(isolate, ctx1p)->Enter();
1845 }
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001846
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001847 CcTest::heap()->CollectAllAvailableGarbage();
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001848 CHECK_EQ(4, NumberOfGlobalObjects());
1849
Ben Murdoch592a9fc2012-03-05 11:04:45 +00001850 {
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001851 v8::HandleScope inner_scope(isolate);
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001852 CompileRun("var v = function() { return 42; }");
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001853 v8::Local<v8::Context> ctx1 = v8::Local<v8::Context>::New(isolate, ctx1p);
1854 v8::Local<v8::Context> ctx2 = v8::Local<v8::Context>::New(isolate, ctx2p);
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001855 v8::Local<v8::Value> v = ctx1->Global()->Get(v8_str("v"));
1856 ctx2->Enter();
1857 ctx2->Global()->Set(v8_str("o"), v);
1858 v8::Local<v8::Value> res = CompileRun(
1859 "function f(x) { return x(); }"
1860 "for (var i = 0; i < 10; ++i) f(o);"
1861 "%OptimizeFunctionOnNextCall(f);"
1862 "f(o);");
1863 CHECK_EQ(42, res->Int32Value());
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001864 ctx2->Global()->Set(v8_str("o"), v8::Int32::New(isolate, 0));
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001865 ctx2->Exit();
1866 ctx1->Exit();
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001867 ctx1p.Reset();
1868 isolate->ContextDisposedNotification();
Ben Murdoch592a9fc2012-03-05 11:04:45 +00001869 }
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001870 CcTest::heap()->CollectAllAvailableGarbage();
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001871 CHECK_EQ(2, NumberOfGlobalObjects());
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001872 ctx2p.Reset();
1873 CcTest::heap()->CollectAllAvailableGarbage();
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001874 CHECK_EQ(0, NumberOfGlobalObjects());
1875}
1876
1877
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001878TEST(LeakNativeContextViaMapKeyed) {
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001879 i::FLAG_allow_natives_syntax = true;
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001880 v8::Isolate* isolate = CcTest::isolate();
1881 v8::HandleScope outer_scope(isolate);
1882 v8::Persistent<v8::Context> ctx1p;
1883 v8::Persistent<v8::Context> ctx2p;
1884 {
1885 v8::HandleScope scope(isolate);
1886 ctx1p.Reset(isolate, v8::Context::New(isolate));
1887 ctx2p.Reset(isolate, v8::Context::New(isolate));
1888 v8::Local<v8::Context>::New(isolate, ctx1p)->Enter();
1889 }
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001890
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001891 CcTest::heap()->CollectAllAvailableGarbage();
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001892 CHECK_EQ(4, NumberOfGlobalObjects());
1893
1894 {
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001895 v8::HandleScope inner_scope(isolate);
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001896 CompileRun("var v = [42, 43]");
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001897 v8::Local<v8::Context> ctx1 = v8::Local<v8::Context>::New(isolate, ctx1p);
1898 v8::Local<v8::Context> ctx2 = v8::Local<v8::Context>::New(isolate, ctx2p);
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001899 v8::Local<v8::Value> v = ctx1->Global()->Get(v8_str("v"));
1900 ctx2->Enter();
1901 ctx2->Global()->Set(v8_str("o"), v);
1902 v8::Local<v8::Value> res = CompileRun(
1903 "function f() { return o[0]; }"
1904 "for (var i = 0; i < 10; ++i) f();"
1905 "%OptimizeFunctionOnNextCall(f);"
1906 "f();");
1907 CHECK_EQ(42, res->Int32Value());
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001908 ctx2->Global()->Set(v8_str("o"), v8::Int32::New(isolate, 0));
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001909 ctx2->Exit();
1910 ctx1->Exit();
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001911 ctx1p.Reset();
1912 isolate->ContextDisposedNotification();
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001913 }
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001914 CcTest::heap()->CollectAllAvailableGarbage();
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001915 CHECK_EQ(2, NumberOfGlobalObjects());
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001916 ctx2p.Reset();
1917 CcTest::heap()->CollectAllAvailableGarbage();
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001918 CHECK_EQ(0, NumberOfGlobalObjects());
1919}
1920
1921
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001922TEST(LeakNativeContextViaMapProto) {
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001923 i::FLAG_allow_natives_syntax = true;
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001924 v8::Isolate* isolate = CcTest::isolate();
1925 v8::HandleScope outer_scope(isolate);
1926 v8::Persistent<v8::Context> ctx1p;
1927 v8::Persistent<v8::Context> ctx2p;
1928 {
1929 v8::HandleScope scope(isolate);
1930 ctx1p.Reset(isolate, v8::Context::New(isolate));
1931 ctx2p.Reset(isolate, v8::Context::New(isolate));
1932 v8::Local<v8::Context>::New(isolate, ctx1p)->Enter();
1933 }
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001934
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001935 CcTest::heap()->CollectAllAvailableGarbage();
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001936 CHECK_EQ(4, NumberOfGlobalObjects());
1937
1938 {
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001939 v8::HandleScope inner_scope(isolate);
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001940 CompileRun("var v = { y: 42}");
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001941 v8::Local<v8::Context> ctx1 = v8::Local<v8::Context>::New(isolate, ctx1p);
1942 v8::Local<v8::Context> ctx2 = v8::Local<v8::Context>::New(isolate, ctx2p);
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001943 v8::Local<v8::Value> v = ctx1->Global()->Get(v8_str("v"));
1944 ctx2->Enter();
1945 ctx2->Global()->Set(v8_str("o"), v);
1946 v8::Local<v8::Value> res = CompileRun(
1947 "function f() {"
1948 " var p = {x: 42};"
1949 " p.__proto__ = o;"
1950 " return p.x;"
1951 "}"
1952 "for (var i = 0; i < 10; ++i) f();"
1953 "%OptimizeFunctionOnNextCall(f);"
1954 "f();");
1955 CHECK_EQ(42, res->Int32Value());
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001956 ctx2->Global()->Set(v8_str("o"), v8::Int32::New(isolate, 0));
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001957 ctx2->Exit();
1958 ctx1->Exit();
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001959 ctx1p.Reset();
1960 isolate->ContextDisposedNotification();
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001961 }
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001962 CcTest::heap()->CollectAllAvailableGarbage();
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001963 CHECK_EQ(2, NumberOfGlobalObjects());
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001964 ctx2p.Reset();
1965 CcTest::heap()->CollectAllAvailableGarbage();
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001966 CHECK_EQ(0, NumberOfGlobalObjects());
1967}
1968
1969
1970TEST(InstanceOfStubWriteBarrier) {
1971 i::FLAG_allow_natives_syntax = true;
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001972#ifdef VERIFY_HEAP
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001973 i::FLAG_verify_heap = true;
1974#endif
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001975
1976 CcTest::InitializeVM();
1977 if (!CcTest::i_isolate()->use_crankshaft()) return;
1978 if (i::FLAG_force_marking_deque_overflows) return;
1979 v8::HandleScope outer_scope(CcTest::isolate());
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001980
1981 {
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001982 v8::HandleScope scope(CcTest::isolate());
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001983 CompileRun(
1984 "function foo () { }"
1985 "function mkbar () { return new (new Function(\"\")) (); }"
1986 "function f (x) { return (x instanceof foo); }"
1987 "function g () { f(mkbar()); }"
1988 "f(new foo()); f(new foo());"
1989 "%OptimizeFunctionOnNextCall(f);"
1990 "f(new foo()); g();");
1991 }
1992
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001993 IncrementalMarking* marking = CcTest::heap()->incremental_marking();
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001994 marking->Abort();
1995 marking->Start();
1996
1997 Handle<JSFunction> f =
1998 v8::Utils::OpenHandle(
1999 *v8::Handle<v8::Function>::Cast(
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002000 CcTest::global()->Get(v8_str("f"))));
Ben Murdoch3ef787d2012-04-12 10:51:47 +01002001
2002 CHECK(f->IsOptimized());
2003
2004 while (!Marking::IsBlack(Marking::MarkBitFrom(f->code())) &&
2005 !marking->IsStopped()) {
Ben Murdoch8f9999f2012-04-23 10:39:17 +01002006 // Discard any pending GC requests otherwise we will get GC when we enter
2007 // code below.
2008 marking->Step(MB, IncrementalMarking::NO_GC_VIA_STACK_GUARD);
Ben Murdoch3ef787d2012-04-12 10:51:47 +01002009 }
2010
2011 CHECK(marking->IsMarking());
2012
Ben Murdoch3ef787d2012-04-12 10:51:47 +01002013 {
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002014 v8::HandleScope scope(CcTest::isolate());
2015 v8::Handle<v8::Object> global = CcTest::global();
Ben Murdoch3ef787d2012-04-12 10:51:47 +01002016 v8::Handle<v8::Function> g =
2017 v8::Handle<v8::Function>::Cast(global->Get(v8_str("g")));
2018 g->Call(global, 0, NULL);
2019 }
2020
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002021 CcTest::heap()->incremental_marking()->set_should_hurry(true);
2022 CcTest::heap()->CollectGarbage(OLD_POINTER_SPACE);
Ben Murdoch3ef787d2012-04-12 10:51:47 +01002023}
2024
2025
2026TEST(PrototypeTransitionClearing) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002027 if (FLAG_never_compact) return;
2028 CcTest::InitializeVM();
2029 Isolate* isolate = CcTest::i_isolate();
2030 Factory* factory = isolate->factory();
2031 v8::HandleScope scope(CcTest::isolate());
2032
2033 CompileRun("var base = {};");
2034 Handle<JSObject> baseObject =
2035 v8::Utils::OpenHandle(
2036 *v8::Handle<v8::Object>::Cast(
2037 CcTest::global()->Get(v8_str("base"))));
2038 int initialTransitions = baseObject->map()->NumberOfProtoTransitions();
Ben Murdoch3ef787d2012-04-12 10:51:47 +01002039
2040 CompileRun(
Ben Murdoch3ef787d2012-04-12 10:51:47 +01002041 "var live = [];"
2042 "for (var i = 0; i < 10; i++) {"
2043 " var object = {};"
2044 " var prototype = {};"
2045 " object.__proto__ = prototype;"
2046 " if (i >= 3) live.push(object, prototype);"
2047 "}");
2048
Ben Murdoch3ef787d2012-04-12 10:51:47 +01002049 // Verify that only dead prototype transitions are cleared.
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002050 CHECK_EQ(initialTransitions + 10,
2051 baseObject->map()->NumberOfProtoTransitions());
2052 CcTest::heap()->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask);
2053 const int transitions = 10 - 3;
2054 CHECK_EQ(initialTransitions + transitions,
2055 baseObject->map()->NumberOfProtoTransitions());
Ben Murdoch3ef787d2012-04-12 10:51:47 +01002056
2057 // Verify that prototype transitions array was compacted.
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002058 FixedArray* trans = baseObject->map()->GetPrototypeTransitions();
2059 for (int i = initialTransitions; i < initialTransitions + transitions; i++) {
Ben Murdoch3ef787d2012-04-12 10:51:47 +01002060 int j = Map::kProtoTransitionHeaderSize +
2061 i * Map::kProtoTransitionElementsPerEntry;
2062 CHECK(trans->get(j + Map::kProtoTransitionMapOffset)->IsMap());
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002063 Object* proto = trans->get(j + Map::kProtoTransitionPrototypeOffset);
2064 CHECK(proto->IsJSObject());
Ben Murdoch3ef787d2012-04-12 10:51:47 +01002065 }
2066
2067 // Make sure next prototype is placed on an old-space evacuation candidate.
2068 Handle<JSObject> prototype;
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002069 PagedSpace* space = CcTest::heap()->old_pointer_space();
2070 {
2071 AlwaysAllocateScope always_allocate(isolate);
2072 SimulateFullSpace(space);
2073 prototype = factory->NewJSArray(32 * KB, FAST_HOLEY_ELEMENTS, TENURED);
2074 }
Ben Murdoch3ef787d2012-04-12 10:51:47 +01002075
2076 // Add a prototype on an evacuation candidate and verify that transition
2077 // clearing correctly records slots in prototype transition array.
2078 i::FLAG_always_compact = true;
2079 Handle<Map> map(baseObject->map());
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002080 CHECK(!space->LastPage()->Contains(
2081 map->GetPrototypeTransitions()->address()));
Ben Murdoch3ef787d2012-04-12 10:51:47 +01002082 CHECK(space->LastPage()->Contains(prototype->address()));
Ben Murdochc7cc0282012-03-05 14:35:55 +00002083}
Ben Murdoch8f9999f2012-04-23 10:39:17 +01002084
2085
2086TEST(ResetSharedFunctionInfoCountersDuringIncrementalMarking) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002087 i::FLAG_stress_compaction = false;
Ben Murdoch8f9999f2012-04-23 10:39:17 +01002088 i::FLAG_allow_natives_syntax = true;
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002089#ifdef VERIFY_HEAP
Ben Murdoch8f9999f2012-04-23 10:39:17 +01002090 i::FLAG_verify_heap = true;
2091#endif
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002092
2093 CcTest::InitializeVM();
2094 if (!CcTest::i_isolate()->use_crankshaft()) return;
2095 v8::HandleScope outer_scope(CcTest::isolate());
Ben Murdoch8f9999f2012-04-23 10:39:17 +01002096
2097 {
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002098 v8::HandleScope scope(CcTest::isolate());
Ben Murdoch8f9999f2012-04-23 10:39:17 +01002099 CompileRun(
2100 "function f () {"
2101 " var s = 0;"
2102 " for (var i = 0; i < 100; i++) s += i;"
2103 " return s;"
2104 "}"
2105 "f(); f();"
2106 "%OptimizeFunctionOnNextCall(f);"
2107 "f();");
2108 }
2109 Handle<JSFunction> f =
2110 v8::Utils::OpenHandle(
2111 *v8::Handle<v8::Function>::Cast(
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002112 CcTest::global()->Get(v8_str("f"))));
Ben Murdoch8f9999f2012-04-23 10:39:17 +01002113 CHECK(f->IsOptimized());
2114
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002115 IncrementalMarking* marking = CcTest::heap()->incremental_marking();
Ben Murdoch8f9999f2012-04-23 10:39:17 +01002116 marking->Abort();
2117 marking->Start();
2118
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002119 // The following two calls will increment CcTest::heap()->global_ic_age().
Ben Murdoch8f9999f2012-04-23 10:39:17 +01002120 const int kLongIdlePauseInMs = 1000;
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002121 CcTest::isolate()->ContextDisposedNotification();
2122 CcTest::isolate()->IdleNotification(kLongIdlePauseInMs);
Ben Murdoch8f9999f2012-04-23 10:39:17 +01002123
2124 while (!marking->IsStopped() && !marking->IsComplete()) {
2125 marking->Step(1 * MB, IncrementalMarking::NO_GC_VIA_STACK_GUARD);
2126 }
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002127 if (!marking->IsStopped() || marking->should_hurry()) {
2128 // We don't normally finish a GC via Step(), we normally finish by
2129 // setting the stack guard and then do the final steps in the stack
2130 // guard interrupt. But here we didn't ask for that, and there is no
2131 // JS code running to trigger the interrupt, so we explicitly finalize
2132 // here.
2133 CcTest::heap()->CollectAllGarbage(Heap::kNoGCFlags,
2134 "Test finalizing incremental mark-sweep");
2135 }
Ben Murdoch8f9999f2012-04-23 10:39:17 +01002136
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002137 CHECK_EQ(CcTest::heap()->global_ic_age(), f->shared()->ic_age());
Ben Murdoch8f9999f2012-04-23 10:39:17 +01002138 CHECK_EQ(0, f->shared()->opt_count());
2139 CHECK_EQ(0, f->shared()->code()->profiler_ticks());
2140}
2141
2142
2143TEST(ResetSharedFunctionInfoCountersDuringMarkSweep) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002144 i::FLAG_stress_compaction = false;
Ben Murdoch8f9999f2012-04-23 10:39:17 +01002145 i::FLAG_allow_natives_syntax = true;
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002146#ifdef VERIFY_HEAP
Ben Murdoch8f9999f2012-04-23 10:39:17 +01002147 i::FLAG_verify_heap = true;
2148#endif
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002149
2150 CcTest::InitializeVM();
2151 if (!CcTest::i_isolate()->use_crankshaft()) return;
2152 v8::HandleScope outer_scope(CcTest::isolate());
Ben Murdoch8f9999f2012-04-23 10:39:17 +01002153
2154 {
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002155 v8::HandleScope scope(CcTest::isolate());
Ben Murdoch8f9999f2012-04-23 10:39:17 +01002156 CompileRun(
2157 "function f () {"
2158 " var s = 0;"
2159 " for (var i = 0; i < 100; i++) s += i;"
2160 " return s;"
2161 "}"
2162 "f(); f();"
2163 "%OptimizeFunctionOnNextCall(f);"
2164 "f();");
2165 }
2166 Handle<JSFunction> f =
2167 v8::Utils::OpenHandle(
2168 *v8::Handle<v8::Function>::Cast(
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002169 CcTest::global()->Get(v8_str("f"))));
Ben Murdoch8f9999f2012-04-23 10:39:17 +01002170 CHECK(f->IsOptimized());
2171
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002172 CcTest::heap()->incremental_marking()->Abort();
Ben Murdoch8f9999f2012-04-23 10:39:17 +01002173
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002174 // The following two calls will increment CcTest::heap()->global_ic_age().
Ben Murdoch8f9999f2012-04-23 10:39:17 +01002175 // Since incremental marking is off, IdleNotification will do full GC.
2176 const int kLongIdlePauseInMs = 1000;
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002177 CcTest::isolate()->ContextDisposedNotification();
2178 CcTest::isolate()->IdleNotification(kLongIdlePauseInMs);
Ben Murdoch8f9999f2012-04-23 10:39:17 +01002179
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002180 CHECK_EQ(CcTest::heap()->global_ic_age(), f->shared()->ic_age());
Ben Murdoch8f9999f2012-04-23 10:39:17 +01002181 CHECK_EQ(0, f->shared()->opt_count());
2182 CHECK_EQ(0, f->shared()->code()->profiler_ticks());
2183}
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002184
2185
2186// Test that HAllocateObject will always return an object in new-space.
2187TEST(OptimizedAllocationAlwaysInNewSpace) {
2188 i::FLAG_allow_natives_syntax = true;
2189 CcTest::InitializeVM();
2190 if (!CcTest::i_isolate()->use_crankshaft() || i::FLAG_always_opt) return;
2191 if (i::FLAG_gc_global || i::FLAG_stress_compaction) return;
2192 v8::HandleScope scope(CcTest::isolate());
2193
2194 SimulateFullSpace(CcTest::heap()->new_space());
2195 AlwaysAllocateScope always_allocate(CcTest::i_isolate());
2196 v8::Local<v8::Value> res = CompileRun(
2197 "function c(x) {"
2198 " this.x = x;"
2199 " for (var i = 0; i < 32; i++) {"
2200 " this['x' + i] = x;"
2201 " }"
2202 "}"
2203 "function f(x) { return new c(x); };"
2204 "f(1); f(2); f(3);"
2205 "%OptimizeFunctionOnNextCall(f);"
2206 "f(4);");
2207 CHECK_EQ(4, res->ToObject()->GetRealNamedProperty(v8_str("x"))->Int32Value());
2208
2209 Handle<JSObject> o =
2210 v8::Utils::OpenHandle(*v8::Handle<v8::Object>::Cast(res));
2211
2212 CHECK(CcTest::heap()->InNewSpace(*o));
2213}
2214
2215
2216TEST(OptimizedPretenuringAllocationFolding) {
2217 i::FLAG_allow_natives_syntax = true;
2218 i::FLAG_expose_gc = true;
2219 CcTest::InitializeVM();
2220 if (!CcTest::i_isolate()->use_crankshaft() || i::FLAG_always_opt) return;
2221 if (i::FLAG_gc_global || i::FLAG_stress_compaction) return;
2222 v8::HandleScope scope(CcTest::isolate());
2223
2224 // Grow new space unitl maximum capacity reached.
2225 while (!CcTest::heap()->new_space()->IsAtMaximumCapacity()) {
2226 CcTest::heap()->new_space()->Grow();
2227 }
2228
2229 i::ScopedVector<char> source(1024);
2230 i::SNPrintF(
2231 source,
2232 "var number_elements = %d;"
2233 "var elements = new Array();"
2234 "function f() {"
2235 " for (var i = 0; i < number_elements; i++) {"
2236 " elements[i] = [[{}], [1.1]];"
2237 " }"
2238 " return elements[number_elements-1]"
2239 "};"
2240 "f(); gc();"
2241 "f(); f();"
2242 "%%OptimizeFunctionOnNextCall(f);"
2243 "f();",
2244 AllocationSite::kPretenureMinimumCreated);
2245
2246 v8::Local<v8::Value> res = CompileRun(source.start());
2247
2248 v8::Local<v8::Value> int_array = v8::Object::Cast(*res)->Get(v8_str("0"));
2249 Handle<JSObject> int_array_handle =
2250 v8::Utils::OpenHandle(*v8::Handle<v8::Object>::Cast(int_array));
2251 v8::Local<v8::Value> double_array = v8::Object::Cast(*res)->Get(v8_str("1"));
2252 Handle<JSObject> double_array_handle =
2253 v8::Utils::OpenHandle(*v8::Handle<v8::Object>::Cast(double_array));
2254
2255 Handle<JSObject> o =
2256 v8::Utils::OpenHandle(*v8::Handle<v8::Object>::Cast(res));
2257 CHECK(CcTest::heap()->InOldPointerSpace(*o));
2258 CHECK(CcTest::heap()->InOldPointerSpace(*int_array_handle));
2259 CHECK(CcTest::heap()->InOldPointerSpace(int_array_handle->elements()));
2260 CHECK(CcTest::heap()->InOldPointerSpace(*double_array_handle));
2261 CHECK(CcTest::heap()->InOldDataSpace(double_array_handle->elements()));
2262}
2263
2264
2265TEST(OptimizedPretenuringObjectArrayLiterals) {
2266 i::FLAG_allow_natives_syntax = true;
2267 i::FLAG_expose_gc = true;
2268 CcTest::InitializeVM();
2269 if (!CcTest::i_isolate()->use_crankshaft() || i::FLAG_always_opt) return;
2270 if (i::FLAG_gc_global || i::FLAG_stress_compaction) return;
2271 v8::HandleScope scope(CcTest::isolate());
2272
2273 // Grow new space unitl maximum capacity reached.
2274 while (!CcTest::heap()->new_space()->IsAtMaximumCapacity()) {
2275 CcTest::heap()->new_space()->Grow();
2276 }
2277
2278 i::ScopedVector<char> source(1024);
2279 i::SNPrintF(
2280 source,
2281 "var number_elements = %d;"
2282 "var elements = new Array(number_elements);"
2283 "function f() {"
2284 " for (var i = 0; i < number_elements; i++) {"
2285 " elements[i] = [{}, {}, {}];"
2286 " }"
2287 " return elements[number_elements - 1];"
2288 "};"
2289 "f(); gc();"
2290 "f(); f();"
2291 "%%OptimizeFunctionOnNextCall(f);"
2292 "f();",
2293 AllocationSite::kPretenureMinimumCreated);
2294
2295 v8::Local<v8::Value> res = CompileRun(source.start());
2296
2297 Handle<JSObject> o =
2298 v8::Utils::OpenHandle(*v8::Handle<v8::Object>::Cast(res));
2299
2300 CHECK(CcTest::heap()->InOldPointerSpace(o->elements()));
2301 CHECK(CcTest::heap()->InOldPointerSpace(*o));
2302}
2303
2304
2305TEST(OptimizedPretenuringMixedInObjectProperties) {
2306 i::FLAG_allow_natives_syntax = true;
2307 i::FLAG_expose_gc = true;
2308 CcTest::InitializeVM();
2309 if (!CcTest::i_isolate()->use_crankshaft() || i::FLAG_always_opt) return;
2310 if (i::FLAG_gc_global || i::FLAG_stress_compaction) return;
2311 v8::HandleScope scope(CcTest::isolate());
2312
2313 // Grow new space unitl maximum capacity reached.
2314 while (!CcTest::heap()->new_space()->IsAtMaximumCapacity()) {
2315 CcTest::heap()->new_space()->Grow();
2316 }
2317
2318
2319 i::ScopedVector<char> source(1024);
2320 i::SNPrintF(
2321 source,
2322 "var number_elements = %d;"
2323 "var elements = new Array(number_elements);"
2324 "function f() {"
2325 " for (var i = 0; i < number_elements; i++) {"
2326 " elements[i] = {a: {c: 2.2, d: {}}, b: 1.1};"
2327 " }"
2328 " return elements[number_elements - 1];"
2329 "};"
2330 "f(); gc();"
2331 "f(); f();"
2332 "%%OptimizeFunctionOnNextCall(f);"
2333 "f();",
2334 AllocationSite::kPretenureMinimumCreated);
2335
2336 v8::Local<v8::Value> res = CompileRun(source.start());
2337
2338 Handle<JSObject> o =
2339 v8::Utils::OpenHandle(*v8::Handle<v8::Object>::Cast(res));
2340
2341 CHECK(CcTest::heap()->InOldPointerSpace(*o));
2342 FieldIndex idx1 = FieldIndex::ForPropertyIndex(o->map(), 0);
2343 FieldIndex idx2 = FieldIndex::ForPropertyIndex(o->map(), 1);
2344 CHECK(CcTest::heap()->InOldPointerSpace(o->RawFastPropertyAt(idx1)));
2345 CHECK(CcTest::heap()->InOldDataSpace(o->RawFastPropertyAt(idx2)));
2346
2347 JSObject* inner_object =
2348 reinterpret_cast<JSObject*>(o->RawFastPropertyAt(idx1));
2349 CHECK(CcTest::heap()->InOldPointerSpace(inner_object));
2350 CHECK(CcTest::heap()->InOldDataSpace(inner_object->RawFastPropertyAt(idx1)));
2351 CHECK(CcTest::heap()->InOldPointerSpace(
2352 inner_object->RawFastPropertyAt(idx2)));
2353}
2354
2355
2356TEST(OptimizedPretenuringDoubleArrayProperties) {
2357 i::FLAG_allow_natives_syntax = true;
2358 i::FLAG_expose_gc = true;
2359 CcTest::InitializeVM();
2360 if (!CcTest::i_isolate()->use_crankshaft() || i::FLAG_always_opt) return;
2361 if (i::FLAG_gc_global || i::FLAG_stress_compaction) return;
2362 v8::HandleScope scope(CcTest::isolate());
2363
2364 // Grow new space unitl maximum capacity reached.
2365 while (!CcTest::heap()->new_space()->IsAtMaximumCapacity()) {
2366 CcTest::heap()->new_space()->Grow();
2367 }
2368
2369 i::ScopedVector<char> source(1024);
2370 i::SNPrintF(
2371 source,
2372 "var number_elements = %d;"
2373 "var elements = new Array(number_elements);"
2374 "function f() {"
2375 " for (var i = 0; i < number_elements; i++) {"
2376 " elements[i] = {a: 1.1, b: 2.2};"
2377 " }"
2378 " return elements[i - 1];"
2379 "};"
2380 "f(); gc();"
2381 "f(); f();"
2382 "%%OptimizeFunctionOnNextCall(f);"
2383 "f();",
2384 AllocationSite::kPretenureMinimumCreated);
2385
2386 v8::Local<v8::Value> res = CompileRun(source.start());
2387
2388 Handle<JSObject> o =
2389 v8::Utils::OpenHandle(*v8::Handle<v8::Object>::Cast(res));
2390
2391 CHECK(CcTest::heap()->InOldPointerSpace(*o));
2392 CHECK(CcTest::heap()->InOldDataSpace(o->properties()));
2393}
2394
2395
2396TEST(OptimizedPretenuringdoubleArrayLiterals) {
2397 i::FLAG_allow_natives_syntax = true;
2398 i::FLAG_expose_gc = true;
2399 CcTest::InitializeVM();
2400 if (!CcTest::i_isolate()->use_crankshaft() || i::FLAG_always_opt) return;
2401 if (i::FLAG_gc_global || i::FLAG_stress_compaction) return;
2402 v8::HandleScope scope(CcTest::isolate());
2403
2404 // Grow new space unitl maximum capacity reached.
2405 while (!CcTest::heap()->new_space()->IsAtMaximumCapacity()) {
2406 CcTest::heap()->new_space()->Grow();
2407 }
2408
2409 i::ScopedVector<char> source(1024);
2410 i::SNPrintF(
2411 source,
2412 "var number_elements = %d;"
2413 "var elements = new Array(number_elements);"
2414 "function f() {"
2415 " for (var i = 0; i < number_elements; i++) {"
2416 " elements[i] = [1.1, 2.2, 3.3];"
2417 " }"
2418 " return elements[number_elements - 1];"
2419 "};"
2420 "f(); gc();"
2421 "f(); f();"
2422 "%%OptimizeFunctionOnNextCall(f);"
2423 "f();",
2424 AllocationSite::kPretenureMinimumCreated);
2425
2426 v8::Local<v8::Value> res = CompileRun(source.start());
2427
2428 Handle<JSObject> o =
2429 v8::Utils::OpenHandle(*v8::Handle<v8::Object>::Cast(res));
2430
2431 CHECK(CcTest::heap()->InOldDataSpace(o->elements()));
2432 CHECK(CcTest::heap()->InOldPointerSpace(*o));
2433}
2434
2435
2436TEST(OptimizedPretenuringNestedMixedArrayLiterals) {
2437 i::FLAG_allow_natives_syntax = true;
2438 i::FLAG_expose_gc = true;
2439 CcTest::InitializeVM();
2440 if (!CcTest::i_isolate()->use_crankshaft() || i::FLAG_always_opt) return;
2441 if (i::FLAG_gc_global || i::FLAG_stress_compaction) return;
2442 v8::HandleScope scope(CcTest::isolate());
2443
2444 // Grow new space unitl maximum capacity reached.
2445 while (!CcTest::heap()->new_space()->IsAtMaximumCapacity()) {
2446 CcTest::heap()->new_space()->Grow();
2447 }
2448
2449 i::ScopedVector<char> source(1024);
2450 i::SNPrintF(
2451 source,
2452 "var number_elements = 100;"
2453 "var elements = new Array(number_elements);"
2454 "function f() {"
2455 " for (var i = 0; i < number_elements; i++) {"
2456 " elements[i] = [[{}, {}, {}], [1.1, 2.2, 3.3]];"
2457 " }"
2458 " return elements[number_elements - 1];"
2459 "};"
2460 "f(); gc();"
2461 "f(); f();"
2462 "%%OptimizeFunctionOnNextCall(f);"
2463 "f();");
2464
2465 v8::Local<v8::Value> res = CompileRun(source.start());
2466
2467 v8::Local<v8::Value> int_array = v8::Object::Cast(*res)->Get(v8_str("0"));
2468 Handle<JSObject> int_array_handle =
2469 v8::Utils::OpenHandle(*v8::Handle<v8::Object>::Cast(int_array));
2470 v8::Local<v8::Value> double_array = v8::Object::Cast(*res)->Get(v8_str("1"));
2471 Handle<JSObject> double_array_handle =
2472 v8::Utils::OpenHandle(*v8::Handle<v8::Object>::Cast(double_array));
2473
2474 Handle<JSObject> o =
2475 v8::Utils::OpenHandle(*v8::Handle<v8::Object>::Cast(res));
2476 CHECK(CcTest::heap()->InOldPointerSpace(*o));
2477 CHECK(CcTest::heap()->InOldPointerSpace(*int_array_handle));
2478 CHECK(CcTest::heap()->InOldPointerSpace(int_array_handle->elements()));
2479 CHECK(CcTest::heap()->InOldPointerSpace(*double_array_handle));
2480 CHECK(CcTest::heap()->InOldDataSpace(double_array_handle->elements()));
2481}
2482
2483
2484TEST(OptimizedPretenuringNestedObjectLiterals) {
2485 i::FLAG_allow_natives_syntax = true;
2486 i::FLAG_expose_gc = true;
2487 CcTest::InitializeVM();
2488 if (!CcTest::i_isolate()->use_crankshaft() || i::FLAG_always_opt) return;
2489 if (i::FLAG_gc_global || i::FLAG_stress_compaction) return;
2490 v8::HandleScope scope(CcTest::isolate());
2491
2492 // Grow new space unitl maximum capacity reached.
2493 while (!CcTest::heap()->new_space()->IsAtMaximumCapacity()) {
2494 CcTest::heap()->new_space()->Grow();
2495 }
2496
2497 i::ScopedVector<char> source(1024);
2498 i::SNPrintF(
2499 source,
2500 "var number_elements = %d;"
2501 "var elements = new Array(number_elements);"
2502 "function f() {"
2503 " for (var i = 0; i < number_elements; i++) {"
2504 " elements[i] = [[{}, {}, {}],[{}, {}, {}]];"
2505 " }"
2506 " return elements[number_elements - 1];"
2507 "};"
2508 "f(); gc();"
2509 "f(); f();"
2510 "%%OptimizeFunctionOnNextCall(f);"
2511 "f();",
2512 AllocationSite::kPretenureMinimumCreated);
2513
2514 v8::Local<v8::Value> res = CompileRun(source.start());
2515
2516 v8::Local<v8::Value> int_array_1 = v8::Object::Cast(*res)->Get(v8_str("0"));
2517 Handle<JSObject> int_array_handle_1 =
2518 v8::Utils::OpenHandle(*v8::Handle<v8::Object>::Cast(int_array_1));
2519 v8::Local<v8::Value> int_array_2 = v8::Object::Cast(*res)->Get(v8_str("1"));
2520 Handle<JSObject> int_array_handle_2 =
2521 v8::Utils::OpenHandle(*v8::Handle<v8::Object>::Cast(int_array_2));
2522
2523 Handle<JSObject> o =
2524 v8::Utils::OpenHandle(*v8::Handle<v8::Object>::Cast(res));
2525 CHECK(CcTest::heap()->InOldPointerSpace(*o));
2526 CHECK(CcTest::heap()->InOldPointerSpace(*int_array_handle_1));
2527 CHECK(CcTest::heap()->InOldPointerSpace(int_array_handle_1->elements()));
2528 CHECK(CcTest::heap()->InOldPointerSpace(*int_array_handle_2));
2529 CHECK(CcTest::heap()->InOldPointerSpace(int_array_handle_2->elements()));
2530}
2531
2532
2533TEST(OptimizedPretenuringNestedDoubleLiterals) {
2534 i::FLAG_allow_natives_syntax = true;
2535 i::FLAG_expose_gc = true;
2536 CcTest::InitializeVM();
2537 if (!CcTest::i_isolate()->use_crankshaft() || i::FLAG_always_opt) return;
2538 if (i::FLAG_gc_global || i::FLAG_stress_compaction) return;
2539 v8::HandleScope scope(CcTest::isolate());
2540
2541 // Grow new space unitl maximum capacity reached.
2542 while (!CcTest::heap()->new_space()->IsAtMaximumCapacity()) {
2543 CcTest::heap()->new_space()->Grow();
2544 }
2545
2546 i::ScopedVector<char> source(1024);
2547 i::SNPrintF(
2548 source,
2549 "var number_elements = %d;"
2550 "var elements = new Array(number_elements);"
2551 "function f() {"
2552 " for (var i = 0; i < number_elements; i++) {"
2553 " elements[i] = [[1.1, 1.2, 1.3],[2.1, 2.2, 2.3]];"
2554 " }"
2555 " return elements[number_elements - 1];"
2556 "};"
2557 "f(); gc();"
2558 "f(); f();"
2559 "%%OptimizeFunctionOnNextCall(f);"
2560 "f();",
2561 AllocationSite::kPretenureMinimumCreated);
2562
2563 v8::Local<v8::Value> res = CompileRun(source.start());
2564
2565 v8::Local<v8::Value> double_array_1 =
2566 v8::Object::Cast(*res)->Get(v8_str("0"));
2567 Handle<JSObject> double_array_handle_1 =
2568 v8::Utils::OpenHandle(*v8::Handle<v8::Object>::Cast(double_array_1));
2569 v8::Local<v8::Value> double_array_2 =
2570 v8::Object::Cast(*res)->Get(v8_str("1"));
2571 Handle<JSObject> double_array_handle_2 =
2572 v8::Utils::OpenHandle(*v8::Handle<v8::Object>::Cast(double_array_2));
2573
2574 Handle<JSObject> o =
2575 v8::Utils::OpenHandle(*v8::Handle<v8::Object>::Cast(res));
2576 CHECK(CcTest::heap()->InOldPointerSpace(*o));
2577 CHECK(CcTest::heap()->InOldPointerSpace(*double_array_handle_1));
2578 CHECK(CcTest::heap()->InOldDataSpace(double_array_handle_1->elements()));
2579 CHECK(CcTest::heap()->InOldPointerSpace(*double_array_handle_2));
2580 CHECK(CcTest::heap()->InOldDataSpace(double_array_handle_2->elements()));
2581}
2582
2583
2584// Make sure pretenuring feedback is gathered for constructed objects as well
2585// as for literals.
2586TEST(OptimizedPretenuringConstructorCalls) {
2587 if (!i::FLAG_pretenuring_call_new) {
2588 // FLAG_pretenuring_call_new needs to be synced with the snapshot.
2589 return;
2590 }
2591 i::FLAG_allow_natives_syntax = true;
2592 i::FLAG_expose_gc = true;
2593 CcTest::InitializeVM();
2594 if (!CcTest::i_isolate()->use_crankshaft() || i::FLAG_always_opt) return;
2595 if (i::FLAG_gc_global || i::FLAG_stress_compaction) return;
2596 v8::HandleScope scope(CcTest::isolate());
2597
2598 // Grow new space unitl maximum capacity reached.
2599 while (!CcTest::heap()->new_space()->IsAtMaximumCapacity()) {
2600 CcTest::heap()->new_space()->Grow();
2601 }
2602
2603 i::ScopedVector<char> source(1024);
2604 // Call new is doing slack tracking for the first
2605 // JSFunction::kGenerousAllocationCount allocations, and we can't find
2606 // mementos during that time.
2607 i::SNPrintF(
2608 source,
2609 "var number_elements = %d;"
2610 "var elements = new Array(number_elements);"
2611 "function foo() {"
2612 " this.a = 3;"
2613 " this.b = {};"
2614 "}"
2615 "function f() {"
2616 " for (var i = 0; i < number_elements; i++) {"
2617 " elements[i] = new foo();"
2618 " }"
2619 " return elements[number_elements - 1];"
2620 "};"
2621 "f(); gc();"
2622 "f(); f();"
2623 "%%OptimizeFunctionOnNextCall(f);"
2624 "f();",
2625 AllocationSite::kPretenureMinimumCreated +
2626 JSFunction::kGenerousAllocationCount);
2627
2628 v8::Local<v8::Value> res = CompileRun(source.start());
2629
2630 Handle<JSObject> o =
2631 v8::Utils::OpenHandle(*v8::Handle<v8::Object>::Cast(res));
2632
2633 CHECK(CcTest::heap()->InOldPointerSpace(*o));
2634}
2635
2636
2637TEST(OptimizedPretenuringCallNew) {
2638 if (!i::FLAG_pretenuring_call_new) {
2639 // FLAG_pretenuring_call_new needs to be synced with the snapshot.
2640 return;
2641 }
2642 i::FLAG_allow_natives_syntax = true;
2643 i::FLAG_expose_gc = true;
2644 CcTest::InitializeVM();
2645 if (!CcTest::i_isolate()->use_crankshaft() || i::FLAG_always_opt) return;
2646 if (i::FLAG_gc_global || i::FLAG_stress_compaction) return;
2647 v8::HandleScope scope(CcTest::isolate());
2648
2649 // Grow new space unitl maximum capacity reached.
2650 while (!CcTest::heap()->new_space()->IsAtMaximumCapacity()) {
2651 CcTest::heap()->new_space()->Grow();
2652 }
2653
2654 i::ScopedVector<char> source(1024);
2655 // Call new is doing slack tracking for the first
2656 // JSFunction::kGenerousAllocationCount allocations, and we can't find
2657 // mementos during that time.
2658 i::SNPrintF(
2659 source,
2660 "var number_elements = %d;"
2661 "var elements = new Array(number_elements);"
2662 "function g() { this.a = 0; }"
2663 "function f() {"
2664 " for (var i = 0; i < number_elements; i++) {"
2665 " elements[i] = new g();"
2666 " }"
2667 " return elements[number_elements - 1];"
2668 "};"
2669 "f(); gc();"
2670 "f(); f();"
2671 "%%OptimizeFunctionOnNextCall(f);"
2672 "f();",
2673 AllocationSite::kPretenureMinimumCreated +
2674 JSFunction::kGenerousAllocationCount);
2675
2676 v8::Local<v8::Value> res = CompileRun(source.start());
2677
2678 Handle<JSObject> o =
2679 v8::Utils::OpenHandle(*v8::Handle<v8::Object>::Cast(res));
2680 CHECK(CcTest::heap()->InOldPointerSpace(*o));
2681}
2682
2683
2684// Test regular array literals allocation.
2685TEST(OptimizedAllocationArrayLiterals) {
2686 i::FLAG_allow_natives_syntax = true;
2687 CcTest::InitializeVM();
2688 if (!CcTest::i_isolate()->use_crankshaft() || i::FLAG_always_opt) return;
2689 if (i::FLAG_gc_global || i::FLAG_stress_compaction) return;
2690 v8::HandleScope scope(CcTest::isolate());
2691
2692 v8::Local<v8::Value> res = CompileRun(
2693 "function f() {"
2694 " var numbers = new Array(1, 2, 3);"
2695 " numbers[0] = 3.14;"
2696 " return numbers;"
2697 "};"
2698 "f(); f(); f();"
2699 "%OptimizeFunctionOnNextCall(f);"
2700 "f();");
2701 CHECK_EQ(static_cast<int>(3.14),
2702 v8::Object::Cast(*res)->Get(v8_str("0"))->Int32Value());
2703
2704 Handle<JSObject> o =
2705 v8::Utils::OpenHandle(*v8::Handle<v8::Object>::Cast(res));
2706
2707 CHECK(CcTest::heap()->InNewSpace(o->elements()));
2708}
2709
2710
2711static int CountMapTransitions(Map* map) {
2712 return map->transitions()->number_of_transitions();
2713}
2714
2715
2716// Test that map transitions are cleared and maps are collected with
2717// incremental marking as well.
2718TEST(Regress1465) {
2719 i::FLAG_stress_compaction = false;
2720 i::FLAG_allow_natives_syntax = true;
2721 i::FLAG_trace_incremental_marking = true;
2722 CcTest::InitializeVM();
2723 v8::HandleScope scope(CcTest::isolate());
2724 static const int transitions_count = 256;
2725
2726 CompileRun("function F() {}");
2727 {
2728 AlwaysAllocateScope always_allocate(CcTest::i_isolate());
2729 for (int i = 0; i < transitions_count; i++) {
2730 EmbeddedVector<char, 64> buffer;
2731 SNPrintF(buffer, "var o = new F; o.prop%d = %d;", i, i);
2732 CompileRun(buffer.start());
2733 }
2734 CompileRun("var root = new F;");
2735 }
2736
2737 Handle<JSObject> root =
2738 v8::Utils::OpenHandle(
2739 *v8::Handle<v8::Object>::Cast(
2740 CcTest::global()->Get(v8_str("root"))));
2741
2742 // Count number of live transitions before marking.
2743 int transitions_before = CountMapTransitions(root->map());
2744 CompileRun("%DebugPrint(root);");
2745 CHECK_EQ(transitions_count, transitions_before);
2746
2747 SimulateIncrementalMarking(CcTest::heap());
2748 CcTest::heap()->CollectAllGarbage(Heap::kNoGCFlags);
2749
2750 // Count number of live transitions after marking. Note that one transition
2751 // is left, because 'o' still holds an instance of one transition target.
2752 int transitions_after = CountMapTransitions(root->map());
2753 CompileRun("%DebugPrint(root);");
2754 CHECK_EQ(1, transitions_after);
2755}
2756
2757
2758#ifdef DEBUG
2759static void AddTransitions(int transitions_count) {
2760 AlwaysAllocateScope always_allocate(CcTest::i_isolate());
2761 for (int i = 0; i < transitions_count; i++) {
2762 EmbeddedVector<char, 64> buffer;
2763 SNPrintF(buffer, "var o = new F; o.prop%d = %d;", i, i);
2764 CompileRun(buffer.start());
2765 }
2766}
2767
2768
2769static Handle<JSObject> GetByName(const char* name) {
2770 return v8::Utils::OpenHandle(
2771 *v8::Handle<v8::Object>::Cast(
2772 CcTest::global()->Get(v8_str(name))));
2773}
2774
2775
2776static void AddPropertyTo(
2777 int gc_count, Handle<JSObject> object, const char* property_name) {
2778 Isolate* isolate = CcTest::i_isolate();
2779 Factory* factory = isolate->factory();
2780 Handle<String> prop_name = factory->InternalizeUtf8String(property_name);
2781 Handle<Smi> twenty_three(Smi::FromInt(23), isolate);
2782 i::FLAG_gc_interval = gc_count;
2783 i::FLAG_gc_global = true;
2784 CcTest::heap()->set_allocation_timeout(gc_count);
2785 JSReceiver::SetProperty(object, prop_name, twenty_three, SLOPPY).Check();
2786}
2787
2788
2789TEST(TransitionArrayShrinksDuringAllocToZero) {
2790 i::FLAG_stress_compaction = false;
2791 i::FLAG_allow_natives_syntax = true;
2792 CcTest::InitializeVM();
2793 v8::HandleScope scope(CcTest::isolate());
2794 static const int transitions_count = 10;
2795 CompileRun("function F() { }");
2796 AddTransitions(transitions_count);
2797 CompileRun("var root = new F;");
2798 Handle<JSObject> root = GetByName("root");
2799
2800 // Count number of live transitions before marking.
2801 int transitions_before = CountMapTransitions(root->map());
2802 CHECK_EQ(transitions_count, transitions_before);
2803
2804 // Get rid of o
2805 CompileRun("o = new F;"
2806 "root = new F");
2807 root = GetByName("root");
2808 AddPropertyTo(2, root, "funny");
2809
2810 // Count number of live transitions after marking. Note that one transition
2811 // is left, because 'o' still holds an instance of one transition target.
2812 int transitions_after = CountMapTransitions(
2813 Map::cast(root->map()->GetBackPointer()));
2814 CHECK_EQ(1, transitions_after);
2815}
2816
2817
2818TEST(TransitionArrayShrinksDuringAllocToOne) {
2819 i::FLAG_stress_compaction = false;
2820 i::FLAG_allow_natives_syntax = true;
2821 CcTest::InitializeVM();
2822 v8::HandleScope scope(CcTest::isolate());
2823 static const int transitions_count = 10;
2824 CompileRun("function F() {}");
2825 AddTransitions(transitions_count);
2826 CompileRun("var root = new F;");
2827 Handle<JSObject> root = GetByName("root");
2828
2829 // Count number of live transitions before marking.
2830 int transitions_before = CountMapTransitions(root->map());
2831 CHECK_EQ(transitions_count, transitions_before);
2832
2833 root = GetByName("root");
2834 AddPropertyTo(2, root, "funny");
2835
2836 // Count number of live transitions after marking. Note that one transition
2837 // is left, because 'o' still holds an instance of one transition target.
2838 int transitions_after = CountMapTransitions(
2839 Map::cast(root->map()->GetBackPointer()));
2840 CHECK_EQ(2, transitions_after);
2841}
2842
2843
2844TEST(TransitionArrayShrinksDuringAllocToOnePropertyFound) {
2845 i::FLAG_stress_compaction = false;
2846 i::FLAG_allow_natives_syntax = true;
2847 CcTest::InitializeVM();
2848 v8::HandleScope scope(CcTest::isolate());
2849 static const int transitions_count = 10;
2850 CompileRun("function F() {}");
2851 AddTransitions(transitions_count);
2852 CompileRun("var root = new F;");
2853 Handle<JSObject> root = GetByName("root");
2854
2855 // Count number of live transitions before marking.
2856 int transitions_before = CountMapTransitions(root->map());
2857 CHECK_EQ(transitions_count, transitions_before);
2858
2859 root = GetByName("root");
2860 AddPropertyTo(0, root, "prop9");
2861 CcTest::i_isolate()->heap()->CollectGarbage(OLD_POINTER_SPACE);
2862
2863 // Count number of live transitions after marking. Note that one transition
2864 // is left, because 'o' still holds an instance of one transition target.
2865 int transitions_after = CountMapTransitions(
2866 Map::cast(root->map()->GetBackPointer()));
2867 CHECK_EQ(1, transitions_after);
2868}
2869
2870
2871TEST(TransitionArraySimpleToFull) {
2872 i::FLAG_stress_compaction = false;
2873 i::FLAG_allow_natives_syntax = true;
2874 CcTest::InitializeVM();
2875 v8::HandleScope scope(CcTest::isolate());
2876 static const int transitions_count = 1;
2877 CompileRun("function F() {}");
2878 AddTransitions(transitions_count);
2879 CompileRun("var root = new F;");
2880 Handle<JSObject> root = GetByName("root");
2881
2882 // Count number of live transitions before marking.
2883 int transitions_before = CountMapTransitions(root->map());
2884 CHECK_EQ(transitions_count, transitions_before);
2885
2886 CompileRun("o = new F;"
2887 "root = new F");
2888 root = GetByName("root");
2889 DCHECK(root->map()->transitions()->IsSimpleTransition());
2890 AddPropertyTo(2, root, "happy");
2891
2892 // Count number of live transitions after marking. Note that one transition
2893 // is left, because 'o' still holds an instance of one transition target.
2894 int transitions_after = CountMapTransitions(
2895 Map::cast(root->map()->GetBackPointer()));
2896 CHECK_EQ(1, transitions_after);
2897}
2898#endif // DEBUG
2899
2900
2901TEST(Regress2143a) {
2902 i::FLAG_collect_maps = true;
2903 i::FLAG_incremental_marking = true;
2904 CcTest::InitializeVM();
2905 v8::HandleScope scope(CcTest::isolate());
2906
2907 // Prepare a map transition from the root object together with a yet
2908 // untransitioned root object.
2909 CompileRun("var root = new Object;"
2910 "root.foo = 0;"
2911 "root = new Object;");
2912
2913 SimulateIncrementalMarking(CcTest::heap());
2914
2915 // Compile a StoreIC that performs the prepared map transition. This
2916 // will restart incremental marking and should make sure the root is
2917 // marked grey again.
2918 CompileRun("function f(o) {"
2919 " o.foo = 0;"
2920 "}"
2921 "f(new Object);"
2922 "f(root);");
2923
2924 // This bug only triggers with aggressive IC clearing.
2925 CcTest::heap()->AgeInlineCaches();
2926
2927 // Explicitly request GC to perform final marking step and sweeping.
2928 CcTest::heap()->CollectAllGarbage(Heap::kNoGCFlags);
2929
2930 Handle<JSObject> root =
2931 v8::Utils::OpenHandle(
2932 *v8::Handle<v8::Object>::Cast(
2933 CcTest::global()->Get(v8_str("root"))));
2934
2935 // The root object should be in a sane state.
2936 CHECK(root->IsJSObject());
2937 CHECK(root->map()->IsMap());
2938}
2939
2940
2941TEST(Regress2143b) {
2942 i::FLAG_collect_maps = true;
2943 i::FLAG_incremental_marking = true;
2944 i::FLAG_allow_natives_syntax = true;
2945 CcTest::InitializeVM();
2946 v8::HandleScope scope(CcTest::isolate());
2947
2948 // Prepare a map transition from the root object together with a yet
2949 // untransitioned root object.
2950 CompileRun("var root = new Object;"
2951 "root.foo = 0;"
2952 "root = new Object;");
2953
2954 SimulateIncrementalMarking(CcTest::heap());
2955
2956 // Compile an optimized LStoreNamedField that performs the prepared
2957 // map transition. This will restart incremental marking and should
2958 // make sure the root is marked grey again.
2959 CompileRun("function f(o) {"
2960 " o.foo = 0;"
2961 "}"
2962 "f(new Object);"
2963 "f(new Object);"
2964 "%OptimizeFunctionOnNextCall(f);"
2965 "f(root);"
2966 "%DeoptimizeFunction(f);");
2967
2968 // This bug only triggers with aggressive IC clearing.
2969 CcTest::heap()->AgeInlineCaches();
2970
2971 // Explicitly request GC to perform final marking step and sweeping.
2972 CcTest::heap()->CollectAllGarbage(Heap::kNoGCFlags);
2973
2974 Handle<JSObject> root =
2975 v8::Utils::OpenHandle(
2976 *v8::Handle<v8::Object>::Cast(
2977 CcTest::global()->Get(v8_str("root"))));
2978
2979 // The root object should be in a sane state.
2980 CHECK(root->IsJSObject());
2981 CHECK(root->map()->IsMap());
2982}
2983
2984
2985TEST(ReleaseOverReservedPages) {
2986 if (FLAG_never_compact) return;
2987 i::FLAG_trace_gc = true;
2988 // The optimizer can allocate stuff, messing up the test.
2989 i::FLAG_crankshaft = false;
2990 i::FLAG_always_opt = false;
2991 CcTest::InitializeVM();
2992 Isolate* isolate = CcTest::i_isolate();
2993 Factory* factory = isolate->factory();
2994 Heap* heap = isolate->heap();
2995 v8::HandleScope scope(CcTest::isolate());
2996 static const int number_of_test_pages = 20;
2997
2998 // Prepare many pages with low live-bytes count.
2999 PagedSpace* old_pointer_space = heap->old_pointer_space();
3000 CHECK_EQ(1, old_pointer_space->CountTotalPages());
3001 for (int i = 0; i < number_of_test_pages; i++) {
3002 AlwaysAllocateScope always_allocate(isolate);
3003 SimulateFullSpace(old_pointer_space);
3004 factory->NewFixedArray(1, TENURED);
3005 }
3006 CHECK_EQ(number_of_test_pages + 1, old_pointer_space->CountTotalPages());
3007
3008 // Triggering one GC will cause a lot of garbage to be discovered but
3009 // even spread across all allocated pages.
3010 heap->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask,
3011 "triggered for preparation");
3012 CHECK_GE(number_of_test_pages + 1, old_pointer_space->CountTotalPages());
3013
3014 // Triggering subsequent GCs should cause at least half of the pages
3015 // to be released to the OS after at most two cycles.
3016 heap->CollectAllGarbage(Heap::kNoGCFlags, "triggered by test 1");
3017 CHECK_GE(number_of_test_pages + 1, old_pointer_space->CountTotalPages());
3018 heap->CollectAllGarbage(Heap::kNoGCFlags, "triggered by test 2");
3019 CHECK_GE(number_of_test_pages + 1, old_pointer_space->CountTotalPages() * 2);
3020
3021 // Triggering a last-resort GC should cause all pages to be released to the
3022 // OS so that other processes can seize the memory. If we get a failure here
3023 // where there are 2 pages left instead of 1, then we should increase the
3024 // size of the first page a little in SizeOfFirstPage in spaces.cc. The
3025 // first page should be small in order to reduce memory used when the VM
3026 // boots, but if the 20 small arrays don't fit on the first page then that's
3027 // an indication that it is too small.
3028 heap->CollectAllAvailableGarbage("triggered really hard");
3029 CHECK_EQ(1, old_pointer_space->CountTotalPages());
3030}
3031
3032
3033TEST(Regress2237) {
3034 i::FLAG_stress_compaction = false;
3035 CcTest::InitializeVM();
3036 Isolate* isolate = CcTest::i_isolate();
3037 Factory* factory = isolate->factory();
3038 v8::HandleScope scope(CcTest::isolate());
3039 Handle<String> slice(CcTest::heap()->empty_string());
3040
3041 {
3042 // Generate a parent that lives in new-space.
3043 v8::HandleScope inner_scope(CcTest::isolate());
3044 const char* c = "This text is long enough to trigger sliced strings.";
3045 Handle<String> s = factory->NewStringFromAsciiChecked(c);
3046 CHECK(s->IsSeqOneByteString());
3047 CHECK(CcTest::heap()->InNewSpace(*s));
3048
3049 // Generate a sliced string that is based on the above parent and
3050 // lives in old-space.
3051 SimulateFullSpace(CcTest::heap()->new_space());
3052 AlwaysAllocateScope always_allocate(isolate);
3053 Handle<String> t = factory->NewProperSubString(s, 5, 35);
3054 CHECK(t->IsSlicedString());
3055 CHECK(!CcTest::heap()->InNewSpace(*t));
3056 *slice.location() = *t.location();
3057 }
3058
3059 CHECK(SlicedString::cast(*slice)->parent()->IsSeqOneByteString());
3060 CcTest::heap()->CollectAllGarbage(Heap::kNoGCFlags);
3061 CHECK(SlicedString::cast(*slice)->parent()->IsSeqOneByteString());
3062}
3063
3064
3065#ifdef OBJECT_PRINT
3066TEST(PrintSharedFunctionInfo) {
3067 CcTest::InitializeVM();
3068 v8::HandleScope scope(CcTest::isolate());
3069 const char* source = "f = function() { return 987654321; }\n"
3070 "g = function() { return 123456789; }\n";
3071 CompileRun(source);
3072 Handle<JSFunction> g =
3073 v8::Utils::OpenHandle(
3074 *v8::Handle<v8::Function>::Cast(
3075 CcTest::global()->Get(v8_str("g"))));
3076
3077 OFStream os(stdout);
3078 g->shared()->Print(os);
3079 os << endl;
3080}
3081#endif // OBJECT_PRINT
3082
3083
3084TEST(Regress2211) {
3085 CcTest::InitializeVM();
3086 v8::HandleScope scope(CcTest::isolate());
3087
3088 v8::Handle<v8::String> value = v8_str("val string");
3089 Smi* hash = Smi::FromInt(321);
3090 Factory* factory = CcTest::i_isolate()->factory();
3091
3092 for (int i = 0; i < 2; i++) {
3093 // Store identity hash first and common hidden property second.
3094 v8::Handle<v8::Object> obj = v8::Object::New(CcTest::isolate());
3095 Handle<JSObject> internal_obj = v8::Utils::OpenHandle(*obj);
3096 CHECK(internal_obj->HasFastProperties());
3097
3098 // In the first iteration, set hidden value first and identity hash second.
3099 // In the second iteration, reverse the order.
3100 if (i == 0) obj->SetHiddenValue(v8_str("key string"), value);
3101 JSObject::SetIdentityHash(internal_obj, handle(hash, CcTest::i_isolate()));
3102 if (i == 1) obj->SetHiddenValue(v8_str("key string"), value);
3103
3104 // Check values.
3105 CHECK_EQ(hash,
3106 internal_obj->GetHiddenProperty(factory->identity_hash_string()));
3107 CHECK(value->Equals(obj->GetHiddenValue(v8_str("key string"))));
3108
3109 // Check size.
3110 FieldIndex index = FieldIndex::ForDescriptor(internal_obj->map(), 0);
3111 ObjectHashTable* hashtable = ObjectHashTable::cast(
3112 internal_obj->RawFastPropertyAt(index));
3113 // HashTable header (5) and 4 initial entries (8).
3114 CHECK_LE(hashtable->SizeFor(hashtable->length()), 13 * kPointerSize);
3115 }
3116}
3117
3118
3119TEST(IncrementalMarkingClearsTypeFeedbackInfo) {
3120 if (i::FLAG_always_opt) return;
3121 CcTest::InitializeVM();
3122 v8::HandleScope scope(CcTest::isolate());
3123 v8::Local<v8::Value> fun1, fun2;
3124
3125 {
3126 LocalContext env;
3127 CompileRun("function fun() {};");
3128 fun1 = env->Global()->Get(v8_str("fun"));
3129 }
3130
3131 {
3132 LocalContext env;
3133 CompileRun("function fun() {};");
3134 fun2 = env->Global()->Get(v8_str("fun"));
3135 }
3136
3137 // Prepare function f that contains type feedback for closures
3138 // originating from two different native contexts.
3139 CcTest::global()->Set(v8_str("fun1"), fun1);
3140 CcTest::global()->Set(v8_str("fun2"), fun2);
3141 CompileRun("function f(a, b) { a(); b(); } f(fun1, fun2);");
3142
3143 Handle<JSFunction> f =
3144 v8::Utils::OpenHandle(
3145 *v8::Handle<v8::Function>::Cast(
3146 CcTest::global()->Get(v8_str("f"))));
3147
3148 Handle<TypeFeedbackVector> feedback_vector(f->shared()->feedback_vector());
3149
3150 int expected_length = FLAG_vector_ics ? 4 : 2;
3151 CHECK_EQ(expected_length, feedback_vector->length());
3152 for (int i = 0; i < expected_length; i++) {
3153 if ((i % 2) == 1) {
3154 CHECK(feedback_vector->get(i)->IsJSFunction());
3155 }
3156 }
3157
3158 SimulateIncrementalMarking(CcTest::heap());
3159 CcTest::heap()->CollectAllGarbage(Heap::kNoGCFlags);
3160
3161 CHECK_EQ(expected_length, feedback_vector->length());
3162 for (int i = 0; i < expected_length; i++) {
3163 CHECK_EQ(feedback_vector->get(i),
3164 *TypeFeedbackVector::UninitializedSentinel(CcTest::i_isolate()));
3165 }
3166}
3167
3168
3169static Code* FindFirstIC(Code* code, Code::Kind kind) {
3170 int mask = RelocInfo::ModeMask(RelocInfo::CODE_TARGET) |
3171 RelocInfo::ModeMask(RelocInfo::CONSTRUCT_CALL) |
3172 RelocInfo::ModeMask(RelocInfo::CODE_TARGET_WITH_ID);
3173 for (RelocIterator it(code, mask); !it.done(); it.next()) {
3174 RelocInfo* info = it.rinfo();
3175 Code* target = Code::GetCodeFromTargetAddress(info->target_address());
3176 if (target->is_inline_cache_stub() && target->kind() == kind) {
3177 return target;
3178 }
3179 }
3180 return NULL;
3181}
3182
3183
3184TEST(IncrementalMarkingPreservesMonomorphicIC) {
3185 if (i::FLAG_always_opt) return;
3186 CcTest::InitializeVM();
3187 v8::HandleScope scope(CcTest::isolate());
3188
3189 // Prepare function f that contains a monomorphic IC for object
3190 // originating from the same native context.
3191 CompileRun("function fun() { this.x = 1; }; var obj = new fun();"
3192 "function f(o) { return o.x; } f(obj); f(obj);");
3193 Handle<JSFunction> f =
3194 v8::Utils::OpenHandle(
3195 *v8::Handle<v8::Function>::Cast(
3196 CcTest::global()->Get(v8_str("f"))));
3197
3198 Code* ic_before = FindFirstIC(f->shared()->code(), Code::LOAD_IC);
3199 CHECK(ic_before->ic_state() == MONOMORPHIC);
3200
3201 SimulateIncrementalMarking(CcTest::heap());
3202 CcTest::heap()->CollectAllGarbage(Heap::kNoGCFlags);
3203
3204 Code* ic_after = FindFirstIC(f->shared()->code(), Code::LOAD_IC);
3205 CHECK(ic_after->ic_state() == MONOMORPHIC);
3206}
3207
3208
3209TEST(IncrementalMarkingClearsMonomorphicIC) {
3210 if (i::FLAG_always_opt) return;
3211 CcTest::InitializeVM();
3212 v8::HandleScope scope(CcTest::isolate());
3213 v8::Local<v8::Value> obj1;
3214
3215 {
3216 LocalContext env;
3217 CompileRun("function fun() { this.x = 1; }; var obj = new fun();");
3218 obj1 = env->Global()->Get(v8_str("obj"));
3219 }
3220
3221 // Prepare function f that contains a monomorphic IC for object
3222 // originating from a different native context.
3223 CcTest::global()->Set(v8_str("obj1"), obj1);
3224 CompileRun("function f(o) { return o.x; } f(obj1); f(obj1);");
3225 Handle<JSFunction> f =
3226 v8::Utils::OpenHandle(
3227 *v8::Handle<v8::Function>::Cast(
3228 CcTest::global()->Get(v8_str("f"))));
3229
3230 Code* ic_before = FindFirstIC(f->shared()->code(), Code::LOAD_IC);
3231 CHECK(ic_before->ic_state() == MONOMORPHIC);
3232
3233 // Fire context dispose notification.
3234 CcTest::isolate()->ContextDisposedNotification();
3235 SimulateIncrementalMarking(CcTest::heap());
3236 CcTest::heap()->CollectAllGarbage(Heap::kNoGCFlags);
3237
3238 Code* ic_after = FindFirstIC(f->shared()->code(), Code::LOAD_IC);
3239 CHECK(IC::IsCleared(ic_after));
3240}
3241
3242
3243TEST(IncrementalMarkingClearsPolymorphicIC) {
3244 if (i::FLAG_always_opt) return;
3245 CcTest::InitializeVM();
3246 v8::HandleScope scope(CcTest::isolate());
3247 v8::Local<v8::Value> obj1, obj2;
3248
3249 {
3250 LocalContext env;
3251 CompileRun("function fun() { this.x = 1; }; var obj = new fun();");
3252 obj1 = env->Global()->Get(v8_str("obj"));
3253 }
3254
3255 {
3256 LocalContext env;
3257 CompileRun("function fun() { this.x = 2; }; var obj = new fun();");
3258 obj2 = env->Global()->Get(v8_str("obj"));
3259 }
3260
3261 // Prepare function f that contains a polymorphic IC for objects
3262 // originating from two different native contexts.
3263 CcTest::global()->Set(v8_str("obj1"), obj1);
3264 CcTest::global()->Set(v8_str("obj2"), obj2);
3265 CompileRun("function f(o) { return o.x; } f(obj1); f(obj1); f(obj2);");
3266 Handle<JSFunction> f =
3267 v8::Utils::OpenHandle(
3268 *v8::Handle<v8::Function>::Cast(
3269 CcTest::global()->Get(v8_str("f"))));
3270
3271 Code* ic_before = FindFirstIC(f->shared()->code(), Code::LOAD_IC);
3272 CHECK(ic_before->ic_state() == POLYMORPHIC);
3273
3274 // Fire context dispose notification.
3275 CcTest::isolate()->ContextDisposedNotification();
3276 SimulateIncrementalMarking(CcTest::heap());
3277 CcTest::heap()->CollectAllGarbage(Heap::kNoGCFlags);
3278
3279 Code* ic_after = FindFirstIC(f->shared()->code(), Code::LOAD_IC);
3280 CHECK(IC::IsCleared(ic_after));
3281}
3282
3283
3284class SourceResource : public v8::String::ExternalOneByteStringResource {
3285 public:
3286 explicit SourceResource(const char* data)
3287 : data_(data), length_(strlen(data)) { }
3288
3289 virtual void Dispose() {
3290 i::DeleteArray(data_);
3291 data_ = NULL;
3292 }
3293
3294 const char* data() const { return data_; }
3295
3296 size_t length() const { return length_; }
3297
3298 bool IsDisposed() { return data_ == NULL; }
3299
3300 private:
3301 const char* data_;
3302 size_t length_;
3303};
3304
3305
3306void ReleaseStackTraceDataTest(v8::Isolate* isolate, const char* source,
3307 const char* accessor) {
3308 // Test that the data retained by the Error.stack accessor is released
3309 // after the first time the accessor is fired. We use external string
3310 // to check whether the data is being released since the external string
3311 // resource's callback is fired when the external string is GC'ed.
3312 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
3313 v8::HandleScope scope(isolate);
3314 SourceResource* resource = new SourceResource(i::StrDup(source));
3315 {
3316 v8::HandleScope scope(isolate);
3317 v8::Handle<v8::String> source_string =
3318 v8::String::NewExternal(isolate, resource);
3319 i_isolate->heap()->CollectAllAvailableGarbage();
3320 v8::Script::Compile(source_string)->Run();
3321 CHECK(!resource->IsDisposed());
3322 }
3323 // i_isolate->heap()->CollectAllAvailableGarbage();
3324 CHECK(!resource->IsDisposed());
3325
3326 CompileRun(accessor);
3327 i_isolate->heap()->CollectAllAvailableGarbage();
3328
3329 // External source has been released.
3330 CHECK(resource->IsDisposed());
3331 delete resource;
3332}
3333
3334
3335UNINITIALIZED_TEST(ReleaseStackTraceData) {
3336 if (i::FLAG_always_opt) {
3337 // TODO(ulan): Remove this once the memory leak via code_next_link is fixed.
3338 // See: https://codereview.chromium.org/181833004/
3339 return;
3340 }
3341 FLAG_use_ic = false; // ICs retain objects.
3342 FLAG_concurrent_recompilation = false;
3343 v8::Isolate* isolate = v8::Isolate::New();
3344 {
3345 v8::Isolate::Scope isolate_scope(isolate);
3346 v8::HandleScope handle_scope(isolate);
3347 v8::Context::New(isolate)->Enter();
3348 static const char* source1 = "var error = null; "
3349 /* Normal Error */ "try { "
3350 " throw new Error(); "
3351 "} catch (e) { "
3352 " error = e; "
3353 "} ";
3354 static const char* source2 = "var error = null; "
3355 /* Stack overflow */ "try { "
3356 " (function f() { f(); })(); "
3357 "} catch (e) { "
3358 " error = e; "
3359 "} ";
3360 static const char* source3 = "var error = null; "
3361 /* Normal Error */ "try { "
3362 /* as prototype */ " throw new Error(); "
3363 "} catch (e) { "
3364 " error = {}; "
3365 " error.__proto__ = e; "
3366 "} ";
3367 static const char* source4 = "var error = null; "
3368 /* Stack overflow */ "try { "
3369 /* as prototype */ " (function f() { f(); })(); "
3370 "} catch (e) { "
3371 " error = {}; "
3372 " error.__proto__ = e; "
3373 "} ";
3374 static const char* getter = "error.stack";
3375 static const char* setter = "error.stack = 0";
3376
3377 ReleaseStackTraceDataTest(isolate, source1, setter);
3378 ReleaseStackTraceDataTest(isolate, source2, setter);
3379 // We do not test source3 and source4 with setter, since the setter is
3380 // supposed to (untypically) write to the receiver, not the holder. This is
3381 // to emulate the behavior of a data property.
3382
3383 ReleaseStackTraceDataTest(isolate, source1, getter);
3384 ReleaseStackTraceDataTest(isolate, source2, getter);
3385 ReleaseStackTraceDataTest(isolate, source3, getter);
3386 ReleaseStackTraceDataTest(isolate, source4, getter);
3387 }
3388 isolate->Dispose();
3389}
3390
3391
3392TEST(Regress159140) {
3393 i::FLAG_allow_natives_syntax = true;
3394 i::FLAG_flush_code_incrementally = true;
3395 CcTest::InitializeVM();
3396 Isolate* isolate = CcTest::i_isolate();
3397 Heap* heap = isolate->heap();
3398 HandleScope scope(isolate);
3399
3400 // Perform one initial GC to enable code flushing.
3401 heap->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask);
3402
3403 // Prepare several closures that are all eligible for code flushing
3404 // because all reachable ones are not optimized. Make sure that the
3405 // optimized code object is directly reachable through a handle so
3406 // that it is marked black during incremental marking.
3407 Handle<Code> code;
3408 {
3409 HandleScope inner_scope(isolate);
3410 CompileRun("function h(x) {}"
3411 "function mkClosure() {"
3412 " return function(x) { return x + 1; };"
3413 "}"
3414 "var f = mkClosure();"
3415 "var g = mkClosure();"
3416 "f(1); f(2);"
3417 "g(1); g(2);"
3418 "h(1); h(2);"
3419 "%OptimizeFunctionOnNextCall(f); f(3);"
3420 "%OptimizeFunctionOnNextCall(h); h(3);");
3421
3422 Handle<JSFunction> f =
3423 v8::Utils::OpenHandle(
3424 *v8::Handle<v8::Function>::Cast(
3425 CcTest::global()->Get(v8_str("f"))));
3426 CHECK(f->is_compiled());
3427 CompileRun("f = null;");
3428
3429 Handle<JSFunction> g =
3430 v8::Utils::OpenHandle(
3431 *v8::Handle<v8::Function>::Cast(
3432 CcTest::global()->Get(v8_str("g"))));
3433 CHECK(g->is_compiled());
3434 const int kAgingThreshold = 6;
3435 for (int i = 0; i < kAgingThreshold; i++) {
3436 g->code()->MakeOlder(static_cast<MarkingParity>(i % 2));
3437 }
3438
3439 code = inner_scope.CloseAndEscape(Handle<Code>(f->code()));
3440 }
3441
3442 // Simulate incremental marking so that the functions are enqueued as
3443 // code flushing candidates. Then optimize one function. Finally
3444 // finish the GC to complete code flushing.
3445 SimulateIncrementalMarking(heap);
3446 CompileRun("%OptimizeFunctionOnNextCall(g); g(3);");
3447 heap->CollectAllGarbage(Heap::kNoGCFlags);
3448
3449 // Unoptimized code is missing and the deoptimizer will go ballistic.
3450 CompileRun("g('bozo');");
3451}
3452
3453
3454TEST(Regress165495) {
3455 i::FLAG_allow_natives_syntax = true;
3456 i::FLAG_flush_code_incrementally = true;
3457 CcTest::InitializeVM();
3458 Isolate* isolate = CcTest::i_isolate();
3459 Heap* heap = isolate->heap();
3460 HandleScope scope(isolate);
3461
3462 // Perform one initial GC to enable code flushing.
3463 heap->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask);
3464
3465 // Prepare an optimized closure that the optimized code map will get
3466 // populated. Then age the unoptimized code to trigger code flushing
3467 // but make sure the optimized code is unreachable.
3468 {
3469 HandleScope inner_scope(isolate);
3470 CompileRun("function mkClosure() {"
3471 " return function(x) { return x + 1; };"
3472 "}"
3473 "var f = mkClosure();"
3474 "f(1); f(2);"
3475 "%OptimizeFunctionOnNextCall(f); f(3);");
3476
3477 Handle<JSFunction> f =
3478 v8::Utils::OpenHandle(
3479 *v8::Handle<v8::Function>::Cast(
3480 CcTest::global()->Get(v8_str("f"))));
3481 CHECK(f->is_compiled());
3482 const int kAgingThreshold = 6;
3483 for (int i = 0; i < kAgingThreshold; i++) {
3484 f->shared()->code()->MakeOlder(static_cast<MarkingParity>(i % 2));
3485 }
3486
3487 CompileRun("f = null;");
3488 }
3489
3490 // Simulate incremental marking so that unoptimized code is flushed
3491 // even though it still is cached in the optimized code map.
3492 SimulateIncrementalMarking(heap);
3493 heap->CollectAllGarbage(Heap::kNoGCFlags);
3494
3495 // Make a new closure that will get code installed from the code map.
3496 // Unoptimized code is missing and the deoptimizer will go ballistic.
3497 CompileRun("var g = mkClosure(); g('bozo');");
3498}
3499
3500
3501TEST(Regress169209) {
3502 i::FLAG_stress_compaction = false;
3503 i::FLAG_allow_natives_syntax = true;
3504 i::FLAG_flush_code_incrementally = true;
3505
3506 CcTest::InitializeVM();
3507 Isolate* isolate = CcTest::i_isolate();
3508 Heap* heap = isolate->heap();
3509 HandleScope scope(isolate);
3510
3511 // Perform one initial GC to enable code flushing.
3512 heap->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask);
3513
3514 // Prepare a shared function info eligible for code flushing for which
3515 // the unoptimized code will be replaced during optimization.
3516 Handle<SharedFunctionInfo> shared1;
3517 {
3518 HandleScope inner_scope(isolate);
3519 CompileRun("function f() { return 'foobar'; }"
3520 "function g(x) { if (x) f(); }"
3521 "f();"
3522 "g(false);"
3523 "g(false);");
3524
3525 Handle<JSFunction> f =
3526 v8::Utils::OpenHandle(
3527 *v8::Handle<v8::Function>::Cast(
3528 CcTest::global()->Get(v8_str("f"))));
3529 CHECK(f->is_compiled());
3530 const int kAgingThreshold = 6;
3531 for (int i = 0; i < kAgingThreshold; i++) {
3532 f->shared()->code()->MakeOlder(static_cast<MarkingParity>(i % 2));
3533 }
3534
3535 shared1 = inner_scope.CloseAndEscape(handle(f->shared(), isolate));
3536 }
3537
3538 // Prepare a shared function info eligible for code flushing that will
3539 // represent the dangling tail of the candidate list.
3540 Handle<SharedFunctionInfo> shared2;
3541 {
3542 HandleScope inner_scope(isolate);
3543 CompileRun("function flushMe() { return 0; }"
3544 "flushMe(1);");
3545
3546 Handle<JSFunction> f =
3547 v8::Utils::OpenHandle(
3548 *v8::Handle<v8::Function>::Cast(
3549 CcTest::global()->Get(v8_str("flushMe"))));
3550 CHECK(f->is_compiled());
3551 const int kAgingThreshold = 6;
3552 for (int i = 0; i < kAgingThreshold; i++) {
3553 f->shared()->code()->MakeOlder(static_cast<MarkingParity>(i % 2));
3554 }
3555
3556 shared2 = inner_scope.CloseAndEscape(handle(f->shared(), isolate));
3557 }
3558
3559 // Simulate incremental marking and collect code flushing candidates.
3560 SimulateIncrementalMarking(heap);
3561 CHECK(shared1->code()->gc_metadata() != NULL);
3562
3563 // Optimize function and make sure the unoptimized code is replaced.
3564#ifdef DEBUG
3565 FLAG_stop_at = "f";
3566#endif
3567 CompileRun("%OptimizeFunctionOnNextCall(g);"
3568 "g(false);");
3569
3570 // Finish garbage collection cycle.
3571 heap->CollectAllGarbage(Heap::kNoGCFlags);
3572 CHECK(shared1->code()->gc_metadata() == NULL);
3573}
3574
3575
3576// Helper function that simulates a fill new-space in the heap.
3577static inline void AllocateAllButNBytes(v8::internal::NewSpace* space,
3578 int extra_bytes) {
3579 int space_remaining = static_cast<int>(
3580 *space->allocation_limit_address() - *space->allocation_top_address());
3581 CHECK(space_remaining >= extra_bytes);
3582 int new_linear_size = space_remaining - extra_bytes;
3583 v8::internal::AllocationResult allocation =
3584 space->AllocateRaw(new_linear_size);
3585 v8::internal::FreeListNode* node =
3586 v8::internal::FreeListNode::cast(allocation.ToObjectChecked());
3587 node->set_size(space->heap(), new_linear_size);
3588}
3589
3590
3591TEST(Regress169928) {
3592 i::FLAG_allow_natives_syntax = true;
3593 i::FLAG_crankshaft = false;
3594 CcTest::InitializeVM();
3595 Isolate* isolate = CcTest::i_isolate();
3596 Factory* factory = isolate->factory();
3597 v8::HandleScope scope(CcTest::isolate());
3598
3599 // Some flags turn Scavenge collections into Mark-sweep collections
3600 // and hence are incompatible with this test case.
3601 if (FLAG_gc_global || FLAG_stress_compaction) return;
3602
3603 // Prepare the environment
3604 CompileRun("function fastliteralcase(literal, value) {"
3605 " literal[0] = value;"
3606 " return literal;"
3607 "}"
3608 "function get_standard_literal() {"
3609 " var literal = [1, 2, 3];"
3610 " return literal;"
3611 "}"
3612 "obj = fastliteralcase(get_standard_literal(), 1);"
3613 "obj = fastliteralcase(get_standard_literal(), 1.5);"
3614 "obj = fastliteralcase(get_standard_literal(), 2);");
3615
3616 // prepare the heap
3617 v8::Local<v8::String> mote_code_string =
3618 v8_str("fastliteralcase(mote, 2.5);");
3619
3620 v8::Local<v8::String> array_name = v8_str("mote");
3621 CcTest::global()->Set(array_name, v8::Int32::New(CcTest::isolate(), 0));
3622
3623 // First make sure we flip spaces
3624 CcTest::heap()->CollectGarbage(NEW_SPACE);
3625
3626 // Allocate the object.
3627 Handle<FixedArray> array_data = factory->NewFixedArray(2, NOT_TENURED);
3628 array_data->set(0, Smi::FromInt(1));
3629 array_data->set(1, Smi::FromInt(2));
3630
3631 AllocateAllButNBytes(CcTest::heap()->new_space(),
3632 JSArray::kSize + AllocationMemento::kSize +
3633 kPointerSize);
3634
3635 Handle<JSArray> array = factory->NewJSArrayWithElements(array_data,
3636 FAST_SMI_ELEMENTS,
3637 NOT_TENURED);
3638
3639 CHECK_EQ(Smi::FromInt(2), array->length());
3640 CHECK(array->HasFastSmiOrObjectElements());
3641
3642 // We need filler the size of AllocationMemento object, plus an extra
3643 // fill pointer value.
3644 HeapObject* obj = NULL;
3645 AllocationResult allocation = CcTest::heap()->new_space()->AllocateRaw(
3646 AllocationMemento::kSize + kPointerSize);
3647 CHECK(allocation.To(&obj));
3648 Address addr_obj = obj->address();
3649 CcTest::heap()->CreateFillerObjectAt(
3650 addr_obj, AllocationMemento::kSize + kPointerSize);
3651
3652 // Give the array a name, making sure not to allocate strings.
3653 v8::Handle<v8::Object> array_obj = v8::Utils::ToLocal(array);
3654 CcTest::global()->Set(array_name, array_obj);
3655
3656 // This should crash with a protection violation if we are running a build
3657 // with the bug.
3658 AlwaysAllocateScope aa_scope(isolate);
3659 v8::Script::Compile(mote_code_string)->Run();
3660}
3661
3662
3663TEST(Regress168801) {
3664 if (i::FLAG_never_compact) return;
3665 i::FLAG_always_compact = true;
3666 i::FLAG_cache_optimized_code = false;
3667 i::FLAG_allow_natives_syntax = true;
3668 i::FLAG_flush_code_incrementally = true;
3669 CcTest::InitializeVM();
3670 Isolate* isolate = CcTest::i_isolate();
3671 Heap* heap = isolate->heap();
3672 HandleScope scope(isolate);
3673
3674 // Perform one initial GC to enable code flushing.
3675 heap->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask);
3676
3677 // Ensure the code ends up on an evacuation candidate.
3678 SimulateFullSpace(heap->code_space());
3679
3680 // Prepare an unoptimized function that is eligible for code flushing.
3681 Handle<JSFunction> function;
3682 {
3683 HandleScope inner_scope(isolate);
3684 CompileRun("function mkClosure() {"
3685 " return function(x) { return x + 1; };"
3686 "}"
3687 "var f = mkClosure();"
3688 "f(1); f(2);");
3689
3690 Handle<JSFunction> f =
3691 v8::Utils::OpenHandle(
3692 *v8::Handle<v8::Function>::Cast(
3693 CcTest::global()->Get(v8_str("f"))));
3694 CHECK(f->is_compiled());
3695 const int kAgingThreshold = 6;
3696 for (int i = 0; i < kAgingThreshold; i++) {
3697 f->shared()->code()->MakeOlder(static_cast<MarkingParity>(i % 2));
3698 }
3699
3700 function = inner_scope.CloseAndEscape(handle(*f, isolate));
3701 }
3702
3703 // Simulate incremental marking so that unoptimized function is enqueued as a
3704 // candidate for code flushing. The shared function info however will not be
3705 // explicitly enqueued.
3706 SimulateIncrementalMarking(heap);
3707
3708 // Now optimize the function so that it is taken off the candidate list.
3709 {
3710 HandleScope inner_scope(isolate);
3711 CompileRun("%OptimizeFunctionOnNextCall(f); f(3);");
3712 }
3713
3714 // This cycle will bust the heap and subsequent cycles will go ballistic.
3715 heap->CollectAllGarbage(Heap::kNoGCFlags);
3716 heap->CollectAllGarbage(Heap::kNoGCFlags);
3717}
3718
3719
3720TEST(Regress173458) {
3721 if (i::FLAG_never_compact) return;
3722 i::FLAG_always_compact = true;
3723 i::FLAG_cache_optimized_code = false;
3724 i::FLAG_allow_natives_syntax = true;
3725 i::FLAG_flush_code_incrementally = true;
3726 CcTest::InitializeVM();
3727 Isolate* isolate = CcTest::i_isolate();
3728 Heap* heap = isolate->heap();
3729 HandleScope scope(isolate);
3730
3731 // Perform one initial GC to enable code flushing.
3732 heap->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask);
3733
3734 // Ensure the code ends up on an evacuation candidate.
3735 SimulateFullSpace(heap->code_space());
3736
3737 // Prepare an unoptimized function that is eligible for code flushing.
3738 Handle<JSFunction> function;
3739 {
3740 HandleScope inner_scope(isolate);
3741 CompileRun("function mkClosure() {"
3742 " return function(x) { return x + 1; };"
3743 "}"
3744 "var f = mkClosure();"
3745 "f(1); f(2);");
3746
3747 Handle<JSFunction> f =
3748 v8::Utils::OpenHandle(
3749 *v8::Handle<v8::Function>::Cast(
3750 CcTest::global()->Get(v8_str("f"))));
3751 CHECK(f->is_compiled());
3752 const int kAgingThreshold = 6;
3753 for (int i = 0; i < kAgingThreshold; i++) {
3754 f->shared()->code()->MakeOlder(static_cast<MarkingParity>(i % 2));
3755 }
3756
3757 function = inner_scope.CloseAndEscape(handle(*f, isolate));
3758 }
3759
3760 // Simulate incremental marking so that unoptimized function is enqueued as a
3761 // candidate for code flushing. The shared function info however will not be
3762 // explicitly enqueued.
3763 SimulateIncrementalMarking(heap);
3764
3765 // Now enable the debugger which in turn will disable code flushing.
3766 CHECK(isolate->debug()->Load());
3767
3768 // This cycle will bust the heap and subsequent cycles will go ballistic.
3769 heap->CollectAllGarbage(Heap::kNoGCFlags);
3770 heap->CollectAllGarbage(Heap::kNoGCFlags);
3771}
3772
3773
3774class DummyVisitor : public ObjectVisitor {
3775 public:
3776 void VisitPointers(Object** start, Object** end) { }
3777};
3778
3779
3780TEST(DeferredHandles) {
3781 CcTest::InitializeVM();
3782 Isolate* isolate = CcTest::i_isolate();
3783 Heap* heap = isolate->heap();
3784 v8::HandleScope scope(reinterpret_cast<v8::Isolate*>(isolate));
3785 HandleScopeData* data = isolate->handle_scope_data();
3786 Handle<Object> init(heap->empty_string(), isolate);
3787 while (data->next < data->limit) {
3788 Handle<Object> obj(heap->empty_string(), isolate);
3789 }
3790 // An entire block of handles has been filled.
3791 // Next handle would require a new block.
3792 DCHECK(data->next == data->limit);
3793
3794 DeferredHandleScope deferred(isolate);
3795 DummyVisitor visitor;
3796 isolate->handle_scope_implementer()->Iterate(&visitor);
3797 delete deferred.Detach();
3798}
3799
3800
3801TEST(IncrementalMarkingStepMakesBigProgressWithLargeObjects) {
3802 CcTest::InitializeVM();
3803 v8::HandleScope scope(CcTest::isolate());
3804 CompileRun("function f(n) {"
3805 " var a = new Array(n);"
3806 " for (var i = 0; i < n; i += 100) a[i] = i;"
3807 "};"
3808 "f(10 * 1024 * 1024);");
3809 IncrementalMarking* marking = CcTest::heap()->incremental_marking();
3810 if (marking->IsStopped()) marking->Start();
3811 // This big step should be sufficient to mark the whole array.
3812 marking->Step(100 * MB, IncrementalMarking::NO_GC_VIA_STACK_GUARD);
3813 DCHECK(marking->IsComplete());
3814}
3815
3816
3817TEST(DisableInlineAllocation) {
3818 i::FLAG_allow_natives_syntax = true;
3819 CcTest::InitializeVM();
3820 v8::HandleScope scope(CcTest::isolate());
3821 CompileRun("function test() {"
3822 " var x = [];"
3823 " for (var i = 0; i < 10; i++) {"
3824 " x[i] = [ {}, [1,2,3], [1,x,3] ];"
3825 " }"
3826 "}"
3827 "function run() {"
3828 " %OptimizeFunctionOnNextCall(test);"
3829 " test();"
3830 " %DeoptimizeFunction(test);"
3831 "}");
3832
3833 // Warm-up with inline allocation enabled.
3834 CompileRun("test(); test(); run();");
3835
3836 // Run test with inline allocation disabled.
3837 CcTest::heap()->DisableInlineAllocation();
3838 CompileRun("run()");
3839
3840 // Run test with inline allocation re-enabled.
3841 CcTest::heap()->EnableInlineAllocation();
3842 CompileRun("run()");
3843}
3844
3845
3846static int AllocationSitesCount(Heap* heap) {
3847 int count = 0;
3848 for (Object* site = heap->allocation_sites_list();
3849 !(site->IsUndefined());
3850 site = AllocationSite::cast(site)->weak_next()) {
3851 count++;
3852 }
3853 return count;
3854}
3855
3856
3857TEST(EnsureAllocationSiteDependentCodesProcessed) {
3858 if (i::FLAG_always_opt || !i::FLAG_crankshaft) return;
3859 i::FLAG_allow_natives_syntax = true;
3860 CcTest::InitializeVM();
3861 Isolate* isolate = CcTest::i_isolate();
3862 v8::internal::Heap* heap = CcTest::heap();
3863 GlobalHandles* global_handles = isolate->global_handles();
3864
3865 if (!isolate->use_crankshaft()) return;
3866
3867 // The allocation site at the head of the list is ours.
3868 Handle<AllocationSite> site;
3869 {
3870 LocalContext context;
3871 v8::HandleScope scope(context->GetIsolate());
3872
3873 int count = AllocationSitesCount(heap);
3874 CompileRun("var bar = function() { return (new Array()); };"
3875 "var a = bar();"
3876 "bar();"
3877 "bar();");
3878
3879 // One allocation site should have been created.
3880 int new_count = AllocationSitesCount(heap);
3881 CHECK_EQ(new_count, (count + 1));
3882 site = Handle<AllocationSite>::cast(
3883 global_handles->Create(
3884 AllocationSite::cast(heap->allocation_sites_list())));
3885
3886 CompileRun("%OptimizeFunctionOnNextCall(bar); bar();");
3887
3888 DependentCode::GroupStartIndexes starts(site->dependent_code());
3889 CHECK_GE(starts.number_of_entries(), 1);
3890 int index = starts.at(DependentCode::kAllocationSiteTransitionChangedGroup);
3891 CHECK(site->dependent_code()->is_code_at(index));
3892 Code* function_bar = site->dependent_code()->code_at(index);
3893 Handle<JSFunction> bar_handle =
3894 v8::Utils::OpenHandle(
3895 *v8::Handle<v8::Function>::Cast(
3896 CcTest::global()->Get(v8_str("bar"))));
3897 CHECK_EQ(bar_handle->code(), function_bar);
3898 }
3899
3900 // Now make sure that a gc should get rid of the function, even though we
3901 // still have the allocation site alive.
3902 for (int i = 0; i < 4; i++) {
3903 heap->CollectAllGarbage(Heap::kNoGCFlags);
3904 }
3905
3906 // The site still exists because of our global handle, but the code is no
3907 // longer referred to by dependent_code().
3908 DependentCode::GroupStartIndexes starts(site->dependent_code());
3909 int index = starts.at(DependentCode::kAllocationSiteTransitionChangedGroup);
3910 CHECK(!(site->dependent_code()->is_code_at(index)));
3911}
3912
3913
3914TEST(CellsInOptimizedCodeAreWeak) {
3915 if (i::FLAG_always_opt || !i::FLAG_crankshaft) return;
3916 i::FLAG_weak_embedded_objects_in_optimized_code = true;
3917 i::FLAG_allow_natives_syntax = true;
3918 CcTest::InitializeVM();
3919 Isolate* isolate = CcTest::i_isolate();
3920 v8::internal::Heap* heap = CcTest::heap();
3921
3922 if (!isolate->use_crankshaft()) return;
3923 HandleScope outer_scope(heap->isolate());
3924 Handle<Code> code;
3925 {
3926 LocalContext context;
3927 HandleScope scope(heap->isolate());
3928
3929 CompileRun("bar = (function() {"
3930 " function bar() {"
3931 " return foo(1);"
3932 " };"
3933 " var foo = function(x) { with (x) { return 1 + x; } };"
3934 " bar(foo);"
3935 " bar(foo);"
3936 " bar(foo);"
3937 " %OptimizeFunctionOnNextCall(bar);"
3938 " bar(foo);"
3939 " return bar;})();");
3940
3941 Handle<JSFunction> bar =
3942 v8::Utils::OpenHandle(
3943 *v8::Handle<v8::Function>::Cast(
3944 CcTest::global()->Get(v8_str("bar"))));
3945 code = scope.CloseAndEscape(Handle<Code>(bar->code()));
3946 }
3947
3948 // Now make sure that a gc should get rid of the function
3949 for (int i = 0; i < 4; i++) {
3950 heap->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask);
3951 }
3952
3953 DCHECK(code->marked_for_deoptimization());
3954}
3955
3956
3957TEST(ObjectsInOptimizedCodeAreWeak) {
3958 if (i::FLAG_always_opt || !i::FLAG_crankshaft) return;
3959 i::FLAG_weak_embedded_objects_in_optimized_code = true;
3960 i::FLAG_allow_natives_syntax = true;
3961 CcTest::InitializeVM();
3962 Isolate* isolate = CcTest::i_isolate();
3963 v8::internal::Heap* heap = CcTest::heap();
3964
3965 if (!isolate->use_crankshaft()) return;
3966 HandleScope outer_scope(heap->isolate());
3967 Handle<Code> code;
3968 {
3969 LocalContext context;
3970 HandleScope scope(heap->isolate());
3971
3972 CompileRun("function bar() {"
3973 " return foo(1);"
3974 "};"
3975 "function foo(x) { with (x) { return 1 + x; } };"
3976 "bar();"
3977 "bar();"
3978 "bar();"
3979 "%OptimizeFunctionOnNextCall(bar);"
3980 "bar();");
3981
3982 Handle<JSFunction> bar =
3983 v8::Utils::OpenHandle(
3984 *v8::Handle<v8::Function>::Cast(
3985 CcTest::global()->Get(v8_str("bar"))));
3986 code = scope.CloseAndEscape(Handle<Code>(bar->code()));
3987 }
3988
3989 // Now make sure that a gc should get rid of the function
3990 for (int i = 0; i < 4; i++) {
3991 heap->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask);
3992 }
3993
3994 DCHECK(code->marked_for_deoptimization());
3995}
3996
3997
3998TEST(NoWeakHashTableLeakWithIncrementalMarking) {
3999 if (i::FLAG_always_opt || !i::FLAG_crankshaft) return;
4000 if (!i::FLAG_incremental_marking) return;
4001 i::FLAG_weak_embedded_objects_in_optimized_code = true;
4002 i::FLAG_allow_natives_syntax = true;
4003 i::FLAG_compilation_cache = false;
4004 CcTest::InitializeVM();
4005 Isolate* isolate = CcTest::i_isolate();
4006 v8::internal::Heap* heap = CcTest::heap();
4007
4008 if (!isolate->use_crankshaft()) return;
4009 HandleScope outer_scope(heap->isolate());
4010 for (int i = 0; i < 3; i++) {
4011 SimulateIncrementalMarking(heap);
4012 {
4013 LocalContext context;
4014 HandleScope scope(heap->isolate());
4015 EmbeddedVector<char, 256> source;
4016 SNPrintF(source,
4017 "function bar%d() {"
4018 " return foo%d(1);"
4019 "};"
4020 "function foo%d(x) { with (x) { return 1 + x; } };"
4021 "bar%d();"
4022 "bar%d();"
4023 "bar%d();"
4024 "%%OptimizeFunctionOnNextCall(bar%d);"
4025 "bar%d();", i, i, i, i, i, i, i, i);
4026 CompileRun(source.start());
4027 }
4028 heap->CollectAllGarbage(i::Heap::kNoGCFlags);
4029 }
4030 int elements = 0;
4031 if (heap->weak_object_to_code_table()->IsHashTable()) {
4032 WeakHashTable* t = WeakHashTable::cast(heap->weak_object_to_code_table());
4033 elements = t->NumberOfElements();
4034 }
4035 CHECK_EQ(0, elements);
4036}
4037
4038
4039static Handle<JSFunction> OptimizeDummyFunction(const char* name) {
4040 EmbeddedVector<char, 256> source;
4041 SNPrintF(source,
4042 "function %s() { return 0; }"
4043 "%s(); %s();"
4044 "%%OptimizeFunctionOnNextCall(%s);"
4045 "%s();", name, name, name, name, name);
4046 CompileRun(source.start());
4047 Handle<JSFunction> fun =
4048 v8::Utils::OpenHandle(
4049 *v8::Handle<v8::Function>::Cast(
4050 CcTest::global()->Get(v8_str(name))));
4051 return fun;
4052}
4053
4054
4055static int GetCodeChainLength(Code* code) {
4056 int result = 0;
4057 while (code->next_code_link()->IsCode()) {
4058 result++;
4059 code = Code::cast(code->next_code_link());
4060 }
4061 return result;
4062}
4063
4064
4065TEST(NextCodeLinkIsWeak) {
4066 i::FLAG_allow_natives_syntax = true;
4067 i::FLAG_turbo_deoptimization = true;
4068 CcTest::InitializeVM();
4069 Isolate* isolate = CcTest::i_isolate();
4070 v8::internal::Heap* heap = CcTest::heap();
4071
4072 if (!isolate->use_crankshaft()) return;
4073 HandleScope outer_scope(heap->isolate());
4074 Handle<Code> code;
4075 heap->CollectAllAvailableGarbage();
4076 int code_chain_length_before, code_chain_length_after;
4077 {
4078 HandleScope scope(heap->isolate());
4079 Handle<JSFunction> mortal = OptimizeDummyFunction("mortal");
4080 Handle<JSFunction> immortal = OptimizeDummyFunction("immortal");
4081 CHECK_EQ(immortal->code()->next_code_link(), mortal->code());
4082 code_chain_length_before = GetCodeChainLength(immortal->code());
4083 // Keep the immortal code and let the mortal code die.
4084 code = scope.CloseAndEscape(Handle<Code>(immortal->code()));
4085 CompileRun("mortal = null; immortal = null;");
4086 }
4087 heap->CollectAllAvailableGarbage();
4088 // Now mortal code should be dead.
4089 code_chain_length_after = GetCodeChainLength(*code);
4090 CHECK_EQ(code_chain_length_before - 1, code_chain_length_after);
4091}
4092
4093
4094static Handle<Code> DummyOptimizedCode(Isolate* isolate) {
4095 i::byte buffer[i::Assembler::kMinimalBufferSize];
4096 MacroAssembler masm(isolate, buffer, sizeof(buffer));
4097 CodeDesc desc;
4098 masm.Push(isolate->factory()->undefined_value());
4099 masm.Drop(1);
4100 masm.GetCode(&desc);
4101 Handle<Object> undefined(isolate->heap()->undefined_value(), isolate);
4102 Handle<Code> code = isolate->factory()->NewCode(
4103 desc, Code::ComputeFlags(Code::OPTIMIZED_FUNCTION), undefined);
4104 CHECK(code->IsCode());
4105 return code;
4106}
4107
4108
4109TEST(NextCodeLinkIsWeak2) {
4110 i::FLAG_allow_natives_syntax = true;
4111 CcTest::InitializeVM();
4112 Isolate* isolate = CcTest::i_isolate();
4113 v8::internal::Heap* heap = CcTest::heap();
4114
4115 if (!isolate->use_crankshaft()) return;
4116 HandleScope outer_scope(heap->isolate());
4117 heap->CollectAllAvailableGarbage();
4118 Handle<Context> context(Context::cast(heap->native_contexts_list()), isolate);
4119 Handle<Code> new_head;
4120 Handle<Object> old_head(context->get(Context::OPTIMIZED_CODE_LIST), isolate);
4121 {
4122 HandleScope scope(heap->isolate());
4123 Handle<Code> immortal = DummyOptimizedCode(isolate);
4124 Handle<Code> mortal = DummyOptimizedCode(isolate);
4125 mortal->set_next_code_link(*old_head);
4126 immortal->set_next_code_link(*mortal);
4127 context->set(Context::OPTIMIZED_CODE_LIST, *immortal);
4128 new_head = scope.CloseAndEscape(immortal);
4129 }
4130 heap->CollectAllAvailableGarbage();
4131 // Now mortal code should be dead.
4132 CHECK_EQ(*old_head, new_head->next_code_link());
4133}
4134
4135
4136static bool weak_ic_cleared = false;
4137
4138static void ClearWeakIC(const v8::WeakCallbackData<v8::Object, void>& data) {
4139 printf("clear weak is called\n");
4140 weak_ic_cleared = true;
4141 v8::Persistent<v8::Value>* p =
4142 reinterpret_cast<v8::Persistent<v8::Value>*>(data.GetParameter());
4143 CHECK(p->IsNearDeath());
4144 p->Reset();
4145}
4146
4147
4148// Checks that the value returned by execution of the source is weak.
4149void CheckWeakness(const char* source) {
4150 i::FLAG_stress_compaction = false;
4151 CcTest::InitializeVM();
4152 v8::Isolate* isolate = CcTest::isolate();
4153 v8::HandleScope scope(isolate);
4154 v8::Persistent<v8::Object> garbage;
4155 {
4156 v8::HandleScope scope(isolate);
4157 garbage.Reset(isolate, CompileRun(source)->ToObject());
4158 }
4159 weak_ic_cleared = false;
4160 garbage.SetWeak(static_cast<void*>(&garbage), &ClearWeakIC);
4161 Heap* heap = CcTest::i_isolate()->heap();
4162 heap->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask);
4163 CHECK(weak_ic_cleared);
4164}
4165
4166
4167// Each of the following "weak IC" tests creates an IC that embeds a map with
4168// the prototype pointing to _proto_ and checks that the _proto_ dies on GC.
4169TEST(WeakMapInMonomorphicLoadIC) {
4170 CheckWeakness("function loadIC(obj) {"
4171 " return obj.name;"
4172 "}"
4173 " (function() {"
4174 " var proto = {'name' : 'weak'};"
4175 " var obj = Object.create(proto);"
4176 " loadIC(obj);"
4177 " loadIC(obj);"
4178 " loadIC(obj);"
4179 " return proto;"
4180 " })();");
4181}
4182
4183
4184TEST(WeakMapInMonomorphicKeyedLoadIC) {
4185 CheckWeakness("function keyedLoadIC(obj, field) {"
4186 " return obj[field];"
4187 "}"
4188 " (function() {"
4189 " var proto = {'name' : 'weak'};"
4190 " var obj = Object.create(proto);"
4191 " keyedLoadIC(obj, 'name');"
4192 " keyedLoadIC(obj, 'name');"
4193 " keyedLoadIC(obj, 'name');"
4194 " return proto;"
4195 " })();");
4196}
4197
4198
4199TEST(WeakMapInMonomorphicStoreIC) {
4200 CheckWeakness("function storeIC(obj, value) {"
4201 " obj.name = value;"
4202 "}"
4203 " (function() {"
4204 " var proto = {'name' : 'weak'};"
4205 " var obj = Object.create(proto);"
4206 " storeIC(obj, 'x');"
4207 " storeIC(obj, 'x');"
4208 " storeIC(obj, 'x');"
4209 " return proto;"
4210 " })();");
4211}
4212
4213
4214TEST(WeakMapInMonomorphicKeyedStoreIC) {
4215 CheckWeakness("function keyedStoreIC(obj, field, value) {"
4216 " obj[field] = value;"
4217 "}"
4218 " (function() {"
4219 " var proto = {'name' : 'weak'};"
4220 " var obj = Object.create(proto);"
4221 " keyedStoreIC(obj, 'x');"
4222 " keyedStoreIC(obj, 'x');"
4223 " keyedStoreIC(obj, 'x');"
4224 " return proto;"
4225 " })();");
4226}
4227
4228
4229TEST(WeakMapInMonomorphicCompareNilIC) {
4230 CheckWeakness("function compareNilIC(obj) {"
4231 " return obj == null;"
4232 "}"
4233 " (function() {"
4234 " var proto = {'name' : 'weak'};"
4235 " var obj = Object.create(proto);"
4236 " compareNilIC(obj);"
4237 " compareNilIC(obj);"
4238 " compareNilIC(obj);"
4239 " return proto;"
4240 " })();");
4241}
4242
4243
4244#ifdef DEBUG
4245TEST(AddInstructionChangesNewSpacePromotion) {
4246 i::FLAG_allow_natives_syntax = true;
4247 i::FLAG_expose_gc = true;
4248 i::FLAG_stress_compaction = true;
4249 i::FLAG_gc_interval = 1000;
4250 CcTest::InitializeVM();
4251 if (!i::FLAG_allocation_site_pretenuring) return;
4252 v8::HandleScope scope(CcTest::isolate());
4253 Isolate* isolate = CcTest::i_isolate();
4254 Heap* heap = isolate->heap();
4255
4256 CompileRun(
4257 "function add(a, b) {"
4258 " return a + b;"
4259 "}"
4260 "add(1, 2);"
4261 "add(\"a\", \"b\");"
4262 "var oldSpaceObject;"
4263 "gc();"
4264 "function crash(x) {"
4265 " var object = {a: null, b: null};"
4266 " var result = add(1.5, x | 0);"
4267 " object.a = result;"
4268 " oldSpaceObject = object;"
4269 " return object;"
4270 "}"
4271 "crash(1);"
4272 "crash(1);"
4273 "%OptimizeFunctionOnNextCall(crash);"
4274 "crash(1);");
4275
4276 v8::Handle<v8::Object> global = CcTest::global();
4277 v8::Handle<v8::Function> g =
4278 v8::Handle<v8::Function>::Cast(global->Get(v8_str("crash")));
4279 v8::Handle<v8::Value> args1[] = { v8_num(1) };
4280 heap->DisableInlineAllocation();
4281 heap->set_allocation_timeout(1);
4282 g->Call(global, 1, args1);
4283 heap->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask);
4284}
4285
4286
4287void OnFatalErrorExpectOOM(const char* location, const char* message) {
4288 // Exit with 0 if the location matches our expectation.
4289 exit(strcmp(location, "CALL_AND_RETRY_LAST"));
4290}
4291
4292
4293TEST(CEntryStubOOM) {
4294 i::FLAG_allow_natives_syntax = true;
4295 CcTest::InitializeVM();
4296 v8::HandleScope scope(CcTest::isolate());
4297 v8::V8::SetFatalErrorHandler(OnFatalErrorExpectOOM);
4298
4299 v8::Handle<v8::Value> result = CompileRun(
4300 "%SetFlags('--gc-interval=1');"
4301 "var a = [];"
4302 "a.__proto__ = [];"
4303 "a.unshift(1)");
4304
4305 CHECK(result->IsNumber());
4306}
4307
4308#endif // DEBUG
4309
4310
4311static void InterruptCallback357137(v8::Isolate* isolate, void* data) { }
4312
4313
4314static void RequestInterrupt(const v8::FunctionCallbackInfo<v8::Value>& args) {
4315 CcTest::isolate()->RequestInterrupt(&InterruptCallback357137, NULL);
4316}
4317
4318
4319TEST(Regress357137) {
4320 CcTest::InitializeVM();
4321 v8::Isolate* isolate = CcTest::isolate();
4322 v8::HandleScope hscope(isolate);
4323 v8::Handle<v8::ObjectTemplate> global =v8::ObjectTemplate::New(isolate);
4324 global->Set(v8::String::NewFromUtf8(isolate, "interrupt"),
4325 v8::FunctionTemplate::New(isolate, RequestInterrupt));
4326 v8::Local<v8::Context> context = v8::Context::New(isolate, NULL, global);
4327 DCHECK(!context.IsEmpty());
4328 v8::Context::Scope cscope(context);
4329
4330 v8::Local<v8::Value> result = CompileRun(
4331 "var locals = '';"
4332 "for (var i = 0; i < 512; i++) locals += 'var v' + i + '= 42;';"
4333 "eval('function f() {' + locals + 'return function() { return v0; }; }');"
4334 "interrupt();" // This triggers a fake stack overflow in f.
4335 "f()()");
4336 CHECK_EQ(42.0, result->ToNumber()->Value());
4337}
4338
4339
4340TEST(ArrayShiftSweeping) {
4341 i::FLAG_expose_gc = true;
4342 CcTest::InitializeVM();
4343 v8::HandleScope scope(CcTest::isolate());
4344 Isolate* isolate = CcTest::i_isolate();
4345 Heap* heap = isolate->heap();
4346
4347 v8::Local<v8::Value> result = CompileRun(
4348 "var array = new Array(40000);"
4349 "var tmp = new Array(100000);"
4350 "array[0] = 10;"
4351 "gc();"
4352 "gc();"
4353 "array.shift();"
4354 "array;");
4355
4356 Handle<JSObject> o =
4357 v8::Utils::OpenHandle(*v8::Handle<v8::Object>::Cast(result));
4358 CHECK(heap->InOldPointerSpace(o->elements()));
4359 CHECK(heap->InOldPointerSpace(*o));
4360 Page* page = Page::FromAddress(o->elements()->address());
4361 CHECK(page->parallel_sweeping() <= MemoryChunk::SWEEPING_FINALIZE ||
4362 Marking::IsBlack(Marking::MarkBitFrom(o->elements())));
4363}
4364
4365
4366UNINITIALIZED_TEST(PromotionQueue) {
4367 i::FLAG_expose_gc = true;
4368 i::FLAG_max_semi_space_size = 2;
4369 v8::Isolate* isolate = v8::Isolate::New();
4370 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
4371 {
4372 v8::Isolate::Scope isolate_scope(isolate);
4373 v8::HandleScope handle_scope(isolate);
4374 v8::Context::New(isolate)->Enter();
4375 Heap* heap = i_isolate->heap();
4376 NewSpace* new_space = heap->new_space();
4377
4378 // In this test we will try to overwrite the promotion queue which is at the
4379 // end of to-space. To actually make that possible, we need at least two
4380 // semi-space pages and take advantage of fragmentation.
4381 // (1) Grow semi-space to two pages.
4382 // (2) Create a few small long living objects and call the scavenger to
4383 // move them to the other semi-space.
4384 // (3) Create a huge object, i.e., remainder of first semi-space page and
4385 // create another huge object which should be of maximum allocatable memory
4386 // size of the second semi-space page.
4387 // (4) Call the scavenger again.
4388 // What will happen is: the scavenger will promote the objects created in
4389 // (2) and will create promotion queue entries at the end of the second
4390 // semi-space page during the next scavenge when it promotes the objects to
4391 // the old generation. The first allocation of (3) will fill up the first
4392 // semi-space page. The second allocation in (3) will not fit into the
4393 // first semi-space page, but it will overwrite the promotion queue which
4394 // are in the second semi-space page. If the right guards are in place, the
4395 // promotion queue will be evacuated in that case.
4396
4397 // Grow the semi-space to two pages to make semi-space copy overwrite the
4398 // promotion queue, which will be at the end of the second page.
4399 intptr_t old_capacity = new_space->TotalCapacity();
4400
4401 // If we are in a low memory config, we can't grow to two pages and we can't
4402 // run this test. This also means the issue we are testing cannot arise, as
4403 // there is no fragmentation.
4404 if (new_space->IsAtMaximumCapacity()) return;
4405
4406 new_space->Grow();
4407 CHECK(new_space->IsAtMaximumCapacity());
4408 CHECK(2 * old_capacity == new_space->TotalCapacity());
4409
4410 // Call the scavenger two times to get an empty new space
4411 heap->CollectGarbage(NEW_SPACE);
4412 heap->CollectGarbage(NEW_SPACE);
4413
4414 // First create a few objects which will survive a scavenge, and will get
4415 // promoted to the old generation later on. These objects will create
4416 // promotion queue entries at the end of the second semi-space page.
4417 const int number_handles = 12;
4418 Handle<FixedArray> handles[number_handles];
4419 for (int i = 0; i < number_handles; i++) {
4420 handles[i] = i_isolate->factory()->NewFixedArray(1, NOT_TENURED);
4421 }
4422 heap->CollectGarbage(NEW_SPACE);
4423
4424 // Create the first huge object which will exactly fit the first semi-space
4425 // page.
4426 int new_linear_size =
4427 static_cast<int>(*heap->new_space()->allocation_limit_address() -
4428 *heap->new_space()->allocation_top_address());
4429 int length = new_linear_size / kPointerSize - FixedArray::kHeaderSize;
4430 Handle<FixedArray> first =
4431 i_isolate->factory()->NewFixedArray(length, NOT_TENURED);
4432 CHECK(heap->InNewSpace(*first));
4433
4434 // Create the second huge object of maximum allocatable second semi-space
4435 // page size.
4436 new_linear_size =
4437 static_cast<int>(*heap->new_space()->allocation_limit_address() -
4438 *heap->new_space()->allocation_top_address());
4439 length = Page::kMaxRegularHeapObjectSize / kPointerSize -
4440 FixedArray::kHeaderSize;
4441 Handle<FixedArray> second =
4442 i_isolate->factory()->NewFixedArray(length, NOT_TENURED);
4443 CHECK(heap->InNewSpace(*second));
4444
4445 // This scavenge will corrupt memory if the promotion queue is not
4446 // evacuated.
4447 heap->CollectGarbage(NEW_SPACE);
4448 }
4449 isolate->Dispose();
4450}
4451
4452
4453TEST(Regress388880) {
4454 i::FLAG_expose_gc = true;
4455 CcTest::InitializeVM();
4456 v8::HandleScope scope(CcTest::isolate());
4457 Isolate* isolate = CcTest::i_isolate();
4458 Factory* factory = isolate->factory();
4459 Heap* heap = isolate->heap();
4460
4461 Handle<Map> map1 = Map::Create(isolate, 1);
4462 Handle<Map> map2 =
4463 Map::CopyWithField(map1, factory->NewStringFromStaticChars("foo"),
4464 HeapType::Any(isolate), NONE, Representation::Tagged(),
4465 OMIT_TRANSITION).ToHandleChecked();
4466
4467 int desired_offset = Page::kPageSize - map1->instance_size();
4468
4469 // Allocate fixed array in old pointer space so, that object allocated
4470 // afterwards would end at the end of the page.
4471 {
4472 SimulateFullSpace(heap->old_pointer_space());
4473 int padding_size = desired_offset - Page::kObjectStartOffset;
4474 int padding_array_length =
4475 (padding_size - FixedArray::kHeaderSize) / kPointerSize;
4476
4477 Handle<FixedArray> temp2 =
4478 factory->NewFixedArray(padding_array_length, TENURED);
4479 Page* page = Page::FromAddress(temp2->address());
4480 CHECK_EQ(Page::kObjectStartOffset, page->Offset(temp2->address()));
4481 }
4482
4483 Handle<JSObject> o = factory->NewJSObjectFromMap(map1, TENURED, false);
4484 o->set_properties(*factory->empty_fixed_array());
4485
4486 // Ensure that the object allocated where we need it.
4487 Page* page = Page::FromAddress(o->address());
4488 CHECK_EQ(desired_offset, page->Offset(o->address()));
4489
4490 // Now we have an object right at the end of the page.
4491
4492 // Enable incremental marking to trigger actions in Heap::AdjustLiveBytes()
4493 // that would cause crash.
4494 IncrementalMarking* marking = CcTest::heap()->incremental_marking();
4495 marking->Abort();
4496 marking->Start();
4497 CHECK(marking->IsMarking());
4498
4499 // Now everything is set up for crashing in JSObject::MigrateFastToFast()
4500 // when it calls heap->AdjustLiveBytes(...).
4501 JSObject::MigrateToMap(o, map2);
4502}
4503
4504
4505#ifdef DEBUG
4506TEST(PathTracer) {
4507 CcTest::InitializeVM();
4508 v8::HandleScope scope(CcTest::isolate());
4509
4510 v8::Local<v8::Value> result = CompileRun("'abc'");
4511 Handle<Object> o = v8::Utils::OpenHandle(*result);
4512 CcTest::i_isolate()->heap()->TracePathToObject(*o);
4513}
4514#endif // DEBUG