Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(648)

Side by Side Diff: src/builtins/builtins-string.cc

Issue 2165593002: [builtins] Move builtins into own files (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: Remove builtins-error.cc from BUILD.gn Created 4 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « src/builtins/builtins-sharedarraybuffer.cc ('k') | src/builtins/builtins-symbol.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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.h"
6 #include "src/builtins/builtins-utils.h"
7
8 #include "src/code-factory.h"
9
10 namespace v8 {
11 namespace internal {
12
13 // -----------------------------------------------------------------------------
14 // ES6 section 21.1 String Objects
15
16 // ES6 section 21.1.2.1 String.fromCharCode ( ...codeUnits )
17 void Builtins::Generate_StringFromCharCode(CodeStubAssembler* assembler) {
18 typedef CodeStubAssembler::Label Label;
19 typedef compiler::Node Node;
20 typedef CodeStubAssembler::Variable Variable;
21
22 Node* code = assembler->Parameter(1);
23 Node* context = assembler->Parameter(4);
24
25 // Check if we have exactly one argument (plus the implicit receiver), i.e.
26 // if the parent frame is not an arguments adaptor frame.
27 Label if_oneargument(assembler), if_notoneargument(assembler);
28 Node* parent_frame_pointer = assembler->LoadParentFramePointer();
29 Node* parent_frame_type =
30 assembler->Load(MachineType::Pointer(), parent_frame_pointer,
31 assembler->IntPtrConstant(
32 CommonFrameConstants::kContextOrFrameTypeOffset));
33 assembler->Branch(
34 assembler->WordEqual(
35 parent_frame_type,
36 assembler->SmiConstant(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR))),
37 &if_notoneargument, &if_oneargument);
38
39 assembler->Bind(&if_oneargument);
40 {
41 // Single argument case, perform fast single character string cache lookup
42 // for one-byte code units, or fall back to creating a single character
43 // string on the fly otherwise.
44 Node* code32 = assembler->TruncateTaggedToWord32(context, code);
45 Node* code16 = assembler->Word32And(
46 code32, assembler->Int32Constant(String::kMaxUtf16CodeUnit));
47 Node* result = assembler->StringFromCharCode(code16);
48 assembler->Return(result);
49 }
50
51 assembler->Bind(&if_notoneargument);
52 {
53 // Determine the resulting string length.
54 Node* parent_frame_length =
55 assembler->Load(MachineType::Pointer(), parent_frame_pointer,
56 assembler->IntPtrConstant(
57 ArgumentsAdaptorFrameConstants::kLengthOffset));
58 Node* length = assembler->SmiToWord(parent_frame_length);
59
60 // Assume that the resulting string contains only one-byte characters.
61 Node* result = assembler->AllocateSeqOneByteString(context, length);
62
63 // Truncate all input parameters and append them to the resulting string.
64 Variable var_offset(assembler, MachineType::PointerRepresentation());
65 Label loop(assembler, &var_offset), done_loop(assembler);
66 var_offset.Bind(assembler->IntPtrConstant(0));
67 assembler->Goto(&loop);
68 assembler->Bind(&loop);
69 {
70 // Load the current {offset}.
71 Node* offset = var_offset.value();
72
73 // Check if we're done with the string.
74 assembler->GotoIf(assembler->WordEqual(offset, length), &done_loop);
75
76 // Load the next code point and truncate it to a 16-bit value.
77 Node* code = assembler->Load(
78 MachineType::AnyTagged(), parent_frame_pointer,
79 assembler->IntPtrAdd(
80 assembler->WordShl(assembler->IntPtrSub(length, offset),
81 assembler->IntPtrConstant(kPointerSizeLog2)),
82 assembler->IntPtrConstant(
83 CommonFrameConstants::kFixedFrameSizeAboveFp -
84 kPointerSize)));
85 Node* code32 = assembler->TruncateTaggedToWord32(context, code);
86 Node* code16 = assembler->Word32And(
87 code32, assembler->Int32Constant(String::kMaxUtf16CodeUnit));
88
89 // Check if {code16} fits into a one-byte string.
90 Label if_codeisonebyte(assembler), if_codeistwobyte(assembler);
91 assembler->Branch(
92 assembler->Int32LessThanOrEqual(
93 code16, assembler->Int32Constant(String::kMaxOneByteCharCode)),
94 &if_codeisonebyte, &if_codeistwobyte);
95
96 assembler->Bind(&if_codeisonebyte);
97 {
98 // The {code16} fits into the SeqOneByteString {result}.
99 assembler->StoreNoWriteBarrier(
100 MachineRepresentation::kWord8, result,
101 assembler->IntPtrAdd(
102 assembler->IntPtrConstant(SeqOneByteString::kHeaderSize -
103 kHeapObjectTag),
104 offset),
105 code16);
106 var_offset.Bind(
107 assembler->IntPtrAdd(offset, assembler->IntPtrConstant(1)));
108 assembler->Goto(&loop);
109 }
110
111 assembler->Bind(&if_codeistwobyte);
112 {
113 // Allocate a SeqTwoByteString to hold the resulting string.
114 Node* cresult = assembler->AllocateSeqTwoByteString(context, length);
115
116 // Copy all characters that were previously written to the
117 // SeqOneByteString in {result} over to the new {cresult}.
118 Variable var_coffset(assembler, MachineType::PointerRepresentation());
119 Label cloop(assembler, &var_coffset), done_cloop(assembler);
120 var_coffset.Bind(assembler->IntPtrConstant(0));
121 assembler->Goto(&cloop);
122 assembler->Bind(&cloop);
123 {
124 Node* coffset = var_coffset.value();
125 assembler->GotoIf(assembler->WordEqual(coffset, offset), &done_cloop);
126 Node* ccode = assembler->Load(
127 MachineType::Uint8(), result,
128 assembler->IntPtrAdd(
129 assembler->IntPtrConstant(SeqOneByteString::kHeaderSize -
130 kHeapObjectTag),
131 coffset));
132 assembler->StoreNoWriteBarrier(
133 MachineRepresentation::kWord16, cresult,
134 assembler->IntPtrAdd(
135 assembler->IntPtrConstant(SeqTwoByteString::kHeaderSize -
136 kHeapObjectTag),
137 assembler->WordShl(coffset, 1)),
138 ccode);
139 var_coffset.Bind(
140 assembler->IntPtrAdd(coffset, assembler->IntPtrConstant(1)));
141 assembler->Goto(&cloop);
142 }
143
144 // Write the pending {code16} to {offset}.
145 assembler->Bind(&done_cloop);
146 assembler->StoreNoWriteBarrier(
147 MachineRepresentation::kWord16, cresult,
148 assembler->IntPtrAdd(
149 assembler->IntPtrConstant(SeqTwoByteString::kHeaderSize -
150 kHeapObjectTag),
151 assembler->WordShl(offset, 1)),
152 code16);
153
154 // Copy the remaining parameters to the SeqTwoByteString {cresult}.
155 Label floop(assembler, &var_offset), done_floop(assembler);
156 assembler->Goto(&floop);
157 assembler->Bind(&floop);
158 {
159 // Compute the next {offset}.
160 Node* offset = assembler->IntPtrAdd(var_offset.value(),
161 assembler->IntPtrConstant(1));
162
163 // Check if we're done with the string.
164 assembler->GotoIf(assembler->WordEqual(offset, length), &done_floop);
165
166 // Load the next code point and truncate it to a 16-bit value.
167 Node* code = assembler->Load(
168 MachineType::AnyTagged(), parent_frame_pointer,
169 assembler->IntPtrAdd(
170 assembler->WordShl(
171 assembler->IntPtrSub(length, offset),
172 assembler->IntPtrConstant(kPointerSizeLog2)),
173 assembler->IntPtrConstant(
174 CommonFrameConstants::kFixedFrameSizeAboveFp -
175 kPointerSize)));
176 Node* code32 = assembler->TruncateTaggedToWord32(context, code);
177 Node* code16 = assembler->Word32And(
178 code32, assembler->Int32Constant(String::kMaxUtf16CodeUnit));
179
180 // Store the truncated {code} point at the next offset.
181 assembler->StoreNoWriteBarrier(
182 MachineRepresentation::kWord16, cresult,
183 assembler->IntPtrAdd(
184 assembler->IntPtrConstant(SeqTwoByteString::kHeaderSize -
185 kHeapObjectTag),
186 assembler->WordShl(offset, 1)),
187 code16);
188 var_offset.Bind(offset);
189 assembler->Goto(&floop);
190 }
191
192 // Return the SeqTwoByteString.
193 assembler->Bind(&done_floop);
194 assembler->Return(cresult);
195 }
196 }
197
198 assembler->Bind(&done_loop);
199 assembler->Return(result);
200 }
201 }
202
203 namespace { // for String.fromCodePoint
204
205 bool IsValidCodePoint(Isolate* isolate, Handle<Object> value) {
206 if (!value->IsNumber() && !Object::ToNumber(value).ToHandle(&value)) {
207 return false;
208 }
209
210 if (Object::ToInteger(isolate, value).ToHandleChecked()->Number() !=
211 value->Number()) {
212 return false;
213 }
214
215 if (value->Number() < 0 || value->Number() > 0x10FFFF) {
216 return false;
217 }
218
219 return true;
220 }
221
222 uc32 NextCodePoint(Isolate* isolate, BuiltinArguments args, int index) {
223 Handle<Object> value = args.at<Object>(1 + index);
224 ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, value, Object::ToNumber(value), -1);
225 if (!IsValidCodePoint(isolate, value)) {
226 isolate->Throw(*isolate->factory()->NewRangeError(
227 MessageTemplate::kInvalidCodePoint, value));
228 return -1;
229 }
230 return DoubleToUint32(value->Number());
231 }
232
233 } // namespace
234
235 // ES6 section 21.1.2.2 String.fromCodePoint ( ...codePoints )
236 BUILTIN(StringFromCodePoint) {
237 HandleScope scope(isolate);
238 int const length = args.length() - 1;
239 if (length == 0) return isolate->heap()->empty_string();
240 DCHECK_LT(0, length);
241
242 // Optimistically assume that the resulting String contains only one byte
243 // characters.
244 List<uint8_t> one_byte_buffer(length);
245 uc32 code = 0;
246 int index;
247 for (index = 0; index < length; index++) {
248 code = NextCodePoint(isolate, args, index);
249 if (code < 0) {
250 return isolate->heap()->exception();
251 }
252 if (code > String::kMaxOneByteCharCode) {
253 break;
254 }
255 one_byte_buffer.Add(code);
256 }
257
258 if (index == length) {
259 RETURN_RESULT_OR_FAILURE(isolate, isolate->factory()->NewStringFromOneByte(
260 one_byte_buffer.ToConstVector()));
261 }
262
263 List<uc16> two_byte_buffer(length - index);
264
265 while (true) {
266 if (code <= unibrow::Utf16::kMaxNonSurrogateCharCode) {
267 two_byte_buffer.Add(code);
268 } else {
269 two_byte_buffer.Add(unibrow::Utf16::LeadSurrogate(code));
270 two_byte_buffer.Add(unibrow::Utf16::TrailSurrogate(code));
271 }
272
273 if (++index == length) {
274 break;
275 }
276 code = NextCodePoint(isolate, args, index);
277 if (code < 0) {
278 return isolate->heap()->exception();
279 }
280 }
281
282 Handle<SeqTwoByteString> result;
283 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
284 isolate, result,
285 isolate->factory()->NewRawTwoByteString(one_byte_buffer.length() +
286 two_byte_buffer.length()));
287
288 CopyChars(result->GetChars(), one_byte_buffer.ToConstVector().start(),
289 one_byte_buffer.length());
290 CopyChars(result->GetChars() + one_byte_buffer.length(),
291 two_byte_buffer.ToConstVector().start(), two_byte_buffer.length());
292
293 return *result;
294 }
295
296 // ES6 section 21.1.3.1 String.prototype.charAt ( pos )
297 void Builtins::Generate_StringPrototypeCharAt(CodeStubAssembler* assembler) {
298 typedef CodeStubAssembler::Label Label;
299 typedef compiler::Node Node;
300 typedef CodeStubAssembler::Variable Variable;
301
302 Node* receiver = assembler->Parameter(0);
303 Node* position = assembler->Parameter(1);
304 Node* context = assembler->Parameter(4);
305
306 // Check that {receiver} is coercible to Object and convert it to a String.
307 receiver =
308 assembler->ToThisString(context, receiver, "String.prototype.charAt");
309
310 // Convert the {position} to a Smi and check that it's in bounds of the
311 // {receiver}.
312 // TODO(bmeurer): Find an abstraction for this!
313 {
314 // Check if the {position} is already a Smi.
315 Variable var_position(assembler, MachineRepresentation::kTagged);
316 var_position.Bind(position);
317 Label if_positionissmi(assembler),
318 if_positionisnotsmi(assembler, Label::kDeferred);
319 assembler->Branch(assembler->WordIsSmi(position), &if_positionissmi,
320 &if_positionisnotsmi);
321 assembler->Bind(&if_positionisnotsmi);
322 {
323 // Convert the {position} to an Integer via the ToIntegerStub.
324 Callable callable = CodeFactory::ToInteger(assembler->isolate());
325 Node* index = assembler->CallStub(callable, context, position);
326
327 // Check if the resulting {index} is now a Smi.
328 Label if_indexissmi(assembler, Label::kDeferred),
329 if_indexisnotsmi(assembler, Label::kDeferred);
330 assembler->Branch(assembler->WordIsSmi(index), &if_indexissmi,
331 &if_indexisnotsmi);
332
333 assembler->Bind(&if_indexissmi);
334 {
335 var_position.Bind(index);
336 assembler->Goto(&if_positionissmi);
337 }
338
339 assembler->Bind(&if_indexisnotsmi);
340 {
341 // The ToIntegerStub canonicalizes everything in Smi range to Smi
342 // representation, so any HeapNumber returned is not in Smi range.
343 // The only exception here is -0.0, which we treat as 0.
344 Node* index_value = assembler->LoadHeapNumberValue(index);
345 Label if_indexiszero(assembler, Label::kDeferred),
346 if_indexisnotzero(assembler, Label::kDeferred);
347 assembler->Branch(assembler->Float64Equal(
348 index_value, assembler->Float64Constant(0.0)),
349 &if_indexiszero, &if_indexisnotzero);
350
351 assembler->Bind(&if_indexiszero);
352 {
353 var_position.Bind(assembler->SmiConstant(Smi::FromInt(0)));
354 assembler->Goto(&if_positionissmi);
355 }
356
357 assembler->Bind(&if_indexisnotzero);
358 {
359 // The {index} is some other integral Number, that is definitely
360 // neither -0.0 nor in Smi range.
361 assembler->Return(assembler->EmptyStringConstant());
362 }
363 }
364 }
365 assembler->Bind(&if_positionissmi);
366 position = var_position.value();
367
368 // Determine the actual length of the {receiver} String.
369 Node* receiver_length =
370 assembler->LoadObjectField(receiver, String::kLengthOffset);
371
372 // Return "" if the Smi {position} is outside the bounds of the {receiver}.
373 Label if_positioninbounds(assembler),
374 if_positionnotinbounds(assembler, Label::kDeferred);
375 assembler->Branch(assembler->SmiAboveOrEqual(position, receiver_length),
376 &if_positionnotinbounds, &if_positioninbounds);
377 assembler->Bind(&if_positionnotinbounds);
378 assembler->Return(assembler->EmptyStringConstant());
379 assembler->Bind(&if_positioninbounds);
380 }
381
382 // Load the character code at the {position} from the {receiver}.
383 Node* code = assembler->StringCharCodeAt(receiver, position);
384
385 // And return the single character string with only that {code}.
386 Node* result = assembler->StringFromCharCode(code);
387 assembler->Return(result);
388 }
389
390 // ES6 section 21.1.3.2 String.prototype.charCodeAt ( pos )
391 void Builtins::Generate_StringPrototypeCharCodeAt(
392 CodeStubAssembler* assembler) {
393 typedef CodeStubAssembler::Label Label;
394 typedef compiler::Node Node;
395 typedef CodeStubAssembler::Variable Variable;
396
397 Node* receiver = assembler->Parameter(0);
398 Node* position = assembler->Parameter(1);
399 Node* context = assembler->Parameter(4);
400
401 // Check that {receiver} is coercible to Object and convert it to a String.
402 receiver =
403 assembler->ToThisString(context, receiver, "String.prototype.charCodeAt");
404
405 // Convert the {position} to a Smi and check that it's in bounds of the
406 // {receiver}.
407 // TODO(bmeurer): Find an abstraction for this!
408 {
409 // Check if the {position} is already a Smi.
410 Variable var_position(assembler, MachineRepresentation::kTagged);
411 var_position.Bind(position);
412 Label if_positionissmi(assembler),
413 if_positionisnotsmi(assembler, Label::kDeferred);
414 assembler->Branch(assembler->WordIsSmi(position), &if_positionissmi,
415 &if_positionisnotsmi);
416 assembler->Bind(&if_positionisnotsmi);
417 {
418 // Convert the {position} to an Integer via the ToIntegerStub.
419 Callable callable = CodeFactory::ToInteger(assembler->isolate());
420 Node* index = assembler->CallStub(callable, context, position);
421
422 // Check if the resulting {index} is now a Smi.
423 Label if_indexissmi(assembler, Label::kDeferred),
424 if_indexisnotsmi(assembler, Label::kDeferred);
425 assembler->Branch(assembler->WordIsSmi(index), &if_indexissmi,
426 &if_indexisnotsmi);
427
428 assembler->Bind(&if_indexissmi);
429 {
430 var_position.Bind(index);
431 assembler->Goto(&if_positionissmi);
432 }
433
434 assembler->Bind(&if_indexisnotsmi);
435 {
436 // The ToIntegerStub canonicalizes everything in Smi range to Smi
437 // representation, so any HeapNumber returned is not in Smi range.
438 // The only exception here is -0.0, which we treat as 0.
439 Node* index_value = assembler->LoadHeapNumberValue(index);
440 Label if_indexiszero(assembler, Label::kDeferred),
441 if_indexisnotzero(assembler, Label::kDeferred);
442 assembler->Branch(assembler->Float64Equal(
443 index_value, assembler->Float64Constant(0.0)),
444 &if_indexiszero, &if_indexisnotzero);
445
446 assembler->Bind(&if_indexiszero);
447 {
448 var_position.Bind(assembler->SmiConstant(Smi::FromInt(0)));
449 assembler->Goto(&if_positionissmi);
450 }
451
452 assembler->Bind(&if_indexisnotzero);
453 {
454 // The {index} is some other integral Number, that is definitely
455 // neither -0.0 nor in Smi range.
456 assembler->Return(assembler->NaNConstant());
457 }
458 }
459 }
460 assembler->Bind(&if_positionissmi);
461 position = var_position.value();
462
463 // Determine the actual length of the {receiver} String.
464 Node* receiver_length =
465 assembler->LoadObjectField(receiver, String::kLengthOffset);
466
467 // Return NaN if the Smi {position} is outside the bounds of the {receiver}.
468 Label if_positioninbounds(assembler),
469 if_positionnotinbounds(assembler, Label::kDeferred);
470 assembler->Branch(assembler->SmiAboveOrEqual(position, receiver_length),
471 &if_positionnotinbounds, &if_positioninbounds);
472 assembler->Bind(&if_positionnotinbounds);
473 assembler->Return(assembler->NaNConstant());
474 assembler->Bind(&if_positioninbounds);
475 }
476
477 // Load the character at the {position} from the {receiver}.
478 Node* value = assembler->StringCharCodeAt(receiver, position);
479 Node* result = assembler->SmiFromWord32(value);
480 assembler->Return(result);
481 }
482
483 // ES6 section 21.1.3.25 String.prototype.toString ()
484 void Builtins::Generate_StringPrototypeToString(CodeStubAssembler* assembler) {
485 typedef compiler::Node Node;
486
487 Node* receiver = assembler->Parameter(0);
488 Node* context = assembler->Parameter(3);
489
490 Node* result = assembler->ToThisValue(
491 context, receiver, PrimitiveType::kString, "String.prototype.toString");
492 assembler->Return(result);
493 }
494
495 // ES6 section 21.1.3.27 String.prototype.trim ()
496 BUILTIN(StringPrototypeTrim) {
497 HandleScope scope(isolate);
498 TO_THIS_STRING(string, "String.prototype.trim");
499 return *String::Trim(string, String::kTrim);
500 }
501
502 // Non-standard WebKit extension
503 BUILTIN(StringPrototypeTrimLeft) {
504 HandleScope scope(isolate);
505 TO_THIS_STRING(string, "String.prototype.trimLeft");
506 return *String::Trim(string, String::kTrimLeft);
507 }
508
509 // Non-standard WebKit extension
510 BUILTIN(StringPrototypeTrimRight) {
511 HandleScope scope(isolate);
512 TO_THIS_STRING(string, "String.prototype.trimRight");
513 return *String::Trim(string, String::kTrimRight);
514 }
515
516 // ES6 section 21.1.3.28 String.prototype.valueOf ( )
517 void Builtins::Generate_StringPrototypeValueOf(CodeStubAssembler* assembler) {
518 typedef compiler::Node Node;
519
520 Node* receiver = assembler->Parameter(0);
521 Node* context = assembler->Parameter(3);
522
523 Node* result = assembler->ToThisValue(
524 context, receiver, PrimitiveType::kString, "String.prototype.valueOf");
525 assembler->Return(result);
526 }
527
528 } // namespace internal
529 } // namespace v8
OLDNEW
« no previous file with comments | « src/builtins/builtins-sharedarraybuffer.cc ('k') | src/builtins/builtins-symbol.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698