From bd39239f004810865aff78fad28fecf3666f7523 Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Fri, 28 Aug 2015 14:41:30 -0700 Subject: [PATCH] array mutation functions --- src/RJSArray.cpp | 97 ++++++++++++++++++++++++++++++++++++++++++++- src/RJSUtil.hpp | 6 +++ tests/ArrayTests.js | 40 ++++++++++++++++--- 3 files changed, 136 insertions(+), 7 deletions(-) diff --git a/src/RJSArray.cpp b/src/RJSArray.cpp index b32ad848..dfb3e67b 100644 --- a/src/RJSArray.cpp +++ b/src/RJSArray.cpp @@ -83,8 +83,97 @@ void ArrayPropertyNames(JSContextRef ctx, JSObjectRef object, JSPropertyNameAccu JSValueRef ArrayPush(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* jsException) { try { ObjectArray *array = RJSGetInternal(thisObject); - RJSValidateArgumentCount(argumentCount, 1); - array->link_view->add(RJSAccessor::to_object_index(ctx, array->realm, const_cast(arguments[0]), array->object_schema.name, false)); + RJSValidateArgumentCountIsAtLeast(argumentCount, 1); + for (size_t i = 0; i < argumentCount; i++) { + array->link_view->add(RJSAccessor::to_object_index(ctx, array->realm, const_cast(arguments[i]), array->object_schema.name, false)); + } + return JSValueMakeNumber(ctx, array->link_view->size()); + } + catch (std::exception &exp) { + if (jsException) { + *jsException = RJSMakeError(ctx, exp); + } + } + return NULL; +} + +JSValueRef ArrayPop(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* jsException) { + try { + ObjectArray *array = RJSGetInternal(thisObject); + RJSValidateArgumentCount(argumentCount, 0); + if (array->link_view->size() == 0) { + return JSValueMakeUndefined(ctx); + } + size_t index = array->link_view->size()-1; + JSValueRef obj = RJSObjectCreate(ctx, Object(array->realm, array->object_schema, array->get(array->link_view->size()-1))); + array->link_view->remove(index); + return obj; + } + catch (std::exception &exp) { + if (jsException) { + *jsException = RJSMakeError(ctx, exp); + } + } + return NULL; +} + +JSValueRef ArrayUnshift(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* jsException) { + try { + ObjectArray *array = RJSGetInternal(thisObject); + RJSValidateArgumentCountIsAtLeast(argumentCount, 1); + for (size_t i = 0; i < argumentCount; i++) { + array->link_view->insert(i, RJSAccessor::to_object_index(ctx, array->realm, const_cast(arguments[i]), array->object_schema.name, false)); + } + return JSValueMakeNumber(ctx, array->link_view->size()); + } + catch (std::exception &exp) { + if (jsException) { + *jsException = RJSMakeError(ctx, exp); + } + } + return NULL; +} + +JSValueRef ArrayShift(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* jsException) { + try { + ObjectArray *array = RJSGetInternal(thisObject); + RJSValidateArgumentCount(argumentCount, 0); + if (array->link_view->size() == 0) { + return JSValueMakeUndefined(ctx); + } + JSValueRef obj = RJSObjectCreate(ctx, Object(array->realm, array->object_schema, array->get(0))); + array->link_view->remove(0); + return obj; + } + catch (std::exception &exp) { + if (jsException) { + *jsException = RJSMakeError(ctx, exp); + } + } + return NULL; +} + +JSValueRef ArraySplice(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* jsException) { + try { + ObjectArray *array = RJSGetInternal(thisObject); + RJSValidateArgumentCountIsAtLeast(argumentCount, 2); + long index = RJSValidatedValueToNumber(ctx, arguments[0]); + if (index < 0) { + index = array->link_view->size() - index; + } + + long remove = RJSValidatedValueToNumber(ctx, arguments[1]); + if (index + remove >= array->link_view->size()) { + throw std::runtime_error("Attempting to slice elements beyond Array bounds."); + } + + while (remove-- > 0) { + array->link_view->remove(index); + } + for (size_t i = 2; i < argumentCount; i++) { + array->link_view->insert(index + i - 2, RJSAccessor::to_object_index(ctx, array->realm, const_cast(arguments[i]), array->object_schema.name, false)); + } + return JSValueMakeNumber(ctx, array->link_view->size()); } catch (std::exception &exp) { if (jsException) { @@ -101,6 +190,10 @@ JSObjectRef RJSArrayCreate(JSContextRef ctx, realm::ObjectArray *array) { JSClassRef RJSArrayClass() { const JSStaticFunction arrayFuncs[] = { {"push", ArrayPush, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, + {"pop", ArrayPop, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, + {"shift", ArrayShift, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, + {"unshift", ArrayUnshift, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, + {"splice", ArraySplice, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, {NULL, NULL}, }; static JSClassRef s_arrayClass = RJSCreateWrapperClass("RealmArray", ArrayGetProperty, NULL, arrayFuncs, NULL, ArrayPropertyNames); diff --git a/src/RJSUtil.hpp b/src/RJSUtil.hpp index e912c445..bf571158 100644 --- a/src/RJSUtil.hpp +++ b/src/RJSUtil.hpp @@ -66,6 +66,12 @@ inline void RJSValidateArgumentCount(size_t argumentCount, size_t expected) { } } +inline void RJSValidateArgumentCountIsAtLeast(size_t argumentCount, size_t expected) { + if (argumentCount < expected) { + throw std::invalid_argument("Invalid arguments"); + } +} + inline void RJSValidateArgumentRange(size_t argumentCount, size_t min, size_t max) { if (argumentCount < min || argumentCount > max) { throw std::invalid_argument("Invalid arguments"); diff --git a/tests/ArrayTests.js b/tests/ArrayTests.js index c4023300..8f9f5cb7 100644 --- a/tests/ArrayTests.js +++ b/tests/ArrayTests.js @@ -113,18 +113,48 @@ var ArrayTests = { TestCase.assertEqual(obj.arrayCol.length, 1); array = obj.arrayCol; - array.push([4]); + TestCase.assertEqual(array.push([4]), 2); TestCase.assertEqual(array.length, 2); TestCase.assertEqual(array[1].doubleCol, 4); - array.push(obj.objectCol); - TestCase.assertEqual(array.length, 3); + TestCase.assertEqual(array.push(obj.objectCol, obj.objectCol1), 4); + TestCase.assertEqual(array.length, 4); TestCase.assertEqual(array[2].doubleCol, 1); + TestCase.assertEqual(array[3].doubleCol, 2); + + TestCase.assertThrows(function() { + array.push(); + }); }); - TestCase.assertEqual(array.length, 3); + TestCase.assertEqual(array.length, 4); TestCase.assertThrows(function() { array.push([1]); }); - } + }, + + testUnshift: function() { + var realm = new Realm({schema: [LinkTypesObjectSchema, TestObjectSchema]}); + var array; + realm.write(function() { + var obj = realm.create('LinkTypesObject', [[1], [2], [[3]]]); + TestCase.assertEqual(obj.arrayCol.length, 1); + + array = obj.arrayCol; + TestCase.assertEqual(array.unshift([5]), 2); + TestCase.assertEqual(array.length, 2); + TestCase.assertEqual(array[0].doubleCol, 5); + + TestCase.assertEqual(array.unshift(obj.objectCol, obj.objectCol1), 4); + TestCase.assertEqual(array.length, 4); + TestCase.assertEqual(array[0].doubleCol, 1); + TestCase.assertEqual(array[1].doubleCol, 2); + }); + + TestCase.assertEqual(array.length, 4); + TestCase.assertThrows(function() { + array.unshift([1]); + }); + }, + };