Home | History | Annotate | Download | only in objc
      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 
     26 #include "config.h"
     27 #include "objc_utility.h"
     28 
     29 #include "objc_instance.h"
     30 #include "runtime_array.h"
     31 #include "runtime_object.h"
     32 #include "WebScriptObject.h"
     33 #include <runtime/JSGlobalObject.h>
     34 #include <runtime/JSLock.h>
     35 #include <wtf/Assertions.h>
     36 
     37 #if !defined(_C_LNG_LNG)
     38 #define _C_LNG_LNG 'q'
     39 #endif
     40 
     41 #if !defined(_C_ULNG_LNG)
     42 #define _C_ULNG_LNG 'Q'
     43 #endif
     44 
     45 #if !defined(_C_CONST)
     46 #define _C_CONST 'r'
     47 #endif
     48 
     49 #if !defined(_C_BYCOPY)
     50 #define _C_BYCOPY 'O'
     51 #endif
     52 
     53 #if !defined(_C_BYREF)
     54 #define _C_BYREF 'R'
     55 #endif
     56 
     57 #if !defined(_C_ONEWAY)
     58 #define _C_ONEWAY 'V'
     59 #endif
     60 
     61 #if !defined(_C_GCINVISIBLE)
     62 #define _C_GCINVISIBLE '!'
     63 #endif
     64 
     65 namespace JSC {
     66 namespace Bindings {
     67 
     68 /*
     69     By default, a JavaScript method name is produced by concatenating the
     70     components of an ObjectiveC method name, replacing ':' with '_', and
     71     escaping '_' and '$' with a leading '$', such that '_' becomes "$_" and
     72     '$' becomes "$$". For example:
     73 
     74     ObjectiveC name         Default JavaScript name
     75         moveTo::                moveTo__
     76         moveTo_                 moveTo$_
     77         moveTo$_                moveTo$$$_
     78 
     79     This function performs the inverse of that operation.
     80 
     81     @result Fills 'buffer' with the ObjectiveC method name that corresponds to 'JSName'.
     82             Returns true for success, false for failure. (Failure occurs when 'buffer'
     83             is not big enough to hold the result.)
     84 */
     85 bool convertJSMethodNameToObjc(const char *JSName, char *buffer, size_t bufferSize)
     86 {
     87     ASSERT(JSName && buffer);
     88 
     89     const char *sp = JSName; // source pointer
     90     char *dp = buffer; // destination pointer
     91 
     92     char *end = buffer + bufferSize;
     93     while (dp < end) {
     94         if (*sp == '$') {
     95             ++sp;
     96             *dp = *sp;
     97         } else if (*sp == '_')
     98             *dp = ':';
     99         else
    100             *dp = *sp;
    101 
    102         // If a future coder puts funny ++ operators above, we might write off the end
    103         // of the buffer in the middle of this loop. Let's make sure to check for that.
    104         ASSERT(dp < end);
    105 
    106         if (*sp == 0) { // We finished converting JSName
    107             ASSERT(strlen(JSName) < bufferSize);
    108             return true;
    109         }
    110 
    111         ++sp;
    112         ++dp;
    113     }
    114 
    115     return false; // We ran out of buffer before converting JSName
    116 }
    117 
    118 /*
    119 
    120     JavaScript to   ObjC
    121     Number          coerced to char, short, int, long, float, double, or NSNumber, as appropriate
    122     String          NSString
    123     wrapper         id
    124     Object          WebScriptObject
    125     null            NSNull
    126     [], other       exception
    127 
    128 */
    129 ObjcValue convertValueToObjcValue(ExecState* exec, JSValue value, ObjcValueType type)
    130 {
    131     ObjcValue result;
    132     double d = 0;
    133 
    134     if (value.isNumber() || value.isString() || value.isBoolean())
    135         d = value.toNumber(exec);
    136 
    137     switch (type) {
    138         case ObjcObjectType: {
    139             JSLock lock(SilenceAssertionsOnly);
    140 
    141             JSGlobalObject *originGlobalObject = exec->dynamicGlobalObject();
    142             RootObject* originRootObject = findRootObject(originGlobalObject);
    143 
    144             JSGlobalObject* globalObject = 0;
    145             if (value.isObject() && asObject(value)->isGlobalObject())
    146                 globalObject = static_cast<JSGlobalObject*>(asObject(value));
    147 
    148             if (!globalObject)
    149                 globalObject = originGlobalObject;
    150 
    151             RootObject* rootObject = findRootObject(globalObject);
    152             result.objectValue =  rootObject
    153                 ? [webScriptObjectClass() _convertValueToObjcValue:value originRootObject:originRootObject rootObject:rootObject]
    154                 : nil;
    155         }
    156         break;
    157 
    158         case ObjcCharType:
    159         case ObjcUnsignedCharType:
    160             result.charValue = (char)d;
    161             break;
    162         case ObjcShortType:
    163         case ObjcUnsignedShortType:
    164             result.shortValue = (short)d;
    165             break;
    166         case ObjcIntType:
    167         case ObjcUnsignedIntType:
    168             result.intValue = (int)d;
    169             break;
    170         case ObjcLongType:
    171         case ObjcUnsignedLongType:
    172             result.longValue = (long)d;
    173             break;
    174         case ObjcLongLongType:
    175         case ObjcUnsignedLongLongType:
    176             result.longLongValue = (long long)d;
    177             break;
    178         case ObjcFloatType:
    179             result.floatValue = (float)d;
    180             break;
    181         case ObjcDoubleType:
    182             result.doubleValue = (double)d;
    183             break;
    184         case ObjcVoidType:
    185             bzero(&result, sizeof(ObjcValue));
    186             break;
    187 
    188         case ObjcInvalidType:
    189         default:
    190             // FIXME: throw an exception?
    191             break;
    192     }
    193 
    194     return result;
    195 }
    196 
    197 JSValue convertNSStringToString(ExecState* exec, NSString *nsstring)
    198 {
    199     JSLock lock(SilenceAssertionsOnly);
    200 
    201     unichar *chars;
    202     unsigned int length = [nsstring length];
    203     chars = (unichar *)malloc(sizeof(unichar)*length);
    204     [nsstring getCharacters:chars];
    205     UString u((const UChar*)chars, length);
    206     JSValue aValue = jsString(exec, u);
    207     free((void *)chars);
    208     return aValue;
    209 }
    210 
    211 /*
    212     ObjC      to    JavaScript
    213     ----            ----------
    214     char            number
    215     short           number
    216     int             number
    217     long            number
    218     float           number
    219     double          number
    220     NSNumber        boolean or number
    221     NSString        string
    222     NSArray         array
    223     NSNull          null
    224     WebScriptObject underlying JavaScript object
    225     WebUndefined    undefined
    226     id              object wrapper
    227     other           should not happen
    228 */
    229 JSValue convertObjcValueToValue(ExecState* exec, void* buffer, ObjcValueType type, RootObject* rootObject)
    230 {
    231     JSLock lock(SilenceAssertionsOnly);
    232 
    233     switch (type) {
    234         case ObjcObjectType: {
    235             id obj = *(id*)buffer;
    236             if ([obj isKindOfClass:[NSString class]])
    237                 return convertNSStringToString(exec, (NSString *)obj);
    238             if ([obj isKindOfClass:webUndefinedClass()])
    239                 return jsUndefined();
    240             if ((CFBooleanRef)obj == kCFBooleanTrue)
    241                 return jsBoolean(true);
    242             if ((CFBooleanRef)obj == kCFBooleanFalse)
    243                 return jsBoolean(false);
    244             if ([obj isKindOfClass:[NSNumber class]])
    245                 return jsNumber(exec, [obj doubleValue]);
    246             if ([obj isKindOfClass:[NSArray class]])
    247                 return new (exec) RuntimeArray(exec, new ObjcArray(obj, rootObject));
    248             if ([obj isKindOfClass:webScriptObjectClass()]) {
    249                 JSObject* imp = [obj _imp];
    250                 return imp ? imp : jsUndefined();
    251             }
    252             if ([obj isKindOfClass:[NSNull class]])
    253                 return jsNull();
    254             if (obj == 0)
    255                 return jsUndefined();
    256             return ObjcInstance::create(obj, rootObject)->createRuntimeObject(exec);
    257         }
    258         case ObjcCharType:
    259             return jsNumber(exec, *(char*)buffer);
    260         case ObjcUnsignedCharType:
    261             return jsNumber(exec, *(unsigned char*)buffer);
    262         case ObjcShortType:
    263             return jsNumber(exec, *(short*)buffer);
    264         case ObjcUnsignedShortType:
    265             return jsNumber(exec, *(unsigned short*)buffer);
    266         case ObjcIntType:
    267             return jsNumber(exec, *(int*)buffer);
    268         case ObjcUnsignedIntType:
    269             return jsNumber(exec, *(unsigned int*)buffer);
    270         case ObjcLongType:
    271             return jsNumber(exec, *(long*)buffer);
    272         case ObjcUnsignedLongType:
    273             return jsNumber(exec, *(unsigned long*)buffer);
    274         case ObjcLongLongType:
    275             return jsNumber(exec, *(long long*)buffer);
    276         case ObjcUnsignedLongLongType:
    277             return jsNumber(exec, *(unsigned long long*)buffer);
    278         case ObjcFloatType:
    279             return jsNumber(exec, *(float*)buffer);
    280         case ObjcDoubleType:
    281             return jsNumber(exec, *(double*)buffer);
    282         default:
    283             // Should never get here. Argument types are filtered.
    284             fprintf(stderr, "%s: invalid type (%d)\n", __PRETTY_FUNCTION__, (int)type);
    285             ASSERT(false);
    286     }
    287 
    288     return jsUndefined();
    289 }
    290 
    291 ObjcValueType objcValueTypeForType(const char *type)
    292 {
    293     int typeLength = strlen(type);
    294     ObjcValueType objcValueType = ObjcInvalidType;
    295 
    296     for (int i = 0; i < typeLength; ++i) {
    297         char typeChar = type[i];
    298         switch (typeChar) {
    299             case _C_CONST:
    300             case _C_BYCOPY:
    301             case _C_BYREF:
    302             case _C_ONEWAY:
    303             case _C_GCINVISIBLE:
    304                 // skip these type modifiers
    305                 break;
    306             case _C_ID:
    307                 objcValueType = ObjcObjectType;
    308                 break;
    309             case _C_CHR:
    310                 objcValueType = ObjcCharType;
    311                 break;
    312             case _C_UCHR:
    313                 objcValueType = ObjcUnsignedCharType;
    314                 break;
    315             case _C_SHT:
    316                 objcValueType = ObjcShortType;
    317                 break;
    318             case _C_USHT:
    319                 objcValueType = ObjcUnsignedShortType;
    320                 break;
    321             case _C_INT:
    322                 objcValueType = ObjcIntType;
    323                 break;
    324             case _C_UINT:
    325                 objcValueType = ObjcUnsignedIntType;
    326                 break;
    327             case _C_LNG:
    328                 objcValueType = ObjcLongType;
    329                 break;
    330             case _C_ULNG:
    331                 objcValueType = ObjcUnsignedLongType;
    332                 break;
    333             case _C_LNG_LNG:
    334                 objcValueType = ObjcLongLongType;
    335                 break;
    336             case _C_ULNG_LNG:
    337                 objcValueType = ObjcUnsignedLongLongType;
    338                 break;
    339             case _C_FLT:
    340                 objcValueType = ObjcFloatType;
    341                 break;
    342             case _C_DBL:
    343                 objcValueType = ObjcDoubleType;
    344                 break;
    345             case _C_VOID:
    346                 objcValueType = ObjcVoidType;
    347                 break;
    348             default:
    349                 // Unhandled type. We don't handle C structs, unions, etc.
    350                 // FIXME: throw an exception?
    351                 ASSERT(false);
    352         }
    353 
    354         if (objcValueType != ObjcInvalidType)
    355             break;
    356     }
    357 
    358     return objcValueType;
    359 }
    360 
    361 JSObject *throwError(ExecState *exec, ErrorType type, NSString *message)
    362 {
    363     ASSERT(message);
    364     size_t length = [message length];
    365     unichar *buffer = new unichar[length];
    366     [message getCharacters:buffer];
    367     JSObject *error = throwError(exec, type, UString(buffer, length));
    368     delete [] buffer;
    369     return error;
    370 }
    371 
    372 }
    373 }
    374