]> err.no Git - sope/blob - Recycler/NGJavaScript/NGJavaScriptShadow.m
use %p for pointer formats
[sope] / Recycler / NGJavaScript / NGJavaScriptShadow.m
1 /*
2   Copyright (C) 2000-2003 SKYRIX Software AG
3
4   This file is part of OGo
5
6   OGo is free software; you can redistribute it and/or modify it under
7   the terms of the GNU Lesser General Public License as published by the
8   Free Software Foundation; either version 2, or (at your option) any
9   later version.
10
11   OGo is distributed in the hope that it will be useful, but WITHOUT ANY
12   WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
14   License for more details.
15
16   You should have received a copy of the GNU Lesser General Public
17   License along with OGo; see the file COPYING.  If not, write to the
18   Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
19   02111-1307, USA.
20 */
21 // $Id$
22
23 #include "NGJavaScriptShadow.h"
24 #include "NGJavaScriptObjCClassInfo.h"
25 #include "NGJavaScriptObjectMappingContext.h"
26 #include <NGScripting/NGScriptLanguage.h>
27 #include "common.h"
28 #include "globals.h"
29
30 static BOOL IsInPropDefMode = NO;
31
32 @interface NGJavaScriptShadow(Privates)
33 - (BOOL)_applyStaticDefs;
34 @end
35
36 @implementation NGJavaScriptShadow
37
38 static NGScriptLanguage *jslang = nil;
39
40 static inline NGScriptLanguage *_JS(void) {
41   if (jslang == nil)
42     jslang = [[NGScriptLanguage languageWithName:@"javascript"] retain];
43   return jslang;
44 }
45
46 static void _finalize(JSContext *cx, JSObject *obj) {
47   NGJavaScriptShadow *self;
48   
49   if ((self = JS_GetPrivate(cx, obj)) == NULL) {
50     //printf("finalized JS shadow ..\n");
51   }
52   else {
53     NSLog(@"ERROR(%s): finalizing JS shadow j0x%p, "
54           @"still has a private o0x%p !!!",
55           __PRETTY_FUNCTION__, obj, self);
56   }
57 }
58
59 JSClass ObjCShadow_JSClass = {
60   "NGObjCShadow",
61   JSCLASS_HAS_PRIVATE /* flags */,
62   JS_PropertyStub,
63   JS_PropertyStub,
64   JS_PropertyStub,
65   JS_PropertyStub,
66   JS_EnumerateStub,
67   JS_ResolveStub,
68   JS_ConvertStub,
69   _finalize,
70   /* Optionally non-null members start here. */
71   NULL, //JSGetObjectOps getObjectOps;
72   NULL, //JSCheckAccessOp checkAccess;
73   NULL, //JSNative call;
74   NULL, //JSNative construct;
75   NULL, //JSXDRObjectOp xdrObject;
76   NULL, //JSHasInstanceOp hasInstance;
77   //prword spare[2];
78 };
79
80 + (void *)jsObjectClass {
81   return &ObjCShadow_JSClass;
82 }
83
84 static JSBool shadow_FuncDispatcher
85 (JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
86 static JSBool shadow_setStaticProp
87 (JSContext *cx, JSObject *obj, jsval _id, jsval *vp);
88 static JSBool shadow_getStaticProp
89 (JSContext *cx, JSObject *obj, jsval _id, jsval *vp);
90
91 static NSMutableDictionary *classToInfo = nil;
92
93 static void relInfo(void) {
94   if (classToInfo)
95     RELEASE(classToInfo); classToInfo = nil;
96 }
97
98 static NGJavaScriptObjCClassInfo *jsClassInfo(Class _class) {
99   NGJavaScriptObjCClassInfo  *ci;
100
101   if (_class == Nil)
102     return nil;
103   
104   if (classToInfo == nil) {
105     classToInfo = [[NSMutableDictionary alloc] initWithCapacity:64];
106     atexit(relInfo);
107   }
108   
109   if ((ci = [classToInfo objectForKey:_class]) == nil) {
110     ci = [[NGJavaScriptObjCClassInfo alloc]
111                                      initWithClass:_class
112                                      setter:shadow_setStaticProp
113                                      getter:shadow_getStaticProp
114                                      caller:shadow_FuncDispatcher];
115     [classToInfo setObject:ci forKey:_class];
116     AUTORELEASE(ci);
117   }
118   
119   return ci;
120 }
121
122 - (id)initWithHandle:(void *)_handle
123   inMappingContext:(NGObjectMappingContext *)_ctx
124 {
125   if ((self = [super initWithHandle:_handle inMappingContext:_ctx])) {
126     JS_SetPrivate(self->jscx, _handle, self);
127   }
128   return self;
129 }
130
131 - (void)dealloc {
132   if (NGJavaScriptBridge_TRACK_MEMORY) {
133     NSLog(@"%s: dealloc shadow o0x%p j0x%p ctx=0x%p jcx=0x%p",
134           __PRETTY_FUNCTION__, self, self->handle,
135           self->ctx, self->jscx);
136   }
137   
138   if (self->handle)
139     JS_SetPrivate(self->jscx, self->handle, NULL);
140   
141   [super dealloc];
142 }
143
144 - (void)setMasterObject:(id)_master {
145   if ((self->masterObject = _master)) {
146     if (![self _applyStaticDefs]) {
147       self->masterObject = nil;
148       NSLog(@"%s: resetted master object, because static defs could "
149             @"not be applied: %@", __PRETTY_FUNCTION__, _master);
150     }
151   }
152   else {
153     if (NGJavaScriptBridge_TRACK_MEMORY) {
154       NSLog(@"%s: resetted shadow master (rc now %i)",
155             __PRETTY_FUNCTION__,
156             [self retainCount]);
157     }
158   }
159 }
160 - (id)masterObject {
161   return self->masterObject;
162 }
163 - (void)invalidateShadow {
164   [self setMasterObject:nil];
165 }
166
167 - (BOOL)_applyStaticDefs {
168   NGJavaScriptObjCClassInfo *ci;
169   BOOL ok;
170   
171   if (self->masterObject == nil)
172     return NO;
173   
174   ci = jsClassInfo([self->masterObject class]);
175   
176   IsInPropDefMode = YES;
177   ok = [ci applyOnJSObject:self->handle inJSContext:self->jscx];
178   if (!ok)
179     NSLog(@"ERROR(%s): couldn't apply static defs !", __PRETTY_FUNCTION__);
180   IsInPropDefMode = NO;
181   return ok;
182 }
183
184 /* static definition declarations */
185
186 static JSBool shadow_setStaticProp
187 (JSContext *cx, JSObject *obj, jsval _id, jsval *vp)
188 {
189   NGJavaScriptObjCClassInfo *ci;
190   NGJavaScriptShadow *self;
191   SEL sel;
192   id  value;
193   
194   if ((self = JS_GetPrivate(cx, obj)) == NULL)
195     return JS_FALSE;
196   
197   if (self->masterObject == nil) {
198     NSLog(@"%s: master object was deallocated !", __PRETTY_FUNCTION__);
199     return JS_FALSE;
200   }
201   
202   ci = jsClassInfo([self->masterObject class]);
203   NSCAssert(ci, @"missing class info ..");
204   
205   sel = [ci setSelectorForPropertyId:&_id inJSContext:cx];
206   
207   if (sel == NULL) {
208     NSLog(@"%s: did not find selector for id !", __PRETTY_FUNCTION__);
209     return JS_FALSE;
210   }
211   
212   value = [self->ctx objectForJSValue:vp];
213   [self->masterObject performSelector:sel withObject:value];
214   
215   return JS_TRUE;
216 }
217 static JSBool shadow_getStaticProp
218 (JSContext *cx, JSObject *obj, jsval _id, jsval *vp)
219 {
220   NGJavaScriptObjCClassInfo *ci;
221   NGJavaScriptShadow *self;
222   SEL sel;
223   id  result;
224   
225   if ((self = JS_GetPrivate(cx, obj)) == NULL) {
226     NSLog(@"%s: did not find private of JS shadow object !",
227           __PRETTY_FUNCTION__);
228     return JS_FALSE;
229   }
230   
231   if (self->masterObject == nil) {
232     NSLog(@"%s: master object was deallocated !", __PRETTY_FUNCTION__);
233     return JS_FALSE;
234   }
235   
236   ci  = jsClassInfo([self->masterObject class]);
237   sel = [ci getSelectorForPropertyId:&_id inJSContext:cx];
238   
239   if (sel == NULL) {
240     NSLog(@"%s: did not find selector for id !", __PRETTY_FUNCTION__);
241     return JS_FALSE;
242   }
243   
244   result = [self->masterObject performSelector:sel];
245   //NSLog(@"result is %@", result);
246   
247   return [self->ctx jsValue:vp forObject:result]
248     ? JS_TRUE
249     : JS_FALSE;
250 }
251
252 static JSBool shadow_FuncDispatcher
253 (JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
254 {
255   NGJavaScriptShadow *self;
256   JSFunction  *funobj;
257   const char  *funcName;
258   char        *msgname;
259   SEL         sel;
260   unsigned    i;
261   id          *args;
262   NSArray     *argArray;
263   id          result;
264   NSException *exception;
265   JSBool      retcode = 0;
266   
267   if (JS_IsConstructing(cx)) 
268     obj = JS_GetParent(cx, obj);
269   
270 #if DEBUG
271   if (JS_GetClass(obj) != &ObjCShadow_JSClass) {
272     NSLog(@"%s: invoked on invalid object class (eg using 'new' ?) !",
273           __PRETTY_FUNCTION__);
274     return JS_FALSE;
275   }
276 #endif
277   
278   if ((self = JS_GetPrivate(cx, obj)) == NULL)
279     return JS_FALSE;
280   
281 #if DEBUG
282   NSCAssert(JS_TypeOfValue(cx, argv[-2]) == JSTYPE_FUNCTION,
283             @"expected function in argv[-2] !");
284 #endif
285   
286   funobj   = JS_GetPrivate(cx, JSVAL_TO_OBJECT(argv[-2]));
287   funcName = JS_GetFunctionName(funobj);
288   
289   msgname = malloc(strlen(funcName) + 10);
290   strcpy(msgname, "_jsfunc_");
291   strcat(msgname, funcName);
292   strcat(msgname, ":");
293 #if APPLE_RUNTIME || NeXT_RUNTIME
294   sel = sel_getUid(msgname); /* TODO: should be registerName? */
295 #else
296   sel = sel_get_any_uid(msgname);
297 #endif
298   
299   if (argc > 0) {
300     args = calloc(argc, sizeof(id));
301     for (i = 0; i < argc; i++) {
302       args[i] =
303         [self->ctx objectForJSValue:&(argv[i])];
304       
305       if (args[i] == nil) args[i] = [EONull null];
306     }
307     argArray = [NSArray arrayWithObjects:args count:argc];
308     free(args);
309   }
310   else
311     argArray = [NSArray array];
312
313 #if 0  
314   NSLog(@"calling function '%s'(%s), %d args %@\n",
315         funcName, msgname, argc, argArray);
316 #endif
317   
318   exception = nil;
319   NS_DURING {
320     result  = [self->masterObject performSelector:sel withObject:argArray];
321     retcode = [self->ctx jsValue:rval forObject:result] ? JS_TRUE : JS_FALSE;
322   }
323   NS_HANDLER {
324     exception = RETAIN(localException);
325   }
326   NS_ENDHANDLER;
327   
328   if (exception) {
329     jsval exval;
330     
331 #if DEBUG
332     NSLog(@"%s: catched exception: %@", __PRETTY_FUNCTION__, exception);
333 #endif
334     
335     retcode = JS_FALSE;
336     
337     if ([self->ctx jsValue:&exval forObject:[exception description]]) {
338       JS_SetPendingException(cx, exval);
339     }
340     else {
341       NSLog(@"%s: couldn't get JS value for exception: %@",
342             __PRETTY_FUNCTION__, exception);
343     }
344   }
345   
346   return retcode;
347 }
348
349 /* specialized calls */
350
351 - (id)callScriptFunction:(NSString *)_func {
352   return [_JS() callFunction:_func onObject:self];
353 }
354 - (id)callScriptFunction:(NSString *)_func withObject:(id)_obj {
355   return [_JS() callFunction:_func withArgument:_obj onObject:self];
356 }
357 - (BOOL)hasFunctionNamed:(NSString *)_func {
358   return [super hasFunctionNamed:_func];
359 }
360
361 - (id)evaluateScript:(NSString *)_script 
362   source:(NSString *)_src line:(unsigned)_line 
363 {
364   return [_JS() evaluateScript:_script onObject:self source:_src line:_line];
365 }
366 - (id)evaluateScript:(NSString *)_script {
367   return [self evaluateScript:_script source:@"<string>" line:0];
368 }
369
370 /* NSCoding */
371
372 - (id)initWithCoder:(NSCoder *)_coder {
373   if ((self = [super initWithCoder:_coder])) {
374     [self setMasterObject:[_coder decodeObject]];
375   }
376   return self;
377 }
378 - (void)encodeWithCoder:(NSCoder *)_coder {
379   [super encodeWithCoder:_coder];
380   [_coder encodeObject:[self masterObject]];
381 }
382
383 @end /* NGJavaScriptShadow */