~ubuntu-branches/ubuntu/maverick/webkit/maverick

« back to all changes in this revision

Viewing changes to JavaScriptCore/kjs/number_object.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Mike Hommey
  • Date: 2007-08-19 15:54:12 UTC
  • Revision ID: james.westby@ubuntu.com-20070819155412-uxxg1h9plpghmtbi
Tags: upstream-0~svn25144
ImportĀ upstreamĀ versionĀ 0~svn25144

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// -*- c-basic-offset: 2 -*-
 
2
/*
 
3
 *  Copyright (C) 1999-2000,2003 Harri Porten (porten@kde.org)
 
4
 *  Copyright (C) 2007 Apple Inc. All rights reserved.
 
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 "config.h"
 
23
#include "number_object.h"
 
24
#include "number_object.lut.h"
 
25
 
 
26
#include "dtoa.h"
 
27
#include "error_object.h"
 
28
#include "operations.h"
 
29
#include <wtf/MathExtras.h>
 
30
#include <wtf/Vector.h>
 
31
 
 
32
using namespace KJS;
 
33
 
 
34
// ------------------------------ NumberInstance ----------------------------
 
35
 
 
36
const ClassInfo NumberInstance::info = {"Number", 0, 0, 0};
 
37
 
 
38
NumberInstance::NumberInstance(JSObject *proto)
 
39
  : JSWrapperObject(proto)
 
40
{
 
41
}
 
42
// ------------------------------ NumberPrototype ---------------------------
 
43
 
 
44
// ECMA 15.7.4
 
45
 
 
46
NumberPrototype::NumberPrototype(ExecState *exec,
 
47
                                       ObjectPrototype *objProto,
 
48
                                       FunctionPrototype *funcProto)
 
49
  : NumberInstance(objProto)
 
50
{
 
51
  setInternalValue(jsNumber(0));
 
52
 
 
53
  // The constructor will be added later, after NumberObjectImp has been constructed
 
54
 
 
55
  putDirectFunction(new NumberProtoFunc(exec, funcProto, NumberProtoFunc::ToString,       1, exec->propertyNames().toString), DontEnum);
 
56
  putDirectFunction(new NumberProtoFunc(exec, funcProto, NumberProtoFunc::ToLocaleString, 0, exec->propertyNames().toLocaleString), DontEnum);
 
57
  putDirectFunction(new NumberProtoFunc(exec, funcProto, NumberProtoFunc::ValueOf,        0, exec->propertyNames().valueOf), DontEnum);
 
58
  putDirectFunction(new NumberProtoFunc(exec, funcProto, NumberProtoFunc::ToFixed,        1, exec->propertyNames().toFixed), DontEnum);
 
59
  putDirectFunction(new NumberProtoFunc(exec, funcProto, NumberProtoFunc::ToExponential,  1, exec->propertyNames().toExponential), DontEnum);
 
60
  putDirectFunction(new NumberProtoFunc(exec, funcProto, NumberProtoFunc::ToPrecision,    1, exec->propertyNames().toPrecision), DontEnum);
 
61
}
 
62
 
 
63
 
 
64
// ------------------------------ NumberProtoFunc ---------------------------
 
65
 
 
66
NumberProtoFunc::NumberProtoFunc(ExecState* exec, FunctionPrototype* funcProto, int i, int len, const Identifier& name)
 
67
   : InternalFunctionImp(funcProto, name)
 
68
   , id(i)
 
69
{
 
70
  putDirect(exec->propertyNames().length, len, DontDelete|ReadOnly|DontEnum);
 
71
}
 
72
 
 
73
static UString integer_part_noexp(double d)
 
74
{
 
75
    int decimalPoint;
 
76
    int sign;
 
77
    char *result = kjs_dtoa(d, 0, 0, &decimalPoint, &sign, NULL);
 
78
    size_t length = strlen(result);
 
79
    
 
80
    UString str = sign ? "-" : "";
 
81
    if (decimalPoint == 9999) {
 
82
        str += UString(result);
 
83
    } else if (decimalPoint <= 0) {
 
84
        str += UString("0");
 
85
    } else {
 
86
        Vector<char, 1024> buf(decimalPoint + 1);
 
87
        
 
88
        if (static_cast<int>(length) <= decimalPoint) {
 
89
            strcpy(buf.data(), result);
 
90
            memset(buf.data() + length, '0', decimalPoint - length);
 
91
        } else
 
92
            strncpy(buf.data(), result, decimalPoint);
 
93
        
 
94
        buf[decimalPoint] = '\0';
 
95
        str += UString(buf.data());
 
96
    }
 
97
    
 
98
    kjs_freedtoa(result);
 
99
    
 
100
    return str;
 
101
}
 
102
 
 
103
static UString char_sequence(char c, int count)
 
104
{
 
105
    Vector<char, 2048> buf(count + 1, c);
 
106
    buf[count] = '\0';
 
107
 
 
108
    return UString(buf.data());
 
109
}
 
110
 
 
111
static double intPow10(int e)
 
112
{
 
113
  // This function uses the "exponentiation by squaring" algorithm and
 
114
  // long double to quickly and precisely calculate integer powers of 10.0.
 
115
 
 
116
  // This is a handy workaround for <rdar://problem/4494756>
 
117
 
 
118
  if (e == 0)
 
119
    return 1.0;
 
120
 
 
121
  bool negative = e < 0;
 
122
  unsigned exp = negative ? -e : e;
 
123
 
 
124
  long double result = 10.0;
 
125
  bool foundOne = false;
 
126
  for (int bit = 31; bit >= 0; bit--) {
 
127
    if (!foundOne) {
 
128
      if ((exp >> bit) & 1)
 
129
        foundOne = true;
 
130
    } else {
 
131
      result = result * result;
 
132
      if ((exp >> bit) & 1)
 
133
        result = result * 10.0;
 
134
    }
 
135
  }
 
136
 
 
137
  if (negative)
 
138
    return static_cast<double>(1.0 / result);
 
139
  return static_cast<double>(result);
 
140
}
 
141
 
 
142
// ECMA 15.7.4.2 - 15.7.4.7
 
143
JSValue *NumberProtoFunc::callAsFunction(ExecState *exec, JSObject *thisObj, const List &args)
 
144
{
 
145
  // no generic function. "this" has to be a Number object
 
146
  if (!thisObj->inherits(&NumberInstance::info))
 
147
    return throwError(exec, TypeError);
 
148
 
 
149
  JSValue *v = static_cast<NumberInstance*>(thisObj)->internalValue();
 
150
  switch (id) {
 
151
  case ToString: {
 
152
    double dradix = 10;
 
153
    if (!args.isEmpty())
 
154
      dradix = args[0]->toInteger(exec);
 
155
    if (dradix >= 2 && dradix <= 36 && dradix != 10) { // false for NaN
 
156
      int radix = static_cast<int>(dradix);
 
157
      const char digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
 
158
      // INT_MAX results in 1024 characters left of the dot with radix 2
 
159
      // give the same space on the right side. safety checks are in place
 
160
      // unless someone finds a precise rule.
 
161
      char s[2048 + 3];
 
162
      double x = v->toNumber(exec);
 
163
      if (isNaN(x) || isInf(x))
 
164
        return jsString(UString::from(x));
 
165
 
 
166
      // apply algorithm on absolute value. add sign later.
 
167
      bool neg = false;
 
168
      if (x < 0.0) {
 
169
        neg = true;
 
170
        x = -x;
 
171
      }
 
172
      // convert integer portion
 
173
      double f = floor(x);
 
174
      double d = f;
 
175
      char *dot = s + sizeof(s) / 2;
 
176
      char *p = dot;
 
177
      *p = '\0';
 
178
      do {
 
179
        *--p = digits[static_cast<int>(fmod(d, radix))];
 
180
        d /= radix;
 
181
      } while ((d <= -1.0 || d >= 1.0) && p > s);
 
182
      // any decimal fraction ?
 
183
      d = x - f;
 
184
      const double eps = 0.001; // TODO: guessed. base on radix ?
 
185
      if (d < -eps || d > eps) {
 
186
        *dot++ = '.';
 
187
        do {
 
188
          d *= radix;
 
189
          *dot++ = digits[static_cast<int>(d)];
 
190
          d -= static_cast<int>(d);
 
191
        } while ((d < -eps || d > eps) && dot - s < static_cast<int>(sizeof(s)) - 1);
 
192
        *dot = '\0';
 
193
      }
 
194
      // add sign if negative
 
195
      if (neg)
 
196
        *--p = '-';
 
197
      return jsString(p);
 
198
    } else
 
199
      return jsString(v->toString(exec));
 
200
  }
 
201
  case ToLocaleString: /* TODO */
 
