Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2016 the V8 project authors. All rights reserved. | 1 // Copyright 2016 the V8 project authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "src/builtins/builtins-utils.h" | 5 #include "src/builtins/builtins-utils.h" |
| 6 #include "src/builtins/builtins.h" | 6 #include "src/builtins/builtins.h" |
| 7 | 7 |
| 8 #include "src/code-factory.h" | 8 #include "src/code-factory.h" |
| 9 #include "src/regexp/jsregexp.h" | 9 #include "src/regexp/jsregexp.h" |
| 10 #include "src/regexp/regexp-utils.h" | 10 #include "src/regexp/regexp-utils.h" |
| 11 #include "src/string-builder.h" | 11 #include "src/string-builder.h" |
| 12 | 12 |
| 13 namespace v8 { | 13 namespace v8 { |
| 14 namespace internal { | 14 namespace internal { |
| 15 | 15 |
| 16 typedef compiler::Node Node; | 16 typedef compiler::Node Node; |
| 17 typedef CodeStubAssembler::Label CLabel; | |
| 18 typedef CodeStubAssembler::Variable CVariable; | |
| 19 typedef CodeStubAssembler::ParameterMode ParameterMode; | 17 typedef CodeStubAssembler::ParameterMode ParameterMode; |
| 20 typedef compiler::CodeAssemblerState CodeAssemblerState; | 18 typedef compiler::CodeAssemblerState CodeAssemblerState; |
| 21 | 19 |
| 22 class RegExpBuiltinsAssembler : public CodeStubAssembler { | 20 class RegExpBuiltinsAssembler : public CodeStubAssembler { |
| 23 public: | 21 public: |
| 24 explicit RegExpBuiltinsAssembler(compiler::CodeAssemblerState* state) | 22 explicit RegExpBuiltinsAssembler(CodeAssemblerState* state) |
| 25 : CodeStubAssembler(state) {} | 23 : CodeStubAssembler(state) {} |
| 26 | 24 |
| 27 protected: | 25 protected: |
| 26 Node* FastLoadLastIndex(Node* regexp); | |
| 27 Node* SlowLoadLastIndex(Node* context, Node* regexp); | |
| 28 Node* LoadLastIndex(Node* context, Node* regexp, bool is_fastpath); | |
| 29 | |
| 30 void FastStoreLastIndex(Node* regexp, Node* value); | |
| 31 void SlowStoreLastIndex(Node* context, Node* regexp, Node* value); | |
| 32 void StoreLastIndex(Node* context, Node* regexp, Node* value, | |
| 33 bool is_fastpath); | |
| 34 | |
| 35 Node* ConstructNewResultFromMatchInfo(Node* context, Node* match_info, | |
| 36 Node* string); | |
| 37 | |
| 38 Node* RegExpPrototypeExecBodyWithoutResult(Node* const context, | |
| 39 Node* const regexp, | |
| 40 Node* const string, | |
| 41 Label* if_didnotmatch, | |
| 42 const bool is_fastpath); | |
| 43 Node* RegExpPrototypeExecBody(Node* const context, Node* const regexp, | |
| 44 Node* const string, const bool is_fastpath); | |
| 45 | |
| 46 Node* ThrowIfNotJSReceiver(Node* context, Node* maybe_receiver, | |
| 47 MessageTemplate::Template msg_template, | |
| 48 char const* method_name); | |
| 49 | |
| 50 Node* IsInitialRegExpMap(Node* context, Node* map); | |
| 51 void BranchIfFastRegExp(Node* context, Node* map, Label* if_isunmodified, | |
| 52 Label* if_ismodified); | |
| 53 void BranchIfFastRegExpResult(Node* context, Node* map, | |
| 54 Label* if_isunmodified, Label* if_ismodified); | |
| 55 | |
| 56 Node* FlagsGetter(Node* const context, Node* const regexp, bool is_fastpath); | |
| 57 | |
| 58 Node* FastFlagGetter(Node* const regexp, JSRegExp::Flag flag); | |
| 59 Node* SlowFlagGetter(Node* const context, Node* const regexp, | |
| 60 JSRegExp::Flag flag); | |
| 61 Node* FlagGetter(Node* const context, Node* const regexp, JSRegExp::Flag flag, | |
| 62 bool is_fastpath); | |
| 63 void FlagGetter(JSRegExp::Flag flag, v8::Isolate::UseCounterFeature counter, | |
| 64 const char* method_name); | |
| 65 | |
| 66 Node* IsRegExp(Node* const context, Node* const maybe_receiver); | |
| 67 Node* RegExpInitialize(Node* const context, Node* const regexp, | |
| 68 Node* const maybe_pattern, Node* const maybe_flags); | |
| 69 | |
| 70 Node* RegExpExec(Node* context, Node* regexp, Node* string); | |
| 71 | |
| 72 Node* AdvanceStringIndex(Node* const string, Node* const index, | |
| 73 Node* const is_unicode); | |
| 74 | |
| 75 void RegExpPrototypeMatchBody(Node* const context, Node* const regexp, | |
| 76 Node* const string, const bool is_fastpath); | |
| 77 | |
| 78 void RegExpPrototypeSearchBodyFast(Node* const context, Node* const regexp, | |
| 79 Node* const string); | |
| 80 void RegExpPrototypeSearchBodySlow(Node* const context, Node* const regexp, | |
| 81 Node* const string); | |
| 82 | |
| 83 void RegExpPrototypeSplitBody(Node* const context, Node* const regexp, | |
| 84 Node* const string, Node* const limit); | |
| 85 | |
| 86 Node* ReplaceGlobalCallableFastPath(Node* context, Node* regexp, Node* string, | |
| 87 Node* replace_callable); | |
| 88 Node* ReplaceSimpleStringFastPath(Node* context, Node* regexp, Node* string, | |
| 89 Node* replace_string); | |
| 28 }; | 90 }; |
| 29 | 91 |
| 30 // ----------------------------------------------------------------------------- | 92 // ----------------------------------------------------------------------------- |
| 31 // ES6 section 21.2 RegExp Objects | 93 // ES6 section 21.2 RegExp Objects |
| 32 | 94 |
| 33 namespace { | 95 Node* RegExpBuiltinsAssembler::FastLoadLastIndex(Node* regexp) { |
|
Camillo Bruni
2016/12/05 09:52:10
I'd directly define them inline to avoid declaring
| |
| 34 | |
| 35 Node* FastLoadLastIndex(CodeStubAssembler* a, Node* regexp) { | |
| 36 // Load the in-object field. | 96 // Load the in-object field. |
| 37 static const int field_offset = | 97 static const int field_offset = |
| 38 JSRegExp::kSize + JSRegExp::kLastIndexFieldIndex * kPointerSize; | 98 JSRegExp::kSize + JSRegExp::kLastIndexFieldIndex * kPointerSize; |
| 39 return a->LoadObjectField(regexp, field_offset); | 99 return LoadObjectField(regexp, field_offset); |
| 40 } | 100 } |
| 41 | 101 |
| 42 Node* SlowLoadLastIndex(CodeStubAssembler* a, Node* context, Node* regexp) { | 102 Node* RegExpBuiltinsAssembler::SlowLoadLastIndex(Node* context, Node* regexp) { |
| 43 // Load through the GetProperty stub. | 103 // Load through the GetProperty stub. |
| 44 Node* const name = | 104 Node* const name = HeapConstant(isolate()->factory()->lastIndex_string()); |
| 45 a->HeapConstant(a->isolate()->factory()->lastIndex_string()); | 105 Callable getproperty_callable = CodeFactory::GetProperty(isolate()); |
| 46 Callable getproperty_callable = CodeFactory::GetProperty(a->isolate()); | 106 return CallStub(getproperty_callable, context, regexp, name); |
| 47 return a->CallStub(getproperty_callable, context, regexp, name); | |
| 48 } | 107 } |
| 49 | 108 |
| 50 Node* LoadLastIndex(CodeStubAssembler* a, Node* context, Node* regexp, | 109 Node* RegExpBuiltinsAssembler::LoadLastIndex(Node* context, Node* regexp, |
| 51 bool is_fastpath) { | 110 bool is_fastpath) { |
| 52 return is_fastpath ? FastLoadLastIndex(a, regexp) | 111 return is_fastpath ? FastLoadLastIndex(regexp) |
| 53 : SlowLoadLastIndex(a, context, regexp); | 112 : SlowLoadLastIndex(context, regexp); |
| 54 } | 113 } |
| 55 | 114 |
| 56 // The fast-path of StoreLastIndex when regexp is guaranteed to be an unmodified | 115 // The fast-path of StoreLastIndex when regexp is guaranteed to be an unmodified |
| 57 // JSRegExp instance. | 116 // JSRegExp instance. |
| 58 void FastStoreLastIndex(CodeStubAssembler* a, Node* regexp, Node* value) { | 117 void RegExpBuiltinsAssembler::FastStoreLastIndex(Node* regexp, Node* value) { |
| 59 // Store the in-object field. | 118 // Store the in-object field. |
| 60 static const int field_offset = | 119 static const int field_offset = |
| 61 JSRegExp::kSize + JSRegExp::kLastIndexFieldIndex * kPointerSize; | 120 JSRegExp::kSize + JSRegExp::kLastIndexFieldIndex * kPointerSize; |
| 62 a->StoreObjectField(regexp, field_offset, value); | 121 StoreObjectField(regexp, field_offset, value); |
| 63 } | 122 } |
| 64 | 123 |
| 65 void SlowStoreLastIndex(CodeStubAssembler* a, Node* context, Node* regexp, | 124 void RegExpBuiltinsAssembler::SlowStoreLastIndex(Node* context, Node* regexp, |
| 66 Node* value) { | 125 Node* value) { |
| 67 // Store through runtime. | 126 // Store through runtime. |
| 68 // TODO(ishell): Use SetPropertyStub here once available. | 127 // TODO(ishell): Use SetPropertyStub here once available. |
| 69 Node* const name = | 128 Node* const name = HeapConstant(isolate()->factory()->lastIndex_string()); |
| 70 a->HeapConstant(a->isolate()->factory()->lastIndex_string()); | 129 Node* const language_mode = SmiConstant(Smi::FromInt(STRICT)); |
| 71 Node* const language_mode = a->SmiConstant(Smi::FromInt(STRICT)); | 130 CallRuntime(Runtime::kSetProperty, context, regexp, name, value, |
| 72 a->CallRuntime(Runtime::kSetProperty, context, regexp, name, value, | 131 language_mode); |
| 73 language_mode); | |
| 74 } | 132 } |
| 75 | 133 |
| 76 void StoreLastIndex(CodeStubAssembler* a, Node* context, Node* regexp, | 134 void RegExpBuiltinsAssembler::StoreLastIndex(Node* context, Node* regexp, |
| 77 Node* value, bool is_fastpath) { | 135 Node* value, bool is_fastpath) { |
| 78 if (is_fastpath) { | 136 if (is_fastpath) { |
| 79 FastStoreLastIndex(a, regexp, value); | 137 FastStoreLastIndex(regexp, value); |
| 80 } else { | 138 } else { |
| 81 SlowStoreLastIndex(a, context, regexp, value); | 139 SlowStoreLastIndex(context, regexp, value); |
| 82 } | 140 } |
| 83 } | 141 } |
| 84 | 142 |
| 85 Node* ConstructNewResultFromMatchInfo(Isolate* isolate, CodeStubAssembler* a, | 143 Node* RegExpBuiltinsAssembler::ConstructNewResultFromMatchInfo(Node* context, |
| 86 Node* context, Node* match_info, | 144 Node* match_info, |
| 87 Node* string) { | 145 Node* string) { |
| 88 CLabel out(a); | 146 Label out(this); |
| 89 | 147 |
| 90 Node* const num_indices = a->SmiUntag(a->LoadFixedArrayElement( | 148 Node* const num_indices = SmiUntag(LoadFixedArrayElement( |
| 91 match_info, RegExpMatchInfo::kNumberOfCapturesIndex)); | 149 match_info, RegExpMatchInfo::kNumberOfCapturesIndex)); |
| 92 Node* const num_results = a->SmiTag(a->WordShr(num_indices, 1)); | 150 Node* const num_results = SmiTag(WordShr(num_indices, 1)); |
| 93 Node* const start = | 151 Node* const start = |
| 94 a->LoadFixedArrayElement(match_info, RegExpMatchInfo::kFirstCaptureIndex); | 152 LoadFixedArrayElement(match_info, RegExpMatchInfo::kFirstCaptureIndex); |
| 95 Node* const end = a->LoadFixedArrayElement( | 153 Node* const end = LoadFixedArrayElement( |
| 96 match_info, RegExpMatchInfo::kFirstCaptureIndex + 1); | 154 match_info, RegExpMatchInfo::kFirstCaptureIndex + 1); |
| 97 | 155 |
| 98 // Calculate the substring of the first match before creating the result array | 156 // Calculate the substring of the first match before creating the result array |
| 99 // to avoid an unnecessary write barrier storing the first result. | 157 // to avoid an unnecessary write barrier storing the first result. |
| 100 Node* const first = a->SubString(context, string, start, end); | 158 Node* const first = SubString(context, string, start, end); |
| 101 | 159 |
| 102 Node* const result = | 160 Node* const result = |
| 103 a->AllocateRegExpResult(context, num_results, start, string); | 161 AllocateRegExpResult(context, num_results, start, string); |
| 104 Node* const result_elements = a->LoadElements(result); | 162 Node* const result_elements = LoadElements(result); |
| 105 | 163 |
| 106 a->StoreFixedArrayElement(result_elements, 0, first, SKIP_WRITE_BARRIER); | 164 StoreFixedArrayElement(result_elements, 0, first, SKIP_WRITE_BARRIER); |
| 107 | 165 |
| 108 a->GotoIf(a->SmiEqual(num_results, a->SmiConstant(Smi::FromInt(1))), &out); | 166 GotoIf(SmiEqual(num_results, SmiConstant(Smi::FromInt(1))), &out); |
| 109 | 167 |
| 110 // Store all remaining captures. | 168 // Store all remaining captures. |
| 111 Node* const limit = a->IntPtrAdd( | 169 Node* const limit = IntPtrAdd( |
| 112 a->IntPtrConstant(RegExpMatchInfo::kFirstCaptureIndex), num_indices); | 170 IntPtrConstant(RegExpMatchInfo::kFirstCaptureIndex), num_indices); |
| 113 | 171 |
| 114 CVariable var_from_cursor(a, MachineType::PointerRepresentation()); | 172 Variable var_from_cursor(this, MachineType::PointerRepresentation()); |
| 115 CVariable var_to_cursor(a, MachineType::PointerRepresentation()); | 173 Variable var_to_cursor(this, MachineType::PointerRepresentation()); |
| 116 | 174 |
| 117 var_from_cursor.Bind( | 175 var_from_cursor.Bind(IntPtrConstant(RegExpMatchInfo::kFirstCaptureIndex + 2)); |
| 118 a->IntPtrConstant(RegExpMatchInfo::kFirstCaptureIndex + 2)); | 176 var_to_cursor.Bind(IntPtrConstant(1)); |
| 119 var_to_cursor.Bind(a->IntPtrConstant(1)); | |
| 120 | 177 |
| 121 CVariable* vars[] = {&var_from_cursor, &var_to_cursor}; | 178 Variable* vars[] = {&var_from_cursor, &var_to_cursor}; |
| 122 CLabel loop(a, 2, vars); | 179 Label loop(this, 2, vars); |
| 123 | 180 |
| 124 a->Goto(&loop); | 181 Goto(&loop); |
| 125 a->Bind(&loop); | 182 Bind(&loop); |
| 126 { | 183 { |
| 127 Node* const from_cursor = var_from_cursor.value(); | 184 Node* const from_cursor = var_from_cursor.value(); |
| 128 Node* const to_cursor = var_to_cursor.value(); | 185 Node* const to_cursor = var_to_cursor.value(); |
| 129 Node* const start = a->LoadFixedArrayElement(match_info, from_cursor); | 186 Node* const start = LoadFixedArrayElement(match_info, from_cursor); |
| 130 | 187 |
| 131 CLabel next_iter(a); | 188 Label next_iter(this); |
| 132 a->GotoIf(a->SmiEqual(start, a->SmiConstant(Smi::FromInt(-1))), &next_iter); | 189 GotoIf(SmiEqual(start, SmiConstant(Smi::FromInt(-1))), &next_iter); |
| 133 | 190 |
| 134 Node* const from_cursor_plus1 = | 191 Node* const from_cursor_plus1 = IntPtrAdd(from_cursor, IntPtrConstant(1)); |
| 135 a->IntPtrAdd(from_cursor, a->IntPtrConstant(1)); | 192 Node* const end = LoadFixedArrayElement(match_info, from_cursor_plus1); |
| 136 Node* const end = a->LoadFixedArrayElement(match_info, from_cursor_plus1); | |
| 137 | 193 |
| 138 Node* const capture = a->SubString(context, string, start, end); | 194 Node* const capture = SubString(context, string, start, end); |
| 139 a->StoreFixedArrayElement(result_elements, to_cursor, capture); | 195 StoreFixedArrayElement(result_elements, to_cursor, capture); |
| 140 a->Goto(&next_iter); | 196 Goto(&next_iter); |
| 141 | 197 |
| 142 a->Bind(&next_iter); | 198 Bind(&next_iter); |
| 143 var_from_cursor.Bind(a->IntPtrAdd(from_cursor, a->IntPtrConstant(2))); | 199 var_from_cursor.Bind(IntPtrAdd(from_cursor, IntPtrConstant(2))); |
| 144 var_to_cursor.Bind(a->IntPtrAdd(to_cursor, a->IntPtrConstant(1))); | 200 var_to_cursor.Bind(IntPtrAdd(to_cursor, IntPtrConstant(1))); |
| 145 a->Branch(a->UintPtrLessThan(var_from_cursor.value(), limit), &loop, &out); | 201 Branch(UintPtrLessThan(var_from_cursor.value(), limit), &loop, &out); |
| 146 } | 202 } |
| 147 | 203 |
| 148 a->Bind(&out); | 204 Bind(&out); |
| 149 return result; | 205 return result; |
| 150 } | 206 } |
| 151 | 207 |
| 152 // ES#sec-regexp.prototype.exec | 208 // ES#sec-regexp.prototype.exec |
| 153 // RegExp.prototype.exec ( string ) | 209 // RegExp.prototype.exec ( string ) |
| 154 // Implements the core of RegExp.prototype.exec but without actually | 210 // Implements the core of RegExp.prototype.exec but without actually |
| 155 // constructing the JSRegExpResult. Returns either null (if the RegExp did not | 211 // constructing the JSRegExpResult. Returns either null (if the RegExp did not |
| 156 // match) or a fixed array containing match indices as returned by | 212 // match) or a fixed array containing match indices as returned by |
| 157 // RegExpExecStub. | 213 // RegExpExecStub. |
| 158 Node* RegExpPrototypeExecBodyWithoutResult( | 214 Node* RegExpBuiltinsAssembler::RegExpPrototypeExecBodyWithoutResult( |
| 159 CodeStubAssembler* a, Node* const context, Node* const regexp, | 215 Node* const context, Node* const regexp, Node* const string, |
| 160 Node* const string, CLabel* if_didnotmatch, const bool is_fastpath) { | 216 Label* if_didnotmatch, const bool is_fastpath) { |
| 161 Isolate* const isolate = a->isolate(); | 217 Isolate* const isolate = this->isolate(); |
| 162 | 218 |
| 163 Node* const null = a->NullConstant(); | 219 Node* const null = NullConstant(); |
| 164 Node* const int_zero = a->IntPtrConstant(0); | 220 Node* const int_zero = IntPtrConstant(0); |
| 165 Node* const smi_zero = a->SmiConstant(Smi::kZero); | 221 Node* const smi_zero = SmiConstant(Smi::kZero); |
| 166 | 222 |
| 167 if (!is_fastpath) { | 223 if (!is_fastpath) { |
| 168 a->ThrowIfNotInstanceType(context, regexp, JS_REGEXP_TYPE, | 224 ThrowIfNotInstanceType(context, regexp, JS_REGEXP_TYPE, |
| 169 "RegExp.prototype.exec"); | 225 "RegExp.prototype.exec"); |
| 170 } | 226 } |
| 171 | 227 |
| 172 CSA_ASSERT(a, a->IsStringInstanceType(a->LoadInstanceType(string))); | 228 CSA_ASSERT(this, IsStringInstanceType(LoadInstanceType(string))); |
| 173 CSA_ASSERT(a, a->HasInstanceType(regexp, JS_REGEXP_TYPE)); | 229 CSA_ASSERT(this, HasInstanceType(regexp, JS_REGEXP_TYPE)); |
| 174 | 230 |
| 175 CVariable var_result(a, MachineRepresentation::kTagged); | 231 Variable var_result(this, MachineRepresentation::kTagged); |
| 176 CLabel out(a); | 232 Label out(this); |
| 177 | 233 |
| 178 Node* const native_context = a->LoadNativeContext(context); | 234 Node* const native_context = LoadNativeContext(context); |
| 179 Node* const string_length = a->LoadStringLength(string); | 235 Node* const string_length = LoadStringLength(string); |
| 180 | 236 |
| 181 // Check whether the regexp is global or sticky, which determines whether we | 237 // Check whether the regexp is global or sticky, which determines whether we |
| 182 // update last index later on. | 238 // update last index later on. |
| 183 Node* const flags = a->LoadObjectField(regexp, JSRegExp::kFlagsOffset); | 239 Node* const flags = LoadObjectField(regexp, JSRegExp::kFlagsOffset); |
| 184 Node* const is_global_or_sticky = | 240 Node* const is_global_or_sticky = WordAnd( |
| 185 a->WordAnd(a->SmiUntag(flags), | 241 SmiUntag(flags), IntPtrConstant(JSRegExp::kGlobal | JSRegExp::kSticky)); |
| 186 a->IntPtrConstant(JSRegExp::kGlobal | JSRegExp::kSticky)); | |
| 187 Node* const should_update_last_index = | 242 Node* const should_update_last_index = |
| 188 a->WordNotEqual(is_global_or_sticky, int_zero); | 243 WordNotEqual(is_global_or_sticky, int_zero); |
| 189 | 244 |
| 190 // Grab and possibly update last index. | 245 // Grab and possibly update last index. |
| 191 CLabel run_exec(a); | 246 Label run_exec(this); |
| 192 CVariable var_lastindex(a, MachineRepresentation::kTagged); | 247 Variable var_lastindex(this, MachineRepresentation::kTagged); |
| 193 { | 248 { |
| 194 CLabel if_doupdate(a), if_dontupdate(a); | 249 Label if_doupdate(this), if_dontupdate(this); |
| 195 a->Branch(should_update_last_index, &if_doupdate, &if_dontupdate); | 250 Branch(should_update_last_index, &if_doupdate, &if_dontupdate); |
| 196 | 251 |
| 197 a->Bind(&if_doupdate); | 252 Bind(&if_doupdate); |
| 198 { | 253 { |
| 199 Node* const regexp_lastindex = | 254 Node* const regexp_lastindex = |
| 200 LoadLastIndex(a, context, regexp, is_fastpath); | 255 LoadLastIndex(context, regexp, is_fastpath); |
| 201 var_lastindex.Bind(regexp_lastindex); | 256 var_lastindex.Bind(regexp_lastindex); |
| 202 | 257 |
| 203 // Omit ToLength if lastindex is a non-negative smi. | 258 // Omit ToLength if lastindex is a non-negative smi. |
| 204 { | 259 { |
| 205 CLabel call_tolength(a, CLabel::kDeferred), next(a); | 260 Label call_tolength(this, Label::kDeferred), next(this); |
| 206 a->Branch(a->WordIsPositiveSmi(regexp_lastindex), &next, | 261 Branch(WordIsPositiveSmi(regexp_lastindex), &next, &call_tolength); |
| 207 &call_tolength); | 262 |
| 208 | 263 Bind(&call_tolength); |
| 209 a->Bind(&call_tolength); | |
| 210 { | 264 { |
| 211 Callable tolength_callable = CodeFactory::ToLength(isolate); | 265 Callable tolength_callable = CodeFactory::ToLength(isolate); |
| 212 var_lastindex.Bind( | 266 var_lastindex.Bind( |
| 213 a->CallStub(tolength_callable, context, regexp_lastindex)); | 267 CallStub(tolength_callable, context, regexp_lastindex)); |
| 214 a->Goto(&next); | 268 Goto(&next); |
| 215 } | 269 } |
| 216 | 270 |
| 217 a->Bind(&next); | 271 Bind(&next); |
| 218 } | 272 } |
| 219 | 273 |
| 220 Node* const lastindex = var_lastindex.value(); | 274 Node* const lastindex = var_lastindex.value(); |
| 221 | 275 |
| 222 CLabel if_isoob(a, CLabel::kDeferred); | 276 Label if_isoob(this, Label::kDeferred); |
| 223 a->GotoUnless(a->TaggedIsSmi(lastindex), &if_isoob); | 277 GotoUnless(TaggedIsSmi(lastindex), &if_isoob); |
| 224 a->GotoUnless(a->SmiLessThanOrEqual(lastindex, string_length), &if_isoob); | 278 GotoUnless(SmiLessThanOrEqual(lastindex, string_length), &if_isoob); |
| 225 a->Goto(&run_exec); | 279 Goto(&run_exec); |
| 226 | 280 |
| 227 a->Bind(&if_isoob); | 281 Bind(&if_isoob); |
| 228 { | 282 { |
| 229 StoreLastIndex(a, context, regexp, smi_zero, is_fastpath); | 283 StoreLastIndex(context, regexp, smi_zero, is_fastpath); |
| 230 var_result.Bind(null); | 284 var_result.Bind(null); |
| 231 a->Goto(if_didnotmatch); | 285 Goto(if_didnotmatch); |
| 232 } | 286 } |
| 233 } | 287 } |
| 234 | 288 |
| 235 a->Bind(&if_dontupdate); | 289 Bind(&if_dontupdate); |
| 236 { | 290 { |
| 237 var_lastindex.Bind(smi_zero); | 291 var_lastindex.Bind(smi_zero); |
| 238 a->Goto(&run_exec); | 292 Goto(&run_exec); |
| 239 } | 293 } |
| 240 } | 294 } |
| 241 | 295 |
| 242 Node* match_indices; | 296 Node* match_indices; |
| 243 CLabel successful_match(a); | 297 Label successful_match(this); |
| 244 a->Bind(&run_exec); | 298 Bind(&run_exec); |
| 245 { | 299 { |
| 246 // Get last match info from the context. | 300 // Get last match info from the context. |
| 247 Node* const last_match_info = a->LoadContextElement( | 301 Node* const last_match_info = LoadContextElement( |
| 248 native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX); | 302 native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX); |
| 249 | 303 |
| 250 // Call the exec stub. | 304 // Call the exec stub. |
| 251 Callable exec_callable = CodeFactory::RegExpExec(isolate); | 305 Callable exec_callable = CodeFactory::RegExpExec(isolate); |
| 252 match_indices = a->CallStub(exec_callable, context, regexp, string, | 306 match_indices = CallStub(exec_callable, context, regexp, string, |
| 253 var_lastindex.value(), last_match_info); | 307 var_lastindex.value(), last_match_info); |
| 254 var_result.Bind(match_indices); | 308 var_result.Bind(match_indices); |
| 255 | 309 |
| 256 // {match_indices} is either null or the RegExpMatchInfo array. | 310 // {match_indices} is either null or the RegExpMatchInfo array. |
| 257 // Return early if exec failed, possibly updating last index. | 311 // Return early if exec failed, possibly updating last index. |
| 258 a->GotoUnless(a->WordEqual(match_indices, null), &successful_match); | 312 GotoUnless(WordEqual(match_indices, null), &successful_match); |
| 259 | 313 |
| 260 a->GotoUnless(should_update_last_index, if_didnotmatch); | 314 GotoUnless(should_update_last_index, if_didnotmatch); |
| 261 | 315 |
| 262 StoreLastIndex(a, context, regexp, smi_zero, is_fastpath); | 316 StoreLastIndex(context, regexp, smi_zero, is_fastpath); |
| 263 a->Goto(if_didnotmatch); | 317 Goto(if_didnotmatch); |
| 264 } | 318 } |
| 265 | 319 |
| 266 a->Bind(&successful_match); | 320 Bind(&successful_match); |
| 267 { | 321 { |
| 268 a->GotoUnless(should_update_last_index, &out); | 322 GotoUnless(should_update_last_index, &out); |
| 269 | 323 |
| 270 // Update the new last index from {match_indices}. | 324 // Update the new last index from {match_indices}. |
| 271 Node* const new_lastindex = a->LoadFixedArrayElement( | 325 Node* const new_lastindex = LoadFixedArrayElement( |
| 272 match_indices, RegExpMatchInfo::kFirstCaptureIndex + 1); | 326 match_indices, RegExpMatchInfo::kFirstCaptureIndex + 1); |
| 273 | 327 |
| 274 StoreLastIndex(a, context, regexp, new_lastindex, is_fastpath); | 328 StoreLastIndex(context, regexp, new_lastindex, is_fastpath); |
| 275 a->Goto(&out); | 329 Goto(&out); |
| 276 } | 330 } |
| 277 | 331 |
| 278 a->Bind(&out); | 332 Bind(&out); |
| 279 return var_result.value(); | 333 return var_result.value(); |
| 280 } | 334 } |
| 281 | 335 |
| 282 // ES#sec-regexp.prototype.exec | 336 // ES#sec-regexp.prototype.exec |
| 283 // RegExp.prototype.exec ( string ) | 337 // RegExp.prototype.exec ( string ) |
| 284 Node* RegExpPrototypeExecBody(CodeStubAssembler* a, Node* const context, | 338 Node* RegExpBuiltinsAssembler::RegExpPrototypeExecBody(Node* const context, |
| 285 Node* const regexp, Node* const string, | 339 Node* const regexp, |
| 286 const bool is_fastpath) { | 340 Node* const string, |
| 287 Isolate* const isolate = a->isolate(); | 341 const bool is_fastpath) { |
| 288 Node* const null = a->NullConstant(); | 342 Node* const null = NullConstant(); |
| 289 | 343 |
| 290 CVariable var_result(a, MachineRepresentation::kTagged); | 344 Variable var_result(this, MachineRepresentation::kTagged); |
| 291 | 345 |
| 292 CLabel if_didnotmatch(a), out(a); | 346 Label if_didnotmatch(this), out(this); |
| 293 Node* const indices_or_null = RegExpPrototypeExecBodyWithoutResult( | 347 Node* const indices_or_null = RegExpPrototypeExecBodyWithoutResult( |
| 294 a, context, regexp, string, &if_didnotmatch, is_fastpath); | 348 context, regexp, string, &if_didnotmatch, is_fastpath); |
| 295 | 349 |
| 296 // Successful match. | 350 // Successful match. |
| 297 { | 351 { |
| 298 Node* const match_indices = indices_or_null; | 352 Node* const match_indices = indices_or_null; |
| 299 Node* const result = ConstructNewResultFromMatchInfo(isolate, a, context, | 353 Node* const result = |
| 300 match_indices, string); | 354 ConstructNewResultFromMatchInfo(context, match_indices, string); |
| 301 var_result.Bind(result); | 355 var_result.Bind(result); |
| 302 a->Goto(&out); | 356 Goto(&out); |
| 303 } | 357 } |
| 304 | 358 |
| 305 a->Bind(&if_didnotmatch); | 359 Bind(&if_didnotmatch); |
| 306 { | 360 { |
| 307 var_result.Bind(null); | 361 var_result.Bind(null); |
| 308 a->Goto(&out); | 362 Goto(&out); |
| 309 } | 363 } |
| 310 | 364 |
| 311 a->Bind(&out); | 365 Bind(&out); |
| 312 return var_result.value(); | 366 return var_result.value(); |
| 313 } | 367 } |
| 314 | 368 |
| 315 Node* ThrowIfNotJSReceiver(CodeStubAssembler* a, Isolate* isolate, | 369 Node* RegExpBuiltinsAssembler::ThrowIfNotJSReceiver( |
| 316 Node* context, Node* value, | 370 Node* context, Node* maybe_receiver, MessageTemplate::Template msg_template, |
| 317 MessageTemplate::Template msg_template, | 371 char const* method_name) { |
| 318 char const* method_name) { | 372 Label out(this), throw_exception(this, Label::kDeferred); |
| 319 CLabel out(a), throw_exception(a, CLabel::kDeferred); | 373 Variable var_value_map(this, MachineRepresentation::kTagged); |
| 320 CVariable var_value_map(a, MachineRepresentation::kTagged); | 374 |
| 321 | 375 GotoIf(TaggedIsSmi(maybe_receiver), &throw_exception); |
| 322 a->GotoIf(a->TaggedIsSmi(value), &throw_exception); | |
| 323 | 376 |
| 324 // Load the instance type of the {value}. | 377 // Load the instance type of the {value}. |
| 325 var_value_map.Bind(a->LoadMap(value)); | 378 var_value_map.Bind(LoadMap(maybe_receiver)); |
| 326 Node* const value_instance_type = | 379 Node* const value_instance_type = LoadMapInstanceType(var_value_map.value()); |
| 327 a->LoadMapInstanceType(var_value_map.value()); | 380 |
| 328 | 381 Branch(IsJSReceiverInstanceType(value_instance_type), &out, &throw_exception); |
| 329 a->Branch(a->IsJSReceiverInstanceType(value_instance_type), &out, | |
| 330 &throw_exception); | |
| 331 | 382 |
| 332 // The {value} is not a compatible receiver for this method. | 383 // The {value} is not a compatible receiver for this method. |
| 333 a->Bind(&throw_exception); | 384 Bind(&throw_exception); |
| 334 { | 385 { |
| 335 Node* const message_id = a->SmiConstant(Smi::FromInt(msg_template)); | 386 Node* const message_id = SmiConstant(Smi::FromInt(msg_template)); |
| 336 Node* const method_name_str = a->HeapConstant( | 387 Node* const method_name_str = HeapConstant( |
| 337 isolate->factory()->NewStringFromAsciiChecked(method_name, TENURED)); | 388 isolate()->factory()->NewStringFromAsciiChecked(method_name, TENURED)); |
| 338 | 389 |
| 339 Callable callable = CodeFactory::ToString(isolate); | 390 Callable callable = CodeFactory::ToString(isolate()); |
| 340 Node* const value_str = a->CallStub(callable, context, value); | 391 Node* const value_str = CallStub(callable, context, maybe_receiver); |
| 341 | 392 |
| 342 a->CallRuntime(Runtime::kThrowTypeError, context, message_id, | 393 CallRuntime(Runtime::kThrowTypeError, context, message_id, method_name_str, |
| 343 method_name_str, value_str); | 394 value_str); |
| 344 var_value_map.Bind(a->UndefinedConstant()); | 395 var_value_map.Bind(UndefinedConstant()); |
| 345 a->Goto(&out); // Never reached. | 396 Goto(&out); // Never reached. |
| 346 } | 397 } |
| 347 | 398 |
| 348 a->Bind(&out); | 399 Bind(&out); |
| 349 return var_value_map.value(); | 400 return var_value_map.value(); |
| 350 } | 401 } |
| 351 | 402 |
| 352 Node* IsInitialRegExpMap(CodeStubAssembler* a, Node* context, Node* map) { | 403 Node* RegExpBuiltinsAssembler::IsInitialRegExpMap(Node* context, Node* map) { |
| 353 Node* const native_context = a->LoadNativeContext(context); | 404 Node* const native_context = LoadNativeContext(context); |
| 354 Node* const regexp_fun = | 405 Node* const regexp_fun = |
| 355 a->LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX); | 406 LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX); |
| 356 Node* const initial_map = | 407 Node* const initial_map = |
| 357 a->LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset); | 408 LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset); |
| 358 Node* const has_initialmap = a->WordEqual(map, initial_map); | 409 Node* const has_initialmap = WordEqual(map, initial_map); |
| 359 | 410 |
| 360 return has_initialmap; | 411 return has_initialmap; |
| 361 } | 412 } |
| 362 | 413 |
| 363 // RegExp fast path implementations rely on unmodified JSRegExp instances. | 414 // RegExp fast path implementations rely on unmodified JSRegExp instances. |
| 364 // We use a fairly coarse granularity for this and simply check whether both | 415 // We use a fairly coarse granularity for this and simply check whether both |
| 365 // the regexp itself is unmodified (i.e. its map has not changed) and its | 416 // the regexp itself is unmodified (i.e. its map has not changed) and its |
| 366 // prototype is unmodified. | 417 // prototype is unmodified. |
| 367 void BranchIfFastPath(CodeStubAssembler* a, Node* context, Node* map, | 418 void RegExpBuiltinsAssembler::BranchIfFastRegExp(Node* context, Node* map, |
| 368 CLabel* if_isunmodified, CLabel* if_ismodified) { | 419 Label* if_isunmodified, |
| 369 Node* const native_context = a->LoadNativeContext(context); | 420 Label* if_ismodified) { |
| 421 Node* const native_context = LoadNativeContext(context); | |
| 370 Node* const regexp_fun = | 422 Node* const regexp_fun = |
| 371 a->LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX); | 423 LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX); |
| 372 Node* const initial_map = | 424 Node* const initial_map = |
| 373 a->LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset); | 425 LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset); |
| 374 Node* const has_initialmap = a->WordEqual(map, initial_map); | 426 Node* const has_initialmap = WordEqual(map, initial_map); |
| 375 | 427 |
| 376 a->GotoUnless(has_initialmap, if_ismodified); | 428 GotoUnless(has_initialmap, if_ismodified); |
| 377 | 429 |
| 378 Node* const initial_proto_initial_map = a->LoadContextElement( | 430 Node* const initial_proto_initial_map = |
| 379 native_context, Context::REGEXP_PROTOTYPE_MAP_INDEX); | 431 LoadContextElement(native_context, Context::REGEXP_PROTOTYPE_MAP_INDEX); |
| 380 Node* const proto_map = a->LoadMap(a->LoadMapPrototype(map)); | 432 Node* const proto_map = LoadMap(LoadMapPrototype(map)); |
| 381 Node* const proto_has_initialmap = | 433 Node* const proto_has_initialmap = |
| 382 a->WordEqual(proto_map, initial_proto_initial_map); | 434 WordEqual(proto_map, initial_proto_initial_map); |
| 383 | 435 |
| 384 // TODO(ishell): Update this check once map changes for constant field | 436 // TODO(ishell): Update this check once map changes for constant field |
| 385 // tracking are landing. | 437 // tracking are landing. |
| 386 | 438 |
| 387 a->Branch(proto_has_initialmap, if_isunmodified, if_ismodified); | 439 Branch(proto_has_initialmap, if_isunmodified, if_ismodified); |
| 388 } | 440 } |
| 389 | 441 |
| 390 void BranchIfFastRegExpResult(CodeStubAssembler* a, Node* context, Node* map, | 442 void RegExpBuiltinsAssembler::BranchIfFastRegExpResult(Node* context, Node* map, |
| 391 CLabel* if_isunmodified, CLabel* if_ismodified) { | 443 Label* if_isunmodified, |
| 392 Node* const native_context = a->LoadNativeContext(context); | 444 Label* if_ismodified) { |
| 445 Node* const native_context = LoadNativeContext(context); | |
| 393 Node* const initial_regexp_result_map = | 446 Node* const initial_regexp_result_map = |
| 394 a->LoadContextElement(native_context, Context::REGEXP_RESULT_MAP_INDEX); | 447 LoadContextElement(native_context, Context::REGEXP_RESULT_MAP_INDEX); |
| 395 | 448 |
| 396 a->Branch(a->WordEqual(map, initial_regexp_result_map), if_isunmodified, | 449 Branch(WordEqual(map, initial_regexp_result_map), if_isunmodified, |
| 397 if_ismodified); | 450 if_ismodified); |
| 398 } | 451 } |
| 399 | 452 |
| 400 } // namespace | |
| 401 | |
| 402 // ES#sec-regexp.prototype.exec | 453 // ES#sec-regexp.prototype.exec |
| 403 // RegExp.prototype.exec ( string ) | 454 // RegExp.prototype.exec ( string ) |
| 404 TF_BUILTIN(RegExpPrototypeExec, RegExpBuiltinsAssembler) { | 455 TF_BUILTIN(RegExpPrototypeExec, RegExpBuiltinsAssembler) { |
| 405 Node* const maybe_receiver = Parameter(0); | 456 Node* const maybe_receiver = Parameter(0); |
| 406 Node* const maybe_string = Parameter(1); | 457 Node* const maybe_string = Parameter(1); |
| 407 Node* const context = Parameter(4); | 458 Node* const context = Parameter(4); |
| 408 | 459 |
| 409 // Ensure {maybe_receiver} is a JSRegExp. | 460 // Ensure {maybe_receiver} is a JSRegExp. |
| 410 Node* const regexp_map = ThrowIfNotInstanceType( | 461 Node* const regexp_map = ThrowIfNotInstanceType( |
| 411 context, maybe_receiver, JS_REGEXP_TYPE, "RegExp.prototype.exec"); | 462 context, maybe_receiver, JS_REGEXP_TYPE, "RegExp.prototype.exec"); |
| 412 Node* const receiver = maybe_receiver; | 463 Node* const receiver = maybe_receiver; |
| 413 | 464 |
| 414 // Convert {maybe_string} to a String. | 465 // Convert {maybe_string} to a String. |
| 415 Node* const string = ToString(context, maybe_string); | 466 Node* const string = ToString(context, maybe_string); |
| 416 | 467 |
| 417 CLabel if_isfastpath(this), if_isslowpath(this); | 468 Label if_isfastpath(this), if_isslowpath(this); |
| 418 Branch(IsInitialRegExpMap(this, context, regexp_map), &if_isfastpath, | 469 Branch(IsInitialRegExpMap(context, regexp_map), &if_isfastpath, |
| 419 &if_isslowpath); | 470 &if_isslowpath); |
| 420 | 471 |
| 421 Bind(&if_isfastpath); | 472 Bind(&if_isfastpath); |
| 422 { | 473 { |
| 423 Node* const result = | 474 Node* const result = |
| 424 RegExpPrototypeExecBody(this, context, receiver, string, true); | 475 RegExpPrototypeExecBody(context, receiver, string, true); |
| 425 Return(result); | 476 Return(result); |
| 426 } | 477 } |
| 427 | 478 |
| 428 Bind(&if_isslowpath); | 479 Bind(&if_isslowpath); |
| 429 { | 480 { |
| 430 Node* const result = | 481 Node* const result = |
| 431 RegExpPrototypeExecBody(this, context, receiver, string, false); | 482 RegExpPrototypeExecBody(context, receiver, string, false); |
| 432 Return(result); | 483 Return(result); |
| 433 } | 484 } |
| 434 } | 485 } |
| 435 | 486 |
| 436 namespace { | 487 Node* RegExpBuiltinsAssembler::FlagsGetter(Node* const context, |
| 488 Node* const regexp, | |
| 489 bool is_fastpath) { | |
| 490 Isolate* isolate = this->isolate(); | |
| 437 | 491 |
| 438 Node* FlagsGetter(CodeStubAssembler* a, Node* const receiver, | 492 Node* const int_zero = IntPtrConstant(0); |
| 439 Node* const context, bool is_fastpath) { | 493 Node* const int_one = IntPtrConstant(1); |
| 440 Isolate* isolate = a->isolate(); | 494 Variable var_length(this, MachineType::PointerRepresentation()); |
| 441 | 495 Variable var_flags(this, MachineType::PointerRepresentation()); |
| 442 Node* const int_zero = a->IntPtrConstant(0); | |
| 443 Node* const int_one = a->IntPtrConstant(1); | |
| 444 CVariable var_length(a, MachineType::PointerRepresentation()); | |
| 445 CVariable var_flags(a, MachineType::PointerRepresentation()); | |
| 446 | 496 |
| 447 // First, count the number of characters we will need and check which flags | 497 // First, count the number of characters we will need and check which flags |
| 448 // are set. | 498 // are set. |
| 449 | 499 |
| 450 var_length.Bind(int_zero); | 500 var_length.Bind(int_zero); |
| 451 | 501 |
| 452 CLabel construct_string(a); | 502 Label construct_string(this); |
| 453 if (is_fastpath) { | 503 if (is_fastpath) { |
| 454 // Refer to JSRegExp's flag property on the fast-path. | 504 // Refer to JSRegExp's flag property on the fast-path. |
| 455 Node* const flags_smi = | 505 Node* const flags_smi = LoadObjectField(regexp, JSRegExp::kFlagsOffset); |
| 456 a->LoadObjectField(receiver, JSRegExp::kFlagsOffset); | 506 Node* const flags_intptr = SmiUntag(flags_smi); |
| 457 Node* const flags_intptr = a->SmiUntag(flags_smi); | |
| 458 var_flags.Bind(flags_intptr); | 507 var_flags.Bind(flags_intptr); |
| 459 | 508 |
| 460 CLabel label_global(a), label_ignorecase(a), label_multiline(a), | 509 Label label_global(this), label_ignorecase(this), label_multiline(this), |
| 461 label_unicode(a), label_sticky(a); | 510 label_unicode(this), label_sticky(this); |
| 462 | 511 |
| 463 #define CASE_FOR_FLAG(FLAG, LABEL, NEXT_LABEL) \ | 512 #define CASE_FOR_FLAG(FLAG, LABEL, NEXT_LABEL) \ |
| 464 do { \ | 513 do { \ |
| 465 a->Bind(&LABEL); \ | 514 Bind(&LABEL); \ |
| 466 Node* const mask = a->IntPtrConstant(FLAG); \ | 515 Node* const mask = IntPtrConstant(FLAG); \ |
| 467 a->GotoIf(a->WordEqual(a->WordAnd(flags_intptr, mask), int_zero), \ | 516 GotoIf(WordEqual(WordAnd(flags_intptr, mask), int_zero), &NEXT_LABEL); \ |
| 468 &NEXT_LABEL); \ | 517 var_length.Bind(IntPtrAdd(var_length.value(), int_one)); \ |
| 469 var_length.Bind(a->IntPtrAdd(var_length.value(), int_one)); \ | 518 Goto(&NEXT_LABEL); \ |
| 470 a->Goto(&NEXT_LABEL); \ | |
| 471 } while (false) | 519 } while (false) |
| 472 | 520 |
| 473 a->Goto(&label_global); | 521 Goto(&label_global); |
| 474 CASE_FOR_FLAG(JSRegExp::kGlobal, label_global, label_ignorecase); | 522 CASE_FOR_FLAG(JSRegExp::kGlobal, label_global, label_ignorecase); |
| 475 CASE_FOR_FLAG(JSRegExp::kIgnoreCase, label_ignorecase, label_multiline); | 523 CASE_FOR_FLAG(JSRegExp::kIgnoreCase, label_ignorecase, label_multiline); |
| 476 CASE_FOR_FLAG(JSRegExp::kMultiline, label_multiline, label_unicode); | 524 CASE_FOR_FLAG(JSRegExp::kMultiline, label_multiline, label_unicode); |
| 477 CASE_FOR_FLAG(JSRegExp::kUnicode, label_unicode, label_sticky); | 525 CASE_FOR_FLAG(JSRegExp::kUnicode, label_unicode, label_sticky); |
| 478 CASE_FOR_FLAG(JSRegExp::kSticky, label_sticky, construct_string); | 526 CASE_FOR_FLAG(JSRegExp::kSticky, label_sticky, construct_string); |
| 479 #undef CASE_FOR_FLAG | 527 #undef CASE_FOR_FLAG |
| 480 } else { | 528 } else { |
| 481 DCHECK(!is_fastpath); | 529 DCHECK(!is_fastpath); |
| 482 | 530 |
| 483 // Fall back to GetProperty stub on the slow-path. | 531 // Fall back to GetProperty stub on the slow-path. |
| 484 var_flags.Bind(int_zero); | 532 var_flags.Bind(int_zero); |
| 485 | 533 |
| 486 Callable getproperty_callable = CodeFactory::GetProperty(a->isolate()); | 534 Callable getproperty_callable = CodeFactory::GetProperty(isolate); |
| 487 CLabel label_global(a), label_ignorecase(a), label_multiline(a), | 535 Label label_global(this), label_ignorecase(this), label_multiline(this), |
| 488 label_unicode(a), label_sticky(a); | 536 label_unicode(this), label_sticky(this); |
| 489 | 537 |
| 490 #define CASE_FOR_FLAG(NAME, FLAG, LABEL, NEXT_LABEL) \ | 538 #define CASE_FOR_FLAG(NAME, FLAG, LABEL, NEXT_LABEL) \ |
| 491 do { \ | 539 do { \ |
| 492 a->Bind(&LABEL); \ | 540 Bind(&LABEL); \ |
| 493 Node* const name = \ | 541 Node* const name = \ |
| 494 a->HeapConstant(isolate->factory()->NewStringFromAsciiChecked(NAME)); \ | 542 HeapConstant(isolate->factory()->NewStringFromAsciiChecked(NAME)); \ |
| 495 Node* const flag = \ | 543 Node* const flag = CallStub(getproperty_callable, context, regexp, name); \ |
| 496 a->CallStub(getproperty_callable, context, receiver, name); \ | 544 Label if_isflagset(this); \ |
| 497 CLabel if_isflagset(a); \ | 545 BranchIfToBooleanIsTrue(flag, &if_isflagset, &NEXT_LABEL); \ |
| 498 a->BranchIfToBooleanIsTrue(flag, &if_isflagset, &NEXT_LABEL); \ | 546 Bind(&if_isflagset); \ |
| 499 a->Bind(&if_isflagset); \ | 547 var_length.Bind(IntPtrAdd(var_length.value(), int_one)); \ |
| 500 var_length.Bind(a->IntPtrAdd(var_length.value(), int_one)); \ | 548 var_flags.Bind(WordOr(var_flags.value(), IntPtrConstant(FLAG))); \ |
| 501 var_flags.Bind(a->WordOr(var_flags.value(), a->IntPtrConstant(FLAG))); \ | 549 Goto(&NEXT_LABEL); \ |
| 502 a->Goto(&NEXT_LABEL); \ | |
| 503 } while (false) | 550 } while (false) |
| 504 | 551 |
| 505 a->Goto(&label_global); | 552 Goto(&label_global); |
| 506 CASE_FOR_FLAG("global", JSRegExp::kGlobal, label_global, label_ignorecase); | 553 CASE_FOR_FLAG("global", JSRegExp::kGlobal, label_global, label_ignorecase); |
| 507 CASE_FOR_FLAG("ignoreCase", JSRegExp::kIgnoreCase, label_ignorecase, | 554 CASE_FOR_FLAG("ignoreCase", JSRegExp::kIgnoreCase, label_ignorecase, |
| 508 label_multiline); | 555 label_multiline); |
| 509 CASE_FOR_FLAG("multiline", JSRegExp::kMultiline, label_multiline, | 556 CASE_FOR_FLAG("multiline", JSRegExp::kMultiline, label_multiline, |
| 510 label_unicode); | 557 label_unicode); |
| 511 CASE_FOR_FLAG("unicode", JSRegExp::kUnicode, label_unicode, label_sticky); | 558 CASE_FOR_FLAG("unicode", JSRegExp::kUnicode, label_unicode, label_sticky); |
| 512 CASE_FOR_FLAG("sticky", JSRegExp::kSticky, label_sticky, construct_string); | 559 CASE_FOR_FLAG("sticky", JSRegExp::kSticky, label_sticky, construct_string); |
| 513 #undef CASE_FOR_FLAG | 560 #undef CASE_FOR_FLAG |
| 514 } | 561 } |
| 515 | 562 |
| 516 // Allocate a string of the required length and fill it with the corresponding | 563 // Allocate a string of the required length and fill it with the corresponding |
| 517 // char for each set flag. | 564 // char for each set flag. |
| 518 | 565 |
| 519 a->Bind(&construct_string); | 566 Bind(&construct_string); |
| 520 { | 567 { |
| 521 Node* const result = | 568 Node* const result = AllocateSeqOneByteString(context, var_length.value()); |
| 522 a->AllocateSeqOneByteString(context, var_length.value()); | |
| 523 Node* const flags_intptr = var_flags.value(); | 569 Node* const flags_intptr = var_flags.value(); |
| 524 | 570 |
| 525 CVariable var_offset(a, MachineType::PointerRepresentation()); | 571 Variable var_offset(this, MachineType::PointerRepresentation()); |
| 526 var_offset.Bind( | 572 var_offset.Bind( |
| 527 a->IntPtrConstant(SeqOneByteString::kHeaderSize - kHeapObjectTag)); | 573 IntPtrConstant(SeqOneByteString::kHeaderSize - kHeapObjectTag)); |
| 528 | 574 |
| 529 CLabel label_global(a), label_ignorecase(a), label_multiline(a), | 575 Label label_global(this), label_ignorecase(this), label_multiline(this), |
| 530 label_unicode(a), label_sticky(a), out(a); | 576 label_unicode(this), label_sticky(this), out(this); |
| 531 | 577 |
| 532 #define CASE_FOR_FLAG(FLAG, CHAR, LABEL, NEXT_LABEL) \ | 578 #define CASE_FOR_FLAG(FLAG, CHAR, LABEL, NEXT_LABEL) \ |
| 533 do { \ | 579 do { \ |
| 534 a->Bind(&LABEL); \ | 580 Bind(&LABEL); \ |
| 535 Node* const mask = a->IntPtrConstant(FLAG); \ | 581 Node* const mask = IntPtrConstant(FLAG); \ |
| 536 a->GotoIf(a->WordEqual(a->WordAnd(flags_intptr, mask), int_zero), \ | 582 GotoIf(WordEqual(WordAnd(flags_intptr, mask), int_zero), &NEXT_LABEL); \ |
| 537 &NEXT_LABEL); \ | 583 Node* const value = IntPtrConstant(CHAR); \ |
| 538 Node* const value = a->IntPtrConstant(CHAR); \ | 584 StoreNoWriteBarrier(MachineRepresentation::kWord8, result, \ |
| 539 a->StoreNoWriteBarrier(MachineRepresentation::kWord8, result, \ | 585 var_offset.value(), value); \ |
| 540 var_offset.value(), value); \ | 586 var_offset.Bind(IntPtrAdd(var_offset.value(), int_one)); \ |
| 541 var_offset.Bind(a->IntPtrAdd(var_offset.value(), int_one)); \ | 587 Goto(&NEXT_LABEL); \ |
| 542 a->Goto(&NEXT_LABEL); \ | |
| 543 } while (false) | 588 } while (false) |
| 544 | 589 |
| 545 a->Goto(&label_global); | 590 Goto(&label_global); |
| 546 CASE_FOR_FLAG(JSRegExp::kGlobal, 'g', label_global, label_ignorecase); | 591 CASE_FOR_FLAG(JSRegExp::kGlobal, 'g', label_global, label_ignorecase); |
| 547 CASE_FOR_FLAG(JSRegExp::kIgnoreCase, 'i', label_ignorecase, | 592 CASE_FOR_FLAG(JSRegExp::kIgnoreCase, 'i', label_ignorecase, |
| 548 label_multiline); | 593 label_multiline); |
| 549 CASE_FOR_FLAG(JSRegExp::kMultiline, 'm', label_multiline, label_unicode); | 594 CASE_FOR_FLAG(JSRegExp::kMultiline, 'm', label_multiline, label_unicode); |
| 550 CASE_FOR_FLAG(JSRegExp::kUnicode, 'u', label_unicode, label_sticky); | 595 CASE_FOR_FLAG(JSRegExp::kUnicode, 'u', label_unicode, label_sticky); |
| 551 CASE_FOR_FLAG(JSRegExp::kSticky, 'y', label_sticky, out); | 596 CASE_FOR_FLAG(JSRegExp::kSticky, 'y', label_sticky, out); |
| 552 #undef CASE_FOR_FLAG | 597 #undef CASE_FOR_FLAG |
| 553 | 598 |
| 554 a->Bind(&out); | 599 Bind(&out); |
| 555 return result; | 600 return result; |
| 556 } | 601 } |
| 557 } | 602 } |
| 558 | 603 |
| 559 // ES#sec-isregexp IsRegExp ( argument ) | 604 // ES#sec-isregexp IsRegExp ( argument ) |
| 560 Node* IsRegExp(CodeStubAssembler* a, Node* const context, | 605 Node* RegExpBuiltinsAssembler::IsRegExp(Node* const context, |
| 561 Node* const maybe_receiver) { | 606 Node* const maybe_receiver) { |
| 562 CLabel out(a), if_isregexp(a); | 607 Label out(this), if_isregexp(this); |
| 563 | 608 |
| 564 CVariable var_result(a, MachineType::PointerRepresentation()); | 609 Variable var_result(this, MachineType::PointerRepresentation()); |
| 565 var_result.Bind(a->IntPtrConstant(0)); | 610 var_result.Bind(IntPtrConstant(0)); |
| 566 | 611 |
| 567 a->GotoIf(a->TaggedIsSmi(maybe_receiver), &out); | 612 GotoIf(TaggedIsSmi(maybe_receiver), &out); |
| 568 a->GotoUnless(a->IsJSReceiver(maybe_receiver), &out); | 613 GotoUnless(IsJSReceiver(maybe_receiver), &out); |
| 569 | 614 |
| 570 Node* const receiver = maybe_receiver; | 615 Node* const receiver = maybe_receiver; |
| 571 | 616 |
| 572 // Check @@match. | 617 // Check @@match. |
| 573 { | 618 { |
| 574 Callable getproperty_callable = CodeFactory::GetProperty(a->isolate()); | 619 Callable getproperty_callable = CodeFactory::GetProperty(isolate()); |
| 575 Node* const name = a->HeapConstant(a->isolate()->factory()->match_symbol()); | 620 Node* const name = HeapConstant(isolate()->factory()->match_symbol()); |
| 576 Node* const value = | 621 Node* const value = CallStub(getproperty_callable, context, receiver, name); |
| 577 a->CallStub(getproperty_callable, context, receiver, name); | |
| 578 | 622 |
| 579 CLabel match_isundefined(a), match_isnotundefined(a); | 623 Label match_isundefined(this), match_isnotundefined(this); |
| 580 a->Branch(a->IsUndefined(value), &match_isundefined, &match_isnotundefined); | 624 Branch(IsUndefined(value), &match_isundefined, &match_isnotundefined); |
| 581 | 625 |
| 582 a->Bind(&match_isundefined); | 626 Bind(&match_isundefined); |
| 583 a->Branch(a->HasInstanceType(receiver, JS_REGEXP_TYPE), &if_isregexp, &out); | 627 Branch(HasInstanceType(receiver, JS_REGEXP_TYPE), &if_isregexp, &out); |
| 584 | 628 |
| 585 a->Bind(&match_isnotundefined); | 629 Bind(&match_isnotundefined); |
| 586 a->BranchIfToBooleanIsTrue(value, &if_isregexp, &out); | 630 BranchIfToBooleanIsTrue(value, &if_isregexp, &out); |
| 587 } | 631 } |
| 588 | 632 |
| 589 a->Bind(&if_isregexp); | 633 Bind(&if_isregexp); |
| 590 var_result.Bind(a->IntPtrConstant(1)); | 634 var_result.Bind(IntPtrConstant(1)); |
| 591 a->Goto(&out); | 635 Goto(&out); |
| 592 | 636 |
| 593 a->Bind(&out); | 637 Bind(&out); |
| 594 return var_result.value(); | 638 return var_result.value(); |
| 595 } | 639 } |
| 596 | 640 |
| 597 // ES#sec-regexpinitialize | 641 // ES#sec-regexpinitialize |
| 598 // Runtime Semantics: RegExpInitialize ( obj, pattern, flags ) | 642 // Runtime Semantics: RegExpInitialize ( obj, pattern, flags ) |
| 599 Node* RegExpInitialize(CodeStubAssembler* a, Node* const context, | 643 Node* RegExpBuiltinsAssembler::RegExpInitialize(Node* const context, |
| 600 Node* const regexp, Node* const maybe_pattern, | 644 Node* const regexp, |
| 601 Node* const maybe_flags) { | 645 Node* const maybe_pattern, |
| 602 CVariable var_flags(a, MachineRepresentation::kTagged); | 646 Node* const maybe_flags) { |
| 603 CVariable var_pattern(a, MachineRepresentation::kTagged); | 647 Variable var_flags(this, MachineRepresentation::kTagged); |
| 648 Variable var_pattern(this, MachineRepresentation::kTagged); | |
| 604 | 649 |
| 605 // Normalize pattern. | 650 // Normalize pattern. |
| 606 { | 651 { |
| 607 CLabel next(a), if_isundefined(a), if_notundefined(a); | 652 Label next(this), if_isundefined(this), if_notundefined(this); |
| 608 a->Branch(a->IsUndefined(maybe_pattern), &if_isundefined, &if_notundefined); | 653 Branch(IsUndefined(maybe_pattern), &if_isundefined, &if_notundefined); |
| 609 | 654 |
| 610 a->Bind(&if_isundefined); | 655 Bind(&if_isundefined); |
| 611 { | 656 { |
| 612 var_pattern.Bind(a->EmptyStringConstant()); | 657 var_pattern.Bind(EmptyStringConstant()); |
| 613 a->Goto(&next); | 658 Goto(&next); |
| 614 } | 659 } |
| 615 | 660 |
| 616 a->Bind(&if_notundefined); | 661 Bind(&if_notundefined); |
| 617 { | 662 { |
| 618 Node* const pattern = a->ToString(context, maybe_pattern); | 663 Node* const pattern = ToString(context, maybe_pattern); |
| 619 var_pattern.Bind(pattern); | 664 var_pattern.Bind(pattern); |
| 620 a->Goto(&next); | 665 Goto(&next); |
| 621 } | 666 } |
| 622 | 667 |
| 623 a->Bind(&next); | 668 Bind(&next); |
| 624 } | 669 } |
| 625 | 670 |
| 626 // Normalize flags. | 671 // Normalize flags. |
| 627 { | 672 { |
| 628 CLabel next(a), if_isundefined(a), if_notundefined(a); | 673 Label next(this), if_isundefined(this), if_notundefined(this); |
| 629 a->Branch(a->IsUndefined(maybe_flags), &if_isundefined, &if_notundefined); | 674 Branch(IsUndefined(maybe_flags), &if_isundefined, &if_notundefined); |
| 630 | 675 |
| 631 a->Bind(&if_isundefined); | 676 Bind(&if_isundefined); |
| 632 { | 677 { |
| 633 var_flags.Bind(a->EmptyStringConstant()); | 678 var_flags.Bind(EmptyStringConstant()); |
| 634 a->Goto(&next); | 679 Goto(&next); |
| 635 } | 680 } |
| 636 | 681 |
| 637 a->Bind(&if_notundefined); | 682 Bind(&if_notundefined); |
| 638 { | 683 { |
| 639 Node* const flags = a->ToString(context, maybe_flags); | 684 Node* const flags = ToString(context, maybe_flags); |
| 640 var_flags.Bind(flags); | 685 var_flags.Bind(flags); |
| 641 a->Goto(&next); | 686 Goto(&next); |
| 642 } | 687 } |
| 643 | 688 |
| 644 a->Bind(&next); | 689 Bind(&next); |
| 645 } | 690 } |
| 646 | 691 |
| 647 // Initialize. | 692 // Initialize. |
| 648 | 693 |
| 649 { | 694 { |
| 650 Node* const result = | 695 Node* const result = |
| 651 a->CallRuntime(Runtime::kRegExpInitializeAndCompile, context, regexp, | 696 CallRuntime(Runtime::kRegExpInitializeAndCompile, context, regexp, |
| 652 var_pattern.value(), var_flags.value()); | 697 var_pattern.value(), var_flags.value()); |
| 653 return result; | 698 return result; |
| 654 } | 699 } |
| 655 } | 700 } |
| 656 | 701 |
| 657 } // namespace | |
| 658 | |
| 659 TF_BUILTIN(RegExpPrototypeFlagsGetter, RegExpBuiltinsAssembler) { | 702 TF_BUILTIN(RegExpPrototypeFlagsGetter, RegExpBuiltinsAssembler) { |
| 660 Isolate* isolate = this->isolate(); | |
| 661 | |
| 662 Node* const maybe_receiver = Parameter(0); | 703 Node* const maybe_receiver = Parameter(0); |
| 663 Node* const context = Parameter(3); | 704 Node* const context = Parameter(3); |
| 664 | 705 |
| 665 Node* const map = ThrowIfNotJSReceiver(this, isolate, context, maybe_receiver, | 706 Node* const map = ThrowIfNotJSReceiver(context, maybe_receiver, |
| 666 MessageTemplate::kRegExpNonObject, | 707 MessageTemplate::kRegExpNonObject, |
| 667 "RegExp.prototype.flags"); | 708 "RegExp.prototype.flags"); |
| 668 Node* const receiver = maybe_receiver; | 709 Node* const receiver = maybe_receiver; |
| 669 | 710 |
| 670 CLabel if_isfastpath(this), if_isslowpath(this, CLabel::kDeferred); | 711 Label if_isfastpath(this), if_isslowpath(this, Label::kDeferred); |
| 671 Branch(IsInitialRegExpMap(this, context, map), &if_isfastpath, | 712 Branch(IsInitialRegExpMap(context, map), &if_isfastpath, &if_isslowpath); |
| 672 &if_isslowpath); | |
| 673 | 713 |
| 674 Bind(&if_isfastpath); | 714 Bind(&if_isfastpath); |
| 675 Return(FlagsGetter(this, receiver, context, true)); | 715 Return(FlagsGetter(context, receiver, true)); |
| 676 | 716 |
| 677 Bind(&if_isslowpath); | 717 Bind(&if_isslowpath); |
| 678 Return(FlagsGetter(this, receiver, context, false)); | 718 Return(FlagsGetter(context, receiver, false)); |
| 679 } | 719 } |
| 680 | 720 |
| 681 // ES#sec-regexp-pattern-flags | 721 // ES#sec-regexp-pattern-flags |
| 682 // RegExp ( pattern, flags ) | 722 // RegExp ( pattern, flags ) |
| 683 TF_BUILTIN(RegExpConstructor, RegExpBuiltinsAssembler) { | 723 TF_BUILTIN(RegExpConstructor, RegExpBuiltinsAssembler) { |
| 684 Node* const pattern = Parameter(1); | 724 Node* const pattern = Parameter(1); |
| 685 Node* const flags = Parameter(2); | 725 Node* const flags = Parameter(2); |
| 686 Node* const new_target = Parameter(3); | 726 Node* const new_target = Parameter(3); |
| 687 Node* const context = Parameter(5); | 727 Node* const context = Parameter(5); |
| 688 | 728 |
| 689 Isolate* isolate = this->isolate(); | 729 Isolate* isolate = this->isolate(); |
| 690 | 730 |
| 691 CVariable var_flags(this, MachineRepresentation::kTagged); | 731 Variable var_flags(this, MachineRepresentation::kTagged); |
| 692 CVariable var_pattern(this, MachineRepresentation::kTagged); | 732 Variable var_pattern(this, MachineRepresentation::kTagged); |
| 693 CVariable var_new_target(this, MachineRepresentation::kTagged); | 733 Variable var_new_target(this, MachineRepresentation::kTagged); |
| 694 | 734 |
| 695 var_flags.Bind(flags); | 735 var_flags.Bind(flags); |
| 696 var_pattern.Bind(pattern); | 736 var_pattern.Bind(pattern); |
| 697 var_new_target.Bind(new_target); | 737 var_new_target.Bind(new_target); |
| 698 | 738 |
| 699 Node* const native_context = LoadNativeContext(context); | 739 Node* const native_context = LoadNativeContext(context); |
| 700 Node* const regexp_function = | 740 Node* const regexp_function = |
| 701 LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX); | 741 LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX); |
| 702 | 742 |
| 703 Node* const pattern_is_regexp = IsRegExp(this, context, pattern); | 743 Node* const pattern_is_regexp = IsRegExp(context, pattern); |
| 704 | 744 |
| 705 { | 745 { |
| 706 CLabel next(this); | 746 Label next(this); |
| 707 | 747 |
| 708 GotoUnless(IsUndefined(new_target), &next); | 748 GotoUnless(IsUndefined(new_target), &next); |
| 709 var_new_target.Bind(regexp_function); | 749 var_new_target.Bind(regexp_function); |
| 710 | 750 |
| 711 GotoUnless(pattern_is_regexp, &next); | 751 GotoUnless(pattern_is_regexp, &next); |
| 712 GotoUnless(IsUndefined(flags), &next); | 752 GotoUnless(IsUndefined(flags), &next); |
| 713 | 753 |
| 714 Callable getproperty_callable = CodeFactory::GetProperty(isolate); | 754 Callable getproperty_callable = CodeFactory::GetProperty(isolate); |
| 715 Node* const name = HeapConstant(isolate->factory()->constructor_string()); | 755 Node* const name = HeapConstant(isolate->factory()->constructor_string()); |
| 716 Node* const value = CallStub(getproperty_callable, context, pattern, name); | 756 Node* const value = CallStub(getproperty_callable, context, pattern, name); |
| 717 | 757 |
| 718 GotoUnless(WordEqual(value, regexp_function), &next); | 758 GotoUnless(WordEqual(value, regexp_function), &next); |
| 719 Return(pattern); | 759 Return(pattern); |
| 720 | 760 |
| 721 Bind(&next); | 761 Bind(&next); |
| 722 } | 762 } |
| 723 | 763 |
| 724 { | 764 { |
| 725 CLabel next(this), if_patternisfastregexp(this), | 765 Label next(this), if_patternisfastregexp(this), |
| 726 if_patternisslowregexp(this); | 766 if_patternisslowregexp(this); |
| 727 GotoIf(TaggedIsSmi(pattern), &next); | 767 GotoIf(TaggedIsSmi(pattern), &next); |
| 728 | 768 |
| 729 GotoIf(HasInstanceType(pattern, JS_REGEXP_TYPE), &if_patternisfastregexp); | 769 GotoIf(HasInstanceType(pattern, JS_REGEXP_TYPE), &if_patternisfastregexp); |
| 730 | 770 |
| 731 Branch(pattern_is_regexp, &if_patternisslowregexp, &next); | 771 Branch(pattern_is_regexp, &if_patternisslowregexp, &next); |
| 732 | 772 |
| 733 Bind(&if_patternisfastregexp); | 773 Bind(&if_patternisfastregexp); |
| 734 { | 774 { |
| 735 Node* const source = LoadObjectField(pattern, JSRegExp::kSourceOffset); | 775 Node* const source = LoadObjectField(pattern, JSRegExp::kSourceOffset); |
| 736 var_pattern.Bind(source); | 776 var_pattern.Bind(source); |
| 737 | 777 |
| 738 { | 778 { |
| 739 CLabel inner_next(this); | 779 Label inner_next(this); |
| 740 GotoUnless(IsUndefined(flags), &inner_next); | 780 GotoUnless(IsUndefined(flags), &inner_next); |
| 741 | 781 |
| 742 Node* const value = FlagsGetter(this, pattern, context, true); | 782 Node* const value = FlagsGetter(context, pattern, true); |
| 743 var_flags.Bind(value); | 783 var_flags.Bind(value); |
| 744 Goto(&inner_next); | 784 Goto(&inner_next); |
| 745 | 785 |
| 746 Bind(&inner_next); | 786 Bind(&inner_next); |
| 747 } | 787 } |
| 748 | 788 |
| 749 Goto(&next); | 789 Goto(&next); |
| 750 } | 790 } |
| 751 | 791 |
| 752 Bind(&if_patternisslowregexp); | 792 Bind(&if_patternisslowregexp); |
| 753 { | 793 { |
| 754 Callable getproperty_callable = CodeFactory::GetProperty(isolate); | 794 Callable getproperty_callable = CodeFactory::GetProperty(isolate); |
| 755 | 795 |
| 756 { | 796 { |
| 757 Node* const name = HeapConstant(isolate->factory()->source_string()); | 797 Node* const name = HeapConstant(isolate->factory()->source_string()); |
| 758 Node* const value = | 798 Node* const value = |
| 759 CallStub(getproperty_callable, context, pattern, name); | 799 CallStub(getproperty_callable, context, pattern, name); |
| 760 var_pattern.Bind(value); | 800 var_pattern.Bind(value); |
| 761 } | 801 } |
| 762 | 802 |
| 763 { | 803 { |
| 764 CLabel inner_next(this); | 804 Label inner_next(this); |
| 765 GotoUnless(IsUndefined(flags), &inner_next); | 805 GotoUnless(IsUndefined(flags), &inner_next); |
| 766 | 806 |
| 767 Node* const name = HeapConstant(isolate->factory()->flags_string()); | 807 Node* const name = HeapConstant(isolate->factory()->flags_string()); |
| 768 Node* const value = | 808 Node* const value = |
| 769 CallStub(getproperty_callable, context, pattern, name); | 809 CallStub(getproperty_callable, context, pattern, name); |
| 770 var_flags.Bind(value); | 810 var_flags.Bind(value); |
| 771 Goto(&inner_next); | 811 Goto(&inner_next); |
| 772 | 812 |
| 773 Bind(&inner_next); | 813 Bind(&inner_next); |
| 774 } | 814 } |
| 775 | 815 |
| 776 Goto(&next); | 816 Goto(&next); |
| 777 } | 817 } |
| 778 | 818 |
| 779 Bind(&next); | 819 Bind(&next); |
| 780 } | 820 } |
| 781 | 821 |
| 782 // Allocate. | 822 // Allocate. |
| 783 | 823 |
| 784 CVariable var_regexp(this, MachineRepresentation::kTagged); | 824 Variable var_regexp(this, MachineRepresentation::kTagged); |
| 785 { | 825 { |
| 786 CLabel allocate_jsregexp(this), allocate_generic(this, CLabel::kDeferred), | 826 Label allocate_jsregexp(this), allocate_generic(this, Label::kDeferred), |
| 787 next(this); | 827 next(this); |
| 788 Branch(WordEqual(var_new_target.value(), regexp_function), | 828 Branch(WordEqual(var_new_target.value(), regexp_function), |
| 789 &allocate_jsregexp, &allocate_generic); | 829 &allocate_jsregexp, &allocate_generic); |
| 790 | 830 |
| 791 Bind(&allocate_jsregexp); | 831 Bind(&allocate_jsregexp); |
| 792 { | 832 { |
| 793 Node* const initial_map = LoadObjectField( | 833 Node* const initial_map = LoadObjectField( |
| 794 regexp_function, JSFunction::kPrototypeOrInitialMapOffset); | 834 regexp_function, JSFunction::kPrototypeOrInitialMapOffset); |
| 795 Node* const regexp = AllocateJSObjectFromMap(initial_map); | 835 Node* const regexp = AllocateJSObjectFromMap(initial_map); |
| 796 var_regexp.Bind(regexp); | 836 var_regexp.Bind(regexp); |
| 797 Goto(&next); | 837 Goto(&next); |
| 798 } | 838 } |
| 799 | 839 |
| 800 Bind(&allocate_generic); | 840 Bind(&allocate_generic); |
| 801 { | 841 { |
| 802 Callable fastnewobject_callable = CodeFactory::FastNewObject(isolate); | 842 Callable fastnewobject_callable = CodeFactory::FastNewObject(isolate); |
| 803 Node* const regexp = CallStub(fastnewobject_callable, context, | 843 Node* const regexp = CallStub(fastnewobject_callable, context, |
| 804 regexp_function, var_new_target.value()); | 844 regexp_function, var_new_target.value()); |
| 805 var_regexp.Bind(regexp); | 845 var_regexp.Bind(regexp); |
| 806 Goto(&next); | 846 Goto(&next); |
| 807 } | 847 } |
| 808 | 848 |
| 809 Bind(&next); | 849 Bind(&next); |
| 810 } | 850 } |
| 811 | 851 |
| 812 Node* const result = RegExpInitialize(this, context, var_regexp.value(), | 852 Node* const result = RegExpInitialize(context, var_regexp.value(), |
| 813 var_pattern.value(), var_flags.value()); | 853 var_pattern.value(), var_flags.value()); |
| 814 Return(result); | 854 Return(result); |
| 815 } | 855 } |
| 816 | 856 |
| 817 // ES#sec-regexp.prototype.compile | 857 // ES#sec-regexp.prototype.compile |
| 818 // RegExp.prototype.compile ( pattern, flags ) | 858 // RegExp.prototype.compile ( pattern, flags ) |
| 819 TF_BUILTIN(RegExpPrototypeCompile, RegExpBuiltinsAssembler) { | 859 TF_BUILTIN(RegExpPrototypeCompile, RegExpBuiltinsAssembler) { |
| 820 Node* const maybe_receiver = Parameter(0); | 860 Node* const maybe_receiver = Parameter(0); |
| 821 Node* const maybe_pattern = Parameter(1); | 861 Node* const maybe_pattern = Parameter(1); |
| 822 Node* const maybe_flags = Parameter(2); | 862 Node* const maybe_flags = Parameter(2); |
| 823 Node* const context = Parameter(5); | 863 Node* const context = Parameter(5); |
| 824 | 864 |
| 825 ThrowIfNotInstanceType(context, maybe_receiver, JS_REGEXP_TYPE, | 865 ThrowIfNotInstanceType(context, maybe_receiver, JS_REGEXP_TYPE, |
| 826 "RegExp.prototype.compile"); | 866 "RegExp.prototype.compile"); |
| 827 Node* const receiver = maybe_receiver; | 867 Node* const receiver = maybe_receiver; |
| 828 | 868 |
| 829 CVariable var_flags(this, MachineRepresentation::kTagged); | 869 Variable var_flags(this, MachineRepresentation::kTagged); |
| 830 CVariable var_pattern(this, MachineRepresentation::kTagged); | 870 Variable var_pattern(this, MachineRepresentation::kTagged); |
| 831 | 871 |
| 832 var_flags.Bind(maybe_flags); | 872 var_flags.Bind(maybe_flags); |
| 833 var_pattern.Bind(maybe_pattern); | 873 var_pattern.Bind(maybe_pattern); |
| 834 | 874 |
| 835 // Handle a JSRegExp pattern. | 875 // Handle a JSRegExp pattern. |
| 836 { | 876 { |
| 837 CLabel next(this); | 877 Label next(this); |
| 838 | 878 |
| 839 GotoIf(TaggedIsSmi(maybe_pattern), &next); | 879 GotoIf(TaggedIsSmi(maybe_pattern), &next); |
| 840 GotoUnless(HasInstanceType(maybe_pattern, JS_REGEXP_TYPE), &next); | 880 GotoUnless(HasInstanceType(maybe_pattern, JS_REGEXP_TYPE), &next); |
| 841 | 881 |
| 842 Node* const pattern = maybe_pattern; | 882 Node* const pattern = maybe_pattern; |
| 843 | 883 |
| 844 // {maybe_flags} must be undefined in this case, otherwise throw. | 884 // {maybe_flags} must be undefined in this case, otherwise throw. |
| 845 { | 885 { |
| 846 CLabel next(this); | 886 Label next(this); |
| 847 GotoIf(IsUndefined(maybe_flags), &next); | 887 GotoIf(IsUndefined(maybe_flags), &next); |
| 848 | 888 |
| 849 Node* const message_id = SmiConstant(MessageTemplate::kRegExpFlags); | 889 Node* const message_id = SmiConstant(MessageTemplate::kRegExpFlags); |
| 850 TailCallRuntime(Runtime::kThrowTypeError, context, message_id); | 890 TailCallRuntime(Runtime::kThrowTypeError, context, message_id); |
| 851 | 891 |
| 852 Bind(&next); | 892 Bind(&next); |
| 853 } | 893 } |
| 854 | 894 |
| 855 Node* const new_flags = FlagsGetter(this, pattern, context, true); | 895 Node* const new_flags = FlagsGetter(context, pattern, true); |
| 856 Node* const new_pattern = LoadObjectField(pattern, JSRegExp::kSourceOffset); | 896 Node* const new_pattern = LoadObjectField(pattern, JSRegExp::kSourceOffset); |
| 857 | 897 |
| 858 var_flags.Bind(new_flags); | 898 var_flags.Bind(new_flags); |
| 859 var_pattern.Bind(new_pattern); | 899 var_pattern.Bind(new_pattern); |
| 860 | 900 |
| 861 Goto(&next); | 901 Goto(&next); |
| 862 Bind(&next); | 902 Bind(&next); |
| 863 } | 903 } |
| 864 | 904 |
| 865 RegExpInitialize(this, context, receiver, var_pattern.value(), | 905 RegExpInitialize(context, receiver, var_pattern.value(), var_flags.value()); |
| 866 var_flags.value()); | |
| 867 | 906 |
| 868 // Return undefined for compatibility with JSC. | 907 // Return undefined for compatibility with JSC. |
| 869 // See http://crbug.com/585775 for web compat details. | 908 // See http://crbug.com/585775 for web compat details. |
| 870 | 909 |
| 871 Return(UndefinedConstant()); | 910 Return(UndefinedConstant()); |
| 872 } | 911 } |
| 873 | 912 |
| 874 // ES6 21.2.5.10. | 913 // ES6 21.2.5.10. |
| 875 TF_BUILTIN(RegExpPrototypeSourceGetter, RegExpBuiltinsAssembler) { | 914 TF_BUILTIN(RegExpPrototypeSourceGetter, RegExpBuiltinsAssembler) { |
| 876 Node* const receiver = Parameter(0); | 915 Node* const receiver = Parameter(0); |
| 877 Node* const context = Parameter(3); | 916 Node* const context = Parameter(3); |
| 878 | 917 |
| 879 // Check whether we have an unmodified regexp instance. | 918 // Check whether we have an unmodified regexp instance. |
| 880 CLabel if_isjsregexp(this), if_isnotjsregexp(this, CLabel::kDeferred); | 919 Label if_isjsregexp(this), if_isnotjsregexp(this, Label::kDeferred); |
| 881 | 920 |
| 882 GotoIf(TaggedIsSmi(receiver), &if_isnotjsregexp); | 921 GotoIf(TaggedIsSmi(receiver), &if_isnotjsregexp); |
| 883 Branch(HasInstanceType(receiver, JS_REGEXP_TYPE), &if_isjsregexp, | 922 Branch(HasInstanceType(receiver, JS_REGEXP_TYPE), &if_isjsregexp, |
| 884 &if_isnotjsregexp); | 923 &if_isnotjsregexp); |
| 885 | 924 |
| 886 Bind(&if_isjsregexp); | 925 Bind(&if_isjsregexp); |
| 887 { | 926 { |
| 888 Node* const source = LoadObjectField(receiver, JSRegExp::kSourceOffset); | 927 Node* const source = LoadObjectField(receiver, JSRegExp::kSourceOffset); |
| 889 Return(source); | 928 Return(source); |
| 890 } | 929 } |
| 891 | 930 |
| 892 Bind(&if_isnotjsregexp); | 931 Bind(&if_isnotjsregexp); |
| 893 { | 932 { |
| 894 Isolate* isolate = this->isolate(); | 933 Isolate* isolate = this->isolate(); |
| 895 Node* const native_context = LoadNativeContext(context); | 934 Node* const native_context = LoadNativeContext(context); |
| 896 Node* const regexp_fun = | 935 Node* const regexp_fun = |
| 897 LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX); | 936 LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX); |
| 898 Node* const initial_map = | 937 Node* const initial_map = |
| 899 LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset); | 938 LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset); |
| 900 Node* const initial_prototype = LoadMapPrototype(initial_map); | 939 Node* const initial_prototype = LoadMapPrototype(initial_map); |
| 901 | 940 |
| 902 CLabel if_isprototype(this), if_isnotprototype(this); | 941 Label if_isprototype(this), if_isnotprototype(this); |
| 903 Branch(WordEqual(receiver, initial_prototype), &if_isprototype, | 942 Branch(WordEqual(receiver, initial_prototype), &if_isprototype, |
| 904 &if_isnotprototype); | 943 &if_isnotprototype); |
| 905 | 944 |
| 906 Bind(&if_isprototype); | 945 Bind(&if_isprototype); |
| 907 { | 946 { |
| 908 const int counter = v8::Isolate::kRegExpPrototypeSourceGetter; | 947 const int counter = v8::Isolate::kRegExpPrototypeSourceGetter; |
| 909 Node* const counter_smi = SmiConstant(counter); | 948 Node* const counter_smi = SmiConstant(counter); |
| 910 CallRuntime(Runtime::kIncrementUseCounter, context, counter_smi); | 949 CallRuntime(Runtime::kIncrementUseCounter, context, counter_smi); |
| 911 | 950 |
| 912 Node* const result = | 951 Node* const result = |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 963 | 1002 |
| 964 RETURN_RESULT_OR_FAILURE(isolate, builder.Finish()); | 1003 RETURN_RESULT_OR_FAILURE(isolate, builder.Finish()); |
| 965 } | 1004 } |
| 966 | 1005 |
| 967 // ES6 21.2.4.2. | 1006 // ES6 21.2.4.2. |
| 968 TF_BUILTIN(RegExpPrototypeSpeciesGetter, RegExpBuiltinsAssembler) { | 1007 TF_BUILTIN(RegExpPrototypeSpeciesGetter, RegExpBuiltinsAssembler) { |
| 969 Node* const receiver = Parameter(0); | 1008 Node* const receiver = Parameter(0); |
| 970 Return(receiver); | 1009 Return(receiver); |
| 971 } | 1010 } |
| 972 | 1011 |
| 973 namespace { | |
| 974 | |
| 975 // Fast-path implementation for flag checks on an unmodified JSRegExp instance. | 1012 // Fast-path implementation for flag checks on an unmodified JSRegExp instance. |
| 976 Node* FastFlagGetter(CodeStubAssembler* a, Node* const regexp, | 1013 Node* RegExpBuiltinsAssembler::FastFlagGetter(Node* const regexp, |
| 977 JSRegExp::Flag flag) { | 1014 JSRegExp::Flag flag) { |
| 978 Node* const smi_zero = a->SmiConstant(Smi::kZero); | 1015 Node* const smi_zero = SmiConstant(Smi::kZero); |
| 979 Node* const flags = a->LoadObjectField(regexp, JSRegExp::kFlagsOffset); | 1016 Node* const flags = LoadObjectField(regexp, JSRegExp::kFlagsOffset); |
| 980 Node* const mask = a->SmiConstant(Smi::FromInt(flag)); | 1017 Node* const mask = SmiConstant(Smi::FromInt(flag)); |
| 981 Node* const is_flag_set = a->WordNotEqual(a->WordAnd(flags, mask), smi_zero); | 1018 Node* const is_flag_set = WordNotEqual(WordAnd(flags, mask), smi_zero); |
| 982 | 1019 |
| 983 return is_flag_set; | 1020 return is_flag_set; |
| 984 } | 1021 } |
| 985 | 1022 |
| 986 // Load through the GetProperty stub. | 1023 // Load through the GetProperty stub. |
| 987 compiler::Node* SlowFlagGetter(CodeStubAssembler* a, | 1024 Node* RegExpBuiltinsAssembler::SlowFlagGetter(Node* const context, |
| 988 compiler::Node* const context, | 1025 Node* const regexp, |
| 989 compiler::Node* const regexp, | 1026 JSRegExp::Flag flag) { |
| 990 JSRegExp::Flag flag) { | 1027 Factory* factory = isolate()->factory(); |
| 991 Factory* factory = a->isolate()->factory(); | |
| 992 | 1028 |
| 993 CLabel out(a); | 1029 Label out(this); |
| 994 CVariable var_result(a, MachineType::PointerRepresentation()); | 1030 Variable var_result(this, MachineType::PointerRepresentation()); |
| 995 | 1031 |
| 996 Node* name; | 1032 Node* name; |
| 997 | 1033 |
| 998 switch (flag) { | 1034 switch (flag) { |
| 999 case JSRegExp::kGlobal: | 1035 case JSRegExp::kGlobal: |
| 1000 name = a->HeapConstant(factory->global_string()); | 1036 name = HeapConstant(factory->global_string()); |
| 1001 break; | 1037 break; |
| 1002 case JSRegExp::kIgnoreCase: | 1038 case JSRegExp::kIgnoreCase: |
| 1003 name = a->HeapConstant(factory->ignoreCase_string()); | 1039 name = HeapConstant(factory->ignoreCase_string()); |
| 1004 break; | 1040 break; |
| 1005 case JSRegExp::kMultiline: | 1041 case JSRegExp::kMultiline: |
| 1006 name = a->HeapConstant(factory->multiline_string()); | 1042 name = HeapConstant(factory->multiline_string()); |
| 1007 break; | 1043 break; |
| 1008 case JSRegExp::kSticky: | 1044 case JSRegExp::kSticky: |
| 1009 name = a->HeapConstant(factory->sticky_string()); | 1045 name = HeapConstant(factory->sticky_string()); |
| 1010 break; | 1046 break; |
| 1011 case JSRegExp::kUnicode: | 1047 case JSRegExp::kUnicode: |
| 1012 name = a->HeapConstant(factory->unicode_string()); | 1048 name = HeapConstant(factory->unicode_string()); |
| 1013 break; | 1049 break; |
| 1014 default: | 1050 default: |
| 1015 UNREACHABLE(); | 1051 UNREACHABLE(); |
| 1016 } | 1052 } |
| 1017 | 1053 |
| 1018 Callable getproperty_callable = CodeFactory::GetProperty(a->isolate()); | 1054 Callable getproperty_callable = CodeFactory::GetProperty(isolate()); |
| 1019 Node* const value = a->CallStub(getproperty_callable, context, regexp, name); | 1055 Node* const value = CallStub(getproperty_callable, context, regexp, name); |
| 1020 | 1056 |
| 1021 CLabel if_true(a), if_false(a); | 1057 Label if_true(this), if_false(this); |
| 1022 a->BranchIfToBooleanIsTrue(value, &if_true, &if_false); | 1058 BranchIfToBooleanIsTrue(value, &if_true, &if_false); |
| 1023 | 1059 |
| 1024 a->Bind(&if_true); | 1060 Bind(&if_true); |
| 1025 { | 1061 { |
| 1026 var_result.Bind(a->IntPtrConstant(1)); | 1062 var_result.Bind(IntPtrConstant(1)); |
| 1027 a->Goto(&out); | 1063 Goto(&out); |
| 1028 } | 1064 } |
| 1029 | 1065 |
| 1030 a->Bind(&if_false); | 1066 Bind(&if_false); |
| 1031 { | 1067 { |
| 1032 var_result.Bind(a->IntPtrConstant(0)); | 1068 var_result.Bind(IntPtrConstant(0)); |
| 1033 a->Goto(&out); | 1069 Goto(&out); |
| 1034 } | 1070 } |
| 1035 | 1071 |
| 1036 a->Bind(&out); | 1072 Bind(&out); |
| 1037 return var_result.value(); | 1073 return var_result.value(); |
| 1038 } | 1074 } |
| 1039 | 1075 |
| 1040 compiler::Node* FlagGetter(CodeStubAssembler* a, compiler::Node* const context, | 1076 Node* RegExpBuiltinsAssembler::FlagGetter(Node* const context, |
| 1041 compiler::Node* const regexp, JSRegExp::Flag flag, | 1077 Node* const regexp, |
| 1042 bool is_fastpath) { | 1078 JSRegExp::Flag flag, |
| 1043 return is_fastpath ? FastFlagGetter(a, regexp, flag) | 1079 bool is_fastpath) { |
| 1044 : SlowFlagGetter(a, context, regexp, flag); | 1080 return is_fastpath ? FastFlagGetter(regexp, flag) |
| 1081 : SlowFlagGetter(context, regexp, flag); | |
| 1045 } | 1082 } |
| 1046 | 1083 |
| 1047 void Generate_FlagGetter(CodeStubAssembler* a, JSRegExp::Flag flag, | 1084 void RegExpBuiltinsAssembler::FlagGetter(JSRegExp::Flag flag, |
| 1048 v8::Isolate::UseCounterFeature counter, | 1085 v8::Isolate::UseCounterFeature counter, |
| 1049 const char* method_name) { | 1086 const char* method_name) { |
| 1050 Node* const receiver = a->Parameter(0); | 1087 Node* const receiver = Parameter(0); |
| 1051 Node* const context = a->Parameter(3); | 1088 Node* const context = Parameter(3); |
| 1052 | 1089 |
| 1053 Isolate* isolate = a->isolate(); | 1090 Isolate* isolate = this->isolate(); |
| 1054 | 1091 |
| 1055 // Check whether we have an unmodified regexp instance. | 1092 // Check whether we have an unmodified regexp instance. |
| 1056 CLabel if_isunmodifiedjsregexp(a), | 1093 Label if_isunmodifiedjsregexp(this), |
| 1057 if_isnotunmodifiedjsregexp(a, CLabel::kDeferred); | 1094 if_isnotunmodifiedjsregexp(this, Label::kDeferred); |
| 1058 | 1095 |
| 1059 a->GotoIf(a->TaggedIsSmi(receiver), &if_isnotunmodifiedjsregexp); | 1096 GotoIf(TaggedIsSmi(receiver), &if_isnotunmodifiedjsregexp); |
| 1060 | 1097 |
| 1061 Node* const receiver_map = a->LoadMap(receiver); | 1098 Node* const receiver_map = LoadMap(receiver); |
| 1062 Node* const instance_type = a->LoadMapInstanceType(receiver_map); | 1099 Node* const instance_type = LoadMapInstanceType(receiver_map); |
| 1063 | 1100 |
| 1064 a->Branch(a->Word32Equal(instance_type, a->Int32Constant(JS_REGEXP_TYPE)), | 1101 Branch(Word32Equal(instance_type, Int32Constant(JS_REGEXP_TYPE)), |
| 1065 &if_isunmodifiedjsregexp, &if_isnotunmodifiedjsregexp); | 1102 &if_isunmodifiedjsregexp, &if_isnotunmodifiedjsregexp); |
| 1066 | 1103 |
| 1067 a->Bind(&if_isunmodifiedjsregexp); | 1104 Bind(&if_isunmodifiedjsregexp); |
| 1068 { | 1105 { |
| 1069 // Refer to JSRegExp's flag property on the fast-path. | 1106 // Refer to JSRegExp's flag property on the fast-path. |
| 1070 Node* const is_flag_set = FastFlagGetter(a, receiver, flag); | 1107 Node* const is_flag_set = FastFlagGetter(receiver, flag); |
| 1071 a->Return(a->Select(is_flag_set, a->TrueConstant(), a->FalseConstant())); | 1108 Return(Select(is_flag_set, TrueConstant(), FalseConstant())); |
| 1072 } | 1109 } |
| 1073 | 1110 |
| 1074 a->Bind(&if_isnotunmodifiedjsregexp); | 1111 Bind(&if_isnotunmodifiedjsregexp); |
| 1075 { | 1112 { |
| 1076 Node* const native_context = a->LoadNativeContext(context); | 1113 Node* const native_context = LoadNativeContext(context); |
| 1077 Node* const regexp_fun = | 1114 Node* const regexp_fun = |
| 1078 a->LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX); | 1115 LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX); |
| 1079 Node* const initial_map = a->LoadObjectField( | 1116 Node* const initial_map = |
| 1080 regexp_fun, JSFunction::kPrototypeOrInitialMapOffset); | 1117 LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset); |
| 1081 Node* const initial_prototype = a->LoadMapPrototype(initial_map); | 1118 Node* const initial_prototype = LoadMapPrototype(initial_map); |
| 1082 | 1119 |
| 1083 CLabel if_isprototype(a), if_isnotprototype(a); | 1120 Label if_isprototype(this), if_isnotprototype(this); |
| 1084 a->Branch(a->WordEqual(receiver, initial_prototype), &if_isprototype, | 1121 Branch(WordEqual(receiver, initial_prototype), &if_isprototype, |
| 1085 &if_isnotprototype); | 1122 &if_isnotprototype); |
| 1086 | 1123 |
| 1087 a->Bind(&if_isprototype); | 1124 Bind(&if_isprototype); |
| 1088 { | 1125 { |
| 1089 Node* const counter_smi = a->SmiConstant(Smi::FromInt(counter)); | 1126 Node* const counter_smi = SmiConstant(Smi::FromInt(counter)); |
| 1090 a->CallRuntime(Runtime::kIncrementUseCounter, context, counter_smi); | 1127 CallRuntime(Runtime::kIncrementUseCounter, context, counter_smi); |
| 1091 a->Return(a->UndefinedConstant()); | 1128 Return(UndefinedConstant()); |
| 1092 } | 1129 } |
| 1093 | 1130 |
| 1094 a->Bind(&if_isnotprototype); | 1131 Bind(&if_isnotprototype); |
| 1095 { | 1132 { |
| 1096 Node* const message_id = | 1133 Node* const message_id = |
| 1097 a->SmiConstant(Smi::FromInt(MessageTemplate::kRegExpNonRegExp)); | 1134 SmiConstant(Smi::FromInt(MessageTemplate::kRegExpNonRegExp)); |
| 1098 Node* const method_name_str = a->HeapConstant( | 1135 Node* const method_name_str = HeapConstant( |
| 1099 isolate->factory()->NewStringFromAsciiChecked(method_name)); | 1136 isolate->factory()->NewStringFromAsciiChecked(method_name)); |
| 1100 a->CallRuntime(Runtime::kThrowTypeError, context, message_id, | 1137 CallRuntime(Runtime::kThrowTypeError, context, message_id, |
| 1101 method_name_str); | 1138 method_name_str); |
| 1102 a->Return(a->UndefinedConstant()); // Never reached. | 1139 Return(UndefinedConstant()); // Never reached. |
| 1103 } | 1140 } |
| 1104 } | 1141 } |
| 1105 } | 1142 } |
| 1106 | 1143 |
| 1107 } // namespace | |
| 1108 | |
| 1109 // ES6 21.2.5.4. | 1144 // ES6 21.2.5.4. |
| 1110 TF_BUILTIN(RegExpPrototypeGlobalGetter, RegExpBuiltinsAssembler) { | 1145 TF_BUILTIN(RegExpPrototypeGlobalGetter, RegExpBuiltinsAssembler) { |
| 1111 Generate_FlagGetter(this, JSRegExp::kGlobal, | 1146 FlagGetter(JSRegExp::kGlobal, v8::Isolate::kRegExpPrototypeOldFlagGetter, |
| 1112 v8::Isolate::kRegExpPrototypeOldFlagGetter, | 1147 "RegExp.prototype.global"); |
| 1113 "RegExp.prototype.global"); | |
| 1114 } | 1148 } |
| 1115 | 1149 |
| 1116 // ES6 21.2.5.5. | 1150 // ES6 21.2.5.5. |
| 1117 TF_BUILTIN(RegExpPrototypeIgnoreCaseGetter, RegExpBuiltinsAssembler) { | 1151 TF_BUILTIN(RegExpPrototypeIgnoreCaseGetter, RegExpBuiltinsAssembler) { |
| 1118 Generate_FlagGetter(this, JSRegExp::kIgnoreCase, | 1152 FlagGetter(JSRegExp::kIgnoreCase, v8::Isolate::kRegExpPrototypeOldFlagGetter, |
| 1119 v8::Isolate::kRegExpPrototypeOldFlagGetter, | 1153 "RegExp.prototype.ignoreCase"); |
| 1120 "RegExp.prototype.ignoreCase"); | |
| 1121 } | 1154 } |
| 1122 | 1155 |
| 1123 // ES6 21.2.5.7. | 1156 // ES6 21.2.5.7. |
| 1124 TF_BUILTIN(RegExpPrototypeMultilineGetter, RegExpBuiltinsAssembler) { | 1157 TF_BUILTIN(RegExpPrototypeMultilineGetter, RegExpBuiltinsAssembler) { |
| 1125 Generate_FlagGetter(this, JSRegExp::kMultiline, | 1158 FlagGetter(JSRegExp::kMultiline, v8::Isolate::kRegExpPrototypeOldFlagGetter, |
| 1126 v8::Isolate::kRegExpPrototypeOldFlagGetter, | 1159 "RegExp.prototype.multiline"); |
| 1127 "RegExp.prototype.multiline"); | |
| 1128 } | 1160 } |
| 1129 | 1161 |
| 1130 // ES6 21.2.5.12. | 1162 // ES6 21.2.5.12. |
| 1131 TF_BUILTIN(RegExpPrototypeStickyGetter, RegExpBuiltinsAssembler) { | 1163 TF_BUILTIN(RegExpPrototypeStickyGetter, RegExpBuiltinsAssembler) { |
| 1132 Generate_FlagGetter(this, JSRegExp::kSticky, | 1164 FlagGetter(JSRegExp::kSticky, v8::Isolate::kRegExpPrototypeStickyGetter, |
| 1133 v8::Isolate::kRegExpPrototypeStickyGetter, | 1165 "RegExp.prototype.sticky"); |
| 1134 "RegExp.prototype.sticky"); | |
| 1135 } | 1166 } |
| 1136 | 1167 |
| 1137 // ES6 21.2.5.15. | 1168 // ES6 21.2.5.15. |
| 1138 TF_BUILTIN(RegExpPrototypeUnicodeGetter, RegExpBuiltinsAssembler) { | 1169 TF_BUILTIN(RegExpPrototypeUnicodeGetter, RegExpBuiltinsAssembler) { |
| 1139 Generate_FlagGetter(this, JSRegExp::kUnicode, | 1170 FlagGetter(JSRegExp::kUnicode, v8::Isolate::kRegExpPrototypeUnicodeGetter, |
| 1140 v8::Isolate::kRegExpPrototypeUnicodeGetter, | 1171 "RegExp.prototype.unicode"); |
| 1141 "RegExp.prototype.unicode"); | |
| 1142 } | 1172 } |
| 1143 | 1173 |
| 1144 // The properties $1..$9 are the first nine capturing substrings of the last | 1174 // The properties $1..$9 are the first nine capturing substrings of the last |
| 1145 // successful match, or ''. The function RegExpMakeCaptureGetter will be | 1175 // successful match, or ''. The function RegExpMakeCaptureGetter will be |
| 1146 // called with indices from 1 to 9. | 1176 // called with indices from 1 to 9. |
| 1147 #define DEFINE_CAPTURE_GETTER(i) \ | 1177 #define DEFINE_CAPTURE_GETTER(i) \ |
| 1148 BUILTIN(RegExpCapture##i##Getter) { \ | 1178 BUILTIN(RegExpCapture##i##Getter) { \ |
| 1149 HandleScope scope(isolate); \ | 1179 HandleScope scope(isolate); \ |
| 1150 return *RegExpUtils::GenericCaptureGetter( \ | 1180 return *RegExpUtils::GenericCaptureGetter( \ |
| 1151 isolate, isolate->regexp_last_match_info(), i); \ | 1181 isolate, isolate->regexp_last_match_info(), i); \ |
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1217 | 1247 |
| 1218 BUILTIN(RegExpRightContextGetter) { | 1248 BUILTIN(RegExpRightContextGetter) { |
| 1219 HandleScope scope(isolate); | 1249 HandleScope scope(isolate); |
| 1220 Handle<RegExpMatchInfo> match_info = isolate->regexp_last_match_info(); | 1250 Handle<RegExpMatchInfo> match_info = isolate->regexp_last_match_info(); |
| 1221 const int start_index = match_info->Capture(1); | 1251 const int start_index = match_info->Capture(1); |
| 1222 Handle<String> last_subject(match_info->LastSubject()); | 1252 Handle<String> last_subject(match_info->LastSubject()); |
| 1223 const int len = last_subject->length(); | 1253 const int len = last_subject->length(); |
| 1224 return *isolate->factory()->NewSubString(last_subject, start_index, len); | 1254 return *isolate->factory()->NewSubString(last_subject, start_index, len); |
| 1225 } | 1255 } |
| 1226 | 1256 |
| 1227 namespace { | 1257 // ES#sec-regexpexec Runtime Semantics: RegExpExec ( R, S ) |
| 1258 Node* RegExpBuiltinsAssembler::RegExpExec(Node* context, Node* regexp, | |
| 1259 Node* string) { | |
| 1260 Isolate* isolate = this->isolate(); | |
| 1228 | 1261 |
| 1229 // ES#sec-regexpexec Runtime Semantics: RegExpExec ( R, S ) | 1262 Node* const null = NullConstant(); |
| 1230 Node* RegExpExec(CodeStubAssembler* a, Node* context, Node* recv, | |
| 1231 Node* string) { | |
| 1232 Isolate* isolate = a->isolate(); | |
| 1233 | 1263 |
| 1234 Node* const null = a->NullConstant(); | 1264 Variable var_result(this, MachineRepresentation::kTagged); |
| 1265 Label out(this), if_isfastpath(this), if_isslowpath(this); | |
| 1235 | 1266 |
| 1236 CVariable var_result(a, MachineRepresentation::kTagged); | 1267 Node* const map = LoadMap(regexp); |
| 1237 CLabel out(a), if_isfastpath(a), if_isslowpath(a); | 1268 BranchIfFastRegExp(context, map, &if_isfastpath, &if_isslowpath); |
| 1238 | 1269 |
| 1239 Node* const map = a->LoadMap(recv); | 1270 Bind(&if_isfastpath); |
| 1240 BranchIfFastPath(a, context, map, &if_isfastpath, &if_isslowpath); | |
| 1241 | |
| 1242 a->Bind(&if_isfastpath); | |
| 1243 { | 1271 { |
| 1244 Node* const result = | 1272 Node* const result = RegExpPrototypeExecBody(context, regexp, string, true); |
| 1245 RegExpPrototypeExecBody(a, context, recv, string, true); | |
| 1246 var_result.Bind(result); | 1273 var_result.Bind(result); |
| 1247 a->Goto(&out); | 1274 Goto(&out); |
| 1248 } | 1275 } |
| 1249 | 1276 |
| 1250 a->Bind(&if_isslowpath); | 1277 Bind(&if_isslowpath); |
| 1251 { | 1278 { |
| 1252 // Take the slow path of fetching the exec property, calling it, and | 1279 // Take the slow path of fetching the exec property, calling it, and |
| 1253 // verifying its return value. | 1280 // verifying its return value. |
| 1254 | 1281 |
| 1255 // Get the exec property. | 1282 // Get the exec property. |
| 1256 Node* const name = a->HeapConstant(isolate->factory()->exec_string()); | 1283 Node* const name = HeapConstant(isolate->factory()->exec_string()); |
| 1257 Callable getproperty_callable = CodeFactory::GetProperty(a->isolate()); | 1284 Callable getproperty_callable = CodeFactory::GetProperty(isolate); |
| 1258 Node* const exec = a->CallStub(getproperty_callable, context, recv, name); | 1285 Node* const exec = CallStub(getproperty_callable, context, regexp, name); |
| 1259 | 1286 |
| 1260 // Is {exec} callable? | 1287 // Is {exec} callable? |
| 1261 CLabel if_iscallable(a), if_isnotcallable(a); | 1288 Label if_iscallable(this), if_isnotcallable(this); |
| 1262 | 1289 |
| 1263 a->GotoIf(a->TaggedIsSmi(exec), &if_isnotcallable); | 1290 GotoIf(TaggedIsSmi(exec), &if_isnotcallable); |
| 1264 | 1291 |
| 1265 Node* const exec_map = a->LoadMap(exec); | 1292 Node* const exec_map = LoadMap(exec); |
| 1266 a->Branch(a->IsCallableMap(exec_map), &if_iscallable, &if_isnotcallable); | 1293 Branch(IsCallableMap(exec_map), &if_iscallable, &if_isnotcallable); |
| 1267 | 1294 |
| 1268 a->Bind(&if_iscallable); | 1295 Bind(&if_iscallable); |
| 1269 { | 1296 { |
| 1270 Callable call_callable = CodeFactory::Call(isolate); | 1297 Callable call_callable = CodeFactory::Call(isolate); |
| 1271 Node* const result = | 1298 Node* const result = CallJS(call_callable, context, exec, regexp, string); |
| 1272 a->CallJS(call_callable, context, exec, recv, string); | |
| 1273 | 1299 |
| 1274 var_result.Bind(result); | 1300 var_result.Bind(result); |
| 1275 a->GotoIf(a->WordEqual(result, null), &out); | 1301 GotoIf(WordEqual(result, null), &out); |
| 1276 | 1302 |
| 1277 ThrowIfNotJSReceiver(a, isolate, context, result, | 1303 ThrowIfNotJSReceiver(context, result, |
| 1278 MessageTemplate::kInvalidRegExpExecResult, "unused"); | 1304 MessageTemplate::kInvalidRegExpExecResult, "unused"); |
| 1279 | 1305 |
| 1280 a->Goto(&out); | 1306 Goto(&out); |
| 1281 } | 1307 } |
| 1282 | 1308 |
| 1283 a->Bind(&if_isnotcallable); | 1309 Bind(&if_isnotcallable); |
| 1284 { | 1310 { |
| 1285 a->ThrowIfNotInstanceType(context, recv, JS_REGEXP_TYPE, | 1311 ThrowIfNotInstanceType(context, regexp, JS_REGEXP_TYPE, |
| 1286 "RegExp.prototype.exec"); | 1312 "RegExp.prototype.exec"); |
| 1287 | 1313 |
| 1288 Node* const result = | 1314 Node* const result = |
| 1289 RegExpPrototypeExecBody(a, context, recv, string, false); | 1315 RegExpPrototypeExecBody(context, regexp, string, false); |
| 1290 var_result.Bind(result); | 1316 var_result.Bind(result); |
| 1291 a->Goto(&out); | 1317 Goto(&out); |
| 1292 } | 1318 } |
| 1293 } | 1319 } |
| 1294 | 1320 |
| 1295 a->Bind(&out); | 1321 Bind(&out); |
| 1296 return var_result.value(); | 1322 return var_result.value(); |
| 1297 } | 1323 } |
| 1298 | 1324 |
| 1299 } // namespace | |
| 1300 | |
| 1301 // ES#sec-regexp.prototype.test | 1325 // ES#sec-regexp.prototype.test |
| 1302 // RegExp.prototype.test ( S ) | 1326 // RegExp.prototype.test ( S ) |
| 1303 TF_BUILTIN(RegExpPrototypeTest, RegExpBuiltinsAssembler) { | 1327 TF_BUILTIN(RegExpPrototypeTest, RegExpBuiltinsAssembler) { |
| 1304 Isolate* const isolate = this->isolate(); | |
| 1305 | |
| 1306 Node* const maybe_receiver = Parameter(0); | 1328 Node* const maybe_receiver = Parameter(0); |
| 1307 Node* const maybe_string = Parameter(1); | 1329 Node* const maybe_string = Parameter(1); |
| 1308 Node* const context = Parameter(4); | 1330 Node* const context = Parameter(4); |
| 1309 | 1331 |
| 1310 // Ensure {maybe_receiver} is a JSReceiver. | 1332 // Ensure {maybe_receiver} is a JSReceiver. |
| 1311 Node* const map = ThrowIfNotJSReceiver( | 1333 Node* const map = ThrowIfNotJSReceiver( |
| 1312 this, isolate, context, maybe_receiver, | 1334 context, maybe_receiver, MessageTemplate::kIncompatibleMethodReceiver, |
| 1313 MessageTemplate::kIncompatibleMethodReceiver, "RegExp.prototype.test"); | 1335 "RegExp.prototype.test"); |
| 1314 Node* const receiver = maybe_receiver; | 1336 Node* const receiver = maybe_receiver; |
| 1315 | 1337 |
| 1316 // Convert {maybe_string} to a String. | 1338 // Convert {maybe_string} to a String. |
| 1317 Node* const string = ToString(context, maybe_string); | 1339 Node* const string = ToString(context, maybe_string); |
| 1318 | 1340 |
| 1319 CLabel fast_path(this), slow_path(this); | 1341 Label fast_path(this), slow_path(this); |
| 1320 BranchIfFastPath(this, context, map, &fast_path, &slow_path); | 1342 BranchIfFastRegExp(context, map, &fast_path, &slow_path); |
| 1321 | 1343 |
| 1322 Bind(&fast_path); | 1344 Bind(&fast_path); |
| 1323 { | 1345 { |
| 1324 CLabel if_didnotmatch(this); | 1346 Label if_didnotmatch(this); |
| 1325 RegExpPrototypeExecBodyWithoutResult(this, context, receiver, string, | 1347 RegExpPrototypeExecBodyWithoutResult(context, receiver, string, |
| 1326 &if_didnotmatch, true); | 1348 &if_didnotmatch, true); |
| 1327 Return(TrueConstant()); | 1349 Return(TrueConstant()); |
| 1328 | 1350 |
| 1329 Bind(&if_didnotmatch); | 1351 Bind(&if_didnotmatch); |
| 1330 Return(FalseConstant()); | 1352 Return(FalseConstant()); |
| 1331 } | 1353 } |
| 1332 | 1354 |
| 1333 Bind(&slow_path); | 1355 Bind(&slow_path); |
| 1334 { | 1356 { |
| 1335 // Call exec. | 1357 // Call exec. |
| 1336 Node* const match_indices = RegExpExec(this, context, receiver, string); | 1358 Node* const match_indices = RegExpExec(context, receiver, string); |
| 1337 | 1359 |
| 1338 // Return true iff exec matched successfully. | 1360 // Return true iff exec matched successfully. |
| 1339 Node* const result = Select(WordEqual(match_indices, NullConstant()), | 1361 Node* const result = Select(WordEqual(match_indices, NullConstant()), |
| 1340 FalseConstant(), TrueConstant()); | 1362 FalseConstant(), TrueConstant()); |
| 1341 Return(result); | 1363 Return(result); |
| 1342 } | 1364 } |
| 1343 } | 1365 } |
| 1344 | 1366 |
| 1367 Node* RegExpBuiltinsAssembler::AdvanceStringIndex(Node* const string, | |
| 1368 Node* const index, | |
| 1369 Node* const is_unicode) { | |
| 1370 Variable var_result(this, MachineRepresentation::kTagged); | |
| 1371 | |
| 1372 // Default to last_index + 1. | |
| 1373 Node* const index_plus_one = SmiAdd(index, SmiConstant(1)); | |
| 1374 var_result.Bind(index_plus_one); | |
| 1375 | |
| 1376 Label if_isunicode(this), out(this); | |
| 1377 Branch(is_unicode, &if_isunicode, &out); | |
| 1378 | |
| 1379 Bind(&if_isunicode); | |
| 1380 { | |
| 1381 Node* const string_length = LoadStringLength(string); | |
| 1382 GotoUnless(SmiLessThan(index_plus_one, string_length), &out); | |
| 1383 | |
| 1384 Node* const lead = StringCharCodeAt(string, index); | |
| 1385 GotoUnless(Word32Equal(Word32And(lead, Int32Constant(0xFC00)), | |
| 1386 Int32Constant(0xD800)), | |
| 1387 &out); | |
| 1388 | |
| 1389 Node* const trail = StringCharCodeAt(string, index_plus_one); | |
| 1390 GotoUnless(Word32Equal(Word32And(trail, Int32Constant(0xFC00)), | |
| 1391 Int32Constant(0xDC00)), | |
| 1392 &out); | |
| 1393 | |
| 1394 // At a surrogate pair, return index + 2. | |
| 1395 Node* const index_plus_two = SmiAdd(index, SmiConstant(2)); | |
| 1396 var_result.Bind(index_plus_two); | |
| 1397 | |
| 1398 Goto(&out); | |
| 1399 } | |
| 1400 | |
| 1401 Bind(&out); | |
| 1402 return var_result.value(); | |
| 1403 } | |
| 1404 | |
| 1345 namespace { | 1405 namespace { |
| 1346 | 1406 |
| 1347 Node* AdvanceStringIndex(CodeStubAssembler* a, Node* const string, | |
| 1348 Node* const index, Node* const is_unicode) { | |
| 1349 CVariable var_result(a, MachineRepresentation::kTagged); | |
| 1350 | |
| 1351 // Default to last_index + 1. | |
| 1352 Node* const index_plus_one = a->SmiAdd(index, a->SmiConstant(1)); | |
| 1353 var_result.Bind(index_plus_one); | |
| 1354 | |
| 1355 CLabel if_isunicode(a), out(a); | |
| 1356 a->Branch(is_unicode, &if_isunicode, &out); | |
| 1357 | |
| 1358 a->Bind(&if_isunicode); | |
| 1359 { | |
| 1360 Node* const string_length = a->LoadStringLength(string); | |
| 1361 a->GotoUnless(a->SmiLessThan(index_plus_one, string_length), &out); | |
| 1362 | |
| 1363 Node* const lead = a->StringCharCodeAt(string, index); | |
| 1364 a->GotoUnless(a->Word32Equal(a->Word32And(lead, a->Int32Constant(0xFC00)), | |
| 1365 a->Int32Constant(0xD800)), | |
| 1366 &out); | |
| 1367 | |
| 1368 Node* const trail = a->StringCharCodeAt(string, index_plus_one); | |
| 1369 a->GotoUnless(a->Word32Equal(a->Word32And(trail, a->Int32Constant(0xFC00)), | |
| 1370 a->Int32Constant(0xDC00)), | |
| 1371 &out); | |
| 1372 | |
| 1373 // At a surrogate pair, return index + 2. | |
| 1374 Node* const index_plus_two = a->SmiAdd(index, a->SmiConstant(2)); | |
| 1375 var_result.Bind(index_plus_two); | |
| 1376 | |
| 1377 a->Goto(&out); | |
| 1378 } | |
| 1379 | |
| 1380 a->Bind(&out); | |
| 1381 return var_result.value(); | |
| 1382 } | |
| 1383 | |
| 1384 // Utility class implementing a growable fixed array through CSA. | 1407 // Utility class implementing a growable fixed array through CSA. |
| 1385 class GrowableFixedArray { | 1408 class GrowableFixedArray { |
| 1409 typedef CodeStubAssembler::Label Label; | |
| 1410 typedef CodeStubAssembler::Variable Variable; | |
| 1411 | |
| 1386 public: | 1412 public: |
| 1387 explicit GrowableFixedArray(CodeStubAssembler* a) | 1413 explicit GrowableFixedArray(CodeStubAssembler* a) |
| 1388 : assembler_(a), | 1414 : assembler_(a), |
| 1389 var_array_(a, MachineRepresentation::kTagged), | 1415 var_array_(a, MachineRepresentation::kTagged), |
| 1390 var_length_(a, MachineType::PointerRepresentation()), | 1416 var_length_(a, MachineType::PointerRepresentation()), |
| 1391 var_capacity_(a, MachineType::PointerRepresentation()) { | 1417 var_capacity_(a, MachineType::PointerRepresentation()) { |
| 1392 Initialize(); | 1418 Initialize(); |
| 1393 } | 1419 } |
| 1394 | 1420 |
| 1395 Node* length() const { return var_length_.value(); } | 1421 Node* length() const { return var_length_.value(); } |
| 1396 | 1422 |
| 1397 CVariable* var_array() { return &var_array_; } | 1423 Variable* var_array() { return &var_array_; } |
| 1398 CVariable* var_length() { return &var_length_; } | 1424 Variable* var_length() { return &var_length_; } |
| 1399 CVariable* var_capacity() { return &var_capacity_; } | 1425 Variable* var_capacity() { return &var_capacity_; } |
| 1400 | 1426 |
| 1401 void Push(Node* const value) { | 1427 void Push(Node* const value) { |
| 1402 CodeStubAssembler* a = assembler_; | 1428 CodeStubAssembler* a = assembler_; |
| 1403 | 1429 |
| 1404 const WriteBarrierMode barrier_mode = UPDATE_WRITE_BARRIER; | 1430 const WriteBarrierMode barrier_mode = UPDATE_WRITE_BARRIER; |
| 1405 const ParameterMode mode = CodeStubAssembler::INTPTR_PARAMETERS; | 1431 const ParameterMode mode = CodeStubAssembler::INTPTR_PARAMETERS; |
| 1406 | 1432 |
| 1407 Node* const length = var_length_.value(); | 1433 Node* const length = var_length_.value(); |
| 1408 Node* const capacity = var_capacity_.value(); | 1434 Node* const capacity = var_capacity_.value(); |
| 1409 | 1435 |
| 1410 CLabel grow(a), store(a); | 1436 Label grow(a), store(a); |
| 1411 a->Branch(a->IntPtrEqual(capacity, length), &grow, &store); | 1437 a->Branch(a->IntPtrEqual(capacity, length), &grow, &store); |
| 1412 | 1438 |
| 1413 a->Bind(&grow); | 1439 a->Bind(&grow); |
| 1414 { | 1440 { |
| 1415 Node* const new_capacity = NewCapacity(a, capacity); | 1441 Node* const new_capacity = NewCapacity(a, capacity); |
| 1416 Node* const new_array = GrowFixedArray(capacity, new_capacity, mode); | 1442 Node* const new_array = GrowFixedArray(capacity, new_capacity, mode); |
| 1417 | 1443 |
| 1418 var_capacity_.Bind(new_capacity); | 1444 var_capacity_.Bind(new_capacity); |
| 1419 var_array_.Bind(new_array); | 1445 var_array_.Bind(new_array); |
| 1420 a->Goto(&store); | 1446 a->Goto(&store); |
| (...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1497 Node* const to_array = a->AllocateFixedArray(kind, new_capacity, mode); | 1523 Node* const to_array = a->AllocateFixedArray(kind, new_capacity, mode); |
| 1498 a->CopyFixedArrayElements(kind, from_array, kind, to_array, | 1524 a->CopyFixedArrayElements(kind, from_array, kind, to_array, |
| 1499 current_capacity, new_capacity, barrier_mode, | 1525 current_capacity, new_capacity, barrier_mode, |
| 1500 mode); | 1526 mode); |
| 1501 | 1527 |
| 1502 return to_array; | 1528 return to_array; |
| 1503 } | 1529 } |
| 1504 | 1530 |
| 1505 private: | 1531 private: |
| 1506 CodeStubAssembler* const assembler_; | 1532 CodeStubAssembler* const assembler_; |
| 1507 CVariable var_array_; | 1533 Variable var_array_; |
| 1508 CVariable var_length_; | 1534 Variable var_length_; |
| 1509 CVariable var_capacity_; | 1535 Variable var_capacity_; |
| 1510 }; | 1536 }; |
| 1511 | 1537 |
| 1512 void RegExpPrototypeMatchBody(CodeStubAssembler* a, Node* const receiver, | 1538 } // namespace |
| 1513 Node* const string, Node* const context, | |
| 1514 const bool is_fastpath) { | |
| 1515 Isolate* const isolate = a->isolate(); | |
| 1516 | 1539 |
| 1517 Node* const null = a->NullConstant(); | 1540 void RegExpBuiltinsAssembler::RegExpPrototypeMatchBody(Node* const context, |
| 1518 Node* const int_zero = a->IntPtrConstant(0); | 1541 Node* const regexp, |
| 1519 Node* const smi_zero = a->SmiConstant(Smi::kZero); | 1542 Node* const string, |
| 1543 const bool is_fastpath) { | |
| 1544 Isolate* const isolate = this->isolate(); | |
| 1520 | 1545 |
| 1521 Node* const regexp = receiver; | 1546 Node* const null = NullConstant(); |
| 1547 Node* const int_zero = IntPtrConstant(0); | |
| 1548 Node* const smi_zero = SmiConstant(Smi::kZero); | |
| 1549 | |
| 1522 Node* const is_global = | 1550 Node* const is_global = |
| 1523 FlagGetter(a, context, regexp, JSRegExp::kGlobal, is_fastpath); | 1551 FlagGetter(context, regexp, JSRegExp::kGlobal, is_fastpath); |
| 1524 | 1552 |
| 1525 CLabel if_isglobal(a), if_isnotglobal(a); | 1553 Label if_isglobal(this), if_isnotglobal(this); |
| 1526 a->Branch(is_global, &if_isglobal, &if_isnotglobal); | 1554 Branch(is_global, &if_isglobal, &if_isnotglobal); |
| 1527 | 1555 |
| 1528 a->Bind(&if_isnotglobal); | 1556 Bind(&if_isnotglobal); |
| 1529 { | 1557 { |
| 1530 Node* const result = | 1558 Node* const result = |
| 1531 is_fastpath ? RegExpPrototypeExecBody(a, context, regexp, string, true) | 1559 is_fastpath ? RegExpPrototypeExecBody(context, regexp, string, true) |
| 1532 : RegExpExec(a, context, regexp, string); | 1560 : RegExpExec(context, regexp, string); |
| 1533 a->Return(result); | 1561 Return(result); |
| 1534 } | 1562 } |
| 1535 | 1563 |
| 1536 a->Bind(&if_isglobal); | 1564 Bind(&if_isglobal); |
| 1537 { | 1565 { |
| 1538 Node* const is_unicode = | 1566 Node* const is_unicode = |
| 1539 FlagGetter(a, context, regexp, JSRegExp::kUnicode, is_fastpath); | 1567 FlagGetter(context, regexp, JSRegExp::kUnicode, is_fastpath); |
| 1540 | 1568 |
| 1541 StoreLastIndex(a, context, regexp, smi_zero, is_fastpath); | 1569 StoreLastIndex(context, regexp, smi_zero, is_fastpath); |
| 1542 | 1570 |
| 1543 // Allocate an array to store the resulting match strings. | 1571 // Allocate an array to store the resulting match strings. |
| 1544 | 1572 |
| 1545 GrowableFixedArray array(a); | 1573 GrowableFixedArray array(this); |
| 1546 | 1574 |
| 1547 // Loop preparations. Within the loop, collect results from RegExpExec | 1575 // Loop preparations. Within the loop, collect results from RegExpExec |
| 1548 // and store match strings in the array. | 1576 // and store match strings in the array. |
| 1549 | 1577 |
| 1550 CVariable* vars[] = {array.var_array(), array.var_length(), | 1578 Variable* vars[] = {array.var_array(), array.var_length(), |
| 1551 array.var_capacity()}; | 1579 array.var_capacity()}; |
| 1552 CLabel loop(a, 3, vars), out(a); | 1580 Label loop(this, 3, vars), out(this); |
| 1553 a->Goto(&loop); | 1581 Goto(&loop); |
| 1554 | 1582 |
| 1555 a->Bind(&loop); | 1583 Bind(&loop); |
| 1556 { | 1584 { |
| 1557 CVariable var_match(a, MachineRepresentation::kTagged); | 1585 Variable var_match(this, MachineRepresentation::kTagged); |
| 1558 | 1586 |
| 1559 CLabel if_didmatch(a), if_didnotmatch(a); | 1587 Label if_didmatch(this), if_didnotmatch(this); |
| 1560 if (is_fastpath) { | 1588 if (is_fastpath) { |
| 1561 // On the fast path, grab the matching string from the raw match index | 1589 // On the fast path, grab the matching string from the raw match index |
| 1562 // array. | 1590 // array. |
| 1563 Node* const match_indices = RegExpPrototypeExecBodyWithoutResult( | 1591 Node* const match_indices = RegExpPrototypeExecBodyWithoutResult( |
| 1564 a, context, regexp, string, &if_didnotmatch, true); | 1592 context, regexp, string, &if_didnotmatch, true); |
| 1565 | 1593 |
| 1566 Node* const match_from = a->LoadFixedArrayElement( | 1594 Node* const match_from = LoadFixedArrayElement( |
| 1567 match_indices, RegExpMatchInfo::kFirstCaptureIndex); | 1595 match_indices, RegExpMatchInfo::kFirstCaptureIndex); |
| 1568 Node* const match_to = a->LoadFixedArrayElement( | 1596 Node* const match_to = LoadFixedArrayElement( |
| 1569 match_indices, RegExpMatchInfo::kFirstCaptureIndex + 1); | 1597 match_indices, RegExpMatchInfo::kFirstCaptureIndex + 1); |
| 1570 | 1598 |
| 1571 Node* match = a->SubString(context, string, match_from, match_to); | 1599 Node* match = SubString(context, string, match_from, match_to); |
| 1572 var_match.Bind(match); | 1600 var_match.Bind(match); |
| 1573 | 1601 |
| 1574 a->Goto(&if_didmatch); | 1602 Goto(&if_didmatch); |
| 1575 } else { | 1603 } else { |
| 1576 DCHECK(!is_fastpath); | 1604 DCHECK(!is_fastpath); |
| 1577 Node* const result = RegExpExec(a, context, regexp, string); | 1605 Node* const result = RegExpExec(context, regexp, string); |
| 1578 | 1606 |
| 1579 CLabel load_match(a); | 1607 Label load_match(this); |
| 1580 a->Branch(a->WordEqual(result, null), &if_didnotmatch, &load_match); | 1608 Branch(WordEqual(result, null), &if_didnotmatch, &load_match); |
| 1581 | 1609 |
| 1582 a->Bind(&load_match); | 1610 Bind(&load_match); |
| 1583 { | 1611 { |
| 1584 CLabel fast_result(a), slow_result(a); | 1612 Label fast_result(this), slow_result(this); |
| 1585 BranchIfFastRegExpResult(a, context, a->LoadMap(result), &fast_result, | 1613 BranchIfFastRegExpResult(context, LoadMap(result), &fast_result, |
| 1586 &slow_result); | 1614 &slow_result); |
| 1587 | 1615 |
| 1588 a->Bind(&fast_result); | 1616 Bind(&fast_result); |
| 1589 { | 1617 { |
| 1590 Node* const result_fixed_array = a->LoadElements(result); | 1618 Node* const result_fixed_array = LoadElements(result); |
| 1591 Node* const match = a->LoadFixedArrayElement(result_fixed_array, 0); | 1619 Node* const match = LoadFixedArrayElement(result_fixed_array, 0); |
| 1592 | 1620 |
| 1593 // The match is guaranteed to be a string on the fast path. | 1621 // The match is guaranteed to be a string on the fast path. |
| 1594 CSA_ASSERT(a, a->IsStringInstanceType(a->LoadInstanceType(match))); | 1622 CSA_ASSERT(this, IsStringInstanceType(LoadInstanceType(match))); |
| 1595 | 1623 |
| 1596 var_match.Bind(match); | 1624 var_match.Bind(match); |
| 1597 a->Goto(&if_didmatch); | 1625 Goto(&if_didmatch); |
| 1598 } | 1626 } |
| 1599 | 1627 |
| 1600 a->Bind(&slow_result); | 1628 Bind(&slow_result); |
| 1601 { | 1629 { |
| 1602 // TODO(ishell): Use GetElement stub once it's available. | 1630 // TODO(ishell): Use GetElement stub once it's available. |
| 1603 Node* const name = smi_zero; | 1631 Node* const name = smi_zero; |
| 1604 Callable getproperty_callable = CodeFactory::GetProperty(isolate); | 1632 Callable getproperty_callable = CodeFactory::GetProperty(isolate); |
| 1605 Node* const match = | 1633 Node* const match = |
| 1606 a->CallStub(getproperty_callable, context, result, name); | 1634 CallStub(getproperty_callable, context, result, name); |
| 1607 | 1635 |
| 1608 var_match.Bind(a->ToString(context, match)); | 1636 var_match.Bind(ToString(context, match)); |
| 1609 a->Goto(&if_didmatch); | 1637 Goto(&if_didmatch); |
| 1610 } | 1638 } |
| 1611 } | 1639 } |
| 1612 } | 1640 } |
| 1613 | 1641 |
| 1614 a->Bind(&if_didnotmatch); | 1642 Bind(&if_didnotmatch); |
| 1615 { | 1643 { |
| 1616 // Return null if there were no matches, otherwise just exit the loop. | 1644 // Return null if there were no matches, otherwise just exit the loop. |
| 1617 a->GotoUnless(a->IntPtrEqual(array.length(), int_zero), &out); | 1645 GotoUnless(IntPtrEqual(array.length(), int_zero), &out); |
| 1618 a->Return(null); | 1646 Return(null); |
| 1619 } | 1647 } |
| 1620 | 1648 |
| 1621 a->Bind(&if_didmatch); | 1649 Bind(&if_didmatch); |
| 1622 { | 1650 { |
| 1623 Node* match = var_match.value(); | 1651 Node* match = var_match.value(); |
| 1624 | 1652 |
| 1625 // Store the match, growing the fixed array if needed. | 1653 // Store the match, growing the fixed array if needed. |
| 1626 | 1654 |
| 1627 array.Push(match); | 1655 array.Push(match); |
| 1628 | 1656 |
| 1629 // Advance last index if the match is the empty string. | 1657 // Advance last index if the match is the empty string. |
| 1630 | 1658 |
| 1631 Node* const match_length = a->LoadStringLength(match); | 1659 Node* const match_length = LoadStringLength(match); |
| 1632 a->GotoUnless(a->SmiEqual(match_length, smi_zero), &loop); | 1660 GotoUnless(SmiEqual(match_length, smi_zero), &loop); |
| 1633 | 1661 |
| 1634 Node* last_index = LoadLastIndex(a, context, regexp, is_fastpath); | 1662 Node* last_index = LoadLastIndex(context, regexp, is_fastpath); |
| 1635 | 1663 |
| 1636 Callable tolength_callable = CodeFactory::ToLength(isolate); | 1664 Callable tolength_callable = CodeFactory::ToLength(isolate); |
| 1637 last_index = a->CallStub(tolength_callable, context, last_index); | 1665 last_index = CallStub(tolength_callable, context, last_index); |
| 1638 | 1666 |
| 1639 Node* const new_last_index = | 1667 Node* const new_last_index = |
| 1640 AdvanceStringIndex(a, string, last_index, is_unicode); | 1668 AdvanceStringIndex(string, last_index, is_unicode); |
| 1641 | 1669 |
| 1642 StoreLastIndex(a, context, regexp, new_last_index, is_fastpath); | 1670 StoreLastIndex(context, regexp, new_last_index, is_fastpath); |
| 1643 | 1671 |
| 1644 a->Goto(&loop); | 1672 Goto(&loop); |
| 1645 } | 1673 } |
| 1646 } | 1674 } |
| 1647 | 1675 |
| 1648 a->Bind(&out); | 1676 Bind(&out); |
| 1649 { | 1677 { |
| 1650 // Wrap the match in a JSArray. | 1678 // Wrap the match in a JSArray. |
| 1651 | 1679 |
| 1652 Node* const result = array.ToJSArray(context); | 1680 Node* const result = array.ToJSArray(context); |
| 1653 a->Return(result); | 1681 Return(result); |
| 1654 } | 1682 } |
| 1655 } | 1683 } |
| 1656 } | 1684 } |
| 1657 | 1685 |
| 1658 } // namespace | |
| 1659 | |
| 1660 // ES#sec-regexp.prototype-@@match | 1686 // ES#sec-regexp.prototype-@@match |
| 1661 // RegExp.prototype [ @@match ] ( string ) | 1687 // RegExp.prototype [ @@match ] ( string ) |
| 1662 TF_BUILTIN(RegExpPrototypeMatch, RegExpBuiltinsAssembler) { | 1688 TF_BUILTIN(RegExpPrototypeMatch, RegExpBuiltinsAssembler) { |
| 1663 Node* const maybe_receiver = Parameter(0); | 1689 Node* const maybe_receiver = Parameter(0); |
| 1664 Node* const maybe_string = Parameter(1); | 1690 Node* const maybe_string = Parameter(1); |
| 1665 Node* const context = Parameter(4); | 1691 Node* const context = Parameter(4); |
| 1666 | 1692 |
| 1667 // Ensure {maybe_receiver} is a JSReceiver. | 1693 // Ensure {maybe_receiver} is a JSReceiver. |
| 1668 Node* const map = ThrowIfNotJSReceiver( | 1694 Node* const map = ThrowIfNotJSReceiver( |
| 1669 this, isolate(), context, maybe_receiver, | 1695 context, maybe_receiver, MessageTemplate::kIncompatibleMethodReceiver, |
| 1670 MessageTemplate::kIncompatibleMethodReceiver, "RegExp.prototype.@@match"); | 1696 "RegExp.prototype.@@match"); |
| 1671 Node* const receiver = maybe_receiver; | 1697 Node* const receiver = maybe_receiver; |
| 1672 | 1698 |
| 1673 // Convert {maybe_string} to a String. | 1699 // Convert {maybe_string} to a String. |
| 1674 Node* const string = ToString(context, maybe_string); | 1700 Node* const string = ToString(context, maybe_string); |
| 1675 | 1701 |
| 1676 CLabel fast_path(this), slow_path(this); | 1702 Label fast_path(this), slow_path(this); |
| 1677 BranchIfFastPath(this, context, map, &fast_path, &slow_path); | 1703 BranchIfFastRegExp(context, map, &fast_path, &slow_path); |
| 1678 | 1704 |
| 1679 Bind(&fast_path); | 1705 Bind(&fast_path); |
| 1680 RegExpPrototypeMatchBody(this, receiver, string, context, true); | 1706 RegExpPrototypeMatchBody(context, receiver, string, true); |
| 1681 | 1707 |
| 1682 Bind(&slow_path); | 1708 Bind(&slow_path); |
| 1683 RegExpPrototypeMatchBody(this, receiver, string, context, false); | 1709 RegExpPrototypeMatchBody(context, receiver, string, false); |
| 1684 } | 1710 } |
| 1685 | 1711 |
| 1686 namespace { | 1712 void RegExpBuiltinsAssembler::RegExpPrototypeSearchBodyFast( |
| 1687 | 1713 Node* const context, Node* const regexp, Node* const string) { |
| 1688 void RegExpPrototypeSearchBodyFast(CodeStubAssembler* a, Node* const receiver, | |
| 1689 Node* const string, Node* const context) { | |
| 1690 // Grab the initial value of last index. | 1714 // Grab the initial value of last index. |
| 1691 Node* const previous_last_index = FastLoadLastIndex(a, receiver); | 1715 Node* const previous_last_index = FastLoadLastIndex(regexp); |
| 1692 | 1716 |
| 1693 // Ensure last index is 0. | 1717 // Ensure last index is 0. |
| 1694 FastStoreLastIndex(a, receiver, a->SmiConstant(Smi::kZero)); | 1718 FastStoreLastIndex(regexp, SmiConstant(Smi::kZero)); |
| 1695 | 1719 |
| 1696 // Call exec. | 1720 // Call exec. |
| 1697 CLabel if_didnotmatch(a); | 1721 Label if_didnotmatch(this); |
| 1698 Node* const match_indices = RegExpPrototypeExecBodyWithoutResult( | 1722 Node* const match_indices = RegExpPrototypeExecBodyWithoutResult( |
| 1699 a, context, receiver, string, &if_didnotmatch, true); | 1723 context, regexp, string, &if_didnotmatch, true); |
| 1700 | 1724 |
| 1701 // Successful match. | 1725 // Successful match. |
| 1702 { | 1726 { |
| 1703 // Reset last index. | 1727 // Reset last index. |
| 1704 FastStoreLastIndex(a, receiver, previous_last_index); | 1728 FastStoreLastIndex(regexp, previous_last_index); |
| 1705 | 1729 |
| 1706 // Return the index of the match. | 1730 // Return the index of the match. |
| 1707 Node* const index = a->LoadFixedArrayElement( | 1731 Node* const index = LoadFixedArrayElement( |
| 1708 match_indices, RegExpMatchInfo::kFirstCaptureIndex); | 1732 match_indices, RegExpMatchInfo::kFirstCaptureIndex); |
| 1709 a->Return(index); | 1733 Return(index); |
| 1710 } | 1734 } |
| 1711 | 1735 |
| 1712 a->Bind(&if_didnotmatch); | 1736 Bind(&if_didnotmatch); |
| 1713 { | 1737 { |
| 1714 // Reset last index and return -1. | 1738 // Reset last index and return -1. |
| 1715 FastStoreLastIndex(a, receiver, previous_last_index); | 1739 FastStoreLastIndex(regexp, previous_last_index); |
| 1716 a->Return(a->SmiConstant(-1)); | 1740 Return(SmiConstant(-1)); |
| 1717 } | 1741 } |
| 1718 } | 1742 } |
| 1719 | 1743 |
| 1720 void RegExpPrototypeSearchBodySlow(CodeStubAssembler* a, Node* const receiver, | 1744 void RegExpBuiltinsAssembler::RegExpPrototypeSearchBodySlow( |
| 1721 Node* const string, Node* const context) { | 1745 Node* const context, Node* const regexp, Node* const string) { |
| 1722 Isolate* const isolate = a->isolate(); | 1746 Isolate* const isolate = this->isolate(); |
| 1723 | 1747 |
| 1724 Node* const smi_zero = a->SmiConstant(Smi::kZero); | 1748 Node* const smi_zero = SmiConstant(Smi::kZero); |
| 1725 | 1749 |
| 1726 // Grab the initial value of last index. | 1750 // Grab the initial value of last index. |
| 1727 Node* const previous_last_index = SlowLoadLastIndex(a, context, receiver); | 1751 Node* const previous_last_index = SlowLoadLastIndex(context, regexp); |
| 1728 | 1752 |
| 1729 // Ensure last index is 0. | 1753 // Ensure last index is 0. |
| 1730 { | 1754 { |
| 1731 CLabel next(a); | 1755 Label next(this); |
| 1732 a->GotoIf(a->SameValue(previous_last_index, smi_zero, context), &next); | 1756 GotoIf(SameValue(previous_last_index, smi_zero, context), &next); |
| 1733 | 1757 |
| 1734 SlowStoreLastIndex(a, context, receiver, smi_zero); | 1758 SlowStoreLastIndex(context, regexp, smi_zero); |
| 1735 a->Goto(&next); | 1759 Goto(&next); |
| 1736 a->Bind(&next); | 1760 Bind(&next); |
| 1737 } | 1761 } |
| 1738 | 1762 |
| 1739 // Call exec. | 1763 // Call exec. |
| 1740 Node* const exec_result = RegExpExec(a, context, receiver, string); | 1764 Node* const exec_result = RegExpExec(context, regexp, string); |
| 1741 | 1765 |
| 1742 // Reset last index if necessary. | 1766 // Reset last index if necessary. |
| 1743 { | 1767 { |
| 1744 CLabel next(a); | 1768 Label next(this); |
| 1745 Node* const current_last_index = SlowLoadLastIndex(a, context, receiver); | 1769 Node* const current_last_index = SlowLoadLastIndex(context, regexp); |
| 1746 | 1770 |
| 1747 a->GotoIf(a->SameValue(current_last_index, previous_last_index, context), | 1771 GotoIf(SameValue(current_last_index, previous_last_index, context), &next); |
| 1748 &next); | 1772 |
| 1749 | 1773 SlowStoreLastIndex(context, regexp, previous_last_index); |
| 1750 SlowStoreLastIndex(a, context, receiver, previous_last_index); | 1774 Goto(&next); |
| 1751 a->Goto(&next); | 1775 |
| 1752 | 1776 Bind(&next); |
| 1753 a->Bind(&next); | |
| 1754 } | 1777 } |
| 1755 | 1778 |
| 1756 // Return -1 if no match was found. | 1779 // Return -1 if no match was found. |
| 1757 { | 1780 { |
| 1758 CLabel next(a); | 1781 Label next(this); |
| 1759 a->GotoUnless(a->WordEqual(exec_result, a->NullConstant()), &next); | 1782 GotoUnless(WordEqual(exec_result, NullConstant()), &next); |
| 1760 a->Return(a->SmiConstant(-1)); | 1783 Return(SmiConstant(-1)); |
| 1761 a->Bind(&next); | 1784 Bind(&next); |
| 1762 } | 1785 } |
| 1763 | 1786 |
| 1764 // Return the index of the match. | 1787 // Return the index of the match. |
| 1765 { | 1788 { |
| 1766 CLabel fast_result(a), slow_result(a, CLabel::kDeferred); | 1789 Label fast_result(this), slow_result(this, Label::kDeferred); |
| 1767 BranchIfFastRegExpResult(a, context, a->LoadMap(exec_result), &fast_result, | 1790 BranchIfFastRegExpResult(context, LoadMap(exec_result), &fast_result, |
| 1768 &slow_result); | 1791 &slow_result); |
| 1769 | 1792 |
| 1770 a->Bind(&fast_result); | 1793 Bind(&fast_result); |
| 1771 { | 1794 { |
| 1772 Node* const index = | 1795 Node* const index = |
| 1773 a->LoadObjectField(exec_result, JSRegExpResult::kIndexOffset); | 1796 LoadObjectField(exec_result, JSRegExpResult::kIndexOffset); |
| 1774 a->Return(index); | 1797 Return(index); |
| 1775 } | 1798 } |
| 1776 | 1799 |
| 1777 a->Bind(&slow_result); | 1800 Bind(&slow_result); |
| 1778 { | 1801 { |
| 1779 Node* const name = a->HeapConstant(isolate->factory()->index_string()); | 1802 Node* const name = HeapConstant(isolate->factory()->index_string()); |
| 1780 Callable getproperty_callable = CodeFactory::GetProperty(a->isolate()); | 1803 Callable getproperty_callable = CodeFactory::GetProperty(isolate); |
| 1781 Node* const index = | 1804 Node* const index = |
| 1782 a->CallStub(getproperty_callable, context, exec_result, name); | 1805 CallStub(getproperty_callable, context, exec_result, name); |
| 1783 a->Return(index); | 1806 Return(index); |
| 1784 } | 1807 } |
| 1785 } | 1808 } |
| 1786 } | 1809 } |
| 1787 | |
| 1788 } // namespace | |
| 1789 | 1810 |
| 1790 // ES#sec-regexp.prototype-@@search | 1811 // ES#sec-regexp.prototype-@@search |
| 1791 // RegExp.prototype [ @@search ] ( string ) | 1812 // RegExp.prototype [ @@search ] ( string ) |
| 1792 TF_BUILTIN(RegExpPrototypeSearch, RegExpBuiltinsAssembler) { | 1813 TF_BUILTIN(RegExpPrototypeSearch, RegExpBuiltinsAssembler) { |
| 1793 Isolate* const isolate = this->isolate(); | |
| 1794 | |
| 1795 Node* const maybe_receiver = Parameter(0); | 1814 Node* const maybe_receiver = Parameter(0); |
| 1796 Node* const maybe_string = Parameter(1); | 1815 Node* const maybe_string = Parameter(1); |
| 1797 Node* const context = Parameter(4); | 1816 Node* const context = Parameter(4); |
| 1798 | 1817 |
| 1799 // Ensure {maybe_receiver} is a JSReceiver. | 1818 // Ensure {maybe_receiver} is a JSReceiver. |
| 1800 Node* const map = | 1819 Node* const map = ThrowIfNotJSReceiver( |
| 1801 ThrowIfNotJSReceiver(this, isolate, context, maybe_receiver, | 1820 context, maybe_receiver, MessageTemplate::kIncompatibleMethodReceiver, |
| 1802 MessageTemplate::kIncompatibleMethodReceiver, | 1821 "RegExp.prototype.@@search"); |
| 1803 "RegExp.prototype.@@search"); | |
| 1804 Node* const receiver = maybe_receiver; | 1822 Node* const receiver = maybe_receiver; |
| 1805 | 1823 |
| 1806 // Convert {maybe_string} to a String. | 1824 // Convert {maybe_string} to a String. |
| 1807 Node* const string = ToString(context, maybe_string); | 1825 Node* const string = ToString(context, maybe_string); |
| 1808 | 1826 |
| 1809 CLabel fast_path(this), slow_path(this); | 1827 Label fast_path(this), slow_path(this); |
| 1810 BranchIfFastPath(this, context, map, &fast_path, &slow_path); | 1828 BranchIfFastRegExp(context, map, &fast_path, &slow_path); |
| 1811 | 1829 |
| 1812 Bind(&fast_path); | 1830 Bind(&fast_path); |
| 1813 RegExpPrototypeSearchBodyFast(this, receiver, string, context); | 1831 RegExpPrototypeSearchBodyFast(context, receiver, string); |
| 1814 | 1832 |
| 1815 Bind(&slow_path); | 1833 Bind(&slow_path); |
| 1816 RegExpPrototypeSearchBodySlow(this, receiver, string, context); | 1834 RegExpPrototypeSearchBodySlow(context, receiver, string); |
| 1817 } | 1835 } |
| 1818 | |
| 1819 namespace { | |
| 1820 | 1836 |
| 1821 // Generates the fast path for @@split. {regexp} is an unmodified JSRegExp, | 1837 // Generates the fast path for @@split. {regexp} is an unmodified JSRegExp, |
| 1822 // {string} is a String, and {limit} is a Smi. | 1838 // {string} is a String, and {limit} is a Smi. |
| 1823 void Generate_RegExpPrototypeSplitBody(CodeStubAssembler* a, Node* const regexp, | 1839 void RegExpBuiltinsAssembler::RegExpPrototypeSplitBody(Node* const context, |
| 1824 Node* const string, Node* const limit, | 1840 Node* const regexp, |
| 1825 Node* const context) { | 1841 Node* const string, |
| 1826 Isolate* isolate = a->isolate(); | 1842 Node* const limit) { |
| 1827 | 1843 Isolate* isolate = this->isolate(); |
| 1828 Node* const null = a->NullConstant(); | 1844 |
| 1829 Node* const smi_zero = a->SmiConstant(0); | 1845 Node* const null = NullConstant(); |
| 1830 Node* const int_zero = a->IntPtrConstant(0); | 1846 Node* const smi_zero = SmiConstant(0); |
| 1831 Node* const int_limit = a->SmiUntag(limit); | 1847 Node* const int_zero = IntPtrConstant(0); |
| 1848 Node* const int_limit = SmiUntag(limit); | |
| 1832 | 1849 |
| 1833 const ElementsKind kind = FAST_ELEMENTS; | 1850 const ElementsKind kind = FAST_ELEMENTS; |
| 1834 const ParameterMode mode = CodeStubAssembler::INTPTR_PARAMETERS; | 1851 const ParameterMode mode = CodeStubAssembler::INTPTR_PARAMETERS; |
| 1835 | 1852 |
| 1836 Node* const allocation_site = nullptr; | 1853 Node* const allocation_site = nullptr; |
| 1837 Node* const native_context = a->LoadNativeContext(context); | 1854 Node* const native_context = LoadNativeContext(context); |
| 1838 Node* const array_map = a->LoadJSArrayElementsMap(kind, native_context); | 1855 Node* const array_map = LoadJSArrayElementsMap(kind, native_context); |
| 1839 | 1856 |
| 1840 CLabel return_empty_array(a, CLabel::kDeferred); | 1857 Label return_empty_array(this, Label::kDeferred); |
| 1841 | 1858 |
| 1842 // If limit is zero, return an empty array. | 1859 // If limit is zero, return an empty array. |
| 1843 { | 1860 { |
| 1844 CLabel next(a), if_limitiszero(a, CLabel::kDeferred); | 1861 Label next(this), if_limitiszero(this, Label::kDeferred); |
| 1845 a->Branch(a->SmiEqual(limit, smi_zero), &return_empty_array, &next); | 1862 Branch(SmiEqual(limit, smi_zero), &return_empty_array, &next); |
| 1846 a->Bind(&next); | 1863 Bind(&next); |
| 1847 } | 1864 } |
| 1848 | 1865 |
| 1849 Node* const string_length = a->LoadStringLength(string); | 1866 Node* const string_length = LoadStringLength(string); |
| 1850 | 1867 |
| 1851 // If passed the empty {string}, return either an empty array or a singleton | 1868 // If passed the empty {string}, return either an empty array or a singleton |
| 1852 // array depending on whether the {regexp} matches. | 1869 // array depending on whether the {regexp} matches. |
| 1853 { | 1870 { |
| 1854 CLabel next(a), if_stringisempty(a, CLabel::kDeferred); | 1871 Label next(this), if_stringisempty(this, Label::kDeferred); |
| 1855 a->Branch(a->SmiEqual(string_length, smi_zero), &if_stringisempty, &next); | 1872 Branch(SmiEqual(string_length, smi_zero), &if_stringisempty, &next); |
| 1856 | 1873 |
| 1857 a->Bind(&if_stringisempty); | 1874 Bind(&if_stringisempty); |
| 1858 { | 1875 { |
| 1859 Node* const last_match_info = a->LoadContextElement( | 1876 Node* const last_match_info = LoadContextElement( |
| 1860 native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX); | 1877 native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX); |
| 1861 | 1878 |
| 1862 Callable exec_callable = CodeFactory::RegExpExec(isolate); | 1879 Callable exec_callable = CodeFactory::RegExpExec(isolate); |
| 1863 Node* const match_indices = a->CallStub( | 1880 Node* const match_indices = CallStub(exec_callable, context, regexp, |
| 1864 exec_callable, context, regexp, string, smi_zero, last_match_info); | 1881 string, smi_zero, last_match_info); |
| 1865 | 1882 |
| 1866 CLabel return_singleton_array(a); | 1883 Label return_singleton_array(this); |
| 1867 a->Branch(a->WordEqual(match_indices, null), &return_singleton_array, | 1884 Branch(WordEqual(match_indices, null), &return_singleton_array, |
| 1868 &return_empty_array); | 1885 &return_empty_array); |
| 1869 | 1886 |
| 1870 a->Bind(&return_singleton_array); | 1887 Bind(&return_singleton_array); |
| 1871 { | 1888 { |
| 1872 Node* const length = a->SmiConstant(1); | 1889 Node* const length = SmiConstant(1); |
| 1873 Node* const capacity = a->IntPtrConstant(1); | 1890 Node* const capacity = IntPtrConstant(1); |
| 1874 Node* const result = a->AllocateJSArray(kind, array_map, capacity, | 1891 Node* const result = AllocateJSArray(kind, array_map, capacity, length, |
| 1875 length, allocation_site, mode); | 1892 allocation_site, mode); |
| 1876 | 1893 |
| 1877 Node* const fixed_array = a->LoadElements(result); | 1894 Node* const fixed_array = LoadElements(result); |
| 1878 a->StoreFixedArrayElement(fixed_array, 0, string); | 1895 StoreFixedArrayElement(fixed_array, 0, string); |
| 1879 | 1896 |
| 1880 a->Return(result); | 1897 Return(result); |
| 1881 } | 1898 } |
| 1882 } | 1899 } |
| 1883 | 1900 |
| 1884 a->Bind(&next); | 1901 Bind(&next); |
| 1885 } | 1902 } |
| 1886 | 1903 |
| 1887 // Loop preparations. | 1904 // Loop preparations. |
| 1888 | 1905 |
| 1889 GrowableFixedArray array(a); | 1906 GrowableFixedArray array(this); |
| 1890 | 1907 |
| 1891 CVariable var_last_matched_until(a, MachineRepresentation::kTagged); | 1908 Variable var_last_matched_until(this, MachineRepresentation::kTagged); |
| 1892 CVariable var_next_search_from(a, MachineRepresentation::kTagged); | 1909 Variable var_next_search_from(this, MachineRepresentation::kTagged); |
| 1893 | 1910 |
| 1894 var_last_matched_until.Bind(smi_zero); | 1911 var_last_matched_until.Bind(smi_zero); |
| 1895 var_next_search_from.Bind(smi_zero); | 1912 var_next_search_from.Bind(smi_zero); |
| 1896 | 1913 |
| 1897 CVariable* vars[] = {array.var_array(), array.var_length(), | 1914 Variable* vars[] = {array.var_array(), array.var_length(), |
| 1898 array.var_capacity(), &var_last_matched_until, | 1915 array.var_capacity(), &var_last_matched_until, |
| 1899 &var_next_search_from}; | 1916 &var_next_search_from}; |
| 1900 const int vars_count = sizeof(vars) / sizeof(vars[0]); | 1917 const int vars_count = sizeof(vars) / sizeof(vars[0]); |
| 1901 CLabel loop(a, vars_count, vars), push_suffix_and_out(a), out(a); | 1918 Label loop(this, vars_count, vars), push_suffix_and_out(this), out(this); |
| 1902 a->Goto(&loop); | 1919 Goto(&loop); |
| 1903 | 1920 |
| 1904 a->Bind(&loop); | 1921 Bind(&loop); |
| 1905 { | 1922 { |
| 1906 Node* const next_search_from = var_next_search_from.value(); | 1923 Node* const next_search_from = var_next_search_from.value(); |
| 1907 Node* const last_matched_until = var_last_matched_until.value(); | 1924 Node* const last_matched_until = var_last_matched_until.value(); |
| 1908 | 1925 |
| 1909 // We're done if we've reached the end of the string. | 1926 // We're done if we've reached the end of the string. |
| 1910 { | 1927 { |
| 1911 CLabel next(a); | 1928 Label next(this); |
| 1912 a->Branch(a->SmiEqual(next_search_from, string_length), | 1929 Branch(SmiEqual(next_search_from, string_length), &push_suffix_and_out, |
| 1913 &push_suffix_and_out, &next); | 1930 &next); |
| 1914 a->Bind(&next); | 1931 Bind(&next); |
| 1915 } | 1932 } |
| 1916 | 1933 |
| 1917 // Search for the given {regexp}. | 1934 // Search for the given {regexp}. |
| 1918 | 1935 |
| 1919 Node* const last_match_info = a->LoadContextElement( | 1936 Node* const last_match_info = LoadContextElement( |
| 1920 native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX); | 1937 native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX); |
| 1921 | 1938 |
| 1922 Callable exec_callable = CodeFactory::RegExpExec(isolate); | 1939 Callable exec_callable = CodeFactory::RegExpExec(isolate); |
| 1923 Node* const match_indices = | 1940 Node* const match_indices = CallStub(exec_callable, context, regexp, string, |
| 1924 a->CallStub(exec_callable, context, regexp, string, next_search_from, | 1941 next_search_from, last_match_info); |
| 1925 last_match_info); | |
| 1926 | 1942 |
| 1927 // We're done if no match was found. | 1943 // We're done if no match was found. |
| 1928 { | 1944 { |
| 1929 CLabel next(a); | 1945 Label next(this); |
| 1930 a->Branch(a->WordEqual(match_indices, null), &push_suffix_and_out, &next); | 1946 Branch(WordEqual(match_indices, null), &push_suffix_and_out, &next); |
| 1931 a->Bind(&next); | 1947 Bind(&next); |
| 1932 } | 1948 } |
| 1933 | 1949 |
| 1934 Node* const match_from = a->LoadFixedArrayElement( | 1950 Node* const match_from = LoadFixedArrayElement( |
| 1935 match_indices, RegExpMatchInfo::kFirstCaptureIndex); | 1951 match_indices, RegExpMatchInfo::kFirstCaptureIndex); |
| 1936 | 1952 |
| 1937 // We're done if the match starts beyond the string. | 1953 // We're done if the match starts beyond the string. |
| 1938 { | 1954 { |
| 1939 CLabel next(a); | 1955 Label next(this); |
| 1940 a->Branch(a->WordEqual(match_from, string_length), &push_suffix_and_out, | 1956 Branch(WordEqual(match_from, string_length), &push_suffix_and_out, &next); |
| 1941 &next); | 1957 Bind(&next); |
| 1942 a->Bind(&next); | 1958 } |
| 1943 } | 1959 |
| 1944 | 1960 Node* const match_to = LoadFixedArrayElement( |
| 1945 Node* const match_to = a->LoadFixedArrayElement( | |
| 1946 match_indices, RegExpMatchInfo::kFirstCaptureIndex + 1); | 1961 match_indices, RegExpMatchInfo::kFirstCaptureIndex + 1); |
| 1947 | 1962 |
| 1948 // Advance index and continue if the match is empty. | 1963 // Advance index and continue if the match is empty. |
| 1949 { | 1964 { |
| 1950 CLabel next(a); | 1965 Label next(this); |
| 1951 | 1966 |
| 1952 a->GotoUnless(a->SmiEqual(match_to, next_search_from), &next); | 1967 GotoUnless(SmiEqual(match_to, next_search_from), &next); |
| 1953 a->GotoUnless(a->SmiEqual(match_to, last_matched_until), &next); | 1968 GotoUnless(SmiEqual(match_to, last_matched_until), &next); |
| 1954 | 1969 |
| 1955 Node* const is_unicode = FastFlagGetter(a, regexp, JSRegExp::kUnicode); | 1970 Node* const is_unicode = FastFlagGetter(regexp, JSRegExp::kUnicode); |
| 1956 Node* const new_next_search_from = | 1971 Node* const new_next_search_from = |
| 1957 AdvanceStringIndex(a, string, next_search_from, is_unicode); | 1972 AdvanceStringIndex(string, next_search_from, is_unicode); |
| 1958 var_next_search_from.Bind(new_next_search_from); | 1973 var_next_search_from.Bind(new_next_search_from); |
| 1959 a->Goto(&loop); | 1974 Goto(&loop); |
| 1960 | 1975 |
| 1961 a->Bind(&next); | 1976 Bind(&next); |
| 1962 } | 1977 } |
| 1963 | 1978 |
| 1964 // A valid match was found, add the new substring to the array. | 1979 // A valid match was found, add the new substring to the array. |
| 1965 { | 1980 { |
| 1966 Node* const from = last_matched_until; | 1981 Node* const from = last_matched_until; |
| 1967 Node* const to = match_from; | 1982 Node* const to = match_from; |
| 1968 | 1983 |
| 1969 Node* const substr = a->SubString(context, string, from, to); | 1984 Node* const substr = SubString(context, string, from, to); |
| 1970 array.Push(substr); | 1985 array.Push(substr); |
| 1971 | 1986 |
| 1972 a->GotoIf(a->WordEqual(array.length(), int_limit), &out); | 1987 GotoIf(WordEqual(array.length(), int_limit), &out); |
| 1973 } | 1988 } |
| 1974 | 1989 |
| 1975 // Add all captures to the array. | 1990 // Add all captures to the array. |
| 1976 { | 1991 { |
| 1977 Node* const num_registers = a->LoadFixedArrayElement( | 1992 Node* const num_registers = LoadFixedArrayElement( |
| 1978 match_indices, RegExpMatchInfo::kNumberOfCapturesIndex); | 1993 match_indices, RegExpMatchInfo::kNumberOfCapturesIndex); |
| 1979 Node* const int_num_registers = a->SmiUntag(num_registers); | 1994 Node* const int_num_registers = SmiUntag(num_registers); |
| 1980 | 1995 |
| 1981 CVariable var_reg(a, MachineType::PointerRepresentation()); | 1996 Variable var_reg(this, MachineType::PointerRepresentation()); |
| 1982 var_reg.Bind(a->IntPtrConstant(2)); | 1997 var_reg.Bind(IntPtrConstant(2)); |
| 1983 | 1998 |
| 1984 CVariable* vars[] = {array.var_array(), array.var_length(), | 1999 Variable* vars[] = {array.var_array(), array.var_length(), |
| 1985 array.var_capacity(), &var_reg}; | 2000 array.var_capacity(), &var_reg}; |
| 1986 const int vars_count = sizeof(vars) / sizeof(vars[0]); | 2001 const int vars_count = sizeof(vars) / sizeof(vars[0]); |
| 1987 CLabel nested_loop(a, vars_count, vars), nested_loop_out(a); | 2002 Label nested_loop(this, vars_count, vars), nested_loop_out(this); |
| 1988 a->Branch(a->IntPtrLessThan(var_reg.value(), int_num_registers), | 2003 Branch(IntPtrLessThan(var_reg.value(), int_num_registers), &nested_loop, |
| 1989 &nested_loop, &nested_loop_out); | 2004 &nested_loop_out); |
| 1990 | 2005 |
| 1991 a->Bind(&nested_loop); | 2006 Bind(&nested_loop); |
| 1992 { | 2007 { |
| 1993 Node* const reg = var_reg.value(); | 2008 Node* const reg = var_reg.value(); |
| 1994 Node* const from = a->LoadFixedArrayElement( | 2009 Node* const from = LoadFixedArrayElement( |
| 1995 match_indices, reg, | 2010 match_indices, reg, |
| 1996 RegExpMatchInfo::kFirstCaptureIndex * kPointerSize, mode); | 2011 RegExpMatchInfo::kFirstCaptureIndex * kPointerSize, mode); |
| 1997 Node* const to = a->LoadFixedArrayElement( | 2012 Node* const to = LoadFixedArrayElement( |
| 1998 match_indices, reg, | 2013 match_indices, reg, |
| 1999 (RegExpMatchInfo::kFirstCaptureIndex + 1) * kPointerSize, mode); | 2014 (RegExpMatchInfo::kFirstCaptureIndex + 1) * kPointerSize, mode); |
| 2000 | 2015 |
| 2001 CLabel select_capture(a), select_undefined(a), store_value(a); | 2016 Label select_capture(this), select_undefined(this), store_value(this); |
| 2002 CVariable var_value(a, MachineRepresentation::kTagged); | 2017 Variable var_value(this, MachineRepresentation::kTagged); |
| 2003 a->Branch(a->SmiEqual(to, a->SmiConstant(-1)), &select_undefined, | 2018 Branch(SmiEqual(to, SmiConstant(-1)), &select_undefined, |
| 2004 &select_capture); | 2019 &select_capture); |
| 2005 | 2020 |
| 2006 a->Bind(&select_capture); | 2021 Bind(&select_capture); |
| 2007 { | 2022 { |
| 2008 Node* const substr = a->SubString(context, string, from, to); | 2023 Node* const substr = SubString(context, string, from, to); |
| 2009 var_value.Bind(substr); | 2024 var_value.Bind(substr); |
| 2010 a->Goto(&store_value); | 2025 Goto(&store_value); |
| 2011 } | 2026 } |
| 2012 | 2027 |
| 2013 a->Bind(&select_undefined); | 2028 Bind(&select_undefined); |
| 2014 { | 2029 { |
| 2015 Node* const undefined = a->UndefinedConstant(); | 2030 Node* const undefined = UndefinedConstant(); |
| 2016 var_value.Bind(undefined); | 2031 var_value.Bind(undefined); |
| 2017 a->Goto(&store_value); | 2032 Goto(&store_value); |
| 2018 } | 2033 } |
| 2019 | 2034 |
| 2020 a->Bind(&store_value); | 2035 Bind(&store_value); |
| 2021 { | 2036 { |
| 2022 array.Push(var_value.value()); | 2037 array.Push(var_value.value()); |
| 2023 a->GotoIf(a->WordEqual(array.length(), int_limit), &out); | 2038 GotoIf(WordEqual(array.length(), int_limit), &out); |
| 2024 | 2039 |
| 2025 Node* const new_reg = a->IntPtrAdd(reg, a->IntPtrConstant(2)); | 2040 Node* const new_reg = IntPtrAdd(reg, IntPtrConstant(2)); |
| 2026 var_reg.Bind(new_reg); | 2041 var_reg.Bind(new_reg); |
| 2027 | 2042 |
| 2028 a->Branch(a->IntPtrLessThan(new_reg, int_num_registers), &nested_loop, | 2043 Branch(IntPtrLessThan(new_reg, int_num_registers), &nested_loop, |
| 2029 &nested_loop_out); | 2044 &nested_loop_out); |
| 2030 } | 2045 } |
| 2031 } | 2046 } |
| 2032 | 2047 |
| 2033 a->Bind(&nested_loop_out); | 2048 Bind(&nested_loop_out); |
| 2034 } | 2049 } |
| 2035 | 2050 |
| 2036 var_last_matched_until.Bind(match_to); | 2051 var_last_matched_until.Bind(match_to); |
| 2037 var_next_search_from.Bind(match_to); | 2052 var_next_search_from.Bind(match_to); |
| 2038 a->Goto(&loop); | 2053 Goto(&loop); |
| 2039 } | 2054 } |
| 2040 | 2055 |
| 2041 a->Bind(&push_suffix_and_out); | 2056 Bind(&push_suffix_and_out); |
| 2042 { | 2057 { |
| 2043 Node* const from = var_last_matched_until.value(); | 2058 Node* const from = var_last_matched_until.value(); |
| 2044 Node* const to = string_length; | 2059 Node* const to = string_length; |
| 2045 | 2060 |
| 2046 Node* const substr = a->SubString(context, string, from, to); | 2061 Node* const substr = SubString(context, string, from, to); |
| 2047 array.Push(substr); | 2062 array.Push(substr); |
| 2048 | 2063 |
| 2049 a->Goto(&out); | 2064 Goto(&out); |
| 2050 } | 2065 } |
| 2051 | 2066 |
| 2052 a->Bind(&out); | 2067 Bind(&out); |
| 2053 { | 2068 { |
| 2054 Node* const result = array.ToJSArray(context); | 2069 Node* const result = array.ToJSArray(context); |
| 2055 a->Return(result); | 2070 Return(result); |
| 2056 } | 2071 } |
| 2057 | 2072 |
| 2058 a->Bind(&return_empty_array); | 2073 Bind(&return_empty_array); |
| 2059 { | 2074 { |
| 2060 Node* const length = smi_zero; | 2075 Node* const length = smi_zero; |
| 2061 Node* const capacity = int_zero; | 2076 Node* const capacity = int_zero; |
| 2062 Node* const result = a->AllocateJSArray(kind, array_map, capacity, length, | 2077 Node* const result = AllocateJSArray(kind, array_map, capacity, length, |
| 2063 allocation_site, mode); | 2078 allocation_site, mode); |
| 2064 a->Return(result); | 2079 Return(result); |
| 2065 } | 2080 } |
| 2066 } | 2081 } |
| 2067 | 2082 |
| 2068 } // namespace | |
| 2069 | |
| 2070 // ES#sec-regexp.prototype-@@split | 2083 // ES#sec-regexp.prototype-@@split |
| 2071 // RegExp.prototype [ @@split ] ( string, limit ) | 2084 // RegExp.prototype [ @@split ] ( string, limit ) |
| 2072 TF_BUILTIN(RegExpPrototypeSplit, RegExpBuiltinsAssembler) { | 2085 TF_BUILTIN(RegExpPrototypeSplit, RegExpBuiltinsAssembler) { |
| 2073 Isolate* const isolate = this->isolate(); | |
| 2074 | |
| 2075 Node* const undefined = UndefinedConstant(); | |
| 2076 | |
| 2077 Node* const maybe_receiver = Parameter(0); | 2086 Node* const maybe_receiver = Parameter(0); |
| 2078 Node* const maybe_string = Parameter(1); | 2087 Node* const maybe_string = Parameter(1); |
| 2079 Node* const maybe_limit = Parameter(2); | 2088 Node* const maybe_limit = Parameter(2); |
| 2080 Node* const context = Parameter(5); | 2089 Node* const context = Parameter(5); |
| 2081 | 2090 |
| 2082 // Ensure {maybe_receiver} is a JSReceiver. | 2091 // Ensure {maybe_receiver} is a JSReceiver. |
| 2083 Node* const map = ThrowIfNotJSReceiver( | 2092 Node* const map = ThrowIfNotJSReceiver( |
| 2084 this, isolate, context, maybe_receiver, | 2093 context, maybe_receiver, MessageTemplate::kIncompatibleMethodReceiver, |
| 2085 MessageTemplate::kIncompatibleMethodReceiver, "RegExp.prototype.@@split"); | 2094 "RegExp.prototype.@@split"); |
| 2086 Node* const receiver = maybe_receiver; | 2095 Node* const receiver = maybe_receiver; |
| 2087 | 2096 |
| 2088 // Convert {maybe_string} to a String. | 2097 // Convert {maybe_string} to a String. |
| 2089 Node* const string = ToString(context, maybe_string); | 2098 Node* const string = ToString(context, maybe_string); |
| 2090 | 2099 |
| 2091 CLabel fast_path(this), slow_path(this); | 2100 Label fast_path(this), slow_path(this); |
| 2092 BranchIfFastPath(this, context, map, &fast_path, &slow_path); | 2101 BranchIfFastRegExp(context, map, &fast_path, &slow_path); |
| 2093 | 2102 |
| 2094 Bind(&fast_path); | 2103 Bind(&fast_path); |
| 2095 { | 2104 { |
| 2105 // TODO(jgruber): Even if map checks send us to the fast path, we still need | |
| 2106 // to verify the constructor property and jump to the slow path if it has | |
| 2107 // been changed. | |
| 2108 | |
| 2096 // Convert {maybe_limit} to a uint32, capping at the maximal smi value. | 2109 // Convert {maybe_limit} to a uint32, capping at the maximal smi value. |
| 2097 CVariable var_limit(this, MachineRepresentation::kTagged); | 2110 Variable var_limit(this, MachineRepresentation::kTagged); |
| 2098 CLabel if_limitissmimax(this), limit_done(this); | 2111 Label if_limitissmimax(this), limit_done(this); |
| 2099 | 2112 |
| 2100 GotoIf(WordEqual(maybe_limit, undefined), &if_limitissmimax); | 2113 GotoIf(IsUndefined(maybe_limit), &if_limitissmimax); |
| 2101 | 2114 |
| 2102 { | 2115 { |
| 2103 Node* const limit = ToUint32(context, maybe_limit); | 2116 Node* const limit = ToUint32(context, maybe_limit); |
| 2104 GotoUnless(TaggedIsSmi(limit), &if_limitissmimax); | 2117 GotoUnless(TaggedIsSmi(limit), &if_limitissmimax); |
| 2105 | 2118 |
| 2106 var_limit.Bind(limit); | 2119 var_limit.Bind(limit); |
| 2107 Goto(&limit_done); | 2120 Goto(&limit_done); |
| 2108 } | 2121 } |
| 2109 | 2122 |
| 2110 Bind(&if_limitissmimax); | 2123 Bind(&if_limitissmimax); |
| 2111 { | 2124 { |
| 2112 // TODO(jgruber): In this case, we can probably generation of limit checks | 2125 // TODO(jgruber): In this case, we can probably generation of limit checks |
| 2113 // in Generate_RegExpPrototypeSplitBody. | 2126 // in Generate_RegExpPrototypeSplitBody. |
| 2114 Node* const smi_max = SmiConstant(Smi::kMaxValue); | 2127 Node* const smi_max = SmiConstant(Smi::kMaxValue); |
| 2115 var_limit.Bind(smi_max); | 2128 var_limit.Bind(smi_max); |
| 2116 Goto(&limit_done); | 2129 Goto(&limit_done); |
| 2117 } | 2130 } |
| 2118 | 2131 |
| 2119 Bind(&limit_done); | 2132 Bind(&limit_done); |
| 2120 { | 2133 { |
| 2121 Node* const limit = var_limit.value(); | 2134 Node* const limit = var_limit.value(); |
| 2122 Generate_RegExpPrototypeSplitBody(this, receiver, string, limit, context); | 2135 RegExpPrototypeSplitBody(context, receiver, string, limit); |
| 2123 } | 2136 } |
| 2124 } | 2137 } |
| 2125 | 2138 |
| 2126 Bind(&slow_path); | 2139 Bind(&slow_path); |
| 2127 { | 2140 { |
| 2128 Node* const result = CallRuntime(Runtime::kRegExpSplit, context, receiver, | 2141 Node* const result = CallRuntime(Runtime::kRegExpSplit, context, receiver, |
| 2129 string, maybe_limit); | 2142 string, maybe_limit); |
| 2130 Return(result); | 2143 Return(result); |
| 2131 } | 2144 } |
| 2132 } | 2145 } |
| 2133 | 2146 |
| 2134 namespace { | 2147 Node* RegExpBuiltinsAssembler::ReplaceGlobalCallableFastPath( |
| 2135 | 2148 Node* context, Node* regexp, Node* string, Node* replace_callable) { |
| 2136 Node* ReplaceGlobalCallableFastPath(CodeStubAssembler* a, Node* context, | |
| 2137 Node* regexp, Node* subject_string, | |
| 2138 Node* replace_callable) { | |
| 2139 // The fast path is reached only if {receiver} is a global unmodified | 2149 // The fast path is reached only if {receiver} is a global unmodified |
| 2140 // JSRegExp instance and {replace_callable} is callable. | 2150 // JSRegExp instance and {replace_callable} is callable. |
| 2141 | 2151 |
| 2142 Isolate* const isolate = a->isolate(); | 2152 Isolate* const isolate = this->isolate(); |
| 2143 | 2153 |
| 2144 Node* const null = a->NullConstant(); | 2154 Node* const null = NullConstant(); |
| 2145 Node* const undefined = a->UndefinedConstant(); | 2155 Node* const undefined = UndefinedConstant(); |
| 2146 Node* const int_zero = a->IntPtrConstant(0); | 2156 Node* const int_zero = IntPtrConstant(0); |
| 2147 Node* const int_one = a->IntPtrConstant(1); | 2157 Node* const int_one = IntPtrConstant(1); |
| 2148 Node* const smi_zero = a->SmiConstant(Smi::kZero); | 2158 Node* const smi_zero = SmiConstant(Smi::kZero); |
| 2149 | 2159 |
| 2150 Node* const native_context = a->LoadNativeContext(context); | 2160 Node* const native_context = LoadNativeContext(context); |
| 2151 | 2161 |
| 2152 CLabel out(a); | 2162 Label out(this); |
| 2153 CVariable var_result(a, MachineRepresentation::kTagged); | 2163 Variable var_result(this, MachineRepresentation::kTagged); |
| 2154 | 2164 |
| 2155 // Set last index to 0. | 2165 // Set last index to 0. |
| 2156 FastStoreLastIndex(a, regexp, smi_zero); | 2166 FastStoreLastIndex(regexp, smi_zero); |
| 2157 | 2167 |
| 2158 // Allocate {result_array}. | 2168 // Allocate {result_array}. |
| 2159 Node* result_array; | 2169 Node* result_array; |
| 2160 { | 2170 { |
| 2161 ElementsKind kind = FAST_ELEMENTS; | 2171 ElementsKind kind = FAST_ELEMENTS; |
| 2162 Node* const array_map = a->LoadJSArrayElementsMap(kind, native_context); | 2172 Node* const array_map = LoadJSArrayElementsMap(kind, native_context); |
| 2163 Node* const capacity = a->IntPtrConstant(16); | 2173 Node* const capacity = IntPtrConstant(16); |
| 2164 Node* const length = smi_zero; | 2174 Node* const length = smi_zero; |
| 2165 Node* const allocation_site = nullptr; | 2175 Node* const allocation_site = nullptr; |
| 2166 ParameterMode capacity_mode = CodeStubAssembler::INTPTR_PARAMETERS; | 2176 ParameterMode capacity_mode = CodeStubAssembler::INTPTR_PARAMETERS; |
| 2167 | 2177 |
| 2168 result_array = a->AllocateJSArray(kind, array_map, capacity, length, | 2178 result_array = AllocateJSArray(kind, array_map, capacity, length, |
| 2169 allocation_site, capacity_mode); | 2179 allocation_site, capacity_mode); |
| 2170 } | 2180 } |
| 2171 | 2181 |
| 2172 // Call into runtime for RegExpExecMultiple. | 2182 // Call into runtime for RegExpExecMultiple. |
| 2173 Node* last_match_info = a->LoadContextElement( | 2183 Node* last_match_info = |
| 2174 native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX); | 2184 LoadContextElement(native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX); |
| 2175 Node* const res = | 2185 Node* const res = CallRuntime(Runtime::kRegExpExecMultiple, context, regexp, |
| 2176 a->CallRuntime(Runtime::kRegExpExecMultiple, context, regexp, | 2186 string, last_match_info, result_array); |
| 2177 subject_string, last_match_info, result_array); | |
| 2178 | 2187 |
| 2179 // Reset last index to 0. | 2188 // Reset last index to 0. |
| 2180 FastStoreLastIndex(a, regexp, smi_zero); | 2189 FastStoreLastIndex(regexp, smi_zero); |
| 2181 | 2190 |
| 2182 // If no matches, return the subject string. | 2191 // If no matches, return the subject string. |
| 2183 var_result.Bind(subject_string); | 2192 var_result.Bind(string); |
| 2184 a->GotoIf(a->WordEqual(res, null), &out); | 2193 GotoIf(WordEqual(res, null), &out); |
| 2185 | 2194 |
| 2186 // Reload last match info since it might have changed. | 2195 // Reload last match info since it might have changed. |
| 2187 last_match_info = a->LoadContextElement( | 2196 last_match_info = |
| 2188 native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX); | 2197 LoadContextElement(native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX); |
| 2189 | 2198 |
| 2190 Node* const res_length = a->LoadJSArrayLength(res); | 2199 Node* const res_length = LoadJSArrayLength(res); |
| 2191 Node* const res_elems = a->LoadElements(res); | 2200 Node* const res_elems = LoadElements(res); |
| 2192 CSA_ASSERT(a, a->HasInstanceType(res_elems, FIXED_ARRAY_TYPE)); | 2201 CSA_ASSERT(this, HasInstanceType(res_elems, FIXED_ARRAY_TYPE)); |
| 2193 | 2202 |
| 2194 Node* const num_capture_registers = a->LoadFixedArrayElement( | 2203 Node* const num_capture_registers = LoadFixedArrayElement( |
| 2195 last_match_info, RegExpMatchInfo::kNumberOfCapturesIndex); | 2204 last_match_info, RegExpMatchInfo::kNumberOfCapturesIndex); |
| 2196 | 2205 |
| 2197 CLabel if_hasexplicitcaptures(a), if_noexplicitcaptures(a), create_result(a); | 2206 Label if_hasexplicitcaptures(this), if_noexplicitcaptures(this), |
| 2198 a->Branch(a->SmiEqual(num_capture_registers, a->SmiConstant(Smi::FromInt(2))), | 2207 create_result(this); |
| 2199 &if_noexplicitcaptures, &if_hasexplicitcaptures); | 2208 Branch(SmiEqual(num_capture_registers, SmiConstant(Smi::FromInt(2))), |
| 2209 &if_noexplicitcaptures, &if_hasexplicitcaptures); | |
| 2200 | 2210 |
| 2201 a->Bind(&if_noexplicitcaptures); | 2211 Bind(&if_noexplicitcaptures); |
| 2202 { | 2212 { |
| 2203 // If the number of captures is two then there are no explicit captures in | 2213 // If the number of captures is two then there are no explicit captures in |
| 2204 // the regexp, just the implicit capture that captures the whole match. In | 2214 // the regexp, just the implicit capture that captures the whole match. In |
| 2205 // this case we can simplify quite a bit and end up with something faster. | 2215 // this case we can simplify quite a bit and end up with something faster. |
| 2206 // The builder will consist of some integers that indicate slices of the | 2216 // The builder will consist of some integers that indicate slices of the |
| 2207 // input string and some replacements that were returned from the replace | 2217 // input string and some replacements that were returned from the replace |
| 2208 // function. | 2218 // function. |
| 2209 | 2219 |
| 2210 CVariable var_match_start(a, MachineRepresentation::kTagged); | 2220 Variable var_match_start(this, MachineRepresentation::kTagged); |
| 2211 var_match_start.Bind(smi_zero); | 2221 var_match_start.Bind(smi_zero); |
| 2212 | 2222 |
| 2213 Node* const end = a->SmiUntag(res_length); | 2223 Node* const end = SmiUntag(res_length); |
| 2214 CVariable var_i(a, MachineType::PointerRepresentation()); | 2224 Variable var_i(this, MachineType::PointerRepresentation()); |
| 2215 var_i.Bind(int_zero); | 2225 var_i.Bind(int_zero); |
| 2216 | 2226 |
| 2217 CVariable* vars[] = {&var_i, &var_match_start}; | 2227 Variable* vars[] = {&var_i, &var_match_start}; |
| 2218 CLabel loop(a, 2, vars); | 2228 Label loop(this, 2, vars); |
| 2219 a->Goto(&loop); | 2229 Goto(&loop); |
| 2220 a->Bind(&loop); | 2230 Bind(&loop); |
| 2221 { | 2231 { |
| 2222 Node* const i = var_i.value(); | 2232 Node* const i = var_i.value(); |
| 2223 a->GotoUnless(a->IntPtrLessThan(i, end), &create_result); | 2233 GotoUnless(IntPtrLessThan(i, end), &create_result); |
| 2224 | 2234 |
| 2225 ParameterMode mode = CodeStubAssembler::INTPTR_PARAMETERS; | 2235 ParameterMode mode = CodeStubAssembler::INTPTR_PARAMETERS; |
| 2226 Node* const elem = a->LoadFixedArrayElement(res_elems, i, 0, mode); | 2236 Node* const elem = LoadFixedArrayElement(res_elems, i, 0, mode); |
| 2227 | 2237 |
| 2228 CLabel if_issmi(a), if_isstring(a), loop_epilogue(a); | 2238 Label if_issmi(this), if_isstring(this), loop_epilogue(this); |
| 2229 a->Branch(a->TaggedIsSmi(elem), &if_issmi, &if_isstring); | 2239 Branch(TaggedIsSmi(elem), &if_issmi, &if_isstring); |
| 2230 | 2240 |
| 2231 a->Bind(&if_issmi); | 2241 Bind(&if_issmi); |
| 2232 { | 2242 { |
| 2233 // Integers represent slices of the original string. | 2243 // Integers represent slices of the original string. |
| 2234 CLabel if_isnegativeorzero(a), if_ispositive(a); | 2244 Label if_isnegativeorzero(this), if_ispositive(this); |
| 2235 a->BranchIfSmiLessThanOrEqual(elem, smi_zero, &if_isnegativeorzero, | 2245 BranchIfSmiLessThanOrEqual(elem, smi_zero, &if_isnegativeorzero, |
| 2236 &if_ispositive); | 2246 &if_ispositive); |
| 2237 | 2247 |
| 2238 a->Bind(&if_ispositive); | 2248 Bind(&if_ispositive); |
| 2239 { | 2249 { |
| 2240 Node* const int_elem = a->SmiUntag(elem); | 2250 Node* const int_elem = SmiUntag(elem); |
| 2241 Node* const new_match_start = | 2251 Node* const new_match_start = |
| 2242 a->IntPtrAdd(a->WordShr(int_elem, a->IntPtrConstant(11)), | 2252 IntPtrAdd(WordShr(int_elem, IntPtrConstant(11)), |
| 2243 a->WordAnd(int_elem, a->IntPtrConstant(0x7ff))); | 2253 WordAnd(int_elem, IntPtrConstant(0x7ff))); |
| 2244 var_match_start.Bind(a->SmiTag(new_match_start)); | 2254 var_match_start.Bind(SmiTag(new_match_start)); |
| 2245 a->Goto(&loop_epilogue); | 2255 Goto(&loop_epilogue); |
| 2246 } | 2256 } |
| 2247 | 2257 |
| 2248 a->Bind(&if_isnegativeorzero); | 2258 Bind(&if_isnegativeorzero); |
| 2249 { | 2259 { |
| 2250 Node* const next_i = a->IntPtrAdd(i, int_one); | 2260 Node* const next_i = IntPtrAdd(i, int_one); |
| 2251 var_i.Bind(next_i); | 2261 var_i.Bind(next_i); |
| 2252 | 2262 |
| 2253 Node* const next_elem = | 2263 Node* const next_elem = |
| 2254 a->LoadFixedArrayElement(res_elems, next_i, 0, mode); | 2264 LoadFixedArrayElement(res_elems, next_i, 0, mode); |
| 2255 | 2265 |
| 2256 Node* const new_match_start = a->SmiSub(next_elem, elem); | 2266 Node* const new_match_start = SmiSub(next_elem, elem); |
| 2257 var_match_start.Bind(new_match_start); | 2267 var_match_start.Bind(new_match_start); |
| 2258 a->Goto(&loop_epilogue); | 2268 Goto(&loop_epilogue); |
| 2259 } | 2269 } |
| 2260 } | 2270 } |
| 2261 | 2271 |
| 2262 a->Bind(&if_isstring); | 2272 Bind(&if_isstring); |
| 2263 { | 2273 { |
| 2264 CSA_ASSERT(a, a->IsStringInstanceType(a->LoadInstanceType(elem))); | 2274 CSA_ASSERT(this, IsStringInstanceType(LoadInstanceType(elem))); |
| 2265 | 2275 |
| 2266 Callable call_callable = CodeFactory::Call(isolate); | 2276 Callable call_callable = CodeFactory::Call(isolate); |
| 2267 Node* const replacement_obj = | 2277 Node* const replacement_obj = |
| 2268 a->CallJS(call_callable, context, replace_callable, undefined, elem, | 2278 CallJS(call_callable, context, replace_callable, undefined, elem, |
| 2269 var_match_start.value(), subject_string); | 2279 var_match_start.value(), string); |
| 2270 | 2280 |
| 2271 Node* const replacement_str = a->ToString(context, replacement_obj); | 2281 Node* const replacement_str = ToString(context, replacement_obj); |
| 2272 a->StoreFixedArrayElement(res_elems, i, replacement_str); | 2282 StoreFixedArrayElement(res_elems, i, replacement_str); |
| 2273 | 2283 |
| 2274 Node* const elem_length = a->LoadStringLength(elem); | 2284 Node* const elem_length = LoadStringLength(elem); |
| 2275 Node* const new_match_start = | 2285 Node* const new_match_start = |
| 2276 a->SmiAdd(var_match_start.value(), elem_length); | 2286 SmiAdd(var_match_start.value(), elem_length); |
| 2277 var_match_start.Bind(new_match_start); | 2287 var_match_start.Bind(new_match_start); |
| 2278 | 2288 |
| 2279 a->Goto(&loop_epilogue); | 2289 Goto(&loop_epilogue); |
| 2280 } | 2290 } |
| 2281 | 2291 |
| 2282 a->Bind(&loop_epilogue); | 2292 Bind(&loop_epilogue); |
| 2283 { | 2293 { |
| 2284 var_i.Bind(a->IntPtrAdd(var_i.value(), int_one)); | 2294 var_i.Bind(IntPtrAdd(var_i.value(), int_one)); |
| 2285 a->Goto(&loop); | 2295 Goto(&loop); |
| 2286 } | 2296 } |
| 2287 } | 2297 } |
| 2288 } | 2298 } |
| 2289 | 2299 |
| 2290 a->Bind(&if_hasexplicitcaptures); | 2300 Bind(&if_hasexplicitcaptures); |
| 2291 { | 2301 { |
| 2292 ParameterMode mode = CodeStubAssembler::INTPTR_PARAMETERS; | 2302 ParameterMode mode = CodeStubAssembler::INTPTR_PARAMETERS; |
| 2293 | 2303 |
| 2294 Node* const from = int_zero; | 2304 Node* const from = int_zero; |
| 2295 Node* const to = a->SmiUntag(res_length); | 2305 Node* const to = SmiUntag(res_length); |
| 2296 const int increment = 1; | 2306 const int increment = 1; |
| 2297 | 2307 |
| 2298 a->BuildFastLoop( | 2308 BuildFastLoop( |
| 2299 MachineType::PointerRepresentation(), from, to, | 2309 MachineType::PointerRepresentation(), from, to, |
| 2300 [res_elems, isolate, native_context, context, undefined, | 2310 [this, res_elems, isolate, native_context, context, undefined, |
| 2301 replace_callable, mode](CodeStubAssembler* a, Node* index) { | 2311 replace_callable, mode](CodeStubAssembler* a, Node* index) { |
| 2302 Node* const elem = | 2312 Node* const elem = LoadFixedArrayElement(res_elems, index, 0, mode); |
| 2303 a->LoadFixedArrayElement(res_elems, index, 0, mode); | 2313 |
| 2304 | 2314 Label do_continue(this); |
| 2305 CLabel do_continue(a); | 2315 GotoIf(TaggedIsSmi(elem), &do_continue); |
| 2306 a->GotoIf(a->TaggedIsSmi(elem), &do_continue); | |
| 2307 | 2316 |
| 2308 // elem must be an Array. | 2317 // elem must be an Array. |
| 2309 // Use the apply argument as backing for global RegExp properties. | 2318 // Use the apply argument as backing for global RegExp properties. |
| 2310 | 2319 |
| 2311 CSA_ASSERT(a, a->HasInstanceType(elem, JS_ARRAY_TYPE)); | 2320 CSA_ASSERT(this, HasInstanceType(elem, JS_ARRAY_TYPE)); |
| 2312 | 2321 |
| 2313 // TODO(jgruber): Remove indirection through Call->ReflectApply. | 2322 // TODO(jgruber): Remove indirection through Call->ReflectApply. |
| 2314 Callable call_callable = CodeFactory::Call(isolate); | 2323 Callable call_callable = CodeFactory::Call(isolate); |
| 2315 Node* const reflect_apply = a->LoadContextElement( | 2324 Node* const reflect_apply = |
| 2316 native_context, Context::REFLECT_APPLY_INDEX); | 2325 LoadContextElement(native_context, Context::REFLECT_APPLY_INDEX); |
| 2317 | 2326 |
| 2318 Node* const replacement_obj = | 2327 Node* const replacement_obj = |
| 2319 a->CallJS(call_callable, context, reflect_apply, undefined, | 2328 CallJS(call_callable, context, reflect_apply, undefined, |
| 2320 replace_callable, undefined, elem); | 2329 replace_callable, undefined, elem); |
| 2321 | 2330 |
| 2322 // Overwrite the i'th element in the results with the string we got | 2331 // Overwrite the i'th element in the results with the string we got |
| 2323 // back from the callback function. | 2332 // back from the callback function. |
| 2324 | 2333 |
| 2325 Node* const replacement_str = a->ToString(context, replacement_obj); | 2334 Node* const replacement_str = ToString(context, replacement_obj); |
| 2326 a->StoreFixedArrayElement(res_elems, index, replacement_str, | 2335 StoreFixedArrayElement(res_elems, index, replacement_str, |
| 2327 UPDATE_WRITE_BARRIER, 0, mode); | 2336 UPDATE_WRITE_BARRIER, 0, mode); |
| 2328 | 2337 |
| 2329 a->Goto(&do_continue); | 2338 Goto(&do_continue); |
| 2330 a->Bind(&do_continue); | 2339 Bind(&do_continue); |
| 2331 }, | 2340 }, |
| 2332 increment, CodeStubAssembler::IndexAdvanceMode::kPost); | 2341 increment, CodeStubAssembler::IndexAdvanceMode::kPost); |
| 2333 | 2342 |
| 2334 a->Goto(&create_result); | 2343 Goto(&create_result); |
| 2335 } | 2344 } |
| 2336 | 2345 |
| 2337 a->Bind(&create_result); | 2346 Bind(&create_result); |
| 2338 { | 2347 { |
| 2339 Node* const result = a->CallRuntime(Runtime::kStringBuilderConcat, context, | 2348 Node* const result = CallRuntime(Runtime::kStringBuilderConcat, context, |
| 2340 res, res_length, subject_string); | 2349 res, res_length, string); |
| 2341 var_result.Bind(result); | 2350 var_result.Bind(result); |
| 2342 a->Goto(&out); | 2351 Goto(&out); |
| 2343 } | 2352 } |
| 2344 | 2353 |
| 2345 a->Bind(&out); | 2354 Bind(&out); |
| 2346 return var_result.value(); | 2355 return var_result.value(); |
| 2347 } | 2356 } |
| 2348 | 2357 |
| 2349 Node* ReplaceSimpleStringFastPath(CodeStubAssembler* a, Node* context, | 2358 Node* RegExpBuiltinsAssembler::ReplaceSimpleStringFastPath( |
| 2350 Node* regexp, Node* subject_string, | 2359 Node* context, Node* regexp, Node* string, Node* replace_string) { |
| 2351 Node* replace_string) { | |
| 2352 // The fast path is reached only if {receiver} is an unmodified | 2360 // The fast path is reached only if {receiver} is an unmodified |
| 2353 // JSRegExp instance, {replace_value} is non-callable, and | 2361 // JSRegExp instance, {replace_value} is non-callable, and |
| 2354 // ToString({replace_value}) does not contain '$', i.e. we're doing a simple | 2362 // ToString({replace_value}) does not contain '$', i.e. we're doing a simple |
| 2355 // string replacement. | 2363 // string replacement. |
| 2356 | 2364 |
| 2357 Isolate* const isolate = a->isolate(); | 2365 Isolate* const isolate = this->isolate(); |
| 2358 | 2366 |
| 2359 Node* const null = a->NullConstant(); | 2367 Node* const null = NullConstant(); |
| 2360 Node* const int_zero = a->IntPtrConstant(0); | 2368 Node* const int_zero = IntPtrConstant(0); |
| 2361 Node* const smi_zero = a->SmiConstant(Smi::kZero); | 2369 Node* const smi_zero = SmiConstant(Smi::kZero); |
| 2362 | 2370 |
| 2363 CLabel out(a); | 2371 Label out(this); |
| 2364 CVariable var_result(a, MachineRepresentation::kTagged); | 2372 Variable var_result(this, MachineRepresentation::kTagged); |
| 2365 | 2373 |
| 2366 // Load the last match info. | 2374 // Load the last match info. |
| 2367 Node* const native_context = a->LoadNativeContext(context); | 2375 Node* const native_context = LoadNativeContext(context); |
| 2368 Node* const last_match_info = a->LoadContextElement( | 2376 Node* const last_match_info = |
| 2369 native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX); | 2377 LoadContextElement(native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX); |
| 2370 | 2378 |
| 2371 // Is {regexp} global? | 2379 // Is {regexp} global? |
| 2372 CLabel if_isglobal(a), if_isnonglobal(a); | 2380 Label if_isglobal(this), if_isnonglobal(this); |
| 2373 Node* const flags = a->LoadObjectField(regexp, JSRegExp::kFlagsOffset); | 2381 Node* const flags = LoadObjectField(regexp, JSRegExp::kFlagsOffset); |
| 2374 Node* const is_global = | 2382 Node* const is_global = |
| 2375 a->WordAnd(a->SmiUntag(flags), a->IntPtrConstant(JSRegExp::kGlobal)); | 2383 WordAnd(SmiUntag(flags), IntPtrConstant(JSRegExp::kGlobal)); |
| 2376 a->Branch(a->WordEqual(is_global, int_zero), &if_isnonglobal, &if_isglobal); | 2384 Branch(WordEqual(is_global, int_zero), &if_isnonglobal, &if_isglobal); |
| 2377 | 2385 |
| 2378 a->Bind(&if_isglobal); | 2386 Bind(&if_isglobal); |
| 2379 { | 2387 { |
| 2380 // Hand off global regexps to runtime. | 2388 // Hand off global regexps to runtime. |
| 2381 FastStoreLastIndex(a, regexp, smi_zero); | 2389 FastStoreLastIndex(regexp, smi_zero); |
| 2382 Node* const result = | 2390 Node* const result = |
| 2383 a->CallRuntime(Runtime::kStringReplaceGlobalRegExpWithString, context, | 2391 CallRuntime(Runtime::kStringReplaceGlobalRegExpWithString, context, |
| 2384 subject_string, regexp, replace_string, last_match_info); | 2392 string, regexp, replace_string, last_match_info); |
| 2385 var_result.Bind(result); | 2393 var_result.Bind(result); |
| 2386 a->Goto(&out); | 2394 Goto(&out); |
| 2387 } | 2395 } |
| 2388 | 2396 |
| 2389 a->Bind(&if_isnonglobal); | 2397 Bind(&if_isnonglobal); |
| 2390 { | 2398 { |
| 2391 // Run exec, then manually construct the resulting string. | 2399 // Run exec, then manually construct the resulting string. |
| 2392 Callable exec_callable = CodeFactory::RegExpExec(isolate); | 2400 Callable exec_callable = CodeFactory::RegExpExec(isolate); |
| 2393 Node* const match_indices = | 2401 Node* const match_indices = CallStub(exec_callable, context, regexp, string, |
| 2394 a->CallStub(exec_callable, context, regexp, subject_string, smi_zero, | 2402 smi_zero, last_match_info); |
| 2395 last_match_info); | 2403 |
| 2396 | 2404 Label if_matched(this), if_didnotmatch(this); |
| 2397 CLabel if_matched(a), if_didnotmatch(a); | 2405 Branch(WordEqual(match_indices, null), &if_didnotmatch, &if_matched); |
| 2398 a->Branch(a->WordEqual(match_indices, null), &if_didnotmatch, &if_matched); | 2406 |
| 2399 | 2407 Bind(&if_didnotmatch); |
| 2400 a->Bind(&if_didnotmatch); | |
| 2401 { | 2408 { |
| 2402 FastStoreLastIndex(a, regexp, smi_zero); | 2409 FastStoreLastIndex(regexp, smi_zero); |
| 2403 var_result.Bind(subject_string); | 2410 var_result.Bind(string); |
| 2404 a->Goto(&out); | 2411 Goto(&out); |
| 2405 } | 2412 } |
| 2406 | 2413 |
| 2407 a->Bind(&if_matched); | 2414 Bind(&if_matched); |
| 2408 { | 2415 { |
| 2409 Node* const subject_start = smi_zero; | 2416 Node* const subject_start = smi_zero; |
| 2410 Node* const match_start = a->LoadFixedArrayElement( | 2417 Node* const match_start = LoadFixedArrayElement( |
| 2411 match_indices, RegExpMatchInfo::kFirstCaptureIndex); | 2418 match_indices, RegExpMatchInfo::kFirstCaptureIndex); |
| 2412 Node* const match_end = a->LoadFixedArrayElement( | 2419 Node* const match_end = LoadFixedArrayElement( |
| 2413 match_indices, RegExpMatchInfo::kFirstCaptureIndex + 1); | 2420 match_indices, RegExpMatchInfo::kFirstCaptureIndex + 1); |
| 2414 Node* const subject_end = a->LoadStringLength(subject_string); | 2421 Node* const subject_end = LoadStringLength(string); |
| 2415 | 2422 |
| 2416 CLabel if_replaceisempty(a), if_replaceisnotempty(a); | 2423 Label if_replaceisempty(this), if_replaceisnotempty(this); |
| 2417 Node* const replace_length = a->LoadStringLength(replace_string); | 2424 Node* const replace_length = LoadStringLength(replace_string); |
| 2418 a->Branch(a->SmiEqual(replace_length, smi_zero), &if_replaceisempty, | 2425 Branch(SmiEqual(replace_length, smi_zero), &if_replaceisempty, |
| 2419 &if_replaceisnotempty); | 2426 &if_replaceisnotempty); |
| 2420 | 2427 |
| 2421 a->Bind(&if_replaceisempty); | 2428 Bind(&if_replaceisempty); |
| 2422 { | 2429 { |
| 2423 // TODO(jgruber): We could skip many of the checks that using SubString | 2430 // TODO(jgruber): We could skip many of the checks that using SubString |
| 2424 // here entails. | 2431 // here entails. |
| 2425 | 2432 |
| 2426 Node* const first_part = | 2433 Node* const first_part = |
| 2427 a->SubString(context, subject_string, subject_start, match_start); | 2434 SubString(context, string, subject_start, match_start); |
| 2428 Node* const second_part = | 2435 Node* const second_part = |
| 2429 a->SubString(context, subject_string, match_end, subject_end); | 2436 SubString(context, string, match_end, subject_end); |
| 2430 | 2437 |
| 2431 Node* const result = a->StringAdd(context, first_part, second_part); | 2438 Node* const result = StringAdd(context, first_part, second_part); |
| 2432 var_result.Bind(result); | 2439 var_result.Bind(result); |
| 2433 a->Goto(&out); | 2440 Goto(&out); |
| 2434 } | 2441 } |
| 2435 | 2442 |
| 2436 a->Bind(&if_replaceisnotempty); | 2443 Bind(&if_replaceisnotempty); |
| 2437 { | 2444 { |
| 2438 Node* const first_part = | 2445 Node* const first_part = |
| 2439 a->SubString(context, subject_string, subject_start, match_start); | 2446 SubString(context, string, subject_start, match_start); |
| 2440 Node* const second_part = replace_string; | 2447 Node* const second_part = replace_string; |
| 2441 Node* const third_part = | 2448 Node* const third_part = |
| 2442 a->SubString(context, subject_string, match_end, subject_end); | 2449 SubString(context, string, match_end, subject_end); |
| 2443 | 2450 |
| 2444 Node* result = a->StringAdd(context, first_part, second_part); | 2451 Node* result = StringAdd(context, first_part, second_part); |
| 2445 result = a->StringAdd(context, result, third_part); | 2452 result = StringAdd(context, result, third_part); |
| 2446 | 2453 |
| 2447 var_result.Bind(result); | 2454 var_result.Bind(result); |
| 2448 a->Goto(&out); | 2455 Goto(&out); |
| 2449 } | 2456 } |
| 2450 } | 2457 } |
| 2451 } | 2458 } |
| 2452 | 2459 |
| 2453 a->Bind(&out); | 2460 Bind(&out); |
| 2454 return var_result.value(); | 2461 return var_result.value(); |
| 2455 } | 2462 } |
| 2456 | 2463 |
| 2457 } // namespace | |
| 2458 | |
| 2459 // ES#sec-regexp.prototype-@@replace | 2464 // ES#sec-regexp.prototype-@@replace |
| 2460 // RegExp.prototype [ @@replace ] ( string, replaceValue ) | 2465 // RegExp.prototype [ @@replace ] ( string, replaceValue ) |
| 2461 TF_BUILTIN(RegExpPrototypeReplace, RegExpBuiltinsAssembler) { | 2466 TF_BUILTIN(RegExpPrototypeReplace, RegExpBuiltinsAssembler) { |
| 2462 Isolate* const isolate = this->isolate(); | |
| 2463 | |
| 2464 Node* const maybe_receiver = Parameter(0); | 2467 Node* const maybe_receiver = Parameter(0); |
| 2465 Node* const maybe_string = Parameter(1); | 2468 Node* const maybe_string = Parameter(1); |
| 2466 Node* const replace_value = Parameter(2); | 2469 Node* const replace_value = Parameter(2); |
| 2467 Node* const context = Parameter(5); | 2470 Node* const context = Parameter(5); |
| 2468 | 2471 |
| 2469 Node* const int_zero = IntPtrConstant(0); | 2472 Node* const int_zero = IntPtrConstant(0); |
| 2470 | 2473 |
| 2471 // Ensure {maybe_receiver} is a JSReceiver. | 2474 // Ensure {maybe_receiver} is a JSReceiver. |
| 2472 Node* const map = | 2475 Node* const map = ThrowIfNotJSReceiver( |
| 2473 ThrowIfNotJSReceiver(this, isolate, context, maybe_receiver, | 2476 context, maybe_receiver, MessageTemplate::kIncompatibleMethodReceiver, |
| 2474 MessageTemplate::kIncompatibleMethodReceiver, | 2477 "RegExp.prototype.@@replace"); |
| 2475 "RegExp.prototype.@@replace"); | |
| 2476 Node* const receiver = maybe_receiver; | 2478 Node* const receiver = maybe_receiver; |
| 2477 | 2479 |
| 2478 // Convert {maybe_string} to a String. | 2480 // Convert {maybe_string} to a String. |
| 2479 Callable tostring_callable = CodeFactory::ToString(isolate); | 2481 Callable tostring_callable = CodeFactory::ToString(isolate()); |
| 2480 Node* const string = CallStub(tostring_callable, context, maybe_string); | 2482 Node* const string = CallStub(tostring_callable, context, maybe_string); |
| 2481 | 2483 |
| 2482 // Fast-path checks: 1. Is the {receiver} an unmodified JSRegExp instance? | 2484 // Fast-path checks: 1. Is the {receiver} an unmodified JSRegExp instance? |
| 2483 CLabel checkreplacecallable(this), runtime(this, CLabel::kDeferred), | 2485 Label checkreplacecallable(this), runtime(this, Label::kDeferred), |
| 2484 fastpath(this); | 2486 fastpath(this); |
| 2485 BranchIfFastPath(this, context, map, &checkreplacecallable, &runtime); | 2487 BranchIfFastRegExp(context, map, &checkreplacecallable, &runtime); |
| 2486 | 2488 |
| 2487 Bind(&checkreplacecallable); | 2489 Bind(&checkreplacecallable); |
| 2488 Node* const regexp = receiver; | 2490 Node* const regexp = receiver; |
| 2489 | 2491 |
| 2490 // 2. Is {replace_value} callable? | 2492 // 2. Is {replace_value} callable? |
| 2491 CLabel checkreplacestring(this), if_iscallable(this); | 2493 Label checkreplacestring(this), if_iscallable(this); |
| 2492 GotoIf(TaggedIsSmi(replace_value), &checkreplacestring); | 2494 GotoIf(TaggedIsSmi(replace_value), &checkreplacestring); |
| 2493 | 2495 |
| 2494 Node* const replace_value_map = LoadMap(replace_value); | 2496 Node* const replace_value_map = LoadMap(replace_value); |
| 2495 Branch(IsCallableMap(replace_value_map), &if_iscallable, &checkreplacestring); | 2497 Branch(IsCallableMap(replace_value_map), &if_iscallable, &checkreplacestring); |
| 2496 | 2498 |
| 2497 // 3. Does ToString({replace_value}) contain '$'? | 2499 // 3. Does ToString({replace_value}) contain '$'? |
| 2498 Bind(&checkreplacestring); | 2500 Bind(&checkreplacestring); |
| 2499 { | 2501 { |
| 2500 Node* const replace_string = | 2502 Node* const replace_string = |
| 2501 CallStub(tostring_callable, context, replace_value); | 2503 CallStub(tostring_callable, context, replace_value); |
| 2502 | 2504 |
| 2503 Node* const dollar_char = IntPtrConstant('$'); | 2505 Node* const dollar_char = IntPtrConstant('$'); |
| 2504 Node* const smi_minusone = SmiConstant(Smi::FromInt(-1)); | 2506 Node* const smi_minusone = SmiConstant(Smi::FromInt(-1)); |
| 2505 GotoUnless(SmiEqual(StringIndexOfChar(context, replace_string, dollar_char, | 2507 GotoUnless(SmiEqual(StringIndexOfChar(context, replace_string, dollar_char, |
| 2506 int_zero), | 2508 int_zero), |
| 2507 smi_minusone), | 2509 smi_minusone), |
| 2508 &runtime); | 2510 &runtime); |
| 2509 | 2511 |
| 2510 Return(ReplaceSimpleStringFastPath(this, context, regexp, string, | 2512 Return( |
| 2511 replace_string)); | 2513 ReplaceSimpleStringFastPath(context, regexp, string, replace_string)); |
| 2512 } | 2514 } |
| 2513 | 2515 |
| 2514 // {regexp} is unmodified and {replace_value} is callable. | 2516 // {regexp} is unmodified and {replace_value} is callable. |
| 2515 Bind(&if_iscallable); | 2517 Bind(&if_iscallable); |
| 2516 { | 2518 { |
| 2517 Node* const replace_callable = replace_value; | 2519 Node* const replace_callable = replace_value; |
| 2518 | 2520 |
| 2519 // Check if the {regexp} is global. | 2521 // Check if the {regexp} is global. |
| 2520 CLabel if_isglobal(this), if_isnotglobal(this); | 2522 Label if_isglobal(this), if_isnotglobal(this); |
| 2521 Node* const is_global = FastFlagGetter(this, regexp, JSRegExp::kGlobal); | 2523 Node* const is_global = FastFlagGetter(regexp, JSRegExp::kGlobal); |
| 2522 Branch(is_global, &if_isglobal, &if_isnotglobal); | 2524 Branch(is_global, &if_isglobal, &if_isnotglobal); |
| 2523 | 2525 |
| 2524 Bind(&if_isglobal); | 2526 Bind(&if_isglobal); |
| 2525 { | 2527 { |
| 2526 Node* const result = ReplaceGlobalCallableFastPath( | 2528 Node* const result = ReplaceGlobalCallableFastPath( |
| 2527 this, context, regexp, string, replace_callable); | 2529 context, regexp, string, replace_callable); |
| 2528 Return(result); | 2530 Return(result); |
| 2529 } | 2531 } |
| 2530 | 2532 |
| 2531 Bind(&if_isnotglobal); | 2533 Bind(&if_isnotglobal); |
| 2532 { | 2534 { |
| 2533 Node* const result = | 2535 Node* const result = |
| 2534 CallRuntime(Runtime::kStringReplaceNonGlobalRegExpWithFunction, | 2536 CallRuntime(Runtime::kStringReplaceNonGlobalRegExpWithFunction, |
| 2535 context, string, regexp, replace_callable); | 2537 context, string, regexp, replace_callable); |
| 2536 Return(result); | 2538 Return(result); |
| 2537 } | 2539 } |
| 2538 } | 2540 } |
| 2539 | 2541 |
| 2540 Bind(&runtime); | 2542 Bind(&runtime); |
| 2541 { | 2543 { |
| 2542 Node* const result = CallRuntime(Runtime::kRegExpReplace, context, receiver, | 2544 Node* const result = CallRuntime(Runtime::kRegExpReplace, context, receiver, |
| 2543 string, replace_value); | 2545 string, replace_value); |
| 2544 Return(result); | 2546 Return(result); |
| 2545 } | 2547 } |
| 2546 } | 2548 } |
| 2547 | 2549 |
| 2548 // Simple string matching functionality for internal use which does not modify | 2550 // Simple string matching functionality for internal use which does not modify |
| 2549 // the last match info. | 2551 // the last match info. |
| 2550 TF_BUILTIN(RegExpInternalMatch, RegExpBuiltinsAssembler) { | 2552 TF_BUILTIN(RegExpInternalMatch, RegExpBuiltinsAssembler) { |
| 2551 Isolate* const isolate = this->isolate(); | |
| 2552 | |
| 2553 Node* const regexp = Parameter(1); | 2553 Node* const regexp = Parameter(1); |
| 2554 Node* const string = Parameter(2); | 2554 Node* const string = Parameter(2); |
| 2555 Node* const context = Parameter(5); | 2555 Node* const context = Parameter(5); |
| 2556 | 2556 |
| 2557 Node* const null = NullConstant(); | 2557 Node* const null = NullConstant(); |
| 2558 Node* const smi_zero = SmiConstant(Smi::FromInt(0)); | 2558 Node* const smi_zero = SmiConstant(Smi::FromInt(0)); |
| 2559 | 2559 |
| 2560 Node* const native_context = LoadNativeContext(context); | 2560 Node* const native_context = LoadNativeContext(context); |
| 2561 Node* const internal_match_info = LoadContextElement( | 2561 Node* const internal_match_info = LoadContextElement( |
| 2562 native_context, Context::REGEXP_INTERNAL_MATCH_INFO_INDEX); | 2562 native_context, Context::REGEXP_INTERNAL_MATCH_INFO_INDEX); |
| 2563 | 2563 |
| 2564 Callable exec_callable = CodeFactory::RegExpExec(isolate); | 2564 Callable exec_callable = CodeFactory::RegExpExec(isolate()); |
| 2565 Node* const match_indices = CallStub(exec_callable, context, regexp, string, | 2565 Node* const match_indices = CallStub(exec_callable, context, regexp, string, |
| 2566 smi_zero, internal_match_info); | 2566 smi_zero, internal_match_info); |
| 2567 | 2567 |
| 2568 CLabel if_matched(this), if_didnotmatch(this); | 2568 Label if_matched(this), if_didnotmatch(this); |
| 2569 Branch(WordEqual(match_indices, null), &if_didnotmatch, &if_matched); | 2569 Branch(WordEqual(match_indices, null), &if_didnotmatch, &if_matched); |
| 2570 | 2570 |
| 2571 Bind(&if_didnotmatch); | 2571 Bind(&if_didnotmatch); |
| 2572 Return(null); | 2572 Return(null); |
| 2573 | 2573 |
| 2574 Bind(&if_matched); | 2574 Bind(&if_matched); |
| 2575 { | 2575 { |
| 2576 Node* result = ConstructNewResultFromMatchInfo(isolate, this, context, | 2576 Node* result = |
| 2577 match_indices, string); | 2577 ConstructNewResultFromMatchInfo(context, match_indices, string); |
| 2578 Return(result); | 2578 Return(result); |
| 2579 } | 2579 } |
| 2580 } | 2580 } |
| 2581 | 2581 |
| 2582 } // namespace internal | 2582 } // namespace internal |
| 2583 } // namespace v8 | 2583 } // namespace v8 |
| OLD | NEW |