Skip to content

Commit

Permalink
Implement RegExp.prototype[@@search] in JS
Browse files Browse the repository at this point in the history
Summary: Implement `RegExp.prototype[@search]` in JS

Reviewed By: dulinriley

Differential Revision: D18256656

fbshipit-source-id: af47c639823e1dfda05260ce58f93212201984c3
  • Loading branch information
Kiwi137 authored and facebook-github-bot committed Nov 6, 2019
1 parent bed3129 commit 3d4f268
Show file tree
Hide file tree
Showing 4 changed files with 155 additions and 67 deletions.
2 changes: 1 addition & 1 deletion include/hermes/VM/NativeFunctions.def
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,6 @@ NATIVE_FUNCTION(regExpLastParenGetter)
NATIVE_FUNCTION(regExpLeftContextGetter)
NATIVE_FUNCTION(regExpPrototypeExec)
NATIVE_FUNCTION(regExpPrototypeSymbolReplace)
NATIVE_FUNCTION(regExpPrototypeSymbolSearch)
NATIVE_FUNCTION(regExpPrototypeSymbolSplit)
NATIVE_FUNCTION(regExpPrototypeTest)
NATIVE_FUNCTION(regExpPrototypeToString)
Expand All @@ -265,6 +264,7 @@ NATIVE_FUNCTION(regExpSourceGetter)

#ifndef HERMESVM_USE_JS_LIBRARY_IMPLEMENTATION
NATIVE_FUNCTION(regExpPrototypeSymbolMatch)
NATIVE_FUNCTION(regExpPrototypeSymbolSearch)
#endif

NATIVE_FUNCTION(require)
Expand Down
65 changes: 65 additions & 0 deletions lib/InternalBytecode/RegExp.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,10 +114,75 @@ RegExp.prototype[_SymbolMatch] = function(string) {
}
};

/**
* ES10.0 21.2.5.10
* Search for the first match of `this` regexp pattern in the given string.
* Keeps this.lastIndex the same value as before running this function, and
* ignore the global flag.
* @this a RegExp-like object.
* @param string string to search in.
* @return the index (of string) of the first match, or -1 if there's no match.
*/
RegExp.prototype[_SymbolSearch] = function(string) {
if (new.target)
throw _TypeError('This function cannot be used as a constructor.');

// 1. Let rx be the this value.
var rx = this;
// 2. If Type(rx) is not Object, throw a TypeError exception.
if (typeof rx !== 'object')
throw _TypeError(
'RegExp.prototype[@@search] should be called on a js object',
);
// 3. Let S be ? ToString(string).
var S = HermesInternal.toString(string);
// 4. Let previousLastIndex be ? Get(rx, "lastIndex").
var previousLastIndex = rx.lastIndex;
// 5. If SameValue(previousLastIndex, 0) is false, then
// Note: -0 === +0 returns true, while SameValue(-0, +0) returns false
if (
previousLastIndex !== 0 ||
(previousLastIndex === 0 && 1 / previousLastIndex < 0)
) {
// a. Perform ? Set(rx, "lastIndex", 0, true).
rx.lastIndex = 0;
}
// 6. Let result be ? RegExpExec(rx, S).
var result = HermesInternal.regExpExec(rx, S);
// 7. Let currentLastIndex be ? Get(rx, "lastIndex").
var currentLastIndex = rx.lastIndex;
// 8. If SameValue(currentLastIndex, previousLastIndex) is false, then
// Note: Only differences between === and SameValue are
// 1) SameValue(NaN, NaN) returns true, and
// 2) SameValue(+0, -0) returns false
// The condition of this if statement is
// 1) currentLastIndex and previousLastIndex are not equal and not NaN, or
// 2) currentLastIndex and previousLastIndex are equal, but one is positive
// and the other is negative.
if (
(currentLastIndex !== previousLastIndex &&
currentLastIndex === currentLastIndex) ||
(currentLastIndex === previousLastIndex &&
1 / currentLastIndex !== 1 / previousLastIndex)
) {
// a. Perform ? Set(rx, "lastIndex", previousLastIndex, true).
rx.lastIndex = previousLastIndex;
}
// 9. If result is null, return -1.
if (result === null) return -1;
// 10. Return ? Get(result, "index").
return result.index;
};

