eb4d261e24
The different length string types was used to encode the string length and the hash in one field. This is now split into two fields one for length and one for hash. The hash field still encodes the array index of the string if it has one. If an array index is encoded in the hash field the string length is added to the top bits of the hash field to avoid a hash value of zero. On 32-bit this causes an additional 4 bytes to be used for all string objects. On 64-bit this will be half on average dur to pointer alignment. Review URL: http://codereview.chromium.org/436001 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@3350 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
797 lines
24 KiB
C++
797 lines
24 KiB
C++
// Copyright 2006-2008 the V8 project authors. All rights reserved.
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include "v8.h"
|
|
|
|
#include "execution.h"
|
|
#include "factory.h"
|
|
#include "macro-assembler.h"
|
|
#include "global-handles.h"
|
|
#include "cctest.h"
|
|
|
|
using namespace v8::internal;
|
|
|
|
static v8::Persistent<v8::Context> env;
|
|
|
|
static void InitializeVM() {
|
|
if (env.IsEmpty()) env = v8::Context::New();
|
|
v8::HandleScope scope;
|
|
env->Enter();
|
|
}
|
|
|
|
|
|
static void CheckMap(Map* map, int type, int instance_size) {
|
|
CHECK(map->IsHeapObject());
|
|
#ifdef DEBUG
|
|
CHECK(Heap::Contains(map));
|
|
#endif
|
|
CHECK_EQ(Heap::meta_map(), map->map());
|
|
CHECK_EQ(type, map->instance_type());
|
|
CHECK_EQ(instance_size, map->instance_size());
|
|
}
|
|
|
|
|
|
TEST(HeapMaps) {
|
|
InitializeVM();
|
|
CheckMap(Heap::meta_map(), MAP_TYPE, Map::kSize);
|
|
CheckMap(Heap::heap_number_map(), HEAP_NUMBER_TYPE, HeapNumber::kSize);
|
|
CheckMap(Heap::fixed_array_map(), FIXED_ARRAY_TYPE, FixedArray::kHeaderSize);
|
|
CheckMap(Heap::string_map(), STRING_TYPE, SeqTwoByteString::kAlignedSize);
|
|
}
|
|
|
|
|
|
static void CheckOddball(Object* obj, const char* string) {
|
|
CHECK(obj->IsOddball());
|
|
bool exc;
|
|
Object* print_string = *Execution::ToString(Handle<Object>(obj), &exc);
|
|
CHECK(String::cast(print_string)->IsEqualTo(CStrVector(string)));
|
|
}
|
|
|
|
|
|
static void CheckSmi(int value, const char* string) {
|
|
bool exc;
|
|
Object* print_string =
|
|
*Execution::ToString(Handle<Object>(Smi::FromInt(value)), &exc);
|
|
CHECK(String::cast(print_string)->IsEqualTo(CStrVector(string)));
|
|
}
|
|
|
|
|
|
static void CheckNumber(double value, const char* string) {
|
|
Object* obj = Heap::NumberFromDouble(value);
|
|
CHECK(obj->IsNumber());
|
|
bool exc;
|
|
Object* print_string = *Execution::ToString(Handle<Object>(obj), &exc);
|
|
CHECK(String::cast(print_string)->IsEqualTo(CStrVector(string)));
|
|
}
|
|
|
|
|
|
static void CheckFindCodeObject() {
|
|
// Test FindCodeObject
|
|
#define __ assm.
|
|
|
|
Assembler assm(NULL, 0);
|
|
|
|
__ nop(); // supported on all architectures
|
|
|
|
CodeDesc desc;
|
|
assm.GetCode(&desc);
|
|
Object* code = Heap::CreateCode(desc,
|
|
NULL,
|
|
Code::ComputeFlags(Code::STUB),
|
|
Handle<Object>(Heap::undefined_value()));
|
|
CHECK(code->IsCode());
|
|
|
|
HeapObject* obj = HeapObject::cast(code);
|
|
Address obj_addr = obj->address();
|
|
|
|
for (int i = 0; i < obj->Size(); i += kPointerSize) {
|
|
Object* found = Heap::FindCodeObject(obj_addr + i);
|
|
CHECK_EQ(code, found);
|
|
}
|
|
|
|
Object* copy = Heap::CreateCode(desc,
|
|
NULL,
|
|
Code::ComputeFlags(Code::STUB),
|
|
Handle<Object>(Heap::undefined_value()));
|
|
CHECK(copy->IsCode());
|
|
HeapObject* obj_copy = HeapObject::cast(copy);
|
|
Object* not_right = Heap::FindCodeObject(obj_copy->address() +
|
|
obj_copy->Size() / 2);
|
|
CHECK(not_right != code);
|
|
}
|
|
|
|
|
|
TEST(HeapObjects) {
|
|
InitializeVM();
|
|
|
|
v8::HandleScope sc;
|
|
Object* value = Heap::NumberFromDouble(1.000123);
|
|
CHECK(value->IsHeapNumber());
|
|
CHECK(value->IsNumber());
|
|
CHECK_EQ(1.000123, value->Number());
|
|
|
|
value = Heap::NumberFromDouble(1.0);
|
|
CHECK(value->IsSmi());
|
|
CHECK(value->IsNumber());
|
|
CHECK_EQ(1.0, value->Number());
|
|
|
|
value = Heap::NumberFromInt32(1024);
|
|
CHECK(value->IsSmi());
|
|
CHECK(value->IsNumber());
|
|
CHECK_EQ(1024.0, value->Number());
|
|
|
|
value = Heap::NumberFromInt32(Smi::kMinValue);
|
|
CHECK(value->IsSmi());
|
|
CHECK(value->IsNumber());
|
|
CHECK_EQ(Smi::kMinValue, Smi::cast(value)->value());
|
|
|
|
value = Heap::NumberFromInt32(Smi::kMaxValue);
|
|
CHECK(value->IsSmi());
|
|
CHECK(value->IsNumber());
|
|
CHECK_EQ(Smi::kMaxValue, Smi::cast(value)->value());
|
|
|
|
#ifndef V8_TARGET_ARCH_X64
|
|
// TODO(lrn): We need a NumberFromIntptr function in order to test this.
|
|
value = Heap::NumberFromInt32(Smi::kMinValue - 1);
|
|
CHECK(value->IsHeapNumber());
|
|
CHECK(value->IsNumber());
|
|
CHECK_EQ(static_cast<double>(Smi::kMinValue - 1), value->Number());
|
|
#endif
|
|
|
|
value = Heap::NumberFromUint32(static_cast<uint32_t>(Smi::kMaxValue) + 1);
|
|
CHECK(value->IsHeapNumber());
|
|
CHECK(value->IsNumber());
|
|
CHECK_EQ(static_cast<double>(static_cast<uint32_t>(Smi::kMaxValue) + 1),
|
|
value->Number());
|
|
|
|
// nan oddball checks
|
|
CHECK(Heap::nan_value()->IsNumber());
|
|
CHECK(isnan(Heap::nan_value()->Number()));
|
|
|
|
Object* str = Heap::AllocateStringFromAscii(CStrVector("fisk hest "));
|
|
if (!str->IsFailure()) {
|
|
String* s = String::cast(str);
|
|
CHECK(s->IsString());
|
|
CHECK_EQ(10, s->length());
|
|
} else {
|
|
CHECK(false);
|
|
}
|
|
|
|
String* object_symbol = String::cast(Heap::Object_symbol());
|
|
CHECK(Top::context()->global()->HasLocalProperty(object_symbol));
|
|
|
|
// Check ToString for oddballs
|
|
CheckOddball(Heap::true_value(), "true");
|
|
CheckOddball(Heap::false_value(), "false");
|
|
CheckOddball(Heap::null_value(), "null");
|
|
CheckOddball(Heap::undefined_value(), "undefined");
|
|
|
|
// Check ToString for Smis
|
|
CheckSmi(0, "0");
|
|
CheckSmi(42, "42");
|
|
CheckSmi(-42, "-42");
|
|
|
|
// Check ToString for Numbers
|
|
CheckNumber(1.1, "1.1");
|
|
|
|
CheckFindCodeObject();
|
|
}
|
|
|
|
|
|
TEST(Tagging) {
|
|
InitializeVM();
|
|
int request = 24;
|
|
CHECK_EQ(request, static_cast<int>(OBJECT_SIZE_ALIGN(request)));
|
|
CHECK(Smi::FromInt(42)->IsSmi());
|
|
CHECK(Failure::RetryAfterGC(request, NEW_SPACE)->IsFailure());
|
|
CHECK_EQ(request, Failure::RetryAfterGC(request, NEW_SPACE)->requested());
|
|
CHECK_EQ(NEW_SPACE,
|
|
Failure::RetryAfterGC(request, NEW_SPACE)->allocation_space());
|
|
CHECK_EQ(OLD_POINTER_SPACE,
|
|
Failure::RetryAfterGC(request,
|
|
OLD_POINTER_SPACE)->allocation_space());
|
|
CHECK(Failure::Exception()->IsFailure());
|
|
CHECK(Smi::FromInt(Smi::kMinValue)->IsSmi());
|
|
CHECK(Smi::FromInt(Smi::kMaxValue)->IsSmi());
|
|
}
|
|
|
|
|
|
TEST(GarbageCollection) {
|
|
InitializeVM();
|
|
|
|
v8::HandleScope sc;
|
|
// check GC when heap is empty
|
|
int free_bytes = Heap::MaxObjectSizeInPagedSpace();
|
|
CHECK(Heap::CollectGarbage(free_bytes, NEW_SPACE));
|
|
|
|
// allocate a function and keep it in global object's property
|
|
String* func_name = String::cast(Heap::LookupAsciiSymbol("theFunction"));
|
|
SharedFunctionInfo* function_share =
|
|
SharedFunctionInfo::cast(Heap::AllocateSharedFunctionInfo(func_name));
|
|
JSFunction* function =
|
|
JSFunction::cast(Heap::AllocateFunction(*Top::function_map(),
|
|
function_share,
|
|
Heap::undefined_value()));
|
|
Map* initial_map =
|
|
Map::cast(Heap::AllocateMap(JS_OBJECT_TYPE, JSObject::kHeaderSize));
|
|
function->set_initial_map(initial_map);
|
|
Top::context()->global()->SetProperty(func_name, function, NONE);
|
|
|
|
// allocate an object, but it is unrooted
|
|
String* prop_name = String::cast(Heap::LookupAsciiSymbol("theSlot"));
|
|
String* prop_namex = String::cast(Heap::LookupAsciiSymbol("theSlotx"));
|
|
JSObject* obj = JSObject::cast(Heap::AllocateJSObject(function));
|
|
obj->SetProperty(prop_name, Smi::FromInt(23), NONE);
|
|
obj->SetProperty(prop_namex, Smi::FromInt(24), NONE);
|
|
|
|
CHECK_EQ(Smi::FromInt(23), obj->GetProperty(prop_name));
|
|
CHECK_EQ(Smi::FromInt(24), obj->GetProperty(prop_namex));
|
|
|
|
CHECK(Heap::CollectGarbage(free_bytes, NEW_SPACE));
|
|
|
|
// function should be alive, func_name might be invalid after GC
|
|
func_name = String::cast(Heap::LookupAsciiSymbol("theFunction"));
|
|
CHECK(Top::context()->global()->HasLocalProperty(func_name));
|
|
// check function is retained
|
|
Object* func_value = Top::context()->global()->GetProperty(func_name);
|
|
CHECK(func_value->IsJSFunction());
|
|
// old function pointer may not be valid
|
|
function = JSFunction::cast(func_value);
|
|
|
|
// allocate another object, make it reachable from global
|
|
obj = JSObject::cast(Heap::AllocateJSObject(function));
|
|
String* obj_name = String::cast(Heap::LookupAsciiSymbol("theObject"));
|
|
Top::context()->global()->SetProperty(obj_name, obj, NONE);
|
|
// set property
|
|
prop_name = String::cast(Heap::LookupAsciiSymbol("theSlot"));
|
|
obj->SetProperty(prop_name, Smi::FromInt(23), NONE);
|
|
|
|
// after gc, it should survive
|
|
CHECK(Heap::CollectGarbage(free_bytes, NEW_SPACE));
|
|
|
|
obj_name = String::cast(Heap::LookupAsciiSymbol("theObject"));
|
|
CHECK(Top::context()->global()->HasLocalProperty(obj_name));
|
|
CHECK(Top::context()->global()->GetProperty(obj_name)->IsJSObject());
|
|
obj = JSObject::cast(Top::context()->global()->GetProperty(obj_name));
|
|
prop_name = String::cast(Heap::LookupAsciiSymbol("theSlot"));
|
|
CHECK_EQ(Smi::FromInt(23), obj->GetProperty(prop_name));
|
|
}
|
|
|
|
|
|
static void VerifyStringAllocation(const char* string) {
|
|
String* s = String::cast(Heap::AllocateStringFromUtf8(CStrVector(string)));
|
|
CHECK_EQ(StrLength(string), s->length());
|
|
for (int index = 0; index < s->length(); index++) {
|
|
CHECK_EQ(static_cast<uint16_t>(string[index]), s->Get(index)); }
|
|
}
|
|
|
|
|
|
TEST(String) {
|
|
InitializeVM();
|
|
|
|
VerifyStringAllocation("a");
|
|
VerifyStringAllocation("ab");
|
|
VerifyStringAllocation("abc");
|
|
VerifyStringAllocation("abcd");
|
|
VerifyStringAllocation("fiskerdrengen er paa havet");
|
|
}
|
|
|
|
|
|
TEST(LocalHandles) {
|
|
InitializeVM();
|
|
|
|
v8::HandleScope scope;
|
|
const char* name = "Kasper the spunky";
|
|
Handle<String> string = Factory::NewStringFromAscii(CStrVector(name));
|
|
CHECK_EQ(StrLength(name), string->length());
|
|
}
|
|
|
|
|
|
TEST(GlobalHandles) {
|
|
InitializeVM();
|
|
|
|
Object* i = Heap::AllocateStringFromAscii(CStrVector("fisk"));
|
|
Object* u = Heap::AllocateHeapNumber(1.12344);
|
|
|
|
Handle<Object> h1 = GlobalHandles::Create(i);
|
|
Handle<Object> h2 = GlobalHandles::Create(u);
|
|
Handle<Object> h3 = GlobalHandles::Create(i);
|
|
Handle<Object> h4 = GlobalHandles::Create(u);
|
|
|
|
// after gc, it should survive
|
|
CHECK(Heap::CollectGarbage(0, NEW_SPACE));
|
|
|
|
CHECK((*h1)->IsString());
|
|
CHECK((*h2)->IsHeapNumber());
|
|
CHECK((*h3)->IsString());
|
|
CHECK((*h4)->IsHeapNumber());
|
|
|
|
CHECK_EQ(*h3, *h1);
|
|
GlobalHandles::Destroy(h1.location());
|
|
GlobalHandles::Destroy(h3.location());
|
|
|
|
CHECK_EQ(*h4, *h2);
|
|
GlobalHandles::Destroy(h2.location());
|
|
GlobalHandles::Destroy(h4.location());
|
|
}
|
|
|
|
|
|
static bool WeakPointerCleared = false;
|
|
|
|
static void TestWeakGlobalHandleCallback(v8::Persistent<v8::Value> handle,
|
|
void* id) {
|
|
USE(handle);
|
|
if (1234 == reinterpret_cast<intptr_t>(id)) WeakPointerCleared = true;
|
|
}
|
|
|
|
|
|
TEST(WeakGlobalHandlesScavenge) {
|
|
InitializeVM();
|
|
|
|
WeakPointerCleared = false;
|
|
|
|
Object* i = Heap::AllocateStringFromAscii(CStrVector("fisk"));
|
|
Object* u = Heap::AllocateHeapNumber(1.12344);
|
|
|
|
Handle<Object> h1 = GlobalHandles::Create(i);
|
|
Handle<Object> h2 = GlobalHandles::Create(u);
|
|
|
|
GlobalHandles::MakeWeak(h2.location(),
|
|
reinterpret_cast<void*>(1234),
|
|
&TestWeakGlobalHandleCallback);
|
|
|
|
// Scavenge treats weak pointers as normal roots.
|
|
Heap::PerformScavenge();
|
|
|
|
CHECK((*h1)->IsString());
|
|
CHECK((*h2)->IsHeapNumber());
|
|
|
|
CHECK(!WeakPointerCleared);
|
|
CHECK(!GlobalHandles::IsNearDeath(h2.location()));
|
|
CHECK(!GlobalHandles::IsNearDeath(h1.location()));
|
|
|
|
GlobalHandles::Destroy(h1.location());
|
|
GlobalHandles::Destroy(h2.location());
|
|
}
|
|
|
|
|
|
TEST(WeakGlobalHandlesMark) {
|
|
InitializeVM();
|
|
|
|
WeakPointerCleared = false;
|
|
|
|
Object* i = Heap::AllocateStringFromAscii(CStrVector("fisk"));
|
|
Object* u = Heap::AllocateHeapNumber(1.12344);
|
|
|
|
Handle<Object> h1 = GlobalHandles::Create(i);
|
|
Handle<Object> h2 = GlobalHandles::Create(u);
|
|
|
|
CHECK(Heap::CollectGarbage(0, OLD_POINTER_SPACE));
|
|
CHECK(Heap::CollectGarbage(0, NEW_SPACE));
|
|
// Make sure the object is promoted.
|
|
|
|
GlobalHandles::MakeWeak(h2.location(),
|
|
reinterpret_cast<void*>(1234),
|
|
&TestWeakGlobalHandleCallback);
|
|
CHECK(!GlobalHandles::IsNearDeath(h1.location()));
|
|
CHECK(!GlobalHandles::IsNearDeath(h2.location()));
|
|
|
|
CHECK(Heap::CollectGarbage(0, OLD_POINTER_SPACE));
|
|
|
|
CHECK((*h1)->IsString());
|
|
|
|
CHECK(WeakPointerCleared);
|
|
CHECK(!GlobalHandles::IsNearDeath(h1.location()));
|
|
CHECK(GlobalHandles::IsNearDeath(h2.location()));
|
|
|
|
GlobalHandles::Destroy(h1.location());
|
|
GlobalHandles::Destroy(h2.location());
|
|
}
|
|
|
|
static void TestDeleteWeakGlobalHandleCallback(
|
|
v8::Persistent<v8::Value> handle,
|
|
void* id) {
|
|
if (1234 == reinterpret_cast<intptr_t>(id)) WeakPointerCleared = true;
|
|
handle.Dispose();
|
|
}
|
|
|
|
TEST(DeleteWeakGlobalHandle) {
|
|
InitializeVM();
|
|
|
|
WeakPointerCleared = false;
|
|
|
|
Object* i = Heap::AllocateStringFromAscii(CStrVector("fisk"));
|
|
Handle<Object> h = GlobalHandles::Create(i);
|
|
|
|
GlobalHandles::MakeWeak(h.location(),
|
|
reinterpret_cast<void*>(1234),
|
|
&TestDeleteWeakGlobalHandleCallback);
|
|
|
|
// Scanvenge does not recognize weak reference.
|
|
Heap::PerformScavenge();
|
|
|
|
CHECK(!WeakPointerCleared);
|
|
|
|
// Mark-compact treats weak reference properly.
|
|
CHECK(Heap::CollectGarbage(0, OLD_POINTER_SPACE));
|
|
|
|
CHECK(WeakPointerCleared);
|
|
}
|
|
|
|
static const char* not_so_random_string_table[] = {
|
|
"abstract",
|
|
"boolean",
|
|
"break",
|
|
"byte",
|
|
"case",
|
|
"catch",
|
|
"char",
|
|
"class",
|
|
"const",
|
|
"continue",
|
|
"debugger",
|
|
"default",
|
|
"delete",
|
|
"do",
|
|
"double",
|
|
"else",
|
|
"enum",
|
|
"export",
|
|
"extends",
|
|
"false",
|
|
"final",
|
|
"finally",
|
|
"float",
|
|
"for",
|
|
"function",
|
|
"goto",
|
|
"if",
|
|
"implements",
|
|
"import",
|
|
"in",
|
|
"instanceof",
|
|
"int",
|
|
"interface",
|
|
"long",
|
|
"native",
|
|
"new",
|
|
"null",
|
|
"package",
|
|
"private",
|
|
"protected",
|
|
"public",
|
|
"return",
|
|
"short",
|
|
"static",
|
|
"super",
|
|
"switch",
|
|
"synchronized",
|
|
"this",
|
|
"throw",
|
|
"throws",
|
|
"transient",
|
|
"true",
|
|
"try",
|
|
"typeof",
|
|
"var",
|
|
"void",
|
|
"volatile",
|
|
"while",
|
|
"with",
|
|
0
|
|
};
|
|
|
|
|
|
static void CheckSymbols(const char** strings) {
|
|
for (const char* string = *strings; *strings != 0; string = *strings++) {
|
|
Object* a = Heap::LookupAsciiSymbol(string);
|
|
// LookupAsciiSymbol may return a failure if a GC is needed.
|
|
if (a->IsFailure()) continue;
|
|
CHECK(a->IsSymbol());
|
|
Object* b = Heap::LookupAsciiSymbol(string);
|
|
if (b->IsFailure()) continue;
|
|
CHECK_EQ(b, a);
|
|
CHECK(String::cast(b)->IsEqualTo(CStrVector(string)));
|
|
}
|
|
}
|
|
|
|
|
|
TEST(SymbolTable) {
|
|
InitializeVM();
|
|
|
|
CheckSymbols(not_so_random_string_table);
|
|
CheckSymbols(not_so_random_string_table);
|
|
}
|
|
|
|
|
|
TEST(FunctionAllocation) {
|
|
InitializeVM();
|
|
|
|
v8::HandleScope sc;
|
|
String* name = String::cast(Heap::LookupAsciiSymbol("theFunction"));
|
|
SharedFunctionInfo* function_share =
|
|
SharedFunctionInfo::cast(Heap::AllocateSharedFunctionInfo(name));
|
|
JSFunction* function =
|
|
JSFunction::cast(Heap::AllocateFunction(*Top::function_map(),
|
|
function_share,
|
|
Heap::undefined_value()));
|
|
Map* initial_map =
|
|
Map::cast(Heap::AllocateMap(JS_OBJECT_TYPE, JSObject::kHeaderSize));
|
|
function->set_initial_map(initial_map);
|
|
|
|
String* prop_name = String::cast(Heap::LookupAsciiSymbol("theSlot"));
|
|
JSObject* obj = JSObject::cast(Heap::AllocateJSObject(function));
|
|
obj->SetProperty(prop_name, Smi::FromInt(23), NONE);
|
|
CHECK_EQ(Smi::FromInt(23), obj->GetProperty(prop_name));
|
|
// Check that we can add properties to function objects.
|
|
function->SetProperty(prop_name, Smi::FromInt(24), NONE);
|
|
CHECK_EQ(Smi::FromInt(24), function->GetProperty(prop_name));
|
|
}
|
|
|
|
|
|
TEST(ObjectProperties) {
|
|
InitializeVM();
|
|
|
|
v8::HandleScope sc;
|
|
JSFunction* constructor =
|
|
JSFunction::cast(
|
|
Top::context()->global()->GetProperty(String::cast(
|
|
Heap::Object_symbol())));
|
|
JSObject* obj = JSObject::cast(Heap::AllocateJSObject(constructor));
|
|
String* first = String::cast(Heap::LookupAsciiSymbol("first"));
|
|
String* second = String::cast(Heap::LookupAsciiSymbol("second"));
|
|
|
|
// check for empty
|
|
CHECK(!obj->HasLocalProperty(first));
|
|
|
|
// add first
|
|
obj->SetProperty(first, Smi::FromInt(1), NONE);
|
|
CHECK(obj->HasLocalProperty(first));
|
|
|
|
// delete first
|
|
CHECK(obj->DeleteProperty(first, JSObject::NORMAL_DELETION));
|
|
CHECK(!obj->HasLocalProperty(first));
|
|
|
|
// add first and then second
|
|
obj->SetProperty(first, Smi::FromInt(1), NONE);
|
|
obj->SetProperty(second, Smi::FromInt(2), NONE);
|
|
CHECK(obj->HasLocalProperty(first));
|
|
CHECK(obj->HasLocalProperty(second));
|
|
|
|
// delete first and then second
|
|
CHECK(obj->DeleteProperty(first, JSObject::NORMAL_DELETION));
|
|
CHECK(obj->HasLocalProperty(second));
|
|
CHECK(obj->DeleteProperty(second, JSObject::NORMAL_DELETION));
|
|
CHECK(!obj->HasLocalProperty(first));
|
|
CHECK(!obj->HasLocalProperty(second));
|
|
|
|
// add first and then second
|
|
obj->SetProperty(first, Smi::FromInt(1), NONE);
|
|
obj->SetProperty(second, Smi::FromInt(2), NONE);
|
|
CHECK(obj->HasLocalProperty(first));
|
|
CHECK(obj->HasLocalProperty(second));
|
|
|
|
// delete second and then first
|
|
CHECK(obj->DeleteProperty(second, JSObject::NORMAL_DELETION));
|
|
CHECK(obj->HasLocalProperty(first));
|
|
CHECK(obj->DeleteProperty(first, JSObject::NORMAL_DELETION));
|
|
CHECK(!obj->HasLocalProperty(first));
|
|
CHECK(!obj->HasLocalProperty(second));
|
|
|
|
// check string and symbol match
|
|
static const char* string1 = "fisk";
|
|
String* s1 =
|
|
String::cast(Heap::AllocateStringFromAscii(CStrVector(string1)));
|
|
obj->SetProperty(s1, Smi::FromInt(1), NONE);
|
|
CHECK(obj->HasLocalProperty(String::cast(Heap::LookupAsciiSymbol(string1))));
|
|
|
|
// check symbol and string match
|
|
static const char* string2 = "fugl";
|
|
String* s2 = String::cast(Heap::LookupAsciiSymbol(string2));
|
|
obj->SetProperty(s2, Smi::FromInt(1), NONE);
|
|
CHECK(obj->HasLocalProperty(
|
|
String::cast(Heap::AllocateStringFromAscii(CStrVector(string2)))));
|
|
}
|
|
|
|
|
|
TEST(JSObjectMaps) {
|
|
InitializeVM();
|
|
|
|
v8::HandleScope sc;
|
|
String* name = String::cast(Heap::LookupAsciiSymbol("theFunction"));
|
|
SharedFunctionInfo* function_share =
|
|
SharedFunctionInfo::cast(Heap::AllocateSharedFunctionInfo(name));
|
|
JSFunction* function =
|
|
JSFunction::cast(Heap::AllocateFunction(*Top::function_map(),
|
|
function_share,
|
|
Heap::undefined_value()));
|
|
Map* initial_map =
|
|
Map::cast(Heap::AllocateMap(JS_OBJECT_TYPE, JSObject::kHeaderSize));
|
|
function->set_initial_map(initial_map);
|
|
String* prop_name = String::cast(Heap::LookupAsciiSymbol("theSlot"));
|
|
JSObject* obj = JSObject::cast(Heap::AllocateJSObject(function));
|
|
|
|
// Set a propery
|
|
obj->SetProperty(prop_name, Smi::FromInt(23), NONE);
|
|
CHECK_EQ(Smi::FromInt(23), obj->GetProperty(prop_name));
|
|
|
|
// Check the map has changed
|
|
CHECK(initial_map != obj->map());
|
|
}
|
|
|
|
|
|
TEST(JSArray) {
|
|
InitializeVM();
|
|
|
|
v8::HandleScope sc;
|
|
String* name = String::cast(Heap::LookupAsciiSymbol("Array"));
|
|
JSFunction* function =
|
|
JSFunction::cast(Top::context()->global()->GetProperty(name));
|
|
|
|
// Allocate the object.
|
|
JSArray* array = JSArray::cast(Heap::AllocateJSObject(function));
|
|
array->Initialize(0);
|
|
|
|
// Set array length to 0.
|
|
array->SetElementsLength(Smi::FromInt(0));
|
|
CHECK_EQ(Smi::FromInt(0), array->length());
|
|
CHECK(array->HasFastElements()); // Must be in fast mode.
|
|
|
|
// array[length] = name.
|
|
array->SetElement(0, name);
|
|
CHECK_EQ(Smi::FromInt(1), array->length());
|
|
CHECK_EQ(array->GetElement(0), name);
|
|
|
|
// Set array length with larger than smi value.
|
|
Object* length =
|
|
Heap::NumberFromUint32(static_cast<uint32_t>(Smi::kMaxValue) + 1);
|
|
array->SetElementsLength(length);
|
|
|
|
uint32_t int_length = 0;
|
|
CHECK(Array::IndexFromObject(length, &int_length));
|
|
CHECK_EQ(length, array->length());
|
|
CHECK(array->HasDictionaryElements()); // Must be in slow mode.
|
|
|
|
// array[length] = name.
|
|
array->SetElement(int_length, name);
|
|
uint32_t new_int_length = 0;
|
|
CHECK(Array::IndexFromObject(array->length(), &new_int_length));
|
|
CHECK_EQ(static_cast<double>(int_length), new_int_length - 1);
|
|
CHECK_EQ(array->GetElement(int_length), name);
|
|
CHECK_EQ(array->GetElement(0), name);
|
|
}
|
|
|
|
|
|
TEST(JSObjectCopy) {
|
|
InitializeVM();
|
|
|
|
v8::HandleScope sc;
|
|
String* name = String::cast(Heap::Object_symbol());
|
|
JSFunction* constructor =
|
|
JSFunction::cast(Top::context()->global()->GetProperty(name));
|
|
JSObject* obj = JSObject::cast(Heap::AllocateJSObject(constructor));
|
|
String* first = String::cast(Heap::LookupAsciiSymbol("first"));
|
|
String* second = String::cast(Heap::LookupAsciiSymbol("second"));
|
|
|
|
obj->SetProperty(first, Smi::FromInt(1), NONE);
|
|
obj->SetProperty(second, Smi::FromInt(2), NONE);
|
|
|
|
obj->SetElement(0, first);
|
|
obj->SetElement(1, second);
|
|
|
|
// Make the clone.
|
|
JSObject* clone = JSObject::cast(Heap::CopyJSObject(obj));
|
|
CHECK(clone != obj);
|
|
|
|
CHECK_EQ(obj->GetElement(0), clone->GetElement(0));
|
|
CHECK_EQ(obj->GetElement(1), clone->GetElement(1));
|
|
|
|
CHECK_EQ(obj->GetProperty(first), clone->GetProperty(first));
|
|
CHECK_EQ(obj->GetProperty(second), clone->GetProperty(second));
|
|
|
|
// Flip the values.
|
|
clone->SetProperty(first, Smi::FromInt(2), NONE);
|
|
clone->SetProperty(second, Smi::FromInt(1), NONE);
|
|
|
|
clone->SetElement(0, second);
|
|
clone->SetElement(1, first);
|
|
|
|
CHECK_EQ(obj->GetElement(1), clone->GetElement(0));
|
|
CHECK_EQ(obj->GetElement(0), clone->GetElement(1));
|
|
|
|
CHECK_EQ(obj->GetProperty(second), clone->GetProperty(first));
|
|
CHECK_EQ(obj->GetProperty(first), clone->GetProperty(second));
|
|
}
|
|
|
|
|
|
TEST(StringAllocation) {
|
|
InitializeVM();
|
|
|
|
|
|
const unsigned char chars[] = { 0xe5, 0xa4, 0xa7 };
|
|
for (int length = 0; length < 100; length++) {
|
|
v8::HandleScope scope;
|
|
char* non_ascii = NewArray<char>(3 * length + 1);
|
|
char* ascii = NewArray<char>(length + 1);
|
|
non_ascii[3 * length] = 0;
|
|
ascii[length] = 0;
|
|
for (int i = 0; i < length; i++) {
|
|
ascii[i] = 'a';
|
|
non_ascii[3 * i] = chars[0];
|
|
non_ascii[3 * i + 1] = chars[1];
|
|
non_ascii[3 * i + 2] = chars[2];
|
|
}
|
|
Handle<String> non_ascii_sym =
|
|
Factory::LookupSymbol(Vector<const char>(non_ascii, 3 * length));
|
|
CHECK_EQ(length, non_ascii_sym->length());
|
|
Handle<String> ascii_sym =
|
|
Factory::LookupSymbol(Vector<const char>(ascii, length));
|
|
CHECK_EQ(length, ascii_sym->length());
|
|
Handle<String> non_ascii_str =
|
|
Factory::NewStringFromUtf8(Vector<const char>(non_ascii, 3 * length));
|
|
non_ascii_str->Hash();
|
|
CHECK_EQ(length, non_ascii_str->length());
|
|
Handle<String> ascii_str =
|
|
Factory::NewStringFromUtf8(Vector<const char>(ascii, length));
|
|
ascii_str->Hash();
|
|
CHECK_EQ(length, ascii_str->length());
|
|
DeleteArray(non_ascii);
|
|
DeleteArray(ascii);
|
|
}
|
|
}
|
|
|
|
|
|
static int ObjectsFoundInHeap(Handle<Object> objs[], int size) {
|
|
// Count the number of objects found in the heap.
|
|
int found_count = 0;
|
|
HeapIterator iterator;
|
|
while (iterator.has_next()) {
|
|
HeapObject* obj = iterator.next();
|
|
CHECK(obj != NULL);
|
|
for (int i = 0; i < size; i++) {
|
|
if (*objs[i] == obj) {
|
|
found_count++;
|
|
}
|
|
}
|
|
}
|
|
CHECK(!iterator.has_next());
|
|
return found_count;
|
|
}
|
|
|
|
|
|
TEST(Iteration) {
|
|
InitializeVM();
|
|
v8::HandleScope scope;
|
|
|
|
// Array of objects to scan haep for.
|
|
const int objs_count = 6;
|
|
Handle<Object> objs[objs_count];
|
|
int next_objs_index = 0;
|
|
|
|
// Allocate a JS array to OLD_POINTER_SPACE and NEW_SPACE
|
|
objs[next_objs_index++] = Factory::NewJSArray(10);
|
|
objs[next_objs_index++] = Factory::NewJSArray(10, TENURED);
|
|
|
|
// Allocate a small string to OLD_DATA_SPACE and NEW_SPACE
|
|
objs[next_objs_index++] =
|
|
Factory::NewStringFromAscii(CStrVector("abcdefghij"));
|
|
objs[next_objs_index++] =
|
|
Factory::NewStringFromAscii(CStrVector("abcdefghij"), TENURED);
|
|
|
|
// Allocate a large string (for large object space).
|
|
int large_size = Heap::MaxObjectSizeInPagedSpace() + 1;
|
|
char* str = new char[large_size];
|
|
for (int i = 0; i < large_size - 1; ++i) str[i] = 'a';
|
|
str[large_size - 1] = '\0';
|
|
objs[next_objs_index++] =
|
|
Factory::NewStringFromAscii(CStrVector(str), TENURED);
|
|
delete[] str;
|
|
|
|
// Add a Map object to look for.
|
|
objs[next_objs_index++] = Handle<Map>(HeapObject::cast(*objs[0])->map());
|
|
|
|
CHECK_EQ(objs_count, next_objs_index);
|
|
CHECK_EQ(objs_count, ObjectsFoundInHeap(objs, objs_count));
|
|
}
|