202
    return jsString(v->toString(exec));
 
203
  case ValueOf:
 
204
    return jsNumber(v->toNumber(exec));
 
205
  case ToFixed: 
 
206
  {
 
207
      JSValue *fractionDigits = args[0];
 
208
      double df = fractionDigits->toInteger(exec);
 
209
      if (fractionDigits->isUndefined())
 
210
            df = 0;
 
211
      if (!(df >= 0 && df <= 20)) // true for NaN
 
212
          return throwError(exec, RangeError, "toFixed() digits argument must be between 0 and 20");
 
213
      int f = (int)df;
 
214
      
 
215
      double x = v->toNumber(exec);
 
216
      if (isNaN(x))
 
217
          return jsString("NaN");
 
218
      
 
219
      UString s = "";
 
220
      if (x < 0) {
 
221
          s += "-";
 
222
          x = -x;
 
223
      }
 
224
      
 
225
      if (x >= pow(10.0, 21.0))
 
226
          return jsString(s+UString::from(x));
 
227
      
 
228
      double n = floor(x*pow(10.0, f));
 
229
      if (fabs(n / pow(10.0, f) - x) >= fabs((n + 1) / pow(10.0, f) - x))
 
230
          n++;
 
231
      
 
232
      UString m = integer_part_noexp(n);
 
233
      
 
234
      int k = m.size();
 
235
      if (k <= f) {
 
236
          UString z = "";
 
237
          for (int i = 0; i < f+1-k; i++)
 
238
              z += "0";
 
239
          m = z + m;
 
240
          k = f + 1;
 
241
          assert(k == m.size());
 
242
      }
 
243
      if (k-f < m.size())
 
244
          return jsString(s+m.substr(0,k-f)+"."+m.substr(k-f));
 
245
      else
 
246
          return jsString(s+m.substr(0,k-f));
 
247
  }
 
