From c803ffcbc015f0ebcb2c96b022dcc27ef3ebacac Mon Sep 17 00:00:00 2001 From: Oguz Bastemur Date: Wed, 4 Oct 2017 22:09:12 +0200 Subject: [PATCH] jsrt: JsObject Delete/Get/Has/OwnP../Set Property AcmeAIR LTO gain (3%) New JSRT property interface that API user can use without going through convert-to-property-id process. See # 3790 for details. Also added a TODO note; ``` TODO: Can we make PropertyString && LiteralStringWithPropertyStringPtr share the string buffer? ``` Once this PR is merged, there will be an additional PR on node-chakracore end to benefit this new interface. --- lib/Common/Codex/Utf8Helper.h | 60 ++- lib/Jsrt/ChakraCore.h | 113 +++++ lib/Jsrt/Jsrt.cpp | 424 +++++++++++++++--- lib/Runtime/Language/JavascriptConversion.cpp | 11 +- lib/Runtime/Language/JavascriptOperators.cpp | 17 +- lib/Runtime/Library/ConcatString.cpp | 128 +++++- lib/Runtime/Library/ConcatString.h | 14 +- lib/Runtime/Library/JavascriptLibrary.cpp | 2 +- lib/Runtime/Library/LiteralString.cpp | 1 - test/native-tests/test-property/Platform.js | 52 +++ test/native-tests/test-property/sample.cpp | 103 +++++ test/native-tests/test_native.sh | 3 + 12 files changed, 809 insertions(+), 119 deletions(-) create mode 100644 test/native-tests/test-property/Platform.js create mode 100644 test/native-tests/test-property/sample.cpp diff --git a/lib/Common/Codex/Utf8Helper.h b/lib/Common/Codex/Utf8Helper.h index c8cd8f86a0d..e6479679e48 100644 --- a/lib/Common/Codex/Utf8Helper.h +++ b/lib/Common/Codex/Utf8Helper.h @@ -83,31 +83,24 @@ namespace utf8 return WideStringToNarrow(Allocator::allocate, sourceString, sourceCount, destStringPtr, destCount, allocateCount); } - /// - /// Use the codex library to encode a UTF8 string to UTF16. - /// The caller is responsible for freeing the memory, which is allocated - /// using Allocator. - /// The returned string is null terminated. - /// - template - HRESULT NarrowStringToWide(_In_ AllocatorFunction allocator,_In_ LPCSTR sourceString, size_t sourceCount, _Out_ LPWSTR* destStringPtr, _Out_ size_t* destCount, size_t* allocateCount = nullptr) + inline HRESULT NarrowStringToWideNoAlloc(_In_ LPCSTR sourceString, size_t sourceCount, + __out_ecount(destBufferCount) LPWSTR destString, size_t destBufferCount, _Out_ size_t* destCount) { - size_t cbSourceString = sourceCount; size_t sourceStart = 0; - size_t cbDestString = (sourceCount + 1) * sizeof(WCHAR); - if (cbDestString < sourceCount) // overflow ? + size_t cbSourceString = sourceCount; + + if (sourceCount >= MAXUINT32) { + destString[0] = WCHAR(0); return E_OUTOFMEMORY; } - WCHAR* destString = (WCHAR*)allocator(cbDestString); - if (destString == nullptr) + if (destString == nullptr || sourceCount >= destBufferCount) { - return E_OUTOFMEMORY; + destString[0] = WCHAR(0); + return E_INVALIDARG; } - if (allocateCount != nullptr) *allocateCount = cbDestString; - for (; sourceStart < sourceCount; sourceStart++) { const char ch = sourceString[sourceStart]; @@ -124,7 +117,6 @@ namespace utf8 { *destCount = sourceCount; destString[sourceCount] = WCHAR(0); - *destStringPtr = destString; } else { @@ -139,14 +131,44 @@ namespace utf8 // instead of replacing them with the "replacement" chracter. Pass a flag to our // decoder to require such behavior utf8::DecodeUnitsIntoAndNullTerminateNoAdvance(remDestString, remSourceString, (LPCUTF8) sourceString + cbSourceString, DecodeOptions::doAllowInvalidWCHARs); - Assert(destString[cchDestString] == 0); + static_assert(sizeof(utf8char_t) == sizeof(char), "Needs to be valid for cast"); - *destStringPtr = destString; *destCount = cchDestString; } + + Assert(destString[*destCount] == 0); + return S_OK; } + /// + /// Use the codex library to encode a UTF8 string to UTF16. + /// The caller is responsible for freeing the memory, which is allocated + /// using Allocator. + /// The returned string is null terminated. + /// + template + HRESULT NarrowStringToWide(_In_ AllocatorFunction allocator,_In_ LPCSTR sourceString, + size_t sourceCount, _Out_ LPWSTR* destStringPtr, _Out_ size_t* destCount, size_t* allocateCount = nullptr) + { + size_t cbDestString = (sourceCount + 1) * sizeof(WCHAR); + if (cbDestString < sourceCount) // overflow ? + { + return E_OUTOFMEMORY; + } + + WCHAR* destString = (WCHAR*)allocator(cbDestString); + if (destString == nullptr) + { + return E_OUTOFMEMORY; + } + + if (allocateCount != nullptr) *allocateCount = cbDestString; + + *destStringPtr = destString; + return NarrowStringToWideNoAlloc(sourceString, sourceCount, destString, sourceCount + 1, destCount); + } + template HRESULT NarrowStringToWide(_In_ LPCSTR sourceString, size_t sourceCount, _Out_ LPWSTR* destStringPtr, _Out_ size_t* destCount, size_t* allocateCount = nullptr) { diff --git a/lib/Jsrt/ChakraCore.h b/lib/Jsrt/ChakraCore.h index f0680558a83..09aeb3226b9 100644 --- a/lib/Jsrt/ChakraCore.h +++ b/lib/Jsrt/ChakraCore.h @@ -745,5 +745,118 @@ CHAKRA_API _Out_opt_ unsigned int *byteOffset, _Out_opt_ unsigned int *byteLength); +/// +/// Gets an object's property. +/// +/// +/// Requires an active script context. +/// +/// The object that contains the property. +/// The key (JavascriptString) to the property. +/// The value of the property. +/// +/// The code JsNoError if the operation succeeded, a failure code otherwise. +/// +CHAKRA_API + JsObjectGetProperty( + _In_ JsValueRef object, + _In_ JsValueRef key, + _Out_ JsValueRef *value); + +/// +/// Puts an object's property. +/// +/// +/// Requires an active script context. +/// +/// The object that contains the property. +/// The key (JavascriptString) to the property. +/// The new value of the property. +/// The property set should follow strict mode rules. +/// +/// The code JsNoError if the operation succeeded, a failure code otherwise. +/// +CHAKRA_API + JsObjectSetProperty( + _In_ JsValueRef object, + _In_ JsValueRef key, + _In_ JsValueRef value, + _In_ bool useStrictRules); + +/// +/// Determines whether an object has a property. +/// +/// +/// Requires an active script context. +/// +/// The object that may contain the property. +/// The key (JavascriptString) to the property. +/// Whether the object (or a prototype) has the property. +/// +/// The code JsNoError if the operation succeeded, a failure code otherwise. +/// +CHAKRA_API + JsObjectHasProperty( + _In_ JsValueRef object, + _In_ JsValueRef key, + _Out_ bool *hasProperty); + +/// +/// Defines a new object's own property from a property descriptor. +/// +/// +/// Requires an active script context. +/// +/// The object that has the property. +/// The key (JavascriptString) to the property. +/// The property descriptor. +/// Whether the property was defined. +/// +/// The code JsNoError if the operation succeeded, a failure code otherwise. +/// +CHAKRA_API + JsObjectDefineProperty( + _In_ JsValueRef object, + _In_ JsValueRef key, + _In_ JsValueRef propertyDescriptor, + _Out_ bool *result); + +/// +/// Deletes an object's property. +/// +/// +/// Requires an active script context. +/// +/// The object that contains the property. +/// The key (JavascriptString) to the property. +/// The property set should follow strict mode rules. +/// Whether the property was deleted. +/// +/// The code JsNoError if the operation succeeded, a failure code otherwise. +/// +CHAKRA_API + JsObjectDeleteProperty( + _In_ JsValueRef object, + _In_ JsValueRef key, + _In_ bool useStrictRules, + _Out_ JsValueRef *result); + +/// +/// Gets a property descriptor for an object's own property. +/// +/// +/// Requires an active script context. +/// +/// The object that has the property. +/// The key (JavascriptString) to the property. +/// The property descriptor. +/// +/// The code JsNoError if the operation succeeded, a failure code otherwise. +/// +CHAKRA_API + JsObjectGetOwnPropertyDescriptor( + _In_ JsValueRef object, + _In_ JsValueRef key, + _Out_ JsValueRef *propertyDescriptor); #endif // _CHAKRACOREBUILD #endif // _CHAKRACORE_H_ diff --git a/lib/Jsrt/Jsrt.cpp b/lib/Jsrt/Jsrt.cpp index c80376942e3..f30be36b39c 100644 --- a/lib/Jsrt/Jsrt.cpp +++ b/lib/Jsrt/Jsrt.cpp @@ -1367,102 +1367,199 @@ CHAKRA_API JsPreventExtension(_In_ JsValueRef object) }); } +static JsErrorCode InternalGetPropertyRecord(Js::ScriptContext * scriptContext, JsValueRef key, _Out_ const Js::PropertyRecord ** propertyRecord) +{ + Assert(propertyRecord != nullptr); + *propertyRecord = nullptr; + + if (VirtualTableInfo::HasVirtualTable(key)) + { + Js::LiteralStringWithPropertyStringPtr * ptrStr = (Js::LiteralStringWithPropertyStringPtr*)key; + *propertyRecord = ptrStr->GetOrAddPropertyString()->GetPropertyRecord(); + } + else if (VirtualTableInfo::HasVirtualTable(key)) + { + Js::PropertyString * ptrStr = (Js::PropertyString*)key; + *propertyRecord = ptrStr->GetPropertyRecord(); + } + else if (Js::JavascriptString::Is(key)) + { + Js::JavascriptString * ptrStr = Js::JavascriptString::FromVar(key); + scriptContext->GetOrAddPropertyRecord(ptrStr->GetSz(), static_cast(ptrStr->GetLength()), + (Js::PropertyRecord const **)propertyRecord); + } + else + { + return JsErrorInvalidArgument; + } + + return JsNoError; +} + +static JsErrorCode JsGetPropertyCommon(Js::ScriptContext * scriptContext, _In_ Js::RecyclableObject * object, + _In_ const Js::PropertyRecord * propertyRecord, _Out_ JsValueRef *value, + TTDRecorder& _actionEntryPopper) +{ + AssertMsg(scriptContext->GetThreadContext()->IsScriptActive(), "Caller is expected to be under ContextAPIWrapper!"); + PERFORM_JSRT_TTD_RECORD_ACTION(scriptContext, RecordJsRTGetProperty, propertyRecord, object); + + *value = Js::JavascriptOperators::GetPropertyNoCache(object, propertyRecord->GetPropertyId(), scriptContext); + Assert(*value == nullptr || !Js::CrossSite::NeedMarshalVar(*value, scriptContext)); + + PERFORM_JSRT_TTD_RECORD_ACTION_RESULT(scriptContext, value); + + return JsNoError; +} + CHAKRA_API JsGetProperty(_In_ JsValueRef object, _In_ JsPropertyIdRef propertyId, _Out_ JsValueRef *value) { - return ContextAPIWrapper([&] (Js::ScriptContext *scriptContext, TTDRecorder& _actionEntryPopper) -> JsErrorCode { - PERFORM_JSRT_TTD_RECORD_ACTION(scriptContext, RecordJsRTGetProperty, (Js::PropertyRecord *)propertyId, object); + return ContextAPIWrapper([&] (Js::ScriptContext *scriptContext, + TTDRecorder& _actionEntryPopper) -> JsErrorCode { VALIDATE_INCOMING_OBJECT(object, scriptContext); VALIDATE_INCOMING_PROPERTYID(propertyId); PARAM_NOT_NULL(value); *value = nullptr; - *value = Js::JavascriptOperators::OP_GetProperty((Js::Var)object, ((Js::PropertyRecord *)propertyId)->GetPropertyId(), scriptContext); - Assert(*value == nullptr || !Js::CrossSite::NeedMarshalVar(*value, scriptContext)); + Js::RecyclableObject * instance = Js::RecyclableObject::FromVar(object); + return JsGetPropertyCommon(scriptContext, instance, (const Js::PropertyRecord *)propertyId, + value, _actionEntryPopper); + }); +} - PERFORM_JSRT_TTD_RECORD_ACTION_RESULT(scriptContext, value); +#ifdef _CHAKRACOREBUILD +CHAKRA_API JsObjectGetProperty(_In_ JsValueRef object, _In_ JsValueRef propertyId, _Out_ JsValueRef *value) +{ + return ContextAPIWrapper([&] (Js::ScriptContext *scriptContext, TTDRecorder& _actionEntryPopper) -> JsErrorCode { + VALIDATE_INCOMING_OBJECT(object, scriptContext); + PARAM_NOT_NULL(propertyId); + PARAM_NOT_NULL(value); + *value = nullptr; - return JsNoError; + const Js::PropertyRecord *propertyRecord = nullptr; + JsErrorCode errorValue = InternalGetPropertyRecord(scriptContext, propertyId, &propertyRecord); + if (errorValue != JsNoError) + { + return errorValue; + } + + Assert(propertyRecord != nullptr); + + Js::RecyclableObject * instance = Js::RecyclableObject::FromVar(object); + return JsGetPropertyCommon(scriptContext, instance, propertyRecord, value, _actionEntryPopper); }); } +#endif + +static JsErrorCode JsGetOwnPropertyDescriptorCommon(Js::ScriptContext * scriptContext, + _In_ JsValueRef object, _In_ const Js::PropertyRecord * propertyRecord, _Out_ JsValueRef *propertyDescriptor, + TTDRecorder& _actionEntryPopper) +{ + AssertMsg(scriptContext->GetThreadContext()->IsScriptActive(), "Caller is expected to be under ContextAPIWrapper!"); + PERFORM_JSRT_TTD_RECORD_ACTION(scriptContext, RecordJsRTGetOwnPropertyInfo, propertyRecord, object); + + Js::PropertyDescriptor propertyDescriptorValue; + if (Js::JavascriptOperators::GetOwnPropertyDescriptor(Js::RecyclableObject::FromVar(object), + propertyRecord->GetPropertyId(), scriptContext, &propertyDescriptorValue)) + { + *propertyDescriptor = Js::JavascriptOperators::FromPropertyDescriptor(propertyDescriptorValue, scriptContext); + } + else + { + *propertyDescriptor = scriptContext->GetLibrary()->GetUndefined(); + } + Assert(*propertyDescriptor == nullptr || !Js::CrossSite::NeedMarshalVar(*propertyDescriptor, scriptContext)); + + PERFORM_JSRT_TTD_RECORD_ACTION_RESULT(scriptContext, propertyDescriptor); + + return JsNoError; +} CHAKRA_API JsGetOwnPropertyDescriptor(_In_ JsValueRef object, _In_ JsPropertyIdRef propertyId, _Out_ JsValueRef *propertyDescriptor) { return ContextAPIWrapper([&] (Js::ScriptContext *scriptContext, TTDRecorder& _actionEntryPopper) -> JsErrorCode { - PERFORM_JSRT_TTD_RECORD_ACTION(scriptContext, RecordJsRTGetOwnPropertyInfo, (Js::PropertyRecord *)propertyId, object); - VALIDATE_INCOMING_OBJECT(object, scriptContext); VALIDATE_INCOMING_PROPERTYID(propertyId); PARAM_NOT_NULL(propertyDescriptor); *propertyDescriptor = nullptr; - Js::PropertyDescriptor propertyDescriptorValue; - if (Js::JavascriptOperators::GetOwnPropertyDescriptor(Js::RecyclableObject::FromVar(object), ((Js::PropertyRecord *)propertyId)->GetPropertyId(), scriptContext, &propertyDescriptorValue)) - { - *propertyDescriptor = Js::JavascriptOperators::FromPropertyDescriptor(propertyDescriptorValue, scriptContext); - } - else - { - *propertyDescriptor = scriptContext->GetLibrary()->GetUndefined(); - } - Assert(*propertyDescriptor == nullptr || !Js::CrossSite::NeedMarshalVar(*propertyDescriptor, scriptContext)); - - PERFORM_JSRT_TTD_RECORD_ACTION_RESULT(scriptContext, propertyDescriptor); - - return JsNoError; + return JsGetOwnPropertyDescriptorCommon(scriptContext, object, (const Js::PropertyRecord *)propertyId, + propertyDescriptor, _actionEntryPopper); }); } -CHAKRA_API JsGetOwnPropertyNames(_In_ JsValueRef object, _Out_ JsValueRef *propertyNames) +#ifdef _CHAKRACOREBUILD +CHAKRA_API JsObjectGetOwnPropertyDescriptor(_In_ JsValueRef object, _In_ JsValueRef propertyId, _Out_ JsValueRef *propertyDescriptor) { return ContextAPIWrapper([&] (Js::ScriptContext *scriptContext, TTDRecorder& _actionEntryPopper) -> JsErrorCode { - PERFORM_JSRT_TTD_RECORD_ACTION(scriptContext, RecordJsRTGetOwnPropertyNamesInfo, object); - VALIDATE_INCOMING_OBJECT(object, scriptContext); - PARAM_NOT_NULL(propertyNames); - *propertyNames = nullptr; + PARAM_NOT_NULL(propertyId); + PARAM_NOT_NULL(propertyDescriptor); + *propertyDescriptor = nullptr; - *propertyNames = Js::JavascriptOperators::GetOwnPropertyNames(object, scriptContext); - Assert(*propertyNames == nullptr || !Js::CrossSite::NeedMarshalVar(*propertyNames, scriptContext)); + const Js::PropertyRecord *propertyRecord = nullptr; + JsErrorCode errorValue = InternalGetPropertyRecord(scriptContext, propertyId, &propertyRecord); + if (errorValue != JsNoError) + { + return errorValue; + } - PERFORM_JSRT_TTD_RECORD_ACTION_RESULT(scriptContext, propertyNames); + Assert(propertyRecord != nullptr); - return JsNoError; + return JsGetOwnPropertyDescriptorCommon(scriptContext, object, propertyRecord, propertyDescriptor, _actionEntryPopper); }); } +#endif -CHAKRA_API JsGetOwnPropertySymbols(_In_ JsValueRef object, _Out_ JsValueRef *propertySymbols) +static JsErrorCode JsSetPropertyCommon(Js::ScriptContext * scriptContext, _In_ JsValueRef object, + _In_ const Js::PropertyRecord * propertyRecord, _In_ JsValueRef value, _In_ bool useStrictRules, + TTDRecorder& _actionEntryPopper) { - return ContextAPIWrapper([&](Js::ScriptContext *scriptContext, TTDRecorder& _actionEntryPopper) -> JsErrorCode { - PERFORM_JSRT_TTD_RECORD_ACTION(scriptContext, RecordJsRTGetOwnPropertySymbolsInfo, object); + AssertMsg(scriptContext->GetThreadContext()->IsScriptActive(), "Caller is expected to be under ContextAPIWrapper!"); + PERFORM_JSRT_TTD_RECORD_ACTION(scriptContext, RecordJsRTSetProperty, object, + propertyRecord, value, useStrictRules); - VALIDATE_INCOMING_OBJECT(object, scriptContext); - PARAM_NOT_NULL(propertySymbols); + Js::JavascriptOperators::OP_SetProperty(object, propertyRecord->GetPropertyId(), + value, scriptContext, nullptr, useStrictRules ? Js::PropertyOperation_StrictMode : Js::PropertyOperation_None); - *propertySymbols = Js::JavascriptOperators::GetOwnPropertySymbols(object, scriptContext); - Assert(*propertySymbols == nullptr || !Js::CrossSite::NeedMarshalVar(*propertySymbols, scriptContext)); + return JsNoError; +} - PERFORM_JSRT_TTD_RECORD_ACTION_RESULT(scriptContext, propertySymbols); +CHAKRA_API JsSetProperty(_In_ JsValueRef object, _In_ JsPropertyIdRef propertyId, _In_ JsValueRef value, _In_ bool useStrictRules) +{ + return ContextAPIWrapper([&] (Js::ScriptContext *scriptContext, + TTDRecorder& _actionEntryPopper) -> JsErrorCode { - return JsNoError; + VALIDATE_INCOMING_OBJECT(object, scriptContext); + VALIDATE_INCOMING_PROPERTYID(propertyId); + VALIDATE_INCOMING_REFERENCE(value, scriptContext); + + return JsSetPropertyCommon(scriptContext, object, (const Js::PropertyRecord *)propertyId, + value, useStrictRules, _actionEntryPopper); }); } -CHAKRA_API JsSetProperty(_In_ JsValueRef object, _In_ JsPropertyIdRef propertyId, _In_ JsValueRef value, _In_ bool useStrictRules) +#ifdef _CHAKRACOREBUILD +CHAKRA_API JsObjectSetProperty(_In_ JsValueRef object, _In_ JsValueRef propertyId, _In_ JsValueRef value, _In_ bool useStrictRules) { return ContextAPIWrapper([&] (Js::ScriptContext *scriptContext, TTDRecorder& _actionEntryPopper) -> JsErrorCode { - PERFORM_JSRT_TTD_RECORD_ACTION(scriptContext, RecordJsRTSetProperty, object, (Js::PropertyRecord *)propertyId, value, useStrictRules); - VALIDATE_INCOMING_OBJECT(object, scriptContext); - VALIDATE_INCOMING_PROPERTYID(propertyId); VALIDATE_INCOMING_REFERENCE(value, scriptContext); + PARAM_NOT_NULL(propertyId); + + const Js::PropertyRecord *propertyRecord = nullptr; + JsErrorCode errorValue = InternalGetPropertyRecord(scriptContext, propertyId, &propertyRecord); + if (errorValue != JsNoError) + { + return errorValue; + } - Js::JavascriptOperators::OP_SetProperty(object, ((Js::PropertyRecord *)propertyId)->GetPropertyId(), value, scriptContext, - nullptr, useStrictRules ? Js::PropertyOperation_StrictMode : Js::PropertyOperation_None); + Assert(propertyRecord != nullptr); - return JsNoError; + return JsSetPropertyCommon(scriptContext, object, propertyRecord, value, useStrictRules, _actionEntryPopper); }); } +#endif CHAKRA_API JsHasProperty(_In_ JsValueRef object, _In_ JsPropertyIdRef propertyId, _Out_ bool *hasProperty) { @@ -1477,7 +1574,8 @@ CHAKRA_API JsHasProperty(_In_ JsValueRef object, _In_ JsPropertyIdRef propertyId PARAM_NOT_NULL(hasProperty); *hasProperty = false; - *hasProperty = Js::JavascriptOperators::OP_HasProperty(object, ((Js::PropertyRecord *)propertyId)->GetPropertyId(), scriptContext) != 0; + Js::RecyclableObject * instance = Js::RecyclableObject::FromVar(object); + *hasProperty = Js::JavascriptOperators::HasProperty(instance, ((Js::PropertyRecord *)propertyId)->GetPropertyId()) != 0; return JsNoError; }; @@ -1500,30 +1598,140 @@ CHAKRA_API JsHasProperty(_In_ JsValueRef object, _In_ JsPropertyIdRef propertyId } } -CHAKRA_API JsDeleteProperty(_In_ JsValueRef object, _In_ JsPropertyIdRef propertyId, _In_ bool useStrictRules, _Out_ JsValueRef *result) +#ifdef _CHAKRACOREBUILD +CHAKRA_API JsObjectHasProperty(_In_ JsValueRef object, _In_ JsValueRef propertyId, _Out_ bool *hasProperty) { - return ContextAPIWrapper([&] (Js::ScriptContext *scriptContext, TTDRecorder& _actionEntryPopper) -> JsErrorCode { - PERFORM_JSRT_TTD_RECORD_ACTION(scriptContext, RecordJsRTDeleteProperty, object, (Js::PropertyRecord *)propertyId, useStrictRules); + VALIDATE_JSREF(object); + if (!Js::JavascriptOperators::IsObject(object)) return JsErrorArgumentNotObject; + + auto internalHasProperty = [&] (Js::ScriptContext *scriptContext, TTDRecorder& _actionEntryPopper) -> JsErrorCode { + VALIDATE_INCOMING_OBJECT(object, scriptContext); + PARAM_NOT_NULL(propertyId); + PARAM_NOT_NULL(hasProperty); + *hasProperty = false; + + const Js::PropertyRecord *propertyRecord = nullptr; + JsErrorCode errorValue = InternalGetPropertyRecord(scriptContext, propertyId, &propertyRecord); + if (errorValue != JsNoError) + { + return errorValue; + } + + PERFORM_JSRT_TTD_RECORD_ACTION(scriptContext, RecordJsRTHasProperty, propertyRecord, object); + + Js::RecyclableObject * instance = Js::RecyclableObject::FromVar(object); + *hasProperty = Js::JavascriptOperators::HasProperty(instance, propertyRecord->GetPropertyId()) != 0; + + return JsNoError; + }; + + Js::RecyclableObject* robject = Js::RecyclableObject::FromVar(object); + Js::TypeId typeId = Js::JavascriptOperators::GetTypeId(robject); + while (typeId != Js::TypeIds_Null && typeId != Js::TypeIds_Proxy) + { + robject = robject->GetPrototype(); + typeId = Js::JavascriptOperators::GetTypeId(robject); + } + + if (typeId == Js::TypeIds_Proxy) + { + return ContextAPIWrapper(internalHasProperty); + } + else + { + return ContextAPINoScriptWrapper(internalHasProperty); + } +} +#endif + +static JsErrorCode JsDeletePropertyCommon(Js::ScriptContext * scriptContext, _In_ JsValueRef object, + _In_ const Js::PropertyRecord * propertyRecord, _In_ bool useStrictRules, _Out_ JsValueRef *result, + TTDRecorder& _actionEntryPopper) +{ + AssertMsg(scriptContext->GetThreadContext()->IsScriptActive(), "Caller is expected to be under ContextAPIWrapper!"); + PERFORM_JSRT_TTD_RECORD_ACTION(scriptContext, RecordJsRTDeleteProperty, object, + propertyRecord, useStrictRules); + + *result = Js::JavascriptOperators::OP_DeleteProperty((Js::Var)object, + propertyRecord->GetPropertyId(), + scriptContext, useStrictRules ? Js::PropertyOperation_StrictMode : Js::PropertyOperation_None); + + Assert(*result == nullptr || !Js::CrossSite::NeedMarshalVar(*result, scriptContext)); + + PERFORM_JSRT_TTD_RECORD_ACTION_RESULT(scriptContext, result); + + return JsNoError; +} + +CHAKRA_API JsDeleteProperty(_In_ JsValueRef object, _In_ JsPropertyIdRef propertyId, + _In_ bool useStrictRules, _Out_ JsValueRef *result) +{ + return ContextAPIWrapper([&] (Js::ScriptContext *scriptContext, + TTDRecorder& _actionEntryPopper) -> JsErrorCode { VALIDATE_INCOMING_OBJECT(object, scriptContext); VALIDATE_INCOMING_PROPERTYID(propertyId); PARAM_NOT_NULL(result); *result = nullptr; - *result = Js::JavascriptOperators::OP_DeleteProperty((Js::Var)object, ((Js::PropertyRecord *)propertyId)->GetPropertyId(), - scriptContext, useStrictRules ? Js::PropertyOperation_StrictMode : Js::PropertyOperation_None); - Assert(*result == nullptr || !Js::CrossSite::NeedMarshalVar(*result, scriptContext)); + return JsDeletePropertyCommon(scriptContext, object, (const Js::PropertyRecord *)propertyId, + useStrictRules, result, _actionEntryPopper); + }); +} - PERFORM_JSRT_TTD_RECORD_ACTION_RESULT(scriptContext, result); +#ifdef _CHAKRACOREBUILD +CHAKRA_API JsObjectDeleteProperty(_In_ JsValueRef object, _In_ JsValueRef propertyId, + _In_ bool useStrictRules, _Out_ JsValueRef *result) +{ + return ContextAPIWrapper([&] (Js::ScriptContext *scriptContext, + TTDRecorder& _actionEntryPopper) -> JsErrorCode { - return JsNoError; + VALIDATE_INCOMING_OBJECT(object, scriptContext); + PARAM_NOT_NULL(propertyId); + PARAM_NOT_NULL(result); + *result = nullptr; + + const Js::PropertyRecord *propertyRecord = nullptr; + JsErrorCode errorValue = InternalGetPropertyRecord(scriptContext, propertyId, &propertyRecord); + if (errorValue != JsNoError) + { + return errorValue; + } + + Assert(propertyRecord != nullptr); + + return JsDeletePropertyCommon(scriptContext, object, propertyRecord, + useStrictRules, result, _actionEntryPopper); }); } +#endif -CHAKRA_API JsDefineProperty(_In_ JsValueRef object, _In_ JsPropertyIdRef propertyId, _In_ JsValueRef propertyDescriptor, _Out_ bool *result) +static JsErrorCode JsDefinePropertyCommon(Js::ScriptContext * scriptContext, _In_ JsValueRef object, + _In_ const Js::PropertyRecord *propertyRecord, _In_ JsValueRef propertyDescriptor, + _Out_ bool *result, TTDRecorder& _actionEntryPopper) { - return ContextAPIWrapper([&] (Js::ScriptContext *scriptContext, TTDRecorder& _actionEntryPopper) -> JsErrorCode { - PERFORM_JSRT_TTD_RECORD_ACTION(scriptContext, RecordJsRTDefineProperty, object, (Js::PropertyRecord *)propertyId, propertyDescriptor); + AssertMsg(scriptContext->GetThreadContext()->IsScriptActive(), "Caller is expected to be under ContextAPIWrapper!"); + PERFORM_JSRT_TTD_RECORD_ACTION(scriptContext, RecordJsRTDefineProperty, object, + propertyRecord, propertyDescriptor); + + Js::PropertyDescriptor propertyDescriptorValue; + if (!Js::JavascriptOperators::ToPropertyDescriptor(propertyDescriptor, &propertyDescriptorValue, scriptContext)) + { + return JsErrorInvalidArgument; + } + + *result = Js::JavascriptOperators::DefineOwnPropertyDescriptor( + Js::RecyclableObject::FromVar(object), propertyRecord->GetPropertyId(), + propertyDescriptorValue, true, scriptContext) != 0; + + return JsNoError; +} + +CHAKRA_API JsDefineProperty(_In_ JsValueRef object, _In_ JsPropertyIdRef propertyId, + _In_ JsValueRef propertyDescriptor, _Out_ bool *result) +{ + return ContextAPIWrapper([&] (Js::ScriptContext *scriptContext, + TTDRecorder& _actionEntryPopper) -> JsErrorCode { VALIDATE_INCOMING_OBJECT(object, scriptContext); VALIDATE_INCOMING_PROPERTYID(propertyId); @@ -1531,15 +1739,66 @@ CHAKRA_API JsDefineProperty(_In_ JsValueRef object, _In_ JsPropertyIdRef propert PARAM_NOT_NULL(result); *result = false; - Js::PropertyDescriptor propertyDescriptorValue; - if (!Js::JavascriptOperators::ToPropertyDescriptor(propertyDescriptor, &propertyDescriptorValue, scriptContext)) + return JsDefinePropertyCommon(scriptContext, object, (const Js::PropertyRecord *)propertyId, + propertyDescriptor, result, _actionEntryPopper); + }); +} + +#ifdef _CHAKRACOREBUILD +CHAKRA_API JsObjectDefineProperty(_In_ JsValueRef object, _In_ JsValueRef propertyId, + _In_ JsValueRef propertyDescriptor, _Out_ bool *result) +{ + return ContextAPIWrapper([&] (Js::ScriptContext *scriptContext, + TTDRecorder& _actionEntryPopper) -> JsErrorCode { + + VALIDATE_INCOMING_OBJECT(object, scriptContext); + PARAM_NOT_NULL(propertyId); + VALIDATE_INCOMING_OBJECT(propertyDescriptor, scriptContext); + PARAM_NOT_NULL(result); + *result = false; + + const Js::PropertyRecord *propertyRecord = nullptr; + JsErrorCode errorValue = InternalGetPropertyRecord(scriptContext, propertyId, &propertyRecord); + if (errorValue != JsNoError) { - return JsErrorInvalidArgument; + return errorValue; } - *result = Js::JavascriptOperators::DefineOwnPropertyDescriptor( - Js::RecyclableObject::FromVar(object), ((Js::PropertyRecord *)propertyId)->GetPropertyId(), propertyDescriptorValue, - true, scriptContext) != 0; + return JsDefinePropertyCommon(scriptContext, object, propertyRecord, propertyDescriptor, result, _actionEntryPopper); + }); +} +#endif + +CHAKRA_API JsGetOwnPropertyNames(_In_ JsValueRef object, _Out_ JsValueRef *propertyNames) +{ + return ContextAPIWrapper([&] (Js::ScriptContext *scriptContext, TTDRecorder& _actionEntryPopper) -> JsErrorCode { + PERFORM_JSRT_TTD_RECORD_ACTION(scriptContext, RecordJsRTGetOwnPropertyNamesInfo, object); + + VALIDATE_INCOMING_OBJECT(object, scriptContext); + PARAM_NOT_NULL(propertyNames); + *propertyNames = nullptr; + + *propertyNames = Js::JavascriptOperators::GetOwnPropertyNames(object, scriptContext); + Assert(*propertyNames == nullptr || !Js::CrossSite::NeedMarshalVar(*propertyNames, scriptContext)); + + PERFORM_JSRT_TTD_RECORD_ACTION_RESULT(scriptContext, propertyNames); + + return JsNoError; + }); +} + +CHAKRA_API JsGetOwnPropertySymbols(_In_ JsValueRef object, _Out_ JsValueRef *propertySymbols) +{ + return ContextAPIWrapper([&](Js::ScriptContext *scriptContext, TTDRecorder& _actionEntryPopper) -> JsErrorCode { + PERFORM_JSRT_TTD_RECORD_ACTION(scriptContext, RecordJsRTGetOwnPropertySymbolsInfo, object); + + VALIDATE_INCOMING_OBJECT(object, scriptContext); + PARAM_NOT_NULL(propertySymbols); + + *propertySymbols = Js::JavascriptOperators::GetOwnPropertySymbols(object, scriptContext); + Assert(*propertySymbols == nullptr || !Js::CrossSite::NeedMarshalVar(*propertySymbols, scriptContext)); + + PERFORM_JSRT_TTD_RECORD_ACTION_RESULT(scriptContext, propertySymbols); return JsNoError; }); @@ -4179,14 +4438,21 @@ CHAKRA_API JsCreateString( _Out_ JsValueRef *value) { PARAM_NOT_NULL(content); + PARAM_NOT_NULL(value); - utf8::NarrowToWide wstr(content, length); - if (!wstr) - { - return JsErrorOutOfMemory; - } + return ContextAPINoScriptWrapper([&](Js::ScriptContext *scriptContext, TTDRecorder& _actionEntryPopper) -> JsErrorCode { + + Js::LiteralStringWithPropertyStringPtr *stringValue = Js::LiteralStringWithPropertyStringPtr:: + NewFromCString(content, (CharCount)length, scriptContext->GetLibrary()); + + PERFORM_JSRT_TTD_RECORD_ACTION(scriptContext, RecordJsRTCreateString, stringValue->GetSz(), stringValue->GetLength()); - return JsPointerToString(wstr, wstr.Length(), value); + *value = stringValue; + + PERFORM_JSRT_TTD_RECORD_ACTION_RESULT(scriptContext, value); + + return JsNoError; + }); } CHAKRA_API JsCreateStringUtf16( @@ -4195,9 +4461,21 @@ CHAKRA_API JsCreateStringUtf16( _Out_ JsValueRef *value) { PARAM_NOT_NULL(content); + PARAM_NOT_NULL(value); + + return ContextAPINoScriptWrapper([&](Js::ScriptContext *scriptContext, TTDRecorder& _actionEntryPopper) -> JsErrorCode { - return JsPointerToString( - reinterpret_cast(content), length, value); + Js::LiteralStringWithPropertyStringPtr *stringValue = Js::LiteralStringWithPropertyStringPtr:: + NewFromWideString(content, (CharCount)length, scriptContext->GetLibrary()); + + PERFORM_JSRT_TTD_RECORD_ACTION(scriptContext, RecordJsRTCreateString, stringValue->GetSz(), stringValue->GetLength()); + + *value = stringValue; + + PERFORM_JSRT_TTD_RECORD_ACTION_RESULT(scriptContext, value); + + return JsNoError; + }); } diff --git a/lib/Runtime/Language/JavascriptConversion.cpp b/lib/Runtime/Language/JavascriptConversion.cpp index 96ef54f43a3..1dfabf5f203 100644 --- a/lib/Runtime/Language/JavascriptConversion.cpp +++ b/lib/Runtime/Language/JavascriptConversion.cpp @@ -298,16 +298,7 @@ namespace Js else if (VirtualTableInfo::HasVirtualTable(propName)) { LiteralStringWithPropertyStringPtr * str = (LiteralStringWithPropertyStringPtr *)propName; - if (str->GetPropertyString()) - { - *propertyRecord = str->GetPropertyString()->GetPropertyRecord(); - } - else - { - scriptContext->GetOrAddPropertyRecord(propName->GetString(), propName->GetLength(), propertyRecord); - PropertyString * propStr = scriptContext->GetPropertyString((*propertyRecord)->GetPropertyId()); - str->SetPropertyString(propStr); - } + *propertyRecord = str->GetOrAddPropertyString()->GetPropertyRecord(); } else { diff --git a/lib/Runtime/Language/JavascriptOperators.cpp b/lib/Runtime/Language/JavascriptOperators.cpp index 5e1ad9bddf6..662300d14c8 100644 --- a/lib/Runtime/Language/JavascriptOperators.cpp +++ b/lib/Runtime/Language/JavascriptOperators.cpp @@ -3746,8 +3746,9 @@ namespace Js else if (VirtualTableInfo::HasVirtualTable(temp)) { LiteralStringWithPropertyStringPtr * str = (LiteralStringWithPropertyStringPtr *)temp; - propertyString = str->GetPropertyString(); + propertyString = str->GetOrAddPropertyString(); } + if(propertyString != nullptr) { RecyclableObject* object = nullptr; @@ -4413,18 +4414,8 @@ namespace Js if (VirtualTableInfo::HasVirtualTable(index)) { LiteralStringWithPropertyStringPtr * str = (LiteralStringWithPropertyStringPtr *)index; - propertyString = str->GetPropertyString(); - if (propertyString == nullptr) - { - scriptContext->GetOrAddPropertyRecord(str->GetString(), str->GetLength(), &propertyRecord); - propertyString = scriptContext->GetPropertyString(propertyRecord->GetPropertyId()); - str->SetPropertyString(propertyString); - } - else - { - propertyRecord = propertyString->GetPropertyRecord(); - } - + propertyString = str->GetOrAddPropertyString(); + propertyRecord = propertyString->GetPropertyRecord(); } else { diff --git a/lib/Runtime/Library/ConcatString.cpp b/lib/Runtime/Library/ConcatString.cpp index 659023f3995..11de8c6ab56 100644 --- a/lib/Runtime/Library/ConcatString.cpp +++ b/lib/Runtime/Library/ConcatString.cpp @@ -3,7 +3,7 @@ // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. //------------------------------------------------------------------------------------------------------- #include "RuntimeLibraryPch.h" - +#include "Codex/Utf8Helper.h" namespace Js @@ -17,11 +17,137 @@ namespace Js { } + LiteralStringWithPropertyStringPtr::LiteralStringWithPropertyStringPtr(const char16 * wString, + const CharCount stringLength, JavascriptLibrary *const library) : + LiteralString(library->GetStringTypeStatic(), wString, stringLength), + propertyString(nullptr) + { + } + + LiteralStringWithPropertyStringPtr *LiteralStringWithPropertyStringPtr:: + NewFromWideString(const uint16_t * wideString, const CharCount charCount, JavascriptLibrary *const library) + { + Assert(library != nullptr && wideString != nullptr); + + switch (charCount) + { + case 0: + { + JavascriptString * emptyString = library->GetEmptyString(); + AssertMsg(VirtualTableInfo::HasVirtualTable(emptyString), + "Library::GetEmptyString is no longer LiteralStringWithPropertyStringPtr ?"); + return (LiteralStringWithPropertyStringPtr*) emptyString; + } + + // TODO: Make CharStringCache LiteralStringWithPropertyStringPtr? + // case 1: + // return library->GetCharStringCache().GetStringForChar(*content); + + default: + break; + } + + Recycler * recycler = library->GetRecycler(); + ScriptContext * scriptContext = library->GetScriptContext(); + char16* destString = RecyclerNewArrayLeaf(recycler, WCHAR, charCount + 1); + + if (destString == nullptr) + { + Js::JavascriptError::ThrowOutOfMemoryError(scriptContext); + } + + js_wmemcpy_s(destString, charCount, (LPWSTR)wideString, charCount); + destString[charCount] = char16(0); + + return RecyclerNew(library->GetRecycler(), LiteralStringWithPropertyStringPtr, destString, charCount, library); + } + + LiteralStringWithPropertyStringPtr* LiteralStringWithPropertyStringPtr::CreateEmptyString(JavascriptLibrary *const library) + { + return RecyclerNew(library->GetRecycler(), LiteralStringWithPropertyStringPtr, _u(""), 0, library); + } + + LiteralStringWithPropertyStringPtr *LiteralStringWithPropertyStringPtr:: + NewFromCString(const char * cString, const CharCount charCount, JavascriptLibrary *const library) + { + Assert(library != nullptr && cString != nullptr); + + switch (charCount) + { + case 0: + { + JavascriptString * emptyString = library->GetEmptyString(); + AssertMsg(VirtualTableInfo::HasVirtualTable(emptyString), + "Library::GetEmptyString is no longer LiteralStringWithPropertyStringPtr ?"); + return (LiteralStringWithPropertyStringPtr*) emptyString; + } + + // TODO: Make CharStringCache LiteralStringWithPropertyStringPtr? + // case 1: + // return library->GetCharStringCache().GetStringForChar(*content); + + default: + break; + } + + ScriptContext * scriptContext = library->GetScriptContext(); + size_t cbDestString = (charCount + 1) * sizeof(WCHAR); + if ((CharCount)cbDestString < charCount) // overflow + { + Js::JavascriptError::ThrowOutOfMemoryError(scriptContext); + } + + Recycler * recycler = library->GetRecycler(); + char16* destString = RecyclerNewArrayLeaf(recycler, WCHAR, cbDestString); + if (destString == nullptr) + { + Js::JavascriptError::ThrowOutOfMemoryError(scriptContext); + } + + HRESULT result = utf8::NarrowStringToWideNoAlloc(cString, charCount, destString, charCount + 1, &cbDestString); + + switch(result) + { + case S_OK: + return RecyclerNew(library->GetRecycler(), LiteralStringWithPropertyStringPtr, destString, (CharCount)cbDestString, library); + + // unlikely + // case E_OUTOFMEMORY: + // return nullptr; // TODO ?: throw OUTOFMEMORY + + // case E_INVALIDARG: + // return nullptr; // TODO ?: throw .... + default: + break; + } + + Assert(false); + return nullptr; // make compiler happy + } + PropertyString * LiteralStringWithPropertyStringPtr::GetPropertyString() const { return this->propertyString; } + PropertyString * LiteralStringWithPropertyStringPtr::GetOrAddPropertyString() + { + if (this->propertyString != nullptr) + { + return this->propertyString; + } + + ScriptContext * scriptContext = this->GetScriptContext(); + + Js::PropertyRecord *propertyRecord = nullptr; + scriptContext->GetOrAddPropertyRecord(this->GetSz(), static_cast(this->GetLength()), + (Js::PropertyRecord const **)&propertyRecord); + + this->propertyString = scriptContext->GetPropertyString(propertyRecord->GetPropertyId()); + + return this->propertyString; + } + void LiteralStringWithPropertyStringPtr::SetPropertyString(PropertyString * propStr) { this->propertyString = propStr; diff --git a/lib/Runtime/Library/ConcatString.h b/lib/Runtime/Library/ConcatString.h index 0619d7aedb5..b2209cd5cf1 100644 --- a/lib/Runtime/Library/ConcatString.h +++ b/lib/Runtime/Library/ConcatString.h @@ -9,18 +9,30 @@ namespace Js class LiteralStringWithPropertyStringPtr : public LiteralString { private: - PropertyString * propertyString; + // TODO: Can we make PropertyString && LiteralStringWithPropertyStringPtr share the string buffer? + Field(PropertyString*) propertyString; public: PropertyString * GetPropertyString() const; + PropertyString * GetOrAddPropertyString(); // Get if it's there, otherwise bring it in. void SetPropertyString(PropertyString * propStr); template static LiteralStringWithPropertyStringPtr * ConvertString(StringType * originalString); static uint GetOffsetOfPropertyString() { return offsetof(LiteralStringWithPropertyStringPtr, propertyString); } + static LiteralStringWithPropertyStringPtr * + NewFromCString(const char * cString, const CharCount charCount, JavascriptLibrary *const library); + + static LiteralStringWithPropertyStringPtr * + NewFromWideString(const uint16_t * wString, const CharCount charCount, JavascriptLibrary *const library); + + static LiteralStringWithPropertyStringPtr* CreateEmptyString(JavascriptLibrary *const library); + protected: LiteralStringWithPropertyStringPtr(StaticType* stringTypeStatic); + LiteralStringWithPropertyStringPtr(const char16 * wString, const CharCount stringLength, JavascriptLibrary *const library); + DEFINE_VTABLE_CTOR(LiteralStringWithPropertyStringPtr, LiteralString); DECLARE_CONCRETE_STRING_CLASS; diff --git a/lib/Runtime/Library/JavascriptLibrary.cpp b/lib/Runtime/Library/JavascriptLibrary.cpp index 610d974abb7..edd3b77d7a4 100644 --- a/lib/Runtime/Library/JavascriptLibrary.cpp +++ b/lib/Runtime/Library/JavascriptLibrary.cpp @@ -5377,7 +5377,7 @@ namespace Js } JavascriptString* JavascriptLibrary::CreateEmptyString() { - return LiteralString::CreateEmptyString(GetStringTypeStatic()); + return LiteralStringWithPropertyStringPtr::CreateEmptyString(this); } JavascriptRegExp* JavascriptLibrary::CreateEmptyRegExp() diff --git a/lib/Runtime/Library/LiteralString.cpp b/lib/Runtime/Library/LiteralString.cpp index 9da14c65056..039415a7b20 100644 --- a/lib/Runtime/Library/LiteralString.cpp +++ b/lib/Runtime/Library/LiteralString.cpp @@ -41,7 +41,6 @@ namespace Js return RecyclerNew(type->GetScriptContext()->GetRecycler(), LiteralString, type, _u(""), 0); } - ArenaLiteralString::ArenaLiteralString(StaticType * type, const char16* content, charcount_t charLength) : JavascriptString(type, charLength, content) { diff --git a/test/native-tests/test-property/Platform.js b/test/native-tests/test-property/Platform.js new file mode 100644 index 00000000000..48c25f2e6cb --- /dev/null +++ b/test/native-tests/test-property/Platform.js @@ -0,0 +1,52 @@ +//------------------------------------------------------------------------------------------------------- +// Copyright (C) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. +//------------------------------------------------------------------------------------------------------- + +var isWindows = !WScript.Platform || WScript.Platform.OS == 'win32'; +var path_sep = isWindows ? '\\' : '/'; +var isStaticBuild = WScript.Platform && WScript.Platform.LINK_TYPE == 'static'; + +if (!isStaticBuild) { + // test will be ignored + print("# IGNORE_THIS_TEST"); +} else { + var platform = WScript.Platform.OS; + var binaryPath = WScript.Platform.BINARY_PATH; + // discard `ch` from path + binaryPath = binaryPath.substr(0, binaryPath.lastIndexOf(path_sep)); + var makefile = +"IDIR=" + binaryPath + "/../../lib/Jsrt \n\ +\n\ +LIBRARY_PATH=" + binaryPath + "/lib\n\ +PLATFORM=" + platform + "\n\ +LDIR=$(LIBRARY_PATH)/libChakraCoreStatic.a \n\ +\n\ +ifeq (darwin, ${PLATFORM})\n\ +\tICU4C_LIBRARY_PATH ?= /usr/local/opt/icu4c\n\ +\tCFLAGS=-lstdc++ -std=c++11 -I$(IDIR)\n\ +\tFORCE_STARTS=-Wl,-force_load,\n\ +\tFORCE_ENDS=\n\ +\tLIBS=-framework CoreFoundation -framework Security -lm -ldl -Wno-c++11-compat-deprecated-writable-strings \ + -Wno-deprecated-declarations -Wno-unknown-warning-option -o sample.o\n\ +\tLDIR+=$(ICU4C_LIBRARY_PATH)/lib/libicudata.a \ + $(ICU4C_LIBRARY_PATH)/lib/libicuuc.a \ + $(ICU4C_LIBRARY_PATH)/lib/libicui18n.a\n\ +else\n\ +\tCFLAGS=-lstdc++ -std=c++0x -I$(IDIR)\n\ +\tFORCE_STARTS=-Wl,--whole-archive\n\ +\tFORCE_ENDS=-Wl,--no-whole-archive\n\ +\tLIBS=-pthread -lm -ldl -licuuc -Wno-c++11-compat-deprecated-writable-strings \ + -Wno-deprecated-declarations -Wno-unknown-warning-option -o sample.o\n\ +endif\n\ +\n\ +testmake:\n\ +\t$(CC) sample.cpp $(CFLAGS) $(FORCE_STARTS) $(LDIR) $(FORCE_ENDS) $(LIBS)\n\ +\n\ +.PHONY: clean\n\ +\n\ +clean:\n\ +\trm sample.o\n"; + + print(makefile) +} diff --git a/test/native-tests/test-property/sample.cpp b/test/native-tests/test-property/sample.cpp new file mode 100644 index 00000000000..f3d4b060f9a --- /dev/null +++ b/test/native-tests/test-property/sample.cpp @@ -0,0 +1,103 @@ +//------------------------------------------------------------------------------------------------------- +// Copyright (C) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. +//------------------------------------------------------------------------------------------------------- + +#include "ChakraCore.h" +#include +#include +#include +#include + +#define FAIL_CHECK(cmd) \ + do \ + { \ + JsErrorCode errCode = cmd; \ + if (errCode != JsNoError) \ + { \ + printf("Error %d at '%s'\n", \ + errCode, #cmd); \ + return 1; \ + } \ + } while(0) + +using namespace std; + +int main() +{ + JsRuntimeHandle runtime; + JsContextRef context; + JsValueRef result; + unsigned currentSourceContext = 0; + + const char* script = "(()=>{return \'SUCCESS\';})()"; + + // Create a runtime. + JsCreateRuntime(JsRuntimeAttributeNone, nullptr, &runtime); + + // Create an execution context. + JsCreateContext(runtime, &context); + + // Now set the current execution context. + JsSetCurrentContext(context); + + JsValueRef fname; + FAIL_CHECK(JsCreateString("sample", sizeof("sample") - 1, &fname)); + + JsValueRef object; + FAIL_CHECK(JsCreateObject(&object)); + + JsValueRef scriptSource; + FAIL_CHECK(JsCreateExternalArrayBuffer((void*)script, (unsigned int)strlen(script), + nullptr, nullptr, &scriptSource)); + + FAIL_CHECK(JsObjectSetProperty(object, fname, fname, false)); + + JsValueRef fname_copy; + FAIL_CHECK(JsObjectGetProperty(object, fname, &fname_copy)); + + bool hasObject = false; + FAIL_CHECK(JsObjectHasProperty(object, fname, &hasObject)); + + JsValueRef pdesc; + FAIL_CHECK(JsObjectGetOwnPropertyDescriptor(object, fname, &pdesc)); + + if (!hasObject || fname_copy != fname || pdesc == nullptr) + { + fprintf(stderr, "JsObject Has/Get/Set etc. Property... failed\n"); + } + + JsValueRef fname_second_copy; + FAIL_CHECK(JsObjectDeleteProperty(object, fname, false, &fname_second_copy)); + + FAIL_CHECK(JsObjectHasProperty(object, fname, &hasObject)); + + if (hasObject || fname_second_copy != fname) + { + fprintf(stderr, "JsObjectDeleteProperty failed\n"); + } + + // Run the script. + FAIL_CHECK(JsRun(scriptSource, currentSourceContext++, fname, JsParseScriptAttributeNone, &result)); + + // Convert your script result to String in JavaScript; redundant if your script returns a String + JsValueRef resultJSString; + FAIL_CHECK(JsConvertValueToString(result, &resultJSString)); + + // Project script result back to C++. + char *resultSTR = nullptr; + size_t stringLength; + FAIL_CHECK(JsCopyString(resultJSString, nullptr, 0, &stringLength)); + resultSTR = (char*) malloc(stringLength + 1); + FAIL_CHECK(JsCopyString(resultJSString, resultSTR, stringLength, nullptr)); + resultSTR[stringLength] = 0; + + printf("Result -> %s \n", resultSTR); + free(resultSTR); + + // Dispose runtime + JsSetCurrentContext(JS_INVALID_REFERENCE); + JsDisposeRuntime(runtime); + + return 0; +} diff --git a/test/native-tests/test_native.sh b/test/native-tests/test_native.sh index 79d97a60721..f5cdab6358d 100755 --- a/test/native-tests/test_native.sh +++ b/test/native-tests/test_native.sh @@ -87,6 +87,9 @@ RUN "test-char16" # test-static-native RUN "test-static-native" +# test-property +RUN "test-property" + # shared lib tests LIB_DIR="$(dirname ${CH_DIR})" if [[ `uname -a` =~ "Darwin" ]]; then