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