Skip to content

Commit

Permalink
runtime: non-string object's keys
Browse files Browse the repository at this point in the history
  • Loading branch information
indutny committed Mar 18, 2012
1 parent a1fb475 commit 56a4085
Show file tree
Hide file tree
Showing 11 changed files with 130 additions and 20 deletions.
1 change: 1 addition & 0 deletions Makefile
Expand Up @@ -85,6 +85,7 @@ test: candor.a cand $(TESTS)
@test/test-gc
@./cand test/functional/basics.can
@./cand test/functional/arrays.can
@./cand test/functional/objects.can
@./cand test/functional/binary.can
@./cand test/functional/while.can

Expand Down
1 change: 0 additions & 1 deletion TODO
@@ -1,4 +1,3 @@
* non-strings as object's keys
* Optimize `while` benchmark (for non-optimized code)
* On-stack replacement and profile-based optimizations
* Incremental GC
Expand Down
4 changes: 2 additions & 2 deletions include/candor.h
Expand Up @@ -169,9 +169,9 @@ class Object : public Value {
public:
static Object* New();

void Set(String* key, Value* value);
void Set(Value* key, Value* value);
void Set(const char* key, Value* value);
Value* Get(String* key);
Value* Get(Value* key);
Value* Get(const char* key);

Array* Keys();
Expand Down
4 changes: 2 additions & 2 deletions src/api.cc
Expand Up @@ -340,7 +340,7 @@ Object* Object::New() {
}


void Object::Set(String* key, Value* value) {
void Object::Set(Value* key, Value* value) {
char** slot = HObject::LookupProperty(Isolate::GetCurrent()->heap,
addr(),
key->addr(),
Expand All @@ -349,7 +349,7 @@ void Object::Set(String* key, Value* value) {
}


Value* Object::Get(String* key) {
Value* Object::Get(Value* key) {
return Value::New(*HObject::LookupProperty(Isolate::GetCurrent()->heap,
addr(),
key->addr(),
Expand Down
2 changes: 1 addition & 1 deletion src/parser.cc
Expand Up @@ -485,7 +485,7 @@ AstNode* Parser::ParseObjectLiteral() {
key = new AstNode(AstNode::kProperty, Peek());
Skip();
} else if (Peek()->is(kNumber)) {
key = new AstNode(AstNode::kProperty, Peek());
key = new AstNode(AstNode::kNumber, Peek());
Skip();
} else {
SetError("Expected string or number as object literal's key");
Expand Down
84 changes: 76 additions & 8 deletions src/runtime.cc
Expand Up @@ -59,6 +59,46 @@ void RuntimeCollectGarbage(Heap* heap, char* stack_top) {
}


off_t RuntimeGetHash(Heap* heap, char* value) {
Heap::HeapTag tag = HValue::GetTag(value);

switch (tag) {
case Heap::kTagString:
return HString::Hash(value);
case Heap::kTagFunction:
case Heap::kTagObject:
case Heap::kTagArray:
case Heap::kTagCData:
return reinterpret_cast<off_t>(value);
case Heap::kTagNil:
return 0;
case Heap::kTagBoolean:
if (HBoolean::Value(value)) {
return 1;
} else {
return 0;
}
case Heap::kTagNumber:
{
int64_t intval;

if (HValue::IsUnboxed(value)) {
intval = HNumber::IntegralValue(value);
} else {
intval = HNumber::DoubleValue(value);
}

// And create new string
return ComputeHash(intval);
}
default:
UNEXPECTED
}

return 0;
}


off_t RuntimeLookupProperty(Heap* heap,
char* obj,
char* key,
Expand All @@ -72,22 +112,22 @@ off_t RuntimeLookupProperty(Heap* heap,

bool is_array = HValue::GetTag(obj) == Heap::kTagArray;

char* strkey;
char* keyptr;
int64_t numkey;
uint32_t hash;

if (is_array) {
numkey = HNumber::IntegralValue(RuntimeToNumber(heap, key));
strkey = reinterpret_cast<char*>(HNumber::Tag(numkey));
keyptr = reinterpret_cast<char*>(HNumber::Tag(numkey));
hash = ComputeHash(numkey);

// Update array's length on insertion (if increased)
if (insert && numkey >= 0 && HArray::Length(obj, false) <= numkey) {
HArray::SetLength(obj, numkey + 1);
}
} else {
strkey = RuntimeToString(heap, key);
hash = HString::Hash(strkey);
keyptr = key;
hash = RuntimeGetHash(heap, key);
}

// Dive into space and walk it in circular manner
Expand All @@ -100,9 +140,9 @@ off_t RuntimeLookupProperty(Heap* heap,
key_slot = *reinterpret_cast<char**>(space + index);
if (key_slot == HNil::New()) break;
if (is_array) {
if (key_slot == strkey) break;
if (key_slot == keyptr) break;
} else {
if (RuntimeStringCompare(key_slot, strkey) == 0) break;
if (RuntimeStrictCompare(key_slot, key) == 0) break;
}

index += 8;
Expand All @@ -114,11 +154,11 @@ off_t RuntimeLookupProperty(Heap* heap,
assert(insert);
RuntimeGrowObject(heap, obj);

return RuntimeLookupProperty(heap, obj, strkey, insert);
return RuntimeLookupProperty(heap, obj, keyptr, insert);
}

if (insert) {
*reinterpret_cast<char**>(space + index) = strkey;
*reinterpret_cast<char**>(space + index) = keyptr;
}

return HMap::space_offset + index + (mask + 8);
Expand Down Expand Up @@ -277,6 +317,34 @@ char* RuntimeToBoolean(Heap* heap, char* value) {
}


size_t RuntimeStrictCompare(char* lhs, char* rhs) {
Heap::HeapTag tag = HValue::GetTag(lhs);
Heap::HeapTag rtag = HValue::GetTag(rhs);

// We can only compare objects with equal type
if (rtag != tag) return -1;

switch (tag) {
case Heap::kTagString:
return RuntimeStringCompare(lhs, rhs);
case Heap::kTagFunction:
case Heap::kTagObject:
case Heap::kTagArray:
case Heap::kTagCData:
case Heap::kTagNil:
return lhs == rhs ? 0 : -1;
case Heap::kTagBoolean:
return HBoolean::Value(lhs) == HBoolean::Value(rhs) ? 0 : -1;
case Heap::kTagNumber:
return HNumber::DoubleValue(lhs) == HNumber::DoubleValue(rhs) ? 0 : -1;
default:
UNEXPECTED
}

return 0;
}


size_t RuntimeStringCompare(char* lhs, char* rhs) {
uint32_t lhs_length = HString::Length(lhs);
uint32_t rhs_length = HString::Length(rhs);
Expand Down
6 changes: 5 additions & 1 deletion src/runtime.h
Expand Up @@ -19,6 +19,9 @@ char* RuntimeAllocate(Heap* heap, uint32_t bytes);
typedef void (*RuntimeCollectGarbageCallback)(Heap* heap, char* stack_top);
void RuntimeCollectGarbage(Heap* heap, char* stack_top);

typedef off_t (*RuntimeGetHashCallback)(Heap* heap, char* value);
off_t RuntimeGetHash(Heap* heap, char* value);

// Performs lookup into a hashmap
// if insert=1 - inserts key into map space
typedef off_t (*RuntimeLookupPropertyCallback)(Heap* heap,
Expand All @@ -38,7 +41,8 @@ char* RuntimeToString(Heap* heap, char* value);
char* RuntimeToNumber(Heap* heap, char* value);
char* RuntimeToBoolean(Heap* heap, char* value);

typedef size_t (*RuntimeStringCompareCallback)(char* lhs, char* rhs);
typedef size_t (*RuntimeCompareCallback)(char* lhs, char* rhs);
size_t RuntimeStrictCompare(char* lhs, char* rhs);
size_t RuntimeStringCompare(char* lhs, char* rhs);

char* RuntimeConcatenateStrings(Heap* heap, char* lhs, char* rhs);
Expand Down
15 changes: 15 additions & 0 deletions test/functional/objects.can
@@ -0,0 +1,15 @@
print = global.print
assert = global.assert

print('-- can: objects --')

a = { i_am_key: true }

obj = {}
obj[a] = 1;

assert(obj[a] == 1, "non-string and non-number key")

keys = keysof obj
assert(sizeof keys == 1, "keysof should work")
assert(keys[0] === a, "key should be the object")
22 changes: 22 additions & 0 deletions test/functional/while.can
@@ -0,0 +1,22 @@
print = global.print
assert = global.assert

print('-- can: while --')

i = 100
j = 0
while (i--) {
if (i % 2) continue
j++
}

assert(j == 50, "continue")

i = 100
j = 0
while (i--) {
if (i < 50) break
j++
}

assert(j == 50, "break")
9 changes: 5 additions & 4 deletions test/test-functional.cc
Expand Up @@ -213,13 +213,14 @@ TEST_START("functional test")
})

// Numeric keys
FUN_TEST("a = { 1: 2, 2: 3}\nreturn a[1] + a[2] + a['1'] + a['2']", {
assert(result->As<Number>()->Value() == 10);
FUN_TEST("a = { 1: 2, 2: 3, '1': 2, '2': 3}\n"
"return a[1] + a[2] + a['1'] + a['2'] + a[1.0] + a[2.0]", {
assert(result->As<Number>()->Value() == 15);
});

FUN_TEST("a = { 1.1: 2, 2.2: 3}\n"
"return a[1.1] + a[2.2] + a['1.1'] + a['2.2']", {
assert(result->As<Number>()->Value() == 10);
"return a[1.1] + a[2.2]", {
assert(result->As<Number>()->Value() == 5);
});

// Arrays
Expand Down
2 changes: 1 addition & 1 deletion test/test-parser.cc
Expand Up @@ -114,7 +114,7 @@ TEST_START("parser test")
"[kProperty x]:[1] [kProperty y]:[2]]]")
PARSER_TEST("a = { 1 : 1, 2 : 2 }",
"[kAssign [a] [kObjectLiteral "
"[kProperty 1]:[1] [kProperty 2]:[2]]]")
"[1]:[1] [2]:[2]]]")
PARSER_TEST("key() {\nreturn 'key'\n}\na = { key: 2 }\nreturn a.key",
"[kFunction [key] @[] [return [kString key]]] "
"[kAssign [a] [kObjectLiteral [kProperty key]:[2]]] "
Expand Down

0 comments on commit 56a4085

Please sign in to comment.