Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 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 | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "src/builtins/builtins-utils.h" | |
| 6 #include "src/builtins/builtins.h" | |
| 7 | |
| 8 #include "src/code-factory.h" | |
| 9 #include "src/regexp/jsregexp.h" | |
| 10 | |
| 11 namespace v8 { | |
| 12 namespace internal { | |
| 13 | |
| 14 namespace { | |
| 15 | |
| 16 compiler::Node* LoadLastIndex(CodeStubAssembler* a, compiler::Node* context, | |
| 17 compiler::Node* has_initialmap, | |
| 18 compiler::Node* regexp) { | |
| 19 typedef CodeStubAssembler::Variable Variable; | |
| 20 typedef CodeStubAssembler::Label Label; | |
| 21 typedef compiler::Node Node; | |
| 22 | |
| 23 Variable var_value(a, MachineRepresentation::kTagged); | |
| 24 | |
| 25 Label out(a), if_unmodified(a), if_modified(a, Label::kDeferred); | |
| 26 a->Branch(has_initialmap, &if_unmodified, &if_modified); | |
| 27 | |
| 28 a->Bind(&if_unmodified); | |
| 29 { | |
| 30 // Load the in-object field. | |
| 31 static const int field_offset = | |
| 32 JSRegExp::kSize + JSRegExp::kLastIndexFieldIndex; | |
|
Igor Sheludko
2016/09/29 12:55:29
kLastIndexFieldIndex is an index, but not an offse
jgruber
2016/09/29 14:40:26
Done.
| |
| 33 var_value.Bind(a->LoadObjectField(regexp, field_offset)); | |
| 34 a->Goto(&out); | |
| 35 } | |
| 36 | |
| 37 a->Bind(&if_modified); | |
| 38 { | |
| 39 // Load through runtime. | |
| 40 Node* const name = | |
| 41 a->HeapConstant(a->isolate()->factory()->last_index_string()); | |
| 42 var_value.Bind( | |
| 43 a->CallRuntime(Runtime::kGetProperty, context, regexp, name)); | |
|
Igor Sheludko
2016/09/29 12:55:28
You can call GetPropertyStub here instead.
jgruber
2016/09/29 14:40:26
Done.
| |
| 44 a->Goto(&out); | |
| 45 } | |
| 46 | |
| 47 a->Bind(&out); | |
| 48 return var_value.value(); | |
| 49 } | |
| 50 | |
| 51 void StoreLastIndex(CodeStubAssembler* a, compiler::Node* context, | |
| 52 compiler::Node* has_initialmap, compiler::Node* regexp, | |
| 53 compiler::Node* value) { | |
| 54 typedef CodeStubAssembler::Label Label; | |
| 55 typedef compiler::Node Node; | |
| 56 | |
| 57 Label out(a), if_unmodified(a), if_modified(a, Label::kDeferred); | |
| 58 a->Branch(has_initialmap, &if_unmodified, &if_modified); | |
| 59 | |
| 60 a->Bind(&if_unmodified); | |
| 61 { | |
| 62 static const int field_offset = | |
| 63 JSRegExp::kSize + JSRegExp::kLastIndexFieldIndex; | |
|
Igor Sheludko
2016/09/29 12:55:28
Same here.
jgruber
2016/09/29 14:40:26
Done.
| |
| 64 a->StoreObjectField(regexp, field_offset, value); | |
| 65 a->Goto(&out); | |
| 66 } | |
| 67 | |
| 68 a->Bind(&if_modified); | |
| 69 { | |
| 70 // Store through runtime. | |
| 71 Node* const name = | |
| 72 a->HeapConstant(a->isolate()->factory()->last_index_string()); | |
| 73 Node* const language_mode = a->SmiConstant(Smi::FromInt(STRICT)); | |
| 74 a->CallRuntime(Runtime::kSetProperty, context, regexp, name, value, | |
|
Igor Sheludko
2016/09/29 12:55:29
// TODO(ishell): Use SetPropertyStub here once ava
jgruber
2016/09/29 14:40:26
Done.
| |
| 75 language_mode); | |
| 76 a->Goto(&out); | |
| 77 } | |
| 78 | |
| 79 a->Bind(&out); | |
| 80 } | |
| 81 | |
| 82 compiler::Node* ConstructNewResultFromMatchInfo(Isolate* isolate, | |
| 83 CodeStubAssembler* a, | |
| 84 compiler::Node* context, | |
| 85 compiler::Node* match_elements, | |
| 86 compiler::Node* string) { | |
| 87 typedef CodeStubAssembler::Variable Variable; | |
| 88 typedef CodeStubAssembler::Label Label; | |
| 89 typedef compiler::Node Node; | |
| 90 | |
| 91 Label out(a); | |
| 92 | |
| 93 Callable substring_callable = CodeFactory::SubString(isolate); | |
|
Benedikt Meurer
2016/09/29 11:10:08
Nit: inline SubString here.
jgruber
2016/09/29 14:40:26
Done.
| |
| 94 Callable constructresult_callable = | |
|
Benedikt Meurer
2016/09/29 11:10:08
Nit: Follow-up CL, please port construct result to
jgruber
2016/09/29 14:40:27
Acknowledged.
| |
| 95 CodeFactory::RegExpConstructResult(isolate); | |
| 96 | |
| 97 Node* const num_indices = a->SmiUntag(a->LoadFixedArrayElement( | |
|
Igor Sheludko
2016/09/29 12:55:28
Once you avoid mixing int32/intptr indices, please
jgruber
2016/09/29 14:40:27
Done.
| |
| 98 match_elements, a->IntPtrConstant(RegExpImpl::kLastCaptureCount))); | |
| 99 Node* const num_results = a->SmiTag(a->Word32Shr(num_indices, 1)); | |
|
Igor Sheludko
2016/09/29 12:55:28
WordShr
jgruber
2016/09/29 14:40:26
Done.
| |
| 100 Node* const start = a->LoadFixedArrayElement( | |
| 101 match_elements, a->IntPtrConstant(RegExpImpl::kFirstCapture)); | |
| 102 Node* const end = a->LoadFixedArrayElement( | |
| 103 match_elements, a->IntPtrConstant(RegExpImpl::kFirstCapture + 1)); | |
| 104 | |
| 105 // Calculate the substring of the first match before creating the result array | |
| 106 // to avoid an unnecessary write barrier storing the first result. | |
| 107 Node* const first = | |
| 108 a->CallStub(substring_callable, context, string, start, end); | |
| 109 | |
| 110 Node* const result = a->CallStub(constructresult_callable, context, | |
| 111 num_results, start, string); | |
| 112 Node* const result_elements = a->LoadElements(result); | |
| 113 | |
| 114 a->StoreFixedArrayElement(result_elements, a->IntPtrConstant(0), first, | |
| 115 SKIP_WRITE_BARRIER); | |
| 116 | |
| 117 a->GotoIf(a->SmiEqual(num_results, a->SmiConstant(Smi::FromInt(1))), &out); | |
| 118 | |
| 119 // Store all remaining captures. | |
| 120 Node* const limit = | |
| 121 a->IntPtrAdd(a->IntPtrConstant(RegExpImpl::kFirstCapture), num_indices); | |
| 122 | |
| 123 Variable var_from_cursor(a, MachineRepresentation::kWord32); | |
|
Igor Sheludko
2016/09/29 12:55:28
MachineType::PointerRepesentation() (which is intp
jgruber
2016/09/29 14:40:26
Done.
| |
| 124 Variable var_to_cursor(a, MachineRepresentation::kWord32); | |
|
Igor Sheludko
2016/09/29 12:55:28
Same here.
jgruber
2016/09/29 14:40:27
Done.
| |
| 125 | |
| 126 var_from_cursor.Bind(a->IntPtrConstant(RegExpImpl::kFirstCapture + 2)); | |
| 127 var_to_cursor.Bind(a->IntPtrConstant(1)); | |
| 128 | |
| 129 Variable* vars[] = {&var_from_cursor, &var_to_cursor}; | |
| 130 Label loop(a, 2, vars); | |
| 131 | |
| 132 a->Goto(&loop); | |
| 133 a->Bind(&loop); | |
| 134 { | |
| 135 Node* from_cursor = var_from_cursor.value(); | |
|
Igor Sheludko
2016/09/29 12:55:28
const?
jgruber
2016/09/29 14:40:26
Done.
| |
| 136 Node* const to_cursor = var_to_cursor.value(); | |
| 137 Node* const start = a->LoadFixedArrayElement(match_elements, from_cursor); | |
| 138 | |
| 139 Label next_iter(a); | |
| 140 a->GotoIf(a->SmiEqual(start, a->SmiConstant(Smi::FromInt(-1))), &next_iter); | |
| 141 | |
| 142 Node* from_cursor_plus1 = a->IntPtrAdd(from_cursor, a->IntPtrConstant(1)); | |
|
Igor Sheludko
2016/09/29 12:55:28
const?
jgruber
2016/09/29 14:40:26
Done.
| |
| 143 Node* const end = | |
| 144 a->LoadFixedArrayElement(match_elements, from_cursor_plus1); | |
| 145 | |
| 146 Node* const capture = | |
| 147 a->CallStub(substring_callable, context, string, start, end); | |
| 148 a->StoreFixedArrayElement(result_elements, to_cursor, capture); | |
| 149 a->Goto(&next_iter); | |
| 150 | |
| 151 a->Bind(&next_iter); | |
| 152 var_from_cursor.Bind(a->IntPtrAdd(from_cursor, a->IntPtrConstant(2))); | |
| 153 var_to_cursor.Bind(a->IntPtrAdd(to_cursor, a->IntPtrConstant(1))); | |
| 154 a->Branch(a->IntPtrLessThan(var_from_cursor.value(), limit), &loop, &out); | |
|
Igor Sheludko
2016/09/29 12:55:28
Since we don't expect negative indices here, I wou
jgruber
2016/09/29 14:40:26
Done.
| |
| 155 } | |
| 156 | |
| 157 a->Bind(&out); | |
| 158 return result; | |
| 159 } | |
| 160 | |
| 161 } // namespace | |
| 162 | |
| 163 // ES#sec-regexp.prototype.exec | |
| 164 // RegExp.prototype.exec ( string ) | |
| 165 void Builtins::Generate_RegExpPrototypeExec(CodeStubAssembler* a) { | |
| 166 typedef CodeStubAssembler::Variable Variable; | |
| 167 typedef CodeStubAssembler::Label Label; | |
| 168 typedef compiler::Node Node; | |
| 169 | |
| 170 Isolate* const isolate = a->isolate(); | |
| 171 | |
| 172 Node* const receiver = a->Parameter(0); | |
| 173 Node* const maybe_string = a->Parameter(1); | |
| 174 Node* const context = a->Parameter(4); | |
| 175 | |
| 176 Node* const null = a->NullConstant(); | |
| 177 Node* const int_zero = a->Int32Constant(0); | |
| 178 Node* const smi_zero = a->SmiConstant(Smi::FromInt(0)); | |
| 179 | |
| 180 // Ensure {receiver} is a JSRegExp. | |
| 181 Node* const regexp_map = a->ThrowIfNotType(context, receiver, JS_REGEXP_TYPE, | |
| 182 "RegExp.prototype.exec"); | |
| 183 Node* const regexp = receiver; | |
| 184 | |
| 185 // Check whether the regexp instance is unmodified. | |
| 186 Node* const native_context = a->LoadNativeContext(context); | |
| 187 Node* const regexp_fun = | |
| 188 a->LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX); | |
| 189 Node* const initial_map = | |
| 190 a->LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset); | |
| 191 Node* const has_initialmap = a->WordEqual(regexp_map, initial_map); | |
| 192 | |
| 193 // Convert {maybe_string} to a string. | |
| 194 Callable tostring_callable = CodeFactory::ToString(isolate); | |
| 195 Node* const string = a->CallStub(tostring_callable, context, maybe_string); | |
| 196 Node* const string_length = a->LoadStringLength(string); | |
| 197 | |
| 198 // Check whether the regexp is global or sticky, which determines whether we | |
| 199 // update last index later on. | |
| 200 Node* const flags = a->LoadObjectField(regexp, JSRegExp::kFlagsOffset); | |
| 201 Node* const is_global_or_sticky = | |
| 202 a->Word32And(a->SmiUntag(flags), | |
|
Igor Sheludko
2016/09/29 12:55:29
You are mixing int32/intptr computations here. I t
jgruber
2016/09/29 14:40:26
Done.
| |
| 203 a->Int32Constant(JSRegExp::kGlobal | JSRegExp::kSticky)); | |
| 204 Node* const should_update_last_index = | |
| 205 a->Word32NotEqual(is_global_or_sticky, int_zero); | |
| 206 | |
| 207 // Grab and possibly update last index. | |
| 208 Label run_exec(a); | |
| 209 Variable var_lastindex(a, MachineRepresentation::kTagged); | |
| 210 { | |
| 211 Label if_doupdate(a), if_dontupdate(a); | |
| 212 a->Branch(should_update_last_index, &if_doupdate, &if_dontupdate); | |
| 213 | |
| 214 a->Bind(&if_doupdate); | |
| 215 { | |
| 216 Node* const regexp_lastindex = | |
| 217 LoadLastIndex(a, context, has_initialmap, regexp); | |
| 218 | |
| 219 Callable tolength_callable = CodeFactory::ToLength(isolate); | |
| 220 Node* const lastindex = | |
| 221 a->CallStub(tolength_callable, context, regexp_lastindex); | |
| 222 var_lastindex.Bind(lastindex); | |
| 223 | |
| 224 Label if_isoob(a, Label::kDeferred); | |
| 225 a->GotoUnless(a->WordIsSmi(lastindex), &if_isoob); | |
| 226 a->GotoUnless(a->SmiLessThanOrEqual(lastindex, string_length), &if_isoob); | |
| 227 a->Goto(&run_exec); | |
| 228 | |
| 229 a->Bind(&if_isoob); | |
| 230 { | |
| 231 StoreLastIndex(a, context, has_initialmap, regexp, smi_zero); | |
| 232 a->Return(null); | |
| 233 } | |
| 234 } | |
| 235 | |
| 236 a->Bind(&if_dontupdate); | |
| 237 { | |
| 238 var_lastindex.Bind(smi_zero); | |
| 239 a->Goto(&run_exec); | |
| 240 } | |
| 241 } | |
| 242 | |
| 243 Node* match_indices; | |
| 244 Label successful_match(a); | |
| 245 a->Bind(&run_exec); | |
| 246 { | |
| 247 // Get last match info from the context. | |
| 248 Node* const native_context = a->LoadNativeContext(context); | |
|
Igor Sheludko
2016/09/29 12:55:28
|native_context| loaded above should be still avai
jgruber
2016/09/29 14:40:27
Done.
| |
| 249 Node* const last_match_info = a->LoadContextElement( | |
| 250 native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX); | |
| 251 | |
| 252 // Call the exec stub. | |
| 253 Callable exec_callable = CodeFactory::RegExpExec(isolate); | |
| 254 match_indices = a->CallStub(exec_callable, context, regexp, string, | |
| 255 var_lastindex.value(), last_match_info); | |
| 256 | |
| 257 // {match_indices} is either null or the RegExpLastMatchInfo array. | |
| 258 // Return early if exec failed, possibly updating last index. | |
| 259 a->GotoUnless(a->WordEqual(match_indices, null), &successful_match); | |
| 260 | |
| 261 Label return_null(a); | |
| 262 a->GotoUnless(should_update_last_index, &return_null); | |
| 263 | |
| 264 StoreLastIndex(a, context, has_initialmap, regexp, smi_zero); | |
| 265 a->Goto(&return_null); | |
| 266 | |
| 267 a->Bind(&return_null); | |
| 268 a->Return(null); | |
| 269 } | |
| 270 | |
| 271 Label construct_result(a); | |
| 272 a->Bind(&successful_match); | |
| 273 { | |
| 274 Node* const match_elements = a->LoadElements(match_indices); | |
| 275 | |
| 276 a->GotoUnless(should_update_last_index, &construct_result); | |
| 277 | |
| 278 // Update the new last index from {match_indices}. | |
| 279 Node* const new_lastindex = a->LoadFixedArrayElement( | |
| 280 match_elements, a->IntPtrConstant(RegExpImpl::kFirstCapture + 1)); | |
| 281 | |
| 282 StoreLastIndex(a, context, has_initialmap, regexp, new_lastindex); | |
| 283 a->Goto(&construct_result); | |
| 284 | |
| 285 a->Bind(&construct_result); | |
| 286 { | |
| 287 Node* result = ConstructNewResultFromMatchInfo(isolate, a, context, | |
| 288 match_elements, string); | |
| 289 a->Return(result); | |
| 290 } | |
| 291 } | |
| 292 } | |
| 293 | |
| 294 } // namespace internal | |
| 295 } // namespace v8 | |
| OLD | NEW |