]> err.no Git - sope/blob - Recycler/NGJavaScript/NGJavaScriptObjectHandler.m
fixed a warning in resource manager
[sope] / Recycler / NGJavaScript / NGJavaScriptObjectHandler.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 "NGJavaScriptObjectHandler.h"
24 #include "NGJavaScriptObjectMappingContext.h"
25 #include "common.h"
26 #include "NGJavaScriptContext.h"
27 #include "NSString+JS.h"
28 #include "NSObject+JS.h"
29 #include "NGJavaScriptObjCClassInfo.h"
30 #include "NGJavaScriptRuntime.h"
31 #include <NGExtensions/NGObjCRuntime.h>
32 #import <EOControl/EONull.h>
33
34 //#define USE_ENUM 1
35 //#define LOG_PROP_RESOLVE 1
36
37 #define PROP_READONLY_FLAGS  0
38 #define PROP_READWRITE_FLAGS 0
39 //#define PROP_READONLY_FLAGS  (JSPROP_READONLY | JSPROP_PERMANENT)
40
41 @interface NGJavaScriptObjectHandler(Privates)
42 - (BOOL)_applyStaticDefs;
43 - (NSString *)stringValue;
44 @end
45
46 static BOOL IsInPropDefMode = NO;
47
48 @interface NSObject(JSFinalization)
49 - (BOOL)isJSCombinedObject;
50 - (NSEnumerator *)jsObjectEnumerator;
51 @end
52
53 #include "globals.h"
54
55 @implementation NGJavaScriptObjectHandler
56
57 static JSBool 
58 staticFuncDispatcher
59 (JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
60 static JSBool NGJavaScriptBridge_setStaticProp
61 (JSContext *cx, JSObject *obj, jsval _id, jsval *vp);
62 static JSBool NGJavaScriptBridge_getStaticProp
63 (JSContext *cx, JSObject *obj, jsval _id, jsval *vp);
64
65 static NSMutableDictionary *classToInfo = nil;
66
67 static void relInfo(void) {
68   if (classToInfo)
69     [classToInfo release]; classToInfo = nil;
70 }
71 static NGJavaScriptObjCClassInfo *jsClassInfo(Class _class) {
72   NGJavaScriptObjCClassInfo  *ci;
73
74   if (_class == Nil)
75     return nil;
76
77   if (classToInfo == nil) {
78     classToInfo = [[NSMutableDictionary alloc] initWithCapacity:64];
79     atexit(relInfo);
80   }
81
82   if ((ci = [classToInfo objectForKey:_class]) == nil) {
83     ci = [[NGJavaScriptObjCClassInfo alloc]
84                                      initWithClass:_class
85                                      setter:NGJavaScriptBridge_setStaticProp
86                                      getter:NGJavaScriptBridge_getStaticProp
87                                      caller:&staticFuncDispatcher];
88     [classToInfo setObject:ci forKey:_class];
89     [ci autorelease];
90   }
91   
92   return ci;
93 }
94
95 static Class NSStringClass = Nil;
96 static Class ObjInfoClass  = Nil;
97
98 static JSBool _addProp(JSContext *cx, JSObject *obj, jsval _id, jsval *vp);
99 static JSBool _delProp(JSContext *cx, JSObject *obj, jsval _id, jsval *vp);
100 static JSBool _getProp(JSContext *cx, JSObject *obj, jsval _id, jsval *vp);
101 static JSBool _setProp(JSContext *cx, JSObject *obj, jsval _id, jsval *vp);
102 static JSBool _resolve(JSContext *cx, JSObject *obj, jsval _id);
103 static JSBool _convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp);
104 static void   _finalize(JSContext *cx, JSObject *obj);
105 //static JSBool _construct
106 //(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
107
108 #if USE_ENUM
109 static JSBool
110 _newEnumObj(JSContext *cx, JSObject *obj,
111             JSIterateOp op, jsval *statep, jsid *idp);
112 #endif
113
114 JSClass NGJavaScriptObjectHandler_JSClass = {
115   "NGJavaScriptObjectHandler",
116 #if USE_ENUM
117   JSCLASS_HAS_PRIVATE | JSCLASS_NEW_ENUMERATE /* flags */,
118 #else
119   JSCLASS_HAS_PRIVATE /* flags */,
120 #endif
121   _addProp,
122   _delProp,
123   _getProp,
124   _setProp,
125 #if USE_ENUM
126   (JSEnumerateOp)_newEnumObj,
127 #else
128   JS_EnumerateStub,
129 #endif
130   _resolve,
131   _convert,
132   _finalize,
133   /* Optionally non-null members start here. */
134   NULL, //JSGetObjectOps getObjectOps;
135   NULL, //JSCheckAccessOp checkAccess;
136   NULL, //JSNative call;
137   NULL,// _construct //JSNative construct;
138   NULL, //JSXDRObjectOp xdrObject;
139   NULL, //JSHasInstanceOp hasInstance;
140   //prword spare[2];
141 };
142
143 + (void)initialize {
144   if (NSStringClass == Nil) NSStringClass = [NSString class];
145   if (ObjInfoClass  == Nil) ObjInfoClass  = [NGJavaScriptObjCClassInfo class];
146 }
147
148 + (void *)defaultJSClassHandle {
149   return &NGJavaScriptObjectHandler_JSClass;
150 }
151 - (void *)jsclassHandle {
152   return [[self class] defaultJSClassHandle];
153 }
154
155 - (id)initWithJSContext:(NGJavaScriptContext *)_ctx {
156   /*
157     TODO: This *is* used. But where ? Document !
158     
159     -testSequence:
160       blah = [[Blah alloc] init];
161       [global setObject:blah forKey:@"blah"];
162     The setObject: triggers the initWithJSContext:.
163   */
164   JSContext *cx;
165   
166   cx = [_ctx handle];
167   
168   self->jsObject = JS_NewObject(cx,
169                                 [self jsclassHandle],
170                                 NULL /* prototype */,
171                                 NULL /* parent    */);
172   if (self->jsObject == NULL) {
173     NSLog(@"WARNING(%s): got no JS object ...", __PRETTY_FUNCTION__);
174     [self release];
175     return nil;
176   }
177   
178   // TODO: is it correct that private is retained ?
179   JS_SetPrivate(cx, self->jsObject, [self retain]);
180   
181   self->jsContext = [_ctx handle];
182   
183   return self;
184 }
185 - (id)initWithJSContext:(NGJavaScriptContext *)_ctx handler:(id)_handler {
186   // TODO: is this actually used anywhere
187   if ((self = [self initWithJSContext:_ctx])) {
188     self->managedObject = _handler;
189     
190     if (![self _applyStaticDefs]) {
191       NSLog(@"ERROR(%s): rejecting creation of handler for %@ in ctx %@ "
192             @"because the static defs could not be applied !",
193             __PRETTY_FUNCTION__, _handler, _ctx);
194       [self release];
195       return nil;
196     }
197   }
198   return self;
199 }
200
201 - (id)initWithObject:(id)_object
202   inMappingContext:(NGObjectMappingContext *)_ctx
203 {
204   /* 
205      this one is called by -proxyForObject: and makeObjectCombined: of 
206      NGJavaScriptObjectMappingContext
207   */
208   if ((self = [self initWithJSContext:[(id)_ctx jsContext]])) {
209     self->ctx           = (NGJavaScriptObjectMappingContext *)_ctx;
210     self->managedObject = _object;
211     
212     if (![self _applyStaticDefs]) {
213       NSLog(@"ERROR(%s): rejecting creation of handler for %@ in ctx %@ "
214             @"because the static defs could not be applied !",
215             __PRETTY_FUNCTION__, _object, _ctx);
216       [self release];
217       return nil;
218     }
219   }
220   return self;
221 }
222
223 - (void)dealloc {
224   JSContext *cx;
225   
226   /* 
227      Note: managedObject could already be deallocated at this stage ! 
228      BUT: if managedObject is deallocated, the "ctx" is not retained
229           by the managedObject and therefore possibly broken !
230   */
231   
232   if (NGJavaScriptBridge_TRACK_MEMORY) {
233     NSLog(@"%s: dealloc 0x%08X<%@> at 0x%08X on j0x%08X",
234           __PRETTY_FUNCTION__,
235           self, NSStringFromClass([self class]),
236           self->managedObject,
237           self->jsObject);
238   }
239   
240   cx = self->jsContext;
241   
242   if (self->jsObject) {
243     if (cx) {
244       id priv;
245       
246       while (self->jsRootRC > 0) 
247         JS_RemoveRoot(cx, self->jsObject);
248       self->jsRootRC = 0;
249       
250       priv = JS_GetPrivate(cx, self->jsObject);
251       if (priv == self) {
252         NSLog(@"ERROR(%s): object handler 0x%08X still has a private ???",
253               __PRETTY_FUNCTION__, self);
254         JS_SetPrivate(cx, self->jsObject, NULL);
255       }
256     }
257   }
258   else {
259     if (self->jsRootRC > 0) {
260       NSLog(@"WARNING(%s): jsRootRc > 0, but jsObject is missing 0x%08X",
261             __PRETTY_FUNCTION__, self);
262     }
263   }
264   
265   [super dealloc];
266 }
267
268 /* accessors */
269
270 - (NGJavaScriptContext *)jsContext {
271   return [NGJavaScriptContext jsContextForHandle:self->jsContext];
272 }
273 - (void *)handle {
274   return self->jsObject;
275 }
276
277 - (NSString *)javaScriptClassName {
278   JSClass *clazz;
279   
280   if (self->jsObject == nil)
281     return nil;
282   
283   if ((clazz = JS_GetClass(self->jsObject)))
284     return nil;
285   
286   return [NSStringClass stringWithCString:clazz->name];
287 }
288
289 - (id)managedObject {
290   return self->managedObject;
291 }
292
293 - (id)parentObject {
294   JSObject *pjso;
295
296   if ((pjso = JS_GetParent(self->jsContext, self->jsObject)) == NULL)
297     return nil;
298   
299   return [self->ctx objectForHandle:pjso];
300 }
301
302 /* JS root references */
303
304 - (const char *)_jsRCName {
305 #if DEBUG && 0
306   /* WATCH OUT: leaks memory ! */
307   char *buf;
308   buf = malloc(32);
309   sprintf(buf, "ObjC:0x%08X", (unsigned)self);
310   return buf;
311 #else
312   return "ObjC root";
313 #endif
314 }
315
316 - (id)jsRetain {
317   if (self->jsRootRC > 0) {
318     self->jsRootRC++;
319   }
320   else {
321     JSBool     ret;
322     const char *c;
323     JSContext  *cx;
324     
325     cx = self->jsContext;
326     
327     NSAssert(self->jsObject,  @"missing JS object !");
328     NSAssert(cx,              @"missing JS context !");
329     
330     c = [self _jsRCName];
331     ret = c != NULL
332       ? JS_AddNamedRoot(cx, &self->jsObject, c)
333       : JS_AddRoot(cx, &self->jsObject);
334     
335     NSAssert(ret, @"couldn't add JS root !");
336     self->jsRootRC = 1;
337   }
338   return self;
339 }
340 - (void)jsRelease {
341   if (self->jsRootRC < 1) {
342     NSLog(@"WARNING(%s): called jsRelease on JS object which is not retained !");
343     return;
344   }
345   self->jsRootRC--;
346   
347   if (self->jsRootRC == 0)
348     JS_RemoveRoot(self->jsContext, &self->jsObject);
349 }
350 - (unsigned)jsRootRetainCount {
351   return self->jsRootRC;
352 }
353
354 /* properties */
355
356 - (BOOL)hasPropertyNamed:(NSString *)_propName {
357   JSBool r;
358   jsval  v;
359
360   r = JS_LookupProperty(self->jsContext,
361                        self->jsObject,
362                         [_propName cString],
363                        &v);
364   if (!r) {
365     NSLog(@"WARNING: JS_LookupProperty call failed !");
366     return NO;
367   }
368   return v == JSVAL_VOID ? NO : YES;
369 }
370
371 - (BOOL)hasElementAtIndex:(unsigned)_idx {
372   JSBool r;
373   jsval  v;
374   
375   r = JS_LookupElement(self->jsContext,
376                        self->jsObject,
377                        _idx,
378                        &v);
379   if (!r) return NO;
380   return v == JSVAL_VOID ? NO : YES;
381 }
382
383 - (void)setValue:(id)_value ofPropertyNamed:(NSString *)_propName {
384   JSBool ret;
385   jsval  val;
386
387   if (![self->ctx jsValue:&val forObject:_value]) {
388     NSLog(@"WARNING: couldn't convert ObjC value to JS: %@", _value);
389     return;
390   }
391   
392   ret = JS_SetProperty(self->jsContext,
393                        self->jsObject,
394                        [_propName cString],
395                        &val);
396   if (!ret) {
397     NSLog(@"WARNING: couldn't set value of property %@ ", _propName);
398     return;
399   }
400 }
401 - (id)valueOfPropertyNamed:(NSString *)_propName {
402   JSBool ret;
403   jsval  val;
404
405   ret = JS_GetProperty(self->jsContext,
406                        self->jsObject,
407                        [_propName cString],
408                        &val);
409   if (!ret) {
410     NSLog(@"WARNING: couldn't get value of property %@ ", _propName);
411     return nil;
412   }
413
414   if (val == JSVAL_VOID)
415     /* property is not defined */
416     return nil;
417
418   return [self->ctx objectForJSValue:&val];
419 }
420
421 /* scripts */
422
423 - (id)callFunctionNamed:(NSString *)_funcName, ... {
424   va_list  va;
425   unsigned argc, i;
426   JSBool   res;
427   jsval    result;
428   jsval    *argv;
429   id       arg;
430   
431   argc = 0;
432   argv = NULL;
433   
434   va_start(va, _funcName);
435   for (arg = va_arg(va, id); arg; arg = va_arg(va, id))
436     argc++;
437   va_end(va);
438   
439   if (argc > 0) {
440     argv = calloc(argc, sizeof(jsval));
441     va_start(va, _funcName);
442     for (arg = va_arg(va, id), i = 0; arg; arg = va_arg(va, id), i++) {
443       if (![self->ctx jsValue:&(argv[i]) forObject:arg])
444         NSLog(@"couldn't convert func argument !");
445     }
446     va_end(va);
447   }
448   
449   res = JS_CallFunctionName(self->jsContext,
450                             self->jsObject,
451                             [_funcName cString],
452                             argc,
453                             argv,
454                             &result);
455   if (argv) free(argv);
456   
457   if (res)
458     return [self->ctx objectForJSValue:&result];
459   
460   [NSException raise:@"JavaScriptEvalException"
461                format:@"could not call function '%@' !", _funcName];
462   return nil;
463 }
464
465 - (id)evaluateScript:(NSString *)_script {
466   JSBool   res;
467   jsval    lastValue;
468   
469   NSAssert(self->jsObject, @"missing JS object ..");
470   
471   res = JS_EvaluateScript(self->jsContext,
472                           self->jsObject /* script obj */,
473                           [_script cString],
474                           [_script cStringLength],
475                           "<string>",  /* source file */
476                           0,           /* line number */
477                           &lastValue);
478   
479   if (res == JS_TRUE)
480     return [self->ctx objectForJSValue:&lastValue];
481   
482   {
483     NSException  *e;
484     NSDictionary *ui;
485
486     ui = [NSDictionary dictionaryWithObjectsAndKeys:
487                          _script,         @"script",
488                          self,            @"objectHandler",
489                          [NGJavaScriptContext jsContextForHandle:
490                                               self->jsContext], @"jscontext",
491                          nil];
492     
493     e = [[NSException alloc] initWithName:@"JavaScriptEvalException"
494                              reason:@"couldn't evaluate script"
495                              userInfo:ui];
496     [e raise];
497   }
498   return nil;
499 }
500
501 /* static definition declarations */
502
503 static JSBool NGJavaScriptBridge_setStaticProp
504 (JSContext *cx, JSObject *obj, jsval _id, jsval *vp)
505 {
506   NGJavaScriptObjectHandler *self;
507   NGJavaScriptObjCClassInfo *ci;
508   SEL        sel;
509   id         value;
510   
511   if ((self = JS_GetPrivate(cx, obj)) == NULL)
512     return JS_FALSE;
513   
514   ci  = jsClassInfo([self->managedObject class]);
515   sel = [ci setSelectorForPropertyId:&_id inJSContext:cx];
516   
517   if (sel == NULL) {
518     NSLog(@"%s: did not find selector for id !", __PRETTY_FUNCTION__);
519     return JS_FALSE;
520   }
521   
522   value = [self->ctx objectForJSValue:vp];
523   [self->managedObject performSelector:sel withObject:value];
524   
525   return JS_TRUE;
526 }
527 static JSBool NGJavaScriptBridge_getStaticProp
528 (JSContext *cx, JSObject *obj, jsval _id, jsval *vp)
529 {
530   NGJavaScriptObjCClassInfo *ci;
531   NGJavaScriptObjectHandler *self;
532   SEL sel;
533   id  result;
534   
535   if ((self = JS_GetPrivate(cx, obj)) == NULL) {
536     NSLog(@"%s: did not get private of JS object !", __PRETTY_FUNCTION__);
537     return JS_FALSE;
538   }
539   
540   ci  = jsClassInfo([self->managedObject class]);
541   sel = [ci getSelectorForPropertyId:&_id inJSContext:cx];
542   
543   if (sel == NULL) {
544     NSLog(@"%s: did not find selector for id !", __PRETTY_FUNCTION__);
545     return JS_FALSE;
546   }
547   
548   result = [self->managedObject performSelector:sel];
549   //NSLog(@"result is %@", result);
550   
551   return [self->ctx jsValue:vp forObject:result]
552     ? JS_TRUE
553     : JS_FALSE;
554 }
555
556 static JSBool 
557 staticFuncDispatcher
558 (JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
559 {
560   NGJavaScriptObjectHandler *self;
561   NSException *exception;
562   JSFunction  *funobj;
563   const char  *funcName;
564   char        *msgname;
565   SEL         sel;
566   unsigned    i;
567   id          *args;
568   NSArray     *argArray;
569   id          result;
570   JSBool      retcode = 0;
571
572 #if DEBUG && 0
573   {
574     JSClass *clazz;
575     const char *cname;
576
577     if ((clazz = JS_GetClass(obj)))
578       cname = clazz->name;
579     else
580       cname = "<no class>";
581     
582     printf("%s: cx=0x%08X, obj=0x%08X<%s>, argc=%d\n",
583            __PRETTY_FUNCTION__,
584            cx, obj, cname, argc);
585   }
586 #endif
587   
588   if (JS_IsConstructing(cx))
589     obj = JS_GetParent(cx, obj);
590   
591 #if DEBUG
592   if (JS_GetClass(obj) != &NGJavaScriptObjectHandler_JSClass) {
593     NSLog(@"%s: invoked on invalid object class (eg using 'new' ?) !",
594           __PRETTY_FUNCTION__);
595     return JS_FALSE;
596   }
597 #endif
598   
599   if ((self = JS_GetPrivate(cx, obj)) == NULL)
600     return JS_FALSE;
601   
602 #if DEBUG
603   NSCAssert(JS_TypeOfValue(cx, argv[-2]) == JSTYPE_FUNCTION,
604             @"expected function in argv[-2] !");
605 #endif
606   
607   funobj   = JS_GetPrivate(cx, JSVAL_TO_OBJECT(argv[-2]));
608   funcName = JS_GetFunctionName(funobj);
609   
610   msgname = malloc(strlen(funcName) + 10);
611   strcpy(msgname, "_jsfunc_");
612   strcat(msgname, funcName);
613   strcat(msgname, ":");
614 #if APPLE_RUNTIME || NeXT_RUNTIME
615   sel = sel_getUid(msgname); /* TODO: should be registerName? */
616 #else
617   sel = sel_get_any_uid(msgname);
618 #endif
619   
620   if (argc > 0) {
621     args = calloc(argc, sizeof(id));
622     for (i = 0; i < argc; i++) {
623       args[i] =
624         [self->ctx objectForJSValue:&(argv[i])];
625       
626       if (args[i] == nil) args[i] = [EONull null];
627     }
628     argArray = [NSArray arrayWithObjects:args count:argc];
629     free(args);
630   }
631   else
632     argArray = [NSArray array];
633
634 #if 0  
635   NSLog(@"calling function '%s'(%s), %d args %@\n",
636         funcName, msgname, argc, argArray);
637 #endif
638   
639   exception = nil;
640   NS_DURING {
641     result  = [self->managedObject performSelector:sel withObject:argArray];
642     retcode = [self->ctx jsValue:rval forObject:result] ? JS_TRUE : JS_FALSE;
643   }
644   NS_HANDLER {
645     exception = [localException retain];
646   }
647   NS_ENDHANDLER;
648   
649   if (exception) {
650     jsval exval;
651     
652 #if DEBUG
653     NSLog(@"%s: catched exception: %@", __PRETTY_FUNCTION__, exception);
654 #endif
655     retcode   = JS_FALSE;
656     
657     if ([self->ctx jsValue:&exval forObject:exception]) {
658       JS_SetPendingException(cx, exval);
659     }
660     else {
661       NSLog(@"%s: couldn't get JS value for exception: %@",
662             __PRETTY_FUNCTION__, exception);
663     }
664   }
665   
666 #if 0
667   NSLog(@"result is %@", result);
668 #endif
669   
670   return retcode;
671 }
672
673 + (void *)jsStaticFuncDispatcher {
674   return &staticFuncDispatcher;
675 }
676
677 - (BOOL)_applyStaticDefs {
678   NGJavaScriptObjCClassInfo *ci;
679   BOOL ok;
680   
681   if (self->managedObject == nil) {
682     NSLog(@"WARNING(%s): cannot apply defs on nil ...", __PRETTY_FUNCTION__);
683     return NO;
684   }
685   
686   ci = jsClassInfo([self->managedObject class]);
687   
688   IsInPropDefMode = YES;
689   ok = [ci applyOnJSObject:self->jsObject inJSContext:self->jsContext];
690   if (!ok)
691     NSLog(@"ERROR(%s): couldn't apply static defs !", __PRETTY_FUNCTION__);
692   IsInPropDefMode = NO;
693   return ok;
694 }
695
696 - (BOOL)isStaticProperty:(NSString *)_name {
697   NGJavaScriptObjCClassInfo *ci;
698   ci = jsClassInfo([self->managedObject class]);
699   return [ci isStaticProperty:_name];
700 }
701
702 /* misc */
703
704 - (BOOL)loadStandardClasses {
705   NSLog(@"NGJavaScriptObjectHandler: load std classes ...");
706   if (!JS_InitStandardClasses(self->jsContext, self->jsObject)) {
707     NSLog(@"NGJavaScriptObjectHandler: failed to load std classes ...");
708     return NO;
709   }
710   return YES;
711 }
712
713 - (void)makeGlobal {
714   JS_SetGlobalObject(self->jsContext, self->jsObject);
715 }
716
717 /* unknown id */
718
719 - (void)failureUnknownIDType:(jsval)_id {
720 #if DEBUG
721   abort();
722 #else
723   NSLog(@"SERIOUS ERROR(%s:%i): unknown id type ..",
724         __PRETTY_FUNCTION__, __LINE__);
725 #endif
726 }
727
728 /* description */
729
730 - (NSString *)description {
731   NSMutableString *ms;
732   
733   ms = [NSMutableString stringWithCapacity:256];
734   [ms appendFormat:@"<%@[0x%08X]:", NSStringFromClass([self class]), self];
735   [ms appendFormat:@" handle=0x%08X", [self handle]];
736   [ms appendFormat:@" class=%@", [self javaScriptClassName]];
737   [ms appendFormat:@" parent=%@", [self parentObject]];
738   [ms appendString:@">"];
739   
740   return ms;
741 }
742
743 /* JS class methods */
744
745 typedef enum {
746   NGPropOp_add,
747   NGPropOp_del,
748   NGPropOp_set,
749   NGPropOp_get,
750   NGPropOp_resolve
751 } NGPropOp;
752
753 static inline JSBool _propOp(JSContext *cx, NGPropOp op,
754                              JSObject *obj, jsval _id, jsval *vp)
755 {
756   NGJavaScriptObjectHandler *self;
757   BOOL res = NO;
758   
759   if ((self = JS_GetPrivate(cx, obj)) == nil) {
760     NSLog(@"WARNING: missing private in NGJavaScriptObjectHandler JS object !");
761     return JS_PropertyStub(cx, obj, _id, vp);
762   }
763   
764   if (IsInPropDefMode)
765     return JS_PropertyStub(cx, obj, _id, vp);
766
767   NSCAssert2(self->managedObject,
768              @"missing managed object (handler=%@, j0x%08X) !",
769              self, obj);
770   
771   if (JSVAL_IS_INT(_id)) {
772     /* lookup by key */
773     int sel;
774     
775     sel = JSVAL_TO_INT(_id);
776     
777     switch (op) {
778       case NGPropOp_get: {
779         if (NGJavaScriptBridge_LOG_PROP_GET)
780           NSLog(@"JS: get by sel %i", sel);
781         
782         if (sel > 0) {
783           if ([self->managedObject respondsToSelector:
784                    @selector(valueForJSPropertyAtIndex:)]) {
785             id v;
786           
787             v   = [self->managedObject valueForJSPropertyAtIndex:sel];
788             res = [self->ctx jsValue:vp forObject:v];
789           }
790         }
791         break;
792       }
793
794       case NGPropOp_set: {
795         if (NGJavaScriptBridge_LOG_PROP_SET)
796           NSLog(@"JS: set by sel %i", sel);
797         
798         if (sel > 0) {
799           if ([self->managedObject respondsToSelector:
800                    @selector(takeValue:forJSPropertyAtIndex:)]) {
801             id v;
802           
803             v = [self->ctx objectForJSValue:vp];
804           
805             res = [self->managedObject takeValue:v forJSPropertyAtIndex:sel];
806           }
807         }
808         
809         break;
810       }
811       
812       case NGPropOp_resolve:
813 #if LOG_PROP_RESOLVE
814         NSLog(@"RESOLVE '%i'", idx);
815 #endif
816         break;
817         
818       case NGPropOp_del:
819       case NGPropOp_add:
820         NSLog(@"int keys are not supported for this operation (%i) !", op);
821         return JS_FALSE;
822     }
823   }
824   else if (JSVAL_IS_STRING(_id)) {
825     /* lookup by name */
826     NSString *name;
827     JSString *jsname;
828
829     res = YES;
830       
831     jsname = JS_ValueToString(cx, _id);
832     name   = [NSStringClass stringWithJavaScriptString:jsname];
833     
834     switch (op) {
835       case NGPropOp_resolve: {
836 #if LOG_PROP_RESOLVE
837         NSLog(@"RESOLVE '%@'", name);
838 #endif
839 #if 0
840         res  = [self resolvePropertyByName:name];
841 #endif
842         break;
843       }
844       
845       case NGPropOp_del: {
846         if ([self isStaticProperty:name])
847           break;
848         
849         if (NGJavaScriptBridge_LOG_PROP_DEL)
850           NSLog(@"JS: NOT REALLY SUPPORTED del by name %@", name);
851         
852         break;
853       }
854       
855       case NGPropOp_add: {
856         if (NGJavaScriptBridge_LOG_PROP_ADD) {
857           NSLog(@"JS: add by name '%@' type %s "
858                 @"j0x%08X o0x%08X<%@> on o0x%08X<%@>",
859                 name, JS_GetTypeName(cx, JS_TypeOfValue(cx, *vp)),
860                 obj, self, NSStringFromClass([self class]),
861                 self->managedObject,
862                 NSStringFromClass([self->managedObject class]));
863         }
864
865         if ([self->managedObject respondsToSelector:
866                  @selector(takeValue:forJSPropertyNamed:)]) {
867           id v;
868         
869           if ((v = [self->ctx objectForJSValue:vp]) == nil)
870             v = [EONull null];
871         
872           res = [self->managedObject takeValue:v forJSPropertyNamed:name];
873         }
874         
875         break;
876       }
877       
878       case NGPropOp_get: {
879         if ([self isStaticProperty:name])
880           break;
881         
882         if (NGJavaScriptBridge_LOG_PROP_GET) {
883           NSLog(@"JS: get by name '%@' type %s "
884                 @"j0x%08X o0x%08X<%@> on o0x%08X<%@>",
885                 name, JS_GetTypeName(cx, JS_TypeOfValue(cx, *vp)),
886                 obj, self, NSStringFromClass([self class]),
887                 self->managedObject,
888                 NSStringFromClass([self->managedObject class]));
889         }
890         
891         if ([self->managedObject respondsToSelector:
892                  @selector(valueForJSPropertyNamed:)]) {
893           NS_DURING {
894             id v;
895           
896             v   = [self->managedObject valueForJSPropertyNamed:name];
897             
898             if (NGJavaScriptBridge_LOG_PROP_GET) {
899               NSLog(@"  return value o0x%08X<%@>",
900                     v, NSStringFromClass([v class]));
901             }
902             
903             res = [self->ctx jsValue:vp forObject:v];
904           }
905           NS_HANDLER {
906             [[self->ctx jsContext] reportException:localException];
907             res = NO;
908           }
909           NS_ENDHANDLER;
910         }
911         break;
912       }
913       
914       case NGPropOp_set: {
915         if ([self isStaticProperty:name])
916           break;
917         
918         if (NGJavaScriptBridge_LOG_PROP_SET) {
919           NSLog(@"JSObjectHandler: set by name '%@' type %s "
920                 @"j0x%08X o0x%08X<%@> on o0x%08X<%@>",
921                 name, JS_GetTypeName(cx, JS_TypeOfValue(cx, *vp)),
922                 obj, self, NSStringFromClass([self class]),
923                 self->managedObject,
924                 NSStringFromClass([self->managedObject class]));
925         }
926       
927         if ([self->managedObject respondsToSelector:
928                  @selector(takeValue:forJSPropertyNamed:)]) {
929           id v;
930         
931           v   = [self->ctx objectForJSValue:vp];
932           res = [self->managedObject takeValue:v forJSPropertyNamed:name];
933         }
934         break;
935       }
936     }
937   }
938   else {
939     [self failureUnknownIDType:_id];
940     res = NO;
941   }
942   
943   return res ? JS_TRUE : JS_FALSE;
944 }
945
946 static JSBool _addProp(JSContext *cx, JSObject *obj, jsval _id, jsval *vp) {
947   return _propOp(cx, NGPropOp_add, obj, _id, vp);
948 }
949 static JSBool _delProp(JSContext *cx, JSObject *obj, jsval _id, jsval *vp) {
950   return _propOp(cx, NGPropOp_del, obj, _id, vp);
951 }
952 static JSBool _getProp(JSContext *cx, JSObject *obj, jsval _id, jsval *vp) {
953   return _propOp(cx, NGPropOp_get, obj, _id, vp);
954 }
955 static JSBool _setProp(JSContext *cx, JSObject *obj, jsval _id, jsval *vp) {
956   return _propOp(cx, NGPropOp_set, obj, _id, vp);
957 }
958
959 #if USE_ENUM
960 static JSBool
961 _newEnumObj(JSContext *cx, JSObject *obj,
962             JSIterateOp op, jsval *statep, jsid *idp)
963 {
964   NGJavaScriptObjectHandler *self;
965
966   if ((self = JS_GetPrivate(cx, obj)) == nil)
967     return JS_TRUE;
968   else {
969     NSEnumerator *e;
970     
971     NSCAssert(self->managedObject, @"missing managed object !");
972     
973     if (![self->managedObject respondsToSelector:@selector(jsObjectEnumerator)])
974       return JS_TRUE;
975     
976     switch (op) {
977       case JSENUMERATE_INIT:
978         e = [[self->managedObject jsObjectEnumerator] retain];
979         *statep = PRIVATE_TO_JSVAL(e);
980         if (idp) *idp = JSVAL_ZERO;
981         break;
982         
983       case JSENUMERATE_NEXT: {
984         id nextObj;
985         
986         e = JSVAL_TO_PRIVATE(*statep);
987         
988         if ((nextObj = [e nextObject])) {
989           jsval idval;
990           
991           // NSLog(@"next id %@ ..", nextObj);
992 #if 0 // can someone explain that ?
993           if (![self->ctx jsValue:&idval forObject:nextObject])
994             return JS_FALSE;
995 #else
996           idval = INT_TO_JSVAL([nextObj intValue]);
997 #endif
998           
999           if (!JS_ValueToId(cx, idval, idp))
1000             return JS_FALSE;
1001           
1002           break;
1003         }
1004         //NSLog(@"no more IDs ..");
1005         /* else fall through */
1006       }
1007       case JSENUMERATE_DESTROY: {
1008         //NSLog(@"destroying enum ..");
1009         if (*statep != JSVAL_NULL) {
1010           if ((e = JSVAL_TO_PRIVATE(*statep))) {
1011             [e release];
1012             e = nil;
1013           }
1014           *statep = JSVAL_NULL;
1015         }
1016         break;
1017       }
1018     }
1019     return JS_TRUE;
1020   }
1021 }
1022 #endif
1023
1024 #if 0
1025 static JSBool _construct
1026 (JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1027 {
1028   NSLog(@"construct called ..");
1029   return JS_FALSE;
1030 }
1031 #endif
1032
1033 static JSBool _resolve(JSContext *cx, JSObject *obj, jsval _id) {
1034   return _propOp(cx, NGPropOp_resolve, obj, _id, NULL);
1035 }
1036
1037 static JSBool _convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp) {
1038   NGJavaScriptObjectHandler *self;
1039   
1040   if ((self = JS_GetPrivate(cx, obj)) == nil)
1041     return JS_ConvertStub(cx, obj, type, vp);
1042   else {
1043     NSCAssert(self->managedObject, @"missing managed object !");
1044     
1045     switch (type) {
1046       case JSTYPE_VOID:
1047       case JSTYPE_STRING: {
1048         NSString *s;
1049         
1050         s = [self->managedObject stringValue];
1051         return [self->ctx jsValue:vp forObject:s];
1052       }
1053       
1054       case JSTYPE_NUMBER:
1055         *vp = INT_TO_JSVAL([self->managedObject intValue]);
1056         return JS_TRUE;
1057         
1058       case JSTYPE_BOOLEAN:
1059         *vp = [self->managedObject boolValue] ? JSVAL_TRUE : JSVAL_FALSE;
1060         return JS_TRUE;
1061         
1062       case JSTYPE_OBJECT:
1063       case JSTYPE_FUNCTION:
1064       default:
1065         return JS_ConvertStub(cx, obj, type, vp);
1066     }
1067   }
1068 }
1069
1070 static void _finalize(JSContext *cx, JSObject *obj) {
1071   NGJavaScriptObjectHandler *self;
1072   
1073   if ((self = JS_GetPrivate(cx, obj)) == nil) {
1074     JS_FinalizeStub(cx, obj);
1075   }
1076   else if (self->jsObject == obj) {
1077     /* the managed JS object is the same as the finalizing object */
1078     
1079     NSCAssert(self->managedObject, @"missing managed object !");
1080     
1081     if (NGJavaScriptBridge_TRACK_FINALIZATION) {
1082       NSLog(@"finalizing j0x%08X o0x%08X<%@>",
1083             obj, 
1084             self->managedObject, 
1085             NSStringFromClass([self->managedObject class]));
1086     }
1087     
1088     [self->ctx forgetObject:self->managedObject];
1089     
1090 #if DEBUG && 0
1091 #warning RC watch is on !
1092     if ([self retainCount] != 1) {
1093       NSLog(@"WARNING: JS object %@ was collected, "
1094             @"but handle is still live (rc=%d) "
1095             @"(could be pending autorelease refs: pending: %d) !",
1096             self, [self retainCount],
1097             [NSAutoreleasePool autoreleaseCountForObject:self]);
1098     }
1099 #endif
1100     
1101     JS_SetPrivate(self->jsContext, obj, NULL);
1102     self->jsObject = NULL;
1103     
1104     /* release private ref */
1105     NSCAssert(self, @"where got self ??");
1106     [self release]; self = nil;
1107   }
1108   else {
1109 #if DEBUG
1110     fprintf(stderr, "%s: finalization error ...\n", __PRETTY_FUNCTION__);
1111     abort();
1112 #else
1113     fprintf(stderr, "%s: finalization error ...\n", __PRETTY_FUNCTION__);
1114 #endif
1115   }
1116 }
1117
1118 @end /* NGJavaScriptObjectHandler */