1 // Copyright 2017 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-math-gen.h"
6
7 #include "src/builtins/builtins-utils-gen.h"
8 #include "src/builtins/builtins.h"
9 #include "src/code-factory.h"
10 #include "src/code-stub-assembler.h"
11
12 namespace v8 {
13 namespace internal {
14
15 // -----------------------------------------------------------------------------
16 // ES6 section 20.2.2 Function Properties of the Math Object
17
18 // ES6 #sec-math.abs
TF_BUILTIN(MathAbs,CodeStubAssembler)19 TF_BUILTIN(MathAbs, CodeStubAssembler) {
20 Node* context = Parameter(Descriptor::kContext);
21
22 // We might need to loop once for ToNumber conversion.
23 VARIABLE(var_x, MachineRepresentation::kTagged);
24 Label loop(this, &var_x);
25 var_x.Bind(Parameter(Descriptor::kX));
26 Goto(&loop);
27 BIND(&loop);
28 {
29 // Load the current {x} value.
30 Node* x = var_x.value();
31
32 // Check if {x} is a Smi or a HeapObject.
33 Label if_xissmi(this), if_xisnotsmi(this);
34 Branch(TaggedIsSmi(x), &if_xissmi, &if_xisnotsmi);
35
36 BIND(&if_xissmi);
37 {
38 Label if_overflow(this, Label::kDeferred);
39
40 // check if support abs function
41 if (IsIntPtrAbsWithOverflowSupported()) {
42 Node* pair = IntPtrAbsWithOverflow(x);
43 Node* overflow = Projection(1, pair);
44 GotoIf(overflow, &if_overflow);
45
46 // There is a Smi representation for negated {x}.
47 Node* result = Projection(0, pair);
48 Return(BitcastWordToTagged(result));
49
50 } else {
51 // Check if {x} is already positive.
52 Label if_xispositive(this), if_xisnotpositive(this);
53 BranchIfSmiLessThanOrEqual(SmiConstant(0), CAST(x), &if_xispositive,
54 &if_xisnotpositive);
55
56 BIND(&if_xispositive);
57 {
58 // Just return the input {x}.
59 Return(x);
60 }
61
62 BIND(&if_xisnotpositive);
63 {
64 // Try to negate the {x} value.
65 TNode<Smi> result = TrySmiSub(SmiConstant(0), CAST(x), &if_overflow);
66 Return(result);
67 }
68 }
69
70 BIND(&if_overflow);
71 { Return(NumberConstant(0.0 - Smi::kMinValue)); }
72 }
73
74 BIND(&if_xisnotsmi);
75 {
76 // Check if {x} is a HeapNumber.
77 Label if_xisheapnumber(this), if_xisnotheapnumber(this, Label::kDeferred);
78 Branch(IsHeapNumber(x), &if_xisheapnumber, &if_xisnotheapnumber);
79
80 BIND(&if_xisheapnumber);
81 {
82 Node* x_value = LoadHeapNumberValue(x);
83 Node* value = Float64Abs(x_value);
84 Node* result = AllocateHeapNumberWithValue(value);
85 Return(result);
86 }
87
88 BIND(&if_xisnotheapnumber);
89 {
90 // Need to convert {x} to a Number first.
91 var_x.Bind(CallBuiltin(Builtins::kNonNumberToNumber, context, x));
92 Goto(&loop);
93 }
94 }
95 }
96 }
97
MathRoundingOperation(Node * context,Node * x,TNode<Float64T> (CodeStubAssembler::* float64op)(SloppyTNode<Float64T>))98 void MathBuiltinsAssembler::MathRoundingOperation(
99 Node* context, Node* x,
100 TNode<Float64T> (CodeStubAssembler::*float64op)(SloppyTNode<Float64T>)) {
101 // We might need to loop once for ToNumber conversion.
102 VARIABLE(var_x, MachineRepresentation::kTagged, x);
103 Label loop(this, &var_x);
104 Goto(&loop);
105 BIND(&loop);
106 {
107 // Load the current {x} value.
108 Node* x = var_x.value();
109
110 // Check if {x} is a Smi or a HeapObject.
111 Label if_xissmi(this), if_xisnotsmi(this);
112 Branch(TaggedIsSmi(x), &if_xissmi, &if_xisnotsmi);
113
114 BIND(&if_xissmi);
115 {
116 // Nothing to do when {x} is a Smi.
117 Return(x);
118 }
119
120 BIND(&if_xisnotsmi);
121 {
122 // Check if {x} is a HeapNumber.
123 Label if_xisheapnumber(this), if_xisnotheapnumber(this, Label::kDeferred);
124 Branch(IsHeapNumber(x), &if_xisheapnumber, &if_xisnotheapnumber);
125
126 BIND(&if_xisheapnumber);
127 {
128 Node* x_value = LoadHeapNumberValue(x);
129 Node* value = (this->*float64op)(x_value);
130 Node* result = ChangeFloat64ToTagged(value);
131 Return(result);
132 }
133
134 BIND(&if_xisnotheapnumber);
135 {
136 // Need to convert {x} to a Number first.
137 var_x.Bind(CallBuiltin(Builtins::kNonNumberToNumber, context, x));
138 Goto(&loop);
139 }
140 }
141 }
142 }
143
MathUnaryOperation(Node * context,Node * x,TNode<Float64T> (CodeStubAssembler::* float64op)(SloppyTNode<Float64T>))144 void MathBuiltinsAssembler::MathUnaryOperation(
145 Node* context, Node* x,
146 TNode<Float64T> (CodeStubAssembler::*float64op)(SloppyTNode<Float64T>)) {
147 Node* x_value = TruncateTaggedToFloat64(context, x);
148 Node* value = (this->*float64op)(x_value);
149 Node* result = AllocateHeapNumberWithValue(value);
150 Return(result);
151 }
152
MathMaxMin(Node * context,Node * argc,TNode<Float64T> (CodeStubAssembler::* float64op)(SloppyTNode<Float64T>,SloppyTNode<Float64T>),double default_val)153 void MathBuiltinsAssembler::MathMaxMin(
154 Node* context, Node* argc,
155 TNode<Float64T> (CodeStubAssembler::*float64op)(SloppyTNode<Float64T>,
156 SloppyTNode<Float64T>),
157 double default_val) {
158 CodeStubArguments arguments(this, ChangeInt32ToIntPtr(argc));
159 argc = arguments.GetLength(INTPTR_PARAMETERS);
160
161 VARIABLE(result, MachineRepresentation::kFloat64);
162 result.Bind(Float64Constant(default_val));
163
164 CodeStubAssembler::VariableList vars({&result}, zone());
165 arguments.ForEach(vars, [=, &result](Node* arg) {
166 Node* float_value = TruncateTaggedToFloat64(context, arg);
167 result.Bind((this->*float64op)(result.value(), float_value));
168 });
169
170 arguments.PopAndReturn(ChangeFloat64ToTagged(result.value()));
171 }
172
173 // ES6 #sec-math.acos
TF_BUILTIN(MathAcos,MathBuiltinsAssembler)174 TF_BUILTIN(MathAcos, MathBuiltinsAssembler) {
175 Node* context = Parameter(Descriptor::kContext);
176 Node* x = Parameter(Descriptor::kX);
177 MathUnaryOperation(context, x, &CodeStubAssembler::Float64Acos);
178 }
179
180 // ES6 #sec-math.acosh
TF_BUILTIN(MathAcosh,MathBuiltinsAssembler)181 TF_BUILTIN(MathAcosh, MathBuiltinsAssembler) {
182 Node* context = Parameter(Descriptor::kContext);
183 Node* x = Parameter(Descriptor::kX);
184 MathUnaryOperation(context, x, &CodeStubAssembler::Float64Acosh);
185 }
186
187 // ES6 #sec-math.asin
TF_BUILTIN(MathAsin,MathBuiltinsAssembler)188 TF_BUILTIN(MathAsin, MathBuiltinsAssembler) {
189 Node* context = Parameter(Descriptor::kContext);
190 Node* x = Parameter(Descriptor::kX);
191 MathUnaryOperation(context, x, &CodeStubAssembler::Float64Asin);
192 }
193
194 // ES6 #sec-math.asinh
TF_BUILTIN(MathAsinh,MathBuiltinsAssembler)195 TF_BUILTIN(MathAsinh, MathBuiltinsAssembler) {
196 Node* context = Parameter(Descriptor::kContext);
197 Node* x = Parameter(Descriptor::kX);
198 MathUnaryOperation(context, x, &CodeStubAssembler::Float64Asinh);
199 }
200
201 // ES6 #sec-math.atan
TF_BUILTIN(MathAtan,MathBuiltinsAssembler)202 TF_BUILTIN(MathAtan, MathBuiltinsAssembler) {
203 Node* context = Parameter(Descriptor::kContext);
204 Node* x = Parameter(Descriptor::kX);
205 MathUnaryOperation(context, x, &CodeStubAssembler::Float64Atan);
206 }
207
208 // ES6 #sec-math.atanh
TF_BUILTIN(MathAtanh,MathBuiltinsAssembler)209 TF_BUILTIN(MathAtanh, MathBuiltinsAssembler) {
210 Node* context = Parameter(Descriptor::kContext);
211 Node* x = Parameter(Descriptor::kX);
212 MathUnaryOperation(context, x, &CodeStubAssembler::Float64Atanh);
213 }
214
215 // ES6 #sec-math.atan2
TF_BUILTIN(MathAtan2,CodeStubAssembler)216 TF_BUILTIN(MathAtan2, CodeStubAssembler) {
217 Node* context = Parameter(Descriptor::kContext);
218 Node* y = Parameter(Descriptor::kY);
219 Node* x = Parameter(Descriptor::kX);
220
221 Node* y_value = TruncateTaggedToFloat64(context, y);
222 Node* x_value = TruncateTaggedToFloat64(context, x);
223 Node* value = Float64Atan2(y_value, x_value);
224 Node* result = AllocateHeapNumberWithValue(value);
225 Return(result);
226 }
227
228 // ES6 #sec-math.ceil
TF_BUILTIN(MathCeil,MathBuiltinsAssembler)229 TF_BUILTIN(MathCeil, MathBuiltinsAssembler) {
230 Node* context = Parameter(Descriptor::kContext);
231 Node* x = Parameter(Descriptor::kX);
232 MathRoundingOperation(context, x, &CodeStubAssembler::Float64Ceil);
233 }
234
235 // ES6 #sec-math.cbrt
TF_BUILTIN(MathCbrt,MathBuiltinsAssembler)236 TF_BUILTIN(MathCbrt, MathBuiltinsAssembler) {
237 Node* context = Parameter(Descriptor::kContext);
238 Node* x = Parameter(Descriptor::kX);
239 MathUnaryOperation(context, x, &CodeStubAssembler::Float64Cbrt);
240 }
241
242 // ES6 #sec-math.clz32
TF_BUILTIN(MathClz32,CodeStubAssembler)243 TF_BUILTIN(MathClz32, CodeStubAssembler) {
244 Node* context = Parameter(Descriptor::kContext);
245
246 // Shared entry point for the clz32 operation.
247 VARIABLE(var_clz32_x, MachineRepresentation::kWord32);
248 Label do_clz32(this);
249
250 // We might need to loop once for ToNumber conversion.
251 VARIABLE(var_x, MachineRepresentation::kTagged);
252 Label loop(this, &var_x);
253 var_x.Bind(Parameter(Descriptor::kX));
254 Goto(&loop);
255 BIND(&loop);
256 {
257 // Load the current {x} value.
258 Node* x = var_x.value();
259
260 // Check if {x} is a Smi or a HeapObject.
261 Label if_xissmi(this), if_xisnotsmi(this);
262 Branch(TaggedIsSmi(x), &if_xissmi, &if_xisnotsmi);
263
264 BIND(&if_xissmi);
265 {
266 var_clz32_x.Bind(SmiToInt32(x));
267 Goto(&do_clz32);
268 }
269
270 BIND(&if_xisnotsmi);
271 {
272 // Check if {x} is a HeapNumber.
273 Label if_xisheapnumber(this), if_xisnotheapnumber(this, Label::kDeferred);
274 Branch(IsHeapNumber(x), &if_xisheapnumber, &if_xisnotheapnumber);
275
276 BIND(&if_xisheapnumber);
277 {
278 var_clz32_x.Bind(TruncateHeapNumberValueToWord32(x));
279 Goto(&do_clz32);
280 }
281
282 BIND(&if_xisnotheapnumber);
283 {
284 // Need to convert {x} to a Number first.
285 var_x.Bind(CallBuiltin(Builtins::kNonNumberToNumber, context, x));
286 Goto(&loop);
287 }
288 }
289 }
290
291 BIND(&do_clz32);
292 {
293 Node* x_value = var_clz32_x.value();
294 Node* value = Word32Clz(x_value);
295 Node* result = ChangeInt32ToTagged(value);
296 Return(result);
297 }
298 }
299
300 // ES6 #sec-math.cos
TF_BUILTIN(MathCos,MathBuiltinsAssembler)301 TF_BUILTIN(MathCos, MathBuiltinsAssembler) {
302 Node* context = Parameter(Descriptor::kContext);
303 Node* x = Parameter(Descriptor::kX);
304 MathUnaryOperation(context, x, &CodeStubAssembler::Float64Cos);
305 }
306
307 // ES6 #sec-math.cosh
TF_BUILTIN(MathCosh,MathBuiltinsAssembler)308 TF_BUILTIN(MathCosh, MathBuiltinsAssembler) {
309 Node* context = Parameter(Descriptor::kContext);
310 Node* x = Parameter(Descriptor::kX);
311 MathUnaryOperation(context, x, &CodeStubAssembler::Float64Cosh);
312 }
313
314 // ES6 #sec-math.exp
TF_BUILTIN(MathExp,MathBuiltinsAssembler)315 TF_BUILTIN(MathExp, MathBuiltinsAssembler) {
316 Node* context = Parameter(Descriptor::kContext);
317 Node* x = Parameter(Descriptor::kX);
318 MathUnaryOperation(context, x, &CodeStubAssembler::Float64Exp);
319 }
320
321 // ES6 #sec-math.expm1
TF_BUILTIN(MathExpm1,MathBuiltinsAssembler)322 TF_BUILTIN(MathExpm1, MathBuiltinsAssembler) {
323 Node* context = Parameter(Descriptor::kContext);
324 Node* x = Parameter(Descriptor::kX);
325 MathUnaryOperation(context, x, &CodeStubAssembler::Float64Expm1);
326 }
327
328 // ES6 #sec-math.floor
TF_BUILTIN(MathFloor,MathBuiltinsAssembler)329 TF_BUILTIN(MathFloor, MathBuiltinsAssembler) {
330 Node* context = Parameter(Descriptor::kContext);
331 Node* x = Parameter(Descriptor::kX);
332 MathRoundingOperation(context, x, &CodeStubAssembler::Float64Floor);
333 }
334
335 // ES6 #sec-math.fround
TF_BUILTIN(MathFround,CodeStubAssembler)336 TF_BUILTIN(MathFround, CodeStubAssembler) {
337 Node* context = Parameter(Descriptor::kContext);
338 Node* x = Parameter(Descriptor::kX);
339 Node* x_value = TruncateTaggedToFloat64(context, x);
340 Node* value32 = TruncateFloat64ToFloat32(x_value);
341 Node* value = ChangeFloat32ToFloat64(value32);
342 Node* result = AllocateHeapNumberWithValue(value);
343 Return(result);
344 }
345
346 // ES6 #sec-math.imul
TF_BUILTIN(MathImul,CodeStubAssembler)347 TF_BUILTIN(MathImul, CodeStubAssembler) {
348 Node* context = Parameter(Descriptor::kContext);
349 Node* x = Parameter(Descriptor::kX);
350 Node* y = Parameter(Descriptor::kY);
351 Node* x_value = TruncateTaggedToWord32(context, x);
352 Node* y_value = TruncateTaggedToWord32(context, y);
353 Node* value = Int32Mul(x_value, y_value);
354 Node* result = ChangeInt32ToTagged(value);
355 Return(result);
356 }
357
358 // ES6 #sec-math.log
TF_BUILTIN(MathLog,MathBuiltinsAssembler)359 TF_BUILTIN(MathLog, MathBuiltinsAssembler) {
360 Node* context = Parameter(Descriptor::kContext);
361 Node* x = Parameter(Descriptor::kX);
362 MathUnaryOperation(context, x, &CodeStubAssembler::Float64Log);
363 }
364
365 // ES6 #sec-math.log1p
TF_BUILTIN(MathLog1p,MathBuiltinsAssembler)366 TF_BUILTIN(MathLog1p, MathBuiltinsAssembler) {
367 Node* context = Parameter(Descriptor::kContext);
368 Node* x = Parameter(Descriptor::kX);
369 MathUnaryOperation(context, x, &CodeStubAssembler::Float64Log1p);
370 }
371
372 // ES6 #sec-math.log10
TF_BUILTIN(MathLog10,MathBuiltinsAssembler)373 TF_BUILTIN(MathLog10, MathBuiltinsAssembler) {
374 Node* context = Parameter(Descriptor::kContext);
375 Node* x = Parameter(Descriptor::kX);
376 MathUnaryOperation(context, x, &CodeStubAssembler::Float64Log10);
377 }
378
379 // ES6 #sec-math.log2
TF_BUILTIN(MathLog2,MathBuiltinsAssembler)380 TF_BUILTIN(MathLog2, MathBuiltinsAssembler) {
381 Node* context = Parameter(Descriptor::kContext);
382 Node* x = Parameter(Descriptor::kX);
383 MathUnaryOperation(context, x, &CodeStubAssembler::Float64Log2);
384 }
385
MathPow(Node * context,Node * base,Node * exponent)386 CodeStubAssembler::Node* MathBuiltinsAssembler::MathPow(Node* context,
387 Node* base,
388 Node* exponent) {
389 Node* base_value = TruncateTaggedToFloat64(context, base);
390 Node* exponent_value = TruncateTaggedToFloat64(context, exponent);
391 Node* value = Float64Pow(base_value, exponent_value);
392 return ChangeFloat64ToTagged(value);
393 }
394
395 // ES6 #sec-math.pow
TF_BUILTIN(MathPow,MathBuiltinsAssembler)396 TF_BUILTIN(MathPow, MathBuiltinsAssembler) {
397 Return(MathPow(Parameter(Descriptor::kContext), Parameter(Descriptor::kBase),
398 Parameter(Descriptor::kExponent)));
399 }
400
401 // ES6 #sec-math.random
TF_BUILTIN(MathRandom,CodeStubAssembler)402 TF_BUILTIN(MathRandom, CodeStubAssembler) {
403 Node* context = Parameter(Descriptor::kContext);
404 Node* native_context = LoadNativeContext(context);
405
406 // Load cache index.
407 TVARIABLE(Smi, smi_index);
408 smi_index = CAST(
409 LoadContextElement(native_context, Context::MATH_RANDOM_INDEX_INDEX));
410
411 // Cached random numbers are exhausted if index is 0. Go to slow path.
412 Label if_cached(this);
413 GotoIf(SmiAbove(smi_index.value(), SmiConstant(0)), &if_cached);
414
415 // Cache exhausted, populate the cache. Return value is the new index.
416 smi_index = CAST(CallRuntime(Runtime::kGenerateRandomNumbers, context));
417 Goto(&if_cached);
418
419 // Compute next index by decrement.
420 BIND(&if_cached);
421 TNode<Smi> new_smi_index = SmiSub(smi_index.value(), SmiConstant(1));
422 StoreContextElement(native_context, Context::MATH_RANDOM_INDEX_INDEX,
423 new_smi_index);
424
425 // Load and return next cached random number.
426 Node* array =
427 LoadContextElement(native_context, Context::MATH_RANDOM_CACHE_INDEX);
428 Node* random = LoadFixedDoubleArrayElement(
429 array, new_smi_index, MachineType::Float64(), 0, SMI_PARAMETERS);
430 Return(AllocateHeapNumberWithValue(random));
431 }
432
433 // ES6 #sec-math.round
TF_BUILTIN(MathRound,MathBuiltinsAssembler)434 TF_BUILTIN(MathRound, MathBuiltinsAssembler) {
435 Node* context = Parameter(Descriptor::kContext);
436 Node* x = Parameter(Descriptor::kX);
437 MathRoundingOperation(context, x, &CodeStubAssembler::Float64Round);
438 }
439
440 // ES6 #sec-math.sign
TF_BUILTIN(MathSign,CodeStubAssembler)441 TF_BUILTIN(MathSign, CodeStubAssembler) {
442 // Convert the {x} value to a Number.
443 Node* context = Parameter(Descriptor::kContext);
444 Node* x = Parameter(Descriptor::kX);
445 Node* x_value = TruncateTaggedToFloat64(context, x);
446
447 // Return -1 if {x} is negative, 1 if {x} is positive, or {x} itself.
448 Label if_xisnegative(this), if_xispositive(this);
449 GotoIf(Float64LessThan(x_value, Float64Constant(0.0)), &if_xisnegative);
450 GotoIf(Float64LessThan(Float64Constant(0.0), x_value), &if_xispositive);
451 Return(ChangeFloat64ToTagged(x_value));
452
453 BIND(&if_xisnegative);
454 Return(SmiConstant(-1));
455
456 BIND(&if_xispositive);
457 Return(SmiConstant(1));
458 }
459
460 // ES6 #sec-math.sin
TF_BUILTIN(MathSin,MathBuiltinsAssembler)461 TF_BUILTIN(MathSin, MathBuiltinsAssembler) {
462 Node* context = Parameter(Descriptor::kContext);
463 Node* x = Parameter(Descriptor::kX);
464 MathUnaryOperation(context, x, &CodeStubAssembler::Float64Sin);
465 }
466
467 // ES6 #sec-math.sinh
TF_BUILTIN(MathSinh,MathBuiltinsAssembler)468 TF_BUILTIN(MathSinh, MathBuiltinsAssembler) {
469 Node* context = Parameter(Descriptor::kContext);
470 Node* x = Parameter(Descriptor::kX);
471 MathUnaryOperation(context, x, &CodeStubAssembler::Float64Sinh);
472 }
473
474 // ES6 #sec-math.sqrt
TF_BUILTIN(MathSqrt,MathBuiltinsAssembler)475 TF_BUILTIN(MathSqrt, MathBuiltinsAssembler) {
476 Node* context = Parameter(Descriptor::kContext);
477 Node* x = Parameter(Descriptor::kX);
478 MathUnaryOperation(context, x, &CodeStubAssembler::Float64Sqrt);
479 }
480
481 // ES6 #sec-math.tan
TF_BUILTIN(MathTan,MathBuiltinsAssembler)482 TF_BUILTIN(MathTan, MathBuiltinsAssembler) {
483 Node* context = Parameter(Descriptor::kContext);
484 Node* x = Parameter(Descriptor::kX);
485 MathUnaryOperation(context, x, &CodeStubAssembler::Float64Tan);
486 }
487
488 // ES6 #sec-math.tanh
TF_BUILTIN(MathTanh,MathBuiltinsAssembler)489 TF_BUILTIN(MathTanh, MathBuiltinsAssembler) {
490 Node* context = Parameter(Descriptor::kContext);
491 Node* x = Parameter(Descriptor::kX);
492 MathUnaryOperation(context, x, &CodeStubAssembler::Float64Tanh);
493 }
494
495 // ES6 #sec-math.trunc
TF_BUILTIN(MathTrunc,MathBuiltinsAssembler)496 TF_BUILTIN(MathTrunc, MathBuiltinsAssembler) {
497 Node* context = Parameter(Descriptor::kContext);
498 Node* x = Parameter(Descriptor::kX);
499 MathRoundingOperation(context, x, &CodeStubAssembler::Float64Trunc);
500 }
501
502 // ES6 #sec-math.max
TF_BUILTIN(MathMax,MathBuiltinsAssembler)503 TF_BUILTIN(MathMax, MathBuiltinsAssembler) {
504 // TODO(ishell): use constants from Descriptor once the JSFunction linkage
505 // arguments are reordered.
506 Node* context = Parameter(Descriptor::kContext);
507 Node* argc = Parameter(Descriptor::kJSActualArgumentsCount);
508 MathMaxMin(context, argc, &CodeStubAssembler::Float64Max, -1.0 * V8_INFINITY);
509 }
510
511 // ES6 #sec-math.min
TF_BUILTIN(MathMin,MathBuiltinsAssembler)512 TF_BUILTIN(MathMin, MathBuiltinsAssembler) {
513 // TODO(ishell): use constants from Descriptor once the JSFunction linkage
514 // arguments are reordered.
515 Node* context = Parameter(Descriptor::kContext);
516 Node* argc = Parameter(Descriptor::kJSActualArgumentsCount);
517 MathMaxMin(context, argc, &CodeStubAssembler::Float64Min, V8_INFINITY);
518 }
519
520 } // namespace internal
521 } // namespace v8
522