2 Copyright (C) 2000-2003 SKYRIX Software AG
4 This file is part of OGo
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
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.
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
23 #include "NGJavaScriptObjectMappingContext.h"
24 #include "NGJavaScriptContext.h"
25 #include "NGJavaScriptObjectHandler.h"
26 #include "NGJavaScriptObject.h"
27 #include "NGJavaScriptShadow.h"
28 #include "NGJavaScriptFunction.h"
29 #include "NSString+JS.h"
33 #if GNUSTEP_BASE_LIBRARY
34 # include <GNUstepBase/behavior.h>
37 @interface NSObject(CombinedObjects)
38 - (void)jsObjectFinalized:(void *)_handle;
39 - (BOOL)_jsGetValue:(void *)_value inJSContext:(NGJavaScriptContext *)_ctx;
40 - (id)_js_parentObject;
43 @interface NGJavaScriptObjectMappingContext(Privates)
44 - (void)_jsFinalizeCombinedObject:(id)_object;
49 NGJavaScriptObjectMappingContext *ctx;
50 NGJavaScriptObjectHandler *handler;
55 extern JSClass ObjCShadow_JSClass;
57 @implementation NGJavaScriptObjectMappingContext
59 static BOOL logHandleForObject = NO;
60 static BOOL logValueConversion = NO;
61 static NSMapTable *combinedToInfo = NULL; // combined objects
64 static BOOL didInit = NO;
65 NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
68 NGJavaScriptBridge_LOG_PROP_DEFINITION
69 = [[ud objectForKey:@"jsLogPropDef"] boolValue];
70 NGJavaScriptBridge_LOG_FUNC_DEFINITION
71 = [[ud objectForKey:@"jsLogFuncDef"] boolValue];
73 logHandleForObject = [ud boolForKey:@"JSLogHandleForObject"];
74 logValueConversion = [ud boolForKey:@"JSLogValueConversion"];
79 - (id)initWithJSContext:(NGJavaScriptContext *)_ctx {
80 if ((self = [super init])) {
81 self->jsContext = [_ctx retain];
83 /* 'combined' ObjC-JS objects */
84 if (combinedToInfo == NULL) {
86 NSCreateMapTable(NSNonOwnedPointerMapKeyCallBacks,
87 NSOwnedPointerMapValueCallBacks,
91 /* 'pure' ObjC objects */
93 NSCreateMapTable(NSObjectMapKeyCallBacks,
94 NSNonOwnedPointerMapValueCallBacks,
97 /* 'pure' JS objects */
99 NSCreateMapTable(NSNonOwnedPointerMapKeyCallBacks,
100 NSNonRetainedObjectMapValueCallBacks,
103 /* make default global */
107 NGJavaScriptObject *glob;
109 glob = [[NGJavaScriptObject alloc] init];
110 [glob applyStandardClasses];
111 [self setGlobalObject:glob];
119 NGJavaScriptContext *ctx;
121 ctx = [[NGJavaScriptContext alloc] init];
122 self = [self initWithJSContext:ctx];
123 [ctx release]; ctx = nil;
128 if (self->jsToObjC) NSFreeMapTable(self->jsToObjC);
129 if (self->objcToJS) NSFreeMapTable(self->objcToJS);
130 [self->jsContext release];
134 - (NGJavaScriptContext *)jsContext {
135 return self->jsContext;
140 - (void)setGlobalObject:(id)_object {
143 glob = [self handleForObject:_object];
144 JS_SetGlobalObject([self->jsContext handle], glob);
149 if ((glob = JS_GetGlobalObject([self->jsContext handle])) == NULL)
152 return [self objectForHandle:glob];
157 - (void *)proxyForObject:(id)_object {
158 /* this is called by handleForObject: */
159 NGJavaScriptObjectHandler *jsHandler;
163 [[NGJavaScriptObjectHandler alloc] initWithObject:_object
164 inMappingContext:self];
166 jso = [jsHandler handle];
168 [jsHandler autorelease];
172 - (id)proxyForPureJSObject:(void *)_handle {
181 /* create a proxy for a 'pure' JavaScript object */
183 cx = [self->jsContext handle];
184 jsClazz = OBJ_GET_CLASS(cx, (JSObject *)_handle);
186 /* use a configurable mapping of JS-class to ObjC class here ? */
187 if (jsClazz == &js_ArrayClass)
188 proxyClass = [NGJavaScriptArray class];
189 else if (jsClazz == &js_FunctionClass)
190 proxyClass = [NGJavaScriptFunction class];
192 proxyClass = [NGJavaScriptObject class];
194 proxy = [[proxyClass alloc] initWithHandle:_handle inMappingContext:self];
195 return AUTORELEASE(proxy);
200 - (void *)handleForObject:(id)_object {
202 What is the _object in this context ?
203 - I guess it's a NGJavaScriptObject
204 - often, but not always, see testjs: called with Blah and MyNum
205 - only "custom" objects, not NGJavaScriptObject are registered !
207 - it checks whether the object itself can return handle
208 (_jsHandleInMapContext:)
209 - this seems always true for NGJavaScriptObject's !
210 - checks, whether a proxy is already registered
211 - I guess a proxy is a JS-object with an attached JSObjectHandler ?
212 - otherwise create a new one
214 - check parent object of new one
216 JSCombinedObjInfo *combinedObjInfo;
220 if (logHandleForObject)
221 NSLog(@"-proxyForObject:0x%08X %@", _object, _object);
223 if (_object == nil) {
224 jso = JSVAL_TO_OBJECT(JSVAL_NULL);
225 if (logHandleForObject) NSLog(@" => is nil: 0x%08X", jso);
229 if ([_object respondsToSelector:@selector(_jsHandleInMapContext:)]) {
230 jso = [_object _jsHandleInMapContext:self];
231 if (logHandleForObject) {
232 NSLog(@" obj (class %@) handles itself: 0x%08X",
233 NSStringFromClass([_object class]), jso);
238 if ((jso = NSMapGet(self->objcToJS, _object))) {
239 /* a proxy is already registered */
240 if (logHandleForObject) NSLog(@" proxy already registered: 0x%08X", jso);
244 if ((combinedObjInfo = NSMapGet(combinedToInfo, _object))) {
245 /* check for correct context */
246 if (combinedObjInfo->ctx != self) {
247 NSLog(@"%s: tried to access combined object 0x%08X<%@> in "
248 @"different mapping context (ctx=0x%08X, required=0x%08X) !",
249 __PRETTY_FUNCTION__, _object, NSStringFromClass([_object class]),
250 self, combinedObjInfo->ctx);
253 if (logHandleForObject) NSLog(@" proxy is combined object: 0x%08X", jso);
254 return combinedObjInfo->jso;
257 /* create a new proxy */
259 if (logHandleForObject) NSLog(@" creating proxy ...");
260 if ((jso = [self proxyForObject:_object])) {
263 NSAssert1(NSMapGet(self->objcToJS, _object) == NULL,
264 @"already registered a proxy for object o0x%08X", _object);
266 if (logHandleForObject) NSLog(@" register handle 0x%08X ...", jso);
267 NSMapInsertKnownAbsent(self->objcToJS, _object, jso);
270 NSLog(@"ERROR(%s): proxy creation failed: %@",
271 __PRETTY_FUNCTION__, _object);
275 /* look for parent of new proxy */
277 if ((parent = [_object _js_parentObject])) {
281 if (logHandleForObject)
282 NSLog(@"register parent 0x%08X for object .."), parent;
284 pjso = [self handleForObject:parent];
286 res = JS_SetParent([self->jsContext handle], jso, pjso);
288 if (res == JS_FALSE) {
289 NSLog(@"WARNING: ctx %@ couldn't register JS parent %@ on object %@",
290 self, parent, _object);
297 - (void)registerObject:(id)_object forImportedHandle:(void *)_handle {
299 NSAssert(_object, @"missing object");
300 NSAssert(_handle, @"missing handle");
302 NSMapInsertKnownAbsent(self->jsToObjC, _handle, _object);
305 - (id)objectForHandle:(void *)_handle {
307 What does it do ? What is the return value ? TODO: Document !
309 extern JSClass NGJavaScriptObjectHandler_JSClass;
310 JSClass *handleClass;
316 if ((handleClass = JS_GetClass(_handle)) == NULL) {
317 NSLog(@"couldn't get class of handle 0x%08X", (unsigned)_handle);
321 /* check for 'reflected' JavaScript objects (combined or ObjC exported) */
323 if (handleClass == &NGJavaScriptObjectHandler_JSClass) {
324 NGJavaScriptObjectHandler *h;
326 if ((h = JS_GetPrivate([self->jsContext handle], _handle)) == nil) {
327 NSLog(@"couldn't get private of JS object 0x%08X "
328 @"(NGJavaScriptObjectHandler)", _handle);
332 return AUTORELEASE(RETAIN([h managedObject]));
335 if (handleClass == &ObjCShadow_JSClass) {
336 NGJavaScriptShadow *h;
338 if ((h = JS_GetPrivate([self->jsContext handle], _handle)) == nil) {
339 NSLog(@"couldn't get private of JS shadow object 0x%08X "
340 @"(NGJavaScriptShadow)", _handle);
344 return AUTORELEASE(RETAIN([h masterObject]));
347 /* check for 'pure' JavaScript objects */
349 if ((obj = NSMapGet(self->jsToObjC, _handle)))
351 return AUTORELEASE(RETAIN(obj));
353 if ((obj = [self proxyForPureJSObject:_handle])) {
355 [self registerObject:obj forImportedHandle:_handle];
359 /* couldn't build a proxy */
363 - (void)forgetObject:(id)_object {
364 JSCombinedObjInfo *combinedObjInfo;
366 NSAssert(_object, @"missing object ..");
368 if ((combinedObjInfo = NSMapGet(combinedToInfo, _object))) {
369 if (combinedObjInfo->ctx != self) {
370 NSLog(@"forget combined object 0x%08X in wrong context !", _object);
373 [self _jsFinalizeCombinedObject:_object];
376 if (NGJavaScriptBridge_TRACK_FORGET) {
379 jso = NSMapGet(self->objcToJS, _object);
380 NSLog(@"forgetting non-combined object o0x%08X<%@> j0x%08X rc %d",
381 _object, NSStringFromClass([_object class]),
383 [_object retainCount]);
386 NSMapRemove(self->objcToJS, _object);
389 [self->jsContext performSelector:@selector(collectGarbage)
395 - (void)forgetImportedHandle:(void *)_handle {
396 NSMapRemove(self->jsToObjC, _handle);
401 - (NGJavaScriptObjectHandler *)handlerForObject:(id)_object {
404 if ((jso = [self handleForObject:_object]) == NULL) {
405 NSLog(@"did not find handle for object 0x%08X", _object);
409 return JS_GetPrivate([self->jsContext handle], jso);
412 /* garbage collection */
415 [self->jsContext collectGarbage];
416 return [super popContext];
419 - (void)collectGarbage {
421 [self->jsContext collectGarbage];
427 - (void)_logExportedJavaScriptObjects {
433 if (NSCountMapTable(jsToObjC) < 1) {
434 printf("no imported JavaScript objects.\n");
438 e = NSEnumerateMapTable(jsToObjC);
439 printf("imported JavaScript objects:\n");
440 printf(" %-10s %-20s %-10s %-26s %-2s\n",
446 while (NSNextMapEnumeratorPair(&e, (void*)&jso, (void*)&proxy)) {
447 jsClass = jso ? JS_GetClass(jso) : NULL;
449 printf(" 0x%08X %-20s 0x%08X %-26s %2d\n",
451 jsClass ? jsClass->name : "<null>",
453 [NSStringFromClass([proxy class]) cString],
454 [proxy retainCount]);
458 - (void)_logExportedObjCObjects {
463 if (NSCountMapTable(objcToJS) < 1) {
464 printf("no exported Objective-C objects.\n");
468 printf("exported Objective-C objects:\n");
469 printf(" %-10s %-20s %-10s %-26s %-2s\n",
475 e = NSEnumerateMapTable(objcToJS);
476 while (NSNextMapEnumeratorPair(&e, (void*)&object, (void*)&jsProxy)) {
479 jsClass = jsProxy ? JS_GetClass(jsProxy) : NULL;
481 printf(" 0x%08X %-20s 0x%08X %-26s %2d\n",
483 [NSStringFromClass([object class]) cString],
485 jsClass ? jsClass->name : "<null>",
486 [object retainCount]);
492 - (id)objectForJSValue:(void *)_value {
497 couldConvert = JS_FALSE;
499 if (JSVAL_IS_NULL(*(jsval *)_value))
502 cx = [self->jsContext handle];
503 jsType = JS_TypeOfValue(cx, *(jsval *)_value);
508 case JSTYPE_FUNCTION:
509 case JSTYPE_OBJECT: {
512 if (!(couldConvert = JS_ValueToObject(cx, *(jsval *)_value, &obj)))
515 return [self objectForHandle:obj];
519 case JSTYPE_FUNCTION: {
522 if ((func = JS_ValueToFunction(cx, *(jsval *)_value))) {
523 static Class FuncClass = Nil;
525 if (FuncClass == Nil)
526 FuncClass = NSClassFromString(@"NGJavaScriptFunction");
528 NSAssert(FuncClass, @"missing JS function class ..");
530 return AUTORELEASE([[FuncClass alloc] initWithHandle:func
531 mappingContext:self]);
534 NSLog(@"%s: couldn't get JS function ..", __PRETTY_FUNCTION__);
541 case JSTYPE_STRING: {
544 if ((s = JS_ValueToString(cx, *(jsval *)_value))) {
545 return [NSString stringWithJavaScriptString:s];
554 if (JSVAL_IS_INT(*(jsval *)_value)) {
557 if ((couldConvert = JS_ValueToInt32(cx, *(jsval *)_value, &i)))
558 return [NSNumber numberWithInt:i];
563 if ((couldConvert = JS_ValueToNumber(cx, *(jsval *)_value, &d)))
564 return [NSNumber numberWithDouble:d];
568 case JSTYPE_BOOLEAN: {
571 couldConvert = JS_ValueToBoolean(cx, *(jsval *)_value, &b);
573 return [NSNumber numberWithBool:b ? YES : NO];
578 [NSException raise:@"InvalidJavaScriptTypeException"
579 format:@"JavaScript value has unknown type %i !", jsType];
583 [NSException raise:@"JavaScriptTypeConvertException"
584 format:@"Could not convert JavaScript value of type %i !",
591 - (BOOL)jsValue:(void *)_value forObject:(id)_obj {
593 This is used to convert ObjC object _obj to a JSVAL.
596 - _obj is a proxy for a JavaScript object (eg NGJavaScriptObject),
597 the proxy will return the value itself
598 - _obj is a primitive Foundation object (eg NSString), the object
599 will convert itself to a primitiv JavaScript type using
600 _jsGetValue:inJSContext:
601 - _obj is a complex object, it will be mapped to a proxy JSObject
603 Primitive types seem to be broken in certain cases right now.
606 *(jsval *)_value = JSVAL_NULL;
610 if ([_obj respondsToSelector:@selector(_jsGetValue:inJSContext:)]) {
611 if (logValueConversion) {
612 NSLog(@"%s(0x%08X, 0x%08X<%@>) => own handling ..",
614 _value, _obj, NSStringFromClass([_obj class]));
616 /* eg this is called on NSString */
617 return [_obj _jsGetValue:_value inJSContext:self->jsContext];
622 if (logValueConversion) {
623 NSLog(@"%s(0x%08X, 0x%08X<%@>) => get handle ..",
625 _value, _obj, NSStringFromClass([_obj class]));
628 if ((jso = [self handleForObject:_obj]) == NULL)
631 *((jsval *)_value) = OBJECT_TO_JSVAL(jso);
635 if (logValueConversion) {
636 NSLog(@"%s(0x%08X, 0x%08X<%@>) => missing value store ?",
638 _value, _obj, NSStringFromClass([_obj class]));
644 @end /* NGJavaScriptObjectMappingContext */
646 @implementation NGJavaScriptObjectMappingContext(CombinedObjects)
648 /* combined objects */
650 - (void)makeObjectCombined:(id)_object {
653 JSCombinedObjInfo *combinedObjInfo;
656 if (NSMapGet(combinedToInfo, _object))
657 /* object is already a combined one */
660 oldRC = [_object retainCount];
661 clazz = [_object class];
663 handler = [[NGJavaScriptObjectHandler alloc]
664 initWithObject:_object
665 inMappingContext:self];
667 if (![clazz isJSCombinedObjectClass]) {
668 // TODO: is this correct shouldn't we add combined behaviour only
669 // *on* combined classes ??, explain !
671 #if NeXT_RUNTIME || APPLE_RUNTIME
672 NSLog(@"ERROR(%s): combined objects not supported on this runtime!",
673 __PRETTY_FUNCTION__);
674 /* TODO: port to MacOSX */
676 static Class BehaviourClass = Nil;
677 BehaviourClass = NSClassFromString(@"JSCombinedObjectBehaviour");
678 NSAssert(BehaviourClass, @"did not find JSCombinedObjectBehaviour !");
679 #if GNUSTEP_BASE_LIBRARY
680 behavior_class_add_class(clazz, BehaviourClass);
682 class_add_behavior(clazz, BehaviourClass);
687 combinedObjInfo = calloc(1, sizeof(JSCombinedObjInfo));
688 combinedObjInfo->jso = [handler handle];
689 combinedObjInfo->handler = handler;
690 combinedObjInfo->ctx = self;
691 combinedObjInfo->rc = oldRC; // -1 ???
693 combinedObjInfo->rootRef = YES;
695 AUTORELEASE(handler);
697 NSMapInsertKnownAbsent(combinedToInfo, _object, combinedObjInfo);
699 if (NGJavaScriptBridge_TRACK_MEMORY) {
700 NSLog(@"combine: o0x%08X<%@>->j0x%08X "
701 @"(handler=0x%08X, old-rc=%d, new-rc=%d)",
702 _object, NSStringFromClass([_object class]),
703 combinedObjInfo->jso, combinedObjInfo->handler, oldRC, [_object retainCount]);
707 NSAssert([_object isJSCombinedObject], @"still not a combined object !");
711 - (BOOL)isCombinedObject:(id)_object {
712 return NSMapGet(combinedToInfo, _object) ? YES : NO;
715 - (void)_logCombinedObjects {
718 JSCombinedObjInfo *combinedObjInfo;
720 printf("Combined objects:\n");
721 printf(" %-10s %-16s %-2s %-10s %-10s %-10s %-2s %-2s\n",
730 e = NSEnumerateMapTable(combinedToInfo);
731 while (NSNextMapEnumeratorPair(&e, (void*)&object, (void*)&combinedObjInfo)) {
732 printf(" 0x%08X %-16s %2d 0x%08X 0x%08X 0x%08X %2d %2d\n",
733 (unsigned)object, [NSStringFromClass([object class]) cString],
734 [object retainCount],
735 (unsigned)combinedObjInfo->jso,
736 (unsigned)combinedObjInfo->ctx,
737 (unsigned)combinedObjInfo->handler,
738 [combinedObjInfo->handler jsRootRetainCount],
739 [combinedObjInfo->handler retainCount]);
743 - (void)_jsFinalizeCombinedObject:(id)_object {
745 This should never be called if ObjC RC > 0 !, since the ObjC object
746 keeps a root-ref to the JS object !
748 JSCombinedObjInfo *combinedObjInfo;
750 if (_object == nil) return;
752 if ((combinedObjInfo = NSMapGet(combinedToInfo, _object))) {
753 NSAssert(combinedObjInfo->ctx == self, @"invalid ctx for combined finalization !");
755 if (combinedObjInfo->rc == 0) {
756 if (combinedObjInfo->rootRef) {
757 [combinedObjInfo->handler jsRelease];
758 combinedObjInfo->rootRef = NO;
761 if (NGJavaScriptBridge_TRACK_MEMORY) {
762 NSLog(@"FREEING COMBINED OBJECT o%08X<%@>-j%08X (handler 0x%08X).",
763 _object, NSStringFromClass([_object class]),
764 combinedObjInfo->jso, combinedObjInfo->handler);
767 NSMapRemove(combinedToInfo, _object);
768 combinedObjInfo = NULL;
770 /* deallocate Objective-C memory of object */
774 NSLog(@"WARNING: finalized JS object, but handler RC > 0 !");
779 @end /* NGJavaScriptObjectMappingContext */
781 @implementation NSObject(JSCombinedObjects)
783 + (BOOL)isJSCombinedObjectClass {
787 - (BOOL)isJSCombinedObject {
790 - (NGJavaScriptObjectMappingContext *)jsObjectMappingContext {
794 @end /* NSObject(JSCombinedObjects) */
796 @implementation JSCombinedObjectBehaviour
798 - (NGJavaScriptObjectMappingContext *)jsObjectMappingContext {
799 JSCombinedObjInfo *combinedObjInfo;
801 if ((combinedObjInfo = NSMapGet(combinedToInfo, self)) == NULL)
804 return combinedObjInfo->ctx;
807 + (BOOL)isJSCombinedObjectClass {
811 - (BOOL)isJSCombinedObject {
812 return NSMapGet(combinedToInfo, self) ? YES : NO;
815 /* retain-counting */
818 JSCombinedObjInfo *combinedObjInfo;
820 if ((combinedObjInfo = NSMapGet(combinedToInfo, self)) == NULL) {
821 if (NGJavaScriptBridge_TRACK_NOINFO_MEMORY) {
822 NSLog(@"CO: NO INFO retain: o%08X<%@>, rc=%d",
823 self, NSStringFromClass([self class]), [self retainCount]);
825 return [super retain];
828 if (combinedObjInfo->handler == nil) {
829 if (NGJavaScriptBridge_TRACK_MEMORY) {
830 NSLog(@"CO: NO HANDLER retain: o%08X<%@>-j0x%08X, rc=%d",
831 self, NSStringFromClass([self class]),
832 combinedObjInfo->jso, [self retainCount]);
834 return [super retain];
837 if (combinedObjInfo->rc == 0) {
838 /* life, but not specially retained (RC=1) */
840 if (!combinedObjInfo->rootRef) {
841 /* ensure that the JS object is life */
842 [combinedObjInfo->handler jsRetain];
843 combinedObjInfo->rootRef = YES;
846 combinedObjInfo->rc++;
848 if (NGJavaScriptBridge_TRACK_MEMORY_RC) {
849 NSLog(@"CO: retain: o%08X<%@>-j%08X (handler=0x%08X), rc=%d, root-rc=%d",
850 self, NSStringFromClass([self class]), combinedObjInfo->jso, combinedObjInfo->handler,
851 combinedObjInfo->rc, [combinedObjInfo->handler jsRootRetainCount]);
857 - (oneway void)release {
858 JSCombinedObjInfo *combinedObjInfo;
860 if ((combinedObjInfo = NSMapGet(combinedToInfo, self)) == NULL) {
861 if (NGJavaScriptBridge_TRACK_NOINFO_MEMORY)
862 NSLog(@"CO: NO INFO release: o%08X, rc=%d", self, [self retainCount]);
868 if (combinedObjInfo->handler == nil) {
869 if (NGJavaScriptBridge_TRACK_MEMORY) {
870 NSLog(@"CO: NO HANDLER release: o%08X<%@>-j0x%08X, rc=%d",
871 self, NSStringFromClass([self class]),
872 combinedObjInfo->jso, [self retainCount]);
879 if (NGJavaScriptBridge_TRACK_MEMORY_RC) {
880 NSLog(@"CO: release: o%08X<%@>-j%08X (handler=0x%08X), rc=%d, root-rc=%d",
881 self, NSStringFromClass([self class]), combinedObjInfo->jso, combinedObjInfo->handler,
882 [self retainCount], [combinedObjInfo->handler jsRootRetainCount]);
884 NSAssert1(combinedObjInfo->handler,
885 @"missing handler for combined object 0x%08X ..", self);
889 this does never dealloc the ObjC object - the ObjC object is deallocated
890 in the JS destructor !
893 combinedObjInfo->rc--;
895 if (combinedObjInfo->rc == 0) {
896 /* not specially retained in the ObjC side anymore */
898 /* JS object is still live, release our root-ref .. */
899 NSAssert(combinedObjInfo->rootRef, @"missing JS root-reference");
900 [combinedObjInfo->handler jsRelease];
901 combinedObjInfo->rootRef = NO;
903 if (NGJavaScriptBridge_TRACK_MEMORY) {
904 NSLog(@"%s: released last ObjC reference of o%08X-j%08X, %d root-refs ..",
906 self, combinedObjInfo->jso, [combinedObjInfo->handler jsRootRetainCount]);
909 [combinedObjInfo->ctx performSelector:@selector(collectGarbage)
915 - (unsigned)retainCount {
916 JSCombinedObjInfo *combinedObjInfo;
918 if ((combinedObjInfo = NSMapGet(combinedToInfo, self)) == NULL)
919 return [super retainCount];
921 if (combinedObjInfo->handler == nil)
922 return [super retainCount];
924 return combinedObjInfo->rc;
929 - (id)evaluateScript:(NSString *)_js language:(NSString *)_language {
930 JSCombinedObjInfo *combinedObjInfo;
932 if ((combinedObjInfo = NSMapGet(combinedToInfo, self)) == NULL) {
934 return [[[NGJavaScriptObjectMappingContext activeObjectMappingContext]
935 handlerForObject:self]
939 return [combinedObjInfo->handler evaluateScript:_js];
941 - (id)evaluateJavaScript:(NSString *)_js {
943 return [self evaluateScript:_js language:@"javascript"];
946 @end /* JSCombinedObjectBehaviour */