• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2020 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
5namespace runtime {
6extern transitioning runtime ToString(Context, BigInt): String;
7}
8
9extern enum OrdinaryToPrimitiveHint { kString, kNumber }
10
11extern macro OrdinaryToPrimitive(implicit context: Context)(
12    JSAny, constexpr OrdinaryToPrimitiveHint): JSPrimitive;
13
14namespace conversion {
15
16builtin StringToNumber(implicit context: Context)(input: String): Number {
17  return ::StringToNumber(input);
18}
19
20transitioning builtin NonNumberToNumber(implicit context: Context)(
21    input: JSAnyNotNumber): Number {
22  return ::NonNumberToNumber(input);
23}
24
25transitioning builtin NonNumberToNumeric(implicit context: Context)(
26    input: JSAnyNotNumber): Numeric {
27  return ::NonNumberToNumeric(input);
28}
29
30transitioning builtin ToNumeric(implicit context: Context)(input: JSAny):
31    Numeric {
32  typeswitch (input) {
33    case (n: Number): {
34      return n;
35    }
36    case (h: JSAnyNotNumber): {
37      return conversion::NonNumberToNumeric(h);
38    }
39  }
40}
41
42// ES section #sec-tostring-applied-to-the-number-type
43builtin NumberToString(implicit context: Context)(input: Number): String {
44  return ::NumberToString(input);
45}
46
47// ES6 section 7.1.2 ToBoolean ( argument )
48builtin ToBoolean(implicit context: Context)(input: JSAny): Boolean {
49  BranchIfToBooleanIsTrue(input) otherwise return TrueConstant(),
50      return FalseConstant();
51}
52
53transitioning builtin ToLength(implicit context: Context)(input: JSAny):
54    Number {
55  // We might need to loop once for ToNumber conversion.
56  let x: JSAny = input;
57  while (true) {
58    typeswitch (x) {
59      case (s: Smi): {
60        if (s < 0) return 0;
61        return s;
62      }
63      case (h: HeapNumber): {
64        let value: float64 = Convert<float64>(h);
65        // The sense of this test is important for the NaN and -0 cases.
66        if (!(value > 0)) return 0;
67        if (value > kMaxSafeInteger) return kMaxSafeInteger;
68        value = math::Float64Floor(value);
69        return ChangeFloat64ToTagged(value);
70      }
71      case (h: JSAnyNotNumber): {
72        x = ::NonNumberToNumber(h);
73      }
74    }
75  }
76  VerifiedUnreachable();
77}
78
79transitioning builtin ToName(implicit context: Context)(input: JSAny): Name {
80  // We might need to loop once for ToNumber conversion.
81  let x: JSAny = input;
82  while (true) {
83    typeswitch (x) {
84      case (n: Name): {
85        return n;
86      }
87      case (n: Number): {
88        return ::NumberToString(n);
89      }
90      case (b: BigInt): {
91        // We don't have a fast-path for BigInt currently, so just
92        // tail call to the %ToString runtime function here for now.
93        tail runtime::ToString(context, b);
94      }
95      case (o: Oddball): {
96        return o.to_string;
97      }
98      case (o: JSReceiver): {
99        x = NonPrimitiveToPrimitive_String(o);
100      }
101    }
102  }
103  VerifiedUnreachable();
104}
105
106const kNoConstructorFunctionIndex:
107    constexpr int31 generates 'Map::kNoConstructorFunctionIndex';
108
109// ES6 section 7.1.13 ToObject (argument)
110transitioning builtin ToObject(implicit context: Context)(input: JSAny):
111    JSReceiver {
112  try {
113    typeswitch (input) {
114      case (Smi): {
115        goto WrapPrimitive(ContextSlot::NUMBER_FUNCTION_INDEX);
116      }
117      case (o: JSReceiver): {
118        return o;
119      }
120      case (o: JSAnyNotSmi): {
121        const index: intptr = Convert<intptr>(
122            o.map.in_object_properties_start_or_constructor_function_index);
123        if (index != kNoConstructorFunctionIndex)
124          goto WrapPrimitive(
125              %RawDownCast<Slot<NativeContext, JSFunction>>(index));
126        ThrowTypeError(MessageTemplate::kUndefinedOrNullToObject, 'ToObject');
127      }
128    }
129  } label WrapPrimitive(constructorIndex: Slot<NativeContext, JSFunction>) {
130    const constructor = *NativeContextSlot(constructorIndex);
131    const map: Map = UnsafeCast<Map>(constructor.prototype_or_initial_map);
132    const wrapper =
133        UnsafeCast<JSPrimitiveWrapper>(AllocateFastOrSlowJSObjectFromMap(map));
134    wrapper.value = input;
135    return wrapper;
136  }
137}
138
139// ES6 section 7.1.1 ToPrimitive ( input [ , PreferredType ] )
140
141transitioning macro TryGetExoticToPrimitive(implicit context: Context)(
142    input: JSAny): JSAny labels OrdinaryToPrimitive {
143  // Look up the @@toPrimitive property.
144  const exoticToPrimitive: JSAny =
145      GetProperty(input, ToPrimitiveSymbolConstant());
146  if (IsNullOrUndefined(exoticToPrimitive)) goto OrdinaryToPrimitive;
147  return exoticToPrimitive;
148}
149
150transitioning macro CallExoticToPrimitive(implicit context: Context)(
151    input: JSAny, exoticToPrimitive: JSAny, hint: String): JSPrimitive {
152  // Invoke the exoticToPrimitive method on the input with a string
153  // representation of the hint.
154  const result: JSAny = Call(context, exoticToPrimitive, input, hint);
155
156  // Verify that the result is primitive.
157  typeswitch (result) {
158    case (o: JSPrimitive): {
159      return o;
160    }
161    case (JSReceiver): {
162      // Somehow the @@toPrimitive method on input didn't yield a primitive.
163      ThrowTypeError(MessageTemplate::kCannotConvertToPrimitive);
164    }
165  }
166}
167
168transitioning builtin NonPrimitiveToPrimitive_Default(
169    implicit context: Context)(input: JSReceiver): JSPrimitive {
170  const exoticToPrimitive: JSAny = TryGetExoticToPrimitive(input)
171      otherwise return OrdinaryToPrimitive_Number(input);
172  return CallExoticToPrimitive(
173      input, exoticToPrimitive, DefaultStringConstant());
174}
175
176transitioning builtin NonPrimitiveToPrimitive_Number(implicit context: Context)(
177    input: JSReceiver): JSPrimitive {
178  const exoticToPrimitive: JSAny = TryGetExoticToPrimitive(input)
179      otherwise return OrdinaryToPrimitive_Number(input);
180  return CallExoticToPrimitive(
181      input, exoticToPrimitive, NumberStringConstant());
182}
183
184transitioning builtin NonPrimitiveToPrimitive_String(implicit context: Context)(
185    input: JSReceiver): JSPrimitive {
186  const exoticToPrimitive: JSAny = TryGetExoticToPrimitive(input)
187      otherwise return OrdinaryToPrimitive_String(input);
188  return CallExoticToPrimitive(
189      input, exoticToPrimitive, StringStringConstant());
190}
191
192// 7.1.1.1 OrdinaryToPrimitive ( O, hint )
193
194transitioning macro TryToPrimitiveMethod(implicit context: Context)(
195    input: JSAny, name: String): JSPrimitive labels Continue {
196  const method: JSAny = GetProperty(input, name);
197  typeswitch (method) {
198    case (Callable): {
199      const value: JSAny = Call(context, method, input);
200      return Cast<JSPrimitive>(value) otherwise Continue;
201    }
202    case (JSAny): {
203      goto Continue;
204    }
205  }
206}
207
208transitioning builtin OrdinaryToPrimitive_Number(implicit context: Context)(
209    input: JSAny): JSPrimitive {
210  try {
211    return TryToPrimitiveMethod(input, ValueOfStringConstant())
212        otherwise String;
213  } label String {
214    return TryToPrimitiveMethod(input, ToStringStringConstant())
215        otherwise Throw;
216  } label Throw {
217    ThrowTypeError(MessageTemplate::kCannotConvertToPrimitive);
218  }
219}
220
221transitioning builtin OrdinaryToPrimitive_String(implicit context: Context)(
222    input: JSAny): JSPrimitive {
223  try {
224    return TryToPrimitiveMethod(input, ToStringStringConstant())
225        otherwise String;
226  } label String {
227    return TryToPrimitiveMethod(input, ValueOfStringConstant()) otherwise Throw;
228  } label Throw {
229    ThrowTypeError(MessageTemplate::kCannotConvertToPrimitive);
230  }
231}
232
233}  // namespace conversion
234