248
  case ToExponential: {
 
249
      double x = v->toNumber(exec);
 
250
      
 
251
      if (isNaN(x) || isInf(x))
 
252
          return jsString(UString::from(x));
 
253
      
 
254
      JSValue *fractionDigits = args[0];
 
255
      double df = fractionDigits->toInteger(exec);
 
256
      if (!fractionDigits->isUndefined() && !(df >= 0 && df <= 20)) // true for NaN
 
257
          return throwError(exec, RangeError, "toExponential() argument must between 0 and 20");
 
258
      int f = (int)df;
 
259
      
 
260
      int decimalAdjust = 0;
 
261
      if (!fractionDigits->isUndefined()) {
 
262
          double logx = floor(log10(fabs(x)));
 
263
          x /= pow(10.0, logx);
 
264
          double fx = floor(x * pow(10.0, f)) / pow(10.0, f);
 
265
          double cx = ceil(x * pow(10.0, f)) / pow(10.0, f);
 
266
          
 
267
          if (fabs(fx-x) < fabs(cx-x))
 
268
              x = fx;
 
269
          else
 
270
              x = cx;
 
271
          
 
272
          decimalAdjust = static_cast<int>(logx);
 
273
      }
 
274
      
 
275
      char buf[80];
 
276
      int decimalPoint;
 
277
      int sign;
 
278
      
 
279
      if (isNaN(x))
 
280
          return jsString("NaN");
 
281
      
 
282
      char *result = kjs_dtoa(x, 0, 0, &decimalPoint, &sign, NULL);
 
283
      size_t length = strlen(result);
 
284
      decimalPoint += decimalAdjust;
 
285
      
 
286
      int i = 0;
 
287
      if (sign) {
 
288
          buf[i++] = '-';
 
289
      }
 
290
      
 
291
      if (decimalPoint == 999) {
 
292
          strcpy(buf + i, result);
 
293
      } else {
 
294
          buf[i++] = result[0];
 
295
          
 
296
          if (fractionDigits->isUndefined())
 
297
              f = static_cast<int>(length) - 1;
 
298
          
 
299
          if (length > 1 && f > 0) {
 
300
              buf[i++] = '.';
 
301
              int haveFDigits = static_cast<int>(length) - 1;
 
302
              if (f < haveFDigits) {
 
303
                  strncpy(buf+i,result+1, f);
 
304
                  i += f;
 
305
              }
 
306
              else {
 
307
                  strcpy(buf+i,result+1);
 
308
                  i += static_cast<int>(length) - 1;
 
309
                  for (int j = 0; j < f-haveFDigits; j++)
 
310
                      buf[i++] = '0';
 
311
              }
 
312
          }
 
313
          
 
314
          buf[i++] = 'e';
 
315
          buf[i++] = (decimalPoint >= 0) ? '+' : '-';
 
316
          // decimalPoint can't be more than 3 digits decimal given the
 
317
          // nature of float representation
 
318
          int exponential = decimalPoint - 1;
 
319
          if (exponential < 0) {
 
320
              exponential = exponential * -1;
 
321
          }
 
322
          if (exponential >= 100) {
 
323
              buf[i++] = static_cast<char>('0' + exponential / 100);
 
324
          }
 
325
          if (exponential >= 10) {
 
326
              buf[i++] = static_cast<char>('0' + (exponential % 100) / 10);
 
327
          }
 
328
          buf[i++] = static_cast<char>('0' + exponential % 10);
 
329
          buf[i++] = '\0';
 
330
      }
 
331
      
 
332
      assert(i <= 80);
 
333
      
 
334
      kjs_freedtoa(result);
 
335
      
 
336
      return jsString(buf);
 
337
  }
 
