Line data Source code
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-gen.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 : #include "src/objects/oddball.h"
11 :
12 : namespace v8 {
13 : namespace internal {
14 :
15 280 : class ConversionBuiltinsAssembler : public CodeStubAssembler {
16 : public:
17 : explicit ConversionBuiltinsAssembler(compiler::CodeAssemblerState* state)
18 280 : : CodeStubAssembler(state) {}
19 :
20 : protected:
21 : void Generate_NonPrimitiveToPrimitive(Node* context, Node* input,
22 : ToPrimitiveHint hint);
23 :
24 : void Generate_OrdinaryToPrimitive(Node* context, Node* input,
25 : OrdinaryToPrimitiveHint hint);
26 : };
27 :
28 : // ES6 section 7.1.1 ToPrimitive ( input [ , PreferredType ] )
29 168 : void ConversionBuiltinsAssembler::Generate_NonPrimitiveToPrimitive(
30 : Node* context, Node* input, ToPrimitiveHint hint) {
31 : // Lookup the @@toPrimitive property on the {input}.
32 : Node* exotic_to_prim =
33 504 : GetProperty(context, input, factory()->to_primitive_symbol());
34 :
35 : // Check if {exotic_to_prim} is neither null nor undefined.
36 168 : Label ordinary_to_primitive(this);
37 336 : GotoIf(IsNullOrUndefined(exotic_to_prim), &ordinary_to_primitive);
38 : {
39 : // Invoke the {exotic_to_prim} method on the {input} with a string
40 : // representation of the {hint}.
41 : Callable callable =
42 168 : CodeFactory::Call(isolate(), ConvertReceiverMode::kNotNullOrUndefined);
43 168 : Node* hint_string = HeapConstant(factory()->ToPrimitiveHintString(hint));
44 : Node* result =
45 168 : CallJS(callable, context, exotic_to_prim, input, hint_string);
46 :
47 : // Verify that the {result} is actually a primitive.
48 168 : Label if_resultisprimitive(this),
49 168 : if_resultisnotprimitive(this, Label::kDeferred);
50 336 : GotoIf(TaggedIsSmi(result), &if_resultisprimitive);
51 336 : Node* result_instance_type = LoadInstanceType(result);
52 336 : Branch(IsPrimitiveInstanceType(result_instance_type), &if_resultisprimitive,
53 168 : &if_resultisnotprimitive);
54 :
55 168 : BIND(&if_resultisprimitive);
56 : {
57 : // Just return the {result}.
58 168 : Return(result);
59 : }
60 :
61 168 : BIND(&if_resultisnotprimitive);
62 : {
63 : // Somehow the @@toPrimitive method on {input} didn't yield a primitive.
64 168 : ThrowTypeError(context, MessageTemplate::kCannotConvertToPrimitive);
65 : }
66 : }
67 :
68 : // Convert using the OrdinaryToPrimitive algorithm instead.
69 168 : BIND(&ordinary_to_primitive);
70 : {
71 : Callable callable = CodeFactory::OrdinaryToPrimitive(
72 : isolate(), (hint == ToPrimitiveHint::kString)
73 : ? OrdinaryToPrimitiveHint::kString
74 168 : : OrdinaryToPrimitiveHint::kNumber);
75 168 : TailCallStub(callable, context, input);
76 : }
77 168 : }
78 :
79 224 : TF_BUILTIN(NonPrimitiveToPrimitive_Default, ConversionBuiltinsAssembler) {
80 : Node* context = Parameter(Descriptor::kContext);
81 : Node* input = Parameter(Descriptor::kArgument);
82 :
83 56 : Generate_NonPrimitiveToPrimitive(context, input, ToPrimitiveHint::kDefault);
84 56 : }
85 :
86 224 : TF_BUILTIN(NonPrimitiveToPrimitive_Number, ConversionBuiltinsAssembler) {
87 : Node* context = Parameter(Descriptor::kContext);
88 : Node* input = Parameter(Descriptor::kArgument);
89 :
90 56 : Generate_NonPrimitiveToPrimitive(context, input, ToPrimitiveHint::kNumber);
91 56 : }
92 :
93 224 : TF_BUILTIN(NonPrimitiveToPrimitive_String, ConversionBuiltinsAssembler) {
94 : Node* context = Parameter(Descriptor::kContext);
95 : Node* input = Parameter(Descriptor::kArgument);
96 :
97 56 : Generate_NonPrimitiveToPrimitive(context, input, ToPrimitiveHint::kString);
98 56 : }
99 :
100 168 : TF_BUILTIN(StringToNumber, CodeStubAssembler) {
101 56 : TNode<String> input = CAST(Parameter(Descriptor::kArgument));
102 :
103 112 : Return(StringToNumber(input));
104 56 : }
105 :
106 168 : TF_BUILTIN(ToName, CodeStubAssembler) {
107 : Node* context = Parameter(Descriptor::kContext);
108 : Node* input = Parameter(Descriptor::kArgument);
109 :
110 112 : VARIABLE(var_input, MachineRepresentation::kTagged, input);
111 56 : Label loop(this, &var_input);
112 56 : Goto(&loop);
113 56 : BIND(&loop);
114 : {
115 : // Load the current {input} value.
116 56 : Node* input = var_input.value();
117 :
118 : // Dispatch based on the type of the {input.}
119 56 : Label if_inputisbigint(this), if_inputisname(this), if_inputisnumber(this),
120 56 : if_inputisoddball(this), if_inputisreceiver(this, Label::kDeferred);
121 112 : GotoIf(TaggedIsSmi(input), &if_inputisnumber);
122 112 : Node* input_instance_type = LoadInstanceType(input);
123 : STATIC_ASSERT(FIRST_NAME_TYPE == FIRST_TYPE);
124 112 : GotoIf(IsNameInstanceType(input_instance_type), &if_inputisname);
125 112 : GotoIf(IsJSReceiverInstanceType(input_instance_type), &if_inputisreceiver);
126 112 : GotoIf(IsHeapNumberInstanceType(input_instance_type), &if_inputisnumber);
127 112 : Branch(IsBigIntInstanceType(input_instance_type), &if_inputisbigint,
128 56 : &if_inputisoddball);
129 :
130 56 : BIND(&if_inputisbigint);
131 : {
132 : // We don't have a fast-path for BigInt currently, so just
133 : // tail call to the %ToString runtime function here for now.
134 56 : TailCallRuntime(Runtime::kToString, context, input);
135 : }
136 :
137 56 : BIND(&if_inputisname);
138 : {
139 : // The {input} is already a Name.
140 56 : Return(input);
141 : }
142 :
143 56 : BIND(&if_inputisnumber);
144 : {
145 : // Convert the String {input} to a Number.
146 56 : TailCallBuiltin(Builtins::kNumberToString, context, input);
147 : }
148 :
149 56 : BIND(&if_inputisoddball);
150 : {
151 : // Just return the {input}'s string representation.
152 : CSA_ASSERT(this, IsOddballInstanceType(input_instance_type));
153 56 : Return(LoadObjectField(input, Oddball::kToStringOffset));
154 : }
155 :
156 56 : BIND(&if_inputisreceiver);
157 : {
158 : // Convert the JSReceiver {input} to a primitive first,
159 : // and then run the loop again with the new {input},
160 : // which is then a primitive value.
161 112 : var_input.Bind(CallBuiltin(Builtins::kNonPrimitiveToPrimitive_String,
162 112 : context, input));
163 56 : Goto(&loop);
164 : }
165 : }
166 56 : }
167 :
168 168 : TF_BUILTIN(NonNumberToNumber, CodeStubAssembler) {
169 : Node* context = Parameter(Descriptor::kContext);
170 : Node* input = Parameter(Descriptor::kArgument);
171 :
172 112 : Return(NonNumberToNumber(context, input));
173 56 : }
174 :
175 168 : TF_BUILTIN(NonNumberToNumeric, CodeStubAssembler) {
176 : Node* context = Parameter(Descriptor::kContext);
177 : Node* input = Parameter(Descriptor::kArgument);
178 :
179 112 : Return(NonNumberToNumeric(context, input));
180 56 : }
181 :
182 168 : TF_BUILTIN(ToNumeric, CodeStubAssembler) {
183 : TNode<Context> context = CAST(Parameter(Descriptor::kContext));
184 : TNode<Object> input = CAST(Parameter(Descriptor::kArgument));
185 :
186 112 : Return(Select<Numeric>(
187 112 : IsNumber(input), [=] { return CAST(input); },
188 336 : [=] { return NonNumberToNumeric(context, CAST(input)); }));
189 56 : }
190 :
191 : // ES6 section 7.1.3 ToNumber ( argument )
192 168 : TF_BUILTIN(ToNumber, CodeStubAssembler) {
193 : Node* context = Parameter(Descriptor::kContext);
194 : Node* input = Parameter(Descriptor::kArgument);
195 :
196 112 : Return(ToNumber(context, input));
197 56 : }
198 :
199 : // Like ToNumber, but also converts BigInts.
200 168 : TF_BUILTIN(ToNumberConvertBigInt, CodeStubAssembler) {
201 : Node* context = Parameter(Descriptor::kContext);
202 : Node* input = Parameter(Descriptor::kArgument);
203 :
204 112 : Return(ToNumber(context, input, BigIntHandling::kConvertToNumber));
205 56 : }
206 :
207 : // ES section #sec-tostring-applied-to-the-number-type
208 168 : TF_BUILTIN(NumberToString, CodeStubAssembler) {
209 56 : TNode<Number> input = CAST(Parameter(Descriptor::kArgument));
210 :
211 112 : Return(NumberToString(input));
212 56 : }
213 :
214 : // ES section #sec-tostring
215 168 : TF_BUILTIN(ToString, CodeStubAssembler) {
216 : Node* context = Parameter(Descriptor::kContext);
217 : Node* input = Parameter(Descriptor::kArgument);
218 :
219 112 : Return(ToString(context, input));
220 56 : }
221 :
222 : // 7.1.1.1 OrdinaryToPrimitive ( O, hint )
223 112 : void ConversionBuiltinsAssembler::Generate_OrdinaryToPrimitive(
224 : Node* context, Node* input, OrdinaryToPrimitiveHint hint) {
225 224 : VARIABLE(var_result, MachineRepresentation::kTagged);
226 112 : Label return_result(this, &var_result);
227 :
228 560 : Handle<String> method_names[2];
229 112 : switch (hint) {
230 : case OrdinaryToPrimitiveHint::kNumber:
231 112 : method_names[0] = factory()->valueOf_string();
232 112 : method_names[1] = factory()->toString_string();
233 56 : break;
234 : case OrdinaryToPrimitiveHint::kString:
235 112 : method_names[0] = factory()->toString_string();
236 112 : method_names[1] = factory()->valueOf_string();
237 56 : break;
238 : }
239 560 : for (Handle<String> name : method_names) {
240 : // Lookup the {name} on the {input}.
241 448 : Node* method = GetProperty(context, input, name);
242 :
243 : // Check if the {method} is callable.
244 224 : Label if_methodiscallable(this),
245 224 : if_methodisnotcallable(this, Label::kDeferred);
246 448 : GotoIf(TaggedIsSmi(method), &if_methodisnotcallable);
247 448 : Node* method_map = LoadMap(method);
248 448 : Branch(IsCallableMap(method_map), &if_methodiscallable,
249 224 : &if_methodisnotcallable);
250 :
251 224 : BIND(&if_methodiscallable);
252 : {
253 : // Call the {method} on the {input}.
254 : Callable callable = CodeFactory::Call(
255 224 : isolate(), ConvertReceiverMode::kNotNullOrUndefined);
256 224 : Node* result = CallJS(callable, context, method, input);
257 224 : var_result.Bind(result);
258 :
259 : // Return the {result} if it is a primitive.
260 448 : GotoIf(TaggedIsSmi(result), &return_result);
261 448 : Node* result_instance_type = LoadInstanceType(result);
262 448 : GotoIf(IsPrimitiveInstanceType(result_instance_type), &return_result);
263 : }
264 :
265 : // Just continue with the next {name} if the {method} is not callable.
266 224 : Goto(&if_methodisnotcallable);
267 224 : BIND(&if_methodisnotcallable);
268 : }
269 :
270 112 : ThrowTypeError(context, MessageTemplate::kCannotConvertToPrimitive);
271 :
272 112 : BIND(&return_result);
273 224 : Return(var_result.value());
274 112 : }
275 :
276 224 : TF_BUILTIN(OrdinaryToPrimitive_Number, ConversionBuiltinsAssembler) {
277 : Node* context = Parameter(Descriptor::kContext);
278 : Node* input = Parameter(Descriptor::kArgument);
279 56 : Generate_OrdinaryToPrimitive(context, input,
280 56 : OrdinaryToPrimitiveHint::kNumber);
281 56 : }
282 :
283 224 : TF_BUILTIN(OrdinaryToPrimitive_String, ConversionBuiltinsAssembler) {
284 : Node* context = Parameter(Descriptor::kContext);
285 : Node* input = Parameter(Descriptor::kArgument);
286 56 : Generate_OrdinaryToPrimitive(context, input,
287 56 : OrdinaryToPrimitiveHint::kString);
288 56 : }
289 :
290 : // ES6 section 7.1.2 ToBoolean ( argument )
291 168 : TF_BUILTIN(ToBoolean, CodeStubAssembler) {
292 : Node* value = Parameter(Descriptor::kArgument);
293 :
294 56 : Label return_true(this), return_false(this);
295 56 : BranchIfToBooleanIsTrue(value, &return_true, &return_false);
296 :
297 56 : BIND(&return_true);
298 112 : Return(TrueConstant());
299 :
300 56 : BIND(&return_false);
301 112 : Return(FalseConstant());
302 56 : }
303 :
304 : // ES6 section 7.1.2 ToBoolean ( argument )
305 : // Requires parameter on stack so that it can be used as a continuation from a
306 : // LAZY deopt.
307 168 : TF_BUILTIN(ToBooleanLazyDeoptContinuation, CodeStubAssembler) {
308 : Node* value = Parameter(Descriptor::kArgument);
309 :
310 56 : Label return_true(this), return_false(this);
311 56 : BranchIfToBooleanIsTrue(value, &return_true, &return_false);
312 :
313 56 : BIND(&return_true);
314 112 : Return(TrueConstant());
315 :
316 56 : BIND(&return_false);
317 112 : Return(FalseConstant());
318 56 : }
319 :
320 168 : TF_BUILTIN(ToLength, CodeStubAssembler) {
321 : Node* context = Parameter(Descriptor::kContext);
322 :
323 : // We might need to loop once for ToNumber conversion.
324 112 : VARIABLE(var_len, MachineRepresentation::kTagged,
325 : Parameter(Descriptor::kArgument));
326 56 : Label loop(this, &var_len);
327 56 : Goto(&loop);
328 56 : BIND(&loop);
329 : {
330 : // Shared entry points.
331 56 : Label return_len(this), return_two53minus1(this, Label::kDeferred),
332 56 : return_zero(this, Label::kDeferred);
333 :
334 : // Load the current {len} value.
335 56 : Node* len = var_len.value();
336 :
337 : // Check if {len} is a positive Smi.
338 112 : GotoIf(TaggedIsPositiveSmi(len), &return_len);
339 :
340 : // Check if {len} is a (negative) Smi.
341 112 : GotoIf(TaggedIsSmi(len), &return_zero);
342 :
343 : // Check if {len} is a HeapNumber.
344 56 : Label if_lenisheapnumber(this),
345 56 : if_lenisnotheapnumber(this, Label::kDeferred);
346 112 : Branch(IsHeapNumber(len), &if_lenisheapnumber, &if_lenisnotheapnumber);
347 :
348 56 : BIND(&if_lenisheapnumber);
349 : {
350 : // Load the floating-point value of {len}.
351 112 : Node* len_value = LoadHeapNumberValue(len);
352 :
353 : // Check if {len} is not greater than zero.
354 168 : GotoIfNot(Float64GreaterThan(len_value, Float64Constant(0.0)),
355 56 : &return_zero);
356 :
357 : // Check if {len} is greater than or equal to 2^53-1.
358 112 : GotoIf(Float64GreaterThanOrEqual(len_value,
359 112 : Float64Constant(kMaxSafeInteger)),
360 56 : &return_two53minus1);
361 :
362 : // Round the {len} towards -Infinity.
363 112 : Node* value = Float64Floor(len_value);
364 112 : Node* result = ChangeFloat64ToTagged(value);
365 56 : Return(result);
366 : }
367 :
368 56 : BIND(&if_lenisnotheapnumber);
369 : {
370 : // Need to convert {len} to a Number first.
371 112 : var_len.Bind(CallBuiltin(Builtins::kNonNumberToNumber, context, len));
372 56 : Goto(&loop);
373 : }
374 :
375 56 : BIND(&return_len);
376 112 : Return(var_len.value());
377 :
378 56 : BIND(&return_two53minus1);
379 112 : Return(NumberConstant(kMaxSafeInteger));
380 :
381 56 : BIND(&return_zero);
382 112 : Return(SmiConstant(0));
383 : }
384 56 : }
385 :
386 168 : TF_BUILTIN(ToInteger, CodeStubAssembler) {
387 : Node* context = Parameter(Descriptor::kContext);
388 : Node* input = Parameter(Descriptor::kArgument);
389 :
390 112 : Return(ToInteger(context, input, kNoTruncation));
391 56 : }
392 :
393 168 : TF_BUILTIN(ToInteger_TruncateMinusZero, CodeStubAssembler) {
394 : Node* context = Parameter(Descriptor::kContext);
395 : Node* input = Parameter(Descriptor::kArgument);
396 :
397 112 : Return(ToInteger(context, input, kTruncateMinusZero));
398 56 : }
399 :
400 : // ES6 section 7.1.13 ToObject (argument)
401 224 : TF_BUILTIN(ToObject, CodeStubAssembler) {
402 112 : Label if_smi(this, Label::kDeferred), if_jsreceiver(this),
403 56 : if_noconstructor(this, Label::kDeferred), if_wrapjsvalue(this);
404 :
405 : Node* context = Parameter(Descriptor::kContext);
406 : Node* object = Parameter(Descriptor::kArgument);
407 :
408 112 : VARIABLE(constructor_function_index_var,
409 : MachineType::PointerRepresentation());
410 :
411 112 : GotoIf(TaggedIsSmi(object), &if_smi);
412 :
413 112 : Node* map = LoadMap(object);
414 112 : Node* instance_type = LoadMapInstanceType(map);
415 112 : GotoIf(IsJSReceiverInstanceType(instance_type), &if_jsreceiver);
416 :
417 112 : Node* constructor_function_index = LoadMapConstructorFunctionIndex(map);
418 112 : GotoIf(WordEqual(constructor_function_index,
419 112 : IntPtrConstant(Map::kNoConstructorFunctionIndex)),
420 56 : &if_noconstructor);
421 56 : constructor_function_index_var.Bind(constructor_function_index);
422 56 : Goto(&if_wrapjsvalue);
423 :
424 56 : BIND(&if_smi);
425 : constructor_function_index_var.Bind(
426 112 : IntPtrConstant(Context::NUMBER_FUNCTION_INDEX));
427 56 : Goto(&if_wrapjsvalue);
428 :
429 56 : BIND(&if_wrapjsvalue);
430 56 : TNode<Context> native_context = LoadNativeContext(context);
431 112 : Node* constructor = LoadContextElement(
432 112 : native_context, constructor_function_index_var.value());
433 : Node* initial_map =
434 : LoadObjectField(constructor, JSFunction::kPrototypeOrInitialMapOffset);
435 112 : Node* js_value = Allocate(JSValue::kSize);
436 56 : StoreMapNoWriteBarrier(js_value, initial_map);
437 : StoreObjectFieldRoot(js_value, JSValue::kPropertiesOrHashOffset,
438 56 : RootIndex::kEmptyFixedArray);
439 : StoreObjectFieldRoot(js_value, JSObject::kElementsOffset,
440 56 : RootIndex::kEmptyFixedArray);
441 56 : StoreObjectField(js_value, JSValue::kValueOffset, object);
442 56 : Return(js_value);
443 :
444 56 : BIND(&if_noconstructor);
445 : ThrowTypeError(context, MessageTemplate::kUndefinedOrNullToObject,
446 56 : "ToObject");
447 :
448 56 : BIND(&if_jsreceiver);
449 56 : Return(object);
450 56 : }
451 :
452 : // ES6 section 12.5.5 typeof operator
453 168 : TF_BUILTIN(Typeof, CodeStubAssembler) {
454 : Node* object = Parameter(Descriptor::kObject);
455 :
456 112 : Return(Typeof(object));
457 56 : }
458 :
459 : } // namespace internal
460 59480 : } // namespace v8
|