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.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 |
OLD | NEW |