OLD | NEW |
1 // Copyright 2017 the V8 project authors. All rights reserved. | 1 // Copyright 2017 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-regexp-gen.h" | 5 #include "src/builtins/builtins-regexp-gen.h" |
6 #include "src/builtins/builtins-utils-gen.h" | 6 #include "src/builtins/builtins-utils-gen.h" |
7 #include "src/builtins/builtins.h" | 7 #include "src/builtins/builtins.h" |
8 #include "src/code-factory.h" | 8 #include "src/code-factory.h" |
9 #include "src/code-stub-assembler.h" | 9 #include "src/code-stub-assembler.h" |
10 #include "src/objects.h" | 10 #include "src/objects.h" |
(...skipping 137 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
148 Node* IndexOfDollarChar(Node* const context, Node* const string); | 148 Node* IndexOfDollarChar(Node* const context, Node* const string); |
149 | 149 |
150 Node* IsNullOrUndefined(Node* const value); | 150 Node* IsNullOrUndefined(Node* const value); |
151 void RequireObjectCoercible(Node* const context, Node* const value, | 151 void RequireObjectCoercible(Node* const context, Node* const value, |
152 const char* method_name); | 152 const char* method_name); |
153 | 153 |
154 Node* SmiIsNegative(Node* const value) { | 154 Node* SmiIsNegative(Node* const value) { |
155 return SmiLessThan(value, SmiConstant(0)); | 155 return SmiLessThan(value, SmiConstant(0)); |
156 } | 156 } |
157 | 157 |
| 158 // substr and slice have a common way of handling the {start} argument. |
| 159 void ConvertAndBoundsCheckStartArgument(Node* context, Variable* var_start, |
| 160 Node* start, Node* string_length) { |
| 161 Node* const start_int = |
| 162 ToInteger(context, start, CodeStubAssembler::kTruncateMinusZero); |
| 163 Node* const zero = SmiConstant(Smi::kZero); |
| 164 |
| 165 Label done(this); |
| 166 Label if_issmi(this), if_isheapnumber(this, Label::kDeferred); |
| 167 Branch(TaggedIsSmi(start_int), &if_issmi, &if_isheapnumber); |
| 168 |
| 169 BIND(&if_issmi); |
| 170 { |
| 171 var_start->Bind( |
| 172 Select(SmiLessThan(start_int, zero), |
| 173 [&] { return SmiMax(SmiAdd(string_length, start_int), zero); }, |
| 174 [&] { return start_int; }, MachineRepresentation::kTagged)); |
| 175 Goto(&done); |
| 176 } |
| 177 |
| 178 BIND(&if_isheapnumber); |
| 179 { |
| 180 // If {start} is a heap number, it is definitely out of bounds. If it is |
| 181 // negative, {start} = max({string_length} + {start}),0) = 0'. If it is |
| 182 // positive, set {start} to {string_length} which ultimately results in |
| 183 // returning an empty string. |
| 184 Node* const float_zero = Float64Constant(0.); |
| 185 Node* const start_float = LoadHeapNumberValue(start_int); |
| 186 var_start->Bind(SelectTaggedConstant( |
| 187 Float64LessThan(start_float, float_zero), zero, string_length)); |
| 188 Goto(&done); |
| 189 } |
| 190 BIND(&done); |
| 191 } |
| 192 |
158 // Implements boilerplate logic for {match, split, replace, search} of the | 193 // Implements boilerplate logic for {match, split, replace, search} of the |
159 // form: | 194 // form: |
160 // | 195 // |
161 // if (!IS_NULL_OR_UNDEFINED(object)) { | 196 // if (!IS_NULL_OR_UNDEFINED(object)) { |
162 // var maybe_function = object[symbol]; | 197 // var maybe_function = object[symbol]; |
163 // if (!IS_UNDEFINED(maybe_function)) { | 198 // if (!IS_UNDEFINED(maybe_function)) { |
164 // return %_Call(maybe_function, ...); | 199 // return %_Call(maybe_function, ...); |
165 // } | 200 // } |
166 // } | 201 // } |
167 // | 202 // |
(...skipping 1118 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1286 BIND(&out); | 1321 BIND(&out); |
1287 { | 1322 { |
1288 Node* const suffix = CallStub(substring_callable, context, subject_string, | 1323 Node* const suffix = CallStub(substring_callable, context, subject_string, |
1289 match_end_index, subject_length); | 1324 match_end_index, subject_length); |
1290 Node* const result = | 1325 Node* const result = |
1291 CallStub(stringadd_callable, context, var_result.value(), suffix); | 1326 CallStub(stringadd_callable, context, var_result.value(), suffix); |
1292 Return(result); | 1327 Return(result); |
1293 } | 1328 } |
1294 } | 1329 } |
1295 | 1330 |
| 1331 // ES6 section 21.1.3.18 String.prototype.slice ( start, end ) |
| 1332 TF_BUILTIN(StringPrototypeSlice, StringBuiltinsAssembler) { |
| 1333 Label out(this); |
| 1334 VARIABLE(var_start, MachineRepresentation::kTagged); |
| 1335 VARIABLE(var_end, MachineRepresentation::kTagged); |
| 1336 |
| 1337 const int kStart = 0; |
| 1338 const int kEnd = 1; |
| 1339 Node* argc = |
| 1340 ChangeInt32ToIntPtr(Parameter(BuiltinDescriptor::kArgumentsCount)); |
| 1341 CodeStubArguments args(this, argc); |
| 1342 Node* const receiver = args.GetReceiver(); |
| 1343 Node* const start = |
| 1344 args.GetOptionalArgumentValue(kStart, UndefinedConstant()); |
| 1345 Node* const end = args.GetOptionalArgumentValue(kEnd, UndefinedConstant()); |
| 1346 Node* const context = Parameter(BuiltinDescriptor::kContext); |
| 1347 |
| 1348 Node* const smi_zero = SmiConstant(0); |
| 1349 |
| 1350 // 1. Let O be ? RequireObjectCoercible(this value). |
| 1351 RequireObjectCoercible(context, receiver, "String.prototype.slice"); |
| 1352 |
| 1353 // 2. Let S be ? ToString(O). |
| 1354 Callable tostring_callable = CodeFactory::ToString(isolate()); |
| 1355 Node* const subject_string = CallStub(tostring_callable, context, receiver); |
| 1356 |
| 1357 // 3. Let len be the number of elements in S. |
| 1358 Node* const length = LoadStringLength(subject_string); |
| 1359 |
| 1360 // Conversions and bounds-checks for {start}. |
| 1361 ConvertAndBoundsCheckStartArgument(context, &var_start, start, length); |
| 1362 |
| 1363 // 5. If end is undefined, let intEnd be len; |
| 1364 var_end.Bind(length); |
| 1365 GotoIf(WordEqual(end, UndefinedConstant()), &out); |
| 1366 |
| 1367 // else let intEnd be ? ToInteger(end). |
| 1368 Node* const end_int = |
| 1369 ToInteger(context, end, CodeStubAssembler::kTruncateMinusZero); |
| 1370 |
| 1371 // 7. If intEnd < 0, let to be max(len + intEnd, 0); |
| 1372 // otherwise let to be min(intEnd, len). |
| 1373 Label if_issmi(this), if_isheapnumber(this, Label::kDeferred); |
| 1374 Branch(TaggedIsSmi(end_int), &if_issmi, &if_isheapnumber); |
| 1375 |
| 1376 BIND(&if_issmi); |
| 1377 { |
| 1378 Node* const length_plus_end = SmiAdd(length, end_int); |
| 1379 var_end.Bind(Select(SmiLessThan(end_int, smi_zero), |
| 1380 [&] { return SmiMax(length_plus_end, smi_zero); }, |
| 1381 [&] { return SmiMin(length, end_int); }, |
| 1382 MachineRepresentation::kTagged)); |
| 1383 Goto(&out); |
| 1384 } |
| 1385 |
| 1386 BIND(&if_isheapnumber); |
| 1387 { |
| 1388 // If {end} is a heap number, it is definitely out of bounds. If it is |
| 1389 // negative, {int_end} = max({length} + {int_end}),0) = 0'. If it is |
| 1390 // positive, set {int_end} to {length} which ultimately results in |
| 1391 // returning an empty string. |
| 1392 Node* const float_zero = Float64Constant(0.); |
| 1393 Node* const end_float = LoadHeapNumberValue(end_int); |
| 1394 var_end.Bind(SelectTaggedConstant(Float64LessThan(end_float, float_zero), |
| 1395 smi_zero, length)); |
| 1396 Goto(&out); |
| 1397 } |
| 1398 |
| 1399 Label return_emptystring(this); |
| 1400 BIND(&out); |
| 1401 { |
| 1402 GotoIf(SmiLessThanOrEqual(var_end.value(), var_start.value()), |
| 1403 &return_emptystring); |
| 1404 Node* const result = |
| 1405 SubString(context, subject_string, var_start.value(), var_end.value(), |
| 1406 SubStringFlags::FROM_TO_ARE_BOUNDED); |
| 1407 args.PopAndReturn(result); |
| 1408 } |
| 1409 |
| 1410 BIND(&return_emptystring); |
| 1411 args.PopAndReturn(EmptyStringConstant()); |
| 1412 } |
| 1413 |
1296 // ES6 section 21.1.3.19 String.prototype.split ( separator, limit ) | 1414 // ES6 section 21.1.3.19 String.prototype.split ( separator, limit ) |
1297 TF_BUILTIN(StringPrototypeSplit, StringBuiltinsAssembler) { | 1415 TF_BUILTIN(StringPrototypeSplit, StringBuiltinsAssembler) { |
1298 Label out(this); | 1416 Label out(this); |
1299 | 1417 |
1300 Node* const receiver = Parameter(Descriptor::kReceiver); | 1418 Node* const receiver = Parameter(Descriptor::kReceiver); |
1301 Node* const separator = Parameter(Descriptor::kSeparator); | 1419 Node* const separator = Parameter(Descriptor::kSeparator); |
1302 Node* const limit = Parameter(Descriptor::kLimit); | 1420 Node* const limit = Parameter(Descriptor::kLimit); |
1303 Node* const context = Parameter(Descriptor::kContext); | 1421 Node* const context = Parameter(Descriptor::kContext); |
1304 | 1422 |
1305 Node* const smi_zero = SmiConstant(0); | 1423 Node* const smi_zero = SmiConstant(0); |
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1390 BIND(&next); | 1508 BIND(&next); |
1391 } | 1509 } |
1392 | 1510 |
1393 Node* const result = | 1511 Node* const result = |
1394 CallRuntime(Runtime::kStringSplit, context, subject_string, | 1512 CallRuntime(Runtime::kStringSplit, context, subject_string, |
1395 separator_string, limit_number); | 1513 separator_string, limit_number); |
1396 Return(result); | 1514 Return(result); |
1397 } | 1515 } |
1398 | 1516 |
1399 // ES6 #sec-string.prototype.substr | 1517 // ES6 #sec-string.prototype.substr |
1400 TF_BUILTIN(StringPrototypeSubstr, CodeStubAssembler) { | 1518 TF_BUILTIN(StringPrototypeSubstr, StringBuiltinsAssembler) { |
1401 Label out(this), handle_length(this); | 1519 Label out(this); |
1402 | 1520 |
1403 VARIABLE(var_start, MachineRepresentation::kTagged); | 1521 VARIABLE(var_start, MachineRepresentation::kTagged); |
1404 VARIABLE(var_length, MachineRepresentation::kTagged); | 1522 VARIABLE(var_length, MachineRepresentation::kTagged); |
1405 | 1523 |
1406 Node* const receiver = Parameter(Descriptor::kReceiver); | 1524 Node* const receiver = Parameter(Descriptor::kReceiver); |
1407 Node* const start = Parameter(Descriptor::kStart); | 1525 Node* const start = Parameter(Descriptor::kStart); |
1408 Node* const length = Parameter(Descriptor::kLength); | 1526 Node* const length = Parameter(Descriptor::kLength); |
1409 Node* const context = Parameter(Descriptor::kContext); | 1527 Node* const context = Parameter(Descriptor::kContext); |
1410 | 1528 |
1411 Node* const zero = SmiConstant(Smi::kZero); | 1529 Node* const zero = SmiConstant(Smi::kZero); |
1412 | 1530 |
1413 // Check that {receiver} is coercible to Object and convert it to a String. | 1531 // Check that {receiver} is coercible to Object and convert it to a String. |
1414 Node* const string = | 1532 Node* const string = |
1415 ToThisString(context, receiver, "String.prototype.substr"); | 1533 ToThisString(context, receiver, "String.prototype.substr"); |
1416 | 1534 |
1417 Node* const string_length = LoadStringLength(string); | 1535 Node* const string_length = LoadStringLength(string); |
1418 | 1536 |
1419 // Conversions and bounds-checks for {start}. | 1537 // Conversions and bounds-checks for {start}. |
| 1538 ConvertAndBoundsCheckStartArgument(context, &var_start, start, string_length); |
| 1539 |
| 1540 // Conversions and bounds-checks for {length}. |
| 1541 Label if_issmi(this), if_isheapnumber(this, Label::kDeferred); |
| 1542 |
| 1543 // Default to {string_length} if {length} is undefined. |
1420 { | 1544 { |
1421 Node* const start_int = | 1545 Label if_isundefined(this, Label::kDeferred), if_isnotundefined(this); |
1422 ToInteger(context, start, CodeStubAssembler::kTruncateMinusZero); | 1546 Branch(WordEqual(length, UndefinedConstant()), &if_isundefined, |
| 1547 &if_isnotundefined); |
1423 | 1548 |
1424 Label if_issmi(this), if_isheapnumber(this, Label::kDeferred); | 1549 BIND(&if_isundefined); |
1425 Branch(TaggedIsSmi(start_int), &if_issmi, &if_isheapnumber); | 1550 var_length.Bind(string_length); |
| 1551 Goto(&if_issmi); |
1426 | 1552 |
1427 BIND(&if_issmi); | 1553 BIND(&if_isnotundefined); |
| 1554 var_length.Bind( |
| 1555 ToInteger(context, length, CodeStubAssembler::kTruncateMinusZero)); |
| 1556 } |
| 1557 |
| 1558 Branch(TaggedIsSmi(var_length.value()), &if_issmi, &if_isheapnumber); |
| 1559 |
| 1560 // Set {length} to min(max({length}, 0), {string_length} - {start} |
| 1561 BIND(&if_issmi); |
| 1562 { |
| 1563 Node* const positive_length = SmiMax(var_length.value(), zero); |
| 1564 |
| 1565 Node* const minimal_length = SmiSub(string_length, var_start.value()); |
| 1566 var_length.Bind(SmiMin(positive_length, minimal_length)); |
| 1567 |
| 1568 GotoIfNot(SmiLessThanOrEqual(var_length.value(), zero), &out); |
| 1569 Return(EmptyStringConstant()); |
| 1570 } |
| 1571 |
| 1572 BIND(&if_isheapnumber); |
| 1573 { |
| 1574 // If {length} is a heap number, it is definitely out of bounds. There are |
| 1575 // two cases according to the spec: if it is negative, "" is returned; if |
| 1576 // it is positive, then length is set to {string_length} - {start}. |
| 1577 |
| 1578 CSA_ASSERT(this, IsHeapNumberMap(LoadMap(var_length.value()))); |
| 1579 |
| 1580 Label if_isnegative(this), if_ispositive(this); |
| 1581 Node* const float_zero = Float64Constant(0.); |
| 1582 Node* const length_float = LoadHeapNumberValue(var_length.value()); |
| 1583 Branch(Float64LessThan(length_float, float_zero), &if_isnegative, |
| 1584 &if_ispositive); |
| 1585 |
| 1586 BIND(&if_isnegative); |
| 1587 Return(EmptyStringConstant()); |
| 1588 |
| 1589 BIND(&if_ispositive); |
1428 { | 1590 { |
1429 Node* const length_plus_start = SmiAdd(string_length, start_int); | 1591 var_length.Bind(SmiSub(string_length, var_start.value())); |
1430 var_start.Bind(Select(SmiLessThan(start_int, zero), | 1592 GotoIfNot(SmiLessThanOrEqual(var_length.value(), zero), &out); |
1431 [&] { return SmiMax(length_plus_start, zero); }, | 1593 Return(EmptyStringConstant()); |
1432 [&] { return start_int; }, | |
1433 MachineRepresentation::kTagged)); | |
1434 Goto(&handle_length); | |
1435 } | |
1436 | |
1437 BIND(&if_isheapnumber); | |
1438 { | |
1439 // If {start} is a heap number, it is definitely out of bounds. If it is | |
1440 // negative, {start} = max({string_length} + {start}),0) = 0'. If it is | |
1441 // positive, set {start} to {string_length} which ultimately results in | |
1442 // returning an empty string. | |
1443 Node* const float_zero = Float64Constant(0.); | |
1444 Node* const start_float = LoadHeapNumberValue(start_int); | |
1445 var_start.Bind(SelectTaggedConstant( | |
1446 Float64LessThan(start_float, float_zero), zero, string_length)); | |
1447 Goto(&handle_length); | |
1448 } | 1594 } |
1449 } | 1595 } |
1450 | 1596 |
1451 // Conversions and bounds-checks for {length}. | |
1452 BIND(&handle_length); | |
1453 { | |
1454 Label if_issmi(this), if_isheapnumber(this, Label::kDeferred); | |
1455 | |
1456 // Default to {string_length} if {length} is undefined. | |
1457 { | |
1458 Label if_isundefined(this, Label::kDeferred), if_isnotundefined(this); | |
1459 Branch(WordEqual(length, UndefinedConstant()), &if_isundefined, | |
1460 &if_isnotundefined); | |
1461 | |
1462 BIND(&if_isundefined); | |
1463 var_length.Bind(string_length); | |
1464 Goto(&if_issmi); | |
1465 | |
1466 BIND(&if_isnotundefined); | |
1467 var_length.Bind( | |
1468 ToInteger(context, length, CodeStubAssembler::kTruncateMinusZero)); | |
1469 } | |
1470 | |
1471 Branch(TaggedIsSmi(var_length.value()), &if_issmi, &if_isheapnumber); | |
1472 | |
1473 // Set {length} to min(max({length}, 0), {string_length} - {start} | |
1474 BIND(&if_issmi); | |
1475 { | |
1476 Node* const positive_length = SmiMax(var_length.value(), zero); | |
1477 | |
1478 Node* const minimal_length = SmiSub(string_length, var_start.value()); | |
1479 var_length.Bind(SmiMin(positive_length, minimal_length)); | |
1480 | |
1481 GotoIfNot(SmiLessThanOrEqual(var_length.value(), zero), &out); | |
1482 Return(EmptyStringConstant()); | |
1483 } | |
1484 | |
1485 BIND(&if_isheapnumber); | |
1486 { | |
1487 // If {length} is a heap number, it is definitely out of bounds. There are | |
1488 // two cases according to the spec: if it is negative, "" is returned; if | |
1489 // it is positive, then length is set to {string_length} - {start}. | |
1490 | |
1491 CSA_ASSERT(this, IsHeapNumberMap(LoadMap(var_length.value()))); | |
1492 | |
1493 Label if_isnegative(this), if_ispositive(this); | |
1494 Node* const float_zero = Float64Constant(0.); | |
1495 Node* const length_float = LoadHeapNumberValue(var_length.value()); | |
1496 Branch(Float64LessThan(length_float, float_zero), &if_isnegative, | |
1497 &if_ispositive); | |
1498 | |
1499 BIND(&if_isnegative); | |
1500 Return(EmptyStringConstant()); | |
1501 | |
1502 BIND(&if_ispositive); | |
1503 { | |
1504 var_length.Bind(SmiSub(string_length, var_start.value())); | |
1505 GotoIfNot(SmiLessThanOrEqual(var_length.value(), zero), &out); | |
1506 Return(EmptyStringConstant()); | |
1507 } | |
1508 } | |
1509 } | |
1510 | |
1511 BIND(&out); | 1597 BIND(&out); |
1512 { | 1598 { |
1513 Node* const end = SmiAdd(var_start.value(), var_length.value()); | 1599 Node* const end = SmiAdd(var_start.value(), var_length.value()); |
1514 Node* const result = SubString(context, string, var_start.value(), end); | 1600 Node* const result = SubString(context, string, var_start.value(), end); |
1515 Return(result); | 1601 Return(result); |
1516 } | 1602 } |
1517 } | 1603 } |
1518 | 1604 |
1519 compiler::Node* StringBuiltinsAssembler::ToSmiBetweenZeroAnd(Node* context, | 1605 compiler::Node* StringBuiltinsAssembler::ToSmiBetweenZeroAnd(Node* context, |
1520 Node* value, | 1606 Node* value, |
(...skipping 264 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1785 CallRuntime(Runtime::kThrowIncompatibleMethodReceiver, context, | 1871 CallRuntime(Runtime::kThrowIncompatibleMethodReceiver, context, |
1786 HeapConstant(factory()->NewStringFromAsciiChecked( | 1872 HeapConstant(factory()->NewStringFromAsciiChecked( |
1787 "String Iterator.prototype.next", TENURED)), | 1873 "String Iterator.prototype.next", TENURED)), |
1788 iterator); | 1874 iterator); |
1789 Unreachable(); | 1875 Unreachable(); |
1790 } | 1876 } |
1791 } | 1877 } |
1792 | 1878 |
1793 } // namespace internal | 1879 } // namespace internal |
1794 } // namespace v8 | 1880 } // namespace v8 |
OLD | NEW |