diff --git a/napi-inl.h b/napi-inl.h index a1d67ac17..162a60650 100644 --- a/napi-inl.h +++ b/napi-inl.h @@ -1305,6 +1305,14 @@ inline Object::PropertyLValue Object::operator [](uint32_t index) { return PropertyLValue(*this, index); } +inline Object::PropertyLValue Object::operator[](Value index) { + return PropertyLValue(*this, index); +} + +inline Object::PropertyLValue Object::operator[](Value index) const { + return PropertyLValue(*this, index); +} + inline MaybeOrValue Object::operator[](const char* utf8name) const { return Get(utf8name); } @@ -1529,6 +1537,83 @@ inline void Object::AddFinalizer(Finalizer finalizeCallback, } } +#ifdef NAPI_CPP_EXCEPTIONS +inline Object::const_iterator::const_iterator(const Object* object, + const Type type) { + _object = object; + _keys = object->GetPropertyNames(); + _index = type == Type::BEGIN ? 0 : _keys.Length(); +} + +inline Object::const_iterator Napi::Object::begin() const { + const_iterator it(this, Object::const_iterator::Type::BEGIN); + return it; +} + +inline Object::const_iterator Napi::Object::end() const { + const_iterator it(this, Object::const_iterator::Type::END); + return it; +} + +inline Object::const_iterator& Object::const_iterator::operator++() { + ++_index; + return *this; +} + +inline bool Object::const_iterator::operator==( + const const_iterator& other) const { + return _index == other._index; +} + +inline bool Object::const_iterator::operator!=( + const const_iterator& other) const { + return _index != other._index; +} + +inline std::pair> +Object::const_iterator::operator*() const { + Value key = _keys[_index]; + const PropertyLValue value = (*_object)[key]; + return {key, value}; +} + +inline Object::iterator::iterator(Object* object, const Type type) { + _object = object; + _keys = object->GetPropertyNames(); + _index = type == Type::BEGIN ? 0 : _keys.Length(); +} + +inline Object::iterator Napi::Object::begin() { + iterator it(this, Object::iterator::Type::BEGIN); + return it; +} + +inline Object::iterator Napi::Object::end() { + iterator it(this, Object::iterator::Type::END); + return it; +} + +inline Object::iterator& Object::iterator::operator++() { + ++_index; + return *this; +} + +inline bool Object::iterator::operator==(const iterator& other) const { + return _index == other._index; +} + +inline bool Object::iterator::operator!=(const iterator& other) const { + return _index != other._index; +} + +inline std::pair> +Object::iterator::operator*() { + Value key = _keys[_index]; + const PropertyLValue value = (*_object)[key]; + return {key, value}; +} +#endif // NAPI_CPP_EXCEPTIONS + #if NAPI_VERSION >= 8 inline MaybeOrValue Object::Freeze() { napi_status status = napi_object_freeze(_env, _value); diff --git a/napi.h b/napi.h index 8475bfc96..526f02d88 100644 --- a/napi.h +++ b/napi.h @@ -741,6 +741,14 @@ namespace Napi { uint32_t index /// Property / element index ); + /// Gets or sets an indexed property or array element. + PropertyLValue operator[](Value index /// Property / element index + ); + + /// Gets or sets an indexed property or array element. + PropertyLValue operator[](Value index /// Property / element index + ) const; + /// Gets a named property. MaybeOrValue operator[]( const char* utf8name ///< UTF-8 encoded null-terminated property name @@ -928,6 +936,21 @@ namespace Napi { inline void AddFinalizer(Finalizer finalizeCallback, T* data, Hint* finalizeHint); + +#ifdef NAPI_CPP_EXCEPTIONS + class const_iterator; + + inline const_iterator begin() const; + + inline const_iterator end() const; + + class iterator; + + inline iterator begin(); + + inline iterator end(); +#endif // NAPI_CPP_EXCEPTIONS + #if NAPI_VERSION >= 8 /// This operation can fail in case of Proxy.[[GetPrototypeOf]] calling into /// JavaScript. @@ -976,6 +999,54 @@ namespace Napi { uint32_t Length() const; }; +#ifdef NAPI_CPP_EXCEPTIONS + class Object::const_iterator { + private: + enum class Type { BEGIN, END }; + + inline const_iterator(const Object* object, const Type type); + + public: + inline const_iterator& operator++(); + + inline bool operator==(const const_iterator& other) const; + + inline bool operator!=(const const_iterator& other) const; + + inline std::pair> operator*() const; + + private: + const Napi::Object* _object; + Array _keys; + uint32_t _index; + + friend class Object; + }; + + class Object::iterator { + private: + enum class Type { BEGIN, END }; + + inline iterator(Object* object, const Type type); + + public: + inline iterator& operator++(); + + inline bool operator==(const iterator& other) const; + + inline bool operator!=(const iterator& other) const; + + inline std::pair> operator*(); + + private: + Napi::Object* _object; + Array _keys; + uint32_t _index; + + friend class Object; + }; +#endif // NAPI_CPP_EXCEPTIONS + /// A JavaScript array buffer value. class ArrayBuffer : public Object { public: diff --git a/test/object/object.cc b/test/object/object.cc index a73b8e354..95d1f1021 100644 --- a/test/object/object.cc +++ b/test/object/object.cc @@ -253,6 +253,19 @@ Value CreateObjectUsingMagic(const CallbackInfo& info) { return obj; } +#ifdef NAPI_CPP_EXCEPTIONS +Value Sum(const CallbackInfo& info) { + Object object = info[0].As(); + int64_t sum = 0; + + for (const auto& e : object) { + sum += static_cast(e.second).As().Int64Value(); + } + + return Number::New(info.Env(), sum); +} +#endif // NAPI_CPP_EXCEPTIONS + Value InstanceOf(const CallbackInfo& info) { Object obj = info[0].As(); Function constructor = info[1].As(); @@ -299,6 +312,9 @@ Object InitObject(Env env) { exports["hasPropertyWithCppStyleString"] = Function::New(env, HasPropertyWithCppStyleString); exports["createObjectUsingMagic"] = Function::New(env, CreateObjectUsingMagic); +#ifdef NAPI_CPP_EXCEPTIONS + exports["sum"] = Function::New(env, Sum); +#endif // NAPI_CPP_EXCEPTIONS exports["addFinalizer"] = Function::New(env, AddFinalizer); exports["addFinalizerWithHint"] = Function::New(env, AddFinalizerWithHint); diff --git a/test/object/object.js b/test/object/object.js index ef06ad2fc..3f3e9c1e3 100644 --- a/test/object/object.js +++ b/test/object/object.js @@ -156,4 +156,46 @@ function test(binding) { assert.strictEqual(binding.object.instanceOf({}, Ctor), false); assert.strictEqual(binding.object.instanceOf(null, Ctor), false); } + + if ('sum' in binding.object) { + { + const obj = { + '-forbid': -0x4B1D, + '-feedcode': -0xFEEDC0DE, + '+office': +0x0FF1CE, + '+forbid': +0x4B1D, + '+deadbeef': +0xDEADBEEF, + '+feedcode': +0xFEEDC0DE, + }; + + let sum = 0; + for (const key in obj) { + sum += obj[key]; + } + + assert.strictEqual(binding.object.sum(obj), sum); + } + + { + const obj = new Proxy({ + '-forbid': -0x4B1D, + '-feedcode': -0xFEEDC0DE, + '+office': +0x0FF1CE, + '+forbid': +0x4B1D, + '+deadbeef': +0xDEADBEEF, + '+feedcode': +0xFEEDC0DE, + }, { + getOwnPropertyDescriptor(target, p) { + throw new Error("getOwnPropertyDescriptor error"); + }, + ownKeys(target) { + throw new Error("ownKeys error"); + }, + }); + + assert.throws(() => { + binding.object.sum(obj); + }, /ownKeys error/); + } + } }