source: webkit/trunk/JavaScriptCore/API/JSCallbackObject.cpp@ 15468

Last change on this file since 15468 was 15468, checked in by mjs, 19 years ago

JavaScriptCore:

Reviewed by Darin.


  • switch property lists to be vector+set of Identifiers instead of list of References


This has the following benefits:


  • no duplicates in property lists
  • simplifies API calls
  • probably more efficient, since linked list is gone
  • entirely removed Reference, ReferenceList and ProtectedReference types from the API
  • kjs/PropertyNameArray.cpp: Added. (KJS::PropertyNameArray::add): Check set, if not already there, add to vector.
  • kjs/PropertyNameArray.h: Added. (KJS::PropertyNameArray::PropertyNameArray): Newly added type, combines a set and a vector to make a unique but ordered list of identifiers. (KJS::PropertyNameArray::begin): ditto (KJS::PropertyNameArray::end): ditto (KJS::PropertyNameArray::size): ditto (KJS::PropertyNameArray::operator[]): ditto
  • kjs/array_instance.h:
  • kjs/array_object.cpp: (ArrayInstance::getPropertyNames): renamed from getPropertyList, updated for PropertyNameArray (ArrayInstance::setLength): updated for PropertyNameArray (ArrayInstance::pushUndefinedObjectsToEnd): ditto
  • kjs/nodes.cpp: (ForInNode::execute): updated for PropertyNameArray
  • kjs/nodes.h:
  • kjs/object.cpp: (KJS::JSObject::getPropertyNames): renamed from getPropertyList, updated for PropertyNameArray
  • kjs/object.h:
  • kjs/property_map.cpp: (KJS::PropertyMap::getEnumerablePropertyNames): updated for PropertyNameArray (KJS::PropertyMap::getSparseArrayPropertyNames): ditto
  • kjs/property_map.h:
  • kjs/protected_reference.h: Removed.
  • kjs/reference.cpp: Removed.
  • kjs/reference.h: Removed.
  • kjs/reference_list.cpp: Removed.
  • kjs/reference_list.h: Removed.
  • kjs/scope_chain.cpp: (KJS::ScopeChain::print): Use PropertyNamesArray instead of ReferenceList.
  • kjs/string_object.cpp: (StringInstance::getPropertyNames): Updated for new approach.
  • kjs/string_object.h:
  • kjs/ustring.h:
  • API/APICast.h: (toJS): Added overload for PropertyNameAccumulatorRef / PropertyNameArray* (toRef): ditto
  • API/JSBase.h:
  • API/JSCallbackObject.cpp: (KJS::JSCallbackObject::getPropertyNames): Fixed for new API.
  • API/JSCallbackObject.h:
  • API/JSObjectRef.cpp: (JSPropertyNameArray::JSPropertyNameArray): Type used for a publicly vended JSPropertyNameArrayRef. (JSObjectCopyPropertyNames): New API call - renamed / refactored from JSObjectCreatePropertyList (JSPropertyNameArrayRetain): new retain call for JSPropertyNameArray. (JSPropertyNameArrayRelease): new release call for - " -. (JSPropertyNameArrayGetCount): Instead of having to use a stateful enumerator you can now get the count and items in any order. (JSPropertyNameArrayGetNameAtIndex): See above. (JSPropertyNameAccumulatorAddName): What you add properties to is now an opaque accumulator object.
  • API/JSObjectRef.h: Prototyped new functions, removed old ones
  • JavaScriptCore.exp: Updated exported symbols.
  • JavaScriptCore.xcodeproj/project.pbxproj: Added new files, removed old.
  • API/testapi.c: (MyObject_getPropertyNames): Renamed / fixed callback to fit new paradigm. (main): Updated for new API.

JavaScriptGlue:

Reviewed by Darin.

  • switch property lists to be vector+set of Identifiers instead of list of References


  • JSUtils.cpp: (KJSValueToCFTypeInternal): updated for JSC SPI changes
  • JSValueWrapper.cpp: (JSValueWrapper::JSObjectCopyPropertyNames): ditto
  • UserObjectImp.cpp: (UserObjectImp::getPropertyNames): ditto
  • UserObjectImp.h:

LayoutTests:

Reviewed by Darin.


  • new test case and updated results for property list changes
  • fast/js/for-in-avoid-duplicates-expected.txt: Added.
  • fast/js/for-in-avoid-duplicates.html: Added.
  • fast/js/kde/Array-expected.txt:
  • fast/js/resources/for-in-avoid-duplicates.js: Added.