338
  case ToPrecision:
 
339
  {
 
340
      int e = 0;
 
341
      UString m;
 
342
      
 
343
      double dp = args[0]->toInteger(exec);
 
344
      double x = v->toNumber(exec);
 
345
      if (isNaN(dp) || isNaN(x) || isInf(x))
 
346
          return jsString(v->toString(exec));
 
347
      
 
348
      UString s = "";
 
349
      if (x < 0) {
 
350
          s = "-";
 
351
          x = -x;
 
352
      }
 
353
      
 
354
      if (!(dp >= 1 && dp <= 21)) // true for NaN
 
355
          return throwError(exec, RangeError, "toPrecision() argument must be between 1 and 21");
 
356
      int p = (int)dp;
 
357
      
 
358
      if (x != 0) {
 
359
          e = static_cast<int>(log10(x));
 
360
          double tens = intPow10(e - p + 1);
 
361
          double n = floor(x / tens);
 
362
          if (n < intPow10(p - 1)) {
 
363
              e = e - 1;
 
364
              tens = intPow10(e - p + 1);
 
365
              n = floor(x / tens);
 
366
          }
 
367
          
 
368
          if (fabs((n + 1.0) * tens - x) <= fabs(n * tens - x))
 
369
            ++n;
 
370
          assert(intPow10(p - 1) <= n);
 
371
          assert(n < intPow10(p));
 
372
          
 
373
          m = integer_part_noexp(n);
 
374
          if (e < -6 || e >= p) {
 
375
              if (m.size() > 1)
 
376
                  m = m.substr(0,1)+"."+m.substr(1);
 
377
              if (e >= 0)
 
378
                  return jsString(s+m+"e+"+UString::from(e));
 
379
              else
 
380
                  return jsString(s+m+"e-"+UString::from(-e));
 
381
          }
 
382
      }
 
383
      else {
 
384
          m = char_sequence('0',p);
 
385
          e = 0;
 
386
      }
 
387
      
 
388
      if (e == p-1) {
 
389
          return jsString(s+m);
 
390
      }
 
391
      else if (e >= 0) {
 
392
          if (e+1 < m.size())
 
393
              return jsString(s+m.substr(0,e+1)+"."+m.substr(e+1));
 
394
          else
 
395
              return jsString(s+m.substr(0,e+1));
 
396
      }
 
397
      else {
 
398
          return jsString(s+"0."+char_sequence('0',-(e+1))+m);
 
399
      }
 
400
   }
 
