• Skip to content
  • Skip to link menu
Trinity API Reference
  • Trinity API Reference
  • kjs
 

kjs

  • kjs
number_object.cpp
1 /*
2  * This file is part of the KDE libraries
3  * Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
4  * Copyright (C) 2003 Peter Kelly (pmk@post.com)
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 "value.h"
23 #include "object.h"
24 #include "types.h"
25 #include "interpreter.h"
26 #include "operations.h"
27 #include "number_object.h"
28 #include "error_object.h"
29 #include "dtoa.h"
30 
31 #include "number_object.lut.h"
32 
33 #include <assert.h>
34 #include <math.h>
35 
36 using namespace KJS;
37 
38 // ------------------------------ NumberInstanceImp ----------------------------
39 
40 const ClassInfo NumberInstanceImp::info = {"Number", 0, 0, 0};
41 
42 NumberInstanceImp::NumberInstanceImp(ObjectImp *proto)
43  : ObjectImp(proto)
44 {
45 }
46 // ------------------------------ NumberPrototypeImp ---------------------------
47 
48 // ECMA 15.7.4
49 
50 NumberPrototypeImp::NumberPrototypeImp(ExecState *exec,
51  ObjectPrototypeImp *objProto,
52  FunctionPrototypeImp *funcProto)
53  : NumberInstanceImp(objProto)
54 {
55  Value protect(this);
56  setInternalValue(NumberImp::zero());
57 
58  // The constructor will be added later, after NumberObjectImp has been constructed
59 
60  putDirect(toStringPropertyName,new NumberProtoFuncImp(exec,funcProto,NumberProtoFuncImp::ToString,
61  1,toStringPropertyName),DontEnum);
62  putDirect(toLocaleStringPropertyName,new NumberProtoFuncImp(exec,funcProto,NumberProtoFuncImp::ToLocaleString,
63  0,toLocaleStringPropertyName),DontEnum);
64  putDirect(valueOfPropertyName,new NumberProtoFuncImp(exec,funcProto,NumberProtoFuncImp::ValueOf,
65  0,valueOfPropertyName),DontEnum);
66  putDirect("toFixed", new NumberProtoFuncImp(exec,funcProto,NumberProtoFuncImp::ToFixed,
67  1,"toFixed"),DontEnum);
68  putDirect("toExponential",new NumberProtoFuncImp(exec,funcProto,NumberProtoFuncImp::ToExponential,
69  1,"toExponential"),DontEnum);
70  putDirect("toPrecision",new NumberProtoFuncImp(exec,funcProto,NumberProtoFuncImp::ToPrecision,
71  1,"toPrecision"),DontEnum);
72 }
73 
74 
75 // ------------------------------ NumberProtoFuncImp ---------------------------
76 
77 NumberProtoFuncImp::NumberProtoFuncImp(ExecState * /*exec*/, FunctionPrototypeImp *funcProto,
78  int i, int len, const Identifier &_ident)
79  : InternalFunctionImp(funcProto), id(i)
80 {
81  Value protect(this);
82  putDirect(lengthPropertyName, len, DontDelete|ReadOnly|DontEnum);
83  ident = _ident;
84 }
85 
86 
87 bool NumberProtoFuncImp::implementsCall() const
88 {
89  return true;
90 }
91 
92 static UString integer_part_noexp(double d)
93 {
94  int decimalPoint;
95  int signDummy;
96  char *result = kjs_dtoa(d, 0, 0, &decimalPoint, &signDummy, NULL);
97  int length = strlen(result);
98 
99  // sign for non-zero, negative numbers
100  UString str = d < 0 ? "-" : "";
101  if (decimalPoint == 9999) {
102  str += UString(result);
103  } else if (decimalPoint <= 0) {
104  str += UString("0");
105  } else {
106  char *buf;
107 
108  if (length <= decimalPoint) {
109  buf = (char*)malloc(decimalPoint+1);
110  strcpy(buf,result);
111  memset(buf+length,'0',decimalPoint-length);
112  } else {
113  buf = (char*)malloc(decimalPoint+1);
114  strncpy(buf,result,decimalPoint);
115  }
116 
117  buf[decimalPoint] = '\0';
118  str += UString(buf);
119  free(buf);
120  }
121 
122  kjs_freedtoa(result);
123 
124  return str;
125 }
126 
127 static UString char_sequence(char c, int count)
128 {
129  char *buf = (char*)malloc(count+1);
130  memset(buf,c,count);
131  buf[count] = '\0';
132  UString s(buf);
133  free(buf);
134  return s;
135 }
136 
137 // ECMA 15.7.4.2 - 15.7.4.7
138 Value NumberProtoFuncImp::call(ExecState *exec, Object &thisObj, const List &args)
139 {
140  Value result;
141 
142  // no generic function. "this" has to be a Number object
143  KJS_CHECK_THIS( NumberInstanceImp, thisObj );
144 
145  // execute "toString()" or "valueOf()", respectively
146  Value v = thisObj.internalValue();
147  switch (id) {
148  case ToString: {
149  int radix = 10;
150  if (!args.isEmpty() && args[0].type() != UndefinedType)
151  radix = args[0].toInteger(exec);
152  if (radix < 2 || radix > 36 || radix == 10)
153  result = String(v.toString(exec));
154  else {
155  const char digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
156  // INT_MAX results in 1024 characters left of the dot with radix 2
157  // give the same space on the right side. safety checks are in place
158  // unless someone finds a precise rule.
159  char s[2048 + 3];
160  double x = v.toNumber(exec);
161  if (isNaN(x) || isInf(x))
162  return String(UString::from(x));
163  // apply algorithm on absolute value. add sign later.
164  bool neg = false;
165  if (x < 0.0) {
166  neg = true;
167  x = -x;
168  }
169  // convert integer portion
170  double f = floor(x);
171  double d = f;
172  char *dot = s + sizeof(s) / 2;
173  char *p = dot;
174  *p = '\0';
175  do {
176  *--p = digits[int(fmod(d, double(radix)))];
177  d /= radix;
178  } while ((d <= -1.0 || d >= 1.0) && p > s);
179  // any decimal fraction ?
180  d = x - f;
181  const double eps = 0.001; // TODO: guessed. base on radix ?
182  if (d < -eps || d > eps) {
183  *dot++ = '.';
184  do {
185  d *= radix;
186  *dot++ = digits[int(d)];
187  d -= int(d);
188  } while ((d < -eps || d > eps) && dot - s < int(sizeof(s)) - 1);
189  *dot = '\0';
190  }
191  // add sign if negative
192  if (neg)
193  *--p = '-';
194  result = String(p);
195  }
196  break;
197  }
198  case ToLocaleString: /* TODO */
199  result = String(v.toString(exec));
200  break;
201  case ValueOf:
202  result = Number(v.toNumber(exec));
203  break;
204  case ToFixed:
205  {
206  // FIXME: firefox works for all values, not just 0..20. This includes
207  // NaN, infinity, undefined, etc. This is just a hack to pass our regression
208  // suite.
209  Value fractionDigits = args[0];
210  int f = -1;
211  double fd = fractionDigits.toNumber(exec);
212  if (isNaN(fd)) {
213  f = 0;
214  } else if (!isInf(fd)) {
215  f = int(fd);
216  }
217  if (f < 0 || f > 20) {
218  Object err = Error::create(exec,RangeError);
219  exec->setException(err);
220  return err;
221  }
222 
223  double x = v.toNumber(exec);
224  if (isNaN(x))
225  return String("NaN");
226 
227  UString s = "";
228  if (x < 0) {
229  s += "-";
230  x = -x;
231  }
232 
233  if (x >= 1e21)
234  return String(s+UString::from(x));
235 
236  double n = floor(x*pow(10.0,f));
237  if (fabs(n/pow(10.0,f)-x) > fabs((n+1)/pow(10.0,f)-x))
238  n++;
239 
240  UString m = integer_part_noexp(n);
241 
242  int k = m.size();
243  if (k <= f) {
244  UString z = "";
245  for (int i = 0; i < f+1-k; i++)
246  z += "0";
247  m = z + m;
248  k = f + 1;
249  assert(k == m.size());
250  }
251  if (k-f < m.size())
252  return String(s+m.substr(0,k-f)+"."+m.substr(k-f));
253  else
254  return String(s+m.substr(0,k-f));
255  }
256  case ToExponential: {
257  double x = v.toNumber(exec);
258 
259  if (isNaN(x) || isInf(x))
260  return String(UString::from(x));
261 
262  int f = 1;
263  Value fractionDigits = args[0];
264  if (args.size() > 0) {
265  f = fractionDigits.toInteger(exec);
266  if (f < 0 || f > 20) {
267  Object err = Error::create(exec,RangeError);
268  exec->setException(err);
269  return err;
270  }
271  }
272 
273  int decimalAdjust = 0;
274  if (!fractionDigits.isA(UndefinedType)) {
275  double logx = floor(log10(fabs(x)));
276  x /= pow(10.0,logx);
277  double fx = floor(x*pow(10.0,f))/pow(10.0,f);
278  double cx = ceil(x*pow(10.0,f))/pow(10.0,f);
279 
280  if (fabs(fx-x) < fabs(cx-x))
281  x = fx;
282  else
283  x = cx;
284 
285  decimalAdjust = int(logx);
286  }
287 
288  char buf[80];
289  int decimalPoint;
290  int sign;
291 
292  if (isNaN(x))
293  return String("NaN");
294 
295  char *result = kjs_dtoa(x, 0, 0, &decimalPoint, &sign, NULL);
296  int length = strlen(result);
297  decimalPoint += decimalAdjust;
298 
299  int i = 0;
300  if (sign) {
301  buf[i++] = '-';
302  }
303 
304  if (decimalPoint == 999) {
305  strcpy(buf + i, result);
306  } else {
307  buf[i++] = result[0];
308 
309  if (fractionDigits.isA(UndefinedType))
310  f = length-1;
311 
312  if (length > 1 && f > 0) {
313  buf[i++] = '.';
314  int haveFDigits = length-1;
315  if (f < haveFDigits) {
316  strncpy(buf+i,result+1, f);
317  i += f;
318  }
319  else {
320  strcpy(buf+i,result+1);
321  i += length-1;
322  for (int j = 0; j < f-haveFDigits; j++)
323  buf[i++] = '0';
324  }
325  }
326 
327  buf[i++] = 'e';
328  buf[i++] = (decimalPoint >= 0) ? '+' : '-';
329  // decimalPoint can't be more than 3 digits decimal given the
330  // nature of float representation
331  int exponential = decimalPoint - 1;
332  if (exponential < 0) {
333  exponential = exponential * -1;
334  }
335  if (exponential >= 100) {
336  buf[i++] = '0' + exponential / 100;
337  }
338  if (exponential >= 10) {
339  buf[i++] = '0' + (exponential % 100) / 10;
340  }
341  buf[i++] = '0' + exponential % 10;
342  buf[i++] = '\0';
343  }
344 
345  assert(i <= 80);
346 
347  kjs_freedtoa(result);
348 
349  return String(UString(buf));
350  }
351  case ToPrecision:
352  {
353  int e = 0;
354  UString m;
355 
356  int p = args[0].toInteger(exec);
357  double x = v.toNumber(exec);
358  if (args[0].isA(UndefinedType) || isNaN(x) || isInf(x))
359  return String(v.toString(exec));
360 
361  UString s = "";
362  if (x < 0) {
363  s = "-";
364  x = -x;
365  }
366 
367  if (p < 1 || p > 21) {
368  Object err = Error::create(exec, RangeError,
369  "toPrecision() argument must be between 1 and 21");
370  exec->setException(err);
371  return err;
372  }
373 
374  if (x != 0) {
375  // suggestions for a better algorithm welcome!
376  e = int(log10(x));
377  double n = floor(x/pow(10.0,e-p+1));
378  if (n < pow(10.0,p-1)) {
379  // first guess was not good
380  e = e - 1;
381  n = floor(x/pow(10.0,e-p+1));
382  if (n >= pow(10.0,p)) {
383  // violated constraint. try something else.
384  n = pow(10.0,p-1);
385  e = int(log10(x/n)) + p - 1;
386  }
387  }
388 
389  if (fabs((n+1)*pow(10.0,e-p+1)-x) < fabs(n*pow(10.0,e-p+1)-x))
390  n++;
391  assert(pow(10.0,p-1) <= n);
392  assert(n < pow(10.0,p));
393 
394  m = integer_part_noexp(n);
395  if (e < -6 || e >= p) {
396  if (m.size() > 1)
397  m = m.substr(0,1)+"."+m.substr(1);
398  if (e >= 0)
399  return String(s+m+"e+"+UString::from(e));
400  else
401  return String(s+m+"e-"+UString::from(-e));
402  }
403  }
404  else {
405  m = char_sequence('0',p);
406  e = 0;
407  }
408 
409  if (e == p-1) {
410  return String(s+m);
411  }
412  else if (e >= 0) {
413  if (e+1 < m.size())
414  return String(s+m.substr(0,e+1)+"."+m.substr(e+1));
415  else
416  return String(s+m.substr(0,e+1));
417  }
418  else {
419  return String(s+"0."+char_sequence('0',-(e+1))+m);
420  }
421  }
422  }
423 
424  return result;
425 }
426 
427 // ------------------------------ NumberObjectImp ------------------------------
428 
429 const ClassInfo NumberObjectImp::info = {"Function", &InternalFunctionImp::info, &numberTable, 0};
430 
431 /* Source for number_object.lut.h
432 @begin numberTable 5
433  NaN NumberObjectImp::NaNValue DontEnum|DontDelete|ReadOnly
434  NEGATIVE_INFINITY NumberObjectImp::NegInfinity DontEnum|DontDelete|ReadOnly
435  POSITIVE_INFINITY NumberObjectImp::PosInfinity DontEnum|DontDelete|ReadOnly
436  MAX_VALUE NumberObjectImp::MaxValue DontEnum|DontDelete|ReadOnly
437  MIN_VALUE NumberObjectImp::MinValue DontEnum|DontDelete|ReadOnly
438 @end
439 */
440 NumberObjectImp::NumberObjectImp(ExecState * /*exec*/,
441  FunctionPrototypeImp *funcProto,
442  NumberPrototypeImp *numberProto)
443  : InternalFunctionImp(funcProto)
444 {
445  Value protect(this);
446  // Number.Prototype
447  putDirect(prototypePropertyName, numberProto, DontEnum|DontDelete|ReadOnly);
448 
449  // no. of arguments for constructor
450  putDirect(lengthPropertyName, NumberImp::one(), ReadOnly|DontDelete|DontEnum);
451 }
452 
453 Value NumberObjectImp::get(ExecState *exec, const Identifier &propertyName) const
454 {
455  return lookupGetValue<NumberObjectImp, InternalFunctionImp>( exec, propertyName, &numberTable, this );
456 }
457 
458 Value NumberObjectImp::getValueProperty(ExecState *, int token) const
459 {
460  // ECMA 15.7.3
461  switch(token) {
462  case NaNValue:
463  return Number(NaN);
464  case NegInfinity:
465  return Number(-Inf);
466  case PosInfinity:
467  return Number(Inf);
468  case MaxValue:
469  return Number(1.7976931348623157E+308);
470  case MinValue:
471  return Number(5E-324);
472  }
473  return Null();
474 }
475 
476 bool NumberObjectImp::implementsConstruct() const
477 {
478  return true;
479 }
480 
481 
482 // ECMA 15.7.1
483 Object NumberObjectImp::construct(ExecState *exec, const List &args)
484 {
485  ObjectImp *proto = exec->lexicalInterpreter()->builtinNumberPrototype().imp();
486  Object obj(new NumberInstanceImp(proto));
487 
488  Number n;
489  if (args.isEmpty())
490  n = Number(0);
491  else
492  n = args[0].toNumber(exec);
493 
494  obj.setInternalValue(n);
495 
496  return obj;
497 }
498 
499 bool NumberObjectImp::implementsCall() const
500 {
501  return true;
502 }
503 
504 // ECMA 15.7.2
505 Value NumberObjectImp::call(ExecState *exec, Object &/*thisObj*/, const List &args)
506 {
507  if (args.isEmpty())
508  return Number(0);
509  else
510  return Number(args[0].toNumber(exec));
511 }
KJS::Value
Value objects are act as wrappers ("smart pointers") around ValueImp objects and their descendents...
Definition: value.h:167
KJS::UString::substr
UString substr(int pos=0, int len=-1) const
Definition: ustring.cpp:868
KJS::InternalFunctionImp
Base class for all function objects.
Definition: function.h:40
KJS::UString::from
static UString from(int i)
Constructs a string from an int.
Definition: ustring.cpp:340
KJS::UString::size
int size() const
Definition: ustring.h:359
KJS::Number
Represents an primitive Number value.
Definition: value.h:367
KJS::Null
Represents an primitive Null value.
Definition: value.h:294
KJS::ExecState::lexicalInterpreter
Interpreter * lexicalInterpreter() const
Returns the interpreter associated with the current scope's global object.
Definition: interpreter.cpp:394
KJS::Error::create
static Object create(ExecState *exec, ErrorType errtype=GeneralError, const char *message=0, int lineno=-1, int sourceId=-1)
Factory method for error objects.
Definition: object.cpp:503
KJS::FunctionPrototypeImp
The initial value of Function.prototype (and thus all objects created with the Function constructor) ...
Definition: function_object.h:34
KJS::Value::toNumber
double toNumber(ExecState *exec) const
Performs the ToNumber type conversion operation on this value (ECMA 9.3)
Definition: value.h:221
KJS::Object
Represents an Object.
Definition: object.h:81
KJS::Value::isA
bool isA(Type t) const
Checks whether or not the value is of a particular tpye.
Definition: value.h:203
KJS::UString
Unicode string class.
Definition: ustring.h:189
KJS::String
Represents an primitive String value.
Definition: value.h:340
KJS
Definition: array_instance.h:27
KJS::Object::internalValue
Value internalValue() const
Returns the internal value of the object.
Definition: object.h:717
KJS::List::size
int size() const
Definition: list.h:90
KJS::List
Native list type.
Definition: list.h:48
KJS::Value::toString
UString toString(ExecState *exec) const
Performs the ToString type conversion operation on this value (ECMA 9.8)
Definition: value.h:246
KJS::ClassInfo
Class Information.
Definition: object.h:58
KJS::Interpreter::builtinNumberPrototype
Object builtinNumberPrototype() const
Returns the builtin "Number.prototype" object.
Definition: interpreter.cpp:243
KJS::Value::toInteger
int toInteger(ExecState *exec) const
Performs the ToInteger type conversion operation on this value (ECMA 9.4)
Definition: value.h:226
KJS::List::isEmpty
bool isEmpty() const
Definition: list.h:86
KJS::ExecState
Represents the current state of script execution.
Definition: interpreter.h:438
KJS::Identifier
Represents an Identifier for a Javascript object.
Definition: identifier.h:32

kjs

Skip menu "kjs"
  • Main Page
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Class Members
  • Related Pages

kjs

Skip menu "kjs"
  • arts
  • dcop
  • dnssd
  • interfaces
  •   kspeech
  •     interface
  •     library
  •   tdetexteditor
  • kate
  • kded
  • kdoctools
  • kimgio
  • kjs
  • libtdemid
  • libtdescreensaver
  •     tdecore
  • tdeabc
  • tdecmshell
  • tdecore
  • tdefx
  • tdehtml
  • tdeinit
  • tdeio
  •   bookmarks
  •   httpfilter
  •   kpasswdserver
  •   kssl
  • tdeioslave
  •   http
  •   tdefile
  •   tdeio
  •   tdeioexec
  • tdemdi
  •   tdemdi
  • tdenewstuff
  • tdeparts
  • tdeprint
  • tderandr
  • tderesources
  • tdespell2
  • tdesu
  • tdeui
  • tdeunittest
  • tdeutils
  • tdewallet
Generated for kjs by doxygen 1.8.8
This website is maintained by Timothy Pearson.