Skip to content

Commit

Permalink
Add wrenSetListElement, correctly allow negative indices on wrenGetLi…
Browse files Browse the repository at this point in the history
…stElement
  • Loading branch information
ruby0x1 committed Dec 3, 2020
1 parent 999acba commit 97ebcc7
Show file tree
Hide file tree
Showing 6 changed files with 86 additions and 3 deletions.
4 changes: 4 additions & 0 deletions src/include/wren.h
Expand Up @@ -484,6 +484,10 @@ int wrenGetListCount(WrenVM* vm, int slot);
// [elementSlot].
void wrenGetListElement(WrenVM* vm, int listSlot, int index, int elementSlot);

// Sets the value stored at [index] in the list at [listSlot],
// to the value from [elementSlot].
void wrenSetListElement(WrenVM* vm, int listSlot, int index, int elementSlot);

// Takes the value stored at [elementSlot] and inserts it into the list stored
// at [listSlot] at [index].
//
Expand Down
11 changes: 11 additions & 0 deletions src/vm/wren_utils.c
Expand Up @@ -194,3 +194,14 @@ int wrenPowerOf2Ceil(int n)

return n;
}

uint32_t wrenValidateIndex(uint32_t count, int64_t value)
{
// Negative indices count from the end.
if (value < 0) value = count + value;

// Check bounds.
if (value >= 0 && value < count) return value;

return UINT32_MAX;
}
5 changes: 5 additions & 0 deletions src/vm/wren_utils.h
Expand Up @@ -118,4 +118,9 @@ int wrenUtf8DecodeNumBytes(uint8_t byte);
// Returns the smallest power of two that is equal to or greater than [n].
int wrenPowerOf2Ceil(int n);

// Validates that [value] is within `[0, count)`. Also allows
// negative indices which map backwards from the end. Returns the valid positive
// index value. If invalid, returns `UINT32_MAX`.
uint32_t wrenValidateIndex(uint32_t count, int64_t value);

#endif
25 changes: 22 additions & 3 deletions src/vm/wren_vm.c
Expand Up @@ -1762,9 +1762,27 @@ void wrenGetListElement(WrenVM* vm, int listSlot, int index, int elementSlot)
validateApiSlot(vm, listSlot);
validateApiSlot(vm, elementSlot);
ASSERT(IS_LIST(vm->apiStack[listSlot]), "Slot must hold a list.");

ValueBuffer elements = AS_LIST(vm->apiStack[listSlot])->elements;
vm->apiStack[elementSlot] = elements.data[index];

uint32_t usedIndex = wrenValidateIndex(elements.count, index);
ASSERT(usedIndex != UINT32_MAX, "Index out of bounds.");

vm->apiStack[elementSlot] = elements.data[usedIndex];
}

void wrenSetListElement(WrenVM* vm, int listSlot, int index, int elementSlot)
{
validateApiSlot(vm, listSlot);
validateApiSlot(vm, elementSlot);
ASSERT(IS_LIST(vm->apiStack[listSlot]), "Slot must hold a list.");

ObjList* list = AS_LIST(vm->apiStack[listSlot]);

uint32_t usedIndex = wrenValidateIndex(list->elements.count, index);
ASSERT(usedIndex != UINT32_MAX, "Index out of bounds.");

list->elements.data[usedIndex] = vm->apiStack[elementSlot];
}

void wrenInsertInList(WrenVM* vm, int listSlot, int index, int elementSlot)
Expand All @@ -1775,7 +1793,8 @@ void wrenInsertInList(WrenVM* vm, int listSlot, int index, int elementSlot)

ObjList* list = AS_LIST(vm->apiStack[listSlot]);

// Negative indices count from the end.
// Negative indices count from the end.
// We don't use wrenValidateIndex here because insert allows 1 past the end.
if (index < 0) index = list->elements.count + 1 + index;

ASSERT(index <= list->elements.count, "Index out of bounds.");
Expand Down
38 changes: 38 additions & 0 deletions test/api/lists.c
Expand Up @@ -15,6 +15,14 @@ static void insertNumber(WrenVM* vm, int index, double value)
wrenInsertInList(vm, 0, index, 1);
}

// Helper function to append a double in a slot then insert it into the list at
// slot zero.
static void appendNumber(WrenVM* vm, double value)
{
wrenSetSlotDouble(vm, 1, value);
wrenInsertInList(vm, 0, -1, 1);
}

static void insert(WrenVM* vm)
{
wrenSetSlotNewList(vm, 0);
Expand All @@ -37,10 +45,40 @@ static void insert(WrenVM* vm)
insertNumber(vm, -3, 9.0);
}

static void get(WrenVM* vm)
{
int listSlot = 1;
int index = (int)wrenGetSlotDouble(vm, 2);

wrenGetListElement(vm, listSlot, index, 0);
}

static void set(WrenVM* vm)
{
wrenSetSlotNewList(vm, 0);

wrenEnsureSlots(vm, 2);

appendNumber(vm, 1.0);
appendNumber(vm, 2.0);
appendNumber(vm, 3.0);
appendNumber(vm, 4.0);

//list[2] = 33
wrenSetSlotDouble(vm, 1, 33);
wrenSetListElement(vm, 0, 2, 1);

//list[-1] = 44
wrenSetSlotDouble(vm, 1, 44);
wrenSetListElement(vm, 0, -1, 1);
}

WrenForeignMethodFn listsBindMethod(const char* signature)
{
if (strcmp(signature, "static Lists.newList()") == 0) return newList;
if (strcmp(signature, "static Lists.insert()") == 0) return insert;
if (strcmp(signature, "static Lists.set()") == 0) return set;
if (strcmp(signature, "static Lists.get(_,_)") == 0) return get;

return NULL;
}
6 changes: 6 additions & 0 deletions test/api/lists.wren
@@ -1,10 +1,16 @@
class Lists {
foreign static newList()
foreign static insert()
foreign static set()
foreign static get(list, index)
}

var list = Lists.newList()
System.print(list is List) // expect: true
System.print(list.count) // expect: 0

System.print(Lists.insert()) // expect: [4, 5, 6, 1, 2, 3, 9, 8, 7]

System.print(Lists.set()) // expect: [1, 2, 33, 44]
System.print(Lists.get([1,2,3,4], -2)) // expect: 3
System.print(Lists.get([1,2,3,4], 1)) // expect: 2

0 comments on commit 97ebcc7

Please sign in to comment.