1/*
2 * Copyright (C) 2004, 2008, 2009, 2013 Apple 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 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 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#import "config.h"
27#import "objc_instance.h"
28
29#import "runtime_method.h"
30#import <runtime/ObjectPrototype.h>
31#import "JSDOMBinding.h"
32#import "ObjCRuntimeObject.h"
33#import "WebScriptObject.h"
34#import <objc/objc-auto.h>
35#import <runtime/Error.h>
36#import <runtime/JSLock.h>
37#import "runtime/FunctionPrototype.h"
38#import <wtf/Assertions.h>
39
40#if PLATFORM(IOS)
41#import <Foundation/NSMapTable.h>
42#endif // PLATFORM(IOS)
43
44#ifdef NDEBUG
45#define OBJC_LOG(formatAndArgs...) ((void)0)
46#else
47#define OBJC_LOG(formatAndArgs...) { \
48    fprintf (stderr, "%s:%d -- %s:  ", __FILE__, __LINE__, __FUNCTION__); \
49    fprintf(stderr, formatAndArgs); \
50}
51#endif
52
53using namespace JSC::Bindings;
54using namespace JSC;
55
56static NSString *s_exception;
57static JSGlobalObject* s_exceptionEnvironment; // No need to protect this value, since we just use it for a pointer comparison.
58static NSMapTable *s_instanceWrapperCache;
59
60#if COMPILER(CLANG)
61#pragma clang diagnostic push
62#pragma clang diagnostic ignored "-Wdeprecated-declarations"
63#endif
64
65static NSMapTable *createInstanceWrapperCache()
66{
67    // NSMapTable with zeroing weak pointers is the recommended way to build caches like this under garbage collection.
68    NSPointerFunctionsOptions keyOptions = NSPointerFunctionsZeroingWeakMemory | NSPointerFunctionsOpaquePersonality;
69    NSPointerFunctionsOptions valueOptions = NSPointerFunctionsOpaqueMemory | NSPointerFunctionsOpaquePersonality;
70    return [[NSMapTable alloc] initWithKeyOptions:keyOptions valueOptions:valueOptions capacity:0];
71}
72
73#if COMPILER(CLANG)
74#pragma clang diagnostic pop
75#endif
76
77RuntimeObject* ObjcInstance::newRuntimeObject(ExecState* exec)
78{
79    // FIXME: deprecatedGetDOMStructure uses the prototype off of the wrong global object.
80    return ObjCRuntimeObject::create(exec->vm(), WebCore::deprecatedGetDOMStructure<ObjCRuntimeObject>(exec), this);
81}
82
83void ObjcInstance::setGlobalException(NSString* exception, JSGlobalObject* exceptionEnvironment)
84{
85    NSString *oldException = s_exception;
86    s_exception = [exception copy];
87    [oldException release];
88
89    s_exceptionEnvironment = exceptionEnvironment;
90}
91
92void ObjcInstance::moveGlobalExceptionToExecState(ExecState* exec)
93{
94    if (!s_exception) {
95        ASSERT(!s_exceptionEnvironment);
96        return;
97    }
98
99    if (!s_exceptionEnvironment || s_exceptionEnvironment == exec->vmEntryGlobalObject()) {
100        JSLockHolder lock(exec);
101        throwError(exec, s_exception);
102    }
103
104    [s_exception release];
105    s_exception = nil;
106    s_exceptionEnvironment = 0;
107}
108
109ObjcInstance::ObjcInstance(id instance, PassRefPtr<RootObject> rootObject)
110    : Instance(rootObject)
111    , _instance(instance)
112    , _class(0)
113    , _pool(0)
114    , _beginCount(0)
115{
116}
117
118PassRefPtr<ObjcInstance> ObjcInstance::create(id instance, PassRefPtr<RootObject> rootObject)
119{
120    if (!s_instanceWrapperCache)
121        s_instanceWrapperCache = createInstanceWrapperCache();
122    if (void* existingWrapper = NSMapGet(s_instanceWrapperCache, instance))
123        return static_cast<ObjcInstance*>(existingWrapper);
124    RefPtr<ObjcInstance> wrapper = adoptRef(new ObjcInstance(instance, rootObject));
125    NSMapInsert(s_instanceWrapperCache, instance, wrapper.get());
126    return wrapper.release();
127}
128
129ObjcInstance::~ObjcInstance()
130{
131    // Both -finalizeForWebScript and -dealloc/-finalize of _instance may require autorelease pools.
132    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
133
134    ASSERT(s_instanceWrapperCache);
135    ASSERT(_instance);
136    NSMapRemove(s_instanceWrapperCache, _instance.get());
137
138    if ([_instance.get() respondsToSelector:@selector(finalizeForWebScript)])
139        [_instance.get() performSelector:@selector(finalizeForWebScript)];
140    _instance = 0;
141
142    [pool drain];
143}
144
145static NSAutoreleasePool* allocateAutoReleasePool()
146{
147    // If GC is enabled an autorelease pool is unnecessary, and the
148    // pool cannot be protected from GC so may be collected leading
149    // to a crash when we try to drain the release pool.
150    if (objc_collectingEnabled())
151        return nil;
152
153    return [[NSAutoreleasePool alloc] init];
154}
155
156void ObjcInstance::virtualBegin()
157{
158    if (!_pool)
159        _pool = allocateAutoReleasePool();
160    _beginCount++;
161}
162
163void ObjcInstance::virtualEnd()
164{
165    _beginCount--;
166    ASSERT(_beginCount >= 0);
167    if (!_beginCount) {
168        [_pool drain];
169        _pool = 0;
170    }
171}
172
173Bindings::Class* ObjcInstance::getClass() const
174{
175    if (!_instance)
176        return 0;
177    if (!_class)
178        _class = ObjcClass::classForIsA(object_getClass(_instance.get()));
179    return static_cast<Bindings::Class*>(_class);
180}
181
182bool ObjcInstance::supportsInvokeDefaultMethod() const
183{
184    return [_instance.get() respondsToSelector:@selector(invokeDefaultMethodWithArguments:)];
185}
186
187class ObjCRuntimeMethod : public RuntimeMethod {
188public:
189    static ObjCRuntimeMethod* create(ExecState* exec, JSGlobalObject* globalObject, const String& name, Bindings::Method* method)
190    {
191        // FIXME: deprecatedGetDOMStructure uses the prototype off of the wrong global object
192        // We need to pass in the right global object for "i".
193        Structure* domStructure = WebCore::deprecatedGetDOMStructure<ObjCRuntimeMethod>(exec);
194        ObjCRuntimeMethod* runtimeMethod = new (NotNull, allocateCell<ObjCRuntimeMethod>(*exec->heap())) ObjCRuntimeMethod(globalObject, domStructure, method);
195        runtimeMethod->finishCreation(exec->vm(), name);
196        return runtimeMethod;
197    }
198
199    static Structure* createStructure(VM& vm, JSC::JSGlobalObject* globalObject, JSValue prototype)
200    {
201        return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info);
202    }
203
204    DECLARE_INFO;
205
206private:
207    typedef RuntimeMethod Base;
208
209    ObjCRuntimeMethod(JSGlobalObject* globalObject, Structure* structure, Bindings::Method* method)
210        : RuntimeMethod(globalObject, structure, method)
211    {
212    }
213
214    void finishCreation(VM& vm, const String& name)
215    {
216        Base::finishCreation(vm, name);
217        ASSERT(inherits(info()));
218    }
219};
220
221const ClassInfo ObjCRuntimeMethod::s_info = { "ObjCRuntimeMethod", &RuntimeMethod::s_info, 0, 0, CREATE_METHOD_TABLE(ObjCRuntimeMethod) };
222
223JSValue ObjcInstance::getMethod(ExecState* exec, PropertyName propertyName)
224{
225    Method* method = getClass()->methodNamed(propertyName, this);
226    return ObjCRuntimeMethod::create(exec, exec->lexicalGlobalObject(), propertyName.publicName(), method);
227}
228
229JSValue ObjcInstance::invokeMethod(ExecState* exec, RuntimeMethod* runtimeMethod)
230{
231    if (!asObject(runtimeMethod)->inherits(ObjCRuntimeMethod::info()))
232        return exec->vm().throwException(exec, createTypeError(exec, "Attempt to invoke non-plug-in method on plug-in object."));
233
234    ObjcMethod *method = static_cast<ObjcMethod*>(runtimeMethod->method());
235    ASSERT(method);
236
237    return invokeObjcMethod(exec, method);
238}
239
240JSValue ObjcInstance::invokeObjcMethod(ExecState* exec, ObjcMethod* method)
241{
242    JSValue result = jsUndefined();
243
244    JSLock::DropAllLocks dropAllLocks(exec); // Can't put this inside the @try scope because it unwinds incorrectly.
245
246    setGlobalException(nil);
247
248@try {
249    NSMethodSignature* signature = method->getMethodSignature();
250    NSInvocation* invocation = [NSInvocation invocationWithMethodSignature:signature];
251    [invocation setSelector:method->selector()];
252    [invocation setTarget:_instance.get()];
253
254    if (method->isFallbackMethod()) {
255        if (objcValueTypeForType([signature methodReturnType]) != ObjcObjectType) {
256            NSLog(@"Incorrect signature for invokeUndefinedMethodFromWebScript:withArguments: -- return type must be object.");
257            return result;
258        }
259
260        // Invoke invokeUndefinedMethodFromWebScript:withArguments:, pass JavaScript function
261        // name as first (actually at 2) argument and array of args as second.
262        NSString* jsName = (NSString* )method->javaScriptName();
263        [invocation setArgument:&jsName atIndex:2];
264
265        NSMutableArray* objcArgs = [NSMutableArray array];
266        int count = exec->argumentCount();
267        for (int i = 0; i < count; i++) {
268            ObjcValue value = convertValueToObjcValue(exec, exec->uncheckedArgument(i), ObjcObjectType);
269            [objcArgs addObject:value.objectValue];
270        }
271        [invocation setArgument:&objcArgs atIndex:3];
272    } else {
273        unsigned count = [signature numberOfArguments];
274        for (unsigned i = 2; i < count; ++i) {
275            const char* type = [signature getArgumentTypeAtIndex:i];
276            ObjcValueType objcValueType = objcValueTypeForType(type);
277
278            // Must have a valid argument type.  This method signature should have
279            // been filtered already to ensure that it has acceptable argument
280            // types.
281            ASSERT(objcValueType != ObjcInvalidType && objcValueType != ObjcVoidType);
282
283            ObjcValue value = convertValueToObjcValue(exec, exec->argument(i - 2), objcValueType);
284
285            switch (objcValueType) {
286                case ObjcObjectType:
287                    [invocation setArgument:&value.objectValue atIndex:i];
288                    break;
289                case ObjcCharType:
290                case ObjcUnsignedCharType:
291                    [invocation setArgument:&value.charValue atIndex:i];
292                    break;
293                case ObjcShortType:
294                case ObjcUnsignedShortType:
295                    [invocation setArgument:&value.shortValue atIndex:i];
296                    break;
297                case ObjcBoolType:
298                    [invocation setArgument:&value.booleanValue atIndex:i];
299                    break;
300                case ObjcIntType:
301                case ObjcUnsignedIntType:
302                    [invocation setArgument:&value.intValue atIndex:i];
303                    break;
304                case ObjcLongType:
305                case ObjcUnsignedLongType:
306                    [invocation setArgument:&value.longValue atIndex:i];
307                    break;
308                case ObjcLongLongType:
309                case ObjcUnsignedLongLongType:
310                    [invocation setArgument:&value.longLongValue atIndex:i];
311                    break;
312                case ObjcFloatType:
313                    [invocation setArgument:&value.floatValue atIndex:i];
314                    break;
315                case ObjcDoubleType:
316                    [invocation setArgument:&value.doubleValue atIndex:i];
317                    break;
318                default:
319                    // Should never get here.  Argument types are filtered (and
320                    // the assert above should have fired in the impossible case
321                    // of an invalid type anyway).
322                    fprintf(stderr, "%s: invalid type (%d)\n", __PRETTY_FUNCTION__, (int)objcValueType);
323                    ASSERT_NOT_REACHED();
324            }
325        }
326    }
327
328    [invocation invoke];
329
330    // Get the return value type.
331    const char* type = [signature methodReturnType];
332    ObjcValueType objcValueType = objcValueTypeForType(type);
333
334    // Must have a valid return type.  This method signature should have
335    // been filtered already to ensure that it have an acceptable return
336    // type.
337    ASSERT(objcValueType != ObjcInvalidType);
338
339    // Get the return value and convert it to a JavaScript value. Length
340    // of return value will never exceed the size of largest scalar
341    // or a pointer.
342    char buffer[1024];
343    ASSERT([signature methodReturnLength] < 1024);
344
345    if (*type != 'v') {
346        [invocation getReturnValue:buffer];
347        result = convertObjcValueToValue(exec, buffer, objcValueType, m_rootObject.get());
348    }
349} @catch(NSException* localException) {
350}
351    moveGlobalExceptionToExecState(exec);
352
353    // Work around problem in some versions of GCC where result gets marked volatile and
354    // it can't handle copying from a volatile to non-volatile.
355    return const_cast<JSValue&>(result);
356}
357
358JSValue ObjcInstance::invokeDefaultMethod(ExecState* exec)
359{
360    JSValue result = jsUndefined();
361
362    JSLock::DropAllLocks dropAllLocks(exec); // Can't put this inside the @try scope because it unwinds incorrectly.
363    setGlobalException(nil);
364
365@try {
366    if (![_instance.get() respondsToSelector:@selector(invokeDefaultMethodWithArguments:)])
367        return result;
368
369    NSMethodSignature* signature = [_instance.get() methodSignatureForSelector:@selector(invokeDefaultMethodWithArguments:)];
370    NSInvocation* invocation = [NSInvocation invocationWithMethodSignature:signature];
371    [invocation setSelector:@selector(invokeDefaultMethodWithArguments:)];
372    [invocation setTarget:_instance.get()];
373
374    if (objcValueTypeForType([signature methodReturnType]) != ObjcObjectType) {
375        NSLog(@"Incorrect signature for invokeDefaultMethodWithArguments: -- return type must be object.");
376        return result;
377    }
378
379    NSMutableArray* objcArgs = [NSMutableArray array];
380    unsigned count = exec->argumentCount();
381    for (unsigned i = 0; i < count; i++) {
382        ObjcValue value = convertValueToObjcValue(exec, exec->uncheckedArgument(i), ObjcObjectType);
383        [objcArgs addObject:value.objectValue];
384    }
385    [invocation setArgument:&objcArgs atIndex:2];
386
387    [invocation invoke];
388
389    // Get the return value type, should always be "@" because of
390    // check above.
391    const char* type = [signature methodReturnType];
392    ObjcValueType objcValueType = objcValueTypeForType(type);
393
394    // Get the return value and convert it to a JavaScript value. Length
395    // of return value will never exceed the size of a pointer, so we're
396    // OK with 32 here.
397    char buffer[32];
398    [invocation getReturnValue:buffer];
399    result = convertObjcValueToValue(exec, buffer, objcValueType, m_rootObject.get());
400} @catch(NSException* localException) {
401}
402    moveGlobalExceptionToExecState(exec);
403
404    // Work around problem in some versions of GCC where result gets marked volatile and
405    // it can't handle copying from a volatile to non-volatile.
406    return const_cast<JSValue&>(result);
407}
408
409bool ObjcInstance::setValueOfUndefinedField(ExecState* exec, PropertyName propertyName, JSValue aValue)
410{
411    String name(propertyName.publicName());
412    if (name.isNull())
413        return false;
414
415    id targetObject = getObject();
416    if (![targetObject respondsToSelector:@selector(setValue:forUndefinedKey:)])
417        return false;
418
419    JSLock::DropAllLocks dropAllLocks(exec); // Can't put this inside the @try scope because it unwinds incorrectly.
420
421    // This check is not really necessary because NSObject implements
422    // setValue:forUndefinedKey:, and unfortunately the default implementation
423    // throws an exception.
424    if ([targetObject respondsToSelector:@selector(setValue:forUndefinedKey:)]){
425        setGlobalException(nil);
426
427        ObjcValue objcValue = convertValueToObjcValue(exec, aValue, ObjcObjectType);
428
429        @try {
430            [targetObject setValue:objcValue.objectValue forUndefinedKey:[NSString stringWithCString:name.ascii().data() encoding:NSASCIIStringEncoding]];
431        } @catch(NSException* localException) {
432            // Do nothing.  Class did not override valueForUndefinedKey:.
433        }
434
435        moveGlobalExceptionToExecState(exec);
436    }
437
438    return true;
439}
440
441JSValue ObjcInstance::getValueOfUndefinedField(ExecState* exec, PropertyName propertyName) const
442{
443    String name(propertyName.publicName());
444    if (name.isNull())
445        return jsUndefined();
446
447    JSValue result = jsUndefined();
448
449    id targetObject = getObject();
450
451    JSLock::DropAllLocks dropAllLocks(exec); // Can't put this inside the @try scope because it unwinds incorrectly.
452
453    // This check is not really necessary because NSObject implements
454    // valueForUndefinedKey:, and unfortunately the default implementation
455    // throws an exception.
456    if ([targetObject respondsToSelector:@selector(valueForUndefinedKey:)]){
457        setGlobalException(nil);
458
459        @try {
460            id objcValue = [targetObject valueForUndefinedKey:[NSString stringWithCString:name.ascii().data() encoding:NSASCIIStringEncoding]];
461            result = convertObjcValueToValue(exec, &objcValue, ObjcObjectType, m_rootObject.get());
462        } @catch(NSException* localException) {
463            // Do nothing.  Class did not override valueForUndefinedKey:.
464        }
465
466        moveGlobalExceptionToExecState(exec);
467    }
468
469    // Work around problem in some versions of GCC where result gets marked volatile and
470    // it can't handle copying from a volatile to non-volatile.
471    return const_cast<JSValue&>(result);
472}
473
474JSValue ObjcInstance::defaultValue(ExecState* exec, PreferredPrimitiveType hint) const
475{
476    if (hint == PreferString)
477        return stringValue(exec);
478    if (hint == PreferNumber)
479        return numberValue(exec);
480    if ([_instance.get() isKindOfClass:[NSString class]])
481        return stringValue(exec);
482    if ([_instance.get() isKindOfClass:[NSNumber class]])
483        return numberValue(exec);
484    return valueOf(exec);
485}
486
487JSValue ObjcInstance::stringValue(ExecState* exec) const
488{
489    return convertNSStringToString(exec, [getObject() description]);
490}
491
492JSValue ObjcInstance::numberValue(ExecState*) const
493{
494    // FIXME:  Implement something sensible
495    return jsNumber(0);
496}
497
498JSValue ObjcInstance::booleanValue() const
499{
500    // FIXME:  Implement something sensible
501    return jsBoolean(false);
502}
503
504JSValue ObjcInstance::valueOf(ExecState* exec) const
505{
506    return stringValue(exec);
507}
508