Skip to content
This repository has been archived by the owner on Oct 15, 2020. It is now read-only.

Commit

Permalink
deps: update ChakraCore to chakra-core/ChakraCore@adf0ecb899
Browse files Browse the repository at this point in the history
[MERGE #4832 @jackhorton] Update PlatformAgnostic case conversion functions to allow expanding-length strings

Merge pull request #4832 from jackhorton:icu/tocase

Fixes #421
Fixes #4526

Also fixes a previously untracked? bug where String.prototype.toLocale{Upper|Lower}Case.call(null) would print

Reviewed-By: chakrabot <chakrabot@users.noreply.github.com>
  • Loading branch information
jackhorton authored and chakrabot committed Mar 21, 2018
1 parent fe79252 commit 3409177
Show file tree
Hide file tree
Showing 16 changed files with 594 additions and 749 deletions.
199 changes: 46 additions & 153 deletions deps/chakrashim/core/lib/Runtime/Library/JavascriptString.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2125,7 +2125,10 @@ namespace Js

Assert(!(callInfo.Flags & CallFlags_New));

return ToLocaleCaseHelper(args[0], false, scriptContext);
JavascriptString * pThis = nullptr;
GetThisStringArgument(args, scriptContext, _u("String.prototype.toLocaleLowerCase"), &pThis);

return ToLocaleCaseHelper<false /* toUpper */>(pThis);
}

Var JavascriptString::EntryToLocaleUpperCase(RecyclableObject* function, CallInfo callInfo, ...)
Expand All @@ -2137,9 +2140,20 @@ namespace Js

Assert(!(callInfo.Flags & CallFlags_New));

return ToLocaleCaseHelper(args[0], true, scriptContext);
JavascriptString * pThis = nullptr;
GetThisStringArgument(args, scriptContext, _u("String.prototype.toLocaleUpperCase"), &pThis);

return ToLocaleCaseHelper<true /* toUpper */>(pThis);
}

template<bool toUpper>
JavascriptString* JavascriptString::ToLocaleCaseHelper(JavascriptString* pThis)
{
// TODO: implement locale-sensitive Intl versions of these functions
return ToCaseCore<toUpper, false>(pThis);
}


Var JavascriptString::EntryToLowerCase(RecyclableObject* function, CallInfo callInfo, ...)
{
PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
Expand All @@ -2152,23 +2166,7 @@ namespace Js
JavascriptString * pThis = nullptr;
GetThisStringArgument(args, scriptContext, _u("String.prototype.toLowerCase"), &pThis);

// Fast path for one character strings
if (pThis->GetLength() == 1)
{
char16 inChar = pThis->GetString()[0];
char16 outChar = inChar;
#if DBG
DWORD converted =
#endif
PlatformAgnostic::UnicodeText::ChangeStringCaseInPlace(
PlatformAgnostic::UnicodeText::CaseFlags::CaseFlagsLower, &outChar, 1);

Assert(converted == 1);

return (inChar == outChar) ? pThis : scriptContext->GetLibrary()->GetCharStringCache().GetStringForChar(outChar);
}

return ToCaseCore(pThis, ToLower);
return ToCaseCore<false, true>(pThis);
}

Var JavascriptString::EntryToString(RecyclableObject* function, CallInfo callInfo, ...)
Expand Down Expand Up @@ -2216,113 +2214,50 @@ namespace Js
JavascriptString* pThis = nullptr;
GetThisStringArgument(args, scriptContext, _u("String.prototype.toUpperCase"), &pThis);

// Fast path for one character strings
if (pThis->GetLength() == 1)
{
char16 inChar = pThis->GetString()[0];
char16 outChar = inChar;
#if DBG
DWORD converted =
#endif
PlatformAgnostic::UnicodeText::ChangeStringCaseInPlace(
PlatformAgnostic::UnicodeText::CaseFlags::CaseFlagsUpper, &outChar, 1);

Assert(converted == 1);

return (inChar == outChar) ? pThis : scriptContext->GetLibrary()->GetCharStringCache().GetStringForChar(outChar);
}

return ToCaseCore(pThis, ToUpper);
return ToCaseCore<true, true>(pThis);
}

