| 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 |