[regexp] Stricter asserts in RegExpPrototypeExecBodyWithoutResult

Previously the fast path only asserted the correct instance types; but
when reading lastIndex we additionally rely on a specific object
shape.  This is checked by HasInitialRegExpMap().

Bug: chromium:1024758
Change-Id: I0b401ffb246dd47153caf798446d8d41bc84bc8e
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1924354
Commit-Queue: Jakob Gruber <jgruber@chromium.org>
Auto-Submit: Jakob Gruber <jgruber@chromium.org>
Reviewed-by: Sigurd Schneider <sigurds@chromium.org>
Cr-Commit-Position: refs/heads/master@{#65071}
This commit is contained in:
Jakob Gruber 2019-11-20 14:56:20 +01:00 committed by Commit Bot
parent 3c98a2a36a
commit b5bdf0a8f4
5 changed files with 21 additions and 61 deletions

View File

@ -702,51 +702,6 @@ TNode<BoolT> RegExpBuiltinsAssembler::IsFastRegExpNoPrototype(
return var_result.value();
}
// We also return true if exec is undefined (and hence per spec)
// the original {exec} will be used.
TNode<BoolT> RegExpBuiltinsAssembler::IsFastRegExpWithOriginalExec(
TNode<Context> context, TNode<JSRegExp> object) {
CSA_ASSERT(this, TaggedIsNotSmi(object));
Label out(this);
Label check_last_index(this);
TVARIABLE(BoolT, var_result);
#ifdef V8_ENABLE_FORCE_SLOW_PATH
var_result = BoolConstant(false);
GotoIfForceSlowPath(&out);
#endif
TNode<BoolT> is_regexp = HasInstanceType(object, JS_REG_EXP_TYPE);
var_result = is_regexp;
GotoIfNot(is_regexp, &out);
TNode<NativeContext> native_context = LoadNativeContext(context);
TNode<Object> original_exec =
LoadContextElement(native_context, Context::REGEXP_EXEC_FUNCTION_INDEX);
TNode<Object> regexp_exec =
GetProperty(context, object, isolate()->factory()->exec_string());
TNode<BoolT> has_initialexec = TaggedEqual(regexp_exec, original_exec);
var_result = has_initialexec;
GotoIf(has_initialexec, &check_last_index);
TNode<BoolT> is_undefined = IsUndefined(regexp_exec);
var_result = is_undefined;
GotoIfNot(is_undefined, &out);
Goto(&check_last_index);
BIND(&check_last_index);
// The smi check is required to omit ToLength(lastIndex) calls with possible
// user-code execution on the fast path.
TNode<Object> last_index = FastLoadLastIndexBeforeSmiCheck(object);
var_result = TaggedIsPositiveSmi(last_index);
Goto(&out);
BIND(&out);
return var_result.value();
}
TNode<BoolT> RegExpBuiltinsAssembler::IsFastRegExpNoPrototype(
TNode<Context> context, TNode<Object> object) {
CSA_ASSERT(this, TaggedIsNotSmi(object));

View File

@ -115,10 +115,6 @@ class RegExpBuiltinsAssembler : public CodeStubAssembler {
TNode<BoolT> IsFastRegExpNoPrototype(TNode<Context> context,
TNode<Object> object, TNode<Map> map);
// For debugging only. Uses a slow GetProperty call to fetch object.exec.
TNode<BoolT> IsFastRegExpWithOriginalExec(TNode<Context> context,
TNode<JSRegExp> object);
void BranchIfFastRegExpResult(const TNode<Context> context,
const TNode<Object> object,
Label* if_isunmodified, Label* if_ismodified);

View File

@ -57,8 +57,7 @@ namespace regexp {
}
case (Object): {
// 4. Let C be ? SpeciesConstructor(R, %RegExp%).
const regexpFun =
UnsafeCast<JSFunction>(nativeContext[REGEXP_FUNCTION_INDEX]);
const regexpFun = LoadRegExpFunction(nativeContext);
const speciesConstructor =
UnsafeCast<Constructor>(SpeciesConstructor(receiver, regexpFun));

View File

@ -25,12 +25,8 @@ namespace regexp {
return SelectBooleanConstant(matchIndices != Null);
}
extern macro RegExpBuiltinsAssembler::IsFastRegExpWithOriginalExec(
implicit context: Context)(JSRegExp): bool;
transitioning builtin RegExpPrototypeTestFast(implicit context: Context)(
receiver: JSRegExp, string: String): Object {
assert(IsFastRegExpWithOriginalExec(receiver));
RegExpPrototypeExecBodyWithoutResultFast(receiver, string)
otherwise return False;
return True;

View File

@ -72,7 +72,9 @@ namespace regexp {
Context)(
maybeRegexp: JSReceiver, string: String,
isFastPath: constexpr bool): RegExpMatchInfo labels IfDidNotMatch {
if (!isFastPath) {
if (isFastPath) {
assert(HasInitialRegExpMap(maybeRegexp));
} else {
IncrementUseCounter(context, SmiConstant(kRegExpExecCalledOnSlowRegExp));
if (!Is<JSRegExp>(maybeRegexp)) {
ThrowTypeError(
@ -153,11 +155,24 @@ namespace regexp {
return ConstructNewResultFromMatchInfo(maybeRegexp, matchIndices, string);
}
macro LoadRegExpFunction(implicit context: Context)(
nativeContext: NativeContext): JSFunction {
return UnsafeCast<JSFunction>(nativeContext[REGEXP_FUNCTION_INDEX]);
}
// Note this doesn't guarantee const-ness of object properties, just
// unchanged object layout.
macro HasInitialRegExpMap(implicit context: Context)(o: HeapObject): bool {
const nativeContext = LoadNativeContext(context);
const function = LoadRegExpFunction(nativeContext);
const initialMap = UnsafeCast<Map>(function.prototype_or_initial_map);
return initialMap == o.map;
}
macro IsReceiverInitialRegExpPrototype(implicit context:
Context)(receiver: Object): bool {
const nativeContext: NativeContext = LoadNativeContext(context);
const regexpFun =
UnsafeCast<JSFunction>(nativeContext[REGEXP_FUNCTION_INDEX]);
const nativeContext = LoadNativeContext(context);
const regexpFun = LoadRegExpFunction(nativeContext);
const initialMap = UnsafeCast<Map>(regexpFun.prototype_or_initial_map);
const initialPrototype: HeapObject = initialMap.prototype;
return TaggedEqual(receiver, initialPrototype);
@ -374,8 +389,7 @@ namespace regexp {
@export
transitioning macro RegExpCreate(implicit context: Context)(
nativeContext: NativeContext, maybeString: JSAny, flags: String): JSAny {
const regexpFun =
UnsafeCast<JSFunction>(nativeContext[REGEXP_FUNCTION_INDEX]);
const regexpFun = LoadRegExpFunction(nativeContext);
const initialMap = UnsafeCast<Map>(regexpFun.prototype_or_initial_map);
return RegExpCreate(initialMap, maybeString, flags);
}