File size: 16.4 KB
Line 
1// -*- mode: c++; c-basic-offset: 4 -*-
2/*
3 * Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include "APICast.h"
28#include "JSCallbackObject.h"
29#include "JSStringRef.h"
30#include "JSClassRef.h"
31#include "JSObjectRef.h"
32#include "internal.h"
33#include "PropertyNameArray.h"
34
35namespace KJS {
36
37const ClassInfo JSCallbackObject::info = { "CallbackObject", 0, 0, 0 };
38
39JSCallbackObject::JSCallbackObject(JSContextRef context, JSClassRef jsClass)
40 : JSObject()
41{
42 init(context, jsClass);
43}
44
45JSCallbackObject::JSCallbackObject(JSContextRef context, JSClassRef jsClass, JSValue* prototype)
46 : JSObject(prototype)
47{
48 init(context, jsClass);
49}
50
51void JSCallbackObject::init(JSContextRef context, JSClassRef jsClass)
52{
53 ExecState* exec = toJS(context);
54
55 m_privateData = 0;
56 m_class = JSClassRetain(jsClass);
57
58 JSObjectRef thisRef = toRef(this);
59
60 do {
61 if (JSObjectInitializeCallback initialize = jsClass->initialize)
62 initialize(context, thisRef, toRef(exec->exceptionSlot()));
63 } while ((jsClass = jsClass->parentClass));
64}
65
66JSCallbackObject::~JSCallbackObject()
67{
68 JSObjectRef thisRef = toRef(this);
69
70 for (JSClassRef jsClass = m_class; jsClass; jsClass = jsClass->parentClass)
71 if (JSObjectFinalizeCallback finalize = jsClass->finalize)
72 finalize(thisRef);
73
74 JSClassRelease(m_class);
75}
76
77UString JSCallbackObject::className() const
78{
79 if (!m_class->className.isNull())
80 return m_class->className;
81
82 return JSObject::className();
83}
84
85bool JSCallbackObject::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
86{
87 JSContextRef context = toRef(exec);
88 JSObjectRef thisRef = toRef(this);
89 JSStringRef propertyNameRef = toRef(propertyName.ustring().rep());
90
91 for (JSClassRef jsClass = m_class; jsClass; jsClass = jsClass->parentClass) {
92 // optional optimization to bypass getProperty in cases when we only need to know if the property exists
93 if (JSObjectHasPropertyCallback hasProperty = jsClass->hasProperty) {
94 if (hasProperty(context, thisRef, propertyNameRef)) {
95 slot.setCustom(this, callbackGetter);
96 return true;
97 }
98 } else if (JSObjectGetPropertyCallback getProperty = jsClass->getProperty) {
99 if (JSValueRef value = getProperty(context, thisRef, propertyNameRef, toRef(exec->exceptionSlot()))) {
100 // cache the value so we don't have to compute it again
101 // FIXME: This violates the PropertySlot design a little bit.
102 // We should either use this optimization everywhere, or nowhere.
103 slot.setCustom(reinterpret_cast<JSObject*>(toJS(value)), cachedValueGetter);
104 return true;
105 }
106 }
107
108 if (__JSClass::StaticValuesTable* staticValues = jsClass->staticValues) {
109 if (StaticValueEntry* entry = staticValues->get(propertyName.ustring().rep())) {
110 if (entry->getProperty) {
111 slot.setCustom(this, staticValueGetter);
112 return true;
113 }
114 }
115 }
116
117 if (__JSClass::StaticFunctionsTable* staticFunctions = jsClass->staticFunctions) {
118 if (staticFunctions->contains(propertyName.ustring().rep())) {
119 slot.setCustom(this, staticFunctionGetter);
120 return true;
121 }
122 }
123 }
124
125 return JSObject::getOwnPropertySlot(exec, propertyName, slot);
126}
127
128bool JSCallbackObject::getOwnPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot)
129{
130 return getOwnPropertySlot(exec, Identifier::from(propertyName), slot);
131}
132
133void JSCallbackObject::put(ExecState* exec, const Identifier& propertyName, JSValue* value, int attr)
134{
135 JSContextRef context = toRef(exec);
136 JSObjectRef thisRef = toRef(this);
137 JSStringRef propertyNameRef = toRef(propertyName.ustring().rep());
138 JSValueRef valueRef = toRef(value);
139
140 for (JSClassRef jsClass = m_class; jsClass; jsClass = jsClass->parentClass) {
141 if (JSObjectSetPropertyCallback setProperty = jsClass->setProperty) {
142 if (setProperty(context, thisRef, propertyNameRef, valueRef, toRef(exec->exceptionSlot())))
143 return;
144 }
145
146 if (__JSClass::StaticValuesTable* staticValues = jsClass->staticValues) {
147 if (StaticValueEntry* entry = staticValues->get(propertyName.ustring().rep())) {
148 if (entry->attributes & kJSPropertyAttributeReadOnly)
149 return;
150 if (JSObjectSetPropertyCallback setProperty = entry->setProperty) {
151 if (setProperty(context, thisRef, propertyNameRef, valueRef, toRef(exec->exceptionSlot())))
152 return;
153 }
154 }
155 }
156
157 if (__JSClass::StaticFunctionsTable* staticFunctions = jsClass->staticFunctions) {
158 if (StaticFunctionEntry* entry = staticFunctions->get(propertyName.ustring().rep())) {
159 if (entry->attributes & kJSPropertyAttributeReadOnly)
160 return;
161 putDirect(propertyName, value, attr); // put as override property
162 return;
163 }
164 }
165 }
166
167 return JSObject::put(exec, propertyName, value, attr);
168}
169
170void JSCallbackObject::put(ExecState* exec, unsigned propertyName, JSValue* value, int attr)
171{
172 return put(exec, Identifier::from(propertyName), value, attr);
173}
174
175bool JSCallbackObject::deleteProperty(ExecState* exec, const Identifier& propertyName)
176{
177 JSContextRef context = toRef(exec);
178 JSObjectRef thisRef = toRef(this);
179 JSStringRef propertyNameRef = toRef(propertyName.ustring().rep());
180
181 for (JSClassRef jsClass = m_class; jsClass; jsClass = jsClass->parentClass) {
182 if (JSObjectDeletePropertyCallback deleteProperty = jsClass->deleteProperty) {
183 if (deleteProperty(context, thisRef, propertyNameRef, toRef(exec->exceptionSlot())))
184 return true;
185 }
186
187 if (__JSClass::StaticValuesTable* staticValues = jsClass->staticValues) {
188 if (StaticValueEntry* entry = staticValues->get(propertyName.ustring().rep())) {
189 if (entry->attributes & kJSPropertyAttributeDontDelete)
190 return false;
191 return true;
192 }
193 }
194
195 if (__JSClass::StaticFunctionsTable* staticFunctions = jsClass->staticFunctions) {
196 if (StaticFunctionEntry* entry = staticFunctions->get(propertyName.ustring().rep())) {
197 if (entry->attributes & kJSPropertyAttributeDontDelete)
198 return false;
199 return true;
200 }
201 }
202 }
203
204 return JSObject::deleteProperty(exec, propertyName);
205}
206
207bool JSCallbackObject::deleteProperty(ExecState* exec, unsigned propertyName)
208{
209 return deleteProperty(exec, Identifier::from(propertyName));
210}
211
212bool JSCallbackObject::implementsConstruct() const
213{
214 for (JSClassRef jsClass = m_class; jsClass; jsClass = jsClass->parentClass)
215 if (jsClass->callAsConstructor)
216 return true;
217
218 return false;
219}
220
221JSObject* JSCallbackObject::construct(ExecState* exec, const List& args)
222{
223 JSContextRef execRef = toRef(exec);
224 JSObjectRef thisRef = toRef(this);
225
226 for (JSClassRef jsClass = m_class; jsClass; jsClass = jsClass->parentClass) {
227 if (JSObjectCallAsConstructorCallback callAsConstructor = jsClass->callAsConstructor) {
228 size_t argumentCount = args.size();
229 JSValueRef arguments[argumentCount];
230 for (size_t i = 0; i < argumentCount; i++)
231 arguments[i] = toRef(args[i]);
232 return toJS(callAsConstructor(execRef, thisRef, argumentCount, arguments, toRef(exec->exceptionSlot())));
233 }
234 }
235
236 ASSERT(0); // implementsConstruct should prevent us from reaching here
237 return 0;
238}
239
240bool JSCallbackObject::implementsHasInstance() const
241{
242 for (JSClassRef jsClass = m_class; jsClass; jsClass = jsClass->parentClass)
243 if (jsClass->hasInstance)
244 return true;
245
246 return false;
247}
248
249bool JSCallbackObject::hasInstance(ExecState *exec, JSValue *value)
250{
251 JSContextRef execRef = toRef(exec);
252 JSObjectRef thisRef = toRef(this);
253
254 for (JSClassRef jsClass = m_class; jsClass; jsClass = jsClass->parentClass)
255 if (JSObjectHasInstanceCallback hasInstance = jsClass->hasInstance)
256 return hasInstance(execRef, thisRef, toRef(value), toRef(exec->exceptionSlot()));
257
258 ASSERT(0); // implementsHasInstance should prevent us from reaching here
259 return 0;
260}
261
262
263bool JSCallbackObject::implementsCall() const
264{
265 for (JSClassRef jsClass = m_class; jsClass; jsClass = jsClass->parentClass)
266 if (jsClass->callAsFunction)
267 return true;
268
269 return false;
270}
271
272JSValue* JSCallbackObject::callAsFunction(ExecState* exec, JSObject* thisObj, const List &args)
273{
274 JSContextRef execRef = toRef(exec);
275 JSObjectRef thisRef = toRef(this);
276 JSObjectRef thisObjRef = toRef(thisObj);
277
278 for (JSClassRef jsClass = m_class; jsClass; jsClass = jsClass->parentClass) {
279 if (JSObjectCallAsFunctionCallback callAsFunction = jsClass->callAsFunction) {
280 size_t argumentCount = args.size();
281 JSValueRef arguments[argumentCount];
282 for (size_t i = 0; i < argumentCount; i++)
283 arguments[i] = toRef(args[i]);
284 return toJS(callAsFunction(execRef, thisRef, thisObjRef, argumentCount, arguments, toRef(exec->exceptionSlot())));
285 }
286 }
287
288 ASSERT(0); // implementsCall should prevent us from reaching here
289 return 0;
290}
291
292void JSCallbackObject::getPropertyNames(ExecState* exec, PropertyNameArray& propertyNames)
293{
294 JSContextRef execRef = toRef(exec);
295 JSObjectRef thisRef = toRef(this);
296
297 for (JSClassRef jsClass = m_class; jsClass; jsClass = jsClass->parentClass) {
298 if (JSObjectGetPropertyNamesCallback getPropertyNames = jsClass->getPropertyNames)
299 getPropertyNames(execRef, thisRef, toRef(&propertyNames));
300
301 if (__JSClass::StaticValuesTable* staticValues = jsClass->staticValues) {
302 typedef __JSClass::StaticValuesTable::const_iterator iterator;
303 iterator end = staticValues->end();
304 for (iterator it = staticValues->begin(); it != end; ++it) {
305 UString::Rep* name = it->first.get();
306 StaticValueEntry* entry = it->second;
307 if (entry->getProperty && !(entry->attributes & kJSPropertyAttributeDontEnum))
308 propertyNames.add(Identifier(name));
309 }
310 }
311
312 if (__JSClass::StaticFunctionsTable* staticFunctions = jsClass->staticFunctions) {
313 typedef __JSClass::StaticFunctionsTable::const_iterator iterator;
314 iterator end = staticFunctions->end();
315 for (iterator it = staticFunctions->begin(); it != end; ++it) {
316 UString::Rep* name = it->first.get();
317 StaticFunctionEntry* entry = it->second;
318 if (!(entry->attributes & kJSPropertyAttributeDontEnum))
319 propertyNames.add(Identifier(name));
320 }
321 }
322 }
323
324 JSObject::getPropertyNames(exec, propertyNames);
325}
326
327double JSCallbackObject::toNumber(ExecState* exec) const
328{
329 JSContextRef context = toRef(exec);
330 JSObjectRef thisRef = toRef(this);
331
332 for (JSClassRef jsClass = m_class; jsClass; jsClass = jsClass->parentClass)
333 if (JSObjectConvertToTypeCallback convertToType = jsClass->convertToType)
334 if (JSValueRef value = convertToType(context, thisRef, kJSTypeNumber, toRef(exec->exceptionSlot())))
335 return toJS(value)->getNumber();
336
337 return JSObject::toNumber(exec);
338}
339
340UString JSCallbackObject::toString(ExecState* exec) const
341{
342 JSContextRef context = toRef(exec);
343 JSObjectRef thisRef = toRef(this);
344
345 for (JSClassRef jsClass = m_class; jsClass; jsClass = jsClass->parentClass)
346 if (JSObjectConvertToTypeCallback convertToType = jsClass->convertToType)
347 if (JSValueRef value = convertToType(context, thisRef, kJSTypeString, toRef(exec->exceptionSlot())))
348 return toJS(value)->getString();
349
350 return JSObject::toString(exec);
351}
352
353void JSCallbackObject::setPrivate(void* data)
354{
355 m_privateData = data;
356}
357
358void* JSCallbackObject::getPrivate()
359{
360 return m_privateData;
361}
362
363bool JSCallbackObject::inherits(JSClassRef c) const
364{
365 for (JSClassRef jsClass = m_class; jsClass; jsClass = jsClass->parentClass)
366 if (jsClass == c)
367 return true;
368
369 return false;
370}
371
372JSValue* JSCallbackObject::cachedValueGetter(ExecState*, JSObject*, const Identifier&, const PropertySlot& slot)
373{
374 JSValue* v = slot.slotBase();
375 ASSERT(v);
376 return v;
377}
378
379JSValue* JSCallbackObject::staticValueGetter(ExecState* exec, JSObject*, const Identifier& propertyName, const PropertySlot& slot)
380{
381 ASSERT(slot.slotBase()->inherits(&JSCallbackObject::info));
382 JSCallbackObject* thisObj = static_cast<JSCallbackObject*>(slot.slotBase());
383
384 JSObjectRef thisRef = toRef(thisObj);
385 JSStringRef propertyNameRef = toRef(propertyName.ustring().rep());
386
387 for (JSClassRef jsClass = thisObj->m_class; jsClass; jsClass = jsClass->parentClass)
388 if (__JSClass::StaticValuesTable* staticValues = jsClass->staticValues)
389 if (StaticValueEntry* entry = staticValues->get(propertyName.ustring().rep()))
390 if (JSObjectGetPropertyCallback getProperty = entry->getProperty)
391 if (JSValueRef value = getProperty(toRef(exec), thisRef, propertyNameRef, toRef(exec->exceptionSlot())))
392 return toJS(value);
393
394 return jsUndefined();
395}
396
397JSValue* JSCallbackObject::staticFunctionGetter(ExecState* exec, JSObject*, const Identifier& propertyName, const PropertySlot& slot)
398{
399 ASSERT(slot.slotBase()->inherits(&JSCallbackObject::info));
400 JSCallbackObject* thisObj = static_cast<JSCallbackObject*>(slot.slotBase());
401
402 if (JSValue* cachedOrOverrideValue = thisObj->getDirect(propertyName))
403 return cachedOrOverrideValue;
404
405 for (JSClassRef jsClass = thisObj->m_class; jsClass; jsClass = jsClass->parentClass) {
406 if (__JSClass::StaticFunctionsTable* staticFunctions = jsClass->staticFunctions) {
407 if (StaticFunctionEntry* entry = staticFunctions->get(propertyName.ustring().rep())) {
408 JSValue* v = toJS(JSObjectMakeFunction(toRef(exec), entry->callAsFunction));
409 thisObj->putDirect(propertyName, v, entry->attributes);
410 return v;
411 }
412 }
413 }
414
415 return jsUndefined();
416}
417
418JSValue* JSCallbackObject::callbackGetter(ExecState* exec, JSObject*, const Identifier& propertyName, const PropertySlot& slot)
419{
420 ASSERT(slot.slotBase()->inherits(&JSCallbackObject::info));
421 JSCallbackObject* thisObj = static_cast<JSCallbackObject*>(slot.slotBase());
422
423 JSObjectRef thisRef = toRef(thisObj);
424 JSStringRef propertyNameRef = toRef(propertyName.ustring().rep());
425
426 for (JSClassRef jsClass = thisObj->m_class; jsClass; jsClass = jsClass->parentClass)
427 if (JSObjectGetPropertyCallback getProperty = jsClass->getProperty)
428 if (JSValueRef value = getProperty(toRef(exec), thisRef, propertyNameRef, toRef(exec->exceptionSlot())))
429 return toJS(value);
430
431 return jsUndefined();
432}
433
434} // namespace KJS
Note: See TracBrowser for help on using the repository browser.