OLD | NEW |
1 // Copyright 2016 the V8 project authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "src/builtins/builtins-utils.h" | 5 #include "src/builtins/builtins-utils.h" |
6 #include "src/builtins/builtins.h" | 6 #include "src/builtins/builtins.h" |
7 #include "src/code-factory.h" | |
8 #include "src/code-stub-assembler.h" | |
9 #include "src/counters.h" | 7 #include "src/counters.h" |
10 #include "src/objects-inl.h" | 8 #include "src/objects-inl.h" |
11 | 9 |
12 namespace v8 { | 10 namespace v8 { |
13 namespace internal { | 11 namespace internal { |
14 | 12 |
15 // ----------------------------------------------------------------------------- | 13 // ----------------------------------------------------------------------------- |
16 // ES6 section 20.2.2 Function Properties of the Math Object | 14 // ES6 section 20.2.2 Function Properties of the Math Object |
17 | 15 |
18 class MathBuiltinsAssembler : public CodeStubAssembler { | |
19 public: | |
20 explicit MathBuiltinsAssembler(compiler::CodeAssemblerState* state) | |
21 : CodeStubAssembler(state) {} | |
22 | |
23 protected: | |
24 void MathRoundingOperation(Node* (CodeStubAssembler::*float64op)(Node*)); | |
25 void MathUnaryOperation(Node* (CodeStubAssembler::*float64op)(Node*)); | |
26 void MathMaxMin(Node* (CodeStubAssembler::*float64op)(Node*, Node*), | |
27 double default_val); | |
28 }; | |
29 | |
30 // ES6 section - 20.2.2.1 Math.abs ( x ) | |
31 TF_BUILTIN(MathAbs, CodeStubAssembler) { | |
32 Node* context = Parameter(4); | |
33 | |
34 // We might need to loop once for ToNumber conversion. | |
35 Variable var_x(this, MachineRepresentation::kTagged); | |
36 Label loop(this, &var_x); | |
37 var_x.Bind(Parameter(1)); | |
38 Goto(&loop); | |
39 Bind(&loop); | |
40 { | |
41 // Load the current {x} value. | |
42 Node* x = var_x.value(); | |
43 | |
44 // Check if {x} is a Smi or a HeapObject. | |
45 Label if_xissmi(this), if_xisnotsmi(this); | |
46 Branch(TaggedIsSmi(x), &if_xissmi, &if_xisnotsmi); | |
47 | |
48 Bind(&if_xissmi); | |
49 { | |
50 // Check if {x} is already positive. | |
51 Label if_xispositive(this), if_xisnotpositive(this); | |
52 BranchIfSmiLessThanOrEqual(SmiConstant(Smi::FromInt(0)), x, | |
53 &if_xispositive, &if_xisnotpositive); | |
54 | |
55 Bind(&if_xispositive); | |
56 { | |
57 // Just return the input {x}. | |
58 Return(x); | |
59 } | |
60 | |
61 Bind(&if_xisnotpositive); | |
62 { | |
63 // Try to negate the {x} value. | |
64 Node* pair = | |
65 IntPtrSubWithOverflow(IntPtrConstant(0), BitcastTaggedToWord(x)); | |
66 Node* overflow = Projection(1, pair); | |
67 Label if_overflow(this, Label::kDeferred), if_notoverflow(this); | |
68 Branch(overflow, &if_overflow, &if_notoverflow); | |
69 | |
70 Bind(&if_notoverflow); | |
71 { | |
72 // There is a Smi representation for negated {x}. | |
73 Node* result = Projection(0, pair); | |
74 Return(BitcastWordToTagged(result)); | |
75 } | |
76 | |
77 Bind(&if_overflow); | |
78 { Return(NumberConstant(0.0 - Smi::kMinValue)); } | |
79 } | |
80 } | |
81 | |
82 Bind(&if_xisnotsmi); | |
83 { | |
84 // Check if {x} is a HeapNumber. | |
85 Label if_xisheapnumber(this), if_xisnotheapnumber(this, Label::kDeferred); | |
86 Branch(IsHeapNumberMap(LoadMap(x)), &if_xisheapnumber, | |
87 &if_xisnotheapnumber); | |
88 | |
89 Bind(&if_xisheapnumber); | |
90 { | |
91 Node* x_value = LoadHeapNumberValue(x); | |
92 Node* value = Float64Abs(x_value); | |
93 Node* result = AllocateHeapNumberWithValue(value); | |
94 Return(result); | |
95 } | |
96 | |
97 Bind(&if_xisnotheapnumber); | |
98 { | |
99 // Need to convert {x} to a Number first. | |
100 Callable callable = CodeFactory::NonNumberToNumber(isolate()); | |
101 var_x.Bind(CallStub(callable, context, x)); | |
102 Goto(&loop); | |
103 } | |
104 } | |
105 } | |
106 } | |
107 | |
108 void MathBuiltinsAssembler::MathRoundingOperation( | |
109 Node* (CodeStubAssembler::*float64op)(Node*)) { | |
110 Node* context = Parameter(4); | |
111 | |
112 // We might need to loop once for ToNumber conversion. | |
113 Variable var_x(this, MachineRepresentation::kTagged); | |
114 Label loop(this, &var_x); | |
115 var_x.Bind(Parameter(1)); | |
116 Goto(&loop); | |
117 Bind(&loop); | |
118 { | |
119 // Load the current {x} value. | |
120 Node* x = var_x.value(); | |
121 | |
122 // Check if {x} is a Smi or a HeapObject. | |
123 Label if_xissmi(this), if_xisnotsmi(this); | |
124 Branch(TaggedIsSmi(x), &if_xissmi, &if_xisnotsmi); | |
125 | |
126 Bind(&if_xissmi); | |
127 { | |
128 // Nothing to do when {x} is a Smi. | |
129 Return(x); | |
130 } | |
131 | |
132 Bind(&if_xisnotsmi); | |
133 { | |
134 // Check if {x} is a HeapNumber. | |
135 Label if_xisheapnumber(this), if_xisnotheapnumber(this, Label::kDeferred); | |
136 Branch(IsHeapNumberMap(LoadMap(x)), &if_xisheapnumber, | |
137 &if_xisnotheapnumber); | |
138 | |
139 Bind(&if_xisheapnumber); | |
140 { | |
141 Node* x_value = LoadHeapNumberValue(x); | |
142 Node* value = (this->*float64op)(x_value); | |
143 Node* result = ChangeFloat64ToTagged(value); | |
144 Return(result); | |
145 } | |
146 | |
147 Bind(&if_xisnotheapnumber); | |
148 { | |
149 // Need to convert {x} to a Number first. | |
150 Callable callable = CodeFactory::NonNumberToNumber(isolate()); | |
151 var_x.Bind(CallStub(callable, context, x)); | |
152 Goto(&loop); | |
153 } | |
154 } | |
155 } | |
156 } | |
157 | |
158 void MathBuiltinsAssembler::MathUnaryOperation( | |
159 Node* (CodeStubAssembler::*float64op)(Node*)) { | |
160 Node* x = Parameter(1); | |
161 Node* context = Parameter(4); | |
162 Node* x_value = TruncateTaggedToFloat64(context, x); | |
163 Node* value = (this->*float64op)(x_value); | |
164 Node* result = AllocateHeapNumberWithValue(value); | |
165 Return(result); | |
166 } | |
167 | |
168 void MathBuiltinsAssembler::MathMaxMin( | |
169 Node* (CodeStubAssembler::*float64op)(Node*, Node*), double default_val) { | |
170 Node* argc = Parameter(BuiltinDescriptor::kArgumentsCount); | |
171 Node* context = Parameter(BuiltinDescriptor::kContext); | |
172 | |
173 CodeStubArguments arguments(this, ChangeInt32ToIntPtr(argc)); | |
174 argc = arguments.GetLength(); | |
175 | |
176 Variable result(this, MachineRepresentation::kFloat64); | |
177 result.Bind(Float64Constant(default_val)); | |
178 | |
179 CodeStubAssembler::VariableList vars({&result}, zone()); | |
180 arguments.ForEach(vars, [this, float64op, context, &result](Node* arg) { | |
181 Node* float_value = TruncateTaggedToFloat64(context, arg); | |
182 result.Bind((this->*float64op)(result.value(), float_value)); | |
183 }); | |
184 | |
185 arguments.PopAndReturn(ChangeFloat64ToTagged(result.value())); | |
186 } | |
187 | |
188 // ES6 section 20.2.2.2 Math.acos ( x ) | |
189 TF_BUILTIN(MathAcos, MathBuiltinsAssembler) { | |
190 MathUnaryOperation(&CodeStubAssembler::Float64Acos); | |
191 } | |
192 | |
193 // ES6 section 20.2.2.3 Math.acosh ( x ) | |
194 TF_BUILTIN(MathAcosh, MathBuiltinsAssembler) { | |
195 MathUnaryOperation(&CodeStubAssembler::Float64Acosh); | |
196 } | |
197 | |
198 // ES6 section 20.2.2.4 Math.asin ( x ) | |
199 TF_BUILTIN(MathAsin, MathBuiltinsAssembler) { | |
200 MathUnaryOperation(&CodeStubAssembler::Float64Asin); | |
201 } | |
202 | |
203 // ES6 section 20.2.2.5 Math.asinh ( x ) | |
204 TF_BUILTIN(MathAsinh, MathBuiltinsAssembler) { | |
205 MathUnaryOperation(&CodeStubAssembler::Float64Asinh); | |
206 } | |
207 // ES6 section 20.2.2.6 Math.atan ( x ) | |
208 TF_BUILTIN(MathAtan, MathBuiltinsAssembler) { | |
209 MathUnaryOperation(&CodeStubAssembler::Float64Atan); | |
210 } | |
211 | |
212 // ES6 section 20.2.2.7 Math.atanh ( x ) | |
213 TF_BUILTIN(MathAtanh, MathBuiltinsAssembler) { | |
214 MathUnaryOperation(&CodeStubAssembler::Float64Atanh); | |
215 } | |
216 | |
217 // ES6 section 20.2.2.8 Math.atan2 ( y, x ) | |
218 TF_BUILTIN(MathAtan2, CodeStubAssembler) { | |
219 Node* y = Parameter(1); | |
220 Node* x = Parameter(2); | |
221 Node* context = Parameter(5); | |
222 | |
223 Node* y_value = TruncateTaggedToFloat64(context, y); | |
224 Node* x_value = TruncateTaggedToFloat64(context, x); | |
225 Node* value = Float64Atan2(y_value, x_value); | |
226 Node* result = AllocateHeapNumberWithValue(value); | |
227 Return(result); | |
228 } | |
229 | |
230 // ES6 section 20.2.2.10 Math.ceil ( x ) | |
231 TF_BUILTIN(MathCeil, MathBuiltinsAssembler) { | |
232 MathRoundingOperation(&CodeStubAssembler::Float64Ceil); | |
233 } | |
234 | |
235 // ES6 section 20.2.2.9 Math.cbrt ( x ) | |
236 TF_BUILTIN(MathCbrt, MathBuiltinsAssembler) { | |
237 MathUnaryOperation(&CodeStubAssembler::Float64Cbrt); | |
238 } | |
239 | |
240 // ES6 section 20.2.2.11 Math.clz32 ( x ) | |
241 TF_BUILTIN(MathClz32, CodeStubAssembler) { | |
242 Node* context = Parameter(4); | |
243 | |
244 // Shared entry point for the clz32 operation. | |
245 Variable var_clz32_x(this, MachineRepresentation::kWord32); | |
246 Label do_clz32(this); | |
247 | |
248 // We might need to loop once for ToNumber conversion. | |
249 Variable var_x(this, MachineRepresentation::kTagged); | |
250 Label loop(this, &var_x); | |
251 var_x.Bind(Parameter(1)); | |
252 Goto(&loop); | |
253 Bind(&loop); | |
254 { | |
255 // Load the current {x} value. | |
256 Node* x = var_x.value(); | |
257 | |
258 // Check if {x} is a Smi or a HeapObject. | |
259 Label if_xissmi(this), if_xisnotsmi(this); | |
260 Branch(TaggedIsSmi(x), &if_xissmi, &if_xisnotsmi); | |
261 | |
262 Bind(&if_xissmi); | |
263 { | |
264 var_clz32_x.Bind(SmiToWord32(x)); | |
265 Goto(&do_clz32); | |
266 } | |
267 | |
268 Bind(&if_xisnotsmi); | |
269 { | |
270 // Check if {x} is a HeapNumber. | |
271 Label if_xisheapnumber(this), if_xisnotheapnumber(this, Label::kDeferred); | |
272 Branch(IsHeapNumberMap(LoadMap(x)), &if_xisheapnumber, | |
273 &if_xisnotheapnumber); | |
274 | |
275 Bind(&if_xisheapnumber); | |
276 { | |
277 var_clz32_x.Bind(TruncateHeapNumberValueToWord32(x)); | |
278 Goto(&do_clz32); | |
279 } | |
280 | |
281 Bind(&if_xisnotheapnumber); | |
282 { | |
283 // Need to convert {x} to a Number first. | |
284 Callable callable = CodeFactory::NonNumberToNumber(isolate()); | |
285 var_x.Bind(CallStub(callable, 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 section 20.2.2.12 Math.cos ( x ) | |
301 TF_BUILTIN(MathCos, MathBuiltinsAssembler) { | |
302 MathUnaryOperation(&CodeStubAssembler::Float64Cos); | |
303 } | |
304 | |
305 // ES6 section 20.2.2.13 Math.cosh ( x ) | |
306 TF_BUILTIN(MathCosh, MathBuiltinsAssembler) { | |
307 MathUnaryOperation(&CodeStubAssembler::Float64Cosh); | |
308 } | |
309 | |
310 // ES6 section 20.2.2.14 Math.exp ( x ) | |
311 TF_BUILTIN(MathExp, MathBuiltinsAssembler) { | |
312 MathUnaryOperation(&CodeStubAssembler::Float64Exp); | |
313 } | |
314 | |
315 // ES6 section 20.2.2.15 Math.expm1 ( x ) | |
316 TF_BUILTIN(MathExpm1, MathBuiltinsAssembler) { | |
317 MathUnaryOperation(&CodeStubAssembler::Float64Expm1); | |
318 } | |
319 | |
320 // ES6 section 20.2.2.16 Math.floor ( x ) | |
321 TF_BUILTIN(MathFloor, MathBuiltinsAssembler) { | |
322 MathRoundingOperation(&CodeStubAssembler::Float64Floor); | |
323 } | |
324 | |
325 // ES6 section 20.2.2.17 Math.fround ( x ) | |
326 TF_BUILTIN(MathFround, CodeStubAssembler) { | |
327 Node* x = Parameter(1); | |
328 Node* context = Parameter(4); | |
329 Node* x_value = TruncateTaggedToFloat64(context, x); | |
330 Node* value32 = TruncateFloat64ToFloat32(x_value); | |
331 Node* value = ChangeFloat32ToFloat64(value32); | |
332 Node* result = AllocateHeapNumberWithValue(value); | |
333 Return(result); | |
334 } | |
335 | |
336 // ES6 section 20.2.2.18 Math.hypot ( value1, value2, ...values ) | 16 // ES6 section 20.2.2.18 Math.hypot ( value1, value2, ...values ) |
337 BUILTIN(MathHypot) { | 17 BUILTIN(MathHypot) { |
338 HandleScope scope(isolate); | 18 HandleScope scope(isolate); |
339 int const length = args.length() - 1; | 19 int const length = args.length() - 1; |
340 if (length == 0) return Smi::kZero; | 20 if (length == 0) return Smi::kZero; |
341 DCHECK_LT(0, length); | 21 DCHECK_LT(0, length); |
342 double max = 0; | 22 double max = 0; |
343 bool one_arg_is_nan = false; | 23 bool one_arg_is_nan = false; |
344 List<double> abs_values(length); | 24 List<double> abs_values(length); |
345 for (int i = 0; i < length; i++) { | 25 for (int i = 0; i < length; i++) { |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
378 double n = abs_values.at(i) / max; | 58 double n = abs_values.at(i) / max; |
379 double summand = n * n - compensation; | 59 double summand = n * n - compensation; |
380 double preliminary = sum + summand; | 60 double preliminary = sum + summand; |
381 compensation = (preliminary - sum) - summand; | 61 compensation = (preliminary - sum) - summand; |
382 sum = preliminary; | 62 sum = preliminary; |
383 } | 63 } |
384 | 64 |
385 return *isolate->factory()->NewNumber(std::sqrt(sum) * max); | 65 return *isolate->factory()->NewNumber(std::sqrt(sum) * max); |
386 } | 66 } |
387 | 67 |
388 // ES6 section 20.2.2.19 Math.imul ( x, y ) | |
389 TF_BUILTIN(MathImul, CodeStubAssembler) { | |
390 Node* x = Parameter(1); | |
391 Node* y = Parameter(2); | |
392 Node* context = Parameter(5); | |
393 Node* x_value = TruncateTaggedToWord32(context, x); | |
394 Node* y_value = TruncateTaggedToWord32(context, y); | |
395 Node* value = Int32Mul(x_value, y_value); | |
396 Node* result = ChangeInt32ToTagged(value); | |
397 Return(result); | |
398 } | |
399 | |
400 // ES6 section 20.2.2.20 Math.log ( x ) | |
401 TF_BUILTIN(MathLog, MathBuiltinsAssembler) { | |
402 MathUnaryOperation(&CodeStubAssembler::Float64Log); | |
403 } | |
404 | |
405 // ES6 section 20.2.2.21 Math.log1p ( x ) | |
406 TF_BUILTIN(MathLog1p, MathBuiltinsAssembler) { | |
407 MathUnaryOperation(&CodeStubAssembler::Float64Log1p); | |
408 } | |
409 | |
410 // ES6 section 20.2.2.22 Math.log10 ( x ) | |
411 TF_BUILTIN(MathLog10, MathBuiltinsAssembler) { | |
412 MathUnaryOperation(&CodeStubAssembler::Float64Log10); | |
413 } | |
414 | |
415 // ES6 section 20.2.2.23 Math.log2 ( x ) | |
416 TF_BUILTIN(MathLog2, MathBuiltinsAssembler) { | |
417 MathUnaryOperation(&CodeStubAssembler::Float64Log2); | |
418 } | |
419 | |
420 // ES6 section 20.2.2.26 Math.pow ( x, y ) | |
421 TF_BUILTIN(MathPow, CodeStubAssembler) { | |
422 Node* x = Parameter(1); | |
423 Node* y = Parameter(2); | |
424 Node* context = Parameter(5); | |
425 Node* x_value = TruncateTaggedToFloat64(context, x); | |
426 Node* y_value = TruncateTaggedToFloat64(context, y); | |
427 Node* value = Float64Pow(x_value, y_value); | |
428 Node* result = ChangeFloat64ToTagged(value); | |
429 Return(result); | |
430 } | |
431 | |
432 // ES6 section 20.2.2.27 Math.random ( ) | |
433 TF_BUILTIN(MathRandom, CodeStubAssembler) { | |
434 Node* context = Parameter(3); | |
435 Node* native_context = LoadNativeContext(context); | |
436 | |
437 // Load cache index. | |
438 Variable smi_index(this, MachineRepresentation::kTagged); | |
439 smi_index.Bind( | |
440 LoadContextElement(native_context, Context::MATH_RANDOM_INDEX_INDEX)); | |
441 | |
442 // Cached random numbers are exhausted if index is 0. Go to slow path. | |
443 Label if_cached(this); | |
444 GotoIf(SmiAbove(smi_index.value(), SmiConstant(Smi::kZero)), &if_cached); | |
445 | |
446 // Cache exhausted, populate the cache. Return value is the new index. | |
447 smi_index.Bind(CallRuntime(Runtime::kGenerateRandomNumbers, context)); | |
448 Goto(&if_cached); | |
449 | |
450 // Compute next index by decrement. | |
451 Bind(&if_cached); | |
452 Node* new_smi_index = SmiSub(smi_index.value(), SmiConstant(Smi::FromInt(1))); | |
453 StoreContextElement(native_context, Context::MATH_RANDOM_INDEX_INDEX, | |
454 new_smi_index); | |
455 | |
456 // Load and return next cached random number. | |
457 Node* array = | |
458 LoadContextElement(native_context, Context::MATH_RANDOM_CACHE_INDEX); | |
459 Node* random = LoadFixedDoubleArrayElement( | |
460 array, new_smi_index, MachineType::Float64(), 0, SMI_PARAMETERS); | |
461 Return(AllocateHeapNumberWithValue(random)); | |
462 } | |
463 | |
464 // ES6 section 20.2.2.28 Math.round ( x ) | |
465 TF_BUILTIN(MathRound, MathBuiltinsAssembler) { | |
466 MathRoundingOperation(&CodeStubAssembler::Float64Round); | |
467 } | |
468 | |
469 // ES6 section 20.2.2.29 Math.sign ( x ) | |
470 TF_BUILTIN(MathSign, CodeStubAssembler) { | |
471 // Convert the {x} value to a Number. | |
472 Node* x = Parameter(1); | |
473 Node* context = Parameter(4); | |
474 Node* x_value = TruncateTaggedToFloat64(context, x); | |
475 | |
476 // Return -1 if {x} is negative, 1 if {x} is positive, or {x} itself. | |
477 Label if_xisnegative(this), if_xispositive(this); | |
478 GotoIf(Float64LessThan(x_value, Float64Constant(0.0)), &if_xisnegative); | |
479 GotoIf(Float64LessThan(Float64Constant(0.0), x_value), &if_xispositive); | |
480 Return(ChangeFloat64ToTagged(x_value)); | |
481 | |
482 Bind(&if_xisnegative); | |
483 Return(SmiConstant(Smi::FromInt(-1))); | |
484 | |
485 Bind(&if_xispositive); | |
486 Return(SmiConstant(Smi::FromInt(1))); | |
487 } | |
488 | |
489 // ES6 section 20.2.2.30 Math.sin ( x ) | |
490 TF_BUILTIN(MathSin, MathBuiltinsAssembler) { | |
491 MathUnaryOperation(&CodeStubAssembler::Float64Sin); | |
492 } | |
493 | |
494 // ES6 section 20.2.2.31 Math.sinh ( x ) | |
495 TF_BUILTIN(MathSinh, MathBuiltinsAssembler) { | |
496 MathUnaryOperation(&CodeStubAssembler::Float64Sinh); | |
497 } | |
498 | |
499 // ES6 section 20.2.2.32 Math.sqrt ( x ) | |
500 TF_BUILTIN(MathSqrt, MathBuiltinsAssembler) { | |
501 MathUnaryOperation(&CodeStubAssembler::Float64Sqrt); | |
502 } | |
503 | |
504 // ES6 section 20.2.2.33 Math.tan ( x ) | |
505 TF_BUILTIN(MathTan, MathBuiltinsAssembler) { | |
506 MathUnaryOperation(&CodeStubAssembler::Float64Tan); | |
507 } | |
508 | |
509 // ES6 section 20.2.2.34 Math.tanh ( x ) | |
510 TF_BUILTIN(MathTanh, MathBuiltinsAssembler) { | |
511 MathUnaryOperation(&CodeStubAssembler::Float64Tanh); | |
512 } | |
513 | |
514 // ES6 section 20.2.2.35 Math.trunc ( x ) | |
515 TF_BUILTIN(MathTrunc, MathBuiltinsAssembler) { | |
516 MathRoundingOperation(&CodeStubAssembler::Float64Trunc); | |
517 } | |
518 | |
519 // ES6 section 20.2.2.24 Math.max ( value1, value2 , ...values ) | |
520 TF_BUILTIN(MathMax, MathBuiltinsAssembler) { | |
521 MathMaxMin(&CodeStubAssembler::Float64Max, -1.0 * V8_INFINITY); | |
522 } | |
523 | |
524 // ES6 section 20.2.2.25 Math.min ( value1, value2 , ...values ) | |
525 TF_BUILTIN(MathMin, MathBuiltinsAssembler) { | |
526 MathMaxMin(&CodeStubAssembler::Float64Min, V8_INFINITY); | |
527 } | |
528 | |
529 } // namespace internal | 68 } // namespace internal |
530 } // namespace v8 | 69 } // namespace v8 |
OLD | NEW |