Skip to content

Commit bd8f6e6

Browse files
RaisinTenmhdawson
authored andcommitted
src: add iterator for Object
Refs: #830 Signed-off-by: Darshan Sen <darshan.sen@postman.com>
1 parent d8fc7b8 commit bd8f6e6

File tree

5 files changed

+285
-0
lines changed

5 files changed

+285
-0
lines changed

doc/object.md

+42
Original file line numberDiff line numberDiff line change
@@ -288,5 +288,47 @@ Napi::Value Napi::Object::operator[] (uint32_t index) const;
288288

289289
Returns an indexed property or array element as a [`Napi::Value`](value.md).
290290

291+
## Iterator
292+
293+
Iterators expose an `std::pair<...>`, where the `first` property is a
294+
[`Napi::Value`](value.md) that holds the currently iterated key and the
295+
`second` property is a [`Napi::Object::PropertyLValue`](propertylvalue.md) that
296+
holds the currently iterated value. Iterators are only available if C++
297+
exceptions are enabled (by defining `NAPI_CPP_EXCEPTIONS` during the build).
298+
299+
### Constant Iterator
300+
301+
In constant iterators, the iterated values are immutable.
302+
303+
```cpp
304+
Value Sum(const CallbackInfo& info) {
305+
Object object = info[0].As<Object>();
306+
int64_t sum = 0;
307+
308+
for (const auto& e : object) {
309+
sum += static_cast<Value>(e.second).As<Number>().Int64Value();
310+
}
311+
312+
return Number::New(info.Env(), sum);
313+
}
314+
```
315+
316+
### Non Constant Iterator
317+
318+
In non constant iterators, the iterated values are mutable.
319+
320+
```cpp
321+
void Increment(const CallbackInfo& info) {
322+
Env env = info.Env();
323+
Object object = info[0].As<Object>();
324+
325+
for (auto e : object) {
326+
int64_t value = static_cast<Value>(e.second).As<Number>().Int64Value();
327+
++value;
328+
e.second = Napi::Number::New(env, value);
329+
}
330+
}
331+
```
332+
291333
[`Napi::Value`]: ./value.md
292334
[`Napi::Value::From`]: ./value.md#from

napi-inl.h

+85
Original file line numberDiff line numberDiff line change
@@ -1306,6 +1306,14 @@ inline Object::PropertyLValue<uint32_t> Object::operator [](uint32_t index) {
13061306
return PropertyLValue<uint32_t>(*this, index);
13071307
}
13081308

1309+
inline Object::PropertyLValue<Value> Object::operator[](Value index) {
1310+
return PropertyLValue<Value>(*this, index);
1311+
}
1312+
1313+
inline Object::PropertyLValue<Value> Object::operator[](Value index) const {
1314+
return PropertyLValue<Value>(*this, index);
1315+
}
1316+
13091317
inline MaybeOrValue<Value> Object::operator[](const char* utf8name) const {
13101318
return Get(utf8name);
13111319
}
@@ -1530,6 +1538,83 @@ inline void Object::AddFinalizer(Finalizer finalizeCallback,
15301538
}
15311539
}
15321540

