Fixed <rdar://problem/4053276> WebScripting protocol in WebKit cannot convert Boolea...
[WebKit-https.git] / JavaScriptCore / bindings / objc / objc_utility.mm
1 /*
2  * Copyright (C) 2004 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  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25 #include <Foundation/Foundation.h>
26
27 #include <JavascriptCore/internal.h>
28
29 #include <objc_instance.h>
30 #include <objc_utility.h>
31
32 #include <runtime_array.h>
33 #include <runtime_object.h>
34 #include <runtime_root.h>
35
36 #include <WebScriptObjectPrivate.h>
37
38
39 using namespace KJS;
40 using namespace KJS::Bindings;
41
42 /*
43     The default name concatenates the components of the
44     ObjectiveC selector name and replaces ':' with '_'.  '_' characters
45     are escaped with an additional '$', i.e. '_' becomes "$_".  '$' are
46     also escaped, i.e.
47         ObjectiveC name         Default script name
48         moveTo::                move__
49         moveTo_                 moveTo$_
50         moveTo$_                moveTo$$$_
51     @result Returns the name to be used to represent the specificed selector in the
52 */
53 void KJS::Bindings::JSMethodNameToObjCMethodName(const char *name, char *buffer, unsigned int len)
54 {
55     const char *np = name;
56     char *bp;
57
58     if (strlen(name)*2+1 > len){
59         *buffer = 0;
60     }
61
62     bp = buffer;
63     while (*np) {
64         if (*np == '$') {
65             np++;
66             *bp++ = *np++;
67             continue;
68         }
69         
70         if (*np == '_') {
71             np++;
72             *bp++ = ':';
73         }
74         else
75             *bp++ = *np++;
76     }
77     *bp++ = 0;
78 }
79
80 /*
81
82     JavaScript to   ObjC
83     Number          coerced to char, short, int, long, float, double, or NSNumber, as appropriate
84     String          NSString
85     wrapper         id
86     Object          WebScriptObject
87     [], other       exception
88
89 */
90 ObjcValue KJS::Bindings::convertValueToObjcValue (KJS::ExecState *exec, const KJS::Value &value, ObjcValueType type)
91 {
92     ObjcValue result;
93     double d = 0;
94    
95     if (value.type() == NumberType || value.type() == StringType || value.type() == BooleanType)
96         d = value.toNumber(exec);
97         
98     switch (type){
99         case ObjcObjectType: {
100             KJS::Interpreter *originInterpreter = exec->interpreter();
101             const Bindings::RootObject *originExecutionContext = rootForInterpreter(originInterpreter);
102
103             KJS::Interpreter *interpreter = 0;
104             if (originInterpreter->isGlobalObject(value)) {
105                 interpreter = originInterpreter->interpreterForGlobalObject (value.imp());
106             }
107
108             if (!interpreter)
109                 interpreter = originInterpreter;
110                 
111             const Bindings::RootObject *executionContext = rootForInterpreter(interpreter);
112             if (!executionContext) {
113                 Bindings::RootObject *newExecutionContext = new KJS::Bindings::RootObject(0);
114                 newExecutionContext->setInterpreter (interpreter);
115                 executionContext = newExecutionContext;
116             }
117             result.objectValue = [WebScriptObject _convertValueToObjcValue:value originExecutionContext:originExecutionContext executionContext:executionContext ];
118         }
119         break;
120         
121         
122         case ObjcCharType: {
123             result.charValue = (char)d;
124         }
125         break;
126
127         case ObjcShortType: {
128             result.shortValue = (short)d;
129         }
130         break;
131
132         case ObjcIntType: {
133             result.intValue = (int)d;
134         }
135         break;
136
137         case ObjcLongType: {
138             result.longValue = (long)d;
139         }
140         break;
141
142         case ObjcFloatType: {
143             result.floatValue = (float)d;
144         }
145         break;
146
147         case ObjcDoubleType: {
148             result.doubleValue = (double)d;
149         }
150         break;
151
152         case ObjcVoidType: {
153             bzero (&result, sizeof(ObjcValue));
154         }
155         break;
156
157         case ObjcInvalidType:
158         default:
159         {
160             // FIXME:  throw an exception
161         }
162         break;
163     }
164     return result;
165 }
166
167 Value KJS::Bindings::convertNSStringToString(NSString *nsstring)
168 {
169     unichar *chars;
170     unsigned int length = [nsstring length];
171     chars = (unichar *)malloc(sizeof(unichar)*length);
172     [nsstring getCharacters:chars];
173     UString u((const KJS::UChar*)chars, length);
174     Value aValue = String (u);
175     free((void *)chars);
176     return aValue;
177 }
178
179 /*
180
181     ObjC      to    JavaScript
182     char            Number
183     short
184     int
185     long
186     float
187     double
188     NSNumber        Number
189     NSString        String
190     NSArray         Array
191     id              Object wrapper
192     other           should not happen
193
194 */
195 Value KJS::Bindings::convertObjcValueToValue (KJS::ExecState *exec, void *buffer, ObjcValueType type)
196 {
197     Value aValue;
198
199     switch (type) {
200         case ObjcObjectType:
201             {
202                 ObjectStructPtr *obj = (ObjectStructPtr *)buffer;
203
204                 /*
205                     NSNumber to Number
206                     NSString to String
207                     NSArray  to Array
208                     id       to Object wrapper
209                 */
210                 if ([*obj isKindOfClass:[NSString class]]){
211                     NSString *string = (NSString *)*obj;
212                     aValue = convertNSStringToString (string);
213                 }
214                 else if (*obj == [WebUndefined undefined]) {
215                     return Undefined();
216                 }
217                 else if ((CFBooleanRef)*obj == kCFBooleanTrue) {
218                     aValue = Boolean(true);
219                 }
220                 else if ((CFBooleanRef)*obj == kCFBooleanFalse) {
221                     aValue = Boolean(false);
222                 }
223                 else if ([*obj isKindOfClass:[NSNumber class]]) {
224                     aValue = Number([*obj doubleValue]);
225                 }
226                 else if ([*obj isKindOfClass:[NSArray class]]) {
227                     aValue = Object(new RuntimeArrayImp(exec, new ObjcArray (*obj)));
228                 }
229                 else if ([*obj isKindOfClass:[WebScriptObject class]]) {
230                     WebScriptObject *jsobject = (WebScriptObject *)*obj;
231                     aValue = Object([jsobject _imp]);
232                 }
233                 else if (*obj == 0) {
234                     return Undefined();
235                 }
236                 else {
237                     aValue = Instance::createRuntimeObject(Instance::ObjectiveCLanguage, (void *)*obj);
238                 }
239             }
240             break;
241         case ObjcCharType:
242             {
243                 char *objcVal = (char *)buffer;
244                 aValue = Number ((short)*objcVal);
245             }
246             break;
247         case ObjcShortType:
248             {
249                 short *objcVal = (short *)buffer;
250                 aValue = Number ((short)*objcVal);
251             }
252             break;
253         case ObjcIntType:
254             {
255                 int *objcVal = (int *)buffer;
256                 aValue = Number ((int)*objcVal);
257             }
258             break;
259         case ObjcLongType:
260             {
261                 long *objcVal = (long *)buffer;
262                 aValue = Number ((long)*objcVal);
263             }
264             break;
265         case ObjcFloatType:
266             {
267                 float *objcVal = (float *)buffer;
268                 aValue = Number ((float)*objcVal);
269             }
270             break;
271         case ObjcDoubleType:
272             {
273                 double *objcVal = (double *)buffer;
274                 aValue = Number ((double)*objcVal);
275             }
276             break;
277         default:
278             // Should never get here.  Argument types are filtered (and
279             // the assert above should have fired in the impossible case
280             // of an invalid type anyway).
281             fprintf (stderr, "%s:  invalid type (%d)\n", __PRETTY_FUNCTION__, (int)type);
282             assert (true);
283     }
284     
285     return aValue;
286 }
287
288
289 ObjcValueType KJS::Bindings::objcValueTypeForType (const char *type)
290 {
291     int typeLength = strlen(type);
292     ObjcValueType objcValueType = ObjcInvalidType;
293     
294     if (typeLength == 1) {
295         char typeChar = type[0];
296         switch (typeChar){
297             case _C_ID: {
298                 objcValueType = ObjcObjectType;
299             }
300             break;
301             case _C_CHR: {
302                 objcValueType = ObjcCharType;
303             }
304             break;
305             case _C_SHT: {
306                 objcValueType = ObjcShortType;
307             }
308             break;
309             case _C_INT: {
310                 objcValueType = ObjcIntType;
311             }
312             break;
313             case _C_LNG: {
314                 objcValueType = ObjcLongType;
315             }
316             break;
317             case _C_FLT: {
318                 objcValueType = ObjcFloatType;
319             }
320             break;
321             case _C_DBL: {
322                 objcValueType = ObjcDoubleType;
323             }
324             break;
325             case _C_VOID: {
326                 objcValueType = ObjcVoidType;
327             }
328             break;
329         }
330     }
331     return objcValueType;
332 }
333
334
335 void *KJS::Bindings::createObjcInstanceForValue (const Object &value, const RootObject *origin, const RootObject *current)
336 {
337     if (value.type() != ObjectType)
338         return 0;
339
340     ObjectImp *imp = static_cast<ObjectImp*>(value.imp());
341     
342     return [[[WebScriptObject alloc] _initWithObjectImp:imp originExecutionContext:origin executionContext:current] autorelease];
343 }