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