Object.defineProperty(RegExp.prototype, _SymbolMatch, builtinFuncDescriptor);
Object.defineProperty(RegExp.prototype, _SymbolSearch, builtinFuncDescriptor);

RegExp.prototype[_SymbolMatch].prototype = undefined;
RegExp.prototype[_SymbolSearch].prototype = undefined;

Object.defineProperty(RegExp.prototype[_SymbolMatch], 'name', {
value: '[Symbol.match]',
});
Object.defineProperty(RegExp.prototype[_SymbolSearch], 'name', {
value: '[Symbol.search]',
});
133 changes: 67 additions & 66 deletions lib/VM/JSLib/RegExp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,16 +88,6 @@ Handle<JSObject> createRegExpConstructor(Runtime *runtime) {
DefinePropertyFlags dpf = DefinePropertyFlags::getDefaultNewPropertyFlags();
dpf.enumerable = 0;

(void)defineMethod(
runtime,
proto,
Predefined::getSymbolID(Predefined::SymbolSearch),
Predefined::getSymbolID(Predefined::squareSymbolSearch),
nullptr,
regExpPrototypeSymbolSearch,
1,
dpf);

(void)defineMethod(
runtime,
proto,
Expand Down Expand Up @@ -174,6 +164,15 @@ Handle<JSObject> createRegExpConstructor(Runtime *runtime) {
regExpPrototypeSymbolMatch,
1,
dpf);
(void)defineMethod(
runtime,
proto,
Predefined::getSymbolID(Predefined::SymbolSearch),
Predefined::getSymbolID(Predefined::squareSymbolSearch),
nullptr,
regExpPrototypeSymbolSearch,
1,
dpf);
#endif // HERMESVM_USE_JS_LIBRARY_IMPLEMENTATION

return cons;
Expand Down Expand Up @@ -916,62 +915,6 @@ regExpLastParenGetter(void *ctx, Runtime *runtime, NativeArgs args) {

return HermesValue::encodeStringValue(
runtime->getPredefinedString(Predefined::emptyString));
} /// ES6.0 21.2.5.9
CallResult<HermesValue>
regExpPrototypeSymbolSearch(void *, Runtime *runtime, NativeArgs args) {
GCScope gcScope{runtime};

// 1. Let rx be the this value.
// 2. If Type(rx) is not Object, throw a TypeError exception.
Handle<JSObject> rx = args.dyncastThis<JSObject>();
if (!rx) {
return runtime->raiseTypeError(
"Calling regExp.prototype[@@search] on a non-object.");
}
// 3. Let S be ToString(string).
// 4. ReturnIfAbrupt(S).
auto strRes = toString_RJS(runtime, args.getArgHandle(0));
if (LLVM_UNLIKELY(strRes == ExecutionStatus::EXCEPTION)) {
return ExecutionStatus::EXCEPTION;
}
auto S = toHandle(runtime, std::move(*strRes));
// 5. Let previousLastIndex be Get(rx, "lastIndex").
// 6. ReturnIfAbrupt(previousLastIndex).
auto propRes = runtime->getNamed(rx, PropCacheID::RegExpLastIndex);
if (LLVM_UNLIKELY(propRes == ExecutionStatus::EXCEPTION)) {
return ExecutionStatus::EXCEPTION;
}
Handle<> previousLastIndex = runtime->makeHandle(propRes.getValue());
// 7. Let status be Set(rx, "lastIndex", 0, true).
auto status = setLastIndex(rx, runtime, 0);
// 8. ReturnIfAbrupt(status).
if (LLVM_UNLIKELY(status == ExecutionStatus::EXCEPTION)) {
return ExecutionStatus::EXCEPTION;
}
// 9. Let result be RegExpExec(rx, S).
// 10. ReturnIfAbrupt(result).
auto execRes = regExpExec(runtime, rx, S);
if (LLVM_UNLIKELY(execRes == ExecutionStatus::EXCEPTION)) {
return ExecutionStatus::EXCEPTION;
}
Handle<> result = runtime->makeHandle(execRes.getValue());
// 11. Let status be Set(rx, "lastIndex", previousLastIndex, true).
status = setLastIndex(rx, runtime, *previousLastIndex);
// 12. ReturnIfAbrupt(status).
if (LLVM_UNLIKELY(status == ExecutionStatus::EXCEPTION)) {
return ExecutionStatus::EXCEPTION;
}
// 13. If result is null, return –1.
if (result->isNull()) {
return HermesValue::encodeNumberValue(-1);
}
// 14. Return Get(result, "index").
auto resultObj = Handle<JSObject>::dyn_vmcast(result);
if (LLVM_UNLIKELY(!resultObj)) {
return ExecutionStatus::EXCEPTION;
}
return JSObject::getNamed_RJS(
resultObj, runtime, Predefined::getSymbolID(Predefined::index));
}

/// ES6.0 21.1.3.14.1
Expand Down Expand Up @@ -1599,6 +1542,64 @@ regExpPrototypeSymbolMatch(void *, Runtime *runtime, NativeArgs args) {
llvm_unreachable(
"RegExp.prototype[@@match] must stop when all matched results are returned.");
}

/// ES6.0 21.2.5.9
CallResult<HermesValue>
regExpPrototypeSymbolSearch(void *, Runtime *runtime, NativeArgs args) {
GCScope gcScope{runtime};

// 1. Let rx be the this value.
// 2. If Type(rx) is not Object, throw a TypeError exception.
Handle<JSObject> rx = args.dyncastThis<JSObject>();
if (!rx) {
return runtime->raiseTypeError(
"Calling regExp.prototype[@@search] on a non-object.");
}
// 3. Let S be ToString(string).
// 4. ReturnIfAbrupt(S).
auto strRes = toString_RJS(runtime, args.getArgHandle(0));
if (LLVM_UNLIKELY(strRes == ExecutionStatus::EXCEPTION)) {
return ExecutionStatus::EXCEPTION;
}
auto S = toHandle(runtime, std::move(*strRes));
// 5. Let previousLastIndex be Get(rx, "lastIndex").
// 6. ReturnIfAbrupt(previousLastIndex).
auto propRes = runtime->getNamed(rx, PropCacheID::RegExpLastIndex);
if (LLVM_UNLIKELY(propRes == ExecutionStatus::EXCEPTION)) {
return ExecutionStatus::EXCEPTION;
}
Handle<> previousLastIndex = runtime->makeHandle(propRes.getValue());
// 7. Let status be Set(rx, "lastIndex", 0, true).
auto status = setLastIndex(rx, runtime, 0);
// 8. ReturnIfAbrupt(status).
if (LLVM_UNLIKELY(status == ExecutionStatus::EXCEPTION)) {
return ExecutionStatus::EXCEPTION;
}
// 9. Let result be RegExpExec(rx, S).
// 10. ReturnIfAbrupt(result).
auto execRes = regExpExec(runtime, rx, S);
if (LLVM_UNLIKELY(execRes == ExecutionStatus::EXCEPTION)) {
return ExecutionStatus::EXCEPTION;
}
Handle<> result = runtime->makeHandle(execRes.getValue());
// 11. Let status be Set(rx, "lastIndex", previousLastIndex, true).
status = setLastIndex(rx, runtime, *previousLastIndex);
// 12. ReturnIfAbrupt(status).
if (LLVM_UNLIKELY(status == ExecutionStatus::EXCEPTION)) {
return ExecutionStatus::EXCEPTION;
}
// 13. If result is null, return –1.
if (result->isNull()) {
return HermesValue::encodeNumberValue(-1);
}
// 14. Return Get(result, "index").
auto resultObj = Handle<JSObject>::dyn_vmcast(result);
if (LLVM_UNLIKELY(!resultObj)) {
return ExecutionStatus::EXCEPTION;
}
return JSObject::getNamed_RJS(
resultObj, runtime, Predefined::getSymbolID(Predefined::index));
}
#endif // HERMESVM_USE_JS_LIBRARY_IMPLEMENTATION

} // namespace vm
Expand Down
22 changes: 22 additions & 0 deletions tools/hvm-bench/regExpSearch.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
*/

(function() {
var numIter = 200000;
var len = 2000;
var s = 'abcd'.repeat(len);
var rx = /d.+d/g;

var _SymbolSearch = Symbol.search;
for (var i = 0; i < numIter; i++) {
rx[_SymbolSearch](s);
}

print('done');
})();

0 comments on commit 3d4f268

Please sign in to comment.