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