1541+
#ifdef NAPI_CPP_EXCEPTIONS
1542+
inline Object::const_iterator::const_iterator(const Object* object,
1543+
const Type type) {
1544+
_object = object;
1545+
_keys = object->GetPropertyNames();
1546+
_index = type == Type::BEGIN ? 0 : _keys.Length();
1547+
}
1548+
1549+
inline Object::const_iterator Napi::Object::begin() const {
1550+
const_iterator it(this, Object::const_iterator::Type::BEGIN);
1551+
return it;
1552+
}
1553+
1554+
inline Object::const_iterator Napi::Object::end() const {
1555+
const_iterator it(this, Object::const_iterator::Type::END);
1556+
return it;
1557+
}
1558+
1559+
inline Object::const_iterator& Object::const_iterator::operator++() {
1560+
++_index;
1561+
return *this;
1562+
}
1563+
1564+
inline bool Object::const_iterator::operator==(
1565+
const const_iterator& other) const {
1566+
return _index == other._index;
1567+
}
1568+
1569+
inline bool Object::const_iterator::operator!=(
1570+
const const_iterator& other) const {
1571+
return _index != other._index;
1572+
}
1573+
1574+
inline const std::pair<Value, Object::PropertyLValue<Value>>
1575+
Object::const_iterator::operator*() const {
1576+
const Value key = _keys[_index];
1577+
const PropertyLValue<Value> value = (*_object)[key];
1578+
return {key, value};
1579+
}
1580+
1581+
inline Object::iterator::iterator(Object* object, const Type type) {
1582+
_object = object;
1583+
_keys = object->GetPropertyNames();
1584+
_index = type == Type::BEGIN ? 0 : _keys.Length();
1585+
}
1586+
1587+
inline Object::iterator Napi::Object::begin() {
1588+
iterator it(this, Object::iterator::Type::BEGIN);
1589+
return it;
1590+
}
1591+
1592+
inline Object::iterator Napi::Object::end() {
1593+
iterator it(this, Object::iterator::Type::END);
1594+
return it;
1595+
}
1596+
1597+
inline Object::iterator& Object::iterator::operator++() {
1598+
++_index;
1599+
return *this;
1600+
}
1601+
1602+
inline bool Object::iterator::operator==(const iterator& other) const {
1603+
return _index == other._index;
1604+
}
1605+
1606+
inline bool Object::iterator::operator!=(const iterator& other) const {
1607+
return _index != other._index;
1608+
}
1609+
1610+
inline std::pair<Value, Object::PropertyLValue<Value>>
1611+
Object::iterator::operator*() {
1612+
Value key = _keys[_index];
1613+
PropertyLValue<Value> value = (*_object)[key];
1614+
return {key, value};
1615+
}
1616+
#endif // NAPI_CPP_EXCEPTIONS
1617+
15331618
#if NAPI_VERSION >= 8
15341619
inline MaybeOrValue<bool> Object::Freeze() {
15351620
napi_status status = napi_object_freeze(_env, _value);

napi.h

+72
Original file line numberDiff line numberDiff line change
@@ -741,6 +741,14 @@ namespace Napi {
741741
uint32_t index /// Property / element index
742742
);
743743

744+
/// Gets or sets an indexed property or array element.
745+
PropertyLValue<Value> operator[](Value index /// Property / element index
746+
);
747+
748+
/// Gets or sets an indexed property or array element.
749+
PropertyLValue<Value> operator[](Value index /// Property / element index
750+
) const;
751+
744752
/// Gets a named property.
745753
MaybeOrValue<Value> operator[](
746754
const char* utf8name ///< UTF-8 encoded null-terminated property name
@@ -928,6 +936,21 @@ namespace Napi {
928936
inline void AddFinalizer(Finalizer finalizeCallback,
929937
T* data,
930938
Hint* finalizeHint);
939+
940+
#ifdef NAPI_CPP_EXCEPTIONS
941+
class const_iterator;
942+
943+
inline const_iterator begin() const;
944+
945+
inline const_iterator end() const;
946+
947+
class iterator;
948+
949+
inline iterator begin();
950+
951+
inline iterator end();
952+
#endif // NAPI_CPP_EXCEPTIONS
953+
931954
#if NAPI_VERSION >= 8
932955
/// This operation can fail in case of Proxy.[[GetPrototypeOf]] calling into
933956
/// JavaScript.
@@ -976,6 +999,55 @@ namespace Napi {
976999
uint32_t Length() const;
9771000
};
9781001

1002+
#ifdef NAPI_CPP_EXCEPTIONS
1003+
class Object::const_iterator {
1004+
private:
1005+
enum class Type { BEGIN, END };
1006+
1007+
inline const_iterator(const Object* object, const Type type);
1008+
1009+
public:
1010+
inline const_iterator& operator++();
1011+
1012+
inline bool operator==(const const_iterator& other) const;
1013+
1014+
inline bool operator!=(const const_iterator& other) const;
1015+
1016+
inline const std::pair<Value, Object::PropertyLValue<Value>> operator*()
1017+
const;
1018+
1019+
private:
1020+
const Napi::Object* _object;
1021+
Array _keys;
1022+
uint32_t _index;
1023+
1024+
friend class Object;
1025+
};
1026+
1027+
class Object::iterator {
1028+
private:
1029+
enum class Type { BEGIN, END };
1030+
1031+
inline iterator(Object* object, const Type type);
1032+
1033+
public:
1034+
inline iterator& operator++();
1035+
1036+
inline bool operator==(const iterator& other) const;
1037+
1038+
inline bool operator!=(const iterator& other) const;
1039+
1040+
inline std::pair<Value, Object::PropertyLValue<Value>> operator*();
1041+
1042+
private:
1043+
Napi::Object* _object;
1044+
Array _keys;
1045+
uint32_t _index;
1046+
1047+
friend class Object;
1048+
};
1049+
#endif // NAPI_CPP_EXCEPTIONS
1050+
9791051
/// A JavaScript array buffer value.
9801052
class ArrayBuffer : public Object {
9811053
public:

test/object/object.cc

+28
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,30 @@ Value CreateObjectUsingMagic(const CallbackInfo& info) {
253253
return obj;
254254
}
255255

256+
#ifdef NAPI_CPP_EXCEPTIONS
257+
Value Sum(const CallbackInfo& info) {
258+
Object object = info[0].As<Object>();
259+
int64_t sum = 0;
260+
261+
for (const auto& e : object) {
262+
sum += static_cast<Value>(e.second).As<Number>().Int64Value();
263+
}
264+
265+
return Number::New(info.Env(), sum);
266+
}
267+
268+
void Increment(const CallbackInfo& info) {
269+
Env env = info.Env();
270+
Object object = info[0].As<Object>();
271+
272+
for (auto e : object) {
273+
int64_t value = static_cast<Value>(e.second).As<Number>().Int64Value();
274+
++value;
275+
e.second = Napi::Number::New(env, value);
276+
}
277+
}
278+
#endif // NAPI_CPP_EXCEPTIONS
279+
256280
Value InstanceOf(const CallbackInfo& info) {
257281
Object obj = info[0].As<Object>();
258282
Function constructor = info[1].As<Function>();
@@ -299,6 +323,10 @@ Object InitObject(Env env) {
299323
exports["hasPropertyWithCppStyleString"] = Function::New(env, HasPropertyWithCppStyleString);
300324

301325
exports["createObjectUsingMagic"] = Function::New(env, CreateObjectUsingMagic);
326+
#ifdef NAPI_CPP_EXCEPTIONS
327+
exports["sum"] = Function::New(env, Sum);
328+
exports["increment"] = Function::New(env, Increment);
329+
#endif // NAPI_CPP_EXCEPTIONS
302330

303331
exports["addFinalizer"] = Function::New(env, AddFinalizer);
304332
exports["addFinalizerWithHint"] = Function::New(env, AddFinalizerWithHint);

test/object/object.js

+58
Original file line numberDiff line numberDiff line change
@@ -156,4 +156,62 @@ function test(binding) {
156156
assert.strictEqual(binding.object.instanceOf({}, Ctor), false);
157157
assert.strictEqual(binding.object.instanceOf(null, Ctor), false);
158158
}
159+
160+
if ('sum' in binding.object) {
161+
{
162+
const obj = {
163+
'-forbid': -0x4B1D,
164+
'-feedcode': -0xFEEDC0DE,
165+
'+office': +0x0FF1CE,
166+
'+forbid': +0x4B1D,
167+
'+deadbeef': +0xDEADBEEF,
168+
'+feedcode': +0xFEEDC0DE,
169+
};
170+
171+
let sum = 0;
172+
for (const key in obj) {
173+
sum += obj[key];
174+
}
175+
176+
assert.strictEqual(binding.object.sum(obj), sum);
177+
}
178+
179+
{
180+
const obj = new Proxy({
181+
'-forbid': -0x4B1D,
182+
'-feedcode': -0xFEEDC0DE,
183+
'+office': +0x0FF1CE,
184+
'+forbid': +0x4B1D,
185+
'+deadbeef': +0xDEADBEEF,
186+
'+feedcode': +0xFEEDC0DE,
187+
}, {
188+
getOwnPropertyDescriptor(target, p) {
189+
throw new Error("getOwnPropertyDescriptor error");
190+
},
191+
ownKeys(target) {
192+
throw new Error("ownKeys error");
193+
},
194+
});
195+
196+
assert.throws(() => {
197+
binding.object.sum(obj);
198+
}, /ownKeys error/);
199+
}
200+
}
201+
202+
if ('increment' in binding.object) {
203+
const obj = {
204+
'a': 0,
205+
'b': 1,
206+
'c': 2,
207+
};
208+
209+
binding.object.increment(obj);
210+
211+
assert.deepStrictEqual(obj, {
212+
'a': 1,
213+
'b': 2,
214+
'c': 3,
215+
});
216+
}
159217
}

0 commit comments

Comments
 (0)