1 /*
2  *  This file is part of the KDE libraries
3  *  Copyright (C) 1999-2002 Harri Porten (porten@kde.org)
4  *  Copyright (C) 2001 Peter Kelly (pmk@post.com)
5  *  Copyright (C) 2004 Apple Computer, Inc.
6  *
7  *  This library is free software; you can redistribute it and/or
8  *  modify it under the terms of the GNU Library General Public
9  *  License as published by the Free Software Foundation; either
10  *  version 2 of the License, or (at your option) any later version.
11  *
12  *  This library is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  *  Library General Public License for more details.
16  *
17  *  You should have received a copy of the GNU Library General Public License
18  *  along with this library; see the file COPYING.LIB.  If not, write to
19  *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20  *  Boston, MA 02110-1301, USA.
21  *
22  */
23 
24 #include "internal.h"
25 
26 #include "array_object.h"
27 #include "bool_object.h"
28 #include "collector.h"
29 #include "date_object.h"
30 #include "debugger.h"
31 #include "error_object.h"
32 #include "function_object.h"
33 #include "lexer.h"
34 #include "math_object.h"
35 #include "nodes.h"
36 #include "number_object.h"
37 #include "object_object.h"
38 #include "operations.h"
39 #include "regexp_object.h"
40 #include "string_object.h"
41 #include <assert.h>
42 #include <wtf/HashMap.h>
43 #include <wtf/HashSet.h>
44 #include <wtf/Vector.h>
45 #include <math.h>
46 #include <stdio.h>
47 
48 namespace KJS
49 {
50 
51 #if defined(WTF_COMPILER_MSVC)
52 #define copysign _copysign
53 #endif
54 
55 static const double D16 = 65536.0;
56 static const double D32 = 4294967296.0;
57 
58 // ------------------------------ StringImp ------------------------------------
59 
toPrimitive(ExecState *,JSType) const60 JSValue *StringImp::toPrimitive(ExecState *, JSType) const
61 {
62     return const_cast<StringImp *>(this);
63 }
64 
getPrimitiveNumber(ExecState *,double & number,JSValue * & value)65 bool GetterSetterImp::getPrimitiveNumber(ExecState *, double &number, JSValue *&value)
66 {
67     ASSERT_NOT_REACHED();
68     number = 0;
69     value = nullptr;
70     return true;
71 }
72 
getPrimitiveNumber(ExecState *,double & number,JSValue * & value)73 bool StringImp::getPrimitiveNumber(ExecState *, double &number, JSValue *&value)
74 {
75     value = this;
76     number = val.toDouble();
77     return false;
78 }
79 
toBoolean(ExecState *) const80 bool StringImp::toBoolean(ExecState *) const
81 {
82     return (val.size() > 0);
83 }
84 
toNumber(ExecState *) const85 double StringImp::toNumber(ExecState *) const
86 {
87     return val.toDouble();
88 }
89 
toString(ExecState *) const90 UString StringImp::toString(ExecState *) const
91 {
92     return val;
93 }
94 
toObject(ExecState * exec) const95 JSObject *StringImp::toObject(ExecState *exec) const
96 {
97     return new StringInstance(exec->lexicalInterpreter()->builtinStringPrototype(), const_cast<StringImp *>(this));
98 }
99 
100 // ------------------------------ NumberImp ------------------------------------
101 
toPrimitive(ExecState *,JSType) const102 JSValue *NumberImp::toPrimitive(ExecState *, JSType) const
103 {
104     return const_cast<NumberImp *>(this);
105 }
106 
getPrimitiveNumber(ExecState *,double & number,JSValue * & value)107 bool NumberImp::getPrimitiveNumber(ExecState *, double &number, JSValue *&value)
108 {
109     number = val;
110     value = this;
111     return true;
112 }
113 
toBoolean(ExecState *) const114 bool NumberImp::toBoolean(ExecState *) const
115 {
116     return val < 0.0 || val > 0.0; // false for NaN
117 }
118 
toNumber(ExecState *) const119 double NumberImp::toNumber(ExecState *) const
120 {
121     return val;
122 }
123 
toString(ExecState *) const124 UString NumberImp::toString(ExecState *) const
125 {
126     if (val == 0.0) { // +0.0 or -0.0
127         return "0";
128     }
129     return UString::from(val);
130 }
131 
toObject(ExecState * exec) const132 JSObject *NumberImp::toObject(ExecState *exec) const
133 {
134     List args;
135     args.append(const_cast<NumberImp *>(this));
136     return static_cast<JSObject *>(exec->lexicalInterpreter()->builtinNumber()->construct(exec, args));
137 }
138 
getUInt32(uint32_t & uint32) const139 bool NumberImp::getUInt32(uint32_t &uint32) const
140 {
141     uint32 = static_cast<uint32_t>(val);
142     return uint32 == val;
143 }
144 
getTruncatedInt32(int32_t & int32) const145 bool NumberImp::getTruncatedInt32(int32_t &int32) const
146 {
147     if (!(val >= -2147483648.0 && val < 2147483648.0)) {
148         return false;
149     }
150     int32 = static_cast<int32_t>(val);
151     return true;
152 }
153 
getTruncatedUInt32(uint32_t & uint32) const154 bool NumberImp::getTruncatedUInt32(uint32_t &uint32) const
155 {
156     if (!(val >= 0.0 && val < 4294967296.0)) {
157         return false;
158     }
159     uint32 = static_cast<uint32_t>(val);
160     return true;
161 }
162 
163 // --------------------------- GetterSetterImp ---------------------------------
mark()164 void GetterSetterImp::mark()
165 {
166     JSCell::mark();
167 
168     if (getter && !getter->marked()) {
169         getter->mark();
170     }
171     if (setter && !setter->marked()) {
172         setter->mark();
173     }
174 }
175 
toPrimitive(ExecState *,JSType) const176 JSValue *GetterSetterImp::toPrimitive(ExecState *, JSType) const
177 {
178     assert(false);
179     return jsNull();
180 }
181 
toBoolean(ExecState *) const182 bool GetterSetterImp::toBoolean(ExecState *) const
183 {
184     assert(false);
185     return false;
186 }
187 
toNumber(ExecState *) const188 double GetterSetterImp::toNumber(ExecState *) const
189 {
190     assert(false);
191     return 0.0;
192 }
193 
toString(ExecState *) const194 UString GetterSetterImp::toString(ExecState *) const
195 {
196     assert(false);
197     return UString::null();
198 }
199 
toObject(ExecState * exec) const200 JSObject *GetterSetterImp::toObject(ExecState *exec) const
201 {
202     assert(false);
203     return JSValue::toObject(jsNull(), exec);
204 }
205 
206 // ------------------------------ InternalFunctionImp --------------------------
207 
208 const ClassInfo InternalFunctionImp::info = {"Function", nullptr, nullptr, nullptr};
209 
InternalFunctionImp()210 InternalFunctionImp::InternalFunctionImp()
211 {
212 }
213 
InternalFunctionImp(FunctionPrototype * funcProto)214 InternalFunctionImp::InternalFunctionImp(FunctionPrototype *funcProto)
215     : JSObject(funcProto)
216 {
217 }
218 
InternalFunctionImp(FunctionPrototype * funcProto,const Identifier & name)219 InternalFunctionImp::InternalFunctionImp(FunctionPrototype *funcProto, const Identifier &name)
220     : JSObject(funcProto)
221     , m_name(name)
222 {
223 }
224 
implementsCall() const225 bool InternalFunctionImp::implementsCall() const
226 {
227     return true;
228 }
229 
implementsHasInstance() const230 bool InternalFunctionImp::implementsHasInstance() const
231 {
232     return true;
233 }
234 
235 // ------------------------------ global functions -----------------------------
236 
roundValue(double d)237 double roundValue(double d)
238 {
239     double ad = fabs(d);
240     if (ad == 0 || isNaN(d) || isInf(d)) {
241         return d;
242     }
243     return copysign(floor(ad), d);
244 }
245 
toInt32(double d)246 int32_t toInt32(double d)
247 {
248     if (isNaN(d) || isInf(d)) {
249         return 0;
250     }
251     double d32 = fmod(roundValue(d), D32);
252 
253     if (d32 >= D32 / 2) {
254         d32 -= D32;
255     } else if (d32 < -D32 / 2) {
256         d32 += D32;
257     }
258 
259     return static_cast<int32_t>(d32);
260 }
261 
toInt32(double d,bool & ok)262 int32_t toInt32(double d, bool &ok)
263 {
264     ok = true;
265     if (isNaN(d) || isInf(d)) {
266         ok = false;
267         return 0;
268     }
269     return toInt32(d);
270 }
271 
toUInt32(double dd)272 uint32_t toUInt32(double dd)
273 {
274     double d = roundValue(dd);
275     if (isNaN(d) || isInf(d)) {
276         return 0;
277     }
278     double d32 = fmod(d, D32);
279 
280     if (d32 < 0) {
281         d32 += D32;
282     }
283 
284     return static_cast<uint32_t>(d32);
285 }
286 
toUInt16(double dd)287 uint16_t toUInt16(double dd)
288 {
289     double d = roundValue(dd);
290     if (isNaN(d) || isInf(d)) {
291         return 0;
292     }
293     double d16 = fmod(d, D16);
294 
295     if (d16 < 0) {
296         d16 += D16;
297     }
298 
299     return static_cast<uint16_t>(d16);
300 }
301 
302 //#ifndef NDEBUG
printInfo(ExecState * exec,const char * s,JSValue * o,int lineno)303 void printInfo(ExecState *exec, const char *s, JSValue *o, int lineno)
304 {
305     UString vString;
306     if (!o) {
307         fprintf(stderr, "KJS: %s: (null)", s);
308     } else {
309         JSValue *v = o;
310 
311         unsigned int arrayLength = 0;
312         bool hadExcep = exec->hadException();
313 
314         UString name;
315         switch (JSValue::type(v)) {
316         case UnspecifiedType:
317             name = "Unspecified";
318             break;
319         case UndefinedType:
320             name = "Undefined";
321             break;
322         case NullType:
323             name = "Null";
324             break;
325         case BooleanType:
326             name = "Boolean";
327             break;
328         case StringType:
329             name = "String";
330             break;
331         case NumberType:
332             name = "Number";
333             break;
334         case ObjectType: {
335             JSObject *obj = static_cast<JSObject *>(v);
336             name = obj->className();
337             if (name.isNull()) {
338                 name = "(unknown class)";
339             }
340 
341             if (obj->inherits(&ArrayInstance::info)) {
342                 arrayLength = JSValue::toUInt32(obj->get(exec, exec->propertyNames().length), exec);
343             }
344             vString = "[object " + name + "]"; // krazy:exclude=doublequote_chars
345             break;
346         }
347         case GetterSetterType:
348             name = "GetterSetter";
349             break;
350         }
351 
352         // Avoid calling toString on a huge array (e.g. 4 billion elements, in mozilla/js/js1_5/Array/array-001.js)
353         if (arrayLength > 100) {
354             vString = UString("[ Array with ") + UString::from(arrayLength) + " elements ]";
355         } else if (JSValue::type(v) != ObjectType) { // Don't want to call a user toString function!
356             vString = JSValue::toString(v, exec);
357         }
358         if (!hadExcep) {
359             exec->clearException();
360         }
361 
362         if (vString.size() > 350) {
363             vString = vString.substr(0, 350) + "...";
364         }
365 
366         // Can't use two UString::ascii() in the same fprintf call
367         CString tempString(vString.cstring());
368 
369         fprintf(stderr, "KJS: %s: %s : %s (%p)",
370                 s, tempString.c_str(), name.ascii(), (void *)v);
371 
372         if (lineno >= 0) {
373             fprintf(stderr, ", line %d\n", lineno);
374         } else {
375             fprintf(stderr, "\n");
376         }
377     }
378 }
379 //#endif
380 
381 }
382