JavaScriptCore
[WebKit-https.git] / JavaScriptCore / bindings / objc / WebScriptObject.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
26 #import <JavaScriptCore/WebScriptObjectPrivate.h>
27
28 #include <JavaScriptCore/internal.h>
29 #include <JavaScriptCore/list.h>
30 #include <JavaScriptCore/value.h>
31
32 #include <objc_jsobject.h>
33 #include <objc_instance.h>
34 #include <objc_utility.h>
35
36 #include <runtime_object.h>
37 #include <runtime_root.h>
38
39 #if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_3
40
41 @interface NSObject (WebExtras)
42 - (void)finalize;
43 @end
44
45 #endif
46
47 using namespace KJS;
48 using namespace KJS::Bindings;
49
50 #define LOG_EXCEPTION(exec) \
51     if (Interpreter::shouldPrintExceptions()) \
52         NSLog (@"%s:%d:[%d]  JavaScript exception:  %s\n", __FILE__, __LINE__, getpid(), exec->exception().toObject(exec).get(exec, messagePropertyName).toString(exec).ascii());
53
54 @implementation WebScriptObjectPrivate
55
56 @end
57
58 @implementation WebScriptObject
59
60 static void _didExecute(WebScriptObject *obj)
61 {
62     ExecState *exec = [obj _executionContext]->interpreter()->globalExec();
63     KJSDidExecuteFunctionPtr func = Instance::didExecuteFunction();
64     if (func)
65         func (exec, static_cast<KJS::ObjectImp*>([obj _executionContext]->rootObjectImp()));
66 }
67
68 - (void)_initializeWithObjectImp:(KJS::ObjectImp *)imp root:(const Bindings::RootObject *)root
69 {
70     _private->imp = imp;
71     _private->root = root;    
72
73     addNativeReference (root, imp);
74 }
75
76 - _initWithObjectImp:(KJS::ObjectImp *)imp root:(const Bindings::RootObject *)root
77 {
78     assert (imp != 0);
79     //assert (root != 0);
80
81     self = [super init];
82
83     _private = [[WebScriptObjectPrivate alloc] init];
84
85     [self _initializeWithObjectImp:imp root:root];
86     
87     return self;
88 }
89
90 - (KJS::ObjectImp *)_imp
91 {
92     if (!_private->imp && _private->isCreatedByDOMWrapper) {
93         // Associate the WebScriptObject with the JS wrapper for the ObjC DOM
94         // wrapper.  This is done on lazily, on demand.
95         [self _initializeScriptDOMNodeImp];
96     }
97     return _private->imp;
98 }
99
100 - (const KJS::Bindings::RootObject *)_executionContext
101 {
102     return _private->root;
103 }
104
105 - (void)dealloc
106 {
107     removeNativeReference(_private->imp);
108     [_private release];
109         
110     [super dealloc];
111 }
112
113 - (void)finalize
114 {
115     removeNativeReference(_private->imp);
116         
117     [super finalize];
118 }
119
120 + (BOOL)throwException:(NSString *)exceptionMessage
121 {
122     InterpreterImp *first, *interp = InterpreterImp::firstInterpreter();
123
124     // This code assumes that we only ever have one running interpreter.  A
125     // good assumption for now, as we depend on that elsewhere.  However,
126     // in the future we may have the ability to run multiple interpreters,
127     // in which case this will have to change.
128     first = interp;
129     do {
130         ExecState *exec = interp->globalExec();
131         // If the interpreter has a context, we set the exception.
132         if (interp->context()) {
133             Object err = Error::create(exec, GeneralError, [exceptionMessage UTF8String]);
134             exec->setException (err);
135             return YES;
136         }
137         interp = interp->nextInterpreter();
138     } while (interp != first);
139     
140     return NO;
141 }
142
143 static KJS::List listFromNSArray(ExecState *exec, NSArray *array)
144 {
145     long i, numObjects = array ? [array count] : 0;
146     KJS::List aList;
147     
148     for (i = 0; i < numObjects; i++) {
149         id anObject = [array objectAtIndex:i];
150         aList.append (convertObjcValueToValue(exec, &anObject, ObjcObjectType));
151     }
152     return aList;
153 }
154
155 - (id)callWebScriptMethod:(NSString *)name withArguments:(NSArray *)args
156 {
157     if (![self _executionContext])
158         return nil;
159
160     // Lookup the function object.
161     ExecState *exec = [self _executionContext]->interpreter()->globalExec();
162     Interpreter::lock();
163     
164     Value v = convertObjcValueToValue(exec, &name, ObjcObjectType);
165     Identifier identifier(v.toString(exec));
166     Value func = [self _imp]->get (exec, identifier);
167     Interpreter::unlock();
168     if (func.isNull() || func.type() == UndefinedType) {
169         // Maybe throw an exception here?
170         return 0;
171     }
172
173     // Call the function object.    
174     Interpreter::lock();
175     ObjectImp *funcImp = static_cast<ObjectImp*>(func.imp());
176     Object thisObj = Object(const_cast<ObjectImp*>([self _imp]));
177     List argList = listFromNSArray(exec, args);
178     Value result = funcImp->call (exec, thisObj, argList);
179     Interpreter::unlock();
180
181     if (exec->hadException()) {
182         LOG_EXCEPTION (exec);
183         result = Undefined();
184     }
185
186     // Convert and return the result of the function call.
187     id resultObj = [WebScriptObject _convertValueToObjcValue:result root:[self _executionContext]];
188
189     _didExecute(self);
190         
191     return resultObj;
192 }
193
194 - (id)evaluateWebScript:(NSString *)script
195 {
196     if (![self _executionContext])
197         return nil;
198
199     ExecState *exec = [self _executionContext]->interpreter()->globalExec();
200     Object thisObj = Object(const_cast<ObjectImp*>([self _imp]));
201     Value result;
202     
203     Interpreter::lock();
204     
205     Value v = convertObjcValueToValue(exec, &script, ObjcObjectType);
206     Completion completion = [self _executionContext]->interpreter()->evaluate(UString(), 0, v.toString(exec));
207     ComplType type = completion.complType();
208     
209     if (type == Normal) {
210         result = completion.value();
211         if (result.isNull()) {
212             result = Undefined();
213         }
214     }
215     else
216         result = Undefined();
217
218     Interpreter::unlock();
219     
220     if (exec->hadException()) {
221         LOG_EXCEPTION (exec);
222         result = Undefined();
223     }
224
225     id resultObj = [WebScriptObject _convertValueToObjcValue:result root:[self _executionContext]];
226
227     _didExecute(self);
228     
229     return resultObj;
230 }
231
232 - (void)setValue:(id)value forKey:(NSString *)key
233 {
234     if (![self _executionContext])
235         return;
236
237     ExecState *exec = [self _executionContext]->interpreter()->globalExec();
238     Interpreter::lock();
239     Value v = convertObjcValueToValue(exec, &key, ObjcObjectType);
240     [self _imp]->put (exec, Identifier (v.toString(exec)), (convertObjcValueToValue(exec, &value, ObjcObjectType)));
241     Interpreter::unlock();
242
243     if (exec->hadException()) {
244         LOG_EXCEPTION (exec);
245     }
246
247     _didExecute(self);
248 }
249
250 - (id)valueForKey:(NSString *)key
251 {
252     if (![self _executionContext])
253         return nil;
254         
255     ExecState *exec = [self _executionContext]->interpreter()->globalExec();
256     Interpreter::lock();
257     Value v = convertObjcValueToValue(exec, &key, ObjcObjectType);
258     Value result = [self _imp]->get (exec, Identifier (v.toString(exec)));
259     Interpreter::unlock();
260     
261     if (exec->hadException()) {
262         LOG_EXCEPTION (exec);
263         result = Undefined();
264     }
265
266     id resultObj = [WebScriptObject _convertValueToObjcValue:result root:[self _executionContext]];
267
268     _didExecute(self);
269     
270     return resultObj;
271 }
272
273 - (void)removeWebScriptKey:(NSString *)key;
274 {
275     if (![self _executionContext])
276         return;
277         
278     ExecState *exec = [self _executionContext]->interpreter()->globalExec();
279     Interpreter::lock();
280     Value v = convertObjcValueToValue(exec, &key, ObjcObjectType);
281     [self _imp]->deleteProperty (exec, Identifier (v.toString(exec)));
282     Interpreter::unlock();
283
284     if (exec->hadException()) {
285         LOG_EXCEPTION (exec);
286     }
287
288     _didExecute(self);
289 }
290
291 - (NSString *)stringRepresentation
292 {
293     Interpreter::lock();
294     Object thisObj = Object(const_cast<ObjectImp*>([self _imp]));
295     ExecState *exec = [self _executionContext]->interpreter()->globalExec();
296     
297     id result = convertValueToObjcValue(exec, thisObj, ObjcObjectType).objectValue;
298
299     Interpreter::unlock();
300     
301     id resultObj = [result description];
302
303     _didExecute(self);
304
305     return resultObj;
306 }
307
308 - (id)webScriptValueAtIndex:(unsigned int)index;
309 {
310     if (![self _executionContext])
311         return nil;
312
313     ExecState *exec = [self _executionContext]->interpreter()->globalExec();
314     Interpreter::lock();
315     Value result = [self _imp]->get (exec, (unsigned)index);
316     Interpreter::unlock();
317
318     if (exec->hadException()) {
319         LOG_EXCEPTION (exec);
320         result = Undefined();
321     }
322
323     id resultObj = [WebScriptObject _convertValueToObjcValue:result root:[self _executionContext]];
324
325     _didExecute(self);
326
327     return resultObj;
328 }
329
330 - (void)setWebScriptValueAtIndex:(unsigned int)index value:(id)value;
331 {
332     if (![self _executionContext])
333         return;
334
335     ExecState *exec = [self _executionContext]->interpreter()->globalExec();
336     Interpreter::lock();
337     [self _imp]->put (exec, (unsigned)index, (convertObjcValueToValue(exec, &value, ObjcObjectType)));
338     Interpreter::unlock();
339
340     if (exec->hadException()) {
341         LOG_EXCEPTION (exec);
342     }
343
344     _didExecute(self);
345 }
346
347 - (void)setException: (NSString *)description;
348 {
349     if (![self _executionContext])
350         return;
351
352     ExecState *exec = [self _executionContext]->interpreter()->globalExec();
353     Object err = Error::create(exec, GeneralError, [description UTF8String]);
354     exec->setException (err);
355 }
356
357 + (id)_convertValueToObjcValue:(KJS::Value)value root:(const Bindings::RootObject *)root
358 {
359     id result = 0;
360
361     // First see if we have a ObjC instance.
362     if (value.type() == KJS::ObjectType){
363         ObjectImp *objectImp = static_cast<ObjectImp*>(value.imp());
364         if (strcmp(objectImp->classInfo()->className, "RuntimeObject") == 0) {
365             RuntimeObjectImp *imp = static_cast<RuntimeObjectImp *>(value.imp());
366             ObjcInstance *instance = static_cast<ObjcInstance*>(imp->getInternalInstance());
367             if (instance)
368                 result = instance->getObject();
369         }
370         // Convert to a WebScriptObject
371         else {
372             result = [[[WebScriptObject alloc] _initWithObjectImp:objectImp root:root] autorelease];
373         }
374     }
375     
376     // Convert JavaScript String value to NSString?
377     else if (value.type() == KJS::StringType) {
378         StringImp *s = static_cast<KJS::StringImp*>(value.imp());
379         UString u = s->value();
380         
381         NSString *string = [NSString stringWithCharacters:(const unichar*)u.data() length:u.size()];
382         result = string;
383     }
384     
385     // Convert JavaScript Number value to NSNumber?
386     else if (value.type() == KJS::NumberType) {
387         Number n = Number::dynamicCast(value);
388         result = [NSNumber numberWithDouble:n.value()];
389     }
390     
391     else if (value.type() == KJS::BooleanType) {
392         KJS::BooleanImp *b = static_cast<KJS::BooleanImp*>(value.imp());
393         result = [NSNumber numberWithBool:b->value()];
394     }
395     
396     return result;
397 }
398
399 @end
400
401
402 @implementation WebUndefined
403
404 static WebUndefined *sharedUndefined = 0;
405
406 + (WebUndefined *)undefined
407 {
408     if (!sharedUndefined)
409         sharedUndefined = [[WebUndefined alloc] init];
410     return sharedUndefined;
411 }
412
413 - (id)initWithCoder:(NSCoder *)coder
414 {
415     return [WebUndefined undefined];
416 }
417
418 - (void)encodeWithCoder:(NSCoder *)encoder
419 {
420 }
421
422 - (id)copyWithZone:(NSZone *)zone
423 {
424     return [WebUndefined undefined];
425 }
426
427 - (id)retain {
428     return [WebUndefined undefined];
429 }
430
431 - (void)release {
432 }
433
434 - (unsigned)retainCount {
435     return 0xFFFFFFFF;
436 }
437
438 - (id)autorelease {
439     return [WebUndefined undefined];
440 }
441
442 - (void)dealloc {
443 }
444
445 - (id)copy {
446     return [WebUndefined undefined];
447 }
448
449 - (id)replacementObjectForPortCoder:(NSPortCoder *)encoder {
450     return [WebUndefined undefined];
451 }
452
453 @end