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