JavaScriptCore:
[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/PropertyNameArray.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 void UserObjectImp::getPropertyNames(ExecState *exec, PropertyNameArray& propertyNames)
126 {
127     JSUserObject* ptr = GetJSUserObject();
128     if (ptr) {
129         CFArrayRef cfPropertyNames = ptr->CopyPropertyNames();
130         if (cfPropertyNames) {
131             CFIndex count = CFArrayGetCount(cfPropertyNames);
132             CFIndex i;
133             for (i = 0; i < count; i++) {
134                 CFStringRef propertyName = (CFStringRef)CFArrayGetValueAtIndex(cfPropertyNames, i);
135                 propertyNames.add(CFStringToIdentifier(propertyName));
136             }
137             CFRelease(cfPropertyNames);
138         }
139     }
140     JSObject::getPropertyNames(exec, propertyNames);
141 }
142
143 JSValue *UserObjectImp::userObjectGetter(ExecState *, JSObject *, const Identifier& propertyName, const PropertySlot& slot)
144 {
145     UserObjectImp *thisObj = static_cast<UserObjectImp *>(slot.slotBase());
146     // getOwnPropertySlot should have guarded against a null fJSUserObject.
147     assert(thisObj->fJSUserObject);
148     
149     CFStringRef cfPropName = IdentifierToCFString(propertyName);
150     JSUserObject *jsResult = thisObj->fJSUserObject->CopyProperty(cfPropName);
151     ReleaseCFType(cfPropName);
152     JSValue *result = JSObjectKJSValue(jsResult);
153     jsResult->Release();
154
155     return result;
156 }
157
158 bool UserObjectImp::getOwnPropertySlot(ExecState *exec, const Identifier& propertyName, PropertySlot& slot)
159 {
160     if (!fJSUserObject)
161         return false;
162
163     CFStringRef cfPropName = IdentifierToCFString(propertyName);
164     JSUserObject *jsResult = fJSUserObject->CopyProperty(cfPropName);
165     ReleaseCFType(cfPropName);
166     if (jsResult) {
167         slot.setCustom(this, userObjectGetter);
168         jsResult->Release();
169         return true;
170     } else {
171         JSValue *kjsValue = toPrimitive(exec);
172         if (kjsValue->type() != NullType && kjsValue->type() != UndefinedType) {
173             JSObject *kjsObject = kjsValue->toObject(exec);
174             if (kjsObject->getPropertySlot(exec, propertyName, slot))
175                 return true;
176         }
177     }
178     return JSObject::getOwnPropertySlot(exec, propertyName, slot);
179 }
180
181 void UserObjectImp::put(ExecState *exec, const Identifier &propertyName, JSValue *value, int attr)
182 {
183     if (!fJSUserObject)
184         return;
185     
186     CFStringRef cfPropName = IdentifierToCFString(propertyName);
187     JSUserObject *jsValueObj = KJSValueToJSObject(value, exec);
188
189     fJSUserObject->SetProperty(cfPropName, jsValueObj);
190
191     if (jsValueObj) jsValueObj->Release();
192     ReleaseCFType(cfPropName);
193 }
194
195 JSUserObject* UserObjectImp::GetJSUserObject() const
196 {
197     return fJSUserObject;
198 }
199
200 JSValue *UserObjectImp::toPrimitive(ExecState *exec, JSType preferredType) const
201 {
202     JSValue *result = jsUndefined();
203     JSUserObject* jsObjPtr = KJSValueToJSObject(toObject(exec), exec);
204     CFTypeRef cfValue = jsObjPtr ? jsObjPtr->CopyCFValue() : 0;
205     if (cfValue) {
206         CFTypeID cfType = CFGetTypeID(cfValue);  // toPrimitive
207         if (cfValue == GetCFNull()) {
208             result = jsNull();
209         }
210         else if (cfType == CFBooleanGetTypeID()) {
211             if (cfValue == kCFBooleanTrue) {
212                 result = jsBoolean(true);
213             } else {
214                 result = jsBoolean(false);
215             }
216         } else if (cfType == CFStringGetTypeID()) {
217             result = jsString(CFStringToUString((CFStringRef)cfValue));
218         } else if (cfType == CFNumberGetTypeID()) {
219             double d = 0.0;
220             CFNumberGetValue((CFNumberRef)cfValue, kCFNumberDoubleType, &d);
221             result = jsNumber(d);
222         } else if (cfType == CFURLGetTypeID()) {
223             CFURLRef absURL = CFURLCopyAbsoluteURL((CFURLRef)cfValue);
224             if (absURL) {
225                 result = jsString(CFStringToUString(CFURLGetString(absURL)));
226                 ReleaseCFType(absURL);
227             }
228         }
229         ReleaseCFType(cfValue);
230     }
231     if (jsObjPtr)
232         jsObjPtr->Release();
233     return result;
234 }
235
236
237 bool UserObjectImp::toBoolean(ExecState *exec) const
238 {
239     bool result = false;
240     JSUserObject* jsObjPtr = KJSValueToJSObject(toObject(exec), exec);
241     CFTypeRef cfValue = jsObjPtr ? jsObjPtr->CopyCFValue() : 0;
242     if (cfValue)
243     {
244         CFTypeID cfType = CFGetTypeID(cfValue);  // toPrimitive
245         if (cfValue == GetCFNull())
246         {
247             //
248         }
249         else if (cfType == CFBooleanGetTypeID())
250         {
251             if (cfValue == kCFBooleanTrue)
252             {
253                 result = true;
254             }
255         }
256         else if (cfType == CFStringGetTypeID())
257         {
258             if (CFStringGetLength((CFStringRef)cfValue))
259             {
260                 result = true;
261             }
262         }
263         else if (cfType == CFNumberGetTypeID())
264         {
265             if (cfValue != kCFNumberNaN)
266             {
267                 double d;
268                 if (CFNumberGetValue((CFNumberRef)cfValue, kCFNumberDoubleType, &d))
269                 {
270                     if (d != 0)
271                     {
272                         result = true;
273                     }
274                 }
275             }
276         }
277         else if (cfType == CFArrayGetTypeID())
278         {
279             if (CFArrayGetCount((CFArrayRef)cfValue))
280             {
281                 result = true;
282             }
283         }
284         else if (cfType == CFDictionaryGetTypeID())
285         {
286             if (CFDictionaryGetCount((CFDictionaryRef)cfValue))
287             {
288                 result = true;
289             }
290         }
291         else if (cfType == CFSetGetTypeID())
292         {
293             if (CFSetGetCount((CFSetRef)cfValue))
294             {
295                 result = true;
296             }
297         }
298         else if (cfType == CFURLGetTypeID())
299         {
300             CFURLRef absURL = CFURLCopyAbsoluteURL((CFURLRef)cfValue);
301             if (absURL)
302             {
303                 CFStringRef cfStr = CFURLGetString(absURL);
304                 if (cfStr && CFStringGetLength(cfStr))
305                 {
306                     result = true;
307                 }
308                 ReleaseCFType(absURL);
309             }
310         }
311     }
312     if (jsObjPtr) jsObjPtr->Release();
313     ReleaseCFType(cfValue);
314     return result;
315 }
316
317 double UserObjectImp::toNumber(ExecState *exec) const
318 {
319     double result = 0;
320     JSUserObject* jsObjPtr = KJSValueToJSObject(toObject(exec), exec);
321     CFTypeRef cfValue = jsObjPtr ? jsObjPtr->CopyCFValue() : 0;
322     if (cfValue)
323     {
324         CFTypeID cfType = CFGetTypeID(cfValue);
325
326         if (cfValue == GetCFNull())
327         {
328             //
329         }
330         else if (cfType == CFBooleanGetTypeID())
331         {
332             if (cfValue == kCFBooleanTrue)
333             {
334                 result = 1;
335             }
336         }
337         else if (cfType == CFStringGetTypeID())
338         {
339             result = CFStringGetDoubleValue((CFStringRef)cfValue);
340         }
341         else if (cfType == CFNumberGetTypeID())
342         {
343             CFNumberGetValue((CFNumberRef)cfValue, kCFNumberDoubleType, &result);
344         }
345     }
346     ReleaseCFType(cfValue);
347     if (jsObjPtr) jsObjPtr->Release();
348     return result;
349 }
350
351 UString UserObjectImp::toString(ExecState *exec) const
352 {
353     UString result;
354     JSUserObject* jsObjPtr = KJSValueToJSObject(toObject(exec), exec);
355     CFTypeRef cfValue = jsObjPtr ? jsObjPtr->CopyCFValue() : 0;
356     if (cfValue)
357     {
358         CFTypeID cfType = CFGetTypeID(cfValue);
359         if (cfValue == GetCFNull())
360         {
361             //
362         }
363         else if (cfType == CFBooleanGetTypeID())
364         {
365             if (cfValue == kCFBooleanTrue)
366             {
367                 result = "true";
368             }
369             else
370             {
371                 result = "false";
372             }
373         }
374         else if (cfType == CFStringGetTypeID())
375         {
376             result = CFStringToUString((CFStringRef)cfValue);
377         }
378         else if (cfType == CFNumberGetTypeID())
379         {
380             if (cfValue == kCFNumberNaN)
381             {
382                 result = "Nan";
383             }
384             else if (CFNumberCompare(kCFNumberPositiveInfinity, (CFNumberRef)cfValue, 0) == 0)
385             {
386                 result = "Infinity";
387             }
388             else if (CFNumberCompare(kCFNumberNegativeInfinity, (CFNumberRef)cfValue, 0) == 0)
389             {
390                 result = "-Infinity";
391             }
392             else
393             {
394                 CFStringRef cfNumStr = 0;
395                 if (CFNumberIsFloatType((CFNumberRef)cfValue))
396                 {
397                     double d = 0;
398                     CFNumberGetValue((CFNumberRef)cfValue, kCFNumberDoubleType, &d);
399                     cfNumStr = CFStringCreateWithFormat(0, 0, CFSTR("%f"), (float)d);
400                 }
401                 else
402                 {
403                     int i = 0;
404                     CFNumberGetValue((CFNumberRef)cfValue, kCFNumberIntType, &i);
405                     cfNumStr = CFStringCreateWithFormat(0, 0, CFSTR("%d"), (int)i);
406                 }
407
408                 if (cfNumStr)
409                 {
410                     result = CFStringToUString(cfNumStr);
411                     ReleaseCFType(cfNumStr);
412                 }
413             }
414         }
415         else if (cfType == CFArrayGetTypeID())
416         {
417             //
418         }
419         else if (cfType == CFDictionaryGetTypeID())
420         {
421             //
422         }
423         else if (cfType == CFSetGetTypeID())
424         {
425             //
426         }
427         else if (cfType == CFURLGetTypeID())
428         {
429             CFURLRef absURL = CFURLCopyAbsoluteURL((CFURLRef)cfValue);
430             if (absURL)
431             {
432                 CFStringRef cfStr = CFURLGetString(absURL);
433                 if (cfStr)
434                 {
435                     result = CFStringToUString(cfStr);
436                 }
437                 ReleaseCFType(absURL);
438             }
439         }
440     }
441     ReleaseCFType(cfValue);
442     if (jsObjPtr) jsObjPtr->Release();
443     return result;
444 }
445
446 void UserObjectImp::mark()
447 {
448     JSObject::mark();
449     if (fJSUserObject)
450         fJSUserObject->Mark();
451 }