401
      
 
402
 }
 
403
  return NULL;
 
404
}
 
405
 
 
406
// ------------------------------ NumberObjectImp ------------------------------
 
407
 
 
408
const ClassInfo NumberObjectImp::info = {"Function", &InternalFunctionImp::info, &numberTable, 0};
 
409
 
 
410
/* Source for number_object.lut.h
 
411
@begin numberTable 5
 
412
  NaN                   NumberObjectImp::NaNValue       DontEnum|DontDelete|ReadOnly
 
413
  NEGATIVE_INFINITY     NumberObjectImp::NegInfinity    DontEnum|DontDelete|ReadOnly
 
414
  POSITIVE_INFINITY     NumberObjectImp::PosInfinity    DontEnum|DontDelete|ReadOnly
 
415
  MAX_VALUE             NumberObjectImp::MaxValue       DontEnum|DontDelete|ReadOnly
 
416
  MIN_VALUE             NumberObjectImp::MinValue       DontEnum|DontDelete|ReadOnly
 
417
@end
 
418
*/
 
419
NumberObjectImp::NumberObjectImp(ExecState* exec, FunctionPrototype* funcProto, NumberPrototype* numberProto)
 
420
  : InternalFunctionImp(funcProto)
 
421
{
 
422
  // Number.Prototype
 
423
  putDirect(exec->propertyNames().prototype, numberProto,DontEnum|DontDelete|ReadOnly);
 
424
 
 
425
  // no. of arguments for constructor
 
426
  putDirect(exec->propertyNames().length, jsNumber(1), ReadOnly|DontDelete|DontEnum);
 
427
}
 
428
 
 
429
bool NumberObjectImp::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
 
430
{
 
431
  return getStaticValueSlot<NumberObjectImp, InternalFunctionImp>(exec, &numberTable, this, propertyName, slot);
 
432
}
 
433
 
 
434
JSValue *NumberObjectImp::getValueProperty(ExecState *, int token) const
 
435
{
 
436
  // ECMA 15.7.3
 
437
  switch(token) {
 
438
  case NaNValue:
 
439
    return jsNaN();
 
440
  case NegInfinity:
 
441
    return jsNumber(-Inf);
 
442
  case PosInfinity:
 
443
    return jsNumber(Inf);
 
444
  case MaxValue:
 
445
    return jsNumber(1.7976931348623157E+308);
 
446
  case MinValue:
 
447
    return jsNumber(5E-324);
 
448
  }
 
449
  return jsNull();
 
450
}
 
451
 
 
452
bool NumberObjectImp::implementsConstruct() const
 
453
{
 
454
  return true;
 
455
}
 
456
 
 
457
 
 
458
// ECMA 15.7.1
 
459
JSObject *NumberObjectImp::construct(ExecState *exec, const List &args)
 
460
{
 
461
  JSObject *proto = exec->lexicalInterpreter()->builtinNumberPrototype();
 
462
  NumberInstance *obj(new NumberInstance(proto));
 
463
 
 
464
  double n;
 
465
  if (args.isEmpty())
 
466
    n = 0;
 
467
  else
 
468
    n = args[0]->toNumber(exec);
 
469
 
 
470
  obj->setInternalValue(jsNumber(n));
 
471
 
 
472
  return obj;
 
473
}
 
474
 
 
475
// ECMA 15.7.2
 
476
JSValue *NumberObjectImp::callAsFunction(ExecState *exec, JSObject* /*thisObj*/, const List &args)
 
477
{
 
478
  if (args.isEmpty())
 
479
    return jsNumber(0);
 
480
  else
 
481
    return jsNumber(args[0]->toNumber(exec));
 
482
}