1// -*- c-basic-offset: 2 -*-
2/*
3 * This file is part of the KDE libraries
4 * Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 *
20 */
21
22#include "math_object.h"
23#include <config-kjs.h>
24#include "math_object.lut.h"
25#include "wtf/MathExtras.h"
26
27#include "operations.h"
28#include <math.h>
29#include <time.h>
30
31#ifndef M_PI
32#define M_PI 3.14159265358979323846
33#endif /* M_PI */
34
35#if COMPILER(MSVC)
36static double cbrt(double x)
37{
38 return pow(abs(x), 1.0 / 3.0);
39}
40
41static double log2(double x)
42{
43 return log(x) / log(2);
44}
45#endif
46
47using namespace KJS;
48
49// ------------------------------ MathObjectImp --------------------------------
50
51const ClassInfo MathObjectImp::info = { "Math", 0, &mathTable, 0 };
52
53/* Source for math_object.lut.h
54@begin mathTable 21
55 E MathObjectImp::Euler DontEnum|DontDelete|ReadOnly
56 LN2 MathObjectImp::Ln2 DontEnum|DontDelete|ReadOnly
57 LN10 MathObjectImp::Ln10 DontEnum|DontDelete|ReadOnly
58 LOG2E MathObjectImp::Log2E DontEnum|DontDelete|ReadOnly
59 LOG10E MathObjectImp::Log10E DontEnum|DontDelete|ReadOnly
60 PI MathObjectImp::Pi DontEnum|DontDelete|ReadOnly
61 SQRT1_2 MathObjectImp::Sqrt1_2 DontEnum|DontDelete|ReadOnly
62 SQRT2 MathObjectImp::Sqrt2 DontEnum|DontDelete|ReadOnly
63 abs MathObjectImp::Abs DontEnum|Function 1
64 acos MathObjectImp::ACos DontEnum|Function 1
65 asin MathObjectImp::ASin DontEnum|Function 1
66 atan MathObjectImp::ATan DontEnum|Function 1
67 atan2 MathObjectImp::ATan2 DontEnum|Function 2
68 ceil MathObjectImp::Ceil DontEnum|Function 1
69 cos MathObjectImp::Cos DontEnum|Function 1
70 exp MathObjectImp::Exp DontEnum|Function 1
71 floor MathObjectImp::Floor DontEnum|Function 1
72 log MathObjectImp::Log DontEnum|Function 1
73 max MathObjectImp::Max DontEnum|Function 2
74 min MathObjectImp::Min DontEnum|Function 2
75 pow MathObjectImp::Pow DontEnum|Function 2
76 random MathObjectImp::Random DontEnum|Function 0
77 round MathObjectImp::Round DontEnum|Function 1
78 sin MathObjectImp::Sin DontEnum|Function 1
79 sqrt MathObjectImp::Sqrt DontEnum|Function 1
80 tan MathObjectImp::Tan DontEnum|Function 1
81 acosh MathObjectImp::ACosH DontEnum|Function 1
82 acosh MathObjectImp::ASinH DontEnum|Function 1
83 atanh MathObjectImp::ATanH DontEnum|Function 1
84 cbrt MathObjectImp::Cbrt DontEnum|Function 1
85 cosh MathObjectImp::CosH DontEnum|Function 1
86 exmp1 MathObjectImp::Exmp1 DontEnum|Function 1
87 log1p MathObjectImp::Log1p DontEnum|Function 1
88 log10 MathObjectImp::Log10 DontEnum|Function 1
89 log2 MathObjectImp::Log2 DontEnum|Function 1
90 sign MathObjectImp::Sign DontEnum|Function 1
91 sinh MathObjectImp::SinH DontEnum|Function 1
92 tanh MathObjectImp::TanH DontEnum|Function 1
93 trunc MathObjectImp::Trunc DontEnum|Function 1
94 hypot MathObjectImp::Hypot DontEnum|Function 0
95 imul MathObjectImp::Imul DontEnum|Function 2
96 fround MathObjectImp::FRound DontEnum|Function 1
97@end
98*/
99
100
101MathObjectImp::MathObjectImp(ExecState * /*exec*/,
102 ObjectPrototype *objProto)
103 : JSObject(objProto)
104{
105}
106
107// ECMA 15.8
108
109bool MathObjectImp::getOwnPropertySlot(ExecState *exec, const Identifier& propertyName, PropertySlot &slot)
110{
111 return getStaticPropertySlot<MathFuncImp, MathObjectImp, JSObject>(exec, &mathTable, this, propertyName, slot);
112}
113
114JSValue *MathObjectImp::getValueProperty(ExecState *, int token) const
115{
116 switch (token) {
117 case Euler:
118 return jsNumber(exp(1.0));
119 case Ln2:
120 return jsNumber(log(2.0));
121 case Ln10:
122 return jsNumber(log(10.0));
123 case Log2E:
124 return jsNumber(1.0/log(2.0));
125 case Log10E:
126 return jsNumber(1.0/log(10.0));
127 case Pi:
128 return jsNumber(piDouble);
129 case Sqrt1_2:
130 return jsNumber(sqrt(0.5));
131 case Sqrt2:
132 return jsNumber(sqrt(2.0));
133 }
134
135 assert(0);
136 return 0;
137}
138
139// ------------------------------ MathObjectImp --------------------------------
140
141static bool randomSeeded = false;
142
143MathFuncImp::MathFuncImp(ExecState* exec, int i, int l, const Identifier& name)
144 : InternalFunctionImp(static_cast<FunctionPrototype*>(exec->lexicalInterpreter()->builtinFunctionPrototype()), name)
145 , id(i)
146{
147 putDirect(exec->propertyNames().length, l, DontDelete|ReadOnly|DontEnum);
148}
149
150JSValue *MathFuncImp::callAsFunction(ExecState *exec, JSObject* /*thisObj*/, const List &args)
151{
152 double arg = args[0]->toNumber(exec);
153 double arg2 = args[1]->toNumber(exec);
154 double result;
155
156 switch (id) {
157 case MathObjectImp::Abs:
158 result = ( arg < 0 || arg == -0) ? (-arg) : arg;
159 break;
160 case MathObjectImp::ACos:
161 result = ::acos(arg);
162 break;
163 case MathObjectImp::ASin:
164 result = ::asin(arg);
165 break;
166 case MathObjectImp::ATan:
167 result = ::atan(arg);
168 break;
169 case MathObjectImp::ATan2:
170 result = ::atan2(arg, arg2);
171 break;
172 case MathObjectImp::Ceil:
173 result = ::ceil(arg);
174 break;
175 case MathObjectImp::Cos:
176 result = ::cos(arg);
177 break;
178 case MathObjectImp::Exp:
179 result = ::exp(arg);
180 break;
181 case MathObjectImp::Floor:
182 result = ::floor(arg);
183 break;
184 case MathObjectImp::Log:
185 result = ::log(arg);
186 break;
187 case MathObjectImp::Max: {
188 unsigned int argsCount = args.size();
189 result = -Inf;
190 for ( unsigned int k = 0 ; k < argsCount ; ++k ) {
191 double val = args[k]->toNumber(exec);
192 if ( isNaN( val ) )
193 {
194 result = NaN;
195 break;
196 }
197 if ( val > result || (val == 0 && result == 0 && !signbit(val)) )
198 result = val;
199 }
200 break;
201 }
202 case MathObjectImp::Min: {
203 unsigned int argsCount = args.size();
204 result = +Inf;
205 for ( unsigned int k = 0 ; k < argsCount ; ++k ) {
206 double val = args[k]->toNumber(exec);
207 if ( isNaN( val ) )
208 {
209 result = NaN;
210 break;
211 }
212 if ( val < result || (val == 0 && result == 0 && signbit(val)) )
213 result = val;
214 }
215 break;
216 }
217 case MathObjectImp::Pow:
218 // ECMA 15.8.2.1.13 (::pow takes care of most of the critera)
219 if (isNaN(arg2))
220 result = NaN;
221 else if (isNaN(arg) && arg2 != 0)
222 result = NaN;
223 else if (::fabs(arg) == 1 && isInf(arg2))
224 result = NaN;
225 else if (arg2 == 0 && arg != 0)
226 result = 1;
227 else
228 result = ::pow(arg, arg2);
229 break;
230 case MathObjectImp::Random:
231 if (!randomSeeded) {
232 srand(static_cast<unsigned>(time(0)));
233 randomSeeded = true;
234 }
235 result = (double)rand() / RAND_MAX;
236 break;
237 case MathObjectImp::Round:
238 if (signbit(arg) && arg >= -0.5)
239 result = -0.0;
240 else
241 result = ::floor(arg + 0.5);
242 break;
243 case MathObjectImp::Sin:
244 result = ::sin(arg);
245 break;
246 case MathObjectImp::Sqrt:
247 result = ::sqrt(arg);
248 break;
249 case MathObjectImp::Tan:
250 result = ::tan(arg);
251 break;
252
253 //ES6 (draft 08.11.2013)
254 case MathObjectImp::ACosH:
255 result = ::acosh(arg);
256 break;
257 case MathObjectImp::ASinH:
258 result = ::asinh(arg);
259 break;
260 case MathObjectImp::ATanH:
261 result = ::atanh(arg);
262 break;
263 case MathObjectImp::Cbrt:
264 result = ::cbrt(arg);
265 break;
266 case MathObjectImp::CosH:
267 result = ::cosh(arg);
268 break;
269 case MathObjectImp::Exmp1:
270 result = ::expm1(arg);
271 break;
272 case MathObjectImp::Log1p:
273 result = ::log1p(arg);
274 break;
275 case MathObjectImp::Log10:
276 result = ::log10(arg);
277 break;
278 case MathObjectImp::Log2:
279 result = ::log2(arg);
280 break;
281 case MathObjectImp::Sign:
282 if (isNaN(arg))
283 {
284 result = KJS::NaN;
285 }
286 else if (signbit(arg))
287 {
288 if (arg == 0)
289 result = -0.0;
290 else
291 result = -1.0;
292 }
293 else
294 {
295 if (arg == 0)
296 result = 0.0;
297 else
298 result = 1.0;
299 }
300 break;
301 case MathObjectImp::SinH:
302 result = ::sinh(arg);
303 break;
304 case MathObjectImp::TanH:
305 result = ::tanh(arg);
306 break;
307 case MathObjectImp::Trunc:
308 result = ::trunc(arg);
309 break;
310 case MathObjectImp::Hypot:
311 {
312 if (args.size() == 0)
313 {
314 result = 0;
315 break;
316 }
317
318 double sum = 0.0;
319 bool foundNaN = false;
320 for (int i = 0; i < args.size(); ++i)
321 {
322 double num = args[i]->toNumber(exec);
323 if (isInf(num))
324 return jsNumber(KJS::Inf);
325 if (foundNaN)
326 continue;
327 if (isNaN(num))
328 {
329 foundNaN = true;
330 continue;
331 }
332
333 sum += ::pow(num, 2);
334 }
335
336 if (foundNaN)
337 return jsNumber(KJS::NaN);
338
339 result = ::sqrt(sum);
340 break;
341 }
342 case MathObjectImp::Imul:
343 {
344 if (args.size() < 2)
345 return jsUndefined();
346 int32_t a = args[0]->toInt32(exec);
347 if (exec->hadException())
348 return jsNumber(a);
349 int32_t b = args[1]->toInt32(exec);
350 if (exec->hadException())
351 return jsNumber(b);
352
353 result = a * b;
354 break;
355 }
356 case MathObjectImp::FRound:
357 if (isNaN(arg) || isInf(arg))
358 return jsNumber(arg);
359
360 result = static_cast<double>(static_cast<float>(arg));
361 break;
362
363 default:
364 result = 0.0;
365 assert(0);
366 }
367
368 return jsNumber(result);
369}
370