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 * kPointerSize; | |
33 var_value.Bind(a->LoadObjectField(regexp, field_offset)); | |
34 a->Goto(&out); | |
35 } | |
36 | |
37 a->Bind(&if_modified); | |
38 { | |
39 // Load through the GetProperty stub. | |
40 Node* const name = | |
41 a->HeapConstant(a->isolate()->factory()->last_index_string()); | |
42 Callable getproperty_callable = CodeFactory::GetProperty(a->isolate()); | |
43 var_value.Bind(a->CallStub(getproperty_callable, context, regexp, name)); | |
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 // Store the in-object field. | |
63 static const int field_offset = | |
64 JSRegExp::kSize + JSRegExp::kLastIndexFieldIndex * kPointerSize; | |
65 a->StoreObjectField(regexp, field_offset, value); | |
66 a->Goto(&out); | |
67 } | |
68 | |
69 a->Bind(&if_modified); | |
70 { | |
71 // Store through runtime. | |
72 // TODO(ishell): Use SetPropertyStub here once available. | |
73 Node* const name = | |
74 a->HeapConstant(a->isolate()->factory()->last_index_string()); | |
75 Node* const language_mode = a->SmiConstant(Smi::FromInt(STRICT)); | |
76 a->CallRuntime(Runtime::kSetProperty, context, regexp, name, value, | |
77 language_mode); | |
78 a->Goto(&out); | |
79 } | |
80 | |
81 a->Bind(&out); | |
82 } | |
83 | |
84 compiler::Node* ConstructNewResultFromMatchInfo(Isolate* isolate, | |
85 CodeStubAssembler* a, | |
86 compiler::Node* context, | |
87 compiler::Node* match_elements, | |
88 compiler::Node* string) { | |
89 typedef CodeStubAssembler::Variable Variable; | |
90 typedef CodeStubAssembler::Label Label; | |
91 typedef compiler::Node Node; | |
92 | |
93 Label out(a); | |
94 | |
95 Callable constructresult_callable = | |
96 CodeFactory::RegExpConstructResult(isolate); | |
97 | |
98 CodeStubAssembler::ParameterMode intptr_mode = | |
Igor Sheludko
2016/09/29 15:01:34
How about just mode? or parameter_mode?
jgruber
2016/09/30 06:53:26
Done.
| |
99 CodeStubAssembler::INTPTR_PARAMETERS; | |
100 Node* const num_indices = a->SmiUntag(a->LoadFixedArrayElement( | |
101 match_elements, a->IntPtrConstant(RegExpImpl::kLastCaptureCount), 0, | |
102 intptr_mode)); | |
103 Node* const num_results = a->SmiTag(a->WordShr(num_indices, 1)); | |
104 Node* const start = a->LoadFixedArrayElement( | |
105 match_elements, a->IntPtrConstant(RegExpImpl::kFirstCapture), 0, | |
106 intptr_mode); | |
107 Node* const end = a->LoadFixedArrayElement( | |
108 match_elements, a->IntPtrConstant(RegExpImpl::kFirstCapture + 1), 0, | |
109 intptr_mode); | |
110 | |
111 // Calculate the substring of the first match before creating the result array | |
112 // to avoid an unnecessary write barrier storing the first result. | |
113 Node* const first = a->SubString(context, string, start, end); | |
114 | |
115 Node* const result = a->CallStub(constructresult_callable, context, | |
116 num_results, start, string); | |
117 Node* const result_elements = a->LoadElements(result); | |
118 | |
119 a->StoreFixedArrayElement(result_elements, a->IntPtrConstant(0), first, | |
120 SKIP_WRITE_BARRIER); | |
121 | |
122 a->GotoIf(a->SmiEqual(num_results, a->SmiConstant(Smi::FromInt(1))), &out); | |
123 | |
124 // Store all remaining captures. | |
125 Node* const limit = | |
126 a->IntPtrAdd(a->IntPtrConstant(RegExpImpl::kFirstCapture), num_indices); | |
127 | |
128 Variable var_from_cursor(a, MachineType::PointerRepresentation()); | |
129 Variable var_to_cursor(a, MachineType::PointerRepresentation()); | |
130 | |
131 var_from_cursor.Bind(a->IntPtrConstant(RegExpImpl::kFirstCapture + 2)); | |
132 var_to_cursor.Bind(a->IntPtrConstant(1)); | |
133 | |
134 Variable* vars[] = {&var_from_cursor, &var_to_cursor}; | |
135 Label loop(a, 2, vars); | |
136 | |
137 a->Goto(&loop); | |
138 a->Bind(&loop); | |
139 { | |
140 Node* const from_cursor = var_from_cursor.value(); | |
141 Node* const to_cursor = var_to_cursor.value(); | |
142 Node* const start = a->LoadFixedArrayElement(match_elements, from_cursor); | |
143 | |
144 Label next_iter(a); | |
145 a->GotoIf(a->SmiEqual(start, a->SmiConstant(Smi::FromInt(-1))), &next_iter); | |
146 | |
147 Node* const from_cursor_plus1 = | |
148 a->IntPtrAdd(from_cursor, a->IntPtrConstant(1)); | |
149 Node* const end = | |
150 a->LoadFixedArrayElement(match_elements, from_cursor_plus1); | |
151 | |
152 Node* const capture = a->SubString(context, string, start, end); | |
153 a->StoreFixedArrayElement(result_elements, to_cursor, capture); | |
154 a->Goto(&next_iter); | |
155 | |
156 a->Bind(&next_iter); | |
157 var_from_cursor.Bind(a->IntPtrAdd(from_cursor, a->IntPtrConstant(2))); | |
158 var_to_cursor.Bind(a->IntPtrAdd(to_cursor, a->IntPtrConstant(1))); | |
159 a->Branch(a->UintPtrLessThan(var_from_cursor.value(), limit), &loop, &out); | |
160 } | |
161 | |
162 a->Bind(&out); | |
163 return result; | |
164 } | |
165 | |
166 } // namespace | |
167 | |
168 // ES#sec-regexp.prototype.exec | |
169 // RegExp.prototype.exec ( string ) | |
170 void Builtins::Generate_RegExpPrototypeExec(CodeStubAssembler* a) { | |
171 typedef CodeStubAssembler::Variable Variable; | |
172 typedef CodeStubAssembler::Label Label; | |
173 typedef compiler::Node Node; | |
174 | |
175 Isolate* const isolate = a->isolate(); | |
176 | |
177 Node* const receiver = a->Parameter(0); | |
178 Node* const maybe_string = a->Parameter(1); | |
179 Node* const context = a->Parameter(4); | |
180 | |
181 Node* const null = a->NullConstant(); | |
182 Node* const int_zero = a->IntPtrConstant(0); | |
183 Node* const smi_zero = a->SmiConstant(Smi::FromInt(0)); | |
184 | |
185 // Ensure {receiver} is a JSRegExp. | |
186 Node* const regexp_map = a->ThrowIfNotInstanceType( | |
187 context, receiver, JS_REGEXP_TYPE, "RegExp.prototype.exec"); | |
188 Node* const regexp = receiver; | |
189 | |
190 // Check whether the regexp instance is unmodified. | |
191 Node* const native_context = a->LoadNativeContext(context); | |
192 Node* const regexp_fun = | |
193 a->LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX); | |
194 Node* const initial_map = | |
195 a->LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset); | |
196 Node* const has_initialmap = a->WordEqual(regexp_map, initial_map); | |
197 | |
198 // Convert {maybe_string} to a string. | |
199 Callable tostring_callable = CodeFactory::ToString(isolate); | |
200 Node* const string = a->CallStub(tostring_callable, context, maybe_string); | |
201 Node* const string_length = a->LoadStringLength(string); | |
202 | |
203 // Check whether the regexp is global or sticky, which determines whether we | |
204 // update last index later on. | |
205 Node* const flags = a->LoadObjectField(regexp, JSRegExp::kFlagsOffset); | |
206 Node* const is_global_or_sticky = | |
207 a->WordAnd(a->SmiUntag(flags), | |
208 a->IntPtrConstant(JSRegExp::kGlobal | JSRegExp::kSticky)); | |
209 Node* const should_update_last_index = | |
210 a->WordNotEqual(is_global_or_sticky, int_zero); | |
211 | |
212 // Grab and possibly update last index. | |
213 Label run_exec(a); | |
214 Variable var_lastindex(a, MachineRepresentation::kTagged); | |
215 { | |
216 Label if_doupdate(a), if_dontupdate(a); | |
217 a->Branch(should_update_last_index, &if_doupdate, &if_dontupdate); | |
218 | |
219 a->Bind(&if_doupdate); | |
220 { | |
221 Node* const regexp_lastindex = | |
222 LoadLastIndex(a, context, has_initialmap, regexp); | |
223 | |
224 Callable tolength_callable = CodeFactory::ToLength(isolate); | |
225 Node* const lastindex = | |
226 a->CallStub(tolength_callable, context, regexp_lastindex); | |
227 var_lastindex.Bind(lastindex); | |
228 | |
229 Label if_isoob(a, Label::kDeferred); | |
230 a->GotoUnless(a->WordIsSmi(lastindex), &if_isoob); | |
231 a->GotoUnless(a->SmiLessThanOrEqual(lastindex, string_length), &if_isoob); | |
232 a->Goto(&run_exec); | |
233 | |
234 a->Bind(&if_isoob); | |
235 { | |
236 StoreLastIndex(a, context, has_initialmap, regexp, smi_zero); | |
237 a->Return(null); | |
238 } | |
239 } | |
240 | |
241 a->Bind(&if_dontupdate); | |
242 { | |
243 var_lastindex.Bind(smi_zero); | |
244 a->Goto(&run_exec); | |
245 } | |
246 } | |
247 | |
248 Node* match_indices; | |
249 Label successful_match(a); | |
250 a->Bind(&run_exec); | |
251 { | |
252 // Get last match info from the context. | |
253 Node* const last_match_info = a->LoadContextElement( | |
254 native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX); | |
255 | |
256 // Call the exec stub. | |
257 Callable exec_callable = CodeFactory::RegExpExec(isolate); | |
258 match_indices = a->CallStub(exec_callable, context, regexp, string, | |
259 var_lastindex.value(), last_match_info); | |
260 | |
261 // {match_indices} is either null or the RegExpLastMatchInfo array. | |
262 // Return early if exec failed, possibly updating last index. | |
263 a->GotoUnless(a->WordEqual(match_indices, null), &successful_match); | |
264 | |
265 Label return_null(a); | |
266 a->GotoUnless(should_update_last_index, &return_null); | |
267 | |
268 StoreLastIndex(a, context, has_initialmap, regexp, smi_zero); | |
269 a->Goto(&return_null); | |
270 | |
271 a->Bind(&return_null); | |
272 a->Return(null); | |
273 } | |
274 | |
275 Label construct_result(a); | |
276 a->Bind(&successful_match); | |
277 { | |
278 Node* const match_elements = a->LoadElements(match_indices); | |
279 | |
280 a->GotoUnless(should_update_last_index, &construct_result); | |
281 | |
282 // Update the new last index from {match_indices}. | |
283 Node* const new_lastindex = a->LoadFixedArrayElement( | |
284 match_elements, a->IntPtrConstant(RegExpImpl::kFirstCapture + 1)); | |
285 | |
286 StoreLastIndex(a, context, has_initialmap, regexp, new_lastindex); | |
287 a->Goto(&construct_result); | |
288 | |
289 a->Bind(&construct_result); | |
290 { | |
291 Node* result = ConstructNewResultFromMatchInfo(isolate, a, context, | |
292 match_elements, string); | |
293 a->Return(result); | |
294 } | |
295 } | |
296 } | |
297 | |
298 } // namespace internal | |
299 } // namespace v8 | |
OLD | NEW |