diff --git a/bin/NativeTests/JsRTApiTest.cpp b/bin/NativeTests/JsRTApiTest.cpp index 058fc0d6818..6b5b0d20459 100644 --- a/bin/NativeTests/JsRTApiTest.cpp +++ b/bin/NativeTests/JsRTApiTest.cpp @@ -252,6 +252,44 @@ namespace JsRTApiTest JsRTApiTest::RunWithAttributes(JsRTApiTest::DeleteObjectIndexedPropertyBug); } + void HasOwnItemTest(JsRuntimeAttributes attributes, JsRuntimeHandle runtime) + { + JsValueRef object; + REQUIRE(JsRunScript(_u("var obj = {a: [1,2], \"1\": 111}; obj.__proto__[3] = 333; obj;"), JS_SOURCE_CONTEXT_NONE, _u(""), &object) == JsNoError); + + JsPropertyIdRef idRef = JS_INVALID_REFERENCE; + JsValueRef result = JS_INVALID_REFERENCE; + // delete property "a" triggers PathTypeHandler -> SimpleDictionaryTypeHandler + REQUIRE(JsGetPropertyIdFromName(_u("a"), &idRef) == JsNoError); + REQUIRE(JsGetProperty(object, idRef, &result) == JsNoError); + bool hasOwnItem = false; + REQUIRE(JsHasOwnItem(result, 0, &hasOwnItem) == JsNoError); + CHECK(hasOwnItem); + + REQUIRE(JsHasOwnItem(result, 1, &hasOwnItem) == JsNoError); + CHECK(hasOwnItem); + + REQUIRE(JsHasOwnItem(result, 2, &hasOwnItem) == JsNoError); + CHECK(!hasOwnItem); // It does not have item on index 2 - so we should not be able to find that. + + REQUIRE(JsHasOwnItem(object, 1, &hasOwnItem) == JsNoError); + CHECK(hasOwnItem); + + REQUIRE(JsHasOwnItem(object, 3, &hasOwnItem) == JsNoError); + CHECK(!hasOwnItem); // index 3 is on prototype. + + bool has = false; + JsValueRef indexRef = JS_INVALID_REFERENCE; + REQUIRE(JsIntToNumber(3, &indexRef) == JsNoError); + REQUIRE(JsHasIndexedProperty(object, indexRef, &has) == JsNoError); + CHECK(has); // index 3 is prototype - so it should be able to find that. + } + + TEST_CASE("ApiTest_HasOwnItemTest", "[ApiTest]") + { + JsRTApiTest::RunWithAttributes(JsRTApiTest::HasOwnItemTest); + } + void CALLBACK ExternalObjectFinalizeCallback(void *data) { CHECK(data == (void *)0xdeadbeef); diff --git a/lib/Jsrt/ChakraCore.h b/lib/Jsrt/ChakraCore.h index 678a89c08cc..569e68afab5 100644 --- a/lib/Jsrt/ChakraCore.h +++ b/lib/Jsrt/ChakraCore.h @@ -961,6 +961,25 @@ CHAKRA_API _Out_ bool *hasOwnProperty); /// +/// Determines whether an object has a non-inherited property. +/// +/// +/// Requires an active script context. +/// +/// The object that may contain the item. +/// The index to find. +/// Whether the object has the non-inherited +/// property. +/// The code JsNoError if the operation succeeded, a failure code +/// otherwise. +/// +CHAKRA_API + JsHasOwnItem( + _In_ JsValueRef object, + _In_ uint32_t index, + _Out_ bool* hasOwnItem); + + /// /// Write JS string value into char string buffer without a null terminator /// /// diff --git a/lib/Jsrt/Core/JsrtCore.cpp b/lib/Jsrt/Core/JsrtCore.cpp index ffa66fe17bd..d7017990dea 100644 --- a/lib/Jsrt/Core/JsrtCore.cpp +++ b/lib/Jsrt/Core/JsrtCore.cpp @@ -516,6 +516,25 @@ JsExternalizeArrayBuffer( }); } +CHAKRA_API +JsHasOwnItem(_In_ JsValueRef object, + _In_ uint32_t index, + _Out_ bool* hasOwnItem) +{ + return ContextAPIWrapper( + [&](Js::ScriptContext* scriptContext, + TTDRecorder& _actionEntryPopper) -> JsErrorCode { + + VALIDATE_INCOMING_OBJECT(object, scriptContext); + PARAM_NOT_NULL(hasOwnItem); + + *hasOwnItem = !!Js::JavascriptOperators::HasOwnItem( + Js::VarTo(object), index); + + return JsNoError; + }); +} + CHAKRA_API JsDetachArrayBuffer( _In_ JsValueRef arrayBuffer) diff --git a/lib/Jsrt/JsrtCommonExports.inc b/lib/Jsrt/JsrtCommonExports.inc index 7f6a6dfbddc..a2cf4c62fd3 100644 --- a/lib/Jsrt/JsrtCommonExports.inc +++ b/lib/Jsrt/JsrtCommonExports.inc @@ -135,6 +135,7 @@ JsGetPropertyIdSymbolIterator JsGetWeakReferenceValue JsHasOwnProperty + JsHasOwnItem JsIsConstructor JsObjectDefineProperty JsObjectDefinePropertyFull