Skip to content

Commit a5a5b8f

Browse files
committed
fix issues with proxy in TypedArray.from and String.replace
1 parent cafc256 commit a5a5b8f

File tree

9 files changed

+71
-24
lines changed

9 files changed

+71
-24
lines changed

lib/Runtime/Library/JavascriptRegularExpression.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -777,9 +777,9 @@ using namespace Js;
777777

778778
Var replaceValue = (args.Info.Count > 2) ? args[2] : scriptContext->GetLibrary()->GetUndefined();
779779

780-
if (VarIs<JavascriptFunction>(replaceValue))
780+
if (JavascriptConversion::IsCallable(replaceValue))
781781
{
782-
JavascriptFunction* replaceFunction = VarTo<JavascriptFunction>(replaceValue);
782+
RecyclableObject* replaceFunction = VarTo<RecyclableObject>(replaceValue);
783783
return RegexHelper::RegexReplaceFunction(scriptContext, thisObj, string, replaceFunction);
784784
}
785785
else

lib/Runtime/Library/JavascriptString.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1614,7 +1614,7 @@ namespace Js
16141614
JavascriptString * pMatch = nullptr;
16151615

16161616
JavascriptString * pReplace = nullptr;
1617-
JavascriptFunction* replacefn = nullptr;
1617+
RecyclableObject* replacefn = nullptr;
16181618

16191619
SearchValueHelper(scriptContext, ((args.Info.Count > 1)?args[1]:scriptContext->GetLibrary()->GetNull()), &pRegEx, &pMatch);
16201620
ReplaceValueHelper(scriptContext, ((args.Info.Count > 2) ? args[2] : scriptContext->GetLibrary()->GetUndefined()), &replacefn, &pReplace);
@@ -1667,14 +1667,14 @@ namespace Js
16671667
}
16681668
}
16691669

