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 #include "src/code-factory.h" 8 9 namespace v8 { 10 namespace internal { 11 12 Handle<Code> Builtins::NonPrimitiveToPrimitive(ToPrimitiveHint hint) { 13 switch (hint) { 14 case ToPrimitiveHint::kDefault: 15 return NonPrimitiveToPrimitive_Default(); 16 case ToPrimitiveHint::kNumber: 17 return NonPrimitiveToPrimitive_Number(); 18 case ToPrimitiveHint::kString: 19 return NonPrimitiveToPrimitive_String(); 20 } 21 UNREACHABLE(); 22 return Handle<Code>::null(); 23 } 24 25 namespace { 26 // ES6 section 7.1.1 ToPrimitive ( input [ , PreferredType ] ) 27 void Generate_NonPrimitiveToPrimitive(CodeStubAssembler* assembler, 28 ToPrimitiveHint hint) { 29 typedef CodeStubAssembler::Label Label; 30 typedef compiler::Node Node; 31 typedef TypeConversionDescriptor Descriptor; 32 33 Node* input = assembler->Parameter(Descriptor::kArgument); 34 Node* context = assembler->Parameter(Descriptor::kContext); 35 36 // Lookup the @@toPrimitive property on the {input}. 37 Callable callable = CodeFactory::GetProperty(assembler->isolate()); 38 Node* to_primitive_symbol = 39 assembler->HeapConstant(assembler->factory()->to_primitive_symbol()); 40 Node* exotic_to_prim = 41 assembler->CallStub(callable, context, input, to_primitive_symbol); 42 43 // Check if {exotic_to_prim} is neither null nor undefined. 44 Label ordinary_to_primitive(assembler); 45 assembler->GotoIf( 46 assembler->WordEqual(exotic_to_prim, assembler->NullConstant()), 47 &ordinary_to_primitive); 48 assembler->GotoIf( 49 assembler->WordEqual(exotic_to_prim, assembler->UndefinedConstant()), 50 &ordinary_to_primitive); 51 { 52 // Invoke the {exotic_to_prim} method on the {input} with a string 53 // representation of the {hint}. 54 Callable callable = CodeFactory::Call(assembler->isolate()); 55 Node* hint_string = assembler->HeapConstant( 56 assembler->factory()->ToPrimitiveHintString(hint)); 57 Node* result = assembler->CallJS(callable, context, exotic_to_prim, input, 58 hint_string); 59 60 // Verify that the {result} is actually a primitive. 61 Label if_resultisprimitive(assembler), 62 if_resultisnotprimitive(assembler, Label::kDeferred); 63 assembler->GotoIf(assembler->TaggedIsSmi(result), &if_resultisprimitive); 64 Node* result_instance_type = assembler->LoadInstanceType(result); 65 STATIC_ASSERT(FIRST_PRIMITIVE_TYPE == FIRST_TYPE); 66 assembler->Branch(assembler->Int32LessThanOrEqual( 67 result_instance_type, 68 assembler->Int32Constant(LAST_PRIMITIVE_TYPE)), 69 &if_resultisprimitive, &if_resultisnotprimitive); 70 71 assembler->Bind(&if_resultisprimitive); 72 { 73 // Just return the {result}. 74 assembler->Return(result); 75 } 76 77 assembler->Bind(&if_resultisnotprimitive); 78 { 79 // Somehow the @@toPrimitive method on {input} didn't yield a primitive. 80 assembler->TailCallRuntime(Runtime::kThrowCannotConvertToPrimitive, 81 context); 82 } 83 } 84 85 // Convert using the OrdinaryToPrimitive algorithm instead. 86 assembler->Bind(&ordinary_to_primitive); 87 { 88 Callable callable = CodeFactory::OrdinaryToPrimitive( 89 assembler->isolate(), (hint == ToPrimitiveHint::kString) 90 ? OrdinaryToPrimitiveHint::kString 91 : OrdinaryToPrimitiveHint::kNumber); 92 assembler->TailCallStub(callable, context, input); 93 } 94 } 95 } // anonymous namespace 96 97 void Builtins::Generate_NonPrimitiveToPrimitive_Default( 98 CodeStubAssembler* assembler) { 99 Generate_NonPrimitiveToPrimitive(assembler, ToPrimitiveHint::kDefault); 100 } 101 102 void Builtins::Generate_NonPrimitiveToPrimitive_Number( 103 CodeStubAssembler* assembler) { 104 Generate_NonPrimitiveToPrimitive(assembler, ToPrimitiveHint::kNumber); 105 } 106 107 void Builtins::Generate_NonPrimitiveToPrimitive_String( 108 CodeStubAssembler* assembler) { 109 Generate_NonPrimitiveToPrimitive(assembler, ToPrimitiveHint::kString); 110 } 111 112 void Builtins::Generate_StringToNumber(CodeStubAssembler* assembler) { 113 typedef compiler::Node Node; 114 typedef TypeConversionDescriptor Descriptor; 115 116 Node* input = assembler->Parameter(Descriptor::kArgument); 117 Node* context = assembler->Parameter(Descriptor::kContext); 118 119 assembler->Return(assembler->StringToNumber(context, input)); 120 } 121 122 void Builtins::Generate_ToName(CodeStubAssembler* assembler) { 123 typedef compiler::Node Node; 124 typedef TypeConversionDescriptor Descriptor; 125 126 Node* input = assembler->Parameter(Descriptor::kArgument); 127 Node* context = assembler->Parameter(Descriptor::kContext); 128 129 assembler->Return(assembler->ToName(context, input)); 130 } 131 132 // static 133 void Builtins::Generate_NonNumberToNumber(CodeStubAssembler* assembler) { 134 typedef compiler::Node Node; 135 typedef TypeConversionDescriptor Descriptor; 136 137 Node* input = assembler->Parameter(Descriptor::kArgument); 138 Node* context = assembler->Parameter(Descriptor::kContext); 139 140 assembler->Return(assembler->NonNumberToNumber(context, input)); 141 } 142 143 // ES6 section 7.1.3 ToNumber ( argument ) 144 void Builtins::Generate_ToNumber(CodeStubAssembler* assembler) { 145 typedef compiler::Node Node; 146 typedef TypeConversionDescriptor Descriptor; 147 148 Node* input = assembler->Parameter(Descriptor::kArgument); 149 Node* context = assembler->Parameter(Descriptor::kContext); 150 151 assembler->Return(assembler->ToNumber(context, input)); 152 } 153 154 void Builtins::Generate_ToString(CodeStubAssembler* assembler) { 155 typedef CodeStubAssembler::Label Label; 156 typedef compiler::Node Node; 157 typedef TypeConversionDescriptor Descriptor; 158 159 Node* input = assembler->Parameter(Descriptor::kArgument); 160 Node* context = assembler->Parameter(Descriptor::kContext); 161 162 Label is_number(assembler); 163 Label runtime(assembler); 164 165 assembler->GotoIf(assembler->TaggedIsSmi(input), &is_number); 166 167 Node* input_map = assembler->LoadMap(input); 168 Node* input_instance_type = assembler->LoadMapInstanceType(input_map); 169 170 Label not_string(assembler); 171 assembler->GotoUnless(assembler->IsStringInstanceType(input_instance_type), 172 ¬_string); 173 assembler->Return(input); 174 175 Label not_heap_number(assembler); 176 177 assembler->Bind(¬_string); 178 { 179 assembler->GotoUnless( 180 assembler->WordEqual(input_map, assembler->HeapNumberMapConstant()), 181 ¬_heap_number); 182 assembler->Goto(&is_number); 183 } 184 185 assembler->Bind(&is_number); 186 { assembler->Return(assembler->NumberToString(context, input)); } 187 188 assembler->Bind(¬_heap_number); 189 { 190 assembler->GotoIf( 191 assembler->Word32NotEqual(input_instance_type, 192 assembler->Int32Constant(ODDBALL_TYPE)), 193 &runtime); 194 assembler->Return( 195 assembler->LoadObjectField(input, Oddball::kToStringOffset)); 196 } 197 198 assembler->Bind(&runtime); 199 { 200 assembler->Return( 201 assembler->CallRuntime(Runtime::kToString, context, input)); 202 } 203 } 204 205 Handle<Code> Builtins::OrdinaryToPrimitive(OrdinaryToPrimitiveHint hint) { 206 switch (hint) { 207 case OrdinaryToPrimitiveHint::kNumber: 208 return OrdinaryToPrimitive_Number(); 209 case OrdinaryToPrimitiveHint::kString: 210 return OrdinaryToPrimitive_String(); 211 } 212 UNREACHABLE(); 213 return Handle<Code>::null(); 214 } 215 216 namespace { 217 // 7.1.1.1 OrdinaryToPrimitive ( O, hint ) 218 void Generate_OrdinaryToPrimitive(CodeStubAssembler* assembler, 219 OrdinaryToPrimitiveHint hint) { 220 typedef CodeStubAssembler::Label Label; 221 typedef compiler::Node Node; 222 typedef CodeStubAssembler::Variable Variable; 223 typedef TypeConversionDescriptor Descriptor; 224 225 Node* input = assembler->Parameter(Descriptor::kArgument); 226 Node* context = assembler->Parameter(Descriptor::kContext); 227 228 Variable var_result(assembler, MachineRepresentation::kTagged); 229 Label return_result(assembler, &var_result); 230 231 Handle<String> method_names[2]; 232 switch (hint) { 233 case OrdinaryToPrimitiveHint::kNumber: 234 method_names[0] = assembler->factory()->valueOf_string(); 235 method_names[1] = assembler->factory()->toString_string(); 236 break; 237 case OrdinaryToPrimitiveHint::kString: 238 method_names[0] = assembler->factory()->toString_string(); 239 method_names[1] = assembler->factory()->valueOf_string(); 240 break; 241 } 242 for (Handle<String> name : method_names) { 243 // Lookup the {name} on the {input}. 244 Callable callable = CodeFactory::GetProperty(assembler->isolate()); 245 Node* name_string = assembler->HeapConstant(name); 246 Node* method = assembler->CallStub(callable, context, input, name_string); 247 248 // Check if the {method} is callable. 249 Label if_methodiscallable(assembler), 250 if_methodisnotcallable(assembler, Label::kDeferred); 251 assembler->GotoIf(assembler->TaggedIsSmi(method), &if_methodisnotcallable); 252 Node* method_map = assembler->LoadMap(method); 253 assembler->Branch(assembler->IsCallableMap(method_map), 254 &if_methodiscallable, &if_methodisnotcallable); 255 256 assembler->Bind(&if_methodiscallable); 257 { 258 // Call the {method} on the {input}. 259 Callable callable = CodeFactory::Call(assembler->isolate()); 260 Node* result = assembler->CallJS(callable, context, method, input); 261 var_result.Bind(result); 262 263 // Return the {result} if it is a primitive. 264 assembler->GotoIf(assembler->TaggedIsSmi(result), &return_result); 265 Node* result_instance_type = assembler->LoadInstanceType(result); 266 STATIC_ASSERT(FIRST_PRIMITIVE_TYPE == FIRST_TYPE); 267 assembler->GotoIf(assembler->Int32LessThanOrEqual( 268 result_instance_type, 269 assembler->Int32Constant(LAST_PRIMITIVE_TYPE)), 270 &return_result); 271 } 272 273 // Just continue with the next {name} if the {method} is not callable. 274 assembler->Goto(&if_methodisnotcallable); 275 assembler->Bind(&if_methodisnotcallable); 276 } 277 278 assembler->TailCallRuntime(Runtime::kThrowCannotConvertToPrimitive, context); 279 280 assembler->Bind(&return_result); 281 assembler->Return(var_result.value()); 282 } 283 } // anonymous namespace 284 285 void Builtins::Generate_OrdinaryToPrimitive_Number( 286 CodeStubAssembler* assembler) { 287 Generate_OrdinaryToPrimitive(assembler, OrdinaryToPrimitiveHint::kNumber); 288 } 289 290 void Builtins::Generate_OrdinaryToPrimitive_String( 291 CodeStubAssembler* assembler) { 292 Generate_OrdinaryToPrimitive(assembler, OrdinaryToPrimitiveHint::kString); 293 } 294 295 // ES6 section 7.1.2 ToBoolean ( argument ) 296 void Builtins::Generate_ToBoolean(CodeStubAssembler* assembler) { 297 typedef compiler::Node Node; 298 typedef CodeStubAssembler::Label Label; 299 typedef TypeConversionDescriptor Descriptor; 300 301 Node* value = assembler->Parameter(Descriptor::kArgument); 302 303 Label return_true(assembler), return_false(assembler); 304 assembler->BranchIfToBooleanIsTrue(value, &return_true, &return_false); 305 306 assembler->Bind(&return_true); 307 assembler->Return(assembler->BooleanConstant(true)); 308 309 assembler->Bind(&return_false); 310 assembler->Return(assembler->BooleanConstant(false)); 311 } 312 313 void Builtins::Generate_ToLength(CodeStubAssembler* assembler) { 314 typedef CodeStubAssembler::Label Label; 315 typedef compiler::Node Node; 316 typedef CodeStubAssembler::Variable Variable; 317 318 Node* context = assembler->Parameter(1); 319 320 // We might need to loop once for ToNumber conversion. 321 Variable var_len(assembler, MachineRepresentation::kTagged); 322 Label loop(assembler, &var_len); 323 var_len.Bind(assembler->Parameter(0)); 324 assembler->Goto(&loop); 325 assembler->Bind(&loop); 326 { 327 // Shared entry points. 328 Label return_len(assembler), 329 return_two53minus1(assembler, Label::kDeferred), 330 return_zero(assembler, Label::kDeferred); 331 332 // Load the current {len} value. 333 Node* len = var_len.value(); 334 335 // Check if {len} is a positive Smi. 336 assembler->GotoIf(assembler->WordIsPositiveSmi(len), &return_len); 337 338 // Check if {len} is a (negative) Smi. 339 assembler->GotoIf(assembler->TaggedIsSmi(len), &return_zero); 340 341 // Check if {len} is a HeapNumber. 342 Label if_lenisheapnumber(assembler), 343 if_lenisnotheapnumber(assembler, Label::kDeferred); 344 assembler->Branch(assembler->IsHeapNumberMap(assembler->LoadMap(len)), 345 &if_lenisheapnumber, &if_lenisnotheapnumber); 346 347 assembler->Bind(&if_lenisheapnumber); 348 { 349 // Load the floating-point value of {len}. 350 Node* len_value = assembler->LoadHeapNumberValue(len); 351 352 // Check if {len} is not greater than zero. 353 assembler->GotoUnless(assembler->Float64GreaterThan( 354 len_value, assembler->Float64Constant(0.0)), 355 &return_zero); 356 357 // Check if {len} is greater than or equal to 2^53-1. 358 assembler->GotoIf( 359 assembler->Float64GreaterThanOrEqual( 360 len_value, assembler->Float64Constant(kMaxSafeInteger)), 361 &return_two53minus1); 362 363 // Round the {len} towards -Infinity. 364 Node* value = assembler->Float64Floor(len_value); 365 Node* result = assembler->ChangeFloat64ToTagged(value); 366 assembler->Return(result); 367 } 368 369 assembler->Bind(&if_lenisnotheapnumber); 370 { 371 // Need to convert {len} to a Number first. 372 Callable callable = CodeFactory::NonNumberToNumber(assembler->isolate()); 373 var_len.Bind(assembler->CallStub(callable, context, len)); 374 assembler->Goto(&loop); 375 } 376 377 assembler->Bind(&return_len); 378 assembler->Return(var_len.value()); 379 380 assembler->Bind(&return_two53minus1); 381 assembler->Return(assembler->NumberConstant(kMaxSafeInteger)); 382 383 assembler->Bind(&return_zero); 384 assembler->Return(assembler->SmiConstant(Smi::kZero)); 385 } 386 } 387 388 void Builtins::Generate_ToInteger(CodeStubAssembler* assembler) { 389 typedef TypeConversionDescriptor Descriptor; 390 391 compiler::Node* input = assembler->Parameter(Descriptor::kArgument); 392 compiler::Node* context = assembler->Parameter(Descriptor::kContext); 393 394 assembler->Return(assembler->ToInteger(context, input)); 395 } 396 397 // ES6 section 7.1.13 ToObject (argument) 398 void Builtins::Generate_ToObject(CodeStubAssembler* assembler) { 399 typedef compiler::Node Node; 400 typedef CodeStubAssembler::Label Label; 401 typedef CodeStubAssembler::Variable Variable; 402 typedef TypeConversionDescriptor Descriptor; 403 404 Label if_number(assembler, Label::kDeferred), if_notsmi(assembler), 405 if_jsreceiver(assembler), if_noconstructor(assembler, Label::kDeferred), 406 if_wrapjsvalue(assembler); 407 408 Node* object = assembler->Parameter(Descriptor::kArgument); 409 Node* context = assembler->Parameter(Descriptor::kContext); 410 411 Variable constructor_function_index_var(assembler, 412 MachineType::PointerRepresentation()); 413 414 assembler->Branch(assembler->TaggedIsSmi(object), &if_number, &if_notsmi); 415 416 assembler->Bind(&if_notsmi); 417 Node* map = assembler->LoadMap(object); 418 419 assembler->GotoIf(assembler->IsHeapNumberMap(map), &if_number); 420 421 Node* instance_type = assembler->LoadMapInstanceType(map); 422 assembler->GotoIf(assembler->IsJSReceiverInstanceType(instance_type), 423 &if_jsreceiver); 424 425 Node* constructor_function_index = 426 assembler->LoadMapConstructorFunctionIndex(map); 427 assembler->GotoIf(assembler->WordEqual(constructor_function_index, 428 assembler->IntPtrConstant( 429 Map::kNoConstructorFunctionIndex)), 430 &if_noconstructor); 431 constructor_function_index_var.Bind(constructor_function_index); 432 assembler->Goto(&if_wrapjsvalue); 433 434 assembler->Bind(&if_number); 435 constructor_function_index_var.Bind( 436 assembler->IntPtrConstant(Context::NUMBER_FUNCTION_INDEX)); 437 assembler->Goto(&if_wrapjsvalue); 438 439 assembler->Bind(&if_wrapjsvalue); 440 Node* native_context = assembler->LoadNativeContext(context); 441 Node* constructor = assembler->LoadFixedArrayElement( 442 native_context, constructor_function_index_var.value(), 0, 443 CodeStubAssembler::INTPTR_PARAMETERS); 444 Node* initial_map = assembler->LoadObjectField( 445 constructor, JSFunction::kPrototypeOrInitialMapOffset); 446 Node* js_value = assembler->Allocate(JSValue::kSize); 447 assembler->StoreMapNoWriteBarrier(js_value, initial_map); 448 assembler->StoreObjectFieldRoot(js_value, JSValue::kPropertiesOffset, 449 Heap::kEmptyFixedArrayRootIndex); 450 assembler->StoreObjectFieldRoot(js_value, JSObject::kElementsOffset, 451 Heap::kEmptyFixedArrayRootIndex); 452 assembler->StoreObjectField(js_value, JSValue::kValueOffset, object); 453 assembler->Return(js_value); 454 455 assembler->Bind(&if_noconstructor); 456 assembler->TailCallRuntime( 457 Runtime::kThrowUndefinedOrNullToObject, context, 458 assembler->HeapConstant(assembler->factory()->NewStringFromAsciiChecked( 459 "ToObject", TENURED))); 460 461 assembler->Bind(&if_jsreceiver); 462 assembler->Return(object); 463 } 464 465 // ES6 section 12.5.5 typeof operator 466 void Builtins::Generate_Typeof(CodeStubAssembler* assembler) { 467 typedef compiler::Node Node; 468 typedef TypeofDescriptor Descriptor; 469 470 Node* object = assembler->Parameter(Descriptor::kObject); 471 Node* context = assembler->Parameter(Descriptor::kContext); 472 473 assembler->Return(assembler->Typeof(object, context)); 474 } 475 476 } // namespace internal 477 } // namespace v8 478