2008-08-22 Anders Carlsson <andersca@apple.com>
[WebKit-https.git] / JavaScriptGlue / UserObjectImp.cpp
1 /*
2  * Copyright (C) 2005 Apple Computer, Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
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  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14  *     its contributors may be used to endorse or promote products derived
15  *     from this software without specific prior written permission. 
16  *
17  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 #include "config.h"
30 #include "UserObjectImp.h"
31
32 #include <JavaScriptCore/JSString.h>
33 #include <JavaScriptCore/PropertyNameArray.h>
34
35 const ClassInfo UserObjectImp::info = { "UserObject", 0, 0, 0 };
36
37 UserObjectImp::UserObjectImp(JSUserObject* userObject)
38     : fJSUserObject((JSUserObject*)userObject->Retain())
39 {
40 }
41
42 UserObjectImp::~UserObjectImp()
43 {
44     if (fJSUserObject)
45         fJSUserObject->Release();
46 }
47
48 const ClassInfo * UserObjectImp::classInfo() const
49 {
50     return &info;
51 }
52
53 CallType UserObjectImp::getCallData(CallData& callData)
54 {
55     return fJSUserObject ? fJSUserObject->getCallData(callData) : CallTypeNone;
56 }
57
58 JSValue *UserObjectImp::callAsFunction(ExecState *exec, JSObject *thisObj, const ArgList &args)
59 {
60     JSValue *result = jsUndefined();
61     JSUserObject* jsThisObj = KJSValueToJSObject(thisObj, exec);
62     if (jsThisObj) {
63         CFIndex argCount = args.size();
64         CFArrayCallBacks arrayCallBacks;
65         JSTypeGetCFArrayCallBacks(&arrayCallBacks);
66         CFMutableArrayRef jsArgs = CFArrayCreateMutable(0, 0, &arrayCallBacks);
67         if (jsArgs) {
68             for (CFIndex i = 0; i < argCount; i++) {
69                 JSUserObject* jsArg = KJSValueToJSObject(args.at(exec, i), exec);
70                 CFArrayAppendValue(jsArgs, (void*)jsArg);
71                 jsArg->Release();
72             }
73         }
74
75         JSUserObject* jsResult;
76         { // scope
77             JSLock::DropAllLocks dropLocks(exec);
78
79             // getCallData should have guarded against a NULL fJSUserObject.
80             assert(fJSUserObject);
81             jsResult = fJSUserObject->CallFunction(jsThisObj, jsArgs);
82         }
83
84         if (jsResult) {
85             result = JSObjectKJSValue(jsResult);
86             jsResult->Release();
87         }
88
89         ReleaseCFType(jsArgs);
90         jsThisObj->Release();
91     }
92     return result;
93 }
94
95
96 void UserObjectImp::getPropertyNames(ExecState *exec, PropertyNameArray& propertyNames)
97 {
98     JSUserObject* ptr = GetJSUserObject();
99     if (ptr) {
100         CFArrayRef cfPropertyNames = ptr->CopyPropertyNames();
101         if (cfPropertyNames) {
102             CFIndex count = CFArrayGetCount(cfPropertyNames);
103             CFIndex i;
104             for (i = 0; i < count; i++) {
105                 CFStringRef propertyName = (CFStringRef)CFArrayGetValueAtIndex(cfPropertyNames, i);
106                 propertyNames.add(CFStringToIdentifier(propertyName, exec));
107             }
108             CFRelease(cfPropertyNames);
109         }
110     }
111     JSObject::getPropertyNames(exec, propertyNames);
112 }
113
114 JSValue* UserObjectImp::userObjectGetter(ExecState*, const Identifier& propertyName, const PropertySlot& slot)
115 {
116     UserObjectImp *thisObj = static_cast<UserObjectImp *>(slot.slotBase());
117     // getOwnPropertySlot should have guarded against a null fJSUserObject.
118     assert(thisObj->fJSUserObject);
119     
120     CFStringRef cfPropName = IdentifierToCFString(propertyName);
121     JSUserObject *jsResult = thisObj->fJSUserObject->CopyProperty(cfPropName);
122     ReleaseCFType(cfPropName);
123     JSValue *result = JSObjectKJSValue(jsResult);
124     jsResult->Release();
125
126     return result;
127 }
128
129 bool UserObjectImp::getOwnPropertySlot(ExecState *exec, const Identifier& propertyName, PropertySlot& slot)
130 {
131     if (!fJSUserObject)
132         return false;
133
134     CFStringRef cfPropName = IdentifierToCFString(propertyName);
135     JSUserObject *jsResult = fJSUserObject->CopyProperty(cfPropName);
136     ReleaseCFType(cfPropName);
137     if (jsResult) {
138         slot.setCustom(this, userObjectGetter);
139         jsResult->Release();
140         return true;
141     } else {
142         JSValue *kjsValue = toPrimitive(exec);
143         if (!kjsValue->isUndefinedOrNull()) {
144             JSObject *kjsObject = kjsValue->toObject(exec);
145             if (kjsObject->getPropertySlot(exec, propertyName, slot))
146                 return true;
147         }
148     }
149     return JSObject::getOwnPropertySlot(exec, propertyName, slot);
150 }
151
152 void UserObjectImp::put(ExecState *exec, const Identifier &propertyName, JSValue *value, int attr)
153 {
154     if (!fJSUserObject)
155         return;
156     
157     CFStringRef cfPropName = IdentifierToCFString(propertyName);
158     JSUserObject *jsValueObj = KJSValueToJSObject(value, exec);
159
160     fJSUserObject->SetProperty(cfPropName, jsValueObj);
161
162     if (jsValueObj) jsValueObj->Release();
163     ReleaseCFType(cfPropName);
164 }
165
166 JSUserObject* UserObjectImp::GetJSUserObject() const
167 {
168     return fJSUserObject;
169 }
170
171 JSValue *UserObjectImp::toPrimitive(ExecState *exec, JSType) const
172 {
173     JSValue *result = jsUndefined();
174     JSUserObject* jsObjPtr = KJSValueToJSObject(toObject(exec), exec);
175     CFTypeRef cfValue = jsObjPtr ? jsObjPtr->CopyCFValue() : 0;
176     if (cfValue) {
177         CFTypeID cfType = CFGetTypeID(cfValue);  // toPrimitive
178         if (cfValue == GetCFNull()) {
179             result = jsNull();
180         }
181         else if (cfType == CFBooleanGetTypeID()) {
182             if (cfValue == kCFBooleanTrue) {
183                 result = jsBoolean(true);
184             } else {
185                 result = jsBoolean(false);
186             }
187         } else if (cfType == CFStringGetTypeID()) {
188             result = jsString(exec, CFStringToUString((CFStringRef)cfValue));
189         } else if (cfType == CFNumberGetTypeID()) {
190             double d = 0.0;
191             CFNumberGetValue((CFNumberRef)cfValue, kCFNumberDoubleType, &d);
192             result = jsNumber(exec, d);
193         } else if (cfType == CFURLGetTypeID()) {
194             CFURLRef absURL = CFURLCopyAbsoluteURL((CFURLRef)cfValue);
195             if (absURL) {
196                 result = jsString(exec, CFStringToUString(CFURLGetString(absURL)));
197                 ReleaseCFType(absURL);
198             }
199         }
200         ReleaseCFType(cfValue);
201     }
202     if (jsObjPtr)
203         jsObjPtr->Release();
204     return result;
205 }
206
207
208 bool UserObjectImp::toBoolean(ExecState *exec) const
209 {
210     bool result = false;
211     JSUserObject* jsObjPtr = KJSValueToJSObject(toObject(exec), exec);
212     CFTypeRef cfValue = jsObjPtr ? jsObjPtr->CopyCFValue() : 0;
213     if (cfValue)
214     {
215         CFTypeID cfType = CFGetTypeID(cfValue);  // toPrimitive
216         if (cfValue == GetCFNull())
217         {
218             //
219         }
220         else if (cfType == CFBooleanGetTypeID())
221         {
222             if (cfValue == kCFBooleanTrue)
223             {
224                 result = true;
225             }
226         }
227         else if (cfType == CFStringGetTypeID())
228         {
229             if (CFStringGetLength((CFStringRef)cfValue))
230             {
231                 result = true;
232             }
233         }
234         else if (cfType == CFNumberGetTypeID())
235         {
236             if (cfValue != kCFNumberNaN)
237             {
238                 double d;
239                 if (CFNumberGetValue((CFNumberRef)cfValue, kCFNumberDoubleType, &d))
240                 {
241                     if (d != 0)
242                     {
243                         result = true;
244                     }
245                 }
246             }
247         }
248         else if (cfType == CFArrayGetTypeID())
249         {
250             if (CFArrayGetCount((CFArrayRef)cfValue))
251             {
252                 result = true;
253             }
254         }
255         else if (cfType == CFDictionaryGetTypeID())
256         {
257             if (CFDictionaryGetCount((CFDictionaryRef)cfValue))
258             {
259                 result = true;
260             }
261         }
262         else if (cfType == CFSetGetTypeID())
263         {
264             if (CFSetGetCount((CFSetRef)cfValue))
265             {
266                 result = true;
267             }
268         }
269         else if (cfType == CFURLGetTypeID())
270         {
271             CFURLRef absURL = CFURLCopyAbsoluteURL((CFURLRef)cfValue);
272             if (absURL)
273             {
274                 CFStringRef cfStr = CFURLGetString(absURL);
275                 if (cfStr && CFStringGetLength(cfStr))
276                 {
277                     result = true;
278                 }
279                 ReleaseCFType(absURL);
280             }
281         }
282     }
283     if (jsObjPtr) jsObjPtr->Release();
284     ReleaseCFType(cfValue);
285     return result;
286 }
287
288 double UserObjectImp::toNumber(ExecState *exec) const
289 {
290     double result = 0;
291     JSUserObject* jsObjPtr = KJSValueToJSObject(toObject(exec), exec);
292     CFTypeRef cfValue = jsObjPtr ? jsObjPtr->CopyCFValue() : 0;
293     if (cfValue)
294     {
295         CFTypeID cfType = CFGetTypeID(cfValue);
296
297         if (cfValue == GetCFNull())
298         {
299             //
300         }
301         else if (cfType == CFBooleanGetTypeID())
302         {
303             if (cfValue == kCFBooleanTrue)
304             {
305                 result = 1;
306             }
307         }
308         else if (cfType == CFStringGetTypeID())
309         {
310             result = CFStringGetDoubleValue((CFStringRef)cfValue);
311         }
312         else if (cfType == CFNumberGetTypeID())
313         {
314             CFNumberGetValue((CFNumberRef)cfValue, kCFNumberDoubleType, &result);
315         }
316     }
317     ReleaseCFType(cfValue);
318     if (jsObjPtr) jsObjPtr->Release();
319     return result;
320 }
321
322 UString UserObjectImp::toString(ExecState *exec) const
323 {
324     UString result;
325     JSUserObject* jsObjPtr = KJSValueToJSObject(toObject(exec), exec);
326     CFTypeRef cfValue = jsObjPtr ? jsObjPtr->CopyCFValue() : 0;
327     if (cfValue)
328     {
329         CFTypeID cfType = CFGetTypeID(cfValue);
330         if (cfValue == GetCFNull())
331         {
332             //
333         }
334         else if (cfType == CFBooleanGetTypeID())
335         {
336             if (cfValue == kCFBooleanTrue)
337             {
338                 result = "true";
339             }
340             else
341             {
342                 result = "false";
343             }
344         }
345         else if (cfType == CFStringGetTypeID())
346         {
347             result = CFStringToUString((CFStringRef)cfValue);
348         }
349         else if (cfType == CFNumberGetTypeID())
350         {
351             if (cfValue == kCFNumberNaN)
352             {
353                 result = "Nan";
354             }
355             else if (CFNumberCompare(kCFNumberPositiveInfinity, (CFNumberRef)cfValue, 0) == 0)
356             {
357                 result = "Infinity";
358             }
359             else if (CFNumberCompare(kCFNumberNegativeInfinity, (CFNumberRef)cfValue, 0) == 0)
360             {
361                 result = "-Infinity";
362             }
363             else
364             {
365                 CFStringRef cfNumStr;
366                 double d = 0;
367                 CFNumberGetValue((CFNumberRef)cfValue, kCFNumberDoubleType, &d);
368                 if (CFNumberIsFloatType((CFNumberRef)cfValue))
369                 {
370                     cfNumStr = CFStringCreateWithFormat(0, 0, CFSTR("%f"), d);
371                 }
372                 else
373                 {
374                     cfNumStr = CFStringCreateWithFormat(0, 0, CFSTR("%.0f"), d);
375                 }
376                 result = CFStringToUString(cfNumStr);
377                 ReleaseCFType(cfNumStr);
378             }
379         }
380         else if (cfType == CFArrayGetTypeID())
381         {
382             //
383         }
384         else if (cfType == CFDictionaryGetTypeID())
385         {
386             //
387         }
388         else if (cfType == CFSetGetTypeID())
389         {
390             //
391         }
392         else if (cfType == CFURLGetTypeID())
393         {
394             CFURLRef absURL = CFURLCopyAbsoluteURL((CFURLRef)cfValue);
395             if (absURL)
396             {
397                 CFStringRef cfStr = CFURLGetString(absURL);
398                 if (cfStr)
399                 {
400                     result = CFStringToUString(cfStr);
401                 }
402                 ReleaseCFType(absURL);
403             }
404         }
405     }
406     ReleaseCFType(cfValue);
407     if (jsObjPtr) jsObjPtr->Release();
408     return result;
409 }
410
411 void UserObjectImp::mark()
412 {
413     JSObject::mark();
414     if (fJSUserObject)
415         fJSUserObject->Mark();
416 }