diff --git a/bin/NativeTests/JsRTApiTest.cpp b/bin/NativeTests/JsRTApiTest.cpp index 66bb4649c3a..3e5d377c899 100644 --- a/bin/NativeTests/JsRTApiTest.cpp +++ b/bin/NativeTests/JsRTApiTest.cpp @@ -2143,4 +2143,32 @@ namespace JsRTApiTest JsRTApiTest::RunWithAttributes(JsRTApiTest::ObjectHasOwnPropertyMethodTest); } + void JsCopyStringOneByteMethodTest(JsRuntimeAttributes attributes, JsRuntimeHandle runtime) + { + size_t written = 0; + char buf[10] = {0}; + JsValueRef value; + REQUIRE(JsCreateStringUtf16(reinterpret_cast(_u("0\x10\x80\xa9\uabcd\U000104377")), 8, &value) == JsNoError); + REQUIRE(JsCopyStringOneByte(value, 0, -1, nullptr, &written) == JsNoError); + CHECK(written == 8); + buf[written] = '\xff'; + + REQUIRE(JsCopyStringOneByte(value, 0, 10, buf, &written) == JsNoError); + CHECK(written == 8); + CHECK(buf[0] == '0'); + CHECK(buf[1] == '\x10'); + CHECK(buf[2] == '\x80'); + CHECK(buf[3] == '\xA9'); + CHECK(buf[4] == '\xcd'); + CHECK(buf[5] == '\x01'); + CHECK(buf[6] == '\x37'); + CHECK(buf[7] == '7'); + CHECK(buf[8] == '\xff'); + } + + TEST_CASE("ApiTest_JsCopyStringOneByteMethodTest", "[ApiTest]") + { + JsRTApiTest::RunWithAttributes(JsRTApiTest::JsCopyStringOneByteMethodTest); + } + } diff --git a/lib/Jsrt/ChakraCore.h b/lib/Jsrt/ChakraCore.h index 66ce4e6a135..83d1d1a76c2 100644 --- a/lib/Jsrt/ChakraCore.h +++ b/lib/Jsrt/ChakraCore.h @@ -679,5 +679,43 @@ CHAKRA_API _In_ JsPropertyIdRef propertyId, _Out_ bool *hasOwnProperty); +/// +/// Write JS string value into char string buffer without a null terminator +/// +/// +/// +/// When size of the `buffer` is unknown, +/// `buffer` argument can be nullptr. +/// In that case, `written` argument will return the length needed. +/// +/// +/// When start is out of range or < 0, returns JsErrorInvalidArgument +/// and `written` will be equal to 0. +/// If calculated length is 0 (It can be due to string length or `start` +/// and length combination), then `written` will be equal to 0 and call +/// returns JsNoError +/// +/// +/// The JS string `value` will be converted one utf16 code point at a time, +/// and if it has code points that do not fit in one byte, those values +/// will be truncated. +/// +/// +/// JavascriptString value +/// Start offset of buffer +/// Number of characters to be written +/// Pointer to buffer +/// Total number of characters written +/// +/// The code JsNoError if the operation succeeded, a failure code otherwise. +/// +CHAKRA_API +JsCopyStringOneByte( + _In_ JsValueRef value, + _In_ int start, + _In_ int length, + _Out_opt_ char* buffer, + _Out_opt_ size_t* written); + #endif // CHAKRACOREBUILD_ #endif // _CHAKRACORE_H_ diff --git a/lib/Jsrt/Jsrt.cpp b/lib/Jsrt/Jsrt.cpp index b040d54f9a7..662583ba8b9 100644 --- a/lib/Jsrt/Jsrt.cpp +++ b/lib/Jsrt/Jsrt.cpp @@ -4181,14 +4181,15 @@ JsErrorCode WriteStringCopy( *written = 0; // init to 0 for default } - const char16* str = nullptr; - size_t strLength = 0; - JsErrorCode errorCode = JsStringToPointer(value, &str, &strLength); - if (errorCode != JsNoError) + if (!Js::JavascriptString::Is(value)) { - return errorCode; + return JsErrorInvalidArgument; } + Js::JavascriptString *jsString = Js::JavascriptString::FromVar(value); + const char16* str = jsString->GetSz(); + size_t strLength = jsString->GetLength(); + if (start < 0 || (size_t)start > strLength) { return JsErrorInvalidArgument; // start out of range, no chars written @@ -4200,7 +4201,7 @@ JsErrorCode WriteStringCopy( return JsNoError; // no chars written } - errorCode = copyFunc(str + start, count, written); + JsErrorCode errorCode = copyFunc(str + start, count, written); if (errorCode != JsNoError) { return errorCode; @@ -4231,10 +4232,6 @@ CHAKRA_API JsCopyStringUtf16( { memmove(buffer, src, sizeof(char16) * count); } - else - { - *needed = count; - } return JsNoError; }); } @@ -4746,4 +4743,27 @@ CHAKRA_API JsHasOwnProperty(_In_ JsValueRef object, _In_ JsPropertyIdRef propert }); } +CHAKRA_API JsCopyStringOneByte( + _In_ JsValueRef value, + _In_ int start, + _In_ int length, + _Out_opt_ char* buffer, + _Out_opt_ size_t* written) +{ + PARAM_NOT_NULL(value); + VALIDATE_JSREF(value); + return WriteStringCopy(value, start, length, written, + [buffer](const char16* src, size_t count, size_t *needed) + { + if (buffer) + { + for (size_t i = 0; i < count; i++) + { + buffer[i] = (char)src[i]; + } + } + return JsNoError; + }); +} + #endif // CHAKRACOREBUILD_ diff --git a/lib/Jsrt/JsrtCommonExports.inc b/lib/Jsrt/JsrtCommonExports.inc index 8a24073d0a3..897a82fc6b4 100644 --- a/lib/Jsrt/JsrtCommonExports.inc +++ b/lib/Jsrt/JsrtCommonExports.inc @@ -120,4 +120,5 @@ JsGetWeakReferenceValue JsGetAndClearExceptionWithMetadata JsHasOwnProperty + JsCopyStringOneByte #endif