Var JavascriptString::ToCaseCore(JavascriptString* pThis, ToCase toCase)
template<bool toUpper, bool useInvariant>
JavascriptString* JavascriptString::ToCaseCore(JavascriptString* pThis)
{
Var resultVar = nullptr;
EnterPinnedScope((volatile void**)& pThis);
charcount_t count = pThis->GetLength();

const char16* inStr = pThis->GetString();
const char16* inStrLim = inStr + count;
const char16* i = inStr;

// Try to find out the chars that do not need casing (in the ASCII range)
if (toCase == ToUpper)
{
while (i < inStrLim)
{
// first range of ascii lower-case (97-122)
// second range of ascii lower-case (223-255)
// non-ascii chars (255+)
if (*i >= 'a')
{
if (*i <= 'z') { break; }
if (*i >= 223) { break; }
}
i++;
}
}
else
using namespace PlatformAgnostic::UnicodeText;
if (pThis->GetLength() == 0)
{
Assert(toCase == ToLower);
while (i < inStrLim)
{
// first range of ascii uppercase (65-90)
// second range of ascii uppercase (192-222)
// non-ascii chars (255+)
if (*i >= 'A')
{
if (*i <= 'Z') { break; }
if (*i >= 192)
{
if (*i < 223) { break; }
if (*i >= 255) { break; }
}
}
i++;
}
return pThis;
}

// If no char needs casing, return immediately
if (i == inStrLim) { return pThis; }
ScriptContext* scriptContext = pThis->type->GetScriptContext();

// Otherwise, copy the string and start casing
charcount_t countToCase = (charcount_t)(inStrLim - i);
BufferStringBuilder builder(count, pThis->type->GetScriptContext());
char16 *outStr = builder.DangerousGetWritableBuffer();
ApiError error = ApiError::NoError;

char16* outStrLim = outStr + count;
char16 *o = outStr;
// pre-flight to get the length required, as it may be longer than the original string
// NOTE: ICU and Win32(/POSIX) implementations of these functions differ slightly in how to get the required number of characters.
// For Win32 (LCMapStringEx), you must provide nullptr/0, as providing a buffer that is too small will cause an error and will *not*
// report the number of characters required. For ICU, however, you can provide a buffer that is too short, and it will still return
// the length it actually needs.
// TODO: Investigate pre-allocating buffers for ICU, as the ICU case will be the default going forward
charcount_t requiredStringLength = ChangeStringLinguisticCase<toUpper, useInvariant>(pThis->GetSz(), pThis->GetLength(), nullptr, 0, &error);
Assert(error == ApiError::NoError || error == ApiError::InsufficientBuffer);

while (o < outStrLim)
{
*o++ = *inStr++;
}
// REVIEW: this assert may be too defensive if strings can get shorter through upper/lower casing
Assert(requiredStringLength >= pThis->GetLength());

if (toCase == ToUpper)
if (requiredStringLength == 1)
{
#if DBG
DWORD converted =
#endif
PlatformAgnostic::UnicodeText::ChangeStringCaseInPlace(
PlatformAgnostic::UnicodeText::CaseFlags::CaseFlagsUpper, outStrLim - countToCase, countToCase);

Assert(converted == countToCase);
// Fast path for one-char strings
char16 buffer[2] = { pThis->GetSz()[0], 0 };
charcount_t actualStringLength = ChangeStringLinguisticCase<toUpper, useInvariant>(pThis->GetSz(), pThis->GetLength(), buffer, 2, &error);
Assert(actualStringLength == 1 && error == ApiError::NoError);
return scriptContext->GetLibrary()->GetCharStringCache().GetStringForChar(buffer[0]);
}
else
{
Assert(toCase == ToLower);
#if DBG
DWORD converted =
#endif
PlatformAgnostic::UnicodeText::ChangeStringCaseInPlace(
PlatformAgnostic::UnicodeText::CaseFlags::CaseFlagsLower, outStrLim - countToCase, countToCase);

Assert(converted == countToCase);
charcount_t bufferLength = requiredStringLength + 1;
char16* buffer = RecyclerNewArrayLeaf(scriptContext->GetRecycler(), char16, bufferLength);
charcount_t actualStringLength = ChangeStringLinguisticCase<toUpper, useInvariant>(pThis->GetSz(), pThis->GetLength(), buffer, bufferLength, &error);
Assert(actualStringLength == requiredStringLength && error == ApiError::NoError);
return JavascriptString::NewWithBuffer(buffer, actualStringLength, scriptContext);
}
resultVar = builder.ToString();
LeavePinnedScope(); // pThis

return resultVar;
}

Var JavascriptString::EntryTrim(RecyclableObject* function, CallInfo callInfo, ...)
Expand Down Expand Up @@ -3320,48 +3255,6 @@ namespace Js

