1// Copyright 2012 the V8 project authors. All rights reserved.
2
3#include <stdlib.h>
4
5#include "v8.h"
6
7#include "execution.h"
8#include "factory.h"
9#include "macro-assembler.h"
10#include "global-handles.h"
11#include "cctest.h"
12
13using namespace v8::internal;
14
15static v8::Persistent<v8::Context> env;
16
17static void InitializeVM() {
18  if (env.IsEmpty()) env = v8::Context::New();
19  v8::HandleScope scope;
20  env->Enter();
21}
22
23
24static void CheckMap(Map* map, int type, int instance_size) {
25  CHECK(map->IsHeapObject());
26#ifdef DEBUG
27  CHECK(HEAP->Contains(map));
28#endif
29  CHECK_EQ(HEAP->meta_map(), map->map());
30  CHECK_EQ(type, map->instance_type());
31  CHECK_EQ(instance_size, map->instance_size());
32}
33
34
35TEST(HeapMaps) {
36  InitializeVM();
37  CheckMap(HEAP->meta_map(), MAP_TYPE, Map::kSize);
38  CheckMap(HEAP->heap_number_map(), HEAP_NUMBER_TYPE, HeapNumber::kSize);
39  CheckMap(HEAP->fixed_array_map(), FIXED_ARRAY_TYPE, kVariableSizeSentinel);
40  CheckMap(HEAP->string_map(), STRING_TYPE, kVariableSizeSentinel);
41}
42
43
44static void CheckOddball(Object* obj, const char* string) {
45  CHECK(obj->IsOddball());
46  bool exc;
47  Object* print_string = *Execution::ToString(Handle<Object>(obj), &exc);
48  CHECK(String::cast(print_string)->IsEqualTo(CStrVector(string)));
49}
50
51
52static void CheckSmi(int value, const char* string) {
53  bool exc;
54  Object* print_string =
55      *Execution::ToString(Handle<Object>(Smi::FromInt(value)), &exc);
56  CHECK(String::cast(print_string)->IsEqualTo(CStrVector(string)));
57}
58
59
60static void CheckNumber(double value, const char* string) {
61  Object* obj = HEAP->NumberFromDouble(value)->ToObjectChecked();
62  CHECK(obj->IsNumber());
63  bool exc;
64  Object* print_string = *Execution::ToString(Handle<Object>(obj), &exc);
65  CHECK(String::cast(print_string)->IsEqualTo(CStrVector(string)));
66}
67
68
69static void CheckFindCodeObject() {
70  // Test FindCodeObject
71#define __ assm.
72
73  Assembler assm(Isolate::Current(), NULL, 0);
74
75  __ nop();  // supported on all architectures
76
77  CodeDesc desc;
78  assm.GetCode(&desc);
79  Object* code = HEAP->CreateCode(
80      desc,
81      Code::ComputeFlags(Code::STUB),
82      Handle<Object>(HEAP->undefined_value()))->ToObjectChecked();
83  CHECK(code->IsCode());
84
85  HeapObject* obj = HeapObject::cast(code);
86  Address obj_addr = obj->address();
87
88  for (int i = 0; i < obj->Size(); i += kPointerSize) {
89    Object* found = HEAP->FindCodeObject(obj_addr + i);
90    CHECK_EQ(code, found);
91  }
92
93  Object* copy = HEAP->CreateCode(
94      desc,
95      Code::ComputeFlags(Code::STUB),
96      Handle<Object>(HEAP->undefined_value()))->ToObjectChecked();
97  CHECK(copy->IsCode());
98  HeapObject* obj_copy = HeapObject::cast(copy);
99  Object* not_right = HEAP->FindCodeObject(obj_copy->address() +
100                                           obj_copy->Size() / 2);
101  CHECK(not_right != code);
102}
103
104
105TEST(HeapObjects) {
106  InitializeVM();
107
108  v8::HandleScope sc;
109  Object* value = HEAP->NumberFromDouble(1.000123)->ToObjectChecked();
110  CHECK(value->IsHeapNumber());
111  CHECK(value->IsNumber());
112  CHECK_EQ(1.000123, value->Number());
113
114  value = HEAP->NumberFromDouble(1.0)->ToObjectChecked();
115  CHECK(value->IsSmi());
116  CHECK(value->IsNumber());
117  CHECK_EQ(1.0, value->Number());
118
119  value = HEAP->NumberFromInt32(1024)->ToObjectChecked();
120  CHECK(value->IsSmi());
121  CHECK(value->IsNumber());
122  CHECK_EQ(1024.0, value->Number());
123
124  value = HEAP->NumberFromInt32(Smi::kMinValue)->ToObjectChecked();
125  CHECK(value->IsSmi());
126  CHECK(value->IsNumber());
127  CHECK_EQ(Smi::kMinValue, Smi::cast(value)->value());
128
129  value = HEAP->NumberFromInt32(Smi::kMaxValue)->ToObjectChecked();
130  CHECK(value->IsSmi());
131  CHECK(value->IsNumber());
132  CHECK_EQ(Smi::kMaxValue, Smi::cast(value)->value());
133
134#ifndef V8_TARGET_ARCH_X64
135  // TODO(lrn): We need a NumberFromIntptr function in order to test this.
136  value = HEAP->NumberFromInt32(Smi::kMinValue - 1)->ToObjectChecked();
137  CHECK(value->IsHeapNumber());
138  CHECK(value->IsNumber());
139  CHECK_EQ(static_cast<double>(Smi::kMinValue - 1), value->Number());
140#endif
141
142  MaybeObject* maybe_value =
143      HEAP->NumberFromUint32(static_cast<uint32_t>(Smi::kMaxValue) + 1);
144  value = maybe_value->ToObjectChecked();
145  CHECK(value->IsHeapNumber());
146  CHECK(value->IsNumber());
147  CHECK_EQ(static_cast<double>(static_cast<uint32_t>(Smi::kMaxValue) + 1),
148           value->Number());
149
150  // nan oddball checks
151  CHECK(HEAP->nan_value()->IsNumber());
152  CHECK(isnan(HEAP->nan_value()->Number()));
153
154  Handle<String> s = FACTORY->NewStringFromAscii(CStrVector("fisk hest "));
155  CHECK(s->IsString());
156  CHECK_EQ(10, s->length());
157
158  String* object_symbol = String::cast(HEAP->Object_symbol());
159  CHECK(
160      Isolate::Current()->context()->global()->HasLocalProperty(object_symbol));
161
162  // Check ToString for oddballs
163  CheckOddball(HEAP->true_value(), "true");
164  CheckOddball(HEAP->false_value(), "false");
165  CheckOddball(HEAP->null_value(), "null");
166  CheckOddball(HEAP->undefined_value(), "undefined");
167
168  // Check ToString for Smis
169  CheckSmi(0, "0");
170  CheckSmi(42, "42");
171  CheckSmi(-42, "-42");
172
173  // Check ToString for Numbers
174  CheckNumber(1.1, "1.1");
175
176  CheckFindCodeObject();
177}
178
179
180TEST(Tagging) {
181  InitializeVM();
182  int request = 24;
183  CHECK_EQ(request, static_cast<int>(OBJECT_POINTER_ALIGN(request)));
184  CHECK(Smi::FromInt(42)->IsSmi());
185  CHECK(Failure::RetryAfterGC(NEW_SPACE)->IsFailure());
186  CHECK_EQ(NEW_SPACE,
187           Failure::RetryAfterGC(NEW_SPACE)->allocation_space());
188  CHECK_EQ(OLD_POINTER_SPACE,
189           Failure::RetryAfterGC(OLD_POINTER_SPACE)->allocation_space());
190  CHECK(Failure::Exception()->IsFailure());
191  CHECK(Smi::FromInt(Smi::kMinValue)->IsSmi());
192  CHECK(Smi::FromInt(Smi::kMaxValue)->IsSmi());
193}
194
195
196TEST(GarbageCollection) {
197  InitializeVM();
198
199  v8::HandleScope sc;
200  // Check GC.
201  HEAP->CollectGarbage(NEW_SPACE);
202
203  Handle<String> name = FACTORY->LookupAsciiSymbol("theFunction");
204  Handle<String> prop_name = FACTORY->LookupAsciiSymbol("theSlot");
205  Handle<String> prop_namex = FACTORY->LookupAsciiSymbol("theSlotx");
206  Handle<String> obj_name = FACTORY->LookupAsciiSymbol("theObject");
207
208  {
209    v8::HandleScope inner_scope;
210    // Allocate a function and keep it in global object's property.
211    Handle<JSFunction> function =
212        FACTORY->NewFunction(name, FACTORY->undefined_value());
213    Handle<Map> initial_map =
214        FACTORY->NewMap(JS_OBJECT_TYPE, JSObject::kHeaderSize);
215    function->set_initial_map(*initial_map);
216    Isolate::Current()->context()->global()->SetProperty(
217        *name, *function, NONE, kNonStrictMode)->ToObjectChecked();
218    // Allocate an object.  Unrooted after leaving the scope.
219    Handle<JSObject> obj = FACTORY->NewJSObject(function);
220    obj->SetProperty(
221        *prop_name, Smi::FromInt(23), NONE, kNonStrictMode)->ToObjectChecked();
222    obj->SetProperty(
223        *prop_namex, Smi::FromInt(24), NONE, kNonStrictMode)->ToObjectChecked();
224
225    CHECK_EQ(Smi::FromInt(23), obj->GetProperty(*prop_name));
226    CHECK_EQ(Smi::FromInt(24), obj->GetProperty(*prop_namex));
227  }
228
229  HEAP->CollectGarbage(NEW_SPACE);
230
231  // Function should be alive.
232  CHECK(Isolate::Current()->context()->global()->HasLocalProperty(*name));
233  // Check function is retained.
234  Object* func_value = Isolate::Current()->context()->global()->
235      GetProperty(*name)->ToObjectChecked();
236  CHECK(func_value->IsJSFunction());
237  Handle<JSFunction> function(JSFunction::cast(func_value));
238
239  {
240    HandleScope inner_scope;
241    // Allocate another object, make it reachable from global.
242    Handle<JSObject> obj = FACTORY->NewJSObject(function);
243    Isolate::Current()->context()->global()->SetProperty(
244        *obj_name, *obj, NONE, kNonStrictMode)->ToObjectChecked();
245    obj->SetProperty(
246        *prop_name, Smi::FromInt(23), NONE, kNonStrictMode)->ToObjectChecked();
247  }
248
249  // After gc, it should survive.
250  HEAP->CollectGarbage(NEW_SPACE);
251
252  CHECK(Isolate::Current()->context()->global()->HasLocalProperty(*obj_name));
253  CHECK(Isolate::Current()->context()->global()->
254        GetProperty(*obj_name)->ToObjectChecked()->IsJSObject());
255  Object* obj = Isolate::Current()->context()->global()->
256      GetProperty(*obj_name)->ToObjectChecked();
257  JSObject* js_obj = JSObject::cast(obj);
258  CHECK_EQ(Smi::FromInt(23), js_obj->GetProperty(*prop_name));
259}
260
261
262static void VerifyStringAllocation(const char* string) {
263  v8::HandleScope scope;
264  Handle<String> s = FACTORY->NewStringFromUtf8(CStrVector(string));
265  CHECK_EQ(StrLength(string), s->length());
266  for (int index = 0; index < s->length(); index++) {
267    CHECK_EQ(static_cast<uint16_t>(string[index]), s->Get(index));
268  }
269}
270
271
272TEST(String) {
273  InitializeVM();
274
275  VerifyStringAllocation("a");
276  VerifyStringAllocation("ab");
277  VerifyStringAllocation("abc");
278  VerifyStringAllocation("abcd");
279  VerifyStringAllocation("fiskerdrengen er paa havet");
280}
281
282
283TEST(LocalHandles) {
284  InitializeVM();
285
286  v8::HandleScope scope;
287  const char* name = "Kasper the spunky";
288  Handle<String> string = FACTORY->NewStringFromAscii(CStrVector(name));
289  CHECK_EQ(StrLength(name), string->length());
290}
291
292
293TEST(GlobalHandles) {
294  InitializeVM();
295  GlobalHandles* global_handles = Isolate::Current()->global_handles();
296
297  Handle<Object> h1;
298  Handle<Object> h2;
299  Handle<Object> h3;
300  Handle<Object> h4;
301
302  {
303    HandleScope scope;
304
305    Handle<Object> i = FACTORY->NewStringFromAscii(CStrVector("fisk"));
306    Handle<Object> u = FACTORY->NewNumber(1.12344);
307
308    h1 = global_handles->Create(*i);
309    h2 = global_handles->Create(*u);
310    h3 = global_handles->Create(*i);
311    h4 = global_handles->Create(*u);
312  }
313
314  // after gc, it should survive
315  HEAP->CollectGarbage(NEW_SPACE);
316
317  CHECK((*h1)->IsString());
318  CHECK((*h2)->IsHeapNumber());
319  CHECK((*h3)->IsString());
320  CHECK((*h4)->IsHeapNumber());
321
322  CHECK_EQ(*h3, *h1);
323  global_handles->Destroy(h1.location());
324  global_handles->Destroy(h3.location());
325
326  CHECK_EQ(*h4, *h2);
327  global_handles->Destroy(h2.location());
328  global_handles->Destroy(h4.location());
329}
330
331
332static bool WeakPointerCleared = false;
333
334static void TestWeakGlobalHandleCallback(v8::Persistent<v8::Value> handle,
335                                         void* id) {
336  if (1234 == reinterpret_cast<intptr_t>(id)) WeakPointerCleared = true;
337  handle.Dispose();
338}
339
340
341TEST(WeakGlobalHandlesScavenge) {
342  InitializeVM();
343  GlobalHandles* global_handles = Isolate::Current()->global_handles();
344
345  WeakPointerCleared = false;
346
347  Handle<Object> h1;
348  Handle<Object> h2;
349
350  {
351    HandleScope scope;
352
353    Handle<Object> i = FACTORY->NewStringFromAscii(CStrVector("fisk"));
354    Handle<Object> u = FACTORY->NewNumber(1.12344);
355
356    h1 = global_handles->Create(*i);
357    h2 = global_handles->Create(*u);
358  }
359
360  global_handles->MakeWeak(h2.location(),
361                           reinterpret_cast<void*>(1234),
362                           &TestWeakGlobalHandleCallback);
363
364  // Scavenge treats weak pointers as normal roots.
365  HEAP->PerformScavenge();
366
367  CHECK((*h1)->IsString());
368  CHECK((*h2)->IsHeapNumber());
369
370  CHECK(!WeakPointerCleared);
371  CHECK(!global_handles->IsNearDeath(h2.location()));
372  CHECK(!global_handles->IsNearDeath(h1.location()));
373
374  global_handles->Destroy(h1.location());
375  global_handles->Destroy(h2.location());
376}
377
378
379TEST(WeakGlobalHandlesMark) {
380  InitializeVM();
381  GlobalHandles* global_handles = Isolate::Current()->global_handles();
382
383  WeakPointerCleared = false;
384
385  Handle<Object> h1;
386  Handle<Object> h2;
387
388  {
389    HandleScope scope;
390
391    Handle<Object> i = FACTORY->NewStringFromAscii(CStrVector("fisk"));
392    Handle<Object> u = FACTORY->NewNumber(1.12344);
393
394    h1 = global_handles->Create(*i);
395    h2 = global_handles->Create(*u);
396  }
397
398  HEAP->CollectGarbage(OLD_POINTER_SPACE);
399  HEAP->CollectGarbage(NEW_SPACE);
400  // Make sure the object is promoted.
401
402  global_handles->MakeWeak(h2.location(),
403                           reinterpret_cast<void*>(1234),
404                           &TestWeakGlobalHandleCallback);
405  CHECK(!GlobalHandles::IsNearDeath(h1.location()));
406  CHECK(!GlobalHandles::IsNearDeath(h2.location()));
407
408  HEAP->CollectGarbage(OLD_POINTER_SPACE);
409
410  CHECK((*h1)->IsString());
411
412  CHECK(WeakPointerCleared);
413  CHECK(!GlobalHandles::IsNearDeath(h1.location()));
414
415  global_handles->Destroy(h1.location());
416}
417
418TEST(DeleteWeakGlobalHandle) {
419  InitializeVM();
420  GlobalHandles* global_handles = Isolate::Current()->global_handles();
421
422  WeakPointerCleared = false;
423
424  Handle<Object> h;
425
426  {
427    HandleScope scope;
428
429    Handle<Object> i = FACTORY->NewStringFromAscii(CStrVector("fisk"));
430    h = global_handles->Create(*i);
431  }
432
433  global_handles->MakeWeak(h.location(),
434                           reinterpret_cast<void*>(1234),
435                           &TestWeakGlobalHandleCallback);
436
437  // Scanvenge does not recognize weak reference.
438  HEAP->PerformScavenge();
439
440  CHECK(!WeakPointerCleared);
441
442  // Mark-compact treats weak reference properly.
443  HEAP->CollectGarbage(OLD_POINTER_SPACE);
444
445  CHECK(WeakPointerCleared);
446}
447
448static const char* not_so_random_string_table[] = {
449  "abstract",
450  "boolean",
451  "break",
452  "byte",
453  "case",
454  "catch",
455  "char",
456  "class",
457  "const",
458  "continue",
459  "debugger",
460  "default",
461  "delete",
462  "do",
463  "double",
464  "else",
465  "enum",
466  "export",
467  "extends",
468  "false",
469  "final",
470  "finally",
471  "float",
472  "for",
473  "function",
474  "goto",
475  "if",
476  "implements",
477  "import",
478  "in",
479  "instanceof",
480  "int",
481  "interface",
482  "long",
483  "native",
484  "new",
485  "null",
486  "package",
487  "private",
488  "protected",
489  "public",
490  "return",
491  "short",
492  "static",
493  "super",
494  "switch",
495  "synchronized",
496  "this",
497  "throw",
498  "throws",
499  "transient",
500  "true",
501  "try",
502  "typeof",
503  "var",
504  "void",
505  "volatile",
506  "while",
507  "with",
508  0
509};
510
511
512static void CheckSymbols(const char** strings) {
513  for (const char* string = *strings; *strings != 0; string = *strings++) {
514    Object* a;
515    MaybeObject* maybe_a = HEAP->LookupAsciiSymbol(string);
516    // LookupAsciiSymbol may return a failure if a GC is needed.
517    if (!maybe_a->ToObject(&a)) continue;
518    CHECK(a->IsSymbol());
519    Object* b;
520    MaybeObject* maybe_b = HEAP->LookupAsciiSymbol(string);
521    if (!maybe_b->ToObject(&b)) continue;
522    CHECK_EQ(b, a);
523    CHECK(String::cast(b)->IsEqualTo(CStrVector(string)));
524  }
525}
526
527
528TEST(SymbolTable) {
529  InitializeVM();
530
531  CheckSymbols(not_so_random_string_table);
532  CheckSymbols(not_so_random_string_table);
533}
534
535
536TEST(FunctionAllocation) {
537  InitializeVM();
538
539  v8::HandleScope sc;
540  Handle<String> name = FACTORY->LookupAsciiSymbol("theFunction");
541  Handle<JSFunction> function =
542      FACTORY->NewFunction(name, FACTORY->undefined_value());
543  Handle<Map> initial_map =
544      FACTORY->NewMap(JS_OBJECT_TYPE, JSObject::kHeaderSize);
545  function->set_initial_map(*initial_map);
546
547  Handle<String> prop_name = FACTORY->LookupAsciiSymbol("theSlot");
548  Handle<JSObject> obj = FACTORY->NewJSObject(function);
549  obj->SetProperty(
550      *prop_name, Smi::FromInt(23), NONE, kNonStrictMode)->ToObjectChecked();
551  CHECK_EQ(Smi::FromInt(23), obj->GetProperty(*prop_name));
552  // Check that we can add properties to function objects.
553  function->SetProperty(
554      *prop_name, Smi::FromInt(24), NONE, kNonStrictMode)->ToObjectChecked();
555  CHECK_EQ(Smi::FromInt(24), function->GetProperty(*prop_name));
556}
557
558
559TEST(ObjectProperties) {
560  InitializeVM();
561
562  v8::HandleScope sc;
563  String* object_symbol = String::cast(HEAP->Object_symbol());
564  Object* raw_object = Isolate::Current()->context()->global()->
565      GetProperty(object_symbol)->ToObjectChecked();
566  JSFunction* object_function = JSFunction::cast(raw_object);
567  Handle<JSFunction> constructor(object_function);
568  Handle<JSObject> obj = FACTORY->NewJSObject(constructor);
569  Handle<String> first = FACTORY->LookupAsciiSymbol("first");
570  Handle<String> second = FACTORY->LookupAsciiSymbol("second");
571
572  // check for empty
573  CHECK(!obj->HasLocalProperty(*first));
574
575  // add first
576  obj->SetProperty(
577      *first, Smi::FromInt(1), NONE, kNonStrictMode)->ToObjectChecked();
578  CHECK(obj->HasLocalProperty(*first));
579
580  // delete first
581  CHECK(obj->DeleteProperty(*first, JSObject::NORMAL_DELETION));
582  CHECK(!obj->HasLocalProperty(*first));
583
584  // add first and then second
585  obj->SetProperty(
586      *first, Smi::FromInt(1), NONE, kNonStrictMode)->ToObjectChecked();
587  obj->SetProperty(
588      *second, Smi::FromInt(2), NONE, kNonStrictMode)->ToObjectChecked();
589  CHECK(obj->HasLocalProperty(*first));
590  CHECK(obj->HasLocalProperty(*second));
591
592  // delete first and then second
593  CHECK(obj->DeleteProperty(*first, JSObject::NORMAL_DELETION));
594  CHECK(obj->HasLocalProperty(*second));
595  CHECK(obj->DeleteProperty(*second, JSObject::NORMAL_DELETION));
596  CHECK(!obj->HasLocalProperty(*first));
597  CHECK(!obj->HasLocalProperty(*second));
598
599  // add first and then second
600  obj->SetProperty(
601      *first, Smi::FromInt(1), NONE, kNonStrictMode)->ToObjectChecked();
602  obj->SetProperty(
603      *second, Smi::FromInt(2), NONE, kNonStrictMode)->ToObjectChecked();
604  CHECK(obj->HasLocalProperty(*first));
605  CHECK(obj->HasLocalProperty(*second));
606
607  // delete second and then first
608  CHECK(obj->DeleteProperty(*second, JSObject::NORMAL_DELETION));
609  CHECK(obj->HasLocalProperty(*first));
610  CHECK(obj->DeleteProperty(*first, JSObject::NORMAL_DELETION));
611  CHECK(!obj->HasLocalProperty(*first));
612  CHECK(!obj->HasLocalProperty(*second));
613
614  // check string and symbol match
615  const char* string1 = "fisk";
616  Handle<String> s1 = FACTORY->NewStringFromAscii(CStrVector(string1));
617  obj->SetProperty(
618      *s1, Smi::FromInt(1), NONE, kNonStrictMode)->ToObjectChecked();
619  Handle<String> s1_symbol = FACTORY->LookupAsciiSymbol(string1);
620  CHECK(obj->HasLocalProperty(*s1_symbol));
621
622  // check symbol and string match
623  const char* string2 = "fugl";
624  Handle<String> s2_symbol = FACTORY->LookupAsciiSymbol(string2);
625  obj->SetProperty(
626      *s2_symbol, Smi::FromInt(1), NONE, kNonStrictMode)->ToObjectChecked();
627  Handle<String> s2 = FACTORY->NewStringFromAscii(CStrVector(string2));
628  CHECK(obj->HasLocalProperty(*s2));
629}
630
631
632TEST(JSObjectMaps) {
633  InitializeVM();
634
635  v8::HandleScope sc;
636  Handle<String> name = FACTORY->LookupAsciiSymbol("theFunction");
637  Handle<JSFunction> function =
638      FACTORY->NewFunction(name, FACTORY->undefined_value());
639  Handle<Map> initial_map =
640      FACTORY->NewMap(JS_OBJECT_TYPE, JSObject::kHeaderSize);
641  function->set_initial_map(*initial_map);
642
643  Handle<String> prop_name = FACTORY->LookupAsciiSymbol("theSlot");
644  Handle<JSObject> obj = FACTORY->NewJSObject(function);
645
646  // Set a propery
647  obj->SetProperty(
648      *prop_name, Smi::FromInt(23), NONE, kNonStrictMode)->ToObjectChecked();
649  CHECK_EQ(Smi::FromInt(23), obj->GetProperty(*prop_name));
650
651  // Check the map has changed
652  CHECK(*initial_map != obj->map());
653}
654
655
656TEST(JSArray) {
657  InitializeVM();
658
659  v8::HandleScope sc;
660  Handle<String> name = FACTORY->LookupAsciiSymbol("Array");
661  Object* raw_object = Isolate::Current()->context()->global()->
662      GetProperty(*name)->ToObjectChecked();
663  Handle<JSFunction> function = Handle<JSFunction>(
664      JSFunction::cast(raw_object));
665
666  // Allocate the object.
667  Handle<JSObject> object = FACTORY->NewJSObject(function);
668  Handle<JSArray> array = Handle<JSArray>::cast(object);
669  // We just initialized the VM, no heap allocation failure yet.
670  array->Initialize(0)->ToObjectChecked();
671
672  // Set array length to 0.
673  array->SetElementsLength(Smi::FromInt(0))->ToObjectChecked();
674  CHECK_EQ(Smi::FromInt(0), array->length());
675  // Must be in fast mode.
676  CHECK(array->HasFastTypeElements());
677
678  // array[length] = name.
679  array->SetElement(0, *name, NONE, kNonStrictMode)->ToObjectChecked();
680  CHECK_EQ(Smi::FromInt(1), array->length());
681  CHECK_EQ(array->GetElement(0), *name);
682
683  // Set array length with larger than smi value.
684  Handle<Object> length =
685      FACTORY->NewNumberFromUint(static_cast<uint32_t>(Smi::kMaxValue) + 1);
686  array->SetElementsLength(*length)->ToObjectChecked();
687
688  uint32_t int_length = 0;
689  CHECK(length->ToArrayIndex(&int_length));
690  CHECK_EQ(*length, array->length());
691  CHECK(array->HasDictionaryElements());  // Must be in slow mode.
692
693  // array[length] = name.
694  array->SetElement(int_length, *name, NONE, kNonStrictMode)->ToObjectChecked();
695  uint32_t new_int_length = 0;
696  CHECK(array->length()->ToArrayIndex(&new_int_length));
697  CHECK_EQ(static_cast<double>(int_length), new_int_length - 1);
698  CHECK_EQ(array->GetElement(int_length), *name);
699  CHECK_EQ(array->GetElement(0), *name);
700}
701
702
703TEST(JSObjectCopy) {
704  InitializeVM();
705
706  v8::HandleScope sc;
707  String* object_symbol = String::cast(HEAP->Object_symbol());
708  Object* raw_object = Isolate::Current()->context()->global()->
709      GetProperty(object_symbol)->ToObjectChecked();
710  JSFunction* object_function = JSFunction::cast(raw_object);
711  Handle<JSFunction> constructor(object_function);
712  Handle<JSObject> obj = FACTORY->NewJSObject(constructor);
713  Handle<String> first = FACTORY->LookupAsciiSymbol("first");
714  Handle<String> second = FACTORY->LookupAsciiSymbol("second");
715
716  obj->SetProperty(
717      *first, Smi::FromInt(1), NONE, kNonStrictMode)->ToObjectChecked();
718  obj->SetProperty(
719      *second, Smi::FromInt(2), NONE, kNonStrictMode)->ToObjectChecked();
720
721  obj->SetElement(0, *first, NONE, kNonStrictMode)->ToObjectChecked();
722  obj->SetElement(1, *second, NONE, kNonStrictMode)->ToObjectChecked();
723
724  // Make the clone.
725  Handle<JSObject> clone = Copy(obj);
726  CHECK(!clone.is_identical_to(obj));
727
728  CHECK_EQ(obj->GetElement(0), clone->GetElement(0));
729  CHECK_EQ(obj->GetElement(1), clone->GetElement(1));
730
731  CHECK_EQ(obj->GetProperty(*first), clone->GetProperty(*first));
732  CHECK_EQ(obj->GetProperty(*second), clone->GetProperty(*second));
733
734  // Flip the values.
735  clone->SetProperty(
736      *first, Smi::FromInt(2), NONE, kNonStrictMode)->ToObjectChecked();
737  clone->SetProperty(
738      *second, Smi::FromInt(1), NONE, kNonStrictMode)->ToObjectChecked();
739
740  clone->SetElement(0, *second, NONE, kNonStrictMode)->ToObjectChecked();
741  clone->SetElement(1, *first, NONE, kNonStrictMode)->ToObjectChecked();
742
743  CHECK_EQ(obj->GetElement(1), clone->GetElement(0));
744  CHECK_EQ(obj->GetElement(0), clone->GetElement(1));
745
746  CHECK_EQ(obj->GetProperty(*second), clone->GetProperty(*first));
747  CHECK_EQ(obj->GetProperty(*first), clone->GetProperty(*second));
748}
749
750
751TEST(StringAllocation) {
752  InitializeVM();
753
754
755  const unsigned char chars[] = { 0xe5, 0xa4, 0xa7 };
756  for (int length = 0; length < 100; length++) {
757    v8::HandleScope scope;
758    char* non_ascii = NewArray<char>(3 * length + 1);
759    char* ascii = NewArray<char>(length + 1);
760    non_ascii[3 * length] = 0;
761    ascii[length] = 0;
762    for (int i = 0; i < length; i++) {
763      ascii[i] = 'a';
764      non_ascii[3 * i] = chars[0];
765      non_ascii[3 * i + 1] = chars[1];
766      non_ascii[3 * i + 2] = chars[2];
767    }
768    Handle<String> non_ascii_sym =
769        FACTORY->LookupSymbol(Vector<const char>(non_ascii, 3 * length));
770    CHECK_EQ(length, non_ascii_sym->length());
771    Handle<String> ascii_sym =
772        FACTORY->LookupSymbol(Vector<const char>(ascii, length));
773    CHECK_EQ(length, ascii_sym->length());
774    Handle<String> non_ascii_str =
775        FACTORY->NewStringFromUtf8(Vector<const char>(non_ascii, 3 * length));
776    non_ascii_str->Hash();
777    CHECK_EQ(length, non_ascii_str->length());
778    Handle<String> ascii_str =
779        FACTORY->NewStringFromUtf8(Vector<const char>(ascii, length));
780    ascii_str->Hash();
781    CHECK_EQ(length, ascii_str->length());
782    DeleteArray(non_ascii);
783    DeleteArray(ascii);
784  }
785}
786
787
788static int ObjectsFoundInHeap(Handle<Object> objs[], int size) {
789  // Count the number of objects found in the heap.
790  int found_count = 0;
791  HeapIterator iterator;
792  for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) {
793    for (int i = 0; i < size; i++) {
794      if (*objs[i] == obj) {
795        found_count++;
796      }
797    }
798  }
799  return found_count;
800}
801
802
803TEST(Iteration) {
804  InitializeVM();
805  v8::HandleScope scope;
806
807  // Array of objects to scan haep for.
808  const int objs_count = 6;
809  Handle<Object> objs[objs_count];
810  int next_objs_index = 0;
811
812  // Allocate a JS array to OLD_POINTER_SPACE and NEW_SPACE
813  objs[next_objs_index++] = FACTORY->NewJSArray(10);
814  objs[next_objs_index++] = FACTORY->NewJSArray(10, FAST_ELEMENTS, TENURED);
815
816  // Allocate a small string to OLD_DATA_SPACE and NEW_SPACE
817  objs[next_objs_index++] =
818      FACTORY->NewStringFromAscii(CStrVector("abcdefghij"));
819  objs[next_objs_index++] =
820      FACTORY->NewStringFromAscii(CStrVector("abcdefghij"), TENURED);
821
822  // Allocate a large string (for large object space).
823  int large_size = Page::kMaxNonCodeHeapObjectSize + 1;
824  char* str = new char[large_size];
825  for (int i = 0; i < large_size - 1; ++i) str[i] = 'a';
826  str[large_size - 1] = '\0';
827  objs[next_objs_index++] =
828      FACTORY->NewStringFromAscii(CStrVector(str), TENURED);
829  delete[] str;
830
831  // Add a Map object to look for.
832  objs[next_objs_index++] = Handle<Map>(HeapObject::cast(*objs[0])->map());
833
834  CHECK_EQ(objs_count, next_objs_index);
835  CHECK_EQ(objs_count, ObjectsFoundInHeap(objs, objs_count));
836}
837
838
839TEST(EmptyHandleEscapeFrom) {
840  InitializeVM();
841
842  v8::HandleScope scope;
843  Handle<JSObject> runaway;
844
845  {
846      v8::HandleScope nested;
847      Handle<JSObject> empty;
848      runaway = empty.EscapeFrom(&nested);
849  }
850
851  CHECK(runaway.is_null());
852}
853
854
855static int LenFromSize(int size) {
856  return (size - FixedArray::kHeaderSize) / kPointerSize;
857}
858
859
860TEST(Regression39128) {
861  // Test case for crbug.com/39128.
862  InitializeVM();
863
864  // Increase the chance of 'bump-the-pointer' allocation in old space.
865  HEAP->CollectAllGarbage(Heap::kNoGCFlags);
866
867  v8::HandleScope scope;
868
869  // The plan: create JSObject which references objects in new space.
870  // Then clone this object (forcing it to go into old space) and check
871  // that region dirty marks are updated correctly.
872
873  // Step 1: prepare a map for the object.  We add 1 inobject property to it.
874  Handle<JSFunction> object_ctor(
875      Isolate::Current()->global_context()->object_function());
876  CHECK(object_ctor->has_initial_map());
877  Handle<Map> object_map(object_ctor->initial_map());
878  // Create a map with single inobject property.
879  Handle<Map> my_map = FACTORY->CopyMap(object_map, 1);
880  int n_properties = my_map->inobject_properties();
881  CHECK_GT(n_properties, 0);
882
883  int object_size = my_map->instance_size();
884
885  // Step 2: allocate a lot of objects so to almost fill new space: we need
886  // just enough room to allocate JSObject and thus fill the newspace.
887
888  int allocation_amount = Min(FixedArray::kMaxSize,
889                              HEAP->MaxObjectSizeInNewSpace());
890  int allocation_len = LenFromSize(allocation_amount);
891  NewSpace* new_space = HEAP->new_space();
892  Address* top_addr = new_space->allocation_top_address();
893  Address* limit_addr = new_space->allocation_limit_address();
894  while ((*limit_addr - *top_addr) > allocation_amount) {
895    CHECK(!HEAP->always_allocate());
896    Object* array = HEAP->AllocateFixedArray(allocation_len)->ToObjectChecked();
897    CHECK(!array->IsFailure());
898    CHECK(new_space->Contains(array));
899  }
900
901  // Step 3: now allocate fixed array and JSObject to fill the whole new space.
902  int to_fill = static_cast<int>(*limit_addr - *top_addr - object_size);
903  int fixed_array_len = LenFromSize(to_fill);
904  CHECK(fixed_array_len < FixedArray::kMaxLength);
905
906  CHECK(!HEAP->always_allocate());
907  Object* array = HEAP->AllocateFixedArray(fixed_array_len)->ToObjectChecked();
908  CHECK(!array->IsFailure());
909  CHECK(new_space->Contains(array));
910
911  Object* object = HEAP->AllocateJSObjectFromMap(*my_map)->ToObjectChecked();
912  CHECK(new_space->Contains(object));
913  JSObject* jsobject = JSObject::cast(object);
914  CHECK_EQ(0, FixedArray::cast(jsobject->elements())->length());
915  CHECK_EQ(0, jsobject->properties()->length());
916  // Create a reference to object in new space in jsobject.
917  jsobject->FastPropertyAtPut(-1, array);
918
919  CHECK_EQ(0, static_cast<int>(*limit_addr - *top_addr));
920
921  // Step 4: clone jsobject, but force always allocate first to create a clone
922  // in old pointer space.
923  Address old_pointer_space_top = HEAP->old_pointer_space()->top();
924  AlwaysAllocateScope aa_scope;
925  Object* clone_obj = HEAP->CopyJSObject(jsobject)->ToObjectChecked();
926  JSObject* clone = JSObject::cast(clone_obj);
927  if (clone->address() != old_pointer_space_top) {
928    // Alas, got allocated from free list, we cannot do checks.
929    return;
930  }
931  CHECK(HEAP->old_pointer_space()->Contains(clone->address()));
932}
933
934
935TEST(TestCodeFlushing) {
936  i::FLAG_allow_natives_syntax = true;
937  // If we do not flush code this test is invalid.
938  if (!FLAG_flush_code) return;
939  InitializeVM();
940  v8::HandleScope scope;
941  const char* source = "function foo() {"
942                       "  var x = 42;"
943                       "  var y = 42;"
944                       "  var z = x + y;"
945                       "};"
946                       "foo()";
947  Handle<String> foo_name = FACTORY->LookupAsciiSymbol("foo");
948
949  // This compile will add the code to the compilation cache.
950  { v8::HandleScope scope;
951    CompileRun(source);
952  }
953
954  // Check function is compiled.
955  Object* func_value = Isolate::Current()->context()->global()->
956      GetProperty(*foo_name)->ToObjectChecked();
957  CHECK(func_value->IsJSFunction());
958  Handle<JSFunction> function(JSFunction::cast(func_value));
959  CHECK(function->shared()->is_compiled());
960
961  // TODO(1609) Currently incremental marker does not support code flushing.
962  HEAP->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask);
963  HEAP->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask);
964
965  CHECK(function->shared()->is_compiled());
966
967  HEAP->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask);
968  HEAP->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask);
969  HEAP->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask);
970  HEAP->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask);
971  HEAP->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask);
972  HEAP->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask);
973
974  // foo should no longer be in the compilation cache
975  CHECK(!function->shared()->is_compiled() || function->IsOptimized());
976  CHECK(!function->is_compiled() || function->IsOptimized());
977  // Call foo to get it recompiled.
978  CompileRun("foo()");
979  CHECK(function->shared()->is_compiled());
980  CHECK(function->is_compiled());
981}
982
983
984// Count the number of global contexts in the weak list of global contexts.
985static int CountGlobalContexts() {
986  int count = 0;
987  Object* object = HEAP->global_contexts_list();
988  while (!object->IsUndefined()) {
989    count++;
990    object = Context::cast(object)->get(Context::NEXT_CONTEXT_LINK);
991  }
992  return count;
993}
994
995
996// Count the number of user functions in the weak list of optimized
997// functions attached to a global context.
998static int CountOptimizedUserFunctions(v8::Handle<v8::Context> context) {
999  int count = 0;
1000  Handle<Context> icontext = v8::Utils::OpenHandle(*context);
1001  Object* object = icontext->get(Context::OPTIMIZED_FUNCTIONS_LIST);
1002  while (object->IsJSFunction() && !JSFunction::cast(object)->IsBuiltin()) {
1003    count++;
1004    object = JSFunction::cast(object)->next_function_link();
1005  }
1006  return count;
1007}
1008
1009
1010TEST(TestInternalWeakLists) {
1011  v8::V8::Initialize();
1012
1013  static const int kNumTestContexts = 10;
1014
1015  v8::HandleScope scope;
1016  v8::Persistent<v8::Context> ctx[kNumTestContexts];
1017
1018  CHECK_EQ(0, CountGlobalContexts());
1019
1020  // Create a number of global contests which gets linked together.
1021  for (int i = 0; i < kNumTestContexts; i++) {
1022    ctx[i] = v8::Context::New();
1023
1024    bool opt = (FLAG_always_opt && i::V8::UseCrankshaft());
1025
1026    CHECK_EQ(i + 1, CountGlobalContexts());
1027
1028    ctx[i]->Enter();
1029
1030    // Create a handle scope so no function objects get stuch in the outer
1031    // handle scope
1032    v8::HandleScope scope;
1033    const char* source = "function f1() { };"
1034                         "function f2() { };"
1035                         "function f3() { };"
1036                         "function f4() { };"
1037                         "function f5() { };";
1038    CompileRun(source);
1039    CHECK_EQ(0, CountOptimizedUserFunctions(ctx[i]));
1040    CompileRun("f1()");
1041    CHECK_EQ(opt ? 1 : 0, CountOptimizedUserFunctions(ctx[i]));
1042    CompileRun("f2()");
1043    CHECK_EQ(opt ? 2 : 0, CountOptimizedUserFunctions(ctx[i]));
1044    CompileRun("f3()");
1045    CHECK_EQ(opt ? 3 : 0, CountOptimizedUserFunctions(ctx[i]));
1046    CompileRun("f4()");
1047    CHECK_EQ(opt ? 4 : 0, CountOptimizedUserFunctions(ctx[i]));
1048    CompileRun("f5()");
1049    CHECK_EQ(opt ? 5 : 0, CountOptimizedUserFunctions(ctx[i]));
1050
1051    // Remove function f1, and
1052    CompileRun("f1=null");
1053
1054    // Scavenge treats these references as strong.
1055    for (int j = 0; j < 10; j++) {
1056      HEAP->PerformScavenge();
1057      CHECK_EQ(opt ? 5 : 0, CountOptimizedUserFunctions(ctx[i]));
1058    }
1059
1060    // Mark compact handles the weak references.
1061    HEAP->CollectAllGarbage(Heap::kNoGCFlags);
1062    CHECK_EQ(opt ? 4 : 0, CountOptimizedUserFunctions(ctx[i]));
1063
1064    // Get rid of f3 and f5 in the same way.
1065    CompileRun("f3=null");
1066    for (int j = 0; j < 10; j++) {
1067      HEAP->PerformScavenge();
1068      CHECK_EQ(opt ? 4 : 0, CountOptimizedUserFunctions(ctx[i]));
1069    }
1070    HEAP->CollectAllGarbage(Heap::kNoGCFlags);
1071    CHECK_EQ(opt ? 3 : 0, CountOptimizedUserFunctions(ctx[i]));
1072    CompileRun("f5=null");
1073    for (int j = 0; j < 10; j++) {
1074      HEAP->PerformScavenge();
1075      CHECK_EQ(opt ? 3 : 0, CountOptimizedUserFunctions(ctx[i]));
1076    }
1077    HEAP->CollectAllGarbage(Heap::kNoGCFlags);
1078    CHECK_EQ(opt ? 2 : 0, CountOptimizedUserFunctions(ctx[i]));
1079
1080    ctx[i]->Exit();
1081  }
1082
1083  // Force compilation cache cleanup.
1084  HEAP->CollectAllGarbage(Heap::kNoGCFlags);
1085
1086  // Dispose the global contexts one by one.
1087  for (int i = 0; i < kNumTestContexts; i++) {
1088    ctx[i].Dispose();
1089    ctx[i].Clear();
1090
1091    // Scavenge treats these references as strong.
1092    for (int j = 0; j < 10; j++) {
1093      HEAP->PerformScavenge();
1094      CHECK_EQ(kNumTestContexts - i, CountGlobalContexts());
1095    }
1096
1097    // Mark compact handles the weak references.
1098    HEAP->CollectAllGarbage(Heap::kNoGCFlags);
1099    CHECK_EQ(kNumTestContexts - i - 1, CountGlobalContexts());
1100  }
1101
1102  CHECK_EQ(0, CountGlobalContexts());
1103}
1104
1105
1106// Count the number of global contexts in the weak list of global contexts
1107// causing a GC after the specified number of elements.
1108static int CountGlobalContextsWithGC(int n) {
1109  int count = 0;
1110  Handle<Object> object(HEAP->global_contexts_list());
1111  while (!object->IsUndefined()) {
1112    count++;
1113    if (count == n) HEAP->CollectAllGarbage(Heap::kNoGCFlags);
1114    object =
1115        Handle<Object>(Context::cast(*object)->get(Context::NEXT_CONTEXT_LINK));
1116  }
1117  return count;
1118}
1119
1120
1121// Count the number of user functions in the weak list of optimized
1122// functions attached to a global context causing a GC after the
1123// specified number of elements.
1124static int CountOptimizedUserFunctionsWithGC(v8::Handle<v8::Context> context,
1125                                             int n) {
1126  int count = 0;
1127  Handle<Context> icontext = v8::Utils::OpenHandle(*context);
1128  Handle<Object> object(icontext->get(Context::OPTIMIZED_FUNCTIONS_LIST));
1129  while (object->IsJSFunction() &&
1130         !Handle<JSFunction>::cast(object)->IsBuiltin()) {
1131    count++;
1132    if (count == n) HEAP->CollectAllGarbage(Heap::kNoGCFlags);
1133    object = Handle<Object>(
1134        Object::cast(JSFunction::cast(*object)->next_function_link()));
1135  }
1136  return count;
1137}
1138
1139
1140TEST(TestInternalWeakListsTraverseWithGC) {
1141  v8::V8::Initialize();
1142
1143  static const int kNumTestContexts = 10;
1144
1145  v8::HandleScope scope;
1146  v8::Persistent<v8::Context> ctx[kNumTestContexts];
1147
1148  CHECK_EQ(0, CountGlobalContexts());
1149
1150  // Create an number of contexts and check the length of the weak list both
1151  // with and without GCs while iterating the list.
1152  for (int i = 0; i < kNumTestContexts; i++) {
1153    ctx[i] = v8::Context::New();
1154    CHECK_EQ(i + 1, CountGlobalContexts());
1155    CHECK_EQ(i + 1, CountGlobalContextsWithGC(i / 2 + 1));
1156  }
1157
1158  bool opt = (FLAG_always_opt && i::V8::UseCrankshaft());
1159
1160  // Compile a number of functions the length of the weak list of optimized
1161  // functions both with and without GCs while iterating the list.
1162  ctx[0]->Enter();
1163  const char* source = "function f1() { };"
1164                       "function f2() { };"
1165                       "function f3() { };"
1166                       "function f4() { };"
1167                       "function f5() { };";
1168  CompileRun(source);
1169  CHECK_EQ(0, CountOptimizedUserFunctions(ctx[0]));
1170  CompileRun("f1()");
1171  CHECK_EQ(opt ? 1 : 0, CountOptimizedUserFunctions(ctx[0]));
1172  CHECK_EQ(opt ? 1 : 0, CountOptimizedUserFunctionsWithGC(ctx[0], 1));
1173  CompileRun("f2()");
1174  CHECK_EQ(opt ? 2 : 0, CountOptimizedUserFunctions(ctx[0]));
1175  CHECK_EQ(opt ? 2 : 0, CountOptimizedUserFunctionsWithGC(ctx[0], 1));
1176  CompileRun("f3()");
1177  CHECK_EQ(opt ? 3 : 0, CountOptimizedUserFunctions(ctx[0]));
1178  CHECK_EQ(opt ? 3 : 0, CountOptimizedUserFunctionsWithGC(ctx[0], 1));
1179  CompileRun("f4()");
1180  CHECK_EQ(opt ? 4 : 0, CountOptimizedUserFunctions(ctx[0]));
1181  CHECK_EQ(opt ? 4 : 0, CountOptimizedUserFunctionsWithGC(ctx[0], 2));
1182  CompileRun("f5()");
1183  CHECK_EQ(opt ? 5 : 0, CountOptimizedUserFunctions(ctx[0]));
1184  CHECK_EQ(opt ? 5 : 0, CountOptimizedUserFunctionsWithGC(ctx[0], 4));
1185
1186  ctx[0]->Exit();
1187}
1188
1189
1190TEST(TestSizeOfObjects) {
1191  v8::V8::Initialize();
1192
1193  // Get initial heap size after several full GCs, which will stabilize
1194  // the heap size and return with sweeping finished completely.
1195  HEAP->CollectAllGarbage(Heap::kNoGCFlags);
1196  HEAP->CollectAllGarbage(Heap::kNoGCFlags);
1197  HEAP->CollectAllGarbage(Heap::kNoGCFlags);
1198  HEAP->CollectAllGarbage(Heap::kNoGCFlags);
1199  CHECK(HEAP->old_pointer_space()->IsSweepingComplete());
1200  int initial_size = static_cast<int>(HEAP->SizeOfObjects());
1201
1202  {
1203    // Allocate objects on several different old-space pages so that
1204    // lazy sweeping kicks in for subsequent GC runs.
1205    AlwaysAllocateScope always_allocate;
1206    int filler_size = static_cast<int>(FixedArray::SizeFor(8192));
1207    for (int i = 1; i <= 100; i++) {
1208      HEAP->AllocateFixedArray(8192, TENURED)->ToObjectChecked();
1209      CHECK_EQ(initial_size + i * filler_size,
1210               static_cast<int>(HEAP->SizeOfObjects()));
1211    }
1212  }
1213
1214  // The heap size should go back to initial size after a full GC, even
1215  // though sweeping didn't finish yet.
1216  HEAP->CollectAllGarbage(Heap::kNoGCFlags);
1217  CHECK(!HEAP->old_pointer_space()->IsSweepingComplete());
1218  CHECK_EQ(initial_size, static_cast<int>(HEAP->SizeOfObjects()));
1219
1220  // Advancing the sweeper step-wise should not change the heap size.
1221  while (!HEAP->old_pointer_space()->IsSweepingComplete()) {
1222    HEAP->old_pointer_space()->AdvanceSweeper(KB);
1223    CHECK_EQ(initial_size, static_cast<int>(HEAP->SizeOfObjects()));
1224  }
1225}
1226
1227
1228TEST(TestSizeOfObjectsVsHeapIteratorPrecision) {
1229  InitializeVM();
1230  HEAP->EnsureHeapIsIterable();
1231  intptr_t size_of_objects_1 = HEAP->SizeOfObjects();
1232  HeapIterator iterator;
1233  intptr_t size_of_objects_2 = 0;
1234  for (HeapObject* obj = iterator.next();
1235       obj != NULL;
1236       obj = iterator.next()) {
1237    size_of_objects_2 += obj->Size();
1238  }
1239  // Delta must be within 5% of the larger result.
1240  // TODO(gc): Tighten this up by distinguishing between byte
1241  // arrays that are real and those that merely mark free space
1242  // on the heap.
1243  if (size_of_objects_1 > size_of_objects_2) {
1244    intptr_t delta = size_of_objects_1 - size_of_objects_2;
1245    PrintF("Heap::SizeOfObjects: %" V8_PTR_PREFIX "d, "
1246           "Iterator: %" V8_PTR_PREFIX "d, "
1247           "delta: %" V8_PTR_PREFIX "d\n",
1248           size_of_objects_1, size_of_objects_2, delta);
1249    CHECK_GT(size_of_objects_1 / 20, delta);
1250  } else {
1251    intptr_t delta = size_of_objects_2 - size_of_objects_1;
1252    PrintF("Heap::SizeOfObjects: %" V8_PTR_PREFIX "d, "
1253           "Iterator: %" V8_PTR_PREFIX "d, "
1254           "delta: %" V8_PTR_PREFIX "d\n",
1255           size_of_objects_1, size_of_objects_2, delta);
1256    CHECK_GT(size_of_objects_2 / 20, delta);
1257  }
1258}
1259
1260
1261static void FillUpNewSpace(NewSpace* new_space) {
1262  // Fill up new space to the point that it is completely full. Make sure
1263  // that the scavenger does not undo the filling.
1264  v8::HandleScope scope;
1265  AlwaysAllocateScope always_allocate;
1266  intptr_t available = new_space->EffectiveCapacity() - new_space->Size();
1267  intptr_t number_of_fillers = (available / FixedArray::SizeFor(1000)) - 10;
1268  for (intptr_t i = 0; i < number_of_fillers; i++) {
1269    CHECK(HEAP->InNewSpace(*FACTORY->NewFixedArray(1000, NOT_TENURED)));
1270  }
1271}
1272
1273
1274TEST(GrowAndShrinkNewSpace) {
1275  InitializeVM();
1276  NewSpace* new_space = HEAP->new_space();
1277
1278  // Explicitly growing should double the space capacity.
1279  intptr_t old_capacity, new_capacity;
1280  old_capacity = new_space->Capacity();
1281  new_space->Grow();
1282  new_capacity = new_space->Capacity();
1283  CHECK(2 * old_capacity == new_capacity);
1284
1285  old_capacity = new_space->Capacity();
1286  FillUpNewSpace(new_space);
1287  new_capacity = new_space->Capacity();
1288  CHECK(old_capacity == new_capacity);
1289
1290  // Explicitly shrinking should not affect space capacity.
1291  old_capacity = new_space->Capacity();
1292  new_space->Shrink();
1293  new_capacity = new_space->Capacity();
1294  CHECK(old_capacity == new_capacity);
1295
1296  // Let the scavenger empty the new space.
1297  HEAP->CollectGarbage(NEW_SPACE);
1298  CHECK_LE(new_space->Size(), old_capacity);
1299
1300  // Explicitly shrinking should halve the space capacity.
1301  old_capacity = new_space->Capacity();
1302  new_space->Shrink();
1303  new_capacity = new_space->Capacity();
1304  CHECK(old_capacity == 2 * new_capacity);
1305
1306  // Consecutive shrinking should not affect space capacity.
1307  old_capacity = new_space->Capacity();
1308  new_space->Shrink();
1309  new_space->Shrink();
1310  new_space->Shrink();
1311  new_capacity = new_space->Capacity();
1312  CHECK(old_capacity == new_capacity);
1313}
1314
1315
1316TEST(CollectingAllAvailableGarbageShrinksNewSpace) {
1317  InitializeVM();
1318  v8::HandleScope scope;
1319  NewSpace* new_space = HEAP->new_space();
1320  intptr_t old_capacity, new_capacity;
1321  old_capacity = new_space->Capacity();
1322  new_space->Grow();
1323  new_capacity = new_space->Capacity();
1324  CHECK(2 * old_capacity == new_capacity);
1325  FillUpNewSpace(new_space);
1326  HEAP->CollectAllAvailableGarbage();
1327  new_capacity = new_space->Capacity();
1328  CHECK(old_capacity == new_capacity);
1329}
1330
1331
1332static int NumberOfGlobalObjects() {
1333  int count = 0;
1334  HeapIterator iterator;
1335  for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) {
1336    if (obj->IsGlobalObject()) count++;
1337  }
1338  return count;
1339}
1340
1341
1342// Test that we don't embed maps from foreign contexts into
1343// optimized code.
1344TEST(LeakGlobalContextViaMap) {
1345  i::FLAG_allow_natives_syntax = true;
1346  v8::HandleScope outer_scope;
1347  v8::Persistent<v8::Context> ctx1 = v8::Context::New();
1348  v8::Persistent<v8::Context> ctx2 = v8::Context::New();
1349  ctx1->Enter();
1350
1351  HEAP->CollectAllAvailableGarbage();
1352  CHECK_EQ(4, NumberOfGlobalObjects());
1353
1354  {
1355    v8::HandleScope inner_scope;
1356    CompileRun("var v = {x: 42}");
1357    v8::Local<v8::Value> v = ctx1->Global()->Get(v8_str("v"));
1358    ctx2->Enter();
1359    ctx2->Global()->Set(v8_str("o"), v);
1360    v8::Local<v8::Value> res = CompileRun(
1361        "function f() { return o.x; }"
1362        "for (var i = 0; i < 10; ++i) f();"
1363        "%OptimizeFunctionOnNextCall(f);"
1364        "f();");
1365    CHECK_EQ(42, res->Int32Value());
1366    ctx2->Global()->Set(v8_str("o"), v8::Int32::New(0));
1367    ctx2->Exit();
1368    ctx1->Exit();
1369    ctx1.Dispose();
1370  }
1371  HEAP->CollectAllAvailableGarbage();
1372  CHECK_EQ(2, NumberOfGlobalObjects());
1373  ctx2.Dispose();
1374  HEAP->CollectAllAvailableGarbage();
1375  CHECK_EQ(0, NumberOfGlobalObjects());
1376}
1377
1378
1379// Test that we don't embed functions from foreign contexts into
1380// optimized code.
1381TEST(LeakGlobalContextViaFunction) {
1382  i::FLAG_allow_natives_syntax = true;
1383  v8::HandleScope outer_scope;
1384  v8::Persistent<v8::Context> ctx1 = v8::Context::New();
1385  v8::Persistent<v8::Context> ctx2 = v8::Context::New();
1386  ctx1->Enter();
1387
1388  HEAP->CollectAllAvailableGarbage();
1389  CHECK_EQ(4, NumberOfGlobalObjects());
1390
1391  {
1392    v8::HandleScope inner_scope;
1393    CompileRun("var v = function() { return 42; }");
1394    v8::Local<v8::Value> v = ctx1->Global()->Get(v8_str("v"));
1395    ctx2->Enter();
1396    ctx2->Global()->Set(v8_str("o"), v);
1397    v8::Local<v8::Value> res = CompileRun(
1398        "function f(x) { return x(); }"
1399        "for (var i = 0; i < 10; ++i) f(o);"
1400        "%OptimizeFunctionOnNextCall(f);"
1401        "f(o);");
1402    CHECK_EQ(42, res->Int32Value());
1403    ctx2->Global()->Set(v8_str("o"), v8::Int32::New(0));
1404    ctx2->Exit();
1405    ctx1->Exit();
1406    ctx1.Dispose();
1407  }
1408  HEAP->CollectAllAvailableGarbage();
1409  CHECK_EQ(2, NumberOfGlobalObjects());
1410  ctx2.Dispose();
1411  HEAP->CollectAllAvailableGarbage();
1412  CHECK_EQ(0, NumberOfGlobalObjects());
1413}
1414
1415
1416TEST(LeakGlobalContextViaMapKeyed) {
1417  i::FLAG_allow_natives_syntax = true;
1418  v8::HandleScope outer_scope;
1419  v8::Persistent<v8::Context> ctx1 = v8::Context::New();
1420  v8::Persistent<v8::Context> ctx2 = v8::Context::New();
1421  ctx1->Enter();
1422
1423  HEAP->CollectAllAvailableGarbage();
1424  CHECK_EQ(4, NumberOfGlobalObjects());
1425
1426  {
1427    v8::HandleScope inner_scope;
1428    CompileRun("var v = [42, 43]");
1429    v8::Local<v8::Value> v = ctx1->Global()->Get(v8_str("v"));
1430    ctx2->Enter();
1431    ctx2->Global()->Set(v8_str("o"), v);
1432    v8::Local<v8::Value> res = CompileRun(
1433        "function f() { return o[0]; }"
1434        "for (var i = 0; i < 10; ++i) f();"
1435        "%OptimizeFunctionOnNextCall(f);"
1436        "f();");
1437    CHECK_EQ(42, res->Int32Value());
1438    ctx2->Global()->Set(v8_str("o"), v8::Int32::New(0));
1439    ctx2->Exit();
1440    ctx1->Exit();
1441    ctx1.Dispose();
1442  }
1443  HEAP->CollectAllAvailableGarbage();
1444  CHECK_EQ(2, NumberOfGlobalObjects());
1445  ctx2.Dispose();
1446  HEAP->CollectAllAvailableGarbage();
1447  CHECK_EQ(0, NumberOfGlobalObjects());
1448}
1449
1450
1451TEST(LeakGlobalContextViaMapProto) {
1452  i::FLAG_allow_natives_syntax = true;
1453  v8::HandleScope outer_scope;
1454  v8::Persistent<v8::Context> ctx1 = v8::Context::New();
1455  v8::Persistent<v8::Context> ctx2 = v8::Context::New();
1456  ctx1->Enter();
1457
1458  HEAP->CollectAllAvailableGarbage();
1459  CHECK_EQ(4, NumberOfGlobalObjects());
1460
1461  {
1462    v8::HandleScope inner_scope;
1463    CompileRun("var v = { y: 42}");
1464    v8::Local<v8::Value> v = ctx1->Global()->Get(v8_str("v"));
1465    ctx2->Enter();
1466    ctx2->Global()->Set(v8_str("o"), v);
1467    v8::Local<v8::Value> res = CompileRun(
1468        "function f() {"
1469        "  var p = {x: 42};"
1470        "  p.__proto__ = o;"
1471        "  return p.x;"
1472        "}"
1473        "for (var i = 0; i < 10; ++i) f();"
1474        "%OptimizeFunctionOnNextCall(f);"
1475        "f();");
1476    CHECK_EQ(42, res->Int32Value());
1477    ctx2->Global()->Set(v8_str("o"), v8::Int32::New(0));
1478    ctx2->Exit();
1479    ctx1->Exit();
1480    ctx1.Dispose();
1481  }
1482  HEAP->CollectAllAvailableGarbage();
1483  CHECK_EQ(2, NumberOfGlobalObjects());
1484  ctx2.Dispose();
1485  HEAP->CollectAllAvailableGarbage();
1486  CHECK_EQ(0, NumberOfGlobalObjects());
1487}
1488
1489
1490TEST(InstanceOfStubWriteBarrier) {
1491  i::FLAG_allow_natives_syntax = true;
1492#ifdef DEBUG
1493  i::FLAG_verify_heap = true;
1494#endif
1495  InitializeVM();
1496  if (!i::V8::UseCrankshaft()) return;
1497  v8::HandleScope outer_scope;
1498
1499  {
1500    v8::HandleScope scope;
1501    CompileRun(
1502        "function foo () { }"
1503        "function mkbar () { return new (new Function(\"\")) (); }"
1504        "function f (x) { return (x instanceof foo); }"
1505        "function g () { f(mkbar()); }"
1506        "f(new foo()); f(new foo());"
1507        "%OptimizeFunctionOnNextCall(f);"
1508        "f(new foo()); g();");
1509  }
1510
1511  IncrementalMarking* marking = HEAP->incremental_marking();
1512  marking->Abort();
1513  marking->Start();
1514
1515  Handle<JSFunction> f =
1516      v8::Utils::OpenHandle(
1517          *v8::Handle<v8::Function>::Cast(
1518              v8::Context::GetCurrent()->Global()->Get(v8_str("f"))));
1519
1520  CHECK(f->IsOptimized());
1521
1522  while (!Marking::IsBlack(Marking::MarkBitFrom(f->code())) &&
1523         !marking->IsStopped()) {
1524    // Discard any pending GC requests otherwise we will get GC when we enter
1525    // code below.
1526    marking->Step(MB, IncrementalMarking::NO_GC_VIA_STACK_GUARD);
1527  }
1528
1529  CHECK(marking->IsMarking());
1530
1531  {
1532    v8::HandleScope scope;
1533    v8::Handle<v8::Object> global = v8::Context::GetCurrent()->Global();
1534    v8::Handle<v8::Function> g =
1535        v8::Handle<v8::Function>::Cast(global->Get(v8_str("g")));
1536    g->Call(global, 0, NULL);
1537  }
1538
1539  HEAP->incremental_marking()->set_should_hurry(true);
1540  HEAP->CollectGarbage(OLD_POINTER_SPACE);
1541}
1542
1543
1544TEST(PrototypeTransitionClearing) {
1545  InitializeVM();
1546  v8::HandleScope scope;
1547
1548  CompileRun(
1549      "var base = {};"
1550      "var live = [];"
1551      "for (var i = 0; i < 10; i++) {"
1552      "  var object = {};"
1553      "  var prototype = {};"
1554      "  object.__proto__ = prototype;"
1555      "  if (i >= 3) live.push(object, prototype);"
1556      "}");
1557
1558  Handle<JSObject> baseObject =
1559      v8::Utils::OpenHandle(
1560          *v8::Handle<v8::Object>::Cast(
1561              v8::Context::GetCurrent()->Global()->Get(v8_str("base"))));
1562
1563  // Verify that only dead prototype transitions are cleared.
1564  CHECK_EQ(10, baseObject->map()->NumberOfProtoTransitions());
1565  HEAP->CollectAllGarbage(Heap::kNoGCFlags);
1566  CHECK_EQ(10 - 3, baseObject->map()->NumberOfProtoTransitions());
1567
1568  // Verify that prototype transitions array was compacted.
1569  FixedArray* trans = baseObject->map()->prototype_transitions();
1570  for (int i = 0; i < 10 - 3; i++) {
1571    int j = Map::kProtoTransitionHeaderSize +
1572        i * Map::kProtoTransitionElementsPerEntry;
1573    CHECK(trans->get(j + Map::kProtoTransitionMapOffset)->IsMap());
1574    CHECK(trans->get(j + Map::kProtoTransitionPrototypeOffset)->IsJSObject());
1575  }
1576
1577  // Make sure next prototype is placed on an old-space evacuation candidate.
1578  Handle<JSObject> prototype;
1579  PagedSpace* space = HEAP->old_pointer_space();
1580  do {
1581    prototype = FACTORY->NewJSArray(32 * KB, FAST_ELEMENTS, TENURED);
1582  } while (space->FirstPage() == space->LastPage() ||
1583      !space->LastPage()->Contains(prototype->address()));
1584
1585  // Add a prototype on an evacuation candidate and verify that transition
1586  // clearing correctly records slots in prototype transition array.
1587  i::FLAG_always_compact = true;
1588  Handle<Map> map(baseObject->map());
1589  CHECK(!space->LastPage()->Contains(map->prototype_transitions()->address()));
1590  CHECK(space->LastPage()->Contains(prototype->address()));
1591  baseObject->SetPrototype(*prototype, false)->ToObjectChecked();
1592  CHECK(map->GetPrototypeTransition(*prototype)->IsMap());
1593  HEAP->CollectAllGarbage(Heap::kNoGCFlags);
1594  CHECK(map->GetPrototypeTransition(*prototype)->IsMap());
1595}
1596
1597
1598TEST(ResetSharedFunctionInfoCountersDuringIncrementalMarking) {
1599  i::FLAG_allow_natives_syntax = true;
1600#ifdef DEBUG
1601  i::FLAG_verify_heap = true;
1602#endif
1603  InitializeVM();
1604  if (!i::V8::UseCrankshaft()) return;
1605  v8::HandleScope outer_scope;
1606
1607  {
1608    v8::HandleScope scope;
1609    CompileRun(
1610        "function f () {"
1611        "  var s = 0;"
1612        "  for (var i = 0; i < 100; i++)  s += i;"
1613        "  return s;"
1614        "}"
1615        "f(); f();"
1616        "%OptimizeFunctionOnNextCall(f);"
1617        "f();");
1618  }
1619  Handle<JSFunction> f =
1620      v8::Utils::OpenHandle(
1621          *v8::Handle<v8::Function>::Cast(
1622              v8::Context::GetCurrent()->Global()->Get(v8_str("f"))));
1623  CHECK(f->IsOptimized());
1624
1625  IncrementalMarking* marking = HEAP->incremental_marking();
1626  marking->Abort();
1627  marking->Start();
1628
1629  // The following two calls will increment HEAP->global_ic_age().
1630  const int kLongIdlePauseInMs = 1000;
1631  v8::V8::ContextDisposedNotification();
1632  v8::V8::IdleNotification(kLongIdlePauseInMs);
1633
1634  while (!marking->IsStopped() && !marking->IsComplete()) {
1635    marking->Step(1 * MB, IncrementalMarking::NO_GC_VIA_STACK_GUARD);
1636  }
1637
1638  CHECK_EQ(HEAP->global_ic_age(), f->shared()->ic_age());
1639  CHECK_EQ(0, f->shared()->opt_count());
1640  CHECK_EQ(0, f->shared()->code()->profiler_ticks());
1641}
1642
1643
1644TEST(ResetSharedFunctionInfoCountersDuringMarkSweep) {
1645  i::FLAG_allow_natives_syntax = true;
1646#ifdef DEBUG
1647  i::FLAG_verify_heap = true;
1648#endif
1649  InitializeVM();
1650  if (!i::V8::UseCrankshaft()) return;
1651  v8::HandleScope outer_scope;
1652
1653  {
1654    v8::HandleScope scope;
1655    CompileRun(
1656        "function f () {"
1657        "  var s = 0;"
1658        "  for (var i = 0; i < 100; i++)  s += i;"
1659        "  return s;"
1660        "}"
1661        "f(); f();"
1662        "%OptimizeFunctionOnNextCall(f);"
1663        "f();");
1664  }
1665  Handle<JSFunction> f =
1666      v8::Utils::OpenHandle(
1667          *v8::Handle<v8::Function>::Cast(
1668              v8::Context::GetCurrent()->Global()->Get(v8_str("f"))));
1669  CHECK(f->IsOptimized());
1670
1671  HEAP->incremental_marking()->Abort();
1672
1673  // The following two calls will increment HEAP->global_ic_age().
1674  // Since incremental marking is off, IdleNotification will do full GC.
1675  const int kLongIdlePauseInMs = 1000;
1676  v8::V8::ContextDisposedNotification();
1677  v8::V8::IdleNotification(kLongIdlePauseInMs);
1678
1679  CHECK_EQ(HEAP->global_ic_age(), f->shared()->ic_age());
1680  CHECK_EQ(0, f->shared()->opt_count());
1681  CHECK_EQ(0, f->shared()->code()->profiler_ticks());
1682}
1683