001: /* -*- Mode: java; tab-width: 4; indent-tabs-mode: 1; c-basic-offset: 4 -*-
002: *
003: * ***** BEGIN LICENSE BLOCK *****
004: * Version: MPL 1.1/GPL 2.0
005: *
006: * The contents of this file are subject to the Mozilla Public License Version
007: * 1.1 (the "License"); you may not use this file except in compliance with
008: * the License. You may obtain a copy of the License at
009: * http://www.mozilla.org/MPL/
010: *
011: * Software distributed under the License is distributed on an "AS IS" basis,
012: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
013: * for the specific language governing rights and limitations under the
014: * License.
015: *
016: * The Original Code is Rhino code, released
017: * May 6, 1999.
018: *
019: * The Initial Developer of the Original Code is
020: * Netscape Communications Corporation.
021: * Portions created by the Initial Developer are Copyright (C) 1997-1999
022: * the Initial Developer. All Rights Reserved.
023: *
024: * Contributor(s):
025: * Norris Boyd
026: * Igor Bukanov
027: *
028: * Alternatively, the contents of this file may be used under the terms of
029: * the GNU General Public License Version 2 or later (the "GPL"), in which
030: * case the provisions of the GPL are applicable instead of those above. If
031: * you wish to allow use of your version of this file only under the terms of
032: * the GPL and not to allow others to use your version of this file under the
033: * MPL, indicate your decision by deleting the provisions above and replacing
034: * them with the notice and other provisions required by the GPL. If you do
035: * not delete the provisions above, a recipient may use your version of this
036: * file under either the MPL or the GPL.
037: *
038: * ***** END LICENSE BLOCK ***** */
039:
040: package org.mozilla.javascript;
041:
042: /**
043: * This class implements the Math native object.
044: * See ECMA 15.8.
045: * @author Norris Boyd
046: */
047:
048: final class NativeMath extends IdScriptableObject {
049: static final long serialVersionUID = -8838847185801131569L;
050:
051: private static final Object MATH_TAG = new Object();
052:
053: static void init(Scriptable scope, boolean sealed) {
054: NativeMath obj = new NativeMath();
055: obj.activatePrototypeMap(MAX_ID);
056: obj.setPrototype(getObjectPrototype(scope));
057: obj.setParentScope(scope);
058: if (sealed) {
059: obj.sealObject();
060: }
061: ScriptableObject.defineProperty(scope, "Math", obj,
062: ScriptableObject.DONTENUM);
063: }
064:
065: private NativeMath() {
066: }
067:
068: public String getClassName() {
069: return "Math";
070: }
071:
072: protected void initPrototypeId(int id) {
073: if (id <= LAST_METHOD_ID) {
074: String name;
075: int arity;
076: switch (id) {
077: case Id_toSource:
078: arity = 0;
079: name = "toSource";
080: break;
081: case Id_abs:
082: arity = 1;
083: name = "abs";
084: break;
085: case Id_acos:
086: arity = 1;
087: name = "acos";
088: break;
089: case Id_asin:
090: arity = 1;
091: name = "asin";
092: break;
093: case Id_atan:
094: arity = 1;
095: name = "atan";
096: break;
097: case Id_atan2:
098: arity = 2;
099: name = "atan2";
100: break;
101: case Id_ceil:
102: arity = 1;
103: name = "ceil";
104: break;
105: case Id_cos:
106: arity = 1;
107: name = "cos";
108: break;
109: case Id_exp:
110: arity = 1;
111: name = "exp";
112: break;
113: case Id_floor:
114: arity = 1;
115: name = "floor";
116: break;
117: case Id_log:
118: arity = 1;
119: name = "log";
120: break;
121: case Id_max:
122: arity = 2;
123: name = "max";
124: break;
125: case Id_min:
126: arity = 2;
127: name = "min";
128: break;
129: case Id_pow:
130: arity = 2;
131: name = "pow";
132: break;
133: case Id_random:
134: arity = 0;
135: name = "random";
136: break;
137: case Id_round:
138: arity = 1;
139: name = "round";
140: break;
141: case Id_sin:
142: arity = 1;
143: name = "sin";
144: break;
145: case Id_sqrt:
146: arity = 1;
147: name = "sqrt";
148: break;
149: case Id_tan:
150: arity = 1;
151: name = "tan";
152: break;
153: default:
154: throw new IllegalStateException(String.valueOf(id));
155: }
156: initPrototypeMethod(MATH_TAG, id, name, arity);
157: } else {
158: String name;
159: double x;
160: switch (id) {
161: case Id_E:
162: x = Math.E;
163: name = "E";
164: break;
165: case Id_PI:
166: x = Math.PI;
167: name = "PI";
168: break;
169: case Id_LN10:
170: x = 2.302585092994046;
171: name = "LN10";
172: break;
173: case Id_LN2:
174: x = 0.6931471805599453;
175: name = "LN2";
176: break;
177: case Id_LOG2E:
178: x = 1.4426950408889634;
179: name = "LOG2E";
180: break;
181: case Id_LOG10E:
182: x = 0.4342944819032518;
183: name = "LOG10E";
184: break;
185: case Id_SQRT1_2:
186: x = 0.7071067811865476;
187: name = "SQRT1_2";
188: break;
189: case Id_SQRT2:
190: x = 1.4142135623730951;
191: name = "SQRT2";
192: break;
193: default:
194: throw new IllegalStateException(String.valueOf(id));
195: }
196: initPrototypeValue(id, name, ScriptRuntime.wrapNumber(x),
197: DONTENUM | READONLY | PERMANENT);
198: }
199: }
200:
201: public Object execIdCall(IdFunctionObject f, Context cx,
202: Scriptable scope, Scriptable this Obj, Object[] args) {
203: if (!f.hasTag(MATH_TAG)) {
204: return super .execIdCall(f, cx, scope, this Obj, args);
205: }
206: double x;
207: int methodId = f.methodId();
208: switch (methodId) {
209: case Id_toSource:
210: return "Math";
211:
212: case Id_abs:
213: x = ScriptRuntime.toNumber(args, 0);
214: // abs(-0.0) should be 0.0, but -0.0 < 0.0 == false
215: x = (x == 0.0) ? 0.0 : (x < 0.0) ? -x : x;
216: break;
217:
218: case Id_acos:
219: case Id_asin:
220: x = ScriptRuntime.toNumber(args, 0);
221: if (x == x && -1.0 <= x && x <= 1.0) {
222: x = (methodId == Id_acos) ? Math.acos(x) : Math.asin(x);
223: } else {
224: x = Double.NaN;
225: }
226: break;
227:
228: case Id_atan:
229: x = ScriptRuntime.toNumber(args, 0);
230: x = Math.atan(x);
231: break;
232:
233: case Id_atan2:
234: x = ScriptRuntime.toNumber(args, 0);
235: x = Math.atan2(x, ScriptRuntime.toNumber(args, 1));
236: break;
237:
238: case Id_ceil:
239: x = ScriptRuntime.toNumber(args, 0);
240: x = Math.ceil(x);
241: break;
242:
243: case Id_cos:
244: x = ScriptRuntime.toNumber(args, 0);
245: x = (x == Double.POSITIVE_INFINITY || x == Double.NEGATIVE_INFINITY) ? Double.NaN
246: : Math.cos(x);
247: break;
248:
249: case Id_exp:
250: x = ScriptRuntime.toNumber(args, 0);
251: x = (x == Double.POSITIVE_INFINITY) ? x
252: : (x == Double.NEGATIVE_INFINITY) ? 0.0 : Math
253: .exp(x);
254: break;
255:
256: case Id_floor:
257: x = ScriptRuntime.toNumber(args, 0);
258: x = Math.floor(x);
259: break;
260:
261: case Id_log:
262: x = ScriptRuntime.toNumber(args, 0);
263: // Java's log(<0) = -Infinity; we need NaN
264: x = (x < 0) ? Double.NaN : Math.log(x);
265: break;
266:
267: case Id_max:
268: case Id_min:
269: x = (methodId == Id_max) ? Double.NEGATIVE_INFINITY
270: : Double.POSITIVE_INFINITY;
271: for (int i = 0; i != args.length; ++i) {
272: double d = ScriptRuntime.toNumber(args[i]);
273: if (d != d) {
274: x = d; // NaN
275: break;
276: }
277: if (methodId == Id_max) {
278: // if (x < d) x = d; does not work due to -0.0 >= +0.0
279: x = Math.max(x, d);
280: } else {
281: x = Math.min(x, d);
282: }
283: }
284: break;
285:
286: case Id_pow:
287: x = ScriptRuntime.toNumber(args, 0);
288: x = js_pow(x, ScriptRuntime.toNumber(args, 1));
289: break;
290:
291: case Id_random:
292: x = Math.random();
293: break;
294:
295: case Id_round:
296: x = ScriptRuntime.toNumber(args, 0);
297: if (x == x && x != Double.POSITIVE_INFINITY
298: && x != Double.NEGATIVE_INFINITY) {
299: // Round only finite x
300: long l = Math.round(x);
301: if (l != 0) {
302: x = l;
303: } else {
304: // We must propagate the sign of d into the result
305: if (x < 0.0) {
306: x = ScriptRuntime.negativeZero;
307: } else if (x != 0.0) {
308: x = 0.0;
309: }
310: }
311: }
312: break;
313:
314: case Id_sin:
315: x = ScriptRuntime.toNumber(args, 0);
316: x = (x == Double.POSITIVE_INFINITY || x == Double.NEGATIVE_INFINITY) ? Double.NaN
317: : Math.sin(x);
318: break;
319:
320: case Id_sqrt:
321: x = ScriptRuntime.toNumber(args, 0);
322: x = Math.sqrt(x);
323: break;
324:
325: case Id_tan:
326: x = ScriptRuntime.toNumber(args, 0);
327: x = Math.tan(x);
328: break;
329:
330: default:
331: throw new IllegalStateException(String.valueOf(methodId));
332: }
333: return ScriptRuntime.wrapNumber(x);
334: }
335:
336: // See Ecma 15.8.2.13
337: private double js_pow(double x, double y) {
338: double result;
339: if (y != y) {
340: // y is NaN, result is always NaN
341: result = y;
342: } else if (y == 0) {
343: // Java's pow(NaN, 0) = NaN; we need 1
344: result = 1.0;
345: } else if (x == 0) {
346: // Many dirrerences from Java's Math.pow
347: if (1 / x > 0) {
348: result = (y > 0) ? 0 : Double.POSITIVE_INFINITY;
349: } else {
350: // x is -0, need to check if y is an odd integer
351: long y_long = (long) y;
352: if (y_long == y && (y_long & 0x1) != 0) {
353: result = (y > 0) ? -0.0 : Double.NEGATIVE_INFINITY;
354: } else {
355: result = (y > 0) ? 0.0 : Double.POSITIVE_INFINITY;
356: }
357: }
358: } else {
359: result = Math.pow(x, y);
360: if (result != result) {
361: // Check for broken Java implementations that gives NaN
362: // when they should return something else
363: if (y == Double.POSITIVE_INFINITY) {
364: if (x < -1.0 || 1.0 < x) {
365: result = Double.POSITIVE_INFINITY;
366: } else if (-1.0 < x && x < 1.0) {
367: result = 0;
368: }
369: } else if (y == Double.NEGATIVE_INFINITY) {
370: if (x < -1.0 || 1.0 < x) {
371: result = 0;
372: } else if (-1.0 < x && x < 1.0) {
373: result = Double.POSITIVE_INFINITY;
374: }
375: } else if (x == Double.POSITIVE_INFINITY) {
376: result = (y > 0) ? Double.POSITIVE_INFINITY : 0.0;
377: } else if (x == Double.NEGATIVE_INFINITY) {
378: long y_long = (long) y;
379: if (y_long == y && (y_long & 0x1) != 0) {
380: // y is odd integer
381: result = (y > 0) ? Double.NEGATIVE_INFINITY
382: : -0.0;
383: } else {
384: result = (y > 0) ? Double.POSITIVE_INFINITY
385: : 0.0;
386: }
387: }
388: }
389: }
390: return result;
391: }
392:
393: // #string_id_map#
394:
395: protected int findPrototypeId(String s) {
396: int id;
397: // #generated# Last update: 2004-03-17 13:51:32 CET
398: L0: {
399: id = 0;
400: String X = null;
401: int c;
402: L: switch (s.length()) {
403: case 1:
404: if (s.charAt(0) == 'E') {
405: id = Id_E;
406: break L0;
407: }
408: break L;
409: case 2:
410: if (s.charAt(0) == 'P' && s.charAt(1) == 'I') {
411: id = Id_PI;
412: break L0;
413: }
414: break L;
415: case 3:
416: switch (s.charAt(0)) {
417: case 'L':
418: if (s.charAt(2) == '2' && s.charAt(1) == 'N') {
419: id = Id_LN2;
420: break L0;
421: }
422: break L;
423: case 'a':
424: if (s.charAt(2) == 's' && s.charAt(1) == 'b') {
425: id = Id_abs;
426: break L0;
427: }
428: break L;
429: case 'c':
430: if (s.charAt(2) == 's' && s.charAt(1) == 'o') {
431: id = Id_cos;
432: break L0;
433: }
434: break L;
435: case 'e':
436: if (s.charAt(2) == 'p' && s.charAt(1) == 'x') {
437: id = Id_exp;
438: break L0;
439: }
440: break L;
441: case 'l':
442: if (s.charAt(2) == 'g' && s.charAt(1) == 'o') {
443: id = Id_log;
444: break L0;
445: }
446: break L;
447: case 'm':
448: c = s.charAt(2);
449: if (c == 'n') {
450: if (s.charAt(1) == 'i') {
451: id = Id_min;
452: break L0;
453: }
454: } else if (c == 'x') {
455: if (s.charAt(1) == 'a') {
456: id = Id_max;
457: break L0;
458: }
459: }
460: break L;
461: case 'p':
462: if (s.charAt(2) == 'w' && s.charAt(1) == 'o') {
463: id = Id_pow;
464: break L0;
465: }
466: break L;
467: case 's':
468: if (s.charAt(2) == 'n' && s.charAt(1) == 'i') {
469: id = Id_sin;
470: break L0;
471: }
472: break L;
473: case 't':
474: if (s.charAt(2) == 'n' && s.charAt(1) == 'a') {
475: id = Id_tan;
476: break L0;
477: }
478: break L;
479: }
480: break L;
481: case 4:
482: switch (s.charAt(1)) {
483: case 'N':
484: X = "LN10";
485: id = Id_LN10;
486: break L;
487: case 'c':
488: X = "acos";
489: id = Id_acos;
490: break L;
491: case 'e':
492: X = "ceil";
493: id = Id_ceil;
494: break L;
495: case 'q':
496: X = "sqrt";
497: id = Id_sqrt;
498: break L;
499: case 's':
500: X = "asin";
501: id = Id_asin;
502: break L;
503: case 't':
504: X = "atan";
505: id = Id_atan;
506: break L;
507: }
508: break L;
509: case 5:
510: switch (s.charAt(0)) {
511: case 'L':
512: X = "LOG2E";
513: id = Id_LOG2E;
514: break L;
515: case 'S':
516: X = "SQRT2";
517: id = Id_SQRT2;
518: break L;
519: case 'a':
520: X = "atan2";
521: id = Id_atan2;
522: break L;
523: case 'f':
524: X = "floor";
525: id = Id_floor;
526: break L;
527: case 'r':
528: X = "round";
529: id = Id_round;
530: break L;
531: }
532: break L;
533: case 6:
534: c = s.charAt(0);
535: if (c == 'L') {
536: X = "LOG10E";
537: id = Id_LOG10E;
538: } else if (c == 'r') {
539: X = "random";
540: id = Id_random;
541: }
542: break L;
543: case 7:
544: X = "SQRT1_2";
545: id = Id_SQRT1_2;
546: break L;
547: case 8:
548: X = "toSource";
549: id = Id_toSource;
550: break L;
551: }
552: if (X != null && X != s && !X.equals(s))
553: id = 0;
554: }
555: // #/generated#
556: return id;
557: }
558:
559: private static final int Id_toSource = 1, Id_abs = 2, Id_acos = 3,
560: Id_asin = 4, Id_atan = 5, Id_atan2 = 6, Id_ceil = 7,
561: Id_cos = 8, Id_exp = 9, Id_floor = 10, Id_log = 11,
562: Id_max = 12, Id_min = 13, Id_pow = 14, Id_random = 15,
563: Id_round = 16, Id_sin = 17, Id_sqrt = 18, Id_tan = 19,
564:
565: LAST_METHOD_ID = 19;
566:
567: private static final int Id_E = LAST_METHOD_ID + 1,
568: Id_PI = LAST_METHOD_ID + 2, Id_LN10 = LAST_METHOD_ID + 3,
569: Id_LN2 = LAST_METHOD_ID + 4, Id_LOG2E = LAST_METHOD_ID + 5,
570: Id_LOG10E = LAST_METHOD_ID + 6,
571: Id_SQRT1_2 = LAST_METHOD_ID + 7,
572: Id_SQRT2 = LAST_METHOD_ID + 8,
573:
574: MAX_ID = LAST_METHOD_ID + 8;
575:
576: // #/string_id_map#
577: }
|