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