Reviewed by Maciej.
[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 #include "JavaScriptCore/reference_list.h"
32
33 const ClassInfo UserObjectImp::info = {"UserObject", 0, 0, 0};
34
35 class UserObjectPrototypeImp : public UserObjectImp {
36   public:
37     UserObjectPrototypeImp();
38     static UserObjectPrototypeImp* GlobalUserObjectPrototypeImp();
39   private:
40     static UserObjectPrototypeImp* sUserObjectPrototypeImp;
41 };
42
43 UserObjectPrototypeImp* UserObjectPrototypeImp::sUserObjectPrototypeImp = 0;
44
45 UserObjectPrototypeImp::UserObjectPrototypeImp()
46   : UserObjectImp()
47 {
48 }
49
50 UserObjectPrototypeImp* UserObjectPrototypeImp::GlobalUserObjectPrototypeImp()
51 {
52     if (!sUserObjectPrototypeImp)
53     {
54             sUserObjectPrototypeImp  = new UserObjectPrototypeImp();
55             static ProtectedPtr<UserObjectPrototypeImp> protectPrototype;
56     }
57     return sUserObjectPrototypeImp;
58 }
59
60
61 UserObjectImp::UserObjectImp(): JSObject(), fJSUserObject(0)
62 {
63 }
64
65 UserObjectImp::UserObjectImp(JSUserObject* userObject) :
66     JSObject(UserObjectPrototypeImp::GlobalUserObjectPrototypeImp()),
67     fJSUserObject((JSUserObject*)userObject->Retain())
68 {
69 }
70
71 UserObjectImp::~UserObjectImp()
72 {
73     if (fJSUserObject)
74         fJSUserObject->Release();
75 }
76
77 const ClassInfo * UserObjectImp::classInfo() const
78 {
79     return &info;
80 }
81
82 bool UserObjectImp::implementsCall() const
83 {
84     return fJSUserObject ? fJSUserObject->ImplementsCall() : false;
85 }
86
87 JSValue *UserObjectImp::callAsFunction(ExecState *exec, JSObject *thisObj, const List &args)
88 {
89     JSValue *result = jsUndefined();
90     JSUserObject* jsThisObj = KJSValueToJSObject(thisObj, exec);
91     if (jsThisObj) {
92         CFIndex argCount = args.size();
93         CFArrayCallBacks arrayCallBacks;
94         JSTypeGetCFArrayCallBacks(&arrayCallBacks);
95         CFMutableArrayRef jsArgs = CFArrayCreateMutable(0, 0, &arrayCallBacks);
96         if (jsArgs) {
97             for (CFIndex i = 0; i < argCount; i++) {
98                 JSUserObject* jsArg = KJSValueToJSObject(args[i], exec);
99                 CFArrayAppendValue(jsArgs, (void*)jsArg);
100                 jsArg->Release();
101             }
102         }
103
104         JSUserObject* jsResult;
105         { // scope
106             JSLock::DropAllLocks dropLocks;
107
108             // implementsCall should have guarded against a NULL fJSUserObject.
109             assert(fJSUserObject);
110             jsResult = fJSUserObject->CallFunction(jsThisObj, jsArgs);
111         }
112
113         if (jsResult) {
114             result = JSObjectKJSValue(jsResult);
115             jsResult->Release();
116         }
117
118         ReleaseCFType(jsArgs);
119         jsThisObj->Release();
120     }
121     return result;
122 }
123
124
125 ReferenceList UserObjectImp::propList(ExecState *exec, bool recursive)
126 {
127     ReferenceList list = JSObject::propList(exec, recursive);
128     JSUserObject* ptr = GetJSUserObject();
129     if (ptr) {
130         CFArrayRef cfPropertyNames = ptr->CopyPropertyNames();
131         if (cfPropertyNames) {
132             CFIndex count = CFArrayGetCount(cfPropertyNames);
133             CFIndex i;
134             for (i = 0; i < count; i++) {
135                 CFStringRef propertyName = (CFStringRef)CFArrayGetValueAtIndex(cfPropertyNames, i);
136                 list.append(Reference(this, CFStringToIdentifier(propertyName)));
137             }
138             CFRelease(cfPropertyNames);
139         }
140     }
141
142     return list;
143 }
144
145 JSValue *UserObjectImp::userObjectGetter(ExecState *, JSObject *, const Identifier& propertyName, const PropertySlot& slot)
146 {
147     UserObjectImp *thisObj = static_cast<UserObjectImp *>(slot.slotBase());
148     // getOwnPropertySlot should have guarded against a null fJSUserObject.
149     assert(thisObj->fJSUserObject);
150     
151     CFStringRef cfPropName = IdentifierToCFString(propertyName);
152     JSUserObject *jsResult = thisObj->fJSUserObject->CopyProperty(cfPropName);
153     ReleaseCFType(cfPropName);
154     JSValue *result = JSObjectKJSValue(jsResult);
155     jsResult->Release();
156
157     return result;
158 }
159
160 bool UserObjectImp::getOwnPropertySlot(ExecState *exec, const Identifier& propertyName, PropertySlot& slot)
161 {
162     if (!fJSUserObject)
163         return false;
164
165     CFStringRef cfPropName = IdentifierToCFString(propertyName);
166     JSUserObject *jsResult = fJSUserObject->CopyProperty(cfPropName);
167     ReleaseCFType(cfPropName);
168     if (jsResult) {
169         slot.setCustom(this, userObjectGetter);
170         jsResult->Release();
171         return true;
172     } else {
173         JSValue *kjsValue = toPrimitive(exec);
174         if (kjsValue->type() != NullType && kjsValue->type() != UndefinedType) {
175             JSObject *kjsObject = kjsValue->toObject(exec);
176             if (kjsObject->getPropertySlot(exec, propertyName, slot))
177                 return true;
178         }
179     }
180     return JSObject::getOwnPropertySlot(exec, propertyName, slot);
181 }
182
183 void UserObjectImp::put(ExecState *exec, const Identifier &propertyName, JSValue *value, int attr)
184 {
185     if (!fJSUserObject)
186         return;
187     
188     CFStringRef cfPropName = IdentifierToCFString(propertyName);
189     JSUserObject *jsValueObj = KJSValueToJSObject(value, exec);
190
191     fJSUserObject->SetProperty(cfPropName, jsValueObj);
192
193     if (jsValueObj) jsValueObj->Release();
194     ReleaseCFType(cfPropName);
195 }
196
197 JSUserObject* UserObjectImp::GetJSUserObject() const
198 {
199     return fJSUserObject;
200 }
201
202 JSValue *UserObjectImp::toPrimitive(ExecState *exec, JSType preferredType) const
203 {
204     JSValue *result = jsUndefined();
205     JSUserObject* jsObjPtr = KJSValueToJSObject(toObject(exec), exec);
206     CFTypeRef cfValue = jsObjPtr ? jsObjPtr->CopyCFValue() : 0;
207     if (cfValue) {
208         CFTypeID cfType = CFGetTypeID(cfValue);  // toPrimitive
209         if (cfValue == GetCFNull()) {
210             result = jsNull();
211         }
212         else if (cfType == CFBooleanGetTypeID()) {
213             if (cfValue == kCFBooleanTrue) {
214                 result = jsBoolean(true);
215             } else {
216                 result = jsBoolean(false);
217             }
218         } else if (cfType == CFStringGetTypeID()) {
219             result = jsString(CFStringToUString((CFStringRef)cfValue));
220         } else if (cfType == CFNumberGetTypeID()) {
221             double d = 0.0;
222             CFNumberGetValue((CFNumberRef)cfValue, kCFNumberDoubleType, &d);
223             result = jsNumber(d);
224         } else if (cfType == CFURLGetTypeID()) {
225             CFURLRef absURL = CFURLCopyAbsoluteURL((CFURLRef)cfValue);
226             if (absURL) {
227                 result = jsString(CFStringToUString(CFURLGetString(absURL)));
228                 ReleaseCFType(absURL);
229             }
230         }
231         ReleaseCFType(cfValue);
232     }
233     if (jsObjPtr)
234         jsObjPtr->Release();
235     return result;
236 }
237
238
239 bool UserObjectImp::toBoolean(ExecState *exec) const
240 {
241     bool result = false;
242     JSUserObject* jsObjPtr = KJSValueToJSObject(toObject(exec), exec);
243     CFTypeRef cfValue = jsObjPtr ? jsObjPtr->CopyCFValue() : 0;
244     if (cfValue)
245     {
246         CFTypeID cfType = CFGetTypeID(cfValue);  // toPrimitive
247         if (cfValue == GetCFNull())
248         {
249             //
250         }
251         else if (cfType == CFBooleanGetTypeID())
252         {
253             if (cfValue == kCFBooleanTrue)
254             {
255                 result = true;
256             }
257         }
258         else if (cfType == CFStringGetTypeID())
259         {
260             if (CFStringGetLength((CFStringRef)cfValue))
261             {
262                 result = true;
263             }
264         }
265         else if (cfType == CFNumberGetTypeID())
266         {
267             if (cfValue != kCFNumberNaN)
268             {
269                 double d;
270                 if (CFNumberGetValue((CFNumberRef)cfValue, kCFNumberDoubleType, &d))
271                 {
272                     if (d != 0)
273                     {
274                         result = true;
275                     }
276                 }
277             }
278         }
279         else if (cfType == CFArrayGetTypeID())
280         {
281             if (CFArrayGetCount((CFArrayRef)cfValue))
282             {
283                 result = true;
284             }
285         }
286         else if (cfType == CFDictionaryGetTypeID())
287         {
288             if (CFDictionaryGetCount((CFDictionaryRef)cfValue))
289             {
290                 result = true;
291             }
292         }
293         else if (cfType == CFSetGetTypeID())
294         {
295             if (CFSetGetCount((CFSetRef)cfValue))
296             {
297                 result = true;
298             }
299         }
300         else if (cfType == CFURLGetTypeID())
301         {
302             CFURLRef absURL = CFURLCopyAbsoluteURL((CFURLRef)cfValue);
303             if (absURL)
304             {
305                 CFStringRef cfStr = CFURLGetString(absURL);
306                 if (cfStr && CFStringGetLength(cfStr))
307                 {
308                     result = true;
309                 }
310                 ReleaseCFType(absURL);
311             }
312         }
313     }
314     if (jsObjPtr) jsObjPtr->Release();
315     ReleaseCFType(cfValue);
316     return result;
317 }
318
319 double UserObjectImp::toNumber(ExecState *exec) const
320 {
321     double result = 0;
322     JSUserObject* jsObjPtr = KJSValueToJSObject(toObject(exec), exec);
323     CFTypeRef cfValue = jsObjPtr ? jsObjPtr->CopyCFValue() : 0;
324     if (cfValue)
325     {
326         CFTypeID cfType = CFGetTypeID(cfValue);
327
328         if (cfValue == GetCFNull())
329         {
330             //
331         }
332         else if (cfType == CFBooleanGetTypeID())
333         {
334             if (cfValue == kCFBooleanTrue)
335             {
336                 result = 1;
337             }
338         }
339         else if (cfType == CFStringGetTypeID())
340         {
341             result = CFStringGetDoubleValue((CFStringRef)cfValue);
342         }
343         else if (cfType == CFNumberGetTypeID())
344         {
345             CFNumberGetValue((CFNumberRef)cfValue, kCFNumberDoubleType, &result);
346         }
347     }
348     ReleaseCFType(cfValue);
349     if (jsObjPtr) jsObjPtr->Release();
350     return result;
351 }
352
353 UString UserObjectImp::toString(ExecState *exec) const
354 {
355     UString result;
356     JSUserObject* jsObjPtr = KJSValueToJSObject(toObject(exec), exec);
357     CFTypeRef cfValue = jsObjPtr ? jsObjPtr->CopyCFValue() : 0;
358     if (cfValue)
359     {
360         CFTypeID cfType = CFGetTypeID(cfValue);
361         if (cfValue == GetCFNull())
362         {
363             //
364         }
365         else if (cfType == CFBooleanGetTypeID())
366         {
367             if (cfValue == kCFBooleanTrue)
368             {
369                 result = "true";
370             }
371             else
372             {
373                 result = "false";
374             }
375         }
376         else if (cfType == CFStringGetTypeID())
377         {
378             result = CFStringToUString((CFStringRef)cfValue);
379         }
380         else if (cfType == CFNumberGetTypeID())
381         {
382             if (cfValue == kCFNumberNaN)
383             {
384                 result = "Nan";
385             }
386             else if (CFNumberCompare(kCFNumberPositiveInfinity, (CFNumberRef)cfValue, 0) == 0)
387             {
388                 result = "Infinity";
389             }
390             else if (CFNumberCompare(kCFNumberNegativeInfinity, (CFNumberRef)cfValue, 0) == 0)
391             {
392                 result = "-Infinity";
393             }
394             else
395             {
396                 CFStringRef cfNumStr = 0;
397                 if (CFNumberIsFloatType((CFNumberRef)cfValue))
398                 {
399                     double d = 0;
400                     CFNumberGetValue((CFNumberRef)cfValue, kCFNumberDoubleType, &d);
401                     cfNumStr = CFStringCreateWithFormat(0, 0, CFSTR("%f"), (float)d);
402                 }
403                 else
404                 {
405                     int i = 0;
406                     CFNumberGetValue((CFNumberRef)cfValue, kCFNumberIntType, &i);
407                     cfNumStr = CFStringCreateWithFormat(0, 0, CFSTR("%d"), (int)i);
408                 }
409
410                 if (cfNumStr)
411                 {
412                     result = CFStringToUString(cfNumStr);
413                     ReleaseCFType(cfNumStr);
414                 }
415             }
416         }
417         else if (cfType == CFArrayGetTypeID())
418         {
419             //
420         }
421         else if (cfType == CFDictionaryGetTypeID())
422         {
423             //
424         }
425         else if (cfType == CFSetGetTypeID())
426         {
427             //
428         }
429         else if (cfType == CFURLGetTypeID())
430         {
431             CFURLRef absURL = CFURLCopyAbsoluteURL((CFURLRef)cfValue);
432             if (absURL)
433             {
434                 CFStringRef cfStr = CFURLGetString(absURL);
435                 if (cfStr)
436                 {
437                     result = CFStringToUString(cfStr);
438                 }
439                 ReleaseCFType(absURL);
440             }
441         }
442     }
443     ReleaseCFType(cfValue);
444     if (jsObjPtr) jsObjPtr->Release();
445     return result;
446 }
447
448 void UserObjectImp::mark()
449 {
450     JSObject::mark();
451     if (fJSUserObject)
452         fJSUserObject->Mark();
453 }