Line data Source code
1 0 : /*
2 :
3 : OOJSConsole.m
4 :
5 :
6 : Oolite
7 : Copyright (C) 2004-2013 Giles C Williams and contributors
8 :
9 : Permission is hereby granted, free of charge, to any person obtaining a copy
10 : of this software and associated documentation files (the "Software"), to deal
11 : in the Software without restriction, including without limitation the rights
12 : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 : copies of the Software, and to permit persons to whom the Software is
14 : furnished to do so, subject to the following conditions:
15 :
16 : The above copyright notice and this permission notice shall be included in all
17 : copies or substantial portions of the Software.
18 :
19 : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25 : SOFTWARE.
26 :
27 : */
28 :
29 : #ifndef OO_EXCLUDE_DEBUG_SUPPORT
30 :
31 : #import "OOJSConsole.h"
32 : #import "OODebugMonitor.h"
33 : #include <stdint.h>
34 :
35 : #import "OOJSEngineTimeManagement.h"
36 : #import "OOJSScript.h"
37 : #import "OOJSVector.h"
38 : #import "OOJSEntity.h"
39 : #import "OOJSCall.h"
40 : #import "OOLoggingExtended.h"
41 : #import "OOConstToString.h"
42 : #import "OOOpenGLExtensionManager.h"
43 : #import "OODebugFlags.h"
44 : #import "OODebugMonitor.h"
45 : #import "OOProfilingStopwatch.h"
46 : #import "ResourceManager.h"
47 :
48 :
49 : @interface Entity (OODebugInspector)
50 :
51 : // Method added by inspector in Debug OXP under OS X only.
52 0 : - (void) inspect;
53 :
54 : @end
55 :
56 :
57 0 : NSString *OOPlatformDescription(void);
58 :
59 :
60 0 : static JSObject *sConsolePrototype = NULL;
61 0 : static JSObject *sConsoleSettingsPrototype = NULL;
62 :
63 :
64 : static JSBool ConsoleGetProperty(JSContext *context, JSObject *this, jsid propID, jsval *value);
65 : static JSBool ConsoleSetProperty(JSContext *context, JSObject *this, jsid propID, JSBool strict, jsval *value);
66 : static void ConsoleFinalize(JSContext *context, JSObject *this);
67 :
68 : // Methods
69 : static JSBool ConsoleConsoleMessage(JSContext *context, uintN argc, jsval *vp);
70 : static JSBool ConsoleClearConsole(JSContext *context, uintN argc, jsval *vp);
71 : static JSBool ConsoleScriptStack(JSContext *context, uintN argc, jsval *vp);
72 : static JSBool ConsoleInspectEntity(JSContext *context, uintN argc, jsval *vp);
73 : #if OO_DEBUG
74 : static JSBool ConsoleCallObjCMethod(JSContext *context, uintN argc, jsval *vp);
75 : static JSBool ConsoleSetUpCallObjC(JSContext *context, uintN argc, jsval *vp);
76 : #endif
77 : static JSBool ConsoleIsExecutableJavaScript(JSContext *context, uintN argc, jsval *vp);
78 : static JSBool ConsoleDisplayMessagesInClass(JSContext *context, uintN argc, jsval *vp);
79 : static JSBool ConsoleSetDisplayMessagesInClass(JSContext *context, uintN argc, jsval *vp);
80 : static JSBool ConsoleWriteLogMarker(JSContext *context, uintN argc, jsval *vp);
81 : static JSBool ConsoleWriteMemoryStats(JSContext *context, uintN argc, jsval *vp);
82 : static JSBool ConsoleWriteJSMemoryStats(JSContext *context, uintN argc, jsval *vp);
83 : static JSBool ConsoleGarbageCollect(JSContext *context, uintN argc, jsval *vp);
84 : #if DEBUG
85 : static JSBool ConsoleDumpNamedRoots(JSContext *context, uintN argc, jsval *vp);
86 : static JSBool ConsoleDumpHeap(JSContext *context, uintN argc, jsval *vp);
87 : #endif
88 : #if OOJS_PROFILE
89 : static JSBool ConsoleProfile(JSContext *context, uintN argc, jsval *vp);
90 : static JSBool ConsoleGetProfile(JSContext *context, uintN argc, jsval *vp);
91 : static JSBool ConsoleTrace(JSContext *context, uintN argc, jsval *vp);
92 : #endif
93 :
94 : static JSBool ConsoleSettingsDeleteProperty(JSContext *context, JSObject *this, jsid propID, jsval *value);
95 : static JSBool ConsoleSettingsGetProperty(JSContext *context, JSObject *this, jsid propID, jsval *value);
96 : static JSBool ConsoleSettingsSetProperty(JSContext *context, JSObject *this, jsid propID, JSBool strict, jsval *value);
97 :
98 : #if OOJS_PROFILE
99 : static JSBool PerformProfiling(JSContext *context, NSString *nominalFunction, uintN argc, jsval *argv, jsval *rval, BOOL trace, OOTimeProfile **profile);
100 : #endif
101 :
102 :
103 0 : static JSClass sConsoleClass =
104 : {
105 : "Console",
106 : JSCLASS_HAS_PRIVATE,
107 :
108 : JS_PropertyStub, // addProperty
109 : JS_PropertyStub, // delProperty
110 : ConsoleGetProperty, // getProperty
111 : ConsoleSetProperty, // setProperty
112 : JS_EnumerateStub, // enumerate
113 : JS_ResolveStub, // resolve
114 : JS_ConvertStub, // convert
115 : ConsoleFinalize, // finalize
116 : JSCLASS_NO_OPTIONAL_MEMBERS
117 : };
118 :
119 :
120 0 : enum
121 : {
122 : // Property IDs
123 : kConsole_debugFlags, // debug flags, integer, read/write
124 : kConsole_detailLevel, // graphics detail level, symbolic string, read/write
125 : kConsole_maximumDetailLevel, // maximum graphics detail level, symbolic string, read-only
126 : kConsole_displayFPS, // display FPS (and related info), boolean, read/write
127 : kConsole_platformDescription, // Information about system we're running on in unspecified format, string, read-only
128 : kConsole_ignoreDroppedPackets, // boolean (default false), read/write
129 : kConsole_pedanticMode, // JS pedantic mode (JS_STRICT flag, not the same as "use strict"), boolean (default true), read/write
130 : kConsole_showErrorLocations, // Show error/warning source locations, boolean (default true), read/write
131 : kConsole_dumpStackForErrors, // Write stack dump when reporting error/exception, boolean (default false), read/write
132 : kConsole_dumpStackForWarnings, // Write stack dump when reporting warning, boolean (default false), read/write
133 :
134 : kConsole_glVendorString, // OpenGL GL_VENDOR string, string, read-only
135 : kConsole_glRendererString, // OpenGL GL_RENDERER string, string, read-only
136 : kConsole_glFixedFunctionTextureUnitCount, // GL_MAX_TEXTURE_UNITS_ARB, integer, read-only
137 : kConsole_glFragmentShaderTextureUnitCount, // GL_MAX_TEXTURE_IMAGE_UNITS_ARB, integer, read-only
138 :
139 : // Symbolic constants for debug flags:
140 : kConsole_DEBUG_LINKED_LISTS,
141 : kConsole_DEBUG_COLLISIONS,
142 : kConsole_DEBUG_DOCKING,
143 : kConsole_DEBUG_OCTREE_LOGGING,
144 : kConsole_DEBUG_BOUNDING_BOXES,
145 : kConsole_DEBUG_OCTREE_DRAW,
146 : kConsole_DEBUG_DRAW_NORMALS,
147 : kConsole_DEBUG_NO_DUST,
148 : kConsole_DEBUG_NO_SHADER_FALLBACK,
149 : kConsole_DEBUG_SHADER_VALIDATION,
150 :
151 : kConsole_DEBUG_MISC
152 : };
153 :
154 :
155 0 : static JSPropertySpec sConsoleProperties[] =
156 : {
157 : // JS name ID flags
158 : { "debugFlags", kConsole_debugFlags, OOJS_PROP_READWRITE_CB },
159 : { "detailLevel", kConsole_detailLevel, OOJS_PROP_READWRITE_CB },
160 : { "maximumDetailLevel", kConsole_maximumDetailLevel, OOJS_PROP_READONLY_CB },
161 : { "displayFPS", kConsole_displayFPS, OOJS_PROP_READWRITE_CB },
162 : { "platformDescription", kConsole_platformDescription, OOJS_PROP_READONLY_CB },
163 : { "pedanticMode", kConsole_pedanticMode, OOJS_PROP_READWRITE_CB },
164 : { "ignoreDroppedPackets", kConsole_ignoreDroppedPackets, OOJS_PROP_READWRITE_CB },
165 : { "__showErrorLocations", kConsole_showErrorLocations, OOJS_PROP_HIDDEN_READWRITE_CB },
166 : { "__dumpStackForErrors", kConsole_dumpStackForErrors, OOJS_PROP_HIDDEN_READWRITE_CB },
167 : { "__dumpStackForWarnings", kConsole_dumpStackForWarnings, OOJS_PROP_HIDDEN_READWRITE_CB },
168 : { "glVendorString", kConsole_glVendorString, OOJS_PROP_READONLY_CB },
169 : { "glRendererString", kConsole_glRendererString, OOJS_PROP_READONLY_CB },
170 : { "glFixedFunctionTextureUnitCount", kConsole_glFixedFunctionTextureUnitCount, OOJS_PROP_READONLY_CB },
171 : { "glFragmentShaderTextureUnitCount", kConsole_glFragmentShaderTextureUnitCount, OOJS_PROP_READONLY_CB },
172 :
173 0 : #define DEBUG_FLAG_DECL(x) { #x, kConsole_##x, OOJS_PROP_READONLY_CB }
174 : DEBUG_FLAG_DECL(DEBUG_LINKED_LISTS),
175 : DEBUG_FLAG_DECL(DEBUG_COLLISIONS),
176 : DEBUG_FLAG_DECL(DEBUG_DOCKING),
177 : DEBUG_FLAG_DECL(DEBUG_OCTREE_LOGGING),
178 : DEBUG_FLAG_DECL(DEBUG_BOUNDING_BOXES),
179 : DEBUG_FLAG_DECL(DEBUG_OCTREE_DRAW),
180 : DEBUG_FLAG_DECL(DEBUG_DRAW_NORMALS),
181 : DEBUG_FLAG_DECL(DEBUG_NO_DUST),
182 : DEBUG_FLAG_DECL(DEBUG_NO_SHADER_FALLBACK),
183 : DEBUG_FLAG_DECL(DEBUG_SHADER_VALIDATION),
184 :
185 : DEBUG_FLAG_DECL(DEBUG_MISC),
186 : #undef DEBUG_FLAG_DECL
187 :
188 : { 0 }
189 : };
190 :
191 :
192 0 : static JSFunctionSpec sConsoleMethods[] =
193 : {
194 : // JS name Function min args
195 : { "consoleMessage", ConsoleConsoleMessage, 2 },
196 : { "clearConsole", ConsoleClearConsole, 0 },
197 : { "scriptStack", ConsoleScriptStack, 0 },
198 : { "inspectEntity", ConsoleInspectEntity, 1 },
199 : #if OO_DEBUG
200 : { "__setUpCallObjC", ConsoleSetUpCallObjC, 1 },
201 : #endif
202 : { "isExecutableJavaScript", ConsoleIsExecutableJavaScript, 2 },
203 : { "displayMessagesInClass", ConsoleDisplayMessagesInClass, 1 },
204 : { "setDisplayMessagesInClass", ConsoleSetDisplayMessagesInClass, 2 },
205 : { "writeLogMarker", ConsoleWriteLogMarker, 0 },
206 : { "writeMemoryStats", ConsoleWriteMemoryStats, 0 },
207 : { "writeJSMemoryStats", ConsoleWriteJSMemoryStats, 0 },
208 : { "garbageCollect", ConsoleGarbageCollect, 0 },
209 : #if DEBUG
210 : { "dumpNamedRoots", ConsoleDumpNamedRoots, 0 },
211 : { "dumpHeap", ConsoleDumpHeap, 0 },
212 : #endif
213 : #if OOJS_PROFILE
214 : { "profile", ConsoleProfile, 1 },
215 : { "getProfile", ConsoleGetProfile, 1 },
216 : { "trace", ConsoleTrace, 1 },
217 : #endif
218 : { 0 }
219 : };
220 :
221 :
222 0 : static JSClass sConsoleSettingsClass =
223 : {
224 : "ConsoleSettings",
225 : JSCLASS_HAS_PRIVATE,
226 :
227 : JS_PropertyStub, // addProperty
228 : ConsoleSettingsDeleteProperty, // delProperty
229 : ConsoleSettingsGetProperty, // getProperty
230 : ConsoleSettingsSetProperty, // setProperty
231 : JS_EnumerateStub, // enumerate. FIXME: this should work.
232 : JS_ResolveStub, // resolve
233 : JS_ConvertStub, // convert
234 : ConsoleFinalize, // finalize (same as Console)
235 : JSCLASS_NO_OPTIONAL_MEMBERS
236 : };
237 :
238 :
239 0 : static void InitOOJSConsole(JSContext *context, JSObject *global)
240 : {
241 : sConsolePrototype = JS_InitClass(context, global, NULL, &sConsoleClass, OOJSUnconstructableConstruct, 0, sConsoleProperties, sConsoleMethods, NULL, NULL);
242 : OOJSRegisterObjectConverter(&sConsoleClass, OOJSBasicPrivateObjectConverter);
243 :
244 : sConsoleSettingsPrototype = JS_InitClass(context, global, NULL, &sConsoleSettingsClass, OOJSUnconstructableConstruct, 0, NULL, NULL, NULL, NULL);
245 : OOJSRegisterObjectConverter(&sConsoleSettingsClass, OOJSBasicPrivateObjectConverter);
246 : }
247 :
248 :
249 0 : void OOJSConsoleDestroy(void)
250 : {
251 : sConsolePrototype = NULL;
252 : }
253 :
254 :
255 0 : JSObject *DebugMonitorToJSConsole(JSContext *context, OODebugMonitor *monitor)
256 : {
257 : OOJS_PROFILE_ENTER
258 :
259 : OOJavaScriptEngine *engine = nil;
260 : JSObject *object = NULL;
261 : JSObject *settingsObject = NULL;
262 : jsval value;
263 :
264 : NSCAssert(JS_EnterLocalRootScope(context), @"Failed to create JS GC root scope");
265 : engine = [OOJavaScriptEngine sharedEngine];
266 :
267 : if (sConsolePrototype == NULL)
268 : {
269 : InitOOJSConsole(context, [engine globalObject]);
270 : }
271 :
272 : // Create Console object
273 : object = JS_NewObject(context, &sConsoleClass, sConsolePrototype, NULL);
274 : if (object != NULL)
275 : {
276 : if (!JS_SetPrivate(context, object, [monitor weakRetain])) object = NULL;
277 : }
278 :
279 : if (object != NULL)
280 : {
281 : // Create ConsoleSettings object
282 : settingsObject = JS_NewObject(context, &sConsoleSettingsClass, sConsoleSettingsPrototype, NULL);
283 : if (settingsObject != NULL)
284 : {
285 : if (!JS_SetPrivate(context, settingsObject, [monitor weakRetain])) settingsObject = NULL;
286 : }
287 : if (settingsObject != NULL)
288 : {
289 : value = OBJECT_TO_JSVAL(settingsObject);
290 : if (!JS_SetProperty(context, object, "settings", &value))
291 : {
292 : settingsObject = NULL;
293 : }
294 : }
295 :
296 : if (settingsObject == NULL) object = NULL;
297 : }
298 :
299 : JS_LeaveLocalRootScope(context);
300 :
301 : return object;
302 : // Analyzer: object leaked. (x2) [Expected, objects are retained by JS object.]
303 :
304 : OOJS_PROFILE_EXIT
305 : }
306 :
307 :
308 0 : static JSBool ConsoleGetProperty(JSContext *context, JSObject *this, jsid propID, jsval *value)
309 : {
310 : if (!JSID_IS_INT(propID)) return YES;
311 :
312 : OOJS_NATIVE_ENTER(context)
313 :
314 : switch (JSID_TO_INT(propID))
315 : {
316 : #ifndef NDEBUG
317 : case kConsole_debugFlags:
318 : *value = INT_TO_JSVAL((uint32_t)gDebugFlags);
319 : break;
320 : #endif
321 :
322 : case kConsole_detailLevel:
323 : *value = [OOStringFromGraphicsDetail([UNIVERSE detailLevel]) oo_jsValueInContext:context];
324 : break;
325 :
326 : case kConsole_maximumDetailLevel:
327 : *value = [OOStringFromGraphicsDetail([[OOOpenGLExtensionManager sharedManager] maximumDetailLevel]) oo_jsValueInContext:context];
328 : break;
329 :
330 : case kConsole_displayFPS:
331 : *value = OOJSValueFromBOOL([UNIVERSE displayFPS]);
332 : break;
333 :
334 : case kConsole_platformDescription:
335 : *value = OOJSValueFromNativeObject(context, OOPlatformDescription());
336 : break;
337 :
338 : case kConsole_pedanticMode:
339 : {
340 : uint32_t options = JS_GetOptions(context);
341 : *value = OOJSValueFromBOOL(options & JSOPTION_STRICT);
342 : }
343 : break;
344 :
345 : case kConsole_ignoreDroppedPackets:
346 : *value = OOJSValueFromBOOL([[OODebugMonitor sharedDebugMonitor] TCPIgnoresDroppedPackets]);
347 : break;
348 :
349 : case kConsole_showErrorLocations:
350 : *value = OOJSValueFromBOOL([[OOJavaScriptEngine sharedEngine] showErrorLocations]);
351 : break;
352 :
353 : case kConsole_dumpStackForErrors:
354 : *value = OOJSValueFromBOOL([[OOJavaScriptEngine sharedEngine] dumpStackForErrors]);
355 : break;
356 :
357 : case kConsole_dumpStackForWarnings:
358 : *value = OOJSValueFromBOOL([[OOJavaScriptEngine sharedEngine] dumpStackForWarnings]);
359 : break;
360 :
361 : case kConsole_glVendorString:
362 : *value = OOJSValueFromNativeObject(context, [[OOOpenGLExtensionManager sharedManager] vendorString]);
363 : break;
364 :
365 : case kConsole_glRendererString:
366 : *value = OOJSValueFromNativeObject(context, [[OOOpenGLExtensionManager sharedManager] rendererString]);
367 : break;
368 :
369 : case kConsole_glFixedFunctionTextureUnitCount:
370 : *value = INT_TO_JSVAL([[OOOpenGLExtensionManager sharedManager] textureUnitCount]);
371 : break;
372 :
373 : case kConsole_glFragmentShaderTextureUnitCount:
374 : *value = INT_TO_JSVAL([[OOOpenGLExtensionManager sharedManager] textureImageUnitCount]);
375 : break;
376 :
377 0 : #define DEBUG_FLAG_CASE(x) case kConsole_##x: *value = INT_TO_JSVAL(x); break;
378 : DEBUG_FLAG_CASE(DEBUG_LINKED_LISTS);
379 : DEBUG_FLAG_CASE(DEBUG_COLLISIONS);
380 : DEBUG_FLAG_CASE(DEBUG_DOCKING);
381 : DEBUG_FLAG_CASE(DEBUG_OCTREE_LOGGING);
382 : DEBUG_FLAG_CASE(DEBUG_BOUNDING_BOXES);
383 : DEBUG_FLAG_CASE(DEBUG_OCTREE_DRAW);
384 : DEBUG_FLAG_CASE(DEBUG_DRAW_NORMALS);
385 : DEBUG_FLAG_CASE(DEBUG_NO_DUST);
386 : DEBUG_FLAG_CASE(DEBUG_NO_SHADER_FALLBACK);
387 : DEBUG_FLAG_CASE(DEBUG_SHADER_VALIDATION);
388 :
389 : DEBUG_FLAG_CASE(DEBUG_MISC);
390 : #undef DEBUG_FLAG_CASE
391 :
392 : default:
393 : OOJSReportBadPropertySelector(context, this, propID, sConsoleProperties);
394 : return NO;
395 : }
396 :
397 : return YES;
398 :
399 : OOJS_NATIVE_EXIT
400 : }
401 :
402 :
403 0 : static JSBool ConsoleSetProperty(JSContext *context, JSObject *this, jsid propID, JSBool strict, jsval *value)
404 : {
405 : if (!JSID_IS_INT(propID)) return YES;
406 :
407 : OOJS_NATIVE_ENTER(context)
408 :
409 : int32 iValue;
410 : JSBool bValue = NO;
411 : NSString *sValue;
412 :
413 : switch (JSID_TO_INT(propID))
414 : {
415 : #ifndef NDEBUG
416 : case kConsole_debugFlags:
417 : if (JS_ValueToInt32(context, *value, &iValue))
418 : {
419 : gDebugFlags = iValue;
420 : }
421 : break;
422 : #endif
423 : case kConsole_detailLevel:
424 : sValue = OOStringFromJSValue(context, *value);
425 : OOJS_BEGIN_FULL_NATIVE(context)
426 : [UNIVERSE setDetailLevel:OOGraphicsDetailFromString(sValue)];
427 : OOJS_END_FULL_NATIVE
428 : break;
429 :
430 : case kConsole_displayFPS:
431 : if (JS_ValueToBoolean(context, *value, &bValue))
432 : {
433 : [UNIVERSE setDisplayFPS:bValue];
434 : }
435 : break;
436 :
437 : case kConsole_pedanticMode:
438 : if (JS_ValueToBoolean(context, *value, &bValue))
439 : {
440 : uint32_t options = JS_GetOptions(context);
441 : if (bValue) options |= JSOPTION_STRICT;
442 : else options &= ~JSOPTION_STRICT;
443 :
444 : JS_SetOptions(context, options);
445 : }
446 : break;
447 :
448 : case kConsole_ignoreDroppedPackets:
449 : if (JS_ValueToBoolean(context, *value, &bValue))
450 : {
451 : [[OODebugMonitor sharedDebugMonitor] setTCPIgnoresDroppedPackets:bValue];
452 : }
453 : break;
454 :
455 : case kConsole_showErrorLocations:
456 : if (JS_ValueToBoolean(context, *value, &bValue))
457 : {
458 : [[OOJavaScriptEngine sharedEngine] setShowErrorLocations:bValue];
459 : }
460 : break;
461 :
462 : case kConsole_dumpStackForErrors:
463 : if (JS_ValueToBoolean(context, *value, &bValue))
464 : {
465 : [[OOJavaScriptEngine sharedEngine] setDumpStackForErrors:bValue];
466 : }
467 : break;
468 :
469 : case kConsole_dumpStackForWarnings:
470 : if (JS_ValueToBoolean(context, *value, &bValue))
471 : {
472 : [[OOJavaScriptEngine sharedEngine] setDumpStackForWarnings:bValue];
473 : }
474 : break;
475 :
476 : default:
477 : OOJSReportBadPropertySelector(context, this, propID, sConsoleProperties);
478 : return NO;
479 : }
480 :
481 : return YES;
482 :
483 : OOJS_NATIVE_EXIT
484 : }
485 :
486 :
487 : static BOOL DoWeDefineAllDebugFlags(enum OODebugFlags flags) GCC_ATTR((unused));
488 0 : static BOOL DoWeDefineAllDebugFlags(enum OODebugFlags flags)
489 : {
490 : /* This function doesn't do anything, but will generate a warning
491 : (Enumeration value 'DEBUG_FOO' not handled in switch) if a debug flag
492 : is added without updating it. The point is that if you get such a
493 : warning, you should first add a JS symbolic constant for the flag,
494 : then add it to the switch to suppress the warning.
495 : NOTE: don't add a default: to this switch, or I will have to hurt you.
496 : -- Ahruman 2010-04-11
497 : */
498 : switch (flags)
499 : {
500 : case DEBUG_LINKED_LISTS:
501 : case DEBUG_COLLISIONS:
502 : case DEBUG_DOCKING:
503 : case DEBUG_OCTREE_LOGGING:
504 : case DEBUG_BOUNDING_BOXES:
505 : case DEBUG_OCTREE_DRAW:
506 : case DEBUG_DRAW_NORMALS:
507 : case DEBUG_NO_DUST:
508 : case DEBUG_NO_SHADER_FALLBACK:
509 : case DEBUG_SHADER_VALIDATION:
510 : case DEBUG_MISC:
511 : return YES;
512 : }
513 :
514 : return NO;
515 : }
516 :
517 :
518 0 : static void ConsoleFinalize(JSContext *context, JSObject *this)
519 : {
520 : OOJS_PROFILE_ENTER
521 :
522 : [(id)JS_GetPrivate(context, this) release];
523 : JS_SetPrivate(context, this, nil);
524 :
525 : OOJS_PROFILE_EXIT_VOID
526 : }
527 :
528 :
529 0 : static JSBool ConsoleSettingsDeleteProperty(JSContext *context, JSObject *this, jsid propID, jsval *value)
530 : {
531 : OOJS_NATIVE_ENTER(context)
532 :
533 : NSString *key = nil;
534 : id monitor = nil;
535 :
536 : if (!JSID_IS_STRING(propID)) return NO;
537 : key = OOStringFromJSString(context, JSID_TO_STRING(propID));
538 :
539 : monitor = OOJSNativeObjectFromJSObject(context, this);
540 : if (![monitor isKindOfClass:[OODebugMonitor class]])
541 : {
542 : OOJSReportError(context, @"Expected OODebugMonitor, got %@ in %s. %@", [monitor class], __PRETTY_FUNCTION__, @"This is an internal error, please report it.");
543 : return NO;
544 : }
545 :
546 : [monitor setConfigurationValue:nil forKey:key];
547 : *value = JSVAL_TRUE;
548 : return YES;
549 :
550 : OOJS_NATIVE_EXIT
551 : }
552 :
553 :
554 0 : static JSBool ConsoleSettingsGetProperty(JSContext *context, JSObject *this, jsid propID, jsval *value)
555 : {
556 : if (!JSID_IS_STRING(propID)) return YES;
557 :
558 : OOJS_NATIVE_ENTER(context)
559 :
560 : NSString *key = nil;
561 : id settingValue = nil;
562 : id monitor = nil;
563 :
564 : key = OOStringFromJSString(context, JSID_TO_STRING(propID));
565 :
566 : monitor = OOJSNativeObjectFromJSObject(context, this);
567 : if (![monitor isKindOfClass:[OODebugMonitor class]])
568 : {
569 : OOJSReportError(context, @"Expected OODebugMonitor, got %@ in %s. %@", [monitor class], __PRETTY_FUNCTION__, @"This is an internal error, please report it.");
570 : return NO;
571 : }
572 :
573 : settingValue = [monitor configurationValueForKey:key];
574 : if (settingValue != NULL) *value = [settingValue oo_jsValueInContext:context];
575 : else *value = JSVAL_VOID;
576 :
577 : return YES;
578 :
579 : OOJS_NATIVE_EXIT
580 : }
581 :
582 :
583 0 : static JSBool ConsoleSettingsSetProperty(JSContext *context, JSObject *this, jsid propID, JSBool strict, jsval *value)
584 : {
585 : if (!JSID_IS_STRING(propID)) return YES;
586 :
587 : OOJS_NATIVE_ENTER(context)
588 :
589 : NSString *key = nil;
590 : id settingValue = nil;
591 : id monitor = nil;
592 :
593 : key = OOStringFromJSString(context, JSID_TO_STRING(propID));
594 :
595 : monitor = OOJSNativeObjectFromJSObject(context, this);
596 : if (![monitor isKindOfClass:[OODebugMonitor class]])
597 : {
598 : OOJSReportError(context, @"Expected OODebugMonitor, got %@ in %s. %@", [monitor class], __PRETTY_FUNCTION__, @"This is an internal error, please report it.");
599 : return NO;
600 : }
601 :
602 : // Not OOJS_BEGIN_FULL_NATIVE() - we use JSAPI while paused.
603 : OOJSPauseTimeLimiter();
604 : if (JSVAL_IS_NULL(*value) || JSVAL_IS_VOID(*value))
605 : {
606 : [monitor setConfigurationValue:nil forKey:key];
607 : }
608 : else
609 : {
610 : settingValue = OOJSNativeObjectFromJSValue(context, *value);
611 : if (settingValue != nil)
612 : {
613 : [monitor setConfigurationValue:settingValue forKey:key];
614 : }
615 : else
616 : {
617 : OOJSReportWarning(context, @"debugConsole.settings: could not convert %@ to native object.", OOStringFromJSValue(context, *value));
618 : }
619 : }
620 : OOJSResumeTimeLimiter();
621 :
622 : return YES;
623 :
624 : OOJS_NATIVE_EXIT
625 : }
626 :
627 :
628 : // *** Methods ***
629 :
630 : // function consoleMessage(colorCode : String, message : String [, emphasisStart : Number, emphasisLength : Number]) : void
631 0 : static JSBool ConsoleConsoleMessage(JSContext *context, uintN argc, jsval *vp)
632 : {
633 : NSRange emphasisRange = {0, 0};
634 :
635 : OOJS_NATIVE_ENTER(context)
636 :
637 : id monitor = nil;
638 : NSString *colorKey = nil,
639 : *message = nil;
640 : jsdouble location, length;
641 :
642 : // Not OOJS_BEGIN_FULL_NATIVE() - we use JSAPI while paused.
643 : OOJSPauseTimeLimiter();
644 : monitor = OOJSNativeObjectOfClassFromJSObject(context, OOJS_THIS, [OODebugMonitor class]);
645 : if (monitor == nil)
646 : {
647 : OOJSReportError(context, @"Expected OODebugMonitor, got %@ in %s. %@", [monitor class], __PRETTY_FUNCTION__, @"This is an internal error, please report it.");
648 : OOJSResumeTimeLimiter();
649 : return NO;
650 : }
651 :
652 : if (argc > 0) colorKey = OOStringFromJSValue(context,OOJS_ARGV[0]);
653 : if (argc > 1) message = OOStringFromJSValue(context,OOJS_ARGV[1]);
654 :
655 : if (argc > 3)
656 : {
657 : // Attempt to get two numbers, specifying an emphasis range.
658 : if (JS_ValueToNumber(context, OOJS_ARGV[2], &location) &&
659 : JS_ValueToNumber(context, OOJS_ARGV[3], &length))
660 : {
661 : emphasisRange = (NSRange){location, length};
662 : }
663 : }
664 :
665 : if (message == nil)
666 : {
667 : if (colorKey == nil)
668 : {
669 : OOJSReportWarning(context, @"Console.consoleMessage() called with no parameters.");
670 : }
671 : else
672 : {
673 : message = colorKey;
674 : colorKey = @"command-result";
675 : }
676 : }
677 :
678 : if (message != nil)
679 : {
680 : [monitor appendJSConsoleLine:message
681 : colorKey:colorKey
682 : emphasisRange:emphasisRange];
683 : }
684 : OOJSResumeTimeLimiter();
685 :
686 : OOJS_RETURN_VOID;
687 :
688 : OOJS_NATIVE_EXIT
689 : }
690 :
691 :
692 : // function clearConsole() : void
693 0 : static JSBool ConsoleClearConsole(JSContext *context, uintN argc, jsval *vp)
694 : {
695 : OOJS_NATIVE_ENTER(context)
696 :
697 : id monitor = nil;
698 :
699 : monitor = OOJSNativeObjectFromJSObject(context, OOJS_THIS);
700 : if (![monitor isKindOfClass:[OODebugMonitor class]])
701 : {
702 : OOJSReportError(context, @"Expected OODebugMonitor, got %@ in %s. %@", [monitor class], __PRETTY_FUNCTION__, @"This is an internal error, please report it.");
703 : return NO;
704 : }
705 :
706 : [monitor clearJSConsole];
707 : OOJS_RETURN_VOID;
708 :
709 : OOJS_NATIVE_EXIT
710 : }
711 :
712 :
713 : // function scriptStack() : Array
714 0 : static JSBool ConsoleScriptStack(JSContext *context, uintN argc, jsval *vp)
715 : {
716 : OOJS_NATIVE_ENTER(context)
717 :
718 : OOJS_RETURN_OBJECT([OOJSScript scriptStack]);
719 :
720 : OOJS_NATIVE_EXIT
721 : }
722 :
723 :
724 : // function inspectEntity(entity : Entity) : void
725 0 : static JSBool ConsoleInspectEntity(JSContext *context, uintN argc, jsval *vp)
726 : {
727 : OOJS_NATIVE_ENTER(context)
728 :
729 : Entity *entity = nil;
730 :
731 : if (JSValueToEntity(context, OOJS_ARGV[0], &entity))
732 : {
733 : OOJS_BEGIN_FULL_NATIVE(context)
734 : if ([entity respondsToSelector:@selector(inspect)])
735 : {
736 : [entity inspect];
737 : }
738 : OOJS_END_FULL_NATIVE
739 : }
740 :
741 : OOJS_RETURN_VOID;
742 :
743 : OOJS_NATIVE_EXIT
744 : }
745 :
746 :
747 : #if OO_DEBUG
748 : // function callObjC(selector : String [, ...]) : Object
749 : static JSBool ConsoleCallObjCMethod(JSContext *context, uintN argc, jsval *vp)
750 : {
751 : OOJS_NATIVE_ENTER(context)
752 :
753 : id object = nil;
754 : jsval result;
755 : BOOL OK;
756 :
757 : object = OOJSNativeObjectFromJSObject(context, OOJS_THIS);
758 : if (object == nil)
759 : {
760 : OOJSReportError(context, @"Attempt to call __callObjCMethod() for non-Objective-C object %@.", OOStringFromJSValueEvenIfNull(context, JS_THIS(context, vp)));
761 : return NO;
762 : }
763 :
764 : OOJSPauseTimeLimiter();
765 : result = JSVAL_VOID;
766 : OK = OOJSCallObjCObjectMethod(context, object, [object oo_jsClassName], argc, OOJS_ARGV, &result);
767 : OOJSResumeTimeLimiter();
768 :
769 : OOJS_SET_RVAL(result);
770 : return OK;
771 :
772 : OOJS_NATIVE_EXIT
773 : }
774 :
775 :
776 : // function __setUpCallObjC(object) -- object is expected to be Object.prototye.
777 : static JSBool ConsoleSetUpCallObjC(JSContext *context, uintN argc, jsval *vp)
778 : {
779 : OOJS_NATIVE_ENTER(context)
780 :
781 : if (EXPECT_NOT(!JSVAL_IS_OBJECT(OOJS_ARGV[0])))
782 : {
783 : OOJSReportBadArguments(context, @"Console", @"__setUpCallObjC", argc, OOJS_ARGV, nil, @"Object.prototype");
784 : return NO;
785 : }
786 :
787 : JSObject *obj = JSVAL_TO_OBJECT(OOJS_ARGV[0]);
788 : JS_DefineFunction(context, obj, "callObjC", ConsoleCallObjCMethod, 1, OOJS_METHOD_READONLY);
789 : OOJS_RETURN_VOID;
790 :
791 : OOJS_NATIVE_EXIT
792 : }
793 : #endif
794 :
795 :
796 : // function isExecutableJavaScript(this : Object, string : String) : Boolean
797 0 : static JSBool ConsoleIsExecutableJavaScript(JSContext *context, uintN argc, jsval *vp)
798 : {
799 : OOJS_NATIVE_ENTER(context)
800 :
801 : BOOL result = NO;
802 : JSObject *target = NULL;
803 :
804 : if (argc < 2 || !JS_ValueToObject(context, OOJS_ARGV[0], &target) || !JSVAL_IS_STRING(OOJS_ARGV[1]))
805 : {
806 : OOJS_RETURN_BOOL(NO); // Fail silently
807 : }
808 :
809 : // Not OOJS_BEGIN_FULL_NATIVE() - we use JSAPI while paused.
810 : OOJSPauseTimeLimiter();
811 :
812 : // FIXME: this must be possible using just JSAPI functions.
813 : NSString *string = OOStringFromJSValue(context, OOJS_ARGV[1]);
814 : NSData *stringData = [string dataUsingEncoding:NSUTF8StringEncoding];
815 : result = JS_BufferIsCompilableUnit(context, target, [stringData bytes], [stringData length]);
816 :
817 : OOJSResumeTimeLimiter();
818 :
819 : OOJS_RETURN_BOOL(result);
820 :
821 : OOJS_NATIVE_EXIT
822 : }
823 :
824 :
825 : // function displayMessagesInClass(class : String) : Boolean
826 0 : static JSBool ConsoleDisplayMessagesInClass(JSContext *context, uintN argc, jsval *vp)
827 : {
828 : OOJS_NATIVE_ENTER(context)
829 :
830 : NSString *messageClass = nil;
831 :
832 : messageClass = OOStringFromJSValue(context, OOJS_ARGV[0]);
833 : OOJS_RETURN_BOOL(messageClass != nil && OOLogWillDisplayMessagesInClass(messageClass));
834 :
835 : OOJS_NATIVE_EXIT
836 : }
837 :
838 :
839 : // function setDisplayMessagesInClass(class : String, flag : Boolean) : void
840 0 : static JSBool ConsoleSetDisplayMessagesInClass(JSContext *context, uintN argc, jsval *vp)
841 : {
842 : OOJS_NATIVE_ENTER(context)
843 :
844 : NSString *messageClass = nil;
845 : JSBool flag;
846 :
847 : messageClass = OOStringFromJSValue(context, OOJS_ARGV[0]);
848 : if (messageClass != nil && JS_ValueToBoolean(context, OOJS_ARGV[1], &flag))
849 : {
850 : OOLogSetDisplayMessagesInClass(messageClass, flag);
851 : }
852 : OOJS_RETURN_VOID;
853 :
854 : OOJS_NATIVE_EXIT
855 : }
856 :
857 :
858 : // function writeLogMarker() : void
859 0 : static JSBool ConsoleWriteLogMarker(JSContext *context, uintN argc, jsval *vp)
860 : {
861 : OOJS_NATIVE_ENTER(context)
862 :
863 : OOLogInsertMarker();
864 : OOJS_RETURN_VOID;
865 :
866 : OOJS_NATIVE_EXIT
867 : }
868 :
869 :
870 : // function writeMemoryStats() : void
871 0 : static JSBool ConsoleWriteMemoryStats(JSContext *context, uintN argc, jsval *vp)
872 : {
873 : OOJS_NATIVE_ENTER(context)
874 :
875 : OOJS_BEGIN_FULL_NATIVE(context)
876 : [[OODebugMonitor sharedDebugMonitor] dumpMemoryStatistics];
877 : OOJS_END_FULL_NATIVE
878 :
879 : OOJS_RETURN_VOID;
880 :
881 : OOJS_NATIVE_EXIT
882 : }
883 :
884 :
885 : // function writeJSMemoryStats() : void
886 0 : static JSBool ConsoleWriteJSMemoryStats(JSContext *context, uintN argc, jsval *vp)
887 : {
888 : OOJS_NATIVE_ENTER(context)
889 :
890 : OOJS_BEGIN_FULL_NATIVE(context)
891 : [[OODebugMonitor sharedDebugMonitor] dumpJSMemoryStatistics];
892 : OOJS_END_FULL_NATIVE
893 :
894 : OOJS_RETURN_VOID;
895 :
896 : OOJS_NATIVE_EXIT
897 : }
898 :
899 :
900 : // function garbageCollect() : string
901 0 : static JSBool ConsoleGarbageCollect(JSContext *context, uintN argc, jsval *vp)
902 : {
903 : OOJS_NATIVE_ENTER(context)
904 :
905 : uint32_t bytesBefore = JS_GetGCParameter(JS_GetRuntime(context), JSGC_BYTES);
906 : JS_GC(context);
907 : uint32_t bytesAfter = JS_GetGCParameter(JS_GetRuntime(context), JSGC_BYTES);
908 :
909 : OOJS_RETURN_OBJECT(([NSString stringWithFormat:@"Bytes before: %u Bytes after: %u", bytesBefore, bytesAfter]));
910 :
911 : OOJS_NATIVE_EXIT
912 : }
913 :
914 :
915 : #if DEBUG
916 : typedef struct
917 : {
918 : JSContext *context;
919 : FILE *file;
920 : } DumpCallbackData;
921 :
922 : static void DumpCallback(const char *name, void *rp, JSGCRootType type, void *datap)
923 : {
924 : assert(type == JS_GC_ROOT_VALUE_PTR || type == JS_GC_ROOT_GCTHING_PTR);
925 :
926 : DumpCallbackData *data = datap;
927 :
928 : const char *typeString = "unknown type";
929 : jsval value;
930 : switch (type)
931 : {
932 : case JS_GC_ROOT_VALUE_PTR:
933 : typeString = "value";
934 : value = *(jsval *)rp;
935 : break;
936 :
937 : case JS_GC_ROOT_GCTHING_PTR:
938 : typeString = "gc-thing";
939 : value = OBJECT_TO_JSVAL(*(JSObject **)rp);
940 : }
941 :
942 : fprintf(data->file, "%s @ %p (%s): %s\n", name, rp, typeString, [OOJSDescribeValue(data->context, value, NO) UTF8String]);
943 : }
944 :
945 :
946 : static JSBool ConsoleDumpNamedRoots(JSContext *context, uintN argc, jsval *vp)
947 : {
948 : OOJS_NATIVE_ENTER(context)
949 :
950 : NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
951 :
952 : BOOL OK = NO;
953 : NSString *path = [[ResourceManager diagnosticFileLocation] stringByAppendingPathComponent:@"js-roots.txt"];
954 : FILE *file = fopen([path UTF8String], "w");
955 : if (file != NULL)
956 : {
957 : DumpCallbackData data =
958 : {
959 : .context = context,
960 : .file = file
961 : };
962 : JS_DumpNamedRoots(JS_GetRuntime(context), DumpCallback, &data);
963 : fclose(file);
964 : OK = YES;
965 : }
966 :
967 : [pool release];
968 : OOJS_RETURN_BOOL(OK);
969 :
970 : OOJS_NATIVE_EXIT
971 : }
972 :
973 :
974 : static JSBool ConsoleDumpHeap(JSContext *context, uintN argc, jsval *vp)
975 : {
976 : OOJS_NATIVE_ENTER(context)
977 :
978 : BOOL OK = NO;
979 : NSString *path = [[ResourceManager diagnosticFileLocation] stringByAppendingPathComponent:@"js-heaps.txt"];
980 : FILE *file = fopen([path UTF8String], "w");
981 : if (file != NULL)
982 : {
983 : OK = JS_DumpHeap(context, file, NULL, 0, NULL, SIZE_MAX, NULL);
984 : fclose(file);
985 : }
986 :
987 : OOJS_RETURN_BOOL(OK);
988 :
989 : OOJS_NATIVE_EXIT
990 : }
991 : #endif
992 :
993 :
994 : #if OOJS_PROFILE
995 :
996 : // function profile(func : function [, Object this = debugConsole.script]) : String
997 0 : static JSBool ConsoleProfile(JSContext *context, uintN argc, jsval *vp)
998 : {
999 : OOJS_NATIVE_ENTER(context)
1000 :
1001 : if (EXPECT_NOT(OOJSIsProfiling()))
1002 : {
1003 : OOJSReportError(context, @"Profiling functions may not be called while already profiling.");
1004 : return NO;
1005 : }
1006 :
1007 : NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1008 : OOTimeProfile *profile = nil;
1009 :
1010 : JSBool result = PerformProfiling(context, @"profile", argc, OOJS_ARGV, NULL, NO, &profile);
1011 : if (result)
1012 : {
1013 : OOJS_SET_RVAL(OOJSValueFromNativeObject(context, [profile description]));
1014 : }
1015 :
1016 : [pool release];
1017 : return result;
1018 :
1019 : OOJS_NATIVE_EXIT
1020 : }
1021 :
1022 :
1023 : // function getProfile(func : function [, Object this = debugConsole.script]) : Object { totalTime : Number, jsTime : Number, extensionTime : Number }
1024 0 : static JSBool ConsoleGetProfile(JSContext *context, uintN argc, jsval *vp)
1025 : {
1026 : OOJS_NATIVE_ENTER(context)
1027 :
1028 :
1029 : if (EXPECT_NOT(OOJSIsProfiling()))
1030 : {
1031 : OOJSReportError(context, @"Profiling functions may not be called while already profiling.");
1032 : return NO;
1033 : }
1034 :
1035 : NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1036 : OOTimeProfile *profile = nil;
1037 :
1038 : JSBool result = PerformProfiling(context, @"getProfile", argc, OOJS_ARGV, NULL, NO, &profile);
1039 : if (result)
1040 : {
1041 : OOJS_SET_RVAL(OOJSValueFromNativeObject(context, profile));
1042 : }
1043 :
1044 : [pool release];
1045 : return result;
1046 :
1047 : OOJS_NATIVE_EXIT
1048 : }
1049 :
1050 :
1051 : // function trace(func : function [, Object this = debugConsole.script]) : [return type of func]
1052 0 : static JSBool ConsoleTrace(JSContext *context, uintN argc, jsval *vp)
1053 : {
1054 : OOJS_NATIVE_ENTER(context)
1055 :
1056 : if (EXPECT_NOT(OOJSIsProfiling()))
1057 : {
1058 : OOJSReportError(context, @"Profiling functions may not be called while already profiling.");
1059 : return NO;
1060 : }
1061 :
1062 : NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1063 : jsval rval;
1064 :
1065 : JSBool result = PerformProfiling(context, @"trace", argc, OOJS_ARGV, &rval, YES, NULL);
1066 : if (result)
1067 : {
1068 : OOJS_SET_RVAL(rval);
1069 : }
1070 :
1071 : [pool release];
1072 : return result;
1073 :
1074 : OOJS_NATIVE_EXIT
1075 : }
1076 :
1077 :
1078 0 : static JSBool PerformProfiling(JSContext *context, NSString *nominalFunction, uintN argc, jsval *argv, jsval *outRval, BOOL trace, OOTimeProfile **outProfile)
1079 : {
1080 : // Get function.
1081 : jsval function = argv[0];
1082 : if (!OOJSValueIsFunction(context, function))
1083 : {
1084 : OOJSReportBadArguments(context, @"Console", nominalFunction, 1, argv, nil, @"function");
1085 : return NO;
1086 : }
1087 :
1088 : // Get "this" object.
1089 : jsval this;
1090 : if (argc > 1) this = argv[1];
1091 : else
1092 : {
1093 : jsval debugConsole = OOJSValueFromNativeObject(context, [OODebugMonitor sharedDebugMonitor]);
1094 : assert(JSVAL_IS_OBJECT(debugConsole) && !JSVAL_IS_NULL(debugConsole));
1095 : JS_GetProperty(context, JSVAL_TO_OBJECT(debugConsole), "script", &this);
1096 : }
1097 :
1098 : JSObject *thisObj;
1099 : if (!JS_ValueToObject(context, this, &thisObj)) thisObj = NULL;
1100 :
1101 : jsval ignored;
1102 : if (outRval == NULL) outRval = &ignored;
1103 :
1104 : // Fiddle with time limiter.
1105 : // We want to save the current limit, reset the limiter, and set the time limit to a long time.
1106 0 : #define LONG_TIME (1e7) // A long time - 115.7 days - but, crucially, finite.
1107 :
1108 : OOTimeDelta originalLimit = OOJSGetTimeLimiterLimit();
1109 : OOJSSetTimeLimiterLimit(LONG_TIME);
1110 : OOJSResetTimeLimiter();
1111 :
1112 : OOJSBeginProfiling(trace);
1113 :
1114 : // Call the function.
1115 : BOOL result = JS_CallFunctionValue(context, thisObj, function, 0, NULL, outRval);
1116 :
1117 : // Get results.
1118 : OOTimeProfile *profile = OOJSEndProfiling();
1119 : if (outProfile != NULL) *outProfile = profile;
1120 :
1121 : // Restore original timer state.
1122 : OOJSSetTimeLimiterLimit(originalLimit);
1123 : OOJSResetTimeLimiter();
1124 :
1125 : JS_ReportPendingException(context);
1126 :
1127 : return result;
1128 : }
1129 :
1130 : #endif // OOJS_PROFILE
1131 :
1132 : #endif /* OO_EXCLUDE_DEBUG_SUPPORT */
|