1 /*
2  *  This file is part of the KDE libraries
3  *  Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
4  *  Copyright (C) 2001 Peter Kelly (pmk@post.com)
5  *  Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, Inc.
6  *  Copyright (C) 2007 Eric Seidel (eric@webkit.org)
7  *
8  *  This library is free software; you can redistribute it and/or
9  *  modify it under the terms of the GNU Library General Public
10  *  License as published by the Free Software Foundation; either
11  *  version 2 of the License, or (at your option) any later version.
12  *
13  *  This library is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  *  Library General Public License for more details.
17  *
18  *  You should have received a copy of the GNU Library General Public License
19  *  along with this library; see the file COPYING.LIB.  If not, write to
20  *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21  *  Boston, MA 02110-1301, USA.
22  *
23  */
24 
25 #include "object.h"
26 
27 #include "error_object.h"
28 #include "lookup.h"
29 #include "nodes.h"
30 #include "operations.h"
31 #include "PropertyNameArray.h"
32 #include <math.h>
33 
34 #include <typeinfo>
35 
36 #define JAVASCRIPT_MARK_TRACING 0
37 
38 namespace KJS
39 {
40 
41 // ------------------------------ JSObject ------------------------------------
42 
mark()43 void JSObject::mark()
44 {
45     JSCell::mark();
46 
47 #if JAVASCRIPT_MARK_TRACING
48     static int markStackDepth = 0;
49     markStackDepth++;
50     for (int i = 0; i < markStackDepth; i++) {
51         putchar('-');
52     }
53 
54     printf("%s (%p)\n", className().UTF8String().c_str(), this);
55 #endif
56 
57     JSValue *proto = _proto;
58     if (!JSValue::marked(proto)) {
59         JSValue::mark(proto);
60     }
61 
62     _prop.mark();
63 
64 #if JAVASCRIPT_MARK_TRACING
65     markStackDepth--;
66 #endif
67 }
68 
type() const69 JSType JSObject::type() const
70 {
71     return ObjectType;
72 }
73 
classInfo() const74 const ClassInfo *JSObject::classInfo() const
75 {
76     return nullptr;
77 }
78 
className() const79 UString JSObject::className() const
80 {
81     const ClassInfo *ci = classInfo();
82     return ci ? ci->className : "Object";
83 }
84 
get(ExecState * exec,const Identifier & propertyName) const85 JSValue *JSObject::get(ExecState *exec, const Identifier &propertyName) const
86 {
87     PropertySlot slot;
88 
89     if (const_cast<JSObject *>(this)->getPropertySlot(exec, propertyName, slot)) {
90         JSValue *val = slot.getValue(exec, const_cast<JSObject *>(this), propertyName);
91         assert(val);
92         return val;
93     }
94 
95     return jsUndefined();
96 }
97 
get(ExecState * exec,unsigned propertyName) const98 JSValue *JSObject::get(ExecState *exec, unsigned propertyName) const
99 {
100     PropertySlot slot;
101     if (const_cast<JSObject *>(this)->getPropertySlot(exec, propertyName, slot)) {
102         return slot.getValue(exec, const_cast<JSObject *>(this), propertyName);
103     }
104 
105     return jsUndefined();
106 }
107 
getPropertySlot(ExecState * exec,unsigned propertyName,PropertySlot & slot)108 bool JSObject::getPropertySlot(ExecState *exec, unsigned propertyName, PropertySlot &slot)
109 {
110     JSObject *imp = this;
111 
112     while (true) {
113         if (imp->getOwnPropertySlot(exec, propertyName, slot)) {
114             return true;
115         }
116 
117         JSValue *proto = imp->_proto;
118         if (!JSValue::isObject(proto)) {
119             break;
120         }
121 
122         imp = static_cast<JSObject *>(proto);
123     }
124 
125     return false;
126 }
127 
getPropertyDescriptor(ExecState * exec,const Identifier & propertyName,PropertyDescriptor & desc)128 bool JSObject::getPropertyDescriptor(ExecState *exec, const Identifier &propertyName, PropertyDescriptor &desc)
129 {
130     JSObject *object = this;
131     while (true) {
132         if (object->getOwnPropertyDescriptor(exec, propertyName, desc)) {
133             return true;
134         }
135         JSValue *prototype = object->prototype();
136         if (!JSValue::isObject(prototype)) {
137             return false;
138         }
139         object = JSValue::toObject(prototype, exec);
140     }
141 }
142 
getOwnPropertySlot(ExecState * exec,unsigned propertyName,PropertySlot & slot)143 bool JSObject::getOwnPropertySlot(ExecState *exec, unsigned propertyName, PropertySlot &slot)
144 {
145     return getOwnPropertySlot(exec, Identifier::from(propertyName), slot);
146 }
147 
148 // Ideally, we would like to inline this, since it's ultra-hot, but with the large VM
149 // loop, it seems like the code side gets the g++-4.3.x inliner in the paranoid mode, so not only
150 // does it not inline this, but it also doesn't inline setValueSlot() and hasGetterSetterProperties() (!!!).
getOwnPropertySlot(ExecState * exec,const Identifier & propertyName,PropertySlot & slot)151 bool JSObject::getOwnPropertySlot(ExecState *exec, const Identifier &propertyName, PropertySlot &slot)
152 {
153     if (JSValue **location = getDirectLocation(propertyName)) {
154         if (_prop.hasGetterSetterProperties() && JSValue::type(location[0]) == GetterSetterType) {
155             fillGetterPropertySlot(slot, location);
156         } else {
157             slot.setValueSlot(this, location);
158         }
159         return true;
160     }
161 
162     // non-standard Netscape extension
163     if (propertyName == exec->propertyNames().underscoreProto) {
164         slot.setValueSlot(this, &_proto);
165         return true;
166     }
167 
168     return false;
169 }
170 
getOwnPropertyDescriptor(ExecState * exec,const Identifier & identifier,PropertyDescriptor & desc)171 bool JSObject::getOwnPropertyDescriptor(ExecState *exec, const Identifier &identifier, PropertyDescriptor &desc)
172 {
173     JSValue *jsVal = getDirect(identifier);
174 
175     // for classes that do not implement getDirect, like the prototypes,
176     // we have to check if they still do own the property and use the propertyslot
177     if (!jsVal) {
178         PropertySlot slot;
179         if (getOwnPropertySlot(exec, identifier, slot)) {
180             jsVal = slot.getValue(exec, this, identifier);
181         }
182     }
183 
184     if (jsVal) {
185         unsigned attr = 0;
186         getPropertyAttributes(identifier, attr);
187         return desc.setPropertyDescriptorValues(exec, jsVal, attr);
188     }
189     return false;
190 }
191 
throwSetterError(ExecState * exec)192 static void throwSetterError(ExecState *exec)
193 {
194     throwError(exec, TypeError, "setting a property that has only a getter");
195 }
196 
197 // ECMA 8.6.2.2
put(ExecState * exec,const Identifier & propertyName,JSValue * value,int attr)198 void JSObject::put(ExecState *exec, const Identifier &propertyName, JSValue *value, int attr)
199 {
200     assert(value);
201 
202     // non-standard netscape extension
203     if (propertyName == exec->propertyNames().underscoreProto) {
204         JSObject *proto = JSValue::getObject(value);
205         while (proto) {
206             if (proto == this) {
207                 throwError(exec, GeneralError, "cyclic __proto__ value");
208                 return;
209             }
210             proto = proto->prototype() ? JSValue::getObject(proto->prototype()) : nullptr;
211         }
212 
213         setPrototype(value);
214         return;
215     }
216 
217     // putValue() is used for JS assignments. It passes no attribute.
218     // Assume that a C++ implementation knows what it is doing
219     // and don't spend time doing a read-only check for it.
220     bool checkRO = (attr == None || attr == DontDelete);
221 
222     if (checkRO) {
223         // Check for static properties that are ReadOnly; the property map will check the dynamic properties.
224         // We don't have to worry about setters being read-only as they can't be added with such an attribute.
225         // We also need to inherit any attributes we have from the entry
226         const HashEntry *entry = findPropertyHashEntry(propertyName);
227         if (entry) {
228             if (entry->attr & ReadOnly) {
229 #ifdef KJS_VERBOSE
230                 fprintf(stderr, "WARNING: static property %s is ReadOnly\n", propertyName.ascii());
231 #endif
232                 return;
233             }
234             attr = entry->attr;
235         }
236     }
237 
238     // Check if there are any setters or getters in the prototype chain
239     JSObject *obj = this;
240     bool hasGettersOrSetters = false;
241     while (true) {
242         if (obj->_prop.hasGetterSetterProperties()) {
243             hasGettersOrSetters = true;
244             break;
245         }
246 
247         if (!JSValue::isObject(obj->_proto)) {
248             break;
249         }
250 
251         obj = static_cast<JSObject *>(obj->_proto);
252     }
253 
254     if (hasGettersOrSetters) {
255         obj = this;
256         while (true) {
257             unsigned attributes;
258             if (JSValue *gs = obj->_prop.get(propertyName, attributes)) {
259                 if (attributes & GetterSetter) {
260                     JSObject *setterFunc = static_cast<GetterSetterImp *>(gs)->getSetter();
261 
262                     if (!setterFunc) {
263                         if (false) { //only throw if strict is set
264                             throwSetterError(exec);
265                         }
266                         return;
267                     }
268 
269                     List args;
270                     args.append(value);
271 
272                     setterFunc->call(exec, this, args);
273                     return;
274                 } else {
275                     // If there's an existing property on the object or one of its
276                     // prototype it should be replaced, so we just break here.
277                     break;
278                 }
279             }
280 
281             if (!JSValue::isObject(obj->_proto)) {
282                 break;
283             }
284 
285             obj = static_cast<JSObject *>(obj->_proto);
286         }
287     }
288 
289     if (!isExtensible() && !_prop.get(propertyName)) {
290         return;
291     }
292     _prop.put(propertyName, value, attr, checkRO);
293 }
294 
put(ExecState * exec,unsigned propertyName,JSValue * value,int attr)295 void JSObject::put(ExecState *exec, unsigned propertyName,
296                    JSValue *value, int attr)
297 {
298     put(exec, Identifier::from(propertyName), value, attr);
299 }
300 
301 // ECMA 8.6.2.3
canPut(ExecState *,const Identifier & propertyName) const302 bool JSObject::canPut(ExecState *, const Identifier &propertyName) const
303 {
304     unsigned attributes;
305 
306     // Don't look in the prototype here. We can always put an override
307     // in the object, even if the prototype has a ReadOnly property.
308 
309     if (!getPropertyAttributes(propertyName, attributes)) {
310         return true;
311     } else {
312         return !(attributes & ReadOnly);
313     }
314 }
315 
316 // ECMA 8.6.2.4
hasProperty(ExecState * exec,const Identifier & propertyName) const317 bool JSObject::hasProperty(ExecState *exec, const Identifier &propertyName) const
318 {
319     PropertySlot slot;
320     return const_cast<JSObject *>(this)->getPropertySlot(exec, propertyName, slot);
321 }
322 
hasProperty(ExecState * exec,unsigned propertyName) const323 bool JSObject::hasProperty(ExecState *exec, unsigned propertyName) const
324 {
325     PropertySlot slot;
326     return const_cast<JSObject *>(this)->getPropertySlot(exec, propertyName, slot);
327 }
328 
329 // ECMA 8.6.2.5
deleteProperty(ExecState *,const Identifier & propertyName)330 bool JSObject::deleteProperty(ExecState * /*exec*/, const Identifier &propertyName)
331 {
332     unsigned attributes;
333     JSValue *v = _prop.get(propertyName, attributes);
334     if (v) {
335         if ((attributes & DontDelete)) {
336             return false;
337         }
338         _prop.remove(propertyName);
339         if (attributes & GetterSetter) {
340             _prop.setHasGetterSetterProperties(_prop.containsGettersOrSetters());
341         }
342         return true;
343     }
344 
345     // Look in the static hashtable of properties
346     const HashEntry *entry = findPropertyHashEntry(propertyName);
347     if (entry && entry->attr & DontDelete) {
348         return false;    // this builtin property can't be deleted
349     }
350     return true;
351 }
352 
deleteProperty(ExecState * exec,unsigned propertyName)353 bool JSObject::deleteProperty(ExecState *exec, unsigned propertyName)
354 {
355     return deleteProperty(exec, Identifier::from(propertyName));
356 }
357 
tryGetAndCallProperty(ExecState * exec,const JSObject * object,const Identifier & propertyName)358 static ALWAYS_INLINE JSValue *tryGetAndCallProperty(ExecState *exec, const JSObject *object, const Identifier &propertyName)
359 {
360     JSValue *v = object->get(exec, propertyName);
361     if (JSValue::isObject(v)) {
362         JSObject *o = static_cast<JSObject *>(v);
363         if (o->implementsCall()) { // spec says "not primitive type" but ...
364             JSObject *thisObj = const_cast<JSObject *>(object);
365             JSValue *def = o->call(exec, thisObj, List::empty());
366             JSType defType = JSValue::type(def);
367             ASSERT(defType != GetterSetterType);
368             if (defType != ObjectType) {
369                 return def;
370             }
371         }
372     }
373     return nullptr;
374 }
375 
getPrimitiveNumber(ExecState * exec,double & number,JSValue * & result)376 bool JSObject::getPrimitiveNumber(ExecState *exec, double &number, JSValue *&result)
377 {
378     result = defaultValue(exec, NumberType);
379     number = JSValue::toNumber(result, exec);
380     return !JSValue::isString(result);
381 }
382 
383 // ECMA 8.6.2.6
defaultValue(ExecState * exec,JSType hint) const384 JSValue *JSObject::defaultValue(ExecState *exec, JSType hint) const
385 {
386     const Identifier *firstPropertyName;
387     const Identifier *secondPropertyName;
388     /* Prefer String for Date objects */
389     if ((hint == StringType) || ((hint != NumberType) && (_proto == exec->lexicalInterpreter()->builtinDatePrototype()))) {
390         firstPropertyName = &exec->propertyNames().toString;
391         secondPropertyName = &exec->propertyNames().valueOf;
392     } else {
393         firstPropertyName = &exec->propertyNames().valueOf;
394         secondPropertyName = &exec->propertyNames().toString;
395     }
396 
397     JSValue *v;
398     if ((v = tryGetAndCallProperty(exec, this, *firstPropertyName))) {
399         return v;
400     }
401     if ((v = tryGetAndCallProperty(exec, this, *secondPropertyName))) {
402         return v;
403     }
404 
405     if (exec->hadException()) {
406         return exec->exception();
407     }
408 
409     return throwError(exec, TypeError, "No default value");
410 }
411 
findPropertyHashEntry(const Identifier & propertyName) const412 const HashEntry *JSObject::findPropertyHashEntry(const Identifier &propertyName) const
413 {
414     for (const ClassInfo *info = classInfo(); info; info = info->parentClass) {
415         if (const HashTable *propHashTable = info->propHashTable) {
416             if (const HashEntry *e = Lookup::findEntry(propHashTable, propertyName)) {
417                 return e;
418             }
419         }
420     }
421     return nullptr;
422 }
423 
defineGetter(ExecState *,const Identifier & propertyName,JSObject * getterFunc)424 void JSObject::defineGetter(ExecState *, const Identifier &propertyName, JSObject *getterFunc)
425 {
426     JSValue *o = getDirect(propertyName);
427     GetterSetterImp *gs;
428 
429     if (o && JSValue::type(o) == GetterSetterType) {
430         gs = static_cast<GetterSetterImp *>(o);
431     } else {
432         gs = new GetterSetterImp;
433         putDirect(propertyName, gs, GetterSetter);
434     }
435 
436     _prop.setHasGetterSetterProperties(true);
437     gs->setGetter(getterFunc);
438 }
439 
defineSetter(ExecState *,const Identifier & propertyName,JSObject * setterFunc)440 void JSObject::defineSetter(ExecState *, const Identifier &propertyName, JSObject *setterFunc)
441 {
442     JSValue *o = getDirect(propertyName);
443     GetterSetterImp *gs;
444 
445     if (o && JSValue::type(o) == GetterSetterType) {
446         gs = static_cast<GetterSetterImp *>(o);
447     } else {
448         gs = new GetterSetterImp;
449         putDirect(propertyName, gs, GetterSetter);
450     }
451 
452     _prop.setHasGetterSetterProperties(true);
453     gs->setSetter(setterFunc);
454 }
455 
456 //ECMA Edition 5.1r6 - 8.12.9
defineOwnProperty(ExecState * exec,const Identifier & propertyName,PropertyDescriptor & desc,bool shouldThrow)457 bool JSObject::defineOwnProperty(ExecState *exec, const Identifier &propertyName, PropertyDescriptor &desc, bool shouldThrow)
458 {
459     PropertyDescriptor current;
460 
461     // if Object does not have propertyName as OwnProperty just push it.
462     if (!getOwnPropertyDescriptor(exec, propertyName, current)) {
463         if (!isExtensible()) {
464             if (shouldThrow) {
465                 throwError(exec, TypeError, "Object is not extensible \'" + propertyName.ustring() + "\'");
466             }
467             return false;
468         }
469         if (desc.isGenericDescriptor() || desc.isDataDescriptor()) {
470             putDirect(propertyName, desc.value() ? desc.value() : jsUndefined(), desc.attributes());
471         } else if (desc.isAccessorDescriptor()) {
472             GetterSetterImp *gs = new GetterSetterImp();
473             putDirect(propertyName, gs, desc.attributes() | GetterSetter);
474             _prop.setHasGetterSetterProperties(true);
475             if (desc.getter() && !JSValue::isUndefined(desc.getter())) {
476                 gs->setGetter(JSValue::toObject(desc.getter(), exec));
477             }
478             if (desc.setter() && !JSValue::isUndefined(desc.setter())) {
479                 gs->setSetter(JSValue::toObject(desc.setter(), exec));
480             }
481         }
482         return true;
483     }
484 
485     //Step 5
486     if (desc.isEmpty()) {
487         return true;
488     }
489 
490     //Step 6
491     if (desc.equalTo(exec, current)) {
492         return true;
493     }
494 
495     //Step 7
496     // Filter out invalid unconfigurable configurations
497     if (!current.configurable()) {
498         if (desc.configurable()) {
499             if (shouldThrow) {
500                 throwError(exec, TypeError, "can not redefine non-configurable property \'" + propertyName.ustring() + "\'");
501             }
502             return false;
503         }
504         if (desc.enumerableSet() && desc.enumerable() != current.enumerable()) {
505             if (shouldThrow) {
506                 throwError(exec, TypeError, "can not change enumerable attribute of unconfigurable property \'" + propertyName.ustring() + "\'");
507             }
508             return false;
509         }
510     }
511 
512     //Step 8.
513     if (!desc.isGenericDescriptor()) {
514         if (current.isDataDescriptor() != desc.isDataDescriptor()) { // Step 9
515             // DataDescriptor updating to AccessorDescriptor, or the other way.
516             if (!current.configurable()) {
517                 if (shouldThrow) {
518                     throwError(exec, TypeError, "can not change access mechanism for an unconfigurable property \'" + propertyName.ustring() + "\'");
519                 }
520                 return false;
521             }
522 
523             deleteProperty(exec, propertyName);
524 
525             if (current.isDataDescriptor()) {
526                 // Updating from DataDescriptor to AccessorDescriptor
527                 GetterSetterImp *gs = new GetterSetterImp();
528                 putDirect(propertyName, gs, current.attributesWithOverride(desc) | GetterSetter);
529                 _prop.setHasGetterSetterProperties(true);
530 
531                 if (desc.getter()) {
532                     if (JSValue::isUndefined(desc.getter())) {
533                         gs->setGetter(nullptr);
534                     } else {
535                         gs->setGetter(JSValue::toObject(desc.getter(), exec));
536                     }
537                 }
538                 if (desc.setter()) {
539                     if (JSValue::isUndefined(desc.setter())) {
540                         gs->setSetter(nullptr);
541                     } else {
542                         gs->setSetter(JSValue::toObject(desc.setter(), exec));
543                     }
544                 }
545             } else {
546                 // Updating from AccessorDescriptor to DataDescriptor
547                 unsigned int newAttr = current.attributesWithOverride(desc);
548                 if (!desc.writable()) {
549                     newAttr |= ReadOnly;
550                 }
551                 putDirect(propertyName, desc.value() ? desc.value() : jsUndefined(), newAttr);
552             }
553             return true;
554         } else if (current.isDataDescriptor() && desc.isDataDescriptor()) { //Step 10
555             // Just updating the value here
556             if (!current.configurable()) {
557                 if (!current.writable() && desc.writable()) {
558                     if (shouldThrow) {
559                         throwError(exec, TypeError, "can not change writable attribute of unconfigurable property \'" + propertyName.ustring() + "\'");
560                     }
561                     return false;
562                 }
563                 if (!current.writable()) {
564                     if (desc.value() && !(current.value() && sameValue(exec, current.value(), desc.value()))) {
565                         if (shouldThrow) {
566                             throwError(exec, TypeError, "can not change value of a readonly property \'" + propertyName.ustring() + "\'");
567                         }
568                         return false;
569                     }
570                 }
571             } else {
572                 if (!deleteProperty(exec, propertyName)) {
573                     removeDirect(propertyName);
574                 }
575 
576                 putDirect(propertyName, desc.value() ? desc.value() : current.value(), current.attributesWithOverride(desc));
577                 return true;
578             }
579         } else if (current.isAccessorDescriptor() && desc.isAccessorDescriptor()) { // Step 11
580             // Filter out unconfigurable combinations
581             if (!current.configurable()) {
582                 if (desc.setter() && !sameValue(exec, desc.setter(), current.setter() ? current.setter() : jsUndefined())) {
583                     if (shouldThrow) {
584                         throwError(exec, TypeError, "can not change the setter of an unconfigurable property \'" + propertyName.ustring() + "\'");
585                     }
586                     return false;
587                 }
588                 if (desc.getter() && !sameValue(exec, desc.getter(), current.getter() ? current.getter() : jsUndefined())) {
589                     if (shouldThrow) {
590                         throwError(exec, TypeError, "can not change the getter of an unconfigurable property \'" + propertyName.ustring() + "\'");
591                     }
592                     return false;
593                 }
594             }
595         }
596     }
597 
598     //Step 12
599     // Everything is allowed here, updating GetterSetter, storing new value
600     JSValue *jsval = getDirect(propertyName);
601     unsigned int newAttr = current.attributesWithOverride(desc);
602     if (jsval && JSValue::type(jsval) == GetterSetterType) {
603         GetterSetterImp *gs = static_cast<GetterSetterImp *>(jsval);
604         if (desc.getter()) {
605             if (JSValue::isUndefined(desc.getter())) {
606                 gs->setGetter(nullptr);
607             } else {
608                 gs->setGetter(JSValue::toObject(desc.getter(), exec));
609             }
610         }
611         if (desc.setter()) {
612             if (JSValue::isUndefined(desc.setter())) {
613                 gs->setSetter(nullptr);
614             } else {
615                 gs->setSetter(JSValue::toObject(desc.setter(), exec));
616             }
617         }
618     } else {
619         jsval = desc.value() ? desc.value() : current.value();
620     }
621 
622     deleteProperty(exec, propertyName);
623     if (JSValue::type(jsval) == GetterSetterType) {
624         putDirect(propertyName, jsval, newAttr | GetterSetter);
625         _prop.setHasGetterSetterProperties(true);
626     } else {
627         put(exec, propertyName, jsval, newAttr);
628     }
629 
630     return true; //Step 13
631 }
632 
preventExtensions()633 void JSObject::preventExtensions()
634 {
635     if (isExtensible()) {
636         _prop.setExtensible(false);
637     }
638 }
639 
implementsConstruct() const640 bool JSObject::implementsConstruct() const
641 {
642     return false;
643 }
644 
construct(ExecState *,const List &)645 JSObject *JSObject::construct(ExecState *, const List & /*args*/)
646 {
647     assert(false);
648     return nullptr;
649 }
650 
construct(ExecState * exec,const List & args,const Identifier &,const UString &,int)651 JSObject *JSObject::construct(ExecState *exec, const List &args, const Identifier & /*functionName*/, const UString & /*sourceURL*/, int /*lineNumber*/)
652 {
653     return construct(exec, args);
654 }
655 
valueClone(Interpreter *) const656 JSObject *JSObject::valueClone(Interpreter * /*targetCtx*/) const
657 {
658     return nullptr;
659 }
660 
isFunctionType() const661 bool JSObject::isFunctionType() const
662 {
663     return implementsCall();
664 }
665 
callAsFunction(ExecState *,JSObject *,const List &)666 JSValue *JSObject::callAsFunction(ExecState * /*exec*/, JSObject * /*thisObj*/, const List &/*args*/)
667 {
668     assert(false);
669     return nullptr;
670 }
671 
implementsHasInstance() const672 bool JSObject::implementsHasInstance() const
673 {
674     return false;
675 }
676 
hasInstance(ExecState * exec,JSValue * value)677 bool JSObject::hasInstance(ExecState *exec, JSValue *value)
678 {
679     JSValue *proto = get(exec, exec->propertyNames().prototype);
680     if (!JSValue::isObject(proto)) {
681         throwError(exec, TypeError, "instanceof called on an object with an invalid prototype property.");
682         return false;
683     }
684 
685     if (!JSValue::isObject(value)) {
686         return false;
687     }
688 
689     JSObject *o = static_cast<JSObject *>(value);
690     while ((o = JSValue::getObject(o->prototype()))) {
691         if (o == proto) {
692             return true;
693         }
694     }
695     return false;
696 }
697 
propertyIsEnumerable(ExecState *,const Identifier & propertyName) const698 bool JSObject::propertyIsEnumerable(ExecState *, const Identifier &propertyName) const
699 {
700     unsigned attributes;
701 
702     if (!getPropertyAttributes(propertyName, attributes)) {
703         return false;
704     } else {
705         return !(attributes & DontEnum);
706     }
707 }
708 
getPropertyAttributes(const Identifier & propertyName,unsigned & attributes) const709 bool JSObject::getPropertyAttributes(const Identifier &propertyName, unsigned &attributes) const
710 {
711     if (_prop.get(propertyName, attributes)) {
712         return true;
713     }
714 
715     // Look in the static hashtable of properties
716     const HashEntry *e = findPropertyHashEntry(propertyName);
717     if (e) {
718         attributes = e->attr;
719         return true;
720     }
721 
722     return false;
723 }
724 
getOwnPropertyNames(ExecState *,PropertyNameArray & propertyNames,PropertyMap::PropertyMode mode)725 void JSObject::getOwnPropertyNames(ExecState * /*exec*/, PropertyNameArray &propertyNames, PropertyMap::PropertyMode mode)
726 {
727     _prop.getPropertyNames(propertyNames, mode);
728 
729     // Add properties from the static hashtable of properties
730     const ClassInfo *info = classInfo();
731     while (info) {
732         if (info->propHashTable) {
733             int size = info->propHashTable->size;
734             const HashEntry *e = info->propHashTable->entries;
735             for (int i = 0; i < size; ++i, ++e) {
736                 if (e->s && PropertyMap::checkEnumerable(e->attr, mode)) {
737                     propertyNames.add(e->s);
738                 }
739             }
740         }
741         info = info->parentClass;
742     }
743 }
744 
toBoolean(ExecState *) const745 bool JSObject::toBoolean(ExecState * /*exec*/) const
746 {
747     return true;
748 }
749 
toNumber(ExecState * exec) const750 double JSObject::toNumber(ExecState *exec) const
751 {
752     JSValue *prim = toPrimitive(exec, NumberType);
753     if (exec->hadException()) { // should be picked up soon in nodes.cpp
754         return 0.0;
755     }
756     return JSValue::toNumber(prim, exec);
757 }
758 
toString(ExecState * exec) const759 UString JSObject::toString(ExecState *exec) const
760 {
761     JSValue *prim = toPrimitive(exec, StringType);
762     if (exec->hadException()) { // should be picked up soon in nodes.cpp
763         return UString(UString::empty);
764     }
765     return JSValue::toString(prim, exec);
766 }
767 
toObject(ExecState *) const768 JSObject *JSObject::toObject(ExecState * /*exec*/) const
769 {
770     return const_cast<JSObject *>(this);
771 }
772 
putDirect(const Identifier & propertyName,int value,int attr)773 void JSObject::putDirect(const Identifier &propertyName, int value, int attr)
774 {
775     _prop.put(propertyName, jsNumber(value), attr);
776 }
777 
removeDirect(const Identifier & propertyName)778 void JSObject::removeDirect(const Identifier &propertyName)
779 {
780     _prop.remove(propertyName);
781 }
782 
putDirectFunction(InternalFunctionImp * func,int attr)783 void JSObject::putDirectFunction(InternalFunctionImp *func, int attr)
784 {
785     putDirect(func->functionName(), func, attr);
786 }
787 
fillGetterPropertySlot(PropertySlot & slot,JSValue ** location)788 void JSObject::fillGetterPropertySlot(PropertySlot &slot, JSValue **location)
789 {
790     GetterSetterImp *gs = static_cast<GetterSetterImp *>(*location);
791     JSObject *getterFunc = gs->getGetter();
792     if (getterFunc) {
793         slot.setGetterSlot(this, getterFunc);
794     } else {
795         slot.setUndefined(this);
796     }
797 }
798 
799 // ------------------------------ Error ----------------------------------------
800 
801 const char *const errorNamesArr[] = {
802     I18N_NOOP("Error"), // GeneralError
803     I18N_NOOP("Evaluation error"), // EvalError
804     I18N_NOOP("Range error"), // RangeError
805     I18N_NOOP("Reference error"), // ReferenceError
806     I18N_NOOP("Syntax error"), // SyntaxError
807     I18N_NOOP("Type error"), // TypeError
808     I18N_NOOP("URI error"), // URIError
809 };
810 
811 const char *const *const Error::errorNames = errorNamesArr;
812 
create(ExecState * exec,ErrorType errtype,const UString & message,int lineno,int sourceId,const UString & sourceURL)813 JSObject *Error::create(ExecState *exec, ErrorType errtype, const UString &message,
814                         int lineno, int sourceId, const UString &sourceURL)
815 {
816 #ifdef KJS_VERBOSE
817     // message could be 0L. Don't enable this on Solaris ;)
818     fprintf(stderr, "WARNING: KJS %s: %s\n", errorNamesArr[errtype], message.ascii());
819 #endif
820 
821     Interpreter *interp = exec->lexicalInterpreter();
822     JSObject *cons;
823     switch (errtype) {
824     case EvalError:
825         cons = interp->builtinEvalError();
826         break;
827     case RangeError:
828         cons = interp->builtinRangeError();
829         break;
830     case ReferenceError:
831         cons = interp->builtinReferenceError();
832         break;
833     case SyntaxError:
834         cons = interp->builtinSyntaxError();
835         break;
836     case TypeError:
837         cons = interp->builtinTypeError();
838         break;
839     case URIError:
840         cons = interp->builtinURIError();
841         break;
842     default:
843         cons = interp->builtinError();
844         break;
845     }
846 
847     List args;
848     if (message.isEmpty()) {
849         args.append(jsString(errorNames[errtype]));
850     } else {
851         args.append(jsString(message));
852     }
853     JSObject *err = static_cast<JSObject *>(cons->construct(exec, args));
854 
855     if (lineno != -1) {
856         err->put(exec, "line", jsNumber(lineno));
857     }
858     if (sourceId != -1) {
859         err->put(exec, "sourceId", jsNumber(sourceId));
860     }
861 
862     if (!sourceURL.isNull()) {
863         err->put(exec, "sourceURL", jsString(sourceURL));
864     }
865 
866     return err;
867 
868     /*
869     #ifndef NDEBUG
870       const char *msg = err->get(messagePropertyName)->toString().value().ascii();
871       if (l >= 0)
872           fprintf(stderr, "KJS: %s at line %d. %s\n", estr, l, msg);
873       else
874           fprintf(stderr, "KJS: %s. %s\n", estr, msg);
875     #endif
876 
877       return err;
878     */
879 }
880 
create(ExecState * exec,ErrorType type,const char * message)881 JSObject *Error::create(ExecState *exec, ErrorType type, const char *message)
882 {
883     return create(exec, type, message, -1, -1, nullptr);
884 }
885 
throwError(ExecState * exec,ErrorType type)886 JSObject *throwError(ExecState *exec, ErrorType type)
887 {
888     JSObject *error = Error::create(exec, type, UString(), -1, -1, nullptr);
889     exec->setException(error);
890     return error;
891 }
892 
throwError(ExecState * exec,ErrorType type,const UString & message)893 JSObject *throwError(ExecState *exec, ErrorType type, const UString &message)
894 {
895     JSObject *error = Error::create(exec, type, message, -1, -1, nullptr);
896     exec->setException(error);
897     return error;
898 }
899 
throwError(ExecState * exec,ErrorType type,const char * message)900 JSObject *throwError(ExecState *exec, ErrorType type, const char *message)
901 {
902     JSObject *error = Error::create(exec, type, message, -1, -1, nullptr);
903     exec->setException(error);
904     return error;
905 }
906 
throwError(ExecState * exec,ErrorType type,const UString & message,int line,int sourceId,const UString & sourceURL)907 JSObject *throwError(ExecState *exec, ErrorType type, const UString &message, int line, int sourceId, const UString &sourceURL)
908 {
909     JSObject *error = Error::create(exec, type, message, line, sourceId, sourceURL);
910     exec->setException(error);
911     return error;
912 }
913 
914 } // namespace KJS
915