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 |