1670-
void JavascriptString::ReplaceValueHelper(ScriptContext* scriptContext, Var aValue, JavascriptFunction ** ppReplaceFn, JavascriptString ** ppReplaceString)
1670+
void JavascriptString::ReplaceValueHelper(ScriptContext* scriptContext, Var aValue, RecyclableObject ** ppReplaceFn, JavascriptString ** ppReplaceString)
16711671
{
16721672
*ppReplaceFn = nullptr;
16731673
*ppReplaceString = nullptr;
16741674

1675-
if (VarIs<JavascriptFunction>(aValue))
1675+
if (JavascriptConversion::IsCallable(aValue))
16761676
{
1677-
*ppReplaceFn = VarTo<JavascriptFunction>(aValue);
1677+
*ppReplaceFn = VarTo<RecyclableObject>(aValue);
16781678
}
16791679
else if (VarIs<JavascriptString>(aValue))
16801680
{

lib/Runtime/Library/JavascriptString.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -334,7 +334,7 @@ namespace Js
334334
static Var StringBracketHelper(Arguments args, ScriptContext *scriptContext, const char16 (&tag)[N1], const char16 (&prop)[N2]);
335335

336336
static void SearchValueHelper(ScriptContext* scriptContext, Var aValue, JavascriptRegExp ** ppSearchRegEx, JavascriptString ** ppSearchString);
337-
static void ReplaceValueHelper(ScriptContext* scriptContext, Var aValue, JavascriptFunction ** ppReplaceFn, JavascriptString ** ppReplaceString);
337+
static void ReplaceValueHelper(ScriptContext* scriptContext, Var aValue, RecyclableObject** ppReplaceFn, JavascriptString ** ppReplaceString);
338338

339339
template<bool toUpper>
340340
static JavascriptString* ToLocaleCaseHelper(JavascriptString* thisObj);

lib/Runtime/Library/RegexHelper.cpp

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -954,7 +954,7 @@ namespace Js
954954
return RegexEs6ReplaceImpl(scriptContext, thisObj, input, appendReplacement, noResult);
955955
}
956956

957-
Var RegexHelper::RegexEs6ReplaceImpl(ScriptContext* scriptContext, RecyclableObject* thisObj, JavascriptString* input, JavascriptFunction* replaceFn)
957+
Var RegexHelper::RegexEs6ReplaceImpl(ScriptContext* scriptContext, RecyclableObject* thisObj, JavascriptString* input, RecyclableObject* replaceFn)
958958
{
959959
auto appendReplacement = [&](
960960
CompoundString::Builder<64 * sizeof(void *) / sizeof(char16)>& resultBuilder,
@@ -978,6 +978,9 @@ namespace Js
978978
ushort argCount = (ushort) numberOfCaptures + 4;
979979

980980
PROBE_STACK_NO_DISPOSE(scriptContext, argCount * sizeof(Var));
981+
982+
ThreadContext* threadContext = scriptContext->GetThreadContext();
983+
981984
Var* args = (Var*) _alloca(argCount * sizeof(Var));
982985

983986
args[0] = scriptContext->GetLibrary()->GetUndefined();
@@ -989,10 +992,9 @@ namespace Js
989992
}
990993
args[numberOfCaptures + 2] = JavascriptNumber::ToVar(position, scriptContext);
991994
args[numberOfCaptures + 3] = input;
992-
993-
Js::Var replaceFnResult = scriptContext->GetThreadContext()->ExecuteImplicitCall(replaceFn, Js::ImplicitCall_Accessor, [=]()->Js::Var
995+
Js::Var replaceFnResult = threadContext->ExecuteImplicitCall(replaceFn, Js::ImplicitCall_Accessor, [=]()->Js::Var
994996
{
995-
return replaceFn->CallFunction(Arguments(CallInfo(argCount), args));
997+
return JavascriptFunction::CallFunction<true>(replaceFn, replaceFn->GetEntryPoint(), Arguments(CallInfo(argCount), args));
996998
});
997999
JavascriptString* replace = JavascriptConversion::ToString(replaceFnResult, scriptContext);
9981000

@@ -1246,7 +1248,7 @@ namespace Js
12461248
return newString;
12471249
}
12481250

1249-
Var RegexHelper::RegexReplaceImpl(ScriptContext* scriptContext, RecyclableObject* thisObj, JavascriptString* input, JavascriptFunction* replacefn)
1251+
Var RegexHelper::RegexReplaceImpl(ScriptContext* scriptContext, RecyclableObject* thisObj, JavascriptString* input, RecyclableObject* replacefn)
12501252
{
12511253
ScriptConfiguration const * scriptConfig = scriptContext->GetConfig();
12521254

@@ -1265,7 +1267,7 @@ namespace Js
12651267
}
12661268

12671269
// String.prototype.replace, replace value is a function (ES5 15.5.4.11)
1268-
Var RegexHelper::RegexEs5ReplaceImpl(ScriptContext* scriptContext, JavascriptRegExp* regularExpression, JavascriptString* input, JavascriptFunction* replacefn)
1270+
Var RegexHelper::RegexEs5ReplaceImpl(ScriptContext* scriptContext, JavascriptRegExp* regularExpression, JavascriptString* input, RecyclableObject* replacefn)
12691271
{
12701272
UnifiedRegex::RegexPattern* pattern = regularExpression->GetPattern();
12711273
JavascriptString* newString = nullptr;
@@ -1348,7 +1350,7 @@ namespace Js
13481350
ThreadContext* threadContext = scriptContext->GetThreadContext();
13491351
Var replaceVar = threadContext->ExecuteImplicitCall(replacefn, ImplicitCall_Accessor, [=]()->Js::Var
13501352
{
1351-
return replacefn->CallFunction(Arguments(CallInfo(UInt16Math::Add(numGroups, 3)), replaceArgs));
1353+
return JavascriptFunction::CallFunction<true>(replacefn, replacefn->GetEntryPoint(), Arguments(CallInfo(UInt16Math::Add(numGroups, 3)), replaceArgs));
13521354
});
13531355
JavascriptString* replace = JavascriptConversion::ToString(replaceVar, scriptContext);
13541356
concatenated.Append(input, offset, lastActualMatch.offset - offset);
@@ -1481,7 +1483,7 @@ namespace Js
14811483
return concatenated.ToString();
14821484
}
14831485

1484-
Var RegexHelper::StringReplace(ScriptContext* scriptContext, JavascriptString* match, JavascriptString* input, JavascriptFunction* replacefn)
1486+
Var RegexHelper::StringReplace(ScriptContext* scriptContext, JavascriptString* match, JavascriptString* input, RecyclableObject* replacefn)
14851487
{
14861488
CharCount indexMatched = JavascriptString::strstr(input, match, true);
14871489
Assert(match->GetScriptContext() == scriptContext);
@@ -2318,7 +2320,7 @@ namespace Js
23182320
return RegexHelper::CheckCrossContextAndMarshalResult(result, entryFunctionContext);
23192321
}
23202322

2321-
Var RegexHelper::RegexReplaceFunction(ScriptContext* entryFunctionContext, RecyclableObject* thisObj, JavascriptString* input, JavascriptFunction* replacefn)
2323+
Var RegexHelper::RegexReplaceFunction(ScriptContext* entryFunctionContext, RecyclableObject* thisObj, JavascriptString* input, RecyclableObject* replacefn)
23222324
{
23232325
Var result = RegexHelper::RegexReplaceImpl(entryFunctionContext, thisObj, input, replacefn);
23242326
return RegexHelper::CheckCrossContextAndMarshalResult(result, entryFunctionContext);

lib/Runtime/Library/RegexHelper.h

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -105,9 +105,9 @@ namespace Js
105105
static Var RegexReplaceResultUsed(ScriptContext* entryFunctionContext, JavascriptRegExp* regularExpression, JavascriptString* input, JavascriptString* replace);
106106
static Var RegexReplaceResultNotUsed(ScriptContext* entryFunctionContext, JavascriptRegExp* regularExpression, JavascriptString* input, JavascriptString* replace);
107107
static Var RegexReplace(ScriptContext* scriptContext, RecyclableObject* thisObj, JavascriptString* input, JavascriptString* replace, bool noResult);
108-
static Var RegexReplaceFunction(ScriptContext* scriptContext, RecyclableObject* thisObj, JavascriptString* input, JavascriptFunction* replacefn);
108+
static Var RegexReplaceFunction(ScriptContext* scriptContext, RecyclableObject* thisObj, JavascriptString* input, RecyclableObject* replacefn);
109109
static Var StringReplace(JavascriptString* regularExpression, JavascriptString* input, JavascriptString* replace);
110-
static Var StringReplace(ScriptContext* scriptContext, JavascriptString* regularExpression, JavascriptString* input, JavascriptFunction* replacefn);
110+
static Var StringReplace(ScriptContext* scriptContext, JavascriptString* regularExpression, JavascriptString* input, RecyclableObject* replacefn);
111111
static Var RegexSplitResultUsed(ScriptContext* scriptContext, JavascriptRegExp* regularExpression, JavascriptString* input, CharCount limit);
112112
static Var RegexSplitResultUsedAndMayBeTemp(void *const stackAllocationPointer, ScriptContext* scriptContext, JavascriptRegExp* regularExpression, JavascriptString* input, CharCount limit);
113113
static Var RegexSplitResultNotUsed(ScriptContext* scriptContext, JavascriptRegExp* regularExpression, JavascriptString* input, CharCount limit);
@@ -129,12 +129,12 @@ namespace Js
129129
static Var RegexReplaceImpl(ScriptContext* scriptContext, RecyclableObject* thisObj, JavascriptString* input, JavascriptString* replace, bool noResult);
130130
static bool IsRegexSymbolReplaceObservable(RecyclableObject* instance, ScriptContext* scriptContext);
131131
static Var RegexEs6ReplaceImpl(ScriptContext* scriptContext, RecyclableObject* thisObj, JavascriptString* input, JavascriptString* replace, bool noResult);
132-
static Var RegexEs6ReplaceImpl(ScriptContext* scriptContext, RecyclableObject* thisObj, JavascriptString* input, JavascriptFunction* replaceFn);
132+
static Var RegexEs6ReplaceImpl(ScriptContext* scriptContext, RecyclableObject* thisObj, JavascriptString* input, RecyclableObject* replaceFn);
133133
template<typename ReplacementFn>
134134
static Var RegexEs6ReplaceImpl(ScriptContext* scriptContext, RecyclableObject* thisObj, JavascriptString* input, ReplacementFn appendReplacement, bool noResult);
135135
static Var RegexEs5ReplaceImpl(ScriptContext* scriptContext, JavascriptRegExp* regularExpression, JavascriptString* input, JavascriptString* replace, bool noResult);
136-
static Var RegexReplaceImpl(ScriptContext* scriptContext, RecyclableObject* thisObj, JavascriptString* input, JavascriptFunction* replacefn);
137-
static Var RegexEs5ReplaceImpl(ScriptContext* scriptContext, JavascriptRegExp* regularExpression, JavascriptString* input, JavascriptFunction* replacefn);
136+
static Var RegexReplaceImpl(ScriptContext* scriptContext, RecyclableObject* thisObj, JavascriptString* input, RecyclableObject* replacefn);
137+
static Var RegexEs5ReplaceImpl(ScriptContext* scriptContext, JavascriptRegExp* regularExpression, JavascriptString* input, RecyclableObject* replacefn);
138138
static Var RegexSearchImpl(ScriptContext* scriptContext, JavascriptRegExp* regularExpression, JavascriptString* input);
139139
inline static UnifiedRegex::RegexPattern *GetSplitPattern(ScriptContext* scriptContext, JavascriptRegExp *regularExpression);
140140
static bool IsRegexSymbolSplitObservable(RecyclableObject* instance, ScriptContext* scriptContext);

lib/Runtime/Library/TypedArray.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1423,17 +1423,17 @@ namespace Js
14231423
}
14241424

14251425
bool mapping = false;
1426-
JavascriptFunction* mapFn = nullptr;
1426+
RecyclableObject* mapFn = nullptr;
14271427
Var mapFnThisArg = nullptr;
14281428

14291429
if (args.Info.Count >= 3)
14301430
{
1431-
if (!VarIs<JavascriptFunction>(args[2]))
1431+
if (!JavascriptConversion::IsCallable(args[2]))
14321432
{
14331433
JavascriptError::ThrowTypeError(scriptContext, JSERR_FunctionArgument_NeedFunction, _u("[TypedArray].from"));
14341434
}
14351435

1436-
mapFn = VarTo<JavascriptFunction>(args[2]);
1436+
mapFn = VarTo<RecyclableObject>(args[2]);
14371437

14381438
if (args.Info.Count >= 4)
14391439
{

test/Regex/regex_replacefn.baseline

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,6 @@ abcxyz
1818
0
1919
0
2020
_abc_abc
21+
0
22+
0
23+
_abc_abc

test/Regex/regex_replacefn.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,3 +44,9 @@ var str = "abcabc";
4444
re.lastIndex = 3;
4545
WScript.Echo(str.replace(re, replacefn));
4646

47+
let proxy = new Proxy(replacefn, {});
48+
var re = /abc/g;
49+
var str = "abcabc";
50+
re.lastIndex = 3;
51+
WScript.Echo(str.replace(re, proxy));
52+

test/es6/regex-symbols.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -516,6 +516,42 @@ var tests = [
516516
verify("global", "a*b*c", 2, "g");
517517
}
518518
},
519+
{
520+
name: "RegExp.prototype[@@replace] should call proxy 'replaceValue' to get the substitution when 'replaceValue' is callable",
521+
body: function () {
522+
var pattern = "(-)(=)";
523+
var string = "a-=b-=c";
524+
var replace = "*$&$1";
525+
526+
function verify(assertMessagePrefix, expectedResult, expectedCallCount, flags) {
527+
withObservableRegExp(function () {
528+
var passedCorrectArguments = false;
529+
var callCount = 0;
530+
var re = new RegExp(pattern, flags);
531+
var replace = function (matched, capture1, capture2, position, stringArg) {
532+
callCount += 1;
533+
passedCorrectArguments =
534+
matched === "-=" &&
535+
capture1 === "-" &&
536+
capture2 === "=" &&
537+
(position === 1 || position === 4) &&
538+
stringArg === string;
539+
return "*";
540+
}
541+
var replaceProxy = new Proxy(replace, {});
542+
543+
var result = re[Symbol.replace](string, replaceProxy);
544+
545+
assert.areEqual(expectedCallCount, callCount, assertMessagePrefix + ": callCount");
546+
assert.isTrue(passedCorrectArguments, assertMessagePrefix + ": replace function arguments");
547+
assert.areEqual(expectedResult, result, assertMessagePrefix + ": result");
548+
})
549+
}
550+
551+
verify("non-global", "a*b-=c", 1, "");
552+
verify("global", "a*b*c", 2, "g");
553+
}
554+
},
519555
{
520556
name: "RegExp.prototype[@@replace] should 'Get' 'global' when it is overridden",
521557
body: function () {

0 commit comments

Comments
 (0)