diff options
author | Myles Borins <mylesborins@google.com> | 2019-09-24 11:56:38 -0400 |
---|---|---|
committer | Myles Borins <myles.borins@gmail.com> | 2019-10-07 03:19:23 -0400 |
commit | f7f6c928c1c9c136b7926f892b8a2fda11d8b4b2 (patch) | |
tree | f5edbccb3ffda2573d70a6e291e7157f290e0ae0 /deps/v8/src/builtins/builtins-regexp-gen.cc | |
parent | ffd22e81983056d09c064c59343a0e488236272d (diff) | |
download | android-node-v8-f7f6c928c1c9c136b7926f892b8a2fda11d8b4b2.tar.gz android-node-v8-f7f6c928c1c9c136b7926f892b8a2fda11d8b4b2.tar.bz2 android-node-v8-f7f6c928c1c9c136b7926f892b8a2fda11d8b4b2.zip |
deps: update V8 to 7.8.279.9
PR-URL: https://github.com/nodejs/node/pull/29694
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Gus Caplan <me@gus.host>
Reviewed-By: Jiawen Geng <technicalcute@gmail.com>
Reviewed-By: Michaël Zasso <targos@protonmail.com>
Reviewed-By: Tobias Nießen <tniessen@tnie.de>
Reviewed-By: Ujjwal Sharma <usharma1998@gmail.com>
Diffstat (limited to 'deps/v8/src/builtins/builtins-regexp-gen.cc')
-rw-r--r-- | deps/v8/src/builtins/builtins-regexp-gen.cc | 941 |
1 files changed, 370 insertions, 571 deletions
diff --git a/deps/v8/src/builtins/builtins-regexp-gen.cc b/deps/v8/src/builtins/builtins-regexp-gen.cc index d53518ff7e..f879d70c67 100644 --- a/deps/v8/src/builtins/builtins-regexp-gen.cc +++ b/deps/v8/src/builtins/builtins-regexp-gen.cc @@ -10,6 +10,8 @@ #include "src/builtins/growable-fixed-array-gen.h" #include "src/codegen/code-factory.h" #include "src/codegen/code-stub-assembler.h" +#include "src/codegen/macro-assembler.h" +#include "src/execution/protectors.h" #include "src/heap/factory-inl.h" #include "src/logging/counters.h" #include "src/objects/js-regexp-string-iterator.h" @@ -24,12 +26,55 @@ using compiler::Node; template <class T> using TNode = compiler::TNode<T>; +// Tail calls the regular expression interpreter. +// static +void Builtins::Generate_RegExpInterpreterTrampoline(MacroAssembler* masm) { + ExternalReference interpreter_code_entry = + ExternalReference::re_match_for_call_from_js(masm->isolate()); + masm->Jump(interpreter_code_entry); +} + TNode<Smi> RegExpBuiltinsAssembler::SmiZero() { return SmiConstant(0); } TNode<IntPtrT> RegExpBuiltinsAssembler::IntPtrZero() { return IntPtrConstant(0); } +// If code is a builtin, return the address to the (possibly embedded) builtin +// code entry, otherwise return the entry of the code object itself. +TNode<RawPtrT> RegExpBuiltinsAssembler::LoadCodeObjectEntry(TNode<Code> code) { + TVARIABLE(RawPtrT, var_result); + + Label if_code_is_off_heap(this), out(this); + TNode<Int32T> builtin_index = UncheckedCast<Int32T>( + LoadObjectField(code, Code::kBuiltinIndexOffset, MachineType::Int32())); + { + GotoIfNot(Word32Equal(builtin_index, Int32Constant(Builtins::kNoBuiltinId)), + &if_code_is_off_heap); + var_result = ReinterpretCast<RawPtrT>( + IntPtrAdd(BitcastTaggedToWord(code), + IntPtrConstant(Code::kHeaderSize - kHeapObjectTag))); + Goto(&out); + } + + BIND(&if_code_is_off_heap); + { + TNode<IntPtrT> builtin_entry_offset_from_isolate_root = + IntPtrAdd(IntPtrConstant(IsolateData::builtin_entry_table_offset()), + ChangeInt32ToIntPtr(Word32Shl( + builtin_index, Int32Constant(kSystemPointerSizeLog2)))); + + var_result = ReinterpretCast<RawPtrT>( + Load(MachineType::Pointer(), + ExternalConstant(ExternalReference::isolate_root(isolate())), + builtin_entry_offset_from_isolate_root)); + Goto(&out); + } + + BIND(&out); + return var_result.value(); +} + // ----------------------------------------------------------------------------- // ES6 section 21.2 RegExp Objects @@ -131,8 +176,8 @@ void RegExpBuiltinsAssembler::FastStoreLastIndex(TNode<JSRegExp> regexp, void RegExpBuiltinsAssembler::SlowStoreLastIndex(SloppyTNode<Context> context, SloppyTNode<Object> regexp, - SloppyTNode<Number> value) { - TNode<Name> name = HeapConstant(isolate()->factory()->lastIndex_string()); + SloppyTNode<Object> value) { + TNode<String> name = HeapConstant(isolate()->factory()->lastIndex_string()); SetPropertyStrict(context, regexp, name, value); } @@ -236,7 +281,7 @@ TNode<JSRegExpResult> RegExpBuiltinsAssembler::ConstructNewResultFromMatchInfo( // index at odd indices. TNode<Object> maybe_names = LoadFixedArrayElement(data, JSRegExp::kIrregexpCaptureNameMapIndex); - GotoIf(WordEqual(maybe_names, SmiZero()), &out); + GotoIf(TaggedEqual(maybe_names, SmiZero()), &out); // One or more named captures exist, add a property for each one. @@ -249,7 +294,7 @@ TNode<JSRegExpResult> RegExpBuiltinsAssembler::ConstructNewResultFromMatchInfo( // root list. TNode<IntPtrT> num_properties = WordSar(names_length, 1); - TNode<Context> native_context = LoadNativeContext(context); + TNode<NativeContext> native_context = LoadNativeContext(context); TNode<Map> map = CAST(LoadContextElement( native_context, Context::SLOW_OBJECT_WITH_NULL_PROTOTYPE_MAP)); TNode<NameDictionary> properties = @@ -320,11 +365,11 @@ void RegExpBuiltinsAssembler::GetStringPointers( ? UINT8_ELEMENTS : UINT16_ELEMENTS; - Node* const from_offset = ElementOffsetFromIndex( + TNode<IntPtrT> const from_offset = ElementOffsetFromIndex( IntPtrAdd(offset, last_index), kind, INTPTR_PARAMETERS); var_string_start->Bind(IntPtrAdd(string_data, from_offset)); - Node* const to_offset = ElementOffsetFromIndex( + TNode<IntPtrT> const to_offset = ElementOffsetFromIndex( IntPtrAdd(offset, string_length), kind, INTPTR_PARAMETERS); var_string_end->Bind(IntPtrAdd(string_data, to_offset)); } @@ -340,9 +385,8 @@ TNode<HeapObject> RegExpBuiltinsAssembler::RegExpExecInternal( // External constants. TNode<ExternalReference> isolate_address = ExternalConstant(ExternalReference::isolate_address(isolate())); - TNode<ExternalReference> regexp_stack_memory_address_address = - ExternalConstant( - ExternalReference::address_of_regexp_stack_memory_address(isolate())); + TNode<ExternalReference> regexp_stack_memory_top_address = ExternalConstant( + ExternalReference::address_of_regexp_stack_memory_top_address(isolate())); TNode<ExternalReference> regexp_stack_memory_size_address = ExternalConstant( ExternalReference::address_of_regexp_stack_memory_size(isolate())); TNode<ExternalReference> static_offsets_vector_address = ExternalConstant( @@ -374,7 +418,9 @@ TNode<HeapObject> RegExpBuiltinsAssembler::RegExpExecInternal( data, IntPtrConstant(JSRegExp::kTagIndex)); int32_t values[] = { - JSRegExp::IRREGEXP, JSRegExp::ATOM, JSRegExp::NOT_COMPILED, + JSRegExp::IRREGEXP, + JSRegExp::ATOM, + JSRegExp::NOT_COMPILED, }; Label* labels[] = {&next, &atom, &runtime}; @@ -398,24 +444,17 @@ TNode<HeapObject> RegExpBuiltinsAssembler::RegExpExecInternal( &runtime); } - // Ensure that a RegExp stack is allocated. This check is after branching off - // for ATOM regexps to avoid unnecessary trips to runtime. - { - TNode<IntPtrT> stack_size = UncheckedCast<IntPtrT>( - Load(MachineType::IntPtr(), regexp_stack_memory_size_address)); - GotoIf(IntPtrEqual(stack_size, IntPtrZero()), &runtime); - } - // Unpack the string if possible. to_direct.TryToDirect(&runtime); - // Load the irregexp code object and offsets into the subject string. Both - // depend on whether the string is one- or two-byte. + // Load the irregexp code or bytecode object and offsets into the subject + // string. Both depend on whether the string is one- or two-byte. TVARIABLE(RawPtrT, var_string_start); TVARIABLE(RawPtrT, var_string_end); TVARIABLE(Object, var_code); + TVARIABLE(Object, var_bytecode); { TNode<RawPtrT> direct_string_data = to_direct.PointerToData(&runtime); @@ -431,6 +470,8 @@ TNode<HeapObject> RegExpBuiltinsAssembler::RegExpExecInternal( &var_string_start, &var_string_end); var_code = UnsafeLoadFixedArrayElement(data, JSRegExp::kIrregexpLatin1CodeIndex); + var_bytecode = UnsafeLoadFixedArrayElement( + data, JSRegExp::kIrregexpLatin1BytecodeIndex); Goto(&next); } @@ -441,6 +482,8 @@ TNode<HeapObject> RegExpBuiltinsAssembler::RegExpExecInternal( &var_string_start, &var_string_end); var_code = UnsafeLoadFixedArrayElement(data, JSRegExp::kIrregexpUC16CodeIndex); + var_bytecode = UnsafeLoadFixedArrayElement( + data, JSRegExp::kIrregexpUC16BytecodeIndex); Goto(&next); } @@ -462,9 +505,32 @@ TNode<HeapObject> RegExpBuiltinsAssembler::RegExpExecInternal( #endif GotoIf(TaggedIsSmi(var_code.value()), &runtime); - GotoIfNot(IsCode(CAST(var_code.value())), &runtime); TNode<Code> code = CAST(var_code.value()); + // Tier-up in runtime if ticks are non-zero and tier-up hasn't happened yet + // and ensure that a RegExp stack is allocated when using compiled Irregexp. + { + Label next(this), check_tier_up(this); + GotoIfNot(TaggedIsSmi(var_bytecode.value()), &check_tier_up); + CSA_ASSERT(this, SmiEqual(CAST(var_bytecode.value()), + SmiConstant(JSRegExp::kUninitializedValue))); + + // Ensure RegExp stack is allocated. + TNode<IntPtrT> stack_size = UncheckedCast<IntPtrT>( + Load(MachineType::IntPtr(), regexp_stack_memory_size_address)); + GotoIf(IntPtrEqual(stack_size, IntPtrZero()), &runtime); + Goto(&next); + + // Check if tier-up is requested. + BIND(&check_tier_up); + TNode<Smi> ticks = CAST( + UnsafeLoadFixedArrayElement(data, JSRegExp::kIrregexpTierUpTicksIndex)); + GotoIf(SmiToInt32(ticks), &runtime); + + Goto(&next); + BIND(&next); + } + Label if_success(this), if_exception(this, Label::kDeferred); { IncrementCounter(isolate()->counters()->regexp_entry_native(), 1); @@ -486,11 +552,13 @@ TNode<HeapObject> RegExpBuiltinsAssembler::RegExpExecInternal( MachineType arg1_type = type_int32; TNode<Int32T> arg1 = TruncateIntPtrToInt32(int_last_index); - // Argument 2: Start of string data. + // Argument 2: Start of string data. This argument is ignored in the + // interpreter. MachineType arg2_type = type_ptr; TNode<RawPtrT> arg2 = var_string_start.value(); - // Argument 3: End of string data. + // Argument 3: End of string data. This argument is ignored in the + // interpreter. MachineType arg3_type = type_ptr; TNode<RawPtrT> arg3 = var_string_end.value(); @@ -498,41 +566,50 @@ TNode<HeapObject> RegExpBuiltinsAssembler::RegExpExecInternal( MachineType arg4_type = type_ptr; TNode<ExternalReference> arg4 = static_offsets_vector_address; - // Argument 5: Set the number of capture registers to zero to force global - // regexps to behave as non-global. This does not affect non-global - // regexps. + // Argument 5: Number of capture registers. + // Setting this to the number of registers required to store all captures + // forces global regexps to behave as non-global. + TNode<Smi> capture_count = CAST(UnsafeLoadFixedArrayElement( + data, JSRegExp::kIrregexpCaptureCountIndex)); + // capture_count is the number of captures without the match itself. + // Required registers = (capture_count + 1) * 2. + STATIC_ASSERT(Internals::IsValidSmi((JSRegExp::kMaxCaptures + 1) << 1)); + TNode<Smi> register_count = + SmiShl(SmiAdd(capture_count, SmiConstant(1)), 1); + MachineType arg5_type = type_int32; - TNode<Int32T> arg5 = Int32Constant(0); + TNode<Int32T> arg5 = SmiToInt32(register_count); - // Argument 6: Start (high end) of backtracking stack memory area. - TNode<RawPtrT> stack_start = UncheckedCast<RawPtrT>( - Load(MachineType::Pointer(), regexp_stack_memory_address_address)); - TNode<IntPtrT> stack_size = UncheckedCast<IntPtrT>( - Load(MachineType::IntPtr(), regexp_stack_memory_size_address)); - TNode<RawPtrT> stack_end = - ReinterpretCast<RawPtrT>(IntPtrAdd(stack_start, stack_size)); + // Argument 6: Start (high end) of backtracking stack memory area. This + // argument is ignored in the interpreter. + TNode<RawPtrT> stack_top = UncheckedCast<RawPtrT>( + Load(MachineType::Pointer(), regexp_stack_memory_top_address)); MachineType arg6_type = type_ptr; - TNode<RawPtrT> arg6 = stack_end; + TNode<RawPtrT> arg6 = stack_top; // Argument 7: Indicate that this is a direct call from JavaScript. MachineType arg7_type = type_int32; - TNode<Int32T> arg7 = Int32Constant(1); + TNode<Int32T> arg7 = Int32Constant(RegExp::CallOrigin::kFromJs); // Argument 8: Pass current isolate address. MachineType arg8_type = type_ptr; TNode<ExternalReference> arg8 = isolate_address; - TNode<RawPtrT> code_entry = ReinterpretCast<RawPtrT>( - IntPtrAdd(BitcastTaggedToWord(code), - IntPtrConstant(Code::kHeaderSize - kHeapObjectTag))); + // Argument 9: Regular expression object. This argument is ignored in native + // irregexp code. + MachineType arg9_type = type_tagged; + TNode<JSRegExp> arg9 = regexp; + + TNode<RawPtrT> code_entry = LoadCodeObjectEntry(code); TNode<Int32T> result = UncheckedCast<Int32T>(CallCFunction( code_entry, retval_type, std::make_pair(arg0_type, arg0), std::make_pair(arg1_type, arg1), std::make_pair(arg2_type, arg2), std::make_pair(arg3_type, arg3), std::make_pair(arg4_type, arg4), std::make_pair(arg5_type, arg5), std::make_pair(arg6_type, arg6), - std::make_pair(arg7_type, arg7), std::make_pair(arg8_type, arg8))); + std::make_pair(arg7_type, arg7), std::make_pair(arg8_type, arg8), + std::make_pair(arg9_type, arg9))); // Check the result. // We expect exactly one result since we force the called regexp to behave @@ -734,7 +811,7 @@ RegExpBuiltinsAssembler::RegExpPrototypeExecBodyWithoutResult( BIND(&run_exec); { // Get last match info from the context. - TNode<Context> native_context = LoadNativeContext(context); + TNode<NativeContext> native_context = LoadNativeContext(context); TNode<RegExpMatchInfo> last_match_info = CAST(LoadContextElement( native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX)); @@ -771,8 +848,8 @@ RegExpBuiltinsAssembler::RegExpPrototypeExecBodyWithoutResult( TNode<RegExpMatchInfo> RegExpBuiltinsAssembler::RegExpPrototypeExecBodyWithoutResultFast( - TNode<Context> context, TNode<JSReceiver> maybe_regexp, - TNode<String> string, Label* if_didnotmatch) { + TNode<Context> context, TNode<JSRegExp> maybe_regexp, TNode<String> string, + Label* if_didnotmatch) { return RegExpPrototypeExecBodyWithoutResult(context, maybe_regexp, string, if_didnotmatch, true); } @@ -805,36 +882,21 @@ TNode<HeapObject> RegExpBuiltinsAssembler::RegExpPrototypeExecBody( return var_result.value(); } -Node* RegExpBuiltinsAssembler::ThrowIfNotJSReceiver( - Node* context, Node* maybe_receiver, MessageTemplate msg_template, - char const* method_name) { - Label out(this), throw_exception(this, Label::kDeferred); - VARIABLE(var_value_map, MachineRepresentation::kTagged); - - GotoIf(TaggedIsSmi(maybe_receiver), &throw_exception); - - // Load the instance type of the {value}. - var_value_map.Bind(LoadMap(maybe_receiver)); - Node* const value_instance_type = LoadMapInstanceType(var_value_map.value()); - - Branch(IsJSReceiverInstanceType(value_instance_type), &out, &throw_exception); - - // The {value} is not a compatible receiver for this method. - BIND(&throw_exception); - { - Node* const value_str = - CallBuiltin(Builtins::kToString, context, maybe_receiver); - ThrowTypeError(context, msg_template, StringConstant(method_name), - value_str); - } - - BIND(&out); - return var_value_map.value(); +TNode<BoolT> RegExpBuiltinsAssembler::IsReceiverInitialRegExpPrototype( + SloppyTNode<Context> context, SloppyTNode<Object> receiver) { + TNode<NativeContext> native_context = LoadNativeContext(context); + TNode<JSFunction> const regexp_fun = + CAST(LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX)); + TNode<Object> const initial_map = + LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset); + TNode<HeapObject> const initial_prototype = + LoadMapPrototype(CAST(initial_map)); + return TaggedEqual(receiver, initial_prototype); } -Node* RegExpBuiltinsAssembler::IsFastRegExpNoPrototype(Node* const context, - Node* const object, - Node* const map) { +Node* RegExpBuiltinsAssembler::IsFastRegExpNoPrototype( + SloppyTNode<Context> context, SloppyTNode<Object> object, + SloppyTNode<Map> map) { Label out(this); VARIABLE(var_result, MachineRepresentation::kWord32); @@ -843,12 +905,12 @@ Node* RegExpBuiltinsAssembler::IsFastRegExpNoPrototype(Node* const context, GotoIfForceSlowPath(&out); #endif - Node* const native_context = LoadNativeContext(context); - Node* const regexp_fun = - LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX); - Node* const initial_map = + TNode<NativeContext> const native_context = LoadNativeContext(context); + TNode<HeapObject> const regexp_fun = + CAST(LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX)); + TNode<Object> const initial_map = LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset); - Node* const has_initialmap = WordEqual(map, initial_map); + TNode<BoolT> const has_initialmap = TaggedEqual(map, initial_map); var_result.Bind(has_initialmap); GotoIfNot(has_initialmap, &out); @@ -882,14 +944,14 @@ TNode<BoolT> RegExpBuiltinsAssembler::IsFastRegExpWithOriginalExec( var_result = is_regexp; GotoIfNot(is_regexp, &out); - TNode<Context> native_context = LoadNativeContext(context); + 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 = WordEqual(regexp_exec, original_exec); + 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); @@ -908,85 +970,80 @@ TNode<BoolT> RegExpBuiltinsAssembler::IsFastRegExpWithOriginalExec( return var_result.value(); } -Node* RegExpBuiltinsAssembler::IsFastRegExpNoPrototype(Node* const context, - Node* const object) { +Node* RegExpBuiltinsAssembler::IsFastRegExpNoPrototype( + SloppyTNode<Context> context, SloppyTNode<Object> object) { CSA_ASSERT(this, TaggedIsNotSmi(object)); - return IsFastRegExpNoPrototype(context, object, LoadMap(object)); + return IsFastRegExpNoPrototype(context, object, LoadMap(CAST(object))); } -// RegExp fast path implementations rely on unmodified JSRegExp instances. -// We use a fairly coarse granularity for this and simply check whether both -// the regexp itself is unmodified (i.e. its map has not changed), its -// prototype is unmodified, and lastIndex is a non-negative smi. void RegExpBuiltinsAssembler::BranchIfFastRegExp( - Node* const context, Node* const object, Node* const map, - base::Optional<DescriptorIndexAndName> additional_property_to_check, - Label* const if_isunmodified, Label* const if_ismodified) { - CSA_ASSERT(this, WordEqual(LoadMap(object), map)); + TNode<Context> context, TNode<HeapObject> object, TNode<Map> map, + PrototypeCheckAssembler::Flags prototype_check_flags, + base::Optional<DescriptorIndexNameValue> additional_property_to_check, + Label* if_isunmodified, Label* if_ismodified) { + CSA_ASSERT(this, TaggedEqual(LoadMap(object), map)); GotoIfForceSlowPath(if_ismodified); // This should only be needed for String.p.(split||matchAll), but we are // conservative here. - TNode<Context> native_context = LoadNativeContext(context); + // Note: we are using the current native context here, which may or may not + // match the object's native context. That's fine: in case of a mismatch, we + // will bail in the next step when comparing the object's map against the + // current native context's initial regexp map. + TNode<NativeContext> native_context = LoadNativeContext(context); GotoIf(IsRegExpSpeciesProtectorCellInvalid(native_context), if_ismodified); - Node* const regexp_fun = - LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX); - Node* const initial_map = - LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset); - Node* const has_initialmap = WordEqual(map, initial_map); + TNode<JSFunction> regexp_fun = + CAST(LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX)); + TNode<Map> initial_map = CAST( + LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset)); + TNode<BoolT> has_initialmap = TaggedEqual(map, initial_map); GotoIfNot(has_initialmap, if_ismodified); - Node* const initial_proto_initial_map = - LoadContextElement(native_context, Context::REGEXP_PROTOTYPE_MAP_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(CAST(object)); + GotoIfNot(TaggedIsPositiveSmi(last_index), if_ismodified); + + // Verify the prototype. + + TNode<Map> initial_proto_initial_map = CAST( + LoadContextElement(native_context, Context::REGEXP_PROTOTYPE_MAP_INDEX)); - DescriptorIndexAndName properties_to_check[2]; + DescriptorIndexNameValue properties_to_check[2]; int property_count = 0; - properties_to_check[property_count++] = DescriptorIndexAndName{ - JSRegExp::kExecFunctionDescriptorIndex, RootIndex::kexec_string}; + properties_to_check[property_count++] = DescriptorIndexNameValue{ + JSRegExp::kExecFunctionDescriptorIndex, RootIndex::kexec_string, + Context::REGEXP_EXEC_FUNCTION_INDEX}; if (additional_property_to_check) { properties_to_check[property_count++] = *additional_property_to_check; } - GotoIfInitialPrototypePropertiesModified( - CAST(map), CAST(initial_proto_initial_map), - Vector<DescriptorIndexAndName>(properties_to_check, property_count), - if_ismodified); + PrototypeCheckAssembler prototype_check_assembler( + state(), prototype_check_flags, native_context, initial_proto_initial_map, + Vector<DescriptorIndexNameValue>(properties_to_check, property_count)); - // The smi check is required to omit ToLength(lastIndex) calls with possible - // user-code execution on the fast path. - TNode<Object> last_index = FastLoadLastIndexBeforeSmiCheck(CAST(object)); - Branch(TaggedIsPositiveSmi(last_index), if_isunmodified, if_ismodified); + TNode<HeapObject> prototype = LoadMapPrototype(map); + prototype_check_assembler.CheckAndBranch(prototype, if_isunmodified, + if_ismodified); } -void RegExpBuiltinsAssembler::BranchIfFastRegExp(Node* const context, - Node* const object, - Label* const if_isunmodified, - Label* const if_ismodified) { - CSA_ASSERT(this, TaggedIsNotSmi(object)); - BranchIfFastRegExp(context, object, LoadMap(object), base::nullopt, - if_isunmodified, if_ismodified); +void RegExpBuiltinsAssembler::BranchIfFastRegExp_Strict( + TNode<Context> context, TNode<HeapObject> object, Label* if_isunmodified, + Label* if_ismodified) { + BranchIfFastRegExp(context, object, LoadMap(object), + PrototypeCheckAssembler::kCheckPrototypePropertyConstness, + base::nullopt, if_isunmodified, if_ismodified); } -TNode<BoolT> RegExpBuiltinsAssembler::IsFastRegExp(SloppyTNode<Context> context, - SloppyTNode<Object> object) { - Label yup(this), nope(this), out(this); - TVARIABLE(BoolT, var_result); - - BranchIfFastRegExp(context, object, &yup, &nope); - - BIND(&yup); - var_result = Int32TrueConstant(); - Goto(&out); - - BIND(&nope); - var_result = Int32FalseConstant(); - Goto(&out); - - BIND(&out); - return var_result.value(); +void RegExpBuiltinsAssembler::BranchIfFastRegExp_Permissive( + TNode<Context> context, TNode<HeapObject> object, Label* if_isunmodified, + Label* if_ismodified) { + BranchIfFastRegExp(context, object, LoadMap(object), + PrototypeCheckAssembler::kCheckFull, base::nullopt, + if_isunmodified, if_ismodified); } void RegExpBuiltinsAssembler::BranchIfFastRegExpResult(Node* const context, @@ -994,13 +1051,13 @@ void RegExpBuiltinsAssembler::BranchIfFastRegExpResult(Node* const context, Label* if_isunmodified, Label* if_ismodified) { // Could be a Smi. - Node* const map = LoadReceiverMap(object); + TNode<Map> const map = LoadReceiverMap(object); - Node* const native_context = LoadNativeContext(context); - Node* const initial_regexp_result_map = + TNode<NativeContext> const native_context = LoadNativeContext(context); + TNode<Object> const initial_regexp_result_map = LoadContextElement(native_context, Context::REGEXP_RESULT_MAP_INDEX); - Branch(WordEqual(map, initial_regexp_result_map), if_isunmodified, + Branch(TaggedEqual(map, initial_regexp_result_map), if_isunmodified, if_ismodified); } @@ -1036,9 +1093,8 @@ TF_BUILTIN(RegExpExecAtom, RegExpBuiltinsAssembler) { UintPtrLessThanOrEqual(SmiUntag(last_index), LoadStringLengthAsWord(subject_string))); - Node* const needle_string = - UnsafeLoadFixedArrayElement(data, JSRegExp::kAtomPatternIndex); - CSA_ASSERT(this, IsString(needle_string)); + TNode<String> const needle_string = + CAST(UnsafeLoadFixedArrayElement(data, JSRegExp::kAtomPatternIndex)); TNode<Smi> const match_from = CAST(CallBuiltin(Builtins::kStringIndexOf, context, subject_string, @@ -1120,9 +1176,9 @@ TF_BUILTIN(RegExpPrototypeExec, RegExpBuiltinsAssembler) { string)); } -Node* RegExpBuiltinsAssembler::FlagsGetter(Node* const context, - Node* const regexp, - bool is_fastpath) { +TNode<String> RegExpBuiltinsAssembler::FlagsGetter(TNode<Context> context, + TNode<Object> regexp, + bool is_fastpath) { Isolate* isolate = this->isolate(); TNode<IntPtrT> const int_one = IntPtrConstant(1); @@ -1134,8 +1190,9 @@ Node* RegExpBuiltinsAssembler::FlagsGetter(Node* const context, if (is_fastpath) { // Refer to JSRegExp's flag property on the fast-path. - CSA_ASSERT(this, IsJSRegExp(regexp)); - Node* const flags_smi = LoadObjectField(regexp, JSRegExp::kFlagsOffset); + CSA_ASSERT(this, IsJSRegExp(CAST(regexp))); + TNode<Smi> const flags_smi = + CAST(LoadObjectField(CAST(regexp), JSRegExp::kFlagsOffset)); var_flags = SmiUntag(flags_smi); #define CASE_FOR_FLAG(FLAG) \ @@ -1163,7 +1220,7 @@ Node* RegExpBuiltinsAssembler::FlagsGetter(Node* const context, #define CASE_FOR_FLAG(NAME, FLAG) \ do { \ Label next(this); \ - Node* const flag = GetProperty( \ + TNode<Object> const flag = GetProperty( \ context, regexp, isolate->factory()->InternalizeUtf8String(NAME)); \ Label if_isflagset(this); \ BranchIfToBooleanIsTrue(flag, &if_isflagset, &next); \ @@ -1187,7 +1244,7 @@ Node* RegExpBuiltinsAssembler::FlagsGetter(Node* const context, // char for each set flag. { - Node* const result = AllocateSeqOneByteString(context, var_length.value()); + TNode<String> const result = AllocateSeqOneByteString(var_length.value()); VARIABLE(var_offset, MachineType::PointerRepresentation(), IntPtrConstant(SeqOneByteString::kHeaderSize - kHeapObjectTag)); @@ -1196,7 +1253,7 @@ Node* RegExpBuiltinsAssembler::FlagsGetter(Node* const context, do { \ Label next(this); \ GotoIfNot(IsSetWord(var_flags.value(), FLAG), &next); \ - Node* const value = Int32Constant(CHAR); \ + TNode<Int32T> const value = Int32Constant(CHAR); \ StoreNoWriteBarrier(MachineRepresentation::kWord8, result, \ var_offset.value(), value); \ var_offset.Bind(IntPtrAdd(var_offset.value(), int_one)); \ @@ -1290,27 +1347,6 @@ Node* RegExpBuiltinsAssembler::RegExpInitialize(Node* const context, pattern, flags); } -// ES #sec-get-regexp.prototype.flags -TF_BUILTIN(RegExpPrototypeFlagsGetter, RegExpBuiltinsAssembler) { - TNode<Object> maybe_receiver = CAST(Parameter(Descriptor::kReceiver)); - TNode<Context> context = CAST(Parameter(Descriptor::kContext)); - - TNode<Map> map = CAST(ThrowIfNotJSReceiver(context, maybe_receiver, - MessageTemplate::kRegExpNonObject, - "RegExp.prototype.flags")); - TNode<JSReceiver> receiver = CAST(maybe_receiver); - - Label if_isfastpath(this), if_isslowpath(this, Label::kDeferred); - BranchIfFastRegExp(context, receiver, map, base::nullopt, &if_isfastpath, - &if_isslowpath); - - BIND(&if_isfastpath); - Return(FlagsGetter(context, receiver, true)); - - BIND(&if_isslowpath); - Return(FlagsGetter(context, receiver, false)); -} - // ES#sec-regexp-pattern-flags // RegExp ( pattern, flags ) TF_BUILTIN(RegExpConstructor, RegExpBuiltinsAssembler) { @@ -1321,13 +1357,13 @@ TF_BUILTIN(RegExpConstructor, RegExpBuiltinsAssembler) { Isolate* isolate = this->isolate(); - VARIABLE(var_flags, MachineRepresentation::kTagged, flags); - VARIABLE(var_pattern, MachineRepresentation::kTagged, pattern); - VARIABLE(var_new_target, MachineRepresentation::kTagged, new_target); + TVARIABLE(Object, var_flags, flags); + TVARIABLE(Object, var_pattern, pattern); + TVARIABLE(Object, var_new_target, new_target); - Node* const native_context = LoadNativeContext(context); - Node* const regexp_function = - LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX); + TNode<NativeContext> native_context = LoadNativeContext(context); + TNode<JSFunction> regexp_function = + CAST(LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX)); TNode<BoolT> pattern_is_regexp = IsRegExp(context, pattern); @@ -1335,15 +1371,15 @@ TF_BUILTIN(RegExpConstructor, RegExpBuiltinsAssembler) { Label next(this); GotoIfNot(IsUndefined(new_target), &next); - var_new_target.Bind(regexp_function); + var_new_target = regexp_function; GotoIfNot(pattern_is_regexp, &next); GotoIfNot(IsUndefined(flags), &next); - Node* const value = + TNode<Object> value = GetProperty(context, pattern, isolate->factory()->constructor_string()); - GotoIfNot(WordEqual(value, regexp_function), &next); + GotoIfNot(TaggedEqual(value, regexp_function), &next); Return(pattern); BIND(&next); @@ -1360,16 +1396,15 @@ TF_BUILTIN(RegExpConstructor, RegExpBuiltinsAssembler) { BIND(&if_patternisfastregexp); { - Node* const source = + TNode<Object> source = LoadObjectField(CAST(pattern), JSRegExp::kSourceOffset); - var_pattern.Bind(source); + var_pattern = source; { Label inner_next(this); GotoIfNot(IsUndefined(flags), &inner_next); - Node* const value = FlagsGetter(context, pattern, true); - var_flags.Bind(value); + var_flags = FlagsGetter(context, pattern, true); Goto(&inner_next); BIND(&inner_next); @@ -1380,19 +1415,15 @@ TF_BUILTIN(RegExpConstructor, RegExpBuiltinsAssembler) { BIND(&if_patternisslowregexp); { - { - Node* const value = - GetProperty(context, pattern, isolate->factory()->source_string()); - var_pattern.Bind(value); - } + var_pattern = + GetProperty(context, pattern, isolate->factory()->source_string()); { Label inner_next(this); GotoIfNot(IsUndefined(flags), &inner_next); - Node* const value = + var_flags = GetProperty(context, pattern, isolate->factory()->flags_string()); - var_flags.Bind(value); Goto(&inner_next); BIND(&inner_next); @@ -1410,14 +1441,14 @@ TF_BUILTIN(RegExpConstructor, RegExpBuiltinsAssembler) { { Label allocate_jsregexp(this), allocate_generic(this, Label::kDeferred), next(this); - Branch(WordEqual(var_new_target.value(), regexp_function), + Branch(TaggedEqual(var_new_target.value(), regexp_function), &allocate_jsregexp, &allocate_generic); BIND(&allocate_jsregexp); { - Node* const initial_map = LoadObjectField( - regexp_function, JSFunction::kPrototypeOrInitialMapOffset); - Node* const regexp = AllocateJSObjectFromMap(initial_map); + TNode<Map> const initial_map = CAST(LoadObjectField( + regexp_function, JSFunction::kPrototypeOrInitialMapOffset)); + TNode<JSObject> const regexp = AllocateJSObjectFromMap(initial_map); var_regexp.Bind(regexp); Goto(&next); } @@ -1425,8 +1456,8 @@ TF_BUILTIN(RegExpConstructor, RegExpBuiltinsAssembler) { BIND(&allocate_generic); { ConstructorBuiltinsAssembler constructor_assembler(this->state()); - Node* const regexp = constructor_assembler.EmitFastNewObject( - context, regexp_function, var_new_target.value()); + TNode<JSObject> const regexp = constructor_assembler.EmitFastNewObject( + context, regexp_function, CAST(var_new_target.value())); var_regexp.Bind(regexp); Goto(&next); } @@ -1473,8 +1504,9 @@ TF_BUILTIN(RegExpPrototypeCompile, RegExpBuiltinsAssembler) { BIND(&next); } - Node* const new_flags = FlagsGetter(context, pattern, true); - Node* const new_pattern = LoadObjectField(pattern, JSRegExp::kSourceOffset); + TNode<String> const new_flags = FlagsGetter(context, CAST(pattern), true); + TNode<Object> const new_pattern = + LoadObjectField(pattern, JSRegExp::kSourceOffset); var_flags.Bind(new_flags); var_pattern.Bind(new_pattern); @@ -1488,69 +1520,22 @@ TF_BUILTIN(RegExpPrototypeCompile, RegExpBuiltinsAssembler) { Return(result); } -// ES6 21.2.5.10. -// ES #sec-get-regexp.prototype.source -TF_BUILTIN(RegExpPrototypeSourceGetter, RegExpBuiltinsAssembler) { - TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver)); - TNode<Context> context = CAST(Parameter(Descriptor::kContext)); - - // Check whether we have an unmodified regexp instance. - Label if_isjsregexp(this), if_isnotjsregexp(this, Label::kDeferred); - - GotoIf(TaggedIsSmi(receiver), &if_isnotjsregexp); - Branch(IsJSRegExp(CAST(receiver)), &if_isjsregexp, &if_isnotjsregexp); - - BIND(&if_isjsregexp); - Return(LoadObjectField(CAST(receiver), JSRegExp::kSourceOffset)); - - BIND(&if_isnotjsregexp); - { - Isolate* isolate = this->isolate(); - Node* const native_context = LoadNativeContext(context); - Node* const regexp_fun = - LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX); - Node* const initial_map = - LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset); - Node* const initial_prototype = LoadMapPrototype(initial_map); - - Label if_isprototype(this), if_isnotprototype(this); - Branch(WordEqual(receiver, initial_prototype), &if_isprototype, - &if_isnotprototype); - - BIND(&if_isprototype); - { - const int counter = v8::Isolate::kRegExpPrototypeSourceGetter; - Node* const counter_smi = SmiConstant(counter); - CallRuntime(Runtime::kIncrementUseCounter, context, counter_smi); - - Node* const result = - HeapConstant(isolate->factory()->NewStringFromAsciiChecked("(?:)")); - Return(result); - } - - BIND(&if_isnotprototype); - { - ThrowTypeError(context, MessageTemplate::kRegExpNonRegExp, - "RegExp.prototype.source"); - } - } -} - // Fast-path implementation for flag checks on an unmodified JSRegExp instance. -TNode<Int32T> RegExpBuiltinsAssembler::FastFlagGetter(TNode<JSRegExp> regexp, - JSRegExp::Flag flag) { +TNode<BoolT> RegExpBuiltinsAssembler::FastFlagGetter(TNode<JSRegExp> regexp, + JSRegExp::Flag flag) { TNode<Smi> flags = CAST(LoadObjectField(regexp, JSRegExp::kFlagsOffset)); TNode<Smi> mask = SmiConstant(flag); - return SmiToInt32(SmiShr(SmiAnd(flags, mask), base::bits::CountTrailingZeros( - static_cast<int>(flag)))); + return ReinterpretCast<BoolT>(SmiToInt32( + SmiShr(SmiAnd(flags, mask), + base::bits::CountTrailingZeros(static_cast<int>(flag))))); } // Load through the GetProperty stub. -TNode<Int32T> RegExpBuiltinsAssembler::SlowFlagGetter(TNode<Context> context, - TNode<Object> regexp, - JSRegExp::Flag flag) { +TNode<BoolT> RegExpBuiltinsAssembler::SlowFlagGetter(TNode<Context> context, + TNode<Object> regexp, + JSRegExp::Flag flag) { Label out(this); - TVARIABLE(Int32T, var_result); + TVARIABLE(BoolT, var_result); Handle<String> name; switch (flag) { @@ -1582,140 +1567,36 @@ TNode<Int32T> RegExpBuiltinsAssembler::SlowFlagGetter(TNode<Context> context, BranchIfToBooleanIsTrue(value, &if_true, &if_false); BIND(&if_true); - var_result = Int32Constant(1); + var_result = BoolConstant(true); Goto(&out); BIND(&if_false); - var_result = Int32Constant(0); + var_result = BoolConstant(false); Goto(&out); BIND(&out); return var_result.value(); } -TNode<Int32T> RegExpBuiltinsAssembler::FlagGetter(TNode<Context> context, - TNode<Object> regexp, - JSRegExp::Flag flag, - bool is_fastpath) { +TNode<BoolT> RegExpBuiltinsAssembler::FlagGetter(TNode<Context> context, + TNode<Object> regexp, + JSRegExp::Flag flag, + bool is_fastpath) { return is_fastpath ? FastFlagGetter(CAST(regexp), flag) : SlowFlagGetter(context, regexp, flag); } -void RegExpBuiltinsAssembler::FlagGetter(Node* context, Node* receiver, - JSRegExp::Flag flag, int counter, - const char* method_name) { - // Check whether we have an unmodified regexp instance. - Label if_isunmodifiedjsregexp(this), - if_isnotunmodifiedjsregexp(this, Label::kDeferred); - - GotoIf(TaggedIsSmi(receiver), &if_isnotunmodifiedjsregexp); - Branch(IsJSRegExp(receiver), &if_isunmodifiedjsregexp, - &if_isnotunmodifiedjsregexp); - - BIND(&if_isunmodifiedjsregexp); - { - // Refer to JSRegExp's flag property on the fast-path. - Node* const is_flag_set = FastFlagGetter(CAST(receiver), flag); - Return(SelectBooleanConstant(is_flag_set)); - } - - BIND(&if_isnotunmodifiedjsregexp); - { - Node* const native_context = LoadNativeContext(context); - Node* const regexp_fun = - LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX); - Node* const initial_map = - LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset); - Node* const initial_prototype = LoadMapPrototype(initial_map); - - Label if_isprototype(this), if_isnotprototype(this); - Branch(WordEqual(receiver, initial_prototype), &if_isprototype, - &if_isnotprototype); - - BIND(&if_isprototype); - { - if (counter != -1) { - Node* const counter_smi = SmiConstant(counter); - CallRuntime(Runtime::kIncrementUseCounter, context, counter_smi); - } - Return(UndefinedConstant()); - } - - BIND(&if_isnotprototype); - { ThrowTypeError(context, MessageTemplate::kRegExpNonRegExp, method_name); } - } -} - -// ES6 21.2.5.4. -// ES #sec-get-regexp.prototype.global -TF_BUILTIN(RegExpPrototypeGlobalGetter, RegExpBuiltinsAssembler) { - TNode<Context> context = CAST(Parameter(Descriptor::kContext)); - TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver)); - FlagGetter(context, receiver, JSRegExp::kGlobal, - v8::Isolate::kRegExpPrototypeOldFlagGetter, - "RegExp.prototype.global"); -} - -// ES6 21.2.5.5. -// ES #sec-get-regexp.prototype.ignorecase -TF_BUILTIN(RegExpPrototypeIgnoreCaseGetter, RegExpBuiltinsAssembler) { - TNode<Context> context = CAST(Parameter(Descriptor::kContext)); - TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver)); - FlagGetter(context, receiver, JSRegExp::kIgnoreCase, - v8::Isolate::kRegExpPrototypeOldFlagGetter, - "RegExp.prototype.ignoreCase"); -} - -// ES6 21.2.5.7. -// ES #sec-get-regexp.prototype.multiline -TF_BUILTIN(RegExpPrototypeMultilineGetter, RegExpBuiltinsAssembler) { - TNode<Context> context = CAST(Parameter(Descriptor::kContext)); - TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver)); - FlagGetter(context, receiver, JSRegExp::kMultiline, - v8::Isolate::kRegExpPrototypeOldFlagGetter, - "RegExp.prototype.multiline"); -} - -// ES #sec-get-regexp.prototype.dotAll -TF_BUILTIN(RegExpPrototypeDotAllGetter, RegExpBuiltinsAssembler) { - TNode<Context> context = CAST(Parameter(Descriptor::kContext)); - TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver)); - static const int kNoCounter = -1; - FlagGetter(context, receiver, JSRegExp::kDotAll, kNoCounter, - "RegExp.prototype.dotAll"); -} - -// ES6 21.2.5.12. -// ES #sec-get-regexp.prototype.sticky -TF_BUILTIN(RegExpPrototypeStickyGetter, RegExpBuiltinsAssembler) { - TNode<Context> context = CAST(Parameter(Descriptor::kContext)); - TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver)); - FlagGetter(context, receiver, JSRegExp::kSticky, - v8::Isolate::kRegExpPrototypeStickyGetter, - "RegExp.prototype.sticky"); -} - -// ES6 21.2.5.15. -// ES #sec-get-regexp.prototype.unicode -TF_BUILTIN(RegExpPrototypeUnicodeGetter, RegExpBuiltinsAssembler) { - TNode<Context> context = CAST(Parameter(Descriptor::kContext)); - TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver)); - FlagGetter(context, receiver, JSRegExp::kUnicode, - v8::Isolate::kRegExpPrototypeUnicodeGetter, - "RegExp.prototype.unicode"); -} - // ES#sec-regexpexec Runtime Semantics: RegExpExec ( R, S ) -Node* RegExpBuiltinsAssembler::RegExpExec(Node* context, Node* regexp, - Node* string) { - VARIABLE(var_result, MachineRepresentation::kTagged); +TNode<Object> RegExpBuiltinsAssembler::RegExpExec(TNode<Context> context, + Node* regexp, Node* string) { + TVARIABLE(Object, var_result); Label out(this); // Take the slow path of fetching the exec property, calling it, and // verifying its return value. // Get the exec property. - Node* const exec = + TNode<Object> const exec = GetProperty(context, regexp, isolate()->factory()->exec_string()); // Is {exec} callable? @@ -1723,18 +1604,17 @@ Node* RegExpBuiltinsAssembler::RegExpExec(Node* context, Node* regexp, GotoIf(TaggedIsSmi(exec), &if_isnotcallable); - Node* const exec_map = LoadMap(exec); + TNode<Map> const exec_map = LoadMap(CAST(exec)); Branch(IsCallableMap(exec_map), &if_iscallable, &if_isnotcallable); BIND(&if_iscallable); { Callable call_callable = CodeFactory::Call(isolate()); - Node* const result = CallJS(call_callable, context, exec, regexp, string); + var_result = CAST(CallJS(call_callable, context, exec, regexp, string)); - var_result.Bind(result); - GotoIf(IsNull(result), &out); + GotoIf(IsNull(var_result.value()), &out); - ThrowIfNotJSReceiver(context, result, + ThrowIfNotJSReceiver(context, var_result.value(), MessageTemplate::kInvalidRegExpExecResult, ""); Goto(&out); @@ -1745,9 +1625,8 @@ Node* RegExpBuiltinsAssembler::RegExpExec(Node* context, Node* regexp, ThrowIfNotInstanceType(context, regexp, JS_REGEXP_TYPE, "RegExp.prototype.exec"); - Node* const result = CallBuiltin(Builtins::kRegExpPrototypeExecSlow, - context, regexp, string); - var_result.Bind(result); + var_result = CallBuiltin(Builtins::kRegExpPrototypeExecSlow, context, + regexp, string); Goto(&out); } @@ -1755,62 +1634,6 @@ Node* RegExpBuiltinsAssembler::RegExpExec(Node* context, Node* regexp, return var_result.value(); } -// ES#sec-regexp.prototype.test -// RegExp.prototype.test ( S ) -TF_BUILTIN(RegExpPrototypeTest, RegExpBuiltinsAssembler) { - TNode<Object> maybe_receiver = CAST(Parameter(Descriptor::kReceiver)); - TNode<Object> maybe_string = CAST(Parameter(Descriptor::kString)); - TNode<Context> context = CAST(Parameter(Descriptor::kContext)); - - // Ensure {maybe_receiver} is a JSReceiver. - ThrowIfNotJSReceiver(context, maybe_receiver, - MessageTemplate::kIncompatibleMethodReceiver, - "RegExp.prototype.test"); - TNode<JSReceiver> receiver = CAST(maybe_receiver); - - // Convert {maybe_string} to a String. - TNode<String> string = ToString_Inline(context, maybe_string); - - Label fast_path(this), slow_path(this); - BranchIfFastRegExp(context, receiver, &fast_path, &slow_path); - - BIND(&fast_path); - { - Label if_didnotmatch(this); - RegExpPrototypeExecBodyWithoutResult(context, receiver, string, - &if_didnotmatch, true); - Return(TrueConstant()); - - BIND(&if_didnotmatch); - Return(FalseConstant()); - } - - BIND(&slow_path); - { - // Call exec. - TNode<HeapObject> match_indices = - CAST(RegExpExec(context, receiver, string)); - - // Return true iff exec matched successfully. - Return(SelectBooleanConstant(IsNotNull(match_indices))); - } -} - -TF_BUILTIN(RegExpPrototypeTestFast, RegExpBuiltinsAssembler) { - TNode<JSRegExp> regexp = CAST(Parameter(Descriptor::kReceiver)); - TNode<String> string = CAST(Parameter(Descriptor::kString)); - TNode<Context> context = CAST(Parameter(Descriptor::kContext)); - - Label if_didnotmatch(this); - CSA_ASSERT(this, IsFastRegExpWithOriginalExec(context, regexp)); - RegExpPrototypeExecBodyWithoutResult(context, regexp, string, &if_didnotmatch, - true); - Return(TrueConstant()); - - BIND(&if_didnotmatch); - Return(FalseConstant()); -} - TNode<Number> RegExpBuiltinsAssembler::AdvanceStringIndex( SloppyTNode<String> string, SloppyTNode<Number> index, SloppyTNode<BoolT> is_unicode, bool is_fastpath) { @@ -1852,12 +1675,12 @@ TNode<Number> RegExpBuiltinsAssembler::AdvanceStringIndex( TNode<IntPtrT> untagged_plus_one = SmiUntag(CAST(index_plus_one)); GotoIfNot(IntPtrLessThan(untagged_plus_one, string_length), &out); - Node* const lead = StringCharCodeAt(string, SmiUntag(CAST(index))); + TNode<Int32T> const lead = StringCharCodeAt(string, SmiUntag(CAST(index))); GotoIfNot(Word32Equal(Word32And(lead, Int32Constant(0xFC00)), Int32Constant(0xD800)), &out); - Node* const trail = StringCharCodeAt(string, untagged_plus_one); + TNode<Int32T> const trail = StringCharCodeAt(string, untagged_plus_one); GotoIfNot(Word32Equal(Word32And(trail, Int32Constant(0xFC00)), Int32Constant(0xDC00)), &out); @@ -1873,29 +1696,34 @@ TNode<Number> RegExpBuiltinsAssembler::AdvanceStringIndex( return var_result.value(); } -void RegExpBuiltinsAssembler::RegExpPrototypeMatchBody(TNode<Context> context, - TNode<Object> regexp, - TNode<String> string, - const bool is_fastpath) { - if (is_fastpath) CSA_ASSERT(this, IsFastRegExp(context, regexp)); +TNode<Object> RegExpBuiltinsAssembler::RegExpPrototypeMatchBody( + TNode<Context> context, TNode<Object> regexp, TNode<String> string, + const bool is_fastpath) { + if (is_fastpath) { + CSA_ASSERT_BRANCH(this, [&](Label* ok, Label* not_ok) { + BranchIfFastRegExp_Strict(context, CAST(regexp), ok, not_ok); + }); + } - Node* const is_global = + TVARIABLE(Object, var_result); + + TNode<BoolT> const is_global = FlagGetter(context, regexp, JSRegExp::kGlobal, is_fastpath); - Label if_isglobal(this), if_isnotglobal(this); + Label if_isglobal(this), if_isnotglobal(this), done(this); Branch(is_global, &if_isglobal, &if_isnotglobal); BIND(&if_isnotglobal); { - Node* const result = is_fastpath ? RegExpPrototypeExecBody( - context, CAST(regexp), string, true) - : RegExpExec(context, regexp, string); - Return(result); + var_result = is_fastpath ? RegExpPrototypeExecBody(context, CAST(regexp), + string, true) + : RegExpExec(context, regexp, string); + Goto(&done); } BIND(&if_isglobal); { - Node* const is_unicode = + TNode<BoolT> const is_unicode = FlagGetter(context, regexp, JSRegExp::kUnicode, is_fastpath); StoreLastIndex(context, regexp, SmiZero(), is_fastpath); @@ -1939,15 +1767,14 @@ void RegExpBuiltinsAssembler::RegExpPrototypeMatchBody(TNode<Context> context, TNode<RegExpMatchInfo> match_indices = RegExpPrototypeExecBodyWithoutResult(context, CAST(regexp), string, &if_didnotmatch, true); - Label dosubstring(this), donotsubstring(this); Branch(var_atom.value(), &donotsubstring, &dosubstring); BIND(&dosubstring); { - Node* const match_from = UnsafeLoadFixedArrayElement( + TNode<Object> const match_from = UnsafeLoadFixedArrayElement( match_indices, RegExpMatchInfo::kFirstCaptureIndex); - Node* const match_to = UnsafeLoadFixedArrayElement( + TNode<Object> const match_to = UnsafeLoadFixedArrayElement( match_indices, RegExpMatchInfo::kFirstCaptureIndex + 1); var_match.Bind(CallBuiltin(Builtins::kSubString, context, string, match_from, match_to)); @@ -1959,7 +1786,7 @@ void RegExpBuiltinsAssembler::RegExpPrototypeMatchBody(TNode<Context> context, Goto(&if_didmatch); } else { DCHECK(!is_fastpath); - Node* const result = RegExpExec(context, regexp, string); + TNode<Object> const result = RegExpExec(context, regexp, string); Label load_match(this); Branch(IsNull(result), &if_didnotmatch, &load_match); @@ -1974,7 +1801,8 @@ void RegExpBuiltinsAssembler::RegExpPrototypeMatchBody(TNode<Context> context, { // Return null if there were no matches, otherwise just exit the loop. GotoIfNot(IntPtrEqual(array.length(), IntPtrZero()), &out); - Return(NullConstant()); + var_result = NullConstant(); + Goto(&done); } BIND(&if_didmatch); @@ -1990,15 +1818,15 @@ void RegExpBuiltinsAssembler::RegExpPrototypeMatchBody(TNode<Context> context, TNode<Smi> const match_length = LoadStringLengthAsSmi(match); GotoIfNot(SmiEqual(match_length, SmiZero()), &loop); - Node* last_index = LoadLastIndex(context, regexp, is_fastpath); + TNode<Object> last_index = LoadLastIndex(context, regexp, is_fastpath); if (is_fastpath) { CSA_ASSERT(this, TaggedIsPositiveSmi(last_index)); } else { last_index = ToLength_Inline(context, last_index); } - TNode<Number> new_last_index = - AdvanceStringIndex(string, last_index, is_unicode, is_fastpath); + TNode<Number> new_last_index = AdvanceStringIndex( + string, CAST(last_index), is_unicode, is_fastpath); if (is_fastpath) { // On the fast path, we can be certain that lastIndex can never be @@ -2018,38 +1846,13 @@ void RegExpBuiltinsAssembler::RegExpPrototypeMatchBody(TNode<Context> context, { // Wrap the match in a JSArray. - Node* const result = array.ToJSArray(context); - Return(result); + var_result = array.ToJSArray(context); + Goto(&done); } } -} - -// ES#sec-regexp.prototype-@@match -// RegExp.prototype [ @@match ] ( string ) -TF_BUILTIN(RegExpPrototypeMatch, RegExpBuiltinsAssembler) { - TNode<Object> maybe_receiver = CAST(Parameter(Descriptor::kReceiver)); - TNode<Object> maybe_string = CAST(Parameter(Descriptor::kString)); - TNode<Context> context = CAST(Parameter(Descriptor::kContext)); - // Ensure {maybe_receiver} is a JSReceiver. - ThrowIfNotJSReceiver(context, maybe_receiver, - MessageTemplate::kIncompatibleMethodReceiver, - "RegExp.prototype.@@match"); - TNode<JSReceiver> receiver = CAST(maybe_receiver); - - // Convert {maybe_string} to a String. - TNode<String> const string = ToString_Inline(context, maybe_string); - - Label fast_path(this), slow_path(this); - BranchIfFastRegExp(context, receiver, &fast_path, &slow_path); - - BIND(&fast_path); - // TODO(pwong): Could be optimized to remove the overhead of calling the - // builtin (at the cost of a larger builtin). - Return(CallBuiltin(Builtins::kRegExpMatchFast, context, receiver, string)); - - BIND(&slow_path); - RegExpPrototypeMatchBody(context, receiver, string, false); + BIND(&done); + return var_result.value(); } void RegExpMatchAllAssembler::Generate(TNode<Context> context, @@ -2066,12 +1869,17 @@ void RegExpMatchAllAssembler::Generate(TNode<Context> context, TNode<String> string = ToString_Inline(context, maybe_string); TVARIABLE(Object, var_matcher); - TVARIABLE(Int32T, var_global); - TVARIABLE(Int32T, var_unicode); + TVARIABLE(BoolT, var_global); + TVARIABLE(BoolT, var_unicode); Label create_iterator(this), if_fast_regexp(this), if_slow_regexp(this, Label::kDeferred); - BranchIfFastRegExp(context, receiver, &if_fast_regexp, &if_slow_regexp); + // Strict, because following code uses the flags property. + // TODO(jgruber): Handle slow flag accesses on the fast path and make this + // permissive. + BranchIfFastRegExp_Strict(context, CAST(receiver), &if_fast_regexp, + &if_slow_regexp); + BIND(&if_fast_regexp); { TNode<JSRegExp> fast_regexp = CAST(receiver); @@ -2081,9 +1889,10 @@ void RegExpMatchAllAssembler::Generate(TNode<Context> context, // 4. Let C be ? SpeciesConstructor(R, %RegExp%). // 5. Let flags be ? ToString(? Get(R, "flags")). // 6. Let matcher be ? Construct(C, « R, flags »). - TNode<String> flags = CAST(FlagsGetter(context, fast_regexp, true)); + TNode<String> flags = FlagsGetter(context, fast_regexp, true); var_matcher = RegExpCreate(context, native_context, source, flags); - CSA_ASSERT(this, IsFastRegExp(context, var_matcher.value())); + CSA_ASSERT(this, + IsFastRegExpPermissive(context, CAST(var_matcher.value()))); // 7. Let lastIndex be ? ToLength(? Get(R, "lastIndex")). // 8. Perform ? Set(matcher, "lastIndex", lastIndex, true). @@ -2130,8 +1939,7 @@ void RegExpMatchAllAssembler::Generate(TNode<Context> context, TNode<Smi> global_ix = CAST(CallBuiltin(Builtins::kStringIndexOf, context, flags_string, global_char_string, SmiZero())); - var_global = - SelectInt32Constant(SmiEqual(global_ix, SmiConstant(-1)), 0, 1); + var_global = SmiNotEqual(global_ix, SmiConstant(-1)); // 11. If flags contains "u", let fullUnicode be true. // 12. Else, let fullUnicode be false. @@ -2139,13 +1947,23 @@ void RegExpMatchAllAssembler::Generate(TNode<Context> context, TNode<Smi> unicode_ix = CAST(CallBuiltin(Builtins::kStringIndexOf, context, flags_string, unicode_char_string, SmiZero())); - var_unicode = - SelectInt32Constant(SmiEqual(unicode_ix, SmiConstant(-1)), 0, 1); + var_unicode = SmiNotEqual(unicode_ix, SmiConstant(-1)); Goto(&create_iterator); } BIND(&create_iterator); { + { + // UseCounter for matchAll with non-g RegExp. + // https://crbug.com/v8/9551 + Label next(this); + GotoIf(var_global.value(), &next); + CallRuntime(Runtime::kIncrementUseCounter, context, + SmiConstant(v8::Isolate::kRegExpMatchAllWithNonGlobalRegExp)); + Goto(&next); + BIND(&next); + } + // 13. Return ! CreateRegExpStringIterator(matcher, S, global, fullUnicode). TNode<Object> iterator = CreateRegExpStringIterator(native_context, var_matcher.value(), string, @@ -2158,7 +1976,7 @@ void RegExpMatchAllAssembler::Generate(TNode<Context> context, // CreateRegExpStringIterator ( R, S, global, fullUnicode ) TNode<Object> RegExpMatchAllAssembler::CreateRegExpStringIterator( TNode<Context> native_context, TNode<Object> regexp, TNode<String> string, - TNode<Int32T> global, TNode<Int32T> full_unicode) { + TNode<BoolT> global, TNode<BoolT> full_unicode) { TNode<Map> map = CAST(LoadContextElement( native_context, Context::INITIAL_REGEXP_STRING_ITERATOR_PROTOTYPE_MAP_INDEX)); @@ -2166,7 +1984,7 @@ TNode<Object> RegExpMatchAllAssembler::CreateRegExpStringIterator( // 4. Let iterator be ObjectCreate(%RegExpStringIteratorPrototype%, « // [[IteratingRegExp]], [[IteratedString]], [[Global]], [[Unicode]], // [[Done]] »). - TNode<Object> iterator = Allocate(JSRegExpStringIterator::kSize); + TNode<HeapObject> iterator = Allocate(JSRegExpStringIterator::kSize); StoreMapNoWriteBarrier(iterator, map); StoreObjectFieldRoot(iterator, JSRegExpStringIterator::kPropertiesOrHashOffset, @@ -2182,26 +2000,18 @@ TNode<Object> RegExpMatchAllAssembler::CreateRegExpStringIterator( StoreObjectFieldNoWriteBarrier( iterator, JSRegExpStringIterator::kIteratedStringOffset, string); -#ifdef DEBUG - // Verify global and full_unicode can be bitwise shifted without masking. - TNode<Int32T> zero = Int32Constant(0); - TNode<Int32T> one = Int32Constant(1); - CSA_ASSERT(this, - Word32Or(Word32Equal(global, zero), Word32Equal(global, one))); - CSA_ASSERT(this, Word32Or(Word32Equal(full_unicode, zero), - Word32Equal(full_unicode, one))); -#endif // DEBUG - // 7. Set iterator.[[Global]] to global. // 8. Set iterator.[[Unicode]] to fullUnicode. // 9. Set iterator.[[Done]] to false. - TNode<Word32T> global_flag = - Word32Shl(global, Int32Constant(JSRegExpStringIterator::kGlobalBit)); - TNode<Word32T> unicode_flag = Word32Shl( - full_unicode, Int32Constant(JSRegExpStringIterator::kUnicodeBit)); - TNode<Word32T> iterator_flags = Word32Or(global_flag, unicode_flag); + TNode<Int32T> global_flag = + Word32Shl(ReinterpretCast<Int32T>(global), + Int32Constant(JSRegExpStringIterator::kGlobalBit)); + TNode<Int32T> unicode_flag = + Word32Shl(ReinterpretCast<Int32T>(full_unicode), + Int32Constant(JSRegExpStringIterator::kUnicodeBit)); + TNode<Int32T> iterator_flags = Word32Or(global_flag, unicode_flag); StoreObjectFieldNoWriteBarrier(iterator, JSRegExpStringIterator::kFlagsOffset, - SmiFromInt32(Signed(iterator_flags))); + SmiFromInt32(iterator_flags)); return iterator; } @@ -2210,26 +2020,15 @@ TNode<Object> RegExpMatchAllAssembler::CreateRegExpStringIterator( // RegExp.prototype [ @@matchAll ] ( string ) TF_BUILTIN(RegExpPrototypeMatchAll, RegExpMatchAllAssembler) { TNode<Context> context = CAST(Parameter(Descriptor::kContext)); - TNode<Context> native_context = LoadNativeContext(context); + TNode<NativeContext> native_context = LoadNativeContext(context); TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver)); TNode<Object> maybe_string = CAST(Parameter(Descriptor::kString)); Generate(context, native_context, receiver, maybe_string); } -// Helper that skips a few initial checks. and assumes... -// 1) receiver is a "fast" RegExp -// 2) pattern is a string -TF_BUILTIN(RegExpMatchFast, RegExpBuiltinsAssembler) { - TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver)); - TNode<String> string = CAST(Parameter(Descriptor::kPattern)); - TNode<Context> context = CAST(Parameter(Descriptor::kContext)); - - RegExpPrototypeMatchBody(context, receiver, string, true); -} - void RegExpBuiltinsAssembler::RegExpPrototypeSearchBodyFast( TNode<Context> context, TNode<JSRegExp> regexp, TNode<String> string) { - CSA_ASSERT(this, IsFastRegExp(context, regexp)); + CSA_ASSERT(this, IsFastRegExpPermissive(context, regexp)); // Grab the initial value of last index. TNode<Smi> previous_last_index = FastLoadLastIndex(regexp); @@ -2248,7 +2047,7 @@ void RegExpBuiltinsAssembler::RegExpPrototypeSearchBodyFast( FastStoreLastIndex(regexp, previous_last_index); // Return the index of the match. - Node* const index = LoadFixedArrayElement( + TNode<Object> const index = LoadFixedArrayElement( match_indices, RegExpMatchInfo::kFirstCaptureIndex); Return(index); } @@ -2262,17 +2061,17 @@ void RegExpBuiltinsAssembler::RegExpPrototypeSearchBodyFast( } void RegExpBuiltinsAssembler::RegExpPrototypeSearchBodySlow( - Node* const context, Node* const regexp, Node* const string) { + TNode<Context> context, Node* const regexp, Node* const string) { CSA_ASSERT(this, IsJSReceiver(regexp)); CSA_ASSERT(this, IsString(string)); Isolate* const isolate = this->isolate(); - Node* const smi_zero = SmiZero(); + TNode<Smi> const smi_zero = SmiZero(); // Grab the initial value of last index. - Node* const previous_last_index = - SlowLoadLastIndex(CAST(context), CAST(regexp)); + TNode<Object> const previous_last_index = + SlowLoadLastIndex(context, CAST(regexp)); // Ensure last index is 0. { @@ -2286,13 +2085,13 @@ void RegExpBuiltinsAssembler::RegExpPrototypeSearchBodySlow( } // Call exec. - Node* const exec_result = RegExpExec(context, regexp, string); + TNode<Object> const exec_result = RegExpExec(context, regexp, string); // Reset last index if necessary. { Label next(this), slow(this, Label::kDeferred); - Node* const current_last_index = - SlowLoadLastIndex(CAST(context), CAST(regexp)); + TNode<Object> const current_last_index = + SlowLoadLastIndex(context, CAST(regexp)); BranchIfSameValue(current_last_index, previous_last_index, &next, &slow); @@ -2317,8 +2116,8 @@ void RegExpBuiltinsAssembler::RegExpPrototypeSearchBodySlow( BIND(&fast_result); { - Node* const index = - LoadObjectField(exec_result, JSRegExpResult::kIndexOffset); + TNode<Object> const index = + LoadObjectField(CAST(exec_result), JSRegExpResult::kIndexOffset); Return(index); } @@ -2341,13 +2140,13 @@ TF_BUILTIN(RegExpPrototypeSearch, RegExpBuiltinsAssembler) { ThrowIfNotJSReceiver(context, maybe_receiver, MessageTemplate::kIncompatibleMethodReceiver, "RegExp.prototype.@@search"); - Node* const receiver = maybe_receiver; + TNode<JSReceiver> receiver = CAST(maybe_receiver); // Convert {maybe_string} to a String. TNode<String> const string = ToString_Inline(context, maybe_string); Label fast_path(this), slow_path(this); - BranchIfFastRegExp(context, receiver, &fast_path, &slow_path); + BranchIfFastRegExp_Permissive(context, receiver, &fast_path, &slow_path); BIND(&fast_path); // TODO(pwong): Could be optimized to remove the overhead of calling the @@ -2371,13 +2170,12 @@ TF_BUILTIN(RegExpSearchFast, RegExpBuiltinsAssembler) { // Generates the fast path for @@split. {regexp} is an unmodified, non-sticky // JSRegExp, {string} is a String, and {limit} is a Smi. -void RegExpBuiltinsAssembler::RegExpPrototypeSplitBody(Node* const context, - Node* const regexp, +void RegExpBuiltinsAssembler::RegExpPrototypeSplitBody(TNode<Context> context, + TNode<JSRegExp> regexp, TNode<String> string, TNode<Smi> const limit) { - CSA_ASSERT(this, IsFastRegExp(context, regexp)); - CSA_ASSERT(this, - Word32BinaryNot(FastFlagGetter(CAST(regexp), JSRegExp::kSticky))); + CSA_ASSERT(this, IsFastRegExpPermissive(context, regexp)); + CSA_ASSERT(this, Word32BinaryNot(FastFlagGetter(regexp, JSRegExp::kSticky))); TNode<IntPtrT> const int_limit = SmiUntag(limit); @@ -2385,7 +2183,7 @@ void RegExpBuiltinsAssembler::RegExpPrototypeSplitBody(Node* const context, const ParameterMode mode = CodeStubAssembler::INTPTR_PARAMETERS; Node* const allocation_site = nullptr; - Node* const native_context = LoadNativeContext(context); + TNode<NativeContext> const native_context = LoadNativeContext(context); TNode<Map> array_map = LoadJSArrayElementsMap(kind, native_context); Label return_empty_array(this, Label::kDeferred); @@ -2407,10 +2205,10 @@ void RegExpBuiltinsAssembler::RegExpPrototypeSplitBody(Node* const context, BIND(&if_stringisempty); { - Node* const last_match_info = LoadContextElement( + TNode<Object> const last_match_info = LoadContextElement( native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX); - Node* const match_indices = + TNode<Object> const match_indices = CallBuiltin(Builtins::kRegExpExecInternal, context, regexp, string, SmiZero(), last_match_info); @@ -2464,7 +2262,7 @@ void RegExpBuiltinsAssembler::RegExpPrototypeSplitBody(Node* const context, // Search for the given {regexp}. - Node* const last_match_info = LoadContextElement( + TNode<Object> const last_match_info = LoadContextElement( native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX); TNode<HeapObject> const match_indices_ho = @@ -2499,8 +2297,9 @@ void RegExpBuiltinsAssembler::RegExpPrototypeSplitBody(Node* const context, GotoIfNot(SmiEqual(match_to, next_search_from), &next); GotoIfNot(SmiEqual(match_to, last_matched_until), &next); - Node* const is_unicode = FastFlagGetter(CAST(regexp), JSRegExp::kUnicode); - Node* const new_next_search_from = + TNode<BoolT> const is_unicode = + FastFlagGetter(regexp, JSRegExp::kUnicode); + TNode<Number> const new_next_search_from = AdvanceStringIndex(string, next_search_from, is_unicode, true); var_next_search_from = CAST(new_next_search_from); Goto(&loop); @@ -2518,9 +2317,9 @@ void RegExpBuiltinsAssembler::RegExpPrototypeSplitBody(Node* const context, // Add all captures to the array. { - Node* const num_registers = LoadFixedArrayElement( - match_indices, RegExpMatchInfo::kNumberOfCapturesIndex); - Node* const int_num_registers = SmiUntag(num_registers); + TNode<Smi> const num_registers = CAST(LoadFixedArrayElement( + match_indices, RegExpMatchInfo::kNumberOfCapturesIndex)); + TNode<IntPtrT> const int_num_registers = SmiUntag(num_registers); VARIABLE(var_reg, MachineType::PointerRepresentation()); var_reg.Bind(IntPtrConstant(2)); @@ -2535,7 +2334,7 @@ void RegExpBuiltinsAssembler::RegExpPrototypeSplitBody(Node* const context, BIND(&nested_loop); { Node* const reg = var_reg.value(); - Node* const from = LoadFixedArrayElement( + TNode<Object> const from = LoadFixedArrayElement( match_indices, reg, RegExpMatchInfo::kFirstCaptureIndex * kTaggedSize, mode); TNode<Smi> const to = CAST(LoadFixedArrayElement( @@ -2565,7 +2364,7 @@ void RegExpBuiltinsAssembler::RegExpPrototypeSplitBody(Node* const context, array.Push(CAST(var_value.value())); GotoIf(WordEqual(array.length(), int_limit), &out); - Node* const new_reg = IntPtrAdd(reg, IntPtrConstant(2)); + TNode<WordT> const new_reg = IntPtrAdd(reg, IntPtrConstant(2)); var_reg.Bind(new_reg); Branch(IntPtrLessThan(new_reg, int_num_registers), &nested_loop, @@ -2583,7 +2382,7 @@ void RegExpBuiltinsAssembler::RegExpPrototypeSplitBody(Node* const context, BIND(&push_suffix_and_out); { - Node* const from = var_last_matched_until.value(); + TNode<Smi> const from = var_last_matched_until.value(); Node* const to = string_length; array.Push(CallBuiltin(Builtins::kSubString, context, string, from, to)); Goto(&out); @@ -2591,7 +2390,7 @@ void RegExpBuiltinsAssembler::RegExpPrototypeSplitBody(Node* const context, BIND(&out); { - Node* const result = array.ToJSArray(CAST(context)); + TNode<JSArray> const result = array.ToJSArray(context); Return(result); } @@ -2612,11 +2411,9 @@ TF_BUILTIN(RegExpSplit, RegExpBuiltinsAssembler) { TNode<Object> maybe_limit = CAST(Parameter(Descriptor::kLimit)); TNode<Context> context = CAST(Parameter(Descriptor::kContext)); - CSA_ASSERT(this, IsFastRegExp(context, regexp)); - - // TODO(jgruber): Even if map checks send us to the fast path, we still need - // to verify the constructor property and jump to the slow path if it has - // been changed. + CSA_ASSERT_BRANCH(this, [&](Label* ok, Label* not_ok) { + BranchIfFastRegExp_Strict(context, regexp, ok, not_ok); + }); // Verify {maybe_limit}. @@ -2679,13 +2476,16 @@ TF_BUILTIN(RegExpPrototypeSplit, RegExpBuiltinsAssembler) { ThrowIfNotJSReceiver(context, maybe_receiver, MessageTemplate::kIncompatibleMethodReceiver, "RegExp.prototype.@@split"); - Node* const receiver = maybe_receiver; + TNode<JSReceiver> receiver = CAST(maybe_receiver); // Convert {maybe_string} to a String. - TNode<String> const string = ToString_Inline(context, maybe_string); + TNode<String> string = ToString_Inline(context, maybe_string); + // Strict: Reads the flags property. + // TODO(jgruber): Handle slow flag accesses on the fast path and make this + // permissive. Label stub(this), runtime(this, Label::kDeferred); - BranchIfFastRegExp(context, receiver, &stub, &runtime); + BranchIfFastRegExp_Strict(context, receiver, &stub, &runtime); BIND(&stub); args.PopAndReturn(CallBuiltin(Builtins::kRegExpSplit, context, receiver, @@ -2753,12 +2553,11 @@ TF_BUILTIN(RegExpStringIteratorPrototypeNext, RegExpStringIteratorAssembler) { GotoIf(HasDoneFlag(flags), &return_empty_done_result); // 5. Let R be O.[[IteratingRegExp]]. - TNode<Object> iterating_regexp = - LoadObjectField(receiver, JSRegExpStringIterator::kIteratingRegExpOffset); + TNode<JSReceiver> iterating_regexp = CAST(LoadObjectField( + receiver, JSRegExpStringIterator::kIteratingRegExpOffset)); - // TODO(jgruber): Verify that this is guaranteed. - CSA_CHECK(this, TaggedIsNotSmi(iterating_regexp)); - CSA_CHECK(this, IsJSReceiver(CAST(iterating_regexp))); + // For extra safety, also check the type in release mode. + CSA_CHECK(this, IsJSReceiver(iterating_regexp)); // 6. Let S be O.[[IteratedString]]. TNode<String> iterating_string = CAST( @@ -2775,23 +2574,23 @@ TF_BUILTIN(RegExpStringIteratorPrototypeNext, RegExpStringIteratorAssembler) { TVARIABLE(BoolT, var_is_fast_regexp); { Label if_fast(this), if_slow(this, Label::kDeferred); - BranchIfFastRegExp(context, iterating_regexp, &if_fast, &if_slow); + BranchIfFastRegExp_Permissive(context, iterating_regexp, &if_fast, + &if_slow); BIND(&if_fast); { TNode<RegExpMatchInfo> match_indices = - RegExpPrototypeExecBodyWithoutResult(context, CAST(iterating_regexp), - iterating_string, &if_no_match, - true); + RegExpPrototypeExecBodyWithoutResult( + context, iterating_regexp, iterating_string, &if_no_match, true); var_match = ConstructNewResultFromMatchInfo( - context, CAST(iterating_regexp), match_indices, iterating_string); + context, iterating_regexp, match_indices, iterating_string); var_is_fast_regexp = Int32TrueConstant(); Goto(&if_match); } BIND(&if_slow); { - var_match = CAST(RegExpExec(context, iterating_regexp, iterating_string)); + var_match = RegExpExec(context, iterating_regexp, iterating_string); var_is_fast_regexp = Int32FalseConstant(); Branch(IsNull(var_match.value()), &if_no_match, &if_match); } @@ -2836,7 +2635,7 @@ TF_BUILTIN(RegExpStringIteratorPrototypeNext, RegExpStringIteratorAssembler) { // When iterating_regexp is fast, we assume it stays fast even after // accessing the first match from the RegExp result. - CSA_ASSERT(this, IsFastRegExp(context, iterating_regexp)); + CSA_ASSERT(this, IsFastRegExpPermissive(context, iterating_regexp)); GotoIfNot(IsEmptyString(match_str), &return_result); // 1. Let thisIndex be ? ToLength(? Get(R, "lastIndex")). |