return builder.ToString();
}
Var JavascriptString::ToLocaleCaseHelper(Var thisObj, bool toUpper, ScriptContext *scriptContext)
{
using namespace PlatformAgnostic::UnicodeText;

JavascriptString * pThis = JavascriptOperators::TryFromVar<JavascriptString>(thisObj);

if (!pThis)
{
pThis = JavascriptConversion::ToString(thisObj, scriptContext);
}

uint32 strLength = pThis->GetLength();
if (strLength == 0)
{
return pThis;
}

// Get the number of chars in the mapped string.
CaseFlags caseFlags = (toUpper ? CaseFlags::CaseFlagsUpper : CaseFlags::CaseFlagsLower);
const char16* str = pThis->GetString();
ApiError err = ApiError::NoError;
int32 count = PlatformAgnostic::UnicodeText::ChangeStringLinguisticCase(caseFlags, str, strLength, nullptr, 0, &err);

if (count <= 0)
{
AssertMsg(err != ApiError::NoError, "LCMapString failed");
Throw::InternalError();
}

BufferStringBuilder builder(count, scriptContext);
char16* stringBuffer = builder.DangerousGetWritableBuffer();

int count1 = PlatformAgnostic::UnicodeText::ChangeStringLinguisticCase(caseFlags, str, strLength, stringBuffer, count, &err);

if (count1 <= 0)
{
AssertMsg(err != ApiError::NoError, "LCMapString failed");
Throw::InternalError();
}

return builder.ToString();
}

int JavascriptString::IndexOfUsingJmpTable(JmpTable jmpTable, const char16* inputStr, charcount_t len, const char16* searchStr, int searchLen, int position)
{
Expand Down
10 changes: 4 additions & 6 deletions deps/chakrashim/core/lib/Runtime/Library/JavascriptString.h
Original file line number Diff line number Diff line change
Expand Up @@ -135,13 +135,10 @@ namespace Js
static int strcmp(JavascriptString *string1, JavascriptString *string2);

private:
enum ToCase{
ToLower,
ToUpper
};
char16* GetSzCopy(); // get a copy of the inner string without compacting the chunks

static Var ToCaseCore(JavascriptString* pThis, ToCase toCase);
template<bool toUpper, bool useInvariant>
static JavascriptString* ToCaseCore(JavascriptString* pThis);
static int IndexOfUsingJmpTable(JmpTable jmpTable, const char16* inputStr, charcount_t len, const char16* searchStr, int searchLen, int position);
static int LastIndexOfUsingJmpTable(JmpTable jmpTable, const char16* inputStr, charcount_t len, const char16* searchStr, charcount_t searchLen, charcount_t position);

Expand Down Expand Up @@ -331,7 +328,8 @@ namespace Js
static void SearchValueHelper(ScriptContext* scriptContext, Var aValue, JavascriptRegExp ** ppSearchRegEx, JavascriptString ** ppSearchString);
static void ReplaceValueHelper(ScriptContext* scriptContext, Var aValue, JavascriptFunction ** ppReplaceFn, JavascriptString ** ppReplaceString);

static Var ToLocaleCaseHelper(Var thisObj, bool toUpper, ScriptContext *scriptContext);
template<bool toUpper>
static JavascriptString* ToLocaleCaseHelper(JavascriptString* thisObj);

static void InstantiateForceInlinedMembers();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@
<ClInclude Include="PerfTrace.h" />
<ClInclude Include="RuntimePlatformAgnosticPch.h" />
<ClInclude Include="UnicodeText.h" />
<ClInclude Include="ChakraICU.h" Condition="'$(UseICU)'=='true'" />
<ClInclude Include="ChakraICU.h" />
<ClInclude Include="UnicodeTextInternal.h" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\JITIDL\Chakra.JITIDL.vcxproj">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,9 @@
<ClInclude Include="PerfTrace.h">
<Filter>Interfaces</Filter>
</ClInclude>
<ClInclude Include="IPlatformAgnosticResource.h">
<Filter>Interfaces</Filter>
</ClInclude>
<ClInclude Include="ChakraICU.h">
<Filter>Interfaces</Filter>
</ClInclude>
<ClInclude Include="UnicodeTextInternal.h" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,13 @@ namespace PlatformAgnostic
namespace Internal
{
template <typename CharType>
inline bool isDigit(__in CharType c)
bool isDigit(__in CharType c)
{
return c >= '0' && c <= '9';
}

template <typename CharType>
inline int readNumber(__inout CharType* &str)
int readNumber(__inout CharType* &str)
{
int num = 0;

Expand Down
Loading

0 comments on commit 3409177

Please sign in to comment.