2 Copyright (C) 2000-2004 SKYRIX Software AG
4 This file is part of OpenGroupware.org.
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 "NGJavaScriptObject.h"
24 #include "NGJavaScriptContext.h"
25 #include <NGExtensions/NGExtensions.h>
26 #include <NGScripting/NGObjectMappingContext.h>
27 #include "NGJavaScriptObjectMappingContext.h"
31 //#define GREEDY_ARCHIVE 1
33 @interface JSIDEnum : NSEnumerator
35 NGJavaScriptObjectMappingContext *ctx;
41 - (id)initWithIdArray:(JSIdArray *)_array
42 mappingContext:(NGJavaScriptObjectMappingContext *)_ctx;
43 - (id)initWithIdArray:(JSIdArray *)_array
45 mappingContext:(NGJavaScriptObjectMappingContext *)_ctx;
48 @interface JSObjChainEnum : NSEnumerator
53 - (id)initWithObject:(id)_obj selector:(SEL)_sel;
56 @implementation NGJavaScriptObject
58 - (id)initWithHandle:(void *)_handle
59 inMappingContext:(NGObjectMappingContext *)_ctx
61 NSAssert(_handle, @"Missing handle ..");
62 NSAssert(_ctx, @"Missing context ..");
64 self->handle = _handle;
65 self->ctx = [_ctx retain];
68 [[(NGJavaScriptObjectMappingContext *)self->ctx jsContext] handle];
71 JS_AddNamedRoot(self->jscx, &(self->handle), self->isa->name);
72 NSAssert(self->addedRoot, @"couldn't add root !");
77 + (void *)jsObjectClass {
81 - (void *)createJSObjectForJSClass:(void *)_class inJSContext:(void *)jsctx {
82 return JS_NewObject(jsctx, _class, NULL, NULL);
85 - (id)initWithJSClass:(void *)_class {
86 NGJavaScriptObjectMappingContext *mctx;
88 if ((mctx = [NGJavaScriptObjectMappingContext activeObjectMappingContext])) {
92 jsctx = [[mctx jsContext] handle];
94 if ((jso = [self createJSObjectForJSClass:_class inJSContext:jsctx])) {
95 self = [self initWithHandle:jso inMappingContext:mctx];
96 [mctx registerObject:self forImportedHandle:jso];
100 [self release]; self = nil;
102 [NSException raise:@"NGJavaScriptException"
103 format:@"couldn't create JS object .."];
108 [NSException raise:@"NGJavaScriptException"
109 format:@"missing active mapping context !"];
113 return [self initWithJSClass:[[self class] jsObjectClass]];
117 if (NGJavaScriptBridge_TRACK_MEMORY) {
118 NSLog(@"%s: dealloc o0x%08X j0x%08X ctx=0x%08X jcx=0x%08X",
119 __PRETTY_FUNCTION__, self, self->handle,
120 self->ctx, self->jscx);
125 JS_RemoveRoot(self->jscx, &self->handle);
126 [self->ctx forgetImportedHandle:self->handle];
130 NSLog(@"%s: missing handle !, couldn't remove root ..", __PRETTY_FUNCTION__);
139 - (BOOL)_jsGetValue:(void *)_value inJSContext:(NGJavaScriptContext *)_ctx {
140 *((jsval *)_value) = OBJECT_TO_JSVAL(self->handle);
143 - (void *)_jsHandleInMapContext:(NGObjectMappingContext *)_ctx {
149 - (void)applyStandardClasses {
150 if (!JS_InitStandardClasses(self->jscx, self->handle)) {
151 NSLog(@"couldn't load standard classes into JS object %@", self);
156 - (void)setParentObject:(id)_parent {
159 p = [self->ctx handleForObject:_parent];
161 if (JS_SetParent(self->jscx, self->handle, p) == JS_FALSE) {
162 NSLog(@"couldn't set parent of object %@", self);
168 if ((p = JS_GetParent(self->jscx, self->handle)) == NULL)
171 return [self->ctx objectForHandle:p];
174 - (NSEnumerator *)parentObjectChain {
177 e = [[JSObjChainEnum alloc]
178 initWithObject:self selector:@selector(parentObject)];
179 return [e autorelease];
182 - (void)setPrototypeObject:(id)_proto {
185 p = [self->ctx handleForObject:_proto];
187 if (JS_SetPrototype(self->jscx, self->handle, p) == JS_FALSE) {
188 NSLog(@"couldn't set prototype of object %@", self);
191 - (id)prototypeObject {
194 if ((p = JS_GetPrototype(self->jscx, self->handle)) == NULL)
197 return [self->ctx objectForHandle:p];
200 - (NSEnumerator *)prototypeObjectChain {
203 e = [[JSObjChainEnum alloc]
204 initWithObject:self selector:@selector(prototypeObject)];
205 return AUTORELEASE(e);
208 - (BOOL)hasPropertyNamed:(NSString *)_key {
210 jsval val = JSVAL_VOID;
214 clen = [_key cStringLength];
215 ckey = malloc(clen + 1);
216 [_key getCString:ckey];
218 ret = JS_LookupProperty(self->jscx, self->handle, ckey, &val);
219 if (ret == JS_FALSE) {
220 NSLog(@"%s: WARNING: couldn't lookup property '%@'",
221 __PRETTY_FUNCTION__, _key);
225 if (val == JSVAL_VOID)
231 - (BOOL)hasFunctionNamed:(NSString *)_key {
233 jsval val = JSVAL_VOID;
238 clen = [_key cStringLength];
239 ckey = malloc(clen + 1);
240 [_key getCString:ckey];
242 ret = JS_GetProperty(self->jscx, self->handle, ckey, &val);
243 if (ret == JS_FALSE) {
244 NSLog(@"WARNING: couldn't lookup property '%@'", _key);
248 if (val == JSVAL_VOID)
251 jsType = JS_TypeOfValue(self->jscx, val);
253 return jsType == JSTYPE_FUNCTION ? YES : NO;
258 - (BOOL)isJavaScriptFunction {
262 val = OBJECT_TO_JSVAL(self->handle);
263 if (val == JSVAL_VOID)
266 jsType = JS_TypeOfValue(self->jscx, val);
268 return jsType == JSTYPE_FUNCTION ? YES : NO;
270 - (BOOL)isScriptFunction {
271 return [self isJavaScriptFunction];
274 - (id)_callOn:(id)_this argc:(int)_argc argv:(jsval *)_argv {
280 val = OBJECT_TO_JSVAL(self->handle);
281 jso = [self->ctx handleForObject:_this];
283 ret = JS_CallFunctionValue(self->jscx, jso, val,
287 return [self->ctx objectForJSValue:&result];
289 NSLog(@"%s: couldn't run function %@", __PRETTY_FUNCTION__, self);
292 - (id)callOn:(id)_this {
293 return [self _callOn:_this argc:0 argv:NULL];
295 - (id)callOn:(id)_this withObject:(id)_arg0 {
298 if ([self->ctx jsValue:&arg0 forObject:_arg0])
299 return [self _callOn:_this argc:1 argv:&arg0];
301 NSLog(@"%s: couldn't convert arg0 %@ for function %@", __PRETTY_FUNCTION__,
306 /* mimic dictionary */
308 - (NSArray *)allKeys {
311 NSMutableArray *keys;
313 if ((e = [self keyEnumerator]) == nil) return nil;
314 keys = [NSMutableArray arrayWithCapacity:8];
315 while ((key = [e nextObject]))
316 [keys addObject:key];
319 - (NSArray *)allValues {
322 NSMutableArray *keys;
324 if ((e = [self objectEnumerator]) == nil) return nil;
325 keys = [NSMutableArray arrayWithCapacity:8];
326 while ((object = [e nextObject]))
327 [keys addObject:object];
331 - (NSEnumerator *)keyEnumerator {
335 if ((idArray = JS_Enumerate(self->jscx, self->handle)) == NULL) {
336 NSLog(@"couldn't enumerate object ..");
340 e = [[JSIDEnum alloc] initWithIdArray:idArray mappingContext:self->ctx];
341 return AUTORELEASE(e);
343 - (NSEnumerator *)objectEnumerator {
347 if ((idArray = JS_Enumerate(self->jscx, self->handle)) == NULL) {
348 NSLog(@"couldn't enumerate object ..");
352 e = [[JSIDEnum alloc] initWithIdArray:idArray
354 mappingContext:self->ctx];
355 return AUTORELEASE(e);
358 - (void)setObject:(id)_obj forStringKey:(NSString *)_key {
364 if (![self->ctx jsValue:&v forObject:_obj]) {
365 NSLog(@"WARNING: couldn't convert ObjC value to JS: %@", _obj);
369 clen = [_key cStringLength];
370 ckey = malloc(clen + 1);
371 [_key getCString:ckey];
373 res = JS_LookupProperty(self->jscx, self->handle, ckey, &lv);
374 if (res == JS_FALSE) {
375 NSLog(@"WARNING: couldn't lookup property '%@'", _key);
380 if (lv == JSVAL_VOID) {
381 /* property does not exist */
382 res = JS_DefineProperty(self->jscx, self->handle, ckey, v,
385 JSPROP_ENUMERATE|JSPROP_EXPORTED);
388 /* property does exist */
389 res = JS_SetProperty(self->jscx, self->handle, ckey, &v);
392 free(ckey); ckey = NULL;
394 if (res == JS_FALSE) {
395 NSLog(@"WARNING: couldn't set ObjC value %@ to JS %@", _obj, _key);
400 - (id)objectForStringKey:(NSString *)_key {
402 jsval val = JSVAL_VOID;
406 clen = [_key cStringLength];
407 ckey = malloc(clen + 1);
408 [_key getCString:ckey];
410 ret = JS_GetProperty(self->jscx, self->handle, ckey, &val);
411 if (ret == JS_FALSE) {
412 NSLog(@"WARNING(%s): couldn't get value of property %@ ",
413 __PRETTY_FUNCTION__, _key);
418 free(ckey); ckey = NULL;
420 if (val == JSVAL_VOID) {
421 /* property is not defined */
423 NSLog(@"%s: got void for key '%s' o0x%08X j0x%08X",
425 ckey, self, self->handle);
430 return [self->ctx objectForJSValue:&val];
433 - (void)removeObjectForStringKey:(NSString *)_key {
436 ret = JS_DeleteProperty(self->jscx, self->handle, [_key cString]);
437 if (ret == JS_FALSE) {
438 NSLog(@"WARNING: couldn't delete property %@ ", _key);
443 - (BOOL)isStringKey:(id)_key {
444 return [_key isKindOfClass:[NSString class]];
446 - (id)unableToHandleKey:(id)_key {
447 NSLog(@"Unable to handle key: %@\n key class: %@\n object: %@\n object class: %@",
448 _key, [_key class], self, [self class]);
452 - (id)objectForKey:(id)_key {
453 if ([self isStringKey:_key])
454 return [self objectForStringKey:_key];
456 return [self unableToHandleKey:_key];
458 - (void)setObject:(id)_obj forKey:(id)_key {
459 if ([self isStringKey:_key]) {
460 [self setObject:_obj forStringKey:(id)_key];
463 [self unableToHandleKey:_key];
465 - (void)removeObjectForKey:(id)_key {
466 if ([self isStringKey:_key])
467 [self removeObjectForStringKey:_key];
469 [self unableToHandleKey:_key];
472 /* convert to dictionary */
474 - (NSDictionary *)convertToNSDictionary {
475 /* could be made far more efficient ... */
478 NSMutableDictionary *dict;
480 if ((e = [self keyEnumerator]) == nil) return nil;
482 dict = [NSMutableDictionary dictionaryWithCapacity:16];
483 while ((key = [e nextObject])) {
484 id value = [self objectForKey:key];
486 [dict setObject:value?value:[NSNull null] forKey:key];
493 - (void)takeValue:(id)_value forKey:(NSString *)_key {
498 [self setObject:_value forKey:_key];
500 - (id)valueForKey:(NSString *)_key {
501 return [self objectForKey:_key];
511 JS_SetGlobalObject(self->jscx, self->handle);
514 - (NSString *)javaScriptClassName {
515 if (self->handle == nil)
517 return [NSString stringWithCString:JS_GetClass(self->handle)->name];
522 - (void)decodeJavaScriptPropertiesWithCoder:(NSCoder *)_coder {
527 props = [_coder decodeObject];
529 keys = [props keyEnumerator];
530 while ((key = [keys nextObject])) {
531 id value = [props objectForKey:key];
533 if ([value isNotNull])
534 [self setObject:value forKey:key];
536 [self setObject:nil forKey:key];
539 - (void)encodeJavaScriptPropertiesWithCoder:(NSCoder *)_coder {
540 NSMutableDictionary *props;
544 props = [NSMutableDictionary dictionaryWithCapacity:16];
545 keys = [self keyEnumerator];
546 while ((key = [keys nextObject])) {
549 if ((value = [self objectForKey:key])) {
550 if ([value isJavaScriptFunction]) {
551 [self debugWithFormat:@"did not encode JS function object: %@", value];
555 [props setObject:value forKey:key];
558 [props setObject:[NSNull null] forKey:key];
560 [_coder encodeObject:props];
563 - (id)initWithCoder:(NSCoder *)_coder {
564 if ((self = [self init])) {
566 id lParent, lPrototype;
568 jsClass = [_coder decodeObject];
569 lParent = [_coder decodeObject];
570 lPrototype = [_coder decodeObject];
572 [self setParentObject:lParent];
573 [self setPrototypeObject:lPrototype];
575 [self decodeJavaScriptPropertiesWithCoder:_coder];
577 if (![[self javaScriptClassName] isEqualToString:jsClass]) {
578 [self logWithFormat:@"WARNING: decoded object is not JS class %@ !!", jsClass];
583 - (void)encodeWithCoder:(NSCoder *)_coder {
584 [_coder encodeObject:[self javaScriptClassName]];
586 [_coder encodeObject:[self parentObject]];
587 [_coder encodeObject:[self prototypeObject]];
589 [_coder encodeConditionalObject:[self parentObject]];
590 [_coder encodeConditionalObject:[self prototypeObject]];
592 [self encodeJavaScriptPropertiesWithCoder:_coder];
597 - (NSString *)description {
601 ms = [NSMutableString stringWithCapacity:32];
602 [ms appendFormat:@"<%@[0x%08X]: handle=0x%08X>",
603 NSStringFromClass([self class]), self,
605 if ((tmp = [self javaScriptClassName]))
606 [ms appendFormat:@" class=%@", tmp];
608 if ([self isJavaScriptFunction])
609 [ms appendString:@" function"];
611 [ms appendString:@">"];
615 @end /* NGJavaScriptObject */
617 @implementation JSIDEnum
619 - (id)initWithIdArray:(JSIdArray *)_array
620 mappingContext:(NGJavaScriptObjectMappingContext *)_ctx
622 if (_array == NULL) {
626 self->idArray = _array;
628 self->cx = [[_ctx jsContext] handle];
631 - (id)initWithIdArray:(JSIdArray *)_array
633 mappingContext:(NGJavaScriptObjectMappingContext *)_ctx
635 if ((self = [self initWithIdArray:_array mappingContext:_ctx])) {
636 self->object = RETAIN(_object);
643 JS_DestroyIdArray(self->cx, self->idArray);
644 RELEASE(self->object);
653 if (self->idArray == NULL)
656 if (self->idArray->length <= self->pos) {
657 JS_DestroyIdArray(self->cx, self->idArray);
658 self->idArray = NULL;
662 jid = self->idArray->vector[self->pos];
664 if (JS_IdToValue(self->cx, jid, &idv) == JS_FALSE) {
665 NSLog(@"couldn't convert id to value ..");
669 jobj = [self->ctx objectForJSValue:&idv];
672 jobj = [(NSDictionary *)self->object objectForKey:jobj];
676 if (self->idArray->length <= self->pos) {
677 JS_DestroyIdArray(self->cx, self->idArray);
678 self->idArray = NULL;
684 - (NSString *)description {
685 return [NSString stringWithFormat:
686 @"<0x%08X[%@]: len=%d>",
687 self, NSStringFromClass([self class]),
688 self->idArray ? self->idArray->length : 0];
693 @implementation JSObjChainEnum
695 - (id)initWithObject:(id)_obj selector:(SEL)_sel {
696 self->object = RETAIN(_obj);
697 self->selector = _sel;
701 RELEASE(self->object);
706 AUTORELEASE(self->object);
707 self->object = RETAIN([self->object performSelector:self->selector]);
711 @end /* JSObjChainEnum */