From b5bdf0a8f40ab5c7f2845653fe40495e488c6e4c Mon Sep 17 00:00:00 2001 From: Jakob Gruber Date: Wed, 20 Nov 2019 14:56:20 +0100 Subject: [PATCH] [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 Auto-Submit: Jakob Gruber Reviewed-by: Sigurd Schneider Cr-Commit-Position: refs/heads/master@{#65071} --- src/builtins/builtins-regexp-gen.cc | 45 ----------------------------- src/builtins/builtins-regexp-gen.h | 4 --- src/builtins/regexp-match-all.tq | 3 +- src/builtins/regexp-test.tq | 4 --- src/builtins/regexp.tq | 26 +++++++++++++---- 5 files changed, 21 insertions(+), 61 deletions(-) diff --git a/src/builtins/builtins-regexp-gen.cc b/src/builtins/builtins-regexp-gen.cc index 25dcc1bec3..31fd022ef0 100644 --- a/src/builtins/builtins-regexp-gen.cc +++ b/src/builtins/builtins-regexp-gen.cc @@ -702,51 +702,6 @@ TNode 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 RegExpBuiltinsAssembler::IsFastRegExpWithOriginalExec( - TNode context, TNode 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 is_regexp = HasInstanceType(object, JS_REG_EXP_TYPE); - - var_result = is_regexp; - GotoIfNot(is_regexp, &out); - - TNode native_context = LoadNativeContext(context); - TNode original_exec = - LoadContextElement(native_context, Context::REGEXP_EXEC_FUNCTION_INDEX); - - TNode regexp_exec = - GetProperty(context, object, isolate()->factory()->exec_string()); - - TNode has_initialexec = TaggedEqual(regexp_exec, original_exec); - var_result = has_initialexec; - GotoIf(has_initialexec, &check_last_index); - TNode 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 last_index = FastLoadLastIndexBeforeSmiCheck(object); - var_result = TaggedIsPositiveSmi(last_index); - Goto(&out); - - BIND(&out); - return var_result.value(); -} - TNode RegExpBuiltinsAssembler::IsFastRegExpNoPrototype( TNode context, TNode object) { CSA_ASSERT(this, TaggedIsNotSmi(object)); diff --git a/src/builtins/builtins-regexp-gen.h b/src/builtins/builtins-regexp-gen.h index d4994b4135..e9b5c1f0b2 100644 --- a/src/builtins/builtins-regexp-gen.h +++ b/src/builtins/builtins-regexp-gen.h @@ -115,10 +115,6 @@ class RegExpBuiltinsAssembler : public CodeStubAssembler { TNode IsFastRegExpNoPrototype(TNode context, TNode object, TNode map); - // For debugging only. Uses a slow GetProperty call to fetch object.exec. - TNode IsFastRegExpWithOriginalExec(TNode context, - TNode object); - void BranchIfFastRegExpResult(const TNode context, const TNode object, Label* if_isunmodified, Label* if_ismodified); diff --git a/src/builtins/regexp-match-all.tq b/src/builtins/regexp-match-all.tq index 13ba27ec32..e46367f97a 100644 --- a/src/builtins/regexp-match-all.tq +++ b/src/builtins/regexp-match-all.tq @@ -57,8 +57,7 @@ namespace regexp { } case (Object): { // 4. Let C be ? SpeciesConstructor(R, %RegExp%). - const regexpFun = - UnsafeCast(nativeContext[REGEXP_FUNCTION_INDEX]); + const regexpFun = LoadRegExpFunction(nativeContext); const speciesConstructor = UnsafeCast(SpeciesConstructor(receiver, regexpFun)); diff --git a/src/builtins/regexp-test.tq b/src/builtins/regexp-test.tq index 44f8d019d1..22482698a6 100644 --- a/src/builtins/regexp-test.tq +++ b/src/builtins/regexp-test.tq @@ -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; diff --git a/src/builtins/regexp.tq b/src/builtins/regexp.tq index 3a9fe9d694..a7ce117d68 100644 --- a/src/builtins/regexp.tq +++ b/src/builtins/regexp.tq @@ -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(maybeRegexp)) { ThrowTypeError( @@ -153,11 +155,24 @@ namespace regexp { return ConstructNewResultFromMatchInfo(maybeRegexp, matchIndices, string); } + macro LoadRegExpFunction(implicit context: Context)( + nativeContext: NativeContext): JSFunction { + return UnsafeCast(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(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(nativeContext[REGEXP_FUNCTION_INDEX]); + const nativeContext = LoadNativeContext(context); + const regexpFun = LoadRegExpFunction(nativeContext); const initialMap = UnsafeCast(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(nativeContext[REGEXP_FUNCTION_INDEX]); + const regexpFun = LoadRegExpFunction(nativeContext); const initialMap = UnsafeCast(regexpFun.prototype_or_initial_map); return RegExpCreate(initialMap, maybeString, flags); }