]> err.no Git - sope/blob - Recycler/NGJavaScript/NGJavaScriptContext.m
use %p for pointer formats
[sope] / Recycler / NGJavaScript / NGJavaScriptContext.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 "NGJavaScriptContext.h"
24 #include "NGJavaScriptRuntime.h"
25 #include "NGJavaScriptObject.h"
26 #include "NGJavaScriptFunction.h"
27 #include "NGJavaScriptObjectHandler.h"
28 #include "NGJavaScriptError.h"
29 #include "NSString+JS.h"
30 #include "common.h"
31
32
33 @interface NGJavaScriptContext(PrivateMethods)
34
35 @end
36
37 static JSBool
38 Print(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
39
40 @implementation NGJavaScriptContext
41
42 static BOOL abortOnJSError = NO;
43 static BOOL debugDealloc   = NO;
44 NSMapTable *jsctxToObjC = NULL;
45
46 + (void)initialize {
47   NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
48   
49   abortOnJSError = [ud boolForKey:@"JSAbortOnError"];
50   debugDealloc   = [ud boolForKey:@"JSDebugContextDealloc"];
51 }
52
53 static JSFunctionSpec functions[] = {
54     {"print",           Print,          0},
55     {0}
56 };
57
58
59 static void jsErrorReporter(JSContext *cx, const char *msg, JSErrorReport *rp);
60 static JSBool jsGCCallback(JSContext *cx, JSGCStatus status)
61      __attribute__((unused));
62
63
64 static JSBool global_resolve(JSContext *cx, JSObject *obj, jsval _id)
65      __attribute__((unused));
66 static JSBool global_resolve(JSContext *cx, JSObject *obj, jsval _id) {
67   NGJavaScriptContext *self;
68   
69   self = NSMapGet(jsctxToObjC, cx);
70   
71   NSLog(@"resolve called on %@.", self);
72   return JS_ResolveStub(cx, obj, _id);
73 }
74
75 + (NGJavaScriptContext *)jsContextForHandle:(void *)_handle {
76   NGJavaScriptContext *ctx;
77
78   ctx = NSMapGet(jsctxToObjC, _handle);
79   return ctx;
80 }
81
82 - (id)initWithRuntime:(NGJavaScriptRuntime *)_rt
83   maximumStackSize:(unsigned)_size
84 {
85   self->handle = JS_NewContext([_rt handle], _size ? _size : 8192);
86   if (self->handle == NULL) {
87     NSLog(@"WARNING(%s): got no handle !", __PRETTY_FUNCTION__);
88     [self release];
89     return nil;
90   }
91   
92   if (jsctxToObjC == NULL) {
93     jsctxToObjC = NSCreateMapTable(NSNonOwnedPointerMapKeyCallBacks,
94                                    NSNonRetainedObjectMapValueCallBacks,
95                                    200);
96   }
97   NSMapInsert(jsctxToObjC, self->handle, self);
98   JS_SetErrorReporter(self->handle, jsErrorReporter);
99   
100   // JSSetGCCallback(self->handle, jsGCCallback);
101   
102 #if 0
103   /* setup initial global */
104   {
105     NGJavaScriptObject *oglob;
106     
107     oglob = [[NGJavaScriptObject alloc] initWithJSContext:self];
108     if (oglob == nil) {
109       [self release];
110       return nil;
111     }
112     [oglob makeGlobal];
113     
114     [oglob release]; oglob = nil;
115   }
116 #endif
117   
118   ASSIGN(self->rt, _rt);
119   
120   return self;
121 }
122 - (id)initWithRuntime:(NGJavaScriptRuntime *)_rt {
123   return [self initWithRuntime:_rt maximumStackSize:0];
124 }
125 - (id)init {
126   return [self initWithRuntime:[NGJavaScriptRuntime standardJavaScriptRuntime]
127                maximumStackSize:0];
128 }
129
130 - (void)dealloc {
131   if (debugDealloc) NSLog(@"dealloc context: %@", self);
132   
133   [self collectGarbage];
134   
135   if (self->handle) {
136     JS_DestroyContext(self->handle);
137     NSMapRemove(jsctxToObjC, self->handle);
138   }
139
140   [self->rt release];
141   [super dealloc];
142   
143   if (debugDealloc) NSLog(@"did dealloc context: 0x%p", self);
144 }
145
146 - (BOOL)loadStandardClasses {
147   JSObject *glob;
148   
149   if ((glob = JS_GetGlobalObject(self->handle)) == NULL) {
150     NSLog(@"NGJavaScriptContext: no global object set ..");
151     return NO;
152   }
153   
154   //NSLog(@"NGJavaScriptContext: loading std classes ..");
155   
156   if (!JS_InitStandardClasses(self->handle, glob)) {
157     NSLog(@"NGJavaScriptContext: could not init standard classes ...");
158     return NO;
159   }
160   if (!JS_DefineFunctions(self->handle, glob, functions)) {
161     NSLog(@"NGJavaScriptContext: could not define global funcs ...");
162     return NO;
163   }
164   
165   return YES;
166 }
167
168 - (void *)handle {
169   return self->handle;
170 }
171
172 /* accessors */
173
174 - (NGJavaScriptRuntime *)runtime {
175   return self->rt;
176 }
177
178 - (BOOL)isRunning {
179   return JS_IsRunning(self->handle) ? YES : NO;
180 }
181
182 - (BOOL)isConstructing {
183   return JS_IsConstructing(self->handle) ? YES : NO;
184 }
185
186 - (void)setJavaScriptVersion:(int)_version {
187   JS_SetVersion(self->handle, _version);
188 }
189 - (int)javaScriptVersion {
190   return JS_GetVersion(self->handle);
191 }
192
193 /* global object */
194
195 - (id)globalObject {
196   JSObject                  *global;
197   NGJavaScriptObjectHandler *oglobal;
198   
199   global = JS_GetGlobalObject(self->handle);
200   NSAssert(global, @"missing global object !");
201   
202   if ((oglobal = JS_GetPrivate(self->handle, global)) == nil)
203     return nil;
204   
205   return [[oglobal retain] autorelease];
206 }
207
208 /* evaluation */
209
210 - (id)evaluateScript:(NSString *)_script {
211   return [[self globalObject] evaluateScript:_script];
212 }
213
214 /* invocation */
215
216 - (id)callFunctionNamed:(NSString *)_funcName, ... {
217   return [[self globalObject] callFunctionNamed:_funcName, nil];
218 }
219
220 /* errors */
221
222 - (void)reportException:(NSException *)_exc {
223   JS_ReportError(self->handle, "%s", [[_exc description] cString]);
224 }
225
226 - (void)reportError:(NSString *)_fmt, ... {
227   NSString *s;
228   va_list va;
229   
230   va_start(va, _fmt);
231
232 #if NG_VARARGS_AS_REFERENCE /* in common.h */
233   s = [[[NSString alloc] initWithFormat:_fmt arguments:va] autorelease];
234 #else
235   s = [NSString stringWithFormat:_fmt arguments:&va];
236 #endif
237   va_end(va);
238   
239   JS_ReportError(self->handle, "%s", [s cString]);
240 }
241
242 - (void)reportOutOfMemory {
243   JS_ReportOutOfMemory(self->handle);
244 }
245
246 - (void)logReportedJavaScriptError:(NGJavaScriptError *)_error {
247   NSLog(@"JS ERROR(%@:%d): %@", [_error path], [_error line], [_error reason]);
248   if (abortOnJSError) abort();
249 }
250
251 - (void)reportError:(NSString *)_msg
252   inFile:(NSString *)_path inLine:(unsigned)_line
253   report:(void *)_report
254 {
255   NGJavaScriptError *e;
256   
257   e = [[NGJavaScriptError alloc] initWithErrorReport:_report message:_msg context:self];
258   
259   [self logReportedJavaScriptError:e];
260   if ([[NSUserDefaults standardUserDefaults] boolForKey:@"AbortOnJSError"])
261     abort();
262     
263   ASSIGN(self->lastError, e);
264
265   if ([[NSUserDefaults standardUserDefaults] boolForKey:@"RaiseOnJSError"])
266     [self->lastError raise];
267 }
268
269 - (NSException *)lastError {
270   return self->lastError;
271 }
272 - (void)clearLastError {
273   [self->lastError release]; self->lastError = nil;
274 }
275
276 /* garbage collector */
277
278 - (void)collectGarbage {
279   JS_GC(self->handle);
280 }
281 - (void)maybeCollectGarbage {
282   JS_MaybeGC(self->handle);
283 }
284
285 - (void *)malloc:(unsigned)_size {
286   return JS_malloc(self->handle, _size);
287 }
288 - (void *)realloc:(void *)_pointer size:(unsigned)_size {
289   return JS_realloc(self->handle, _pointer, _size);
290 }
291 - (void)freePointer:(void *)_pointer {
292   JS_free(self->handle, _pointer);
293 }
294
295 - (BOOL)addRootPointer:(void *)_root {
296   return JS_AddRoot(self->handle, _root) ? YES : NO;
297 }
298 - (BOOL)addRootPointer:(void *)_root name:(NSString *)_name {
299   return JS_AddNamedRoot(self->handle, _root, [_name cString]) ? YES : NO;
300 }
301 - (BOOL)removeRootPointer:(void *)_root {
302   return JS_RemoveRoot(self->handle, _root) ? YES : NO;
303 }
304
305 - (BOOL)lockGCThing:(void *)_ptr {
306   return JS_LockGCThing(self->handle, _ptr) ? YES : NO;
307 }
308 - (BOOL)unlockGCThing:(void *)_ptr {
309   return JS_UnlockGCThing(self->handle, _ptr) ? YES : NO;
310 }
311
312 - (BOOL)beginGarbageCollection {
313   return YES;
314 }
315 - (BOOL)endGarbageCollection {
316   return YES;
317 }
318
319 /* threads */
320
321 #if JS_THREADSAFE
322 - (void)beginRequest {
323   JS_BeginRequest(self->handle);
324 }
325 - (void)endRequest {
326   JS_EndRequest(self->handle);
327 }
328 - (void)suspendRequest {
329   JS_SuspendRequest(self->handle);
330 }
331 - (void)resumeRequest {
332   JS_ResumeRequest(self->handle);
333 }
334 #else
335 - (void)beginRequest {
336 #if LIB_FOUNDATION_LIBRARY
337   [self notImplemented:_cmd];
338 #else
339   [self doesNotRecognizeSelector:_cmd];
340 #endif
341 }
342 - (void)endRequest {
343 #if LIB_FOUNDATION_LIBRARY
344   [self notImplemented:_cmd];
345 #else
346   [self doesNotRecognizeSelector:_cmd];
347 #endif
348 }
349 - (void)suspendRequest {
350 #if LIB_FOUNDATION_LIBRARY
351   [self notImplemented:_cmd];
352 #else
353   [self doesNotRecognizeSelector:_cmd];
354 #endif
355 }
356 - (void)resumeRequest {
357 #if LIB_FOUNDATION_LIBRARY
358   [self notImplemented:_cmd];
359 #else
360   [self doesNotRecognizeSelector:_cmd];
361 #endif
362 }
363 #endif
364
365 /* description */
366
367 - (NSString *)description {
368   return [NSString stringWithFormat:
369                      @"<%@[0x%p]: %@%@handle=0x%p version=%i runtime=%@>",
370                      NSStringFromClass([self class]), self,
371                      [self isRunning]      ? @"running "      : @"",
372                      [self isConstructing] ? @"constructing " : @"",
373                      [self handle],
374                      [self javaScriptVersion],
375                      [self runtime]];
376 }
377
378 /* statics */
379
380 static void jsErrorReporter(JSContext *cx, const char *msg, JSErrorReport *rp) {
381   NGJavaScriptContext *self;
382
383   self = NSMapGet(jsctxToObjC, cx);
384
385   if (self == NULL) {
386     fprintf(stderr, "ERROR(missing ObjC object): %s\n", msg);
387   }
388   
389   [self reportError:msg?[NSString stringWithCString:msg]:@"unknown JavaScript error"
390         inFile:rp->filename?[NSString stringWithCString:rp->filename]:@""
391         inLine:rp->lineno
392         report:rp];
393 }
394
395 static JSBool jsGCCallback(JSContext *cx, JSGCStatus status) {
396   NGJavaScriptContext *self;
397   
398   self = NSMapGet(jsctxToObjC, cx);
399
400   return (status == JSGC_BEGIN)
401     ? ([self beginGarbageCollection] ? JSVAL_TRUE : JSVAL_FALSE)
402     : ([self endGarbageCollection]   ? JSVAL_TRUE : JSVAL_FALSE);
403 }
404
405 @end /* NGJavaScriptContext */
406
407 static JSBool
408 Print(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
409 {
410     uintN i, n;
411     JSString *str;
412
413     for (i = n = 0; i < argc; i++) {
414         str = JS_ValueToString(cx, argv[i]);
415         if (!str)
416             return JS_FALSE;
417         fprintf(stdout, "%s%s", i ? " " : "", JS_GetStringBytes(str));
418     }
419     n++;
420     if (n)
421         fputc('\n', stdout);
422     return JS_TRUE;
423 }