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