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