LCOV - code coverage report
Current view: top level - Core/Scripting - OOJavaScriptEngine.m (source / functions) Hit Total Coverage
Test: coverxygen.info Lines: 0 106 0.0 %
Date: 2025-05-28 07:50:54 Functions: 0 0 -

          Line data    Source code
       1           0 : /*
       2             : 
       3             : OOJavaScriptEngine.m
       4             : 
       5             : JavaScript support for Oolite
       6             : Copyright (C) 2007-2013 David Taylor and Jens Ayton.
       7             : 
       8             : This program is free software; you can redistribute it and/or
       9             : modify it under the terms of the GNU General Public License
      10             : as published by the Free Software Foundation; either version 2
      11             : of the License, or (at your option) any later version.
      12             : 
      13             : This program is distributed in the hope that it will be useful,
      14             : but WITHOUT ANY WARRANTY; without even the implied warranty of
      15             : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      16             : GNU General Public License for more details.
      17             : 
      18             : You should have received a copy of the GNU General Public License
      19             : along with this program; if not, write to the Free Software
      20             : Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
      21             : MA 02110-1301, USA.
      22             : 
      23             : */
      24             : 
      25             : #include <jsdbgapi.h>
      26             : #import "OOJavaScriptEngine.h"
      27             : #import "OOJSEngineTimeManagement.h"
      28             : #import "OOJSScript.h"
      29             : 
      30             : #import "OOCollectionExtractors.h"
      31             : #import "Universe.h"
      32             : #import "OOPlanetEntity.h"
      33             : #import "NSStringOOExtensions.h"
      34             : #import "OOWeakReference.h"
      35             : #import "EntityOOJavaScriptExtensions.h"
      36             : #import "ResourceManager.h"
      37             : #import "NSNumberOOExtensions.h"
      38             : #import "OOConstToJSString.h"
      39             : #import "OOVisualEffectEntity.h"
      40             : #import "OOWaypointEntity.h"
      41             : 
      42             : #import "OOJSGlobal.h"
      43             : #import "OOJSMissionVariables.h"
      44             : #import "OOJSMission.h"
      45             : #import "OOJSVector.h"
      46             : #import "OOJSQuaternion.h"
      47             : #import "OOJSEntity.h"
      48             : #import "OOJSShip.h"
      49             : #import "OOJSStation.h"
      50             : #import "OOJSDock.h"
      51             : #import "OOJSVisualEffect.h"
      52             : #import "OOJSExhaustPlume.h"
      53             : #import "OOJSFlasher.h"
      54             : #import "OOJSWormhole.h"
      55             : #import "OOJSWaypoint.h"
      56             : #import "OOJSPlayer.h"
      57             : #import "OOJSPlayerShip.h"
      58             : #import "OOJSManifest.h"
      59             : #import "OOJSPlanet.h"
      60             : #import "OOJSSystem.h"
      61             : #import "OOJSOolite.h"
      62             : #import "OOJSTimer.h"
      63             : #import "OOJSClock.h"
      64             : #import "OOJSSun.h"
      65             : #import "OOJSWorldScripts.h"
      66             : #import "OOJSSound.h"
      67             : #import "OOJSSoundSource.h"
      68             : #import "OOJSSpecialFunctions.h"
      69             : #import "OOJSSystemInfo.h"
      70             : #import "OOJSEquipmentInfo.h"
      71             : #import "OOJSShipGroup.h"
      72             : #import "OOJSFrameCallbacks.h"
      73             : #import "OOJSFont.h"
      74             : 
      75             : #import "OOProfilingStopwatch.h"
      76             : #import "OOLoggingExtended.h"
      77             : 
      78             : #include <stdlib.h>
      79             : 
      80             : 
      81           0 : #define OOJSENGINE_JSVERSION            JSVERSION_ECMA_5
      82             : #ifdef DEBUG
      83             : #define JIT_OPTIONS                                     0
      84             : #else
      85           0 : #define JIT_OPTIONS                                     JSOPTION_JIT | JSOPTION_METHODJIT | JSOPTION_PROFILING
      86             : #endif
      87           0 : #define OOJSENGINE_CONTEXT_OPTIONS      JSOPTION_VAROBJFIX | JSOPTION_RELIMIT | JSOPTION_ANONFUNFIX | JIT_OPTIONS
      88             : 
      89             : 
      90           0 : #define OOJS_STACK_SIZE                         8192
      91           0 : #define OOJS_RUNTIME_SIZE_MiB                   256
      92             : 
      93             : 
      94           0 : static OOJavaScriptEngine       *sSharedEngine = nil;
      95           0 : static unsigned                         sErrorHandlerStackSkip = 0;
      96             : 
      97           0 : JSContext                                       *gOOJSMainThreadContext = NULL;
      98             : 
      99             : 
     100           0 : NSString * const kOOJavaScriptEngineWillResetNotification = @"org.aegidian.oolite OOJavaScriptEngine will reset";
     101           0 : NSString * const kOOJavaScriptEngineDidResetNotification = @"org.aegidian.oolite OOJavaScriptEngine did reset";
     102             : 
     103             : 
     104             : #if OOJSENGINE_MONITOR_SUPPORT
     105             : 
     106             : @interface OOJavaScriptEngine (OOMonitorSupportInternal)
     107             : 
     108           0 : - (void)sendMonitorError:(JSErrorReport *)errorReport
     109             :                          withMessage:(NSString *)message
     110             :                            inContext:(JSContext *)context;
     111             : 
     112           0 : - (void)sendMonitorLogMessage:(NSString *)message
     113             :                          withMessageClass:(NSString *)messageClass
     114             :                                         inContext:(JSContext *)context;
     115             : 
     116             : @end
     117             : 
     118             : #endif
     119             : 
     120             : 
     121             : @interface OOJavaScriptEngine (Private)
     122             : 
     123           0 : - (BOOL) lookUpStandardClassPointers;
     124           0 : - (void) registerStandardObjectConverters;
     125             : 
     126           0 : - (void) createMainThreadContext;
     127           0 : - (void) destroyMainThreadContext;
     128             : 
     129             : @end
     130             : 
     131             : 
     132             : static void ReportJSError(JSContext *context, const char *message, JSErrorReport *report);
     133             : 
     134             : static id JSArrayConverter(JSContext *context, JSObject *object);
     135             : static id JSStringConverter(JSContext *context, JSObject *object);
     136             : static id JSNumberConverter(JSContext *context, JSObject *object);
     137             : static id JSBooleanConverter(JSContext *context, JSObject *object);
     138             : 
     139             : 
     140             : static void UnregisterObjectConverters(void);
     141             : static void UnregisterSubclasses(void);
     142             : 
     143             : 
     144           0 : static void ReportJSError(JSContext *context, const char *message, JSErrorReport *report)
     145             : {
     146             :         NSString                        *severity = @"error";
     147             :         NSString                        *messageText = nil;
     148             :         NSString                        *lineBuf = nil;
     149             :         NSString                        *messageClass = nil;
     150             :         NSString                        *highlight = @"*****";
     151             :         NSString                        *activeScript = nil;
     152             :         OOJavaScriptEngine      *jsEng = [OOJavaScriptEngine sharedEngine];
     153             :         BOOL                            showLocation = [jsEng showErrorLocations];
     154             :         
     155             :         // Not OOJS_BEGIN_FULL_NATIVE() - we use JSAPI while paused.
     156             :         OOJSPauseTimeLimiter();
     157             :         
     158             :         jschar empty[1] = { 0 };
     159             :         JSErrorReport blankReport =
     160             :         {
     161             :                 .filename = "<unspecified file>",
     162             :                 .linebuf = "",
     163             :                 .uclinebuf = empty,
     164             :                 .uctokenptr = empty,
     165             :                 .ucmessage = empty
     166             :         };
     167             :         if (EXPECT_NOT(report == NULL))  report = &blankReport;
     168             :         if (EXPECT_NOT(message == NULL || *message == '\0'))  message = "<unspecified error>";
     169             :         
     170             :         // Type of problem: error, warning or exception? (Strict flag wilfully ignored.)
     171             :         if (report->flags & JSREPORT_EXCEPTION) severity = @"exception";
     172             :         else if (report->flags & JSREPORT_WARNING)
     173             :         {
     174             :                 severity = @"warning";
     175             :                 highlight = @"-----";
     176             :         }
     177             :         
     178             :         // The error message itself
     179             :         messageText = [NSString stringWithUTF8String:message];
     180             :         
     181             :         // Get offending line, if present, and trim trailing line breaks
     182             :         lineBuf = [NSString stringWithUTF16String:report->uclinebuf];
     183             :         while ([lineBuf hasSuffix:@"\n"] || [lineBuf hasSuffix:@"\r"])  lineBuf = [lineBuf substringToIndex:[lineBuf length] - 1];
     184             :         
     185             :         // Get string for error number, for useful log message classes
     186             :         NSDictionary *errorNames = [ResourceManager dictionaryFromFilesNamed:@"javascript-errors.plist" inFolder:@"Config" andMerge:YES];
     187             :         NSString *errorNumberStr = [NSString stringWithFormat:@"%u", report->errorNumber];
     188             :         NSString *errorName = [errorNames oo_stringForKey:errorNumberStr];
     189             :         if (errorName == nil)  errorName = errorNumberStr;
     190             :         
     191             :         // Log message class
     192             :         messageClass = [NSString stringWithFormat:@"script.javaScript.%@.%@", severity, errorName];
     193             :         
     194             :         // Skip the rest if this is a warning being ignored.
     195             :         if ((report->flags & JSREPORT_WARNING) == 0 || OOLogWillDisplayMessagesInClass(messageClass))
     196             :         {
     197             :                 // First line: problem description
     198             :                 // avoid windows DEP exceptions!
     199             :                 OOJSScript *thisScript = [[OOJSScript currentlyRunningScript] weakRetain];
     200             :                 activeScript = [[thisScript weakRefUnderlyingObject] displayName];
     201             :                 [thisScript release];
     202             :                 
     203             :                 if (activeScript == nil)  activeScript = @"<unidentified script>";
     204             :                 OOLog(messageClass, @"%@ JavaScript %@ (%@): %@", highlight, severity, activeScript, messageText);
     205             :                 
     206             :                 if (showLocation && sErrorHandlerStackSkip == 0 && report->filename != NULL)
     207             :                 {
     208             :                         // Second line: where error occured, and line if provided. (The line is only provided for compile-time errors, not run-time errors.)
     209             :                         if ([lineBuf length] != 0)
     210             :                         {
     211             :                                 OOLog(messageClass, @"      %s, line %d: %@", report->filename, report->lineno, lineBuf);
     212             :                         }
     213             :                         else
     214             :                         {
     215             :                                 OOLog(messageClass, @"      %s, line %d.", report->filename, report->lineno);
     216             :                         }
     217             :                 }
     218             :                 
     219             : #ifndef NDEBUG
     220             :                 BOOL dump;
     221             :                 if (report->flags & JSREPORT_WARNING)  dump = [jsEng dumpStackForWarnings];
     222             :                 else  dump = [jsEng dumpStackForErrors];
     223             :                 if (dump)  OOJSDumpStack(context);
     224             : #endif
     225             :                 
     226             : #if OOJSENGINE_MONITOR_SUPPORT
     227             :                 JSExceptionState *exState = JS_SaveExceptionState(context);
     228             :                 [[OOJavaScriptEngine sharedEngine] sendMonitorError:report
     229             :                                                                                                 withMessage:messageText
     230             :                                                                                                   inContext:context];
     231             :                 JS_RestoreExceptionState(context, exState);
     232             : #endif
     233             :         }
     234             :         
     235             :         OOJSResumeTimeLimiter();
     236             : }
     237             : 
     238             : 
     239             : //===========================================================================
     240             : // JavaScript engine initialisation and shutdown
     241             : //===========================================================================
     242             : 
     243             : @implementation OOJavaScriptEngine
     244             : 
     245             : + (OOJavaScriptEngine *) sharedEngine
     246             : {
     247             :         if (sSharedEngine == nil)  sSharedEngine = [[self alloc] init];
     248             :         
     249             :         return sSharedEngine;
     250             : }
     251             : 
     252             : 
     253             : - (void) runMissionCallback
     254             : {
     255             :         MissionRunCallback();
     256             : }
     257             : 
     258             : 
     259           0 : - (id) init
     260             : {
     261             :         NSAssert(sSharedEngine == nil, @"Attempt to create multiple OOJavaScriptEngines.");
     262             :         
     263             :         if (!(self = [super init]))  return nil;
     264             :         sSharedEngine = self;
     265             :         
     266             :         JS_SetCStringsAreUTF8();
     267             :         
     268             :         NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
     269             : #ifndef NDEBUG
     270             :         /*      Set stack trace preferences from preferences. These will be overriden
     271             :                 by the debug OXP script if installed, but being able to enable traces
     272             :                 without setting up the debug console could be useful for debugging
     273             :                 users' problems.
     274             :         */
     275             :         [self setDumpStackForErrors:[defaults boolForKey:@"dump-stack-for-errors"]];
     276             :         [self setDumpStackForWarnings:[defaults boolForKey:@"dump-stack-for-warnings"]];
     277             : #endif
     278             :         
     279             :         assert(sizeof(jschar) == sizeof(unichar));
     280             :         
     281             :         // initialize the JS run time, and return result in runtime.
     282             :         uint32_t jsRuntimeInMiB = [defaults oo_intForKey:@"jsruntime-size-mib" defaultValue:OOJS_RUNTIME_SIZE_MiB];
     283             :         _runtime = JS_NewRuntime(jsRuntimeInMiB * 1024L * 1024L);
     284             :         
     285             :         // if runtime creation failed, end the program here.
     286             :         if (_runtime == NULL)
     287             :         {
     288             :                 OOLog(@"script.javaScript.init.error", @"***** FATAL ERROR: failed to create JavaScript runtime with size %uMiB.", jsRuntimeInMiB);
     289             :                 exit(1);
     290             :         }
     291             :         
     292             :         // OOJSTimeManagementInit() must be called before any context is created!
     293             :         OOJSTimeManagementInit(self, _runtime);
     294             :         
     295             :         [self createMainThreadContext];
     296             :         
     297             :         return self;
     298             : }
     299             : 
     300             : 
     301           0 : - (void) createMainThreadContext
     302             : {
     303             :         NSAssert(gOOJSMainThreadContext == NULL, @"-[OOJavaScriptEngine createMainThreadContext] called while the main thread context exists.");
     304             :         
     305             :         // create a context and associate it with the JS runtime.
     306             :         gOOJSMainThreadContext = JS_NewContext(_runtime, OOJS_STACK_SIZE);
     307             :         
     308             :         // if context creation failed, end the program here.
     309             :         if (gOOJSMainThreadContext == NULL)
     310             :         {
     311             :                 OOLog(@"script.javaScript.init.error", @"%@", @"***** FATAL ERROR: failed to create JavaScript context.");
     312             :                 exit(1);
     313             :         }
     314             :         
     315             :         JS_BeginRequest(gOOJSMainThreadContext);
     316             :         
     317             :         JS_SetOptions(gOOJSMainThreadContext, OOJSENGINE_CONTEXT_OPTIONS);
     318             :         JS_SetVersion(gOOJSMainThreadContext, OOJSENGINE_JSVERSION);
     319             :         
     320             : #if JS_GC_ZEAL
     321             :         uint8_t gcZeal = [[NSUserDefaults standardUserDefaults]  oo_unsignedCharForKey:@"js-gc-zeal"];
     322             :         if (gcZeal > 0)
     323             :         {
     324             :                 // Useful js-gc-zeal values are 0 (off), 1 and 2.
     325             :                 OOLog(@"script.javaScript.debug.gcZeal", @"Setting JavaScript garbage collector zeal to %u.", gcZeal);
     326             :                 JS_SetGCZeal(gOOJSMainThreadContext, gcZeal);
     327             :         }
     328             : #endif
     329             :         
     330             :         JS_SetErrorReporter(gOOJSMainThreadContext, ReportJSError);
     331             :         
     332             :         // Create the global object.
     333             :         CreateOOJSGlobal(gOOJSMainThreadContext, &_globalObject);
     334             :         
     335             :         // Initialize the built-in JS objects and the global object.
     336             :         JS_InitStandardClasses(gOOJSMainThreadContext, _globalObject);
     337             :         if (![self lookUpStandardClassPointers])
     338             :         {
     339             :                 OOLog(@"script.javaScript.init.error", @"%@", @"***** FATAL ERROR: failed to look up standard JavaScript classes.");
     340             :                 exit(1);
     341             :         }
     342             :         [self registerStandardObjectConverters];
     343             :         
     344             :         SetUpOOJSGlobal(gOOJSMainThreadContext, _globalObject);
     345             :         OOConstToJSStringInit(gOOJSMainThreadContext);
     346             :         
     347             :         // Initialize Oolite classes.
     348             :         InitOOJSMissionVariables(gOOJSMainThreadContext, _globalObject);
     349             :         InitOOJSMission(gOOJSMainThreadContext, _globalObject);
     350             :         InitOOJSOolite(gOOJSMainThreadContext, _globalObject);
     351             :         InitOOJSVector(gOOJSMainThreadContext, _globalObject);
     352             :         InitOOJSQuaternion(gOOJSMainThreadContext, _globalObject);
     353             :         InitOOJSSystem(gOOJSMainThreadContext, _globalObject);
     354             :         InitOOJSEntity(gOOJSMainThreadContext, _globalObject);
     355             :         InitOOJSShip(gOOJSMainThreadContext, _globalObject);
     356             :         InitOOJSStation(gOOJSMainThreadContext, _globalObject);
     357             :         InitOOJSDock(gOOJSMainThreadContext, _globalObject);
     358             :         InitOOJSVisualEffect(gOOJSMainThreadContext, _globalObject);
     359             :         InitOOJSExhaustPlume(gOOJSMainThreadContext, _globalObject);
     360             :         InitOOJSFlasher(gOOJSMainThreadContext, _globalObject);
     361             :         InitOOJSWormhole(gOOJSMainThreadContext, _globalObject);
     362             :         InitOOJSWaypoint(gOOJSMainThreadContext, _globalObject);
     363             :         InitOOJSPlayer(gOOJSMainThreadContext, _globalObject);
     364             :         InitOOJSPlayerShip(gOOJSMainThreadContext, _globalObject);
     365             :         InitOOJSManifest(gOOJSMainThreadContext, _globalObject);
     366             :         InitOOJSSun(gOOJSMainThreadContext, _globalObject);
     367             :         InitOOJSPlanet(gOOJSMainThreadContext, _globalObject);
     368             :         InitOOJSScript(gOOJSMainThreadContext, _globalObject);
     369             :         InitOOJSTimer(gOOJSMainThreadContext, _globalObject);
     370             :         InitOOJSClock(gOOJSMainThreadContext, _globalObject);
     371             :         InitOOJSWorldScripts(gOOJSMainThreadContext, _globalObject);
     372             :         InitOOJSSound(gOOJSMainThreadContext, _globalObject);
     373             :         InitOOJSSoundSource(gOOJSMainThreadContext, _globalObject);
     374             :         InitOOJSSpecialFunctions(gOOJSMainThreadContext, _globalObject);
     375             :         InitOOJSSystemInfo(gOOJSMainThreadContext, _globalObject);
     376             :         InitOOJSEquipmentInfo(gOOJSMainThreadContext, _globalObject);
     377             :         InitOOJSShipGroup(gOOJSMainThreadContext, _globalObject);
     378             :         InitOOJSFrameCallbacks(gOOJSMainThreadContext, _globalObject);
     379             :         InitOOJSFont(gOOJSMainThreadContext, _globalObject);
     380             :         
     381             :         // Run prefix scripts.
     382             :         [OOJSScript jsScriptFromFileNamed:@"oolite-global-prefix.js"
     383             :                                                    properties:[NSDictionary dictionaryWithObject:JSSpecialFunctionsObjectWrapper(gOOJSMainThreadContext)
     384             :                                                                                                                                   forKey:@"special"]];
     385             :         
     386             :         JS_EndRequest(gOOJSMainThreadContext);
     387             :         
     388             :         OOLog(@"script.javaScript.init.success", @"%@", @"Set up JavaScript context.");
     389             : }
     390             : 
     391             : 
     392           0 : - (void) destroyMainThreadContext
     393             : {
     394             :         if (gOOJSMainThreadContext != NULL)
     395             :         {
     396             :                 JSContext *context = OOJSAcquireContext();
     397             :                 JS_ClearScope(gOOJSMainThreadContext, _globalObject);
     398             :                 
     399             :                 _globalObject = NULL;
     400             :                 _objectClass = NULL;
     401             :                 _stringClass = NULL;
     402             :                 _arrayClass = NULL;
     403             :                 _numberClass = NULL;
     404             :                 _booleanClass = NULL;
     405             :                 
     406             :                 UnregisterObjectConverters();
     407             :                 UnregisterSubclasses();
     408             :                 OOConstToJSStringDestroy();
     409             :                 
     410             :                 OOJSRelinquishContext(context);
     411             :                 
     412             :                 _globalObject = NULL;
     413             :                 JS_DestroyContext(gOOJSMainThreadContext);      // Forces unconditional GC.
     414             :                 gOOJSMainThreadContext = NULL;
     415             :         }
     416             : }
     417             : 
     418             : 
     419             : - (BOOL) reset
     420             : {
     421             :         NSAssert(gOOJSMainThreadContext != NULL, @"JavaScript engine not active. Can't reset.");
     422             :         
     423             :         OOJSFrameCallbacksRemoveAll();
     424             :         
     425             : # if 0
     426             :         // deferred JS reset - test harness.
     427             :         static int counter = 3;         // loading a savegame with different strict mode calls js reset twice
     428             :         if (counter-- == 0) {
     429             :         counter = 3;
     430             :         OOLog(@"script.javascript.init.error", @"%@", @"JavaScript processes still pending. Can't reset JavaScript engine.");
     431             :                 return NO;
     432             :         }
     433             :         else
     434             :         {
     435             :                 OOLog(@"script.javascript.init", @"%@", @"JavaScript reset successful.");
     436             :         }
     437             : #endif
     438             :                 
     439             : #if JS_THREADSAFE
     440             :         //NSAssert(!JS_IsInRequest(gOOJSMainThreadContext), @"JavaScript processes still pending. Can't reset JavaScript engine.");
     441             :         
     442             :         if (JS_IsInRequest(gOOJSMainThreadContext))
     443             :         {
     444             :                 // some threads are still pending, this should mean timers are still being removed.
     445             :                 OOLog(@"script.javascript.init.error", @"%@", @"JavaScript processes still pending. Can't reset JavaScript engine.");
     446             :                 return NO;
     447             :         }
     448             :         else
     449             :         {
     450             :                 OOLog(@"script.javascript.init", @"%@", @"JavaScript reset successful.");
     451             :         }
     452             : #endif
     453             :         
     454             :         JSContext *context = OOJSAcquireContext();
     455             :         [[NSNotificationCenter defaultCenter] postNotificationName:kOOJavaScriptEngineWillResetNotification object:self];
     456             :         OOJSRelinquishContext(context);
     457             :         
     458             :         [self destroyMainThreadContext];
     459             :         [self createMainThreadContext];
     460             :         
     461             :         context = OOJSAcquireContext();
     462             :         [[NSNotificationCenter defaultCenter] postNotificationName:kOOJavaScriptEngineDidResetNotification object:self];
     463             :         OOJSRelinquishContext(context);
     464             :         
     465             :         [self garbageCollectionOpportunity:YES];
     466             :         return YES;
     467             : }
     468             : 
     469             : 
     470           0 : - (void) dealloc
     471             : {
     472             :         sSharedEngine = nil;
     473             :         
     474             :         OOJSFrameCallbacksRemoveAll();
     475             :         
     476             :         [self destroyMainThreadContext];
     477             :         JS_DestroyRuntime(_runtime);
     478             :         
     479             :         [super dealloc];
     480             : }
     481             : 
     482             : 
     483             : - (JSObject *) globalObject
     484             : {
     485             :         return _globalObject;
     486             : }
     487             : 
     488             : 
     489             : - (BOOL) callJSFunction:(jsval)function
     490             :                           forObject:(JSObject *)jsThis
     491             :                                    argc:(uintN)argc
     492             :                                    argv:(jsval *)argv
     493             :                                  result:(jsval *)outResult
     494             : {
     495             :         JSContext                                       *context = NULL;
     496             :         BOOL                                            result;
     497             :         
     498             :         NSParameterAssert(OOJSValueIsFunction(context, function));
     499             :         
     500             :         context = OOJSAcquireContext();
     501             :         
     502             :         OOJSStartTimeLimiter();
     503             :         result = JS_CallFunctionValue(context, jsThis, function, argc, argv, outResult);
     504             :         OOJSStopTimeLimiter();
     505             :         
     506             :         JS_ReportPendingException(context);
     507             :         OOJSRelinquishContext(context);
     508             :         
     509             :         return result;
     510             : }
     511             : 
     512             : 
     513             : - (void) removeGCObjectRoot:(JSObject **)rootPtr
     514             : {
     515             :         JSContext *context = OOJSAcquireContext();
     516             :         JS_RemoveObjectRoot(context, rootPtr);
     517             :         OOJSRelinquishContext(context);
     518             : }
     519             : 
     520             : 
     521             : - (void) removeGCValueRoot:(jsval *)rootPtr
     522             : {
     523             :         JSContext *context = OOJSAcquireContext();
     524             :         JS_RemoveValueRoot(context, rootPtr);
     525             :         OOJSRelinquishContext(context);
     526             : }
     527             : 
     528             : 
     529             : - (void) garbageCollectionOpportunity:(BOOL)force
     530             : {
     531             :         JSContext *context = OOJSAcquireContext();
     532             :         if (force)
     533             :         {
     534             :                 JS_GC(context);
     535             :         }
     536             :         else
     537             :         {
     538             :                 JS_MaybeGC(context);
     539             :         }
     540             :         OOJSRelinquishContext(context);
     541             : }
     542             : 
     543             : 
     544             : - (BOOL) showErrorLocations
     545             : {
     546             :         return _showErrorLocations;
     547             : }
     548             : 
     549             : 
     550             : - (void) setShowErrorLocations:(BOOL)value
     551             : {
     552             :         _showErrorLocations = !!value;
     553             : }
     554             : 
     555             : 
     556             : - (JSClass *) objectClass
     557             : {
     558             :         return _objectClass;
     559             : }
     560             : 
     561             : 
     562             : - (JSClass *) stringClass
     563             : {
     564             :         return _stringClass;
     565             : }
     566             : 
     567             : 
     568             : - (JSClass *) arrayClass
     569             : {
     570             :         return _arrayClass;
     571             : }
     572             : 
     573             : 
     574             : - (JSClass *) numberClass
     575             : {
     576             :         return _numberClass;
     577             : }
     578             : 
     579             : 
     580             : - (JSClass *) booleanClass
     581             : {
     582             :         return _booleanClass;
     583             : }
     584             : 
     585             : 
     586           0 : - (BOOL) lookUpStandardClassPointers
     587             : {
     588             :         JSObject                                *templateObject = NULL;
     589             :         
     590             :         templateObject = JS_NewObject(gOOJSMainThreadContext, NULL, NULL, NULL);
     591             :         if (EXPECT_NOT(templateObject == NULL))  return NO;
     592             :         _objectClass = OOJSGetClass(gOOJSMainThreadContext, templateObject);
     593             :         
     594             :         if (EXPECT_NOT(!JS_ValueToObject(gOOJSMainThreadContext, JS_GetEmptyStringValue(gOOJSMainThreadContext), &templateObject)))  return NO;
     595             :         _stringClass = OOJSGetClass(gOOJSMainThreadContext, templateObject);
     596             :         
     597             :         templateObject = JS_NewArrayObject(gOOJSMainThreadContext, 0, NULL);
     598             :         if (EXPECT_NOT(templateObject == NULL))  return NO;
     599             :         _arrayClass = OOJSGetClass(gOOJSMainThreadContext, templateObject);
     600             :         
     601             :         if (EXPECT_NOT(!JS_ValueToObject(gOOJSMainThreadContext, INT_TO_JSVAL(0), &templateObject)))  return NO;
     602             :         _numberClass = OOJSGetClass(gOOJSMainThreadContext, templateObject);
     603             :         
     604             :         if (EXPECT_NOT(!JS_ValueToObject(gOOJSMainThreadContext, JSVAL_FALSE, &templateObject)))  return NO;
     605             :         _booleanClass = OOJSGetClass(gOOJSMainThreadContext, templateObject);
     606             :         
     607             :         return YES;
     608             : }
     609             : 
     610             : 
     611           0 : - (void) registerStandardObjectConverters
     612             : {
     613             :         OOJSRegisterObjectConverter([self objectClass], (OOJSClassConverterCallback)OOJSDictionaryFromJSObject);
     614             :         OOJSRegisterObjectConverter([self stringClass], JSStringConverter);
     615             :         OOJSRegisterObjectConverter([self arrayClass], JSArrayConverter);
     616             :         OOJSRegisterObjectConverter([self numberClass], JSNumberConverter);
     617             :         OOJSRegisterObjectConverter([self booleanClass], JSBooleanConverter);
     618             : }
     619             : 
     620             : 
     621             : #ifndef NDEBUG
     622           0 : static JSTrapStatus DebuggerHook(JSContext *context, JSScript *script, jsbytecode *pc, jsval *rval, void *closure)
     623             : {
     624             :         OOJSPauseTimeLimiter();
     625             :         
     626             :         OOLog(@"script.javaScript.debugger", @"debugger invoked during %@:", [[OOJSScript currentlyRunningScript] displayName]);
     627             :         OOJSDumpStack(context);
     628             :         
     629             :         OOJSResumeTimeLimiter();
     630             :         
     631             :         return JSTRAP_CONTINUE;
     632             : }
     633             : 
     634             : 
     635             : - (BOOL) dumpStackForErrors
     636             : {
     637             :         return _dumpStackForErrors;
     638             : }
     639             : 
     640             : 
     641             : - (void) setDumpStackForErrors:(BOOL)value
     642             : {
     643             :         _dumpStackForErrors = !!value;
     644             : }
     645             : 
     646             : 
     647             : - (BOOL) dumpStackForWarnings
     648             : {
     649             :         return _dumpStackForWarnings;
     650             : }
     651             : 
     652             : 
     653             : - (void) setDumpStackForWarnings:(BOOL)value
     654             : {
     655             :         _dumpStackForWarnings = !!value;
     656             : }
     657             : 
     658             : 
     659             : - (void) enableDebuggerStatement
     660             : {
     661             :         JS_SetDebuggerHandler(_runtime, DebuggerHook, self);
     662             : }
     663             : #endif
     664             : 
     665             : @end
     666             : 
     667             : 
     668             : #if OOJSENGINE_MONITOR_SUPPORT
     669             : 
     670             : @implementation OOJavaScriptEngine (OOMonitorSupport)
     671             : 
     672           0 : - (void) setMonitor:(id<OOJavaScriptEngineMonitor>)inMonitor
     673             : {
     674             :         [_monitor autorelease];
     675             :         _monitor = [inMonitor retain];
     676             : }
     677             : 
     678             : @end
     679             : 
     680             : 
     681             : @implementation OOJavaScriptEngine (OOMonitorSupportInternal)
     682             : 
     683             : - (void) sendMonitorError:(JSErrorReport *)errorReport
     684             :                           withMessage:(NSString *)message
     685             :                                 inContext:(JSContext *)theContext
     686             : {
     687             :         if ([_monitor respondsToSelector:@selector(jsEngine:context:error:stackSkip:showingLocation:withMessage:)])
     688             :         {
     689             :                 [_monitor jsEngine:self context:theContext error:errorReport stackSkip:sErrorHandlerStackSkip showingLocation:[self showErrorLocations] withMessage:message];
     690             :         }
     691             : }
     692             : 
     693             : 
     694             : - (void) sendMonitorLogMessage:(NSString *)message
     695             :                           withMessageClass:(NSString *)messageClass
     696             :                                          inContext:(JSContext *)theContext
     697             : {
     698             :         if ([_monitor respondsToSelector:@selector(jsEngine:context:logMessage:ofClass:)])
     699             :         {
     700             :                 [_monitor jsEngine:self context:theContext logMessage:message ofClass:messageClass];
     701             :         }
     702             : }
     703             : 
     704             : @end
     705             : 
     706             : #endif
     707             : 
     708             : 
     709             : #ifndef NDEBUG
     710             : 
     711           0 : static void DumpVariable(JSContext *context, JSPropertyDesc *prop)
     712             : {
     713             :         NSString *name = OOStringFromJSValueEvenIfNull(context, prop->id);
     714             :         NSString *value = OOJSDescribeValue(context, prop->value, YES);
     715             :         
     716             :         enum
     717             :         {
     718             :                 kInterestingFlags = ~(JSPD_ENUMERATE | JSPD_PERMANENT | JSPD_VARIABLE | JSPD_ARGUMENT)
     719             :         };
     720             :         
     721             :         NSString *flagStr = @"";
     722             :         if ((prop->flags & kInterestingFlags) != 0)
     723             :         {
     724             :                 NSMutableArray *flags = [NSMutableArray array];
     725             :                 if (prop->flags & JSPD_READONLY)  [flags addObject:@"read-only"];
     726             :                 if (prop->flags & JSPD_ALIAS)  [flags addObject:[NSString stringWithFormat:@"alias (%@)", OOJSDescribeValue(context, prop->alias, YES)]];
     727             :                 if (prop->flags & JSPD_EXCEPTION)  [flags addObject:@"exception"];
     728             :                 if (prop->flags & JSPD_ERROR)  [flags addObject:@"error"];
     729             :                 
     730             :                 flagStr = [NSString stringWithFormat:@" [%@]", [flags componentsJoinedByString:@", "]];
     731             :         }
     732             :         
     733             :         OOLog(@"script.javaScript.stackTrace", @"    %@: %@%@", name, value, flagStr);
     734             : }
     735             : 
     736             : 
     737           0 : void OOJSDumpStack(JSContext *context)
     738             : {
     739             :         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
     740             :         
     741             :         @try
     742             :         {
     743             :                 JSStackFrame    *frame = NULL;
     744             :                 unsigned                idx = 0;
     745             :                 unsigned                skip = sErrorHandlerStackSkip;
     746             :                 
     747             :                 while (JS_FrameIterator(context, &frame) != NULL)
     748             :                 {
     749             :                         JSScript                        *script = JS_GetFrameScript(context, frame);
     750             :                         NSString                        *desc = nil;
     751             :                         JSPropertyDescArray     properties = { 0 , NULL };
     752             :                         BOOL                            gotProperties = NO;
     753             :                         
     754             :                         idx++;
     755             :                         
     756             :                         if (!JS_IsScriptFrame(context, frame))
     757             :                         {
     758             :                                 continue;
     759             :                         }
     760             :                         
     761             :                         if (skip != 0)
     762             :                         {
     763             :                                 skip--;
     764             :                                 continue;
     765             :                         }
     766             :                         
     767             :                         if (script != NULL)
     768             :                         {
     769             :                                 NSString        *location = OOJSDescribeLocation(context, frame);
     770             :                                 JSObject        *scope = JS_GetFrameScopeChain(context, frame);
     771             :                                 
     772             :                                 if (scope != NULL)  gotProperties = JS_GetPropertyDescArray(context, scope, &properties);
     773             :                                 
     774             :                                 NSString *funcDesc = nil;
     775             :                                 JSFunction *function = JS_GetFrameFunction(context, frame);
     776             :                                 if (function != NULL)
     777             :                                 {
     778             :                                         JSString *funcName = JS_GetFunctionId(function);
     779             :                                         if (funcName != NULL)
     780             :                                         {
     781             :                                                 funcDesc = OOStringFromJSString(context, funcName);
     782             :                                                 if (!JS_IsConstructorFrame(context, frame))
     783             :                                                 {
     784             :                                                         funcDesc = [funcDesc stringByAppendingString:@"()"];
     785             :                                                 }
     786             :                                                 else
     787             :                                                 {
     788             :                                                         funcDesc = [NSString stringWithFormat:@"new %@()", funcDesc];
     789             :                                                 }
     790             :                                                 
     791             :                                         }
     792             :                                         else
     793             :                                         {
     794             :                                                 funcDesc = @"<anonymous function>";
     795             :                                         }
     796             :                                 }
     797             :                                 else
     798             :                                 {
     799             :                                         funcDesc = @"<not a function frame>";
     800             :                                 }
     801             :                                 
     802             :                                 desc = [NSString stringWithFormat:@"(%@) %@", location, funcDesc];
     803             :                         }
     804             :                         else if (JS_IsDebuggerFrame(context, frame))
     805             :                         {
     806             :                                 desc = @"<debugger frame>";
     807             :                         }
     808             :                         else
     809             :                         {
     810             :                                 desc = @"<Oolite native>";
     811             :                         }
     812             :                         
     813             :                         OOLog(@"script.javaScript.stackTrace", @"%2u %@", idx - 1, desc);
     814             :                         
     815             :                         if (gotProperties)
     816             :                         {
     817             :                                 jsval this;
     818             :                                 if (JS_GetFrameThis(context, frame, &this))
     819             :                                 {
     820             :                                         static BOOL haveThis = NO;
     821             :                                         static jsval thisAtom;
     822             :                                         if (EXPECT_NOT(!haveThis))
     823             :                                         {
     824             :                                                 thisAtom = STRING_TO_JSVAL(JS_InternString(context, "this"));
     825             :                                                 haveThis = YES;
     826             :                                         }
     827             :                                         JSPropertyDesc thisDesc = { .id = thisAtom, .value = this };
     828             :                                         DumpVariable(context, &thisDesc);
     829             :                                 }
     830             :                                 
     831             :                                 // Dump arguments.
     832             :                                 unsigned i;
     833             :                                 for (i = 0; i < properties.length; i++)
     834             :                                 {
     835             :                                         JSPropertyDesc *prop = &properties.array[i];
     836             :                                         if (prop->flags & JSPD_ARGUMENT)  DumpVariable(context, prop);
     837             :                                 }
     838             :                                 
     839             :                                 // Dump locals.
     840             :                                 for (i = 0; i < properties.length; i++)
     841             :                                 {
     842             :                                         JSPropertyDesc *prop = &properties.array[i];
     843             :                                         if (prop->flags & JSPD_VARIABLE)  DumpVariable(context, prop);
     844             :                                 }
     845             :                                 
     846             :                                 // Dump anything else.
     847             :                                 for (i = 0; i < properties.length; i++)
     848             :                                 {
     849             :                                         JSPropertyDesc *prop = &properties.array[i];
     850             :                                         if (!(prop->flags & (JSPD_ARGUMENT | JSPD_VARIABLE)))  DumpVariable(context, prop);
     851             :                                 }
     852             :                                 
     853             :                                 JS_PutPropertyDescArray(context, &properties);
     854             :                         }
     855             :                 }
     856             :         }
     857             :         @catch (NSException *exception)
     858             :         {
     859             :                 OOLog(kOOLogException, @"Exception during JavaScript stack trace: %@:%@", [exception name], [exception reason]);
     860             :         }
     861             :         
     862             :         [pool release];
     863             : }
     864             : 
     865             : 
     866           0 : static const char *sConsoleScriptName;  // Lifetime is lifetime of script object, which is forever.
     867           0 : static NSUInteger sConsoleEvalLineNo;
     868             : 
     869             : 
     870           0 : static void GetLocationNameAndLine(JSContext *context, JSStackFrame *stackFrame, const char **name, NSUInteger *line)
     871             : {
     872             :         NSCParameterAssert(context != NULL && stackFrame != NULL && name != NULL && line != NULL);
     873             :         
     874             :         *name = NULL;
     875             :         *line = 0;
     876             :         
     877             :         JSScript *script = JS_GetFrameScript(context, stackFrame);
     878             :         if (script != NULL)
     879             :         {
     880             :                 *name = JS_GetScriptFilename(context, script);
     881             :                 if (name != NULL)
     882             :                 {
     883             :                         jsbytecode *PC = JS_GetFramePC(context, stackFrame);
     884             :                         *line = JS_PCToLineNumber(context, script, PC);
     885             :                 }
     886             :         }
     887             :         else if (JS_IsDebuggerFrame(context, stackFrame))
     888             :         {
     889             :                 *name = "<debugger frame>";
     890             :         }
     891             : }
     892             : 
     893             : 
     894           0 : NSString *OOJSDescribeLocation(JSContext *context, JSStackFrame *stackFrame)
     895             : {
     896             :         NSCParameterAssert(context != NULL && stackFrame != NULL);
     897             :         
     898             :         const char      *fileName;
     899             :         NSUInteger      lineNo;
     900             :         GetLocationNameAndLine(context, stackFrame, &fileName, &lineNo);
     901             :         if (fileName == NULL)  return nil;
     902             :         
     903             :         // If this stops working, we probably need to switch to strcmp().
     904             :         if (fileName == sConsoleScriptName && lineNo >= sConsoleEvalLineNo)  return @"<console input>";
     905             :         
     906             :         // Objectify it.
     907             :         NSString        *fileNameObj = [NSString stringWithUTF8String:fileName];
     908             :         if (fileNameObj == nil)  fileNameObj = [NSString stringWithCString:fileName encoding:NSISOLatin1StringEncoding];
     909             :         if (fileNameObj == nil)  return nil;
     910             :         
     911             :         NSString        *shortFileName = [fileNameObj lastPathComponent];
     912             :         if (![[shortFileName lowercaseString] isEqualToString:@"script.js"])  fileNameObj = shortFileName;
     913             :         
     914             :         return [NSString stringWithFormat:@"%@:%lu", fileNameObj, lineNo];
     915             : }
     916             : 
     917             : 
     918           0 : void OOJSMarkConsoleEvalLocation(JSContext *context, JSStackFrame *stackFrame)
     919             : {
     920             :         GetLocationNameAndLine(context, stackFrame, &sConsoleScriptName, &sConsoleEvalLineNo);
     921             : }
     922             : #endif
     923             : 
     924             : 
     925           0 : void OOJSInitJSIDCachePRIVATE(const char *name, jsid *idCache)
     926             : {
     927             :         NSCParameterAssert(name != NULL && name[0] != '\0' && idCache != NULL);
     928             :         
     929             :         JSContext *context = OOJSAcquireContext();
     930             :         
     931             :         JSString *string = JS_InternString(context, name);
     932             :         if (EXPECT_NOT(string == NULL))
     933             :         {
     934             :                 [NSException raise:NSGenericException format:@"Failed to initialize JS ID cache for \"%s\".", name];
     935             :         }
     936             :         
     937             :         *idCache = INTERNED_STRING_TO_JSID(string);
     938             :         
     939             :         OOJSRelinquishContext(context);
     940             : }
     941             : 
     942             : 
     943           0 : jsid OOJSIDFromString(NSString *string)
     944             : {
     945             :         if (EXPECT_NOT(string == nil))  return JSID_VOID;
     946             :         
     947             :         JSContext *context = OOJSAcquireContext();
     948             :         
     949             :         enum { kStackBufSize = 1024 };
     950             :         unichar stackBuf[kStackBufSize];
     951             :         unichar *buffer;
     952             :         size_t length = [string length];
     953             :         if (length < kStackBufSize)
     954             :         {
     955             :                 buffer = stackBuf;
     956             :         }
     957             :         else
     958             :         {
     959             :                 buffer = malloc(sizeof (unichar) * length);
     960             :                 if (EXPECT_NOT(buffer == NULL))  return JSID_VOID;
     961             :         }
     962             :         [string getCharacters:buffer];
     963             :         
     964             :         JSString *jsString = JS_InternUCStringN(context, buffer, length);
     965             :         
     966             :         if (EXPECT_NOT(buffer != stackBuf))  free(buffer);
     967             :         
     968             :         OOJSRelinquishContext(context);
     969             :         
     970             :         if (EXPECT(jsString != NULL))  return INTERNED_STRING_TO_JSID(jsString);
     971             :         else  return JSID_VOID;
     972             : }
     973             : 
     974             : 
     975           0 : NSString *OOStringFromJSID(jsid propID)
     976             : {
     977             :         JSContext *context = OOJSAcquireContext();
     978             :         
     979             :         jsval           value;
     980             :         NSString        *result = nil;
     981             :         if (JS_IdToValue(context, propID, &value))
     982             :         {
     983             :                 result = OOStringFromJSString(context, JS_ValueToString(context, value));
     984             :         }
     985             :         
     986             :         OOJSRelinquishContext(context);
     987             :         
     988             :         return result;
     989             : }
     990             : 
     991             : 
     992           0 : static NSString *CallerPrefix(NSString *scriptClass, NSString *function)
     993             : {
     994             :         if (function == nil)  return @"";
     995             :         if (scriptClass == nil)  return [function stringByAppendingString:@": "];
     996             :         return  [NSString stringWithFormat:@"%@.%@: ", scriptClass, function];
     997             : }
     998             : 
     999             : 
    1000           0 : void OOJSReportError(JSContext *context, NSString *format, ...)
    1001             : {
    1002             :         va_list                                 args;
    1003             :         
    1004             :         va_start(args, format);
    1005             :         OOJSReportErrorWithArguments(context, format, args);
    1006             :         va_end(args);
    1007             : }
    1008             : 
    1009             : 
    1010           0 : void OOJSReportErrorForCaller(JSContext *context, NSString *scriptClass, NSString *function, NSString *format, ...)
    1011             : {
    1012             :         va_list                                 args;
    1013             :         NSString                                *msg = nil;
    1014             :         
    1015             :         @try
    1016             :         {
    1017             :                 va_start(args, format);
    1018             :                 msg = [[NSString alloc] initWithFormat:format arguments:args];
    1019             :                 va_end(args);
    1020             :                 
    1021             :                 OOJSReportError(context, @"%@%@", CallerPrefix(scriptClass, function), msg);
    1022             :         }
    1023             :         @catch (id exception)
    1024             :         {
    1025             :                 // Squash any secondary errors during error handling.
    1026             :         }
    1027             :         [msg release];
    1028             : }
    1029             : 
    1030             : 
    1031           0 : void OOJSReportErrorWithArguments(JSContext *context, NSString *format, va_list args)
    1032             : {
    1033             :         NSString                                *msg = nil;
    1034             :         
    1035             :         NSCParameterAssert(JS_IsInRequest(context));
    1036             :         
    1037             :         @try
    1038             :         {
    1039             :                 msg = [[NSString alloc] initWithFormat:format arguments:args];
    1040             :                 JS_ReportError(context, "%s", [msg UTF8String]);
    1041             :         }
    1042             :         @catch (id exception)
    1043             :         {
    1044             :                 // Squash any secondary errors during error handling.
    1045             :         }
    1046             :         [msg release];
    1047             : }
    1048             : 
    1049             : 
    1050           0 : void OOJSReportWrappedException(JSContext *context, id exception)
    1051             : {
    1052             :         if (!JS_IsExceptionPending(context))
    1053             :         {
    1054             :                 if ([exception isKindOfClass:[NSException class]])  OOJSReportError(context, @"Native exception: %@", [exception reason]);
    1055             :                 else  OOJSReportError(context, @"Unidentified native exception");
    1056             :         }
    1057             :         // Else, let the pending exception propagate.
    1058             : }
    1059             : 
    1060             : 
    1061             : #ifndef NDEBUG
    1062             : 
    1063           0 : void OOJSUnreachable(const char *function, const char *file, unsigned line)
    1064             : {
    1065             :         OOLog(@"fatal.unreachable", @"Supposedly unreachable statement reached in %s (%@:%u) -- terminating.", function, OOLogAbbreviatedFileName(file), line);
    1066             :         abort();
    1067             : }
    1068             : 
    1069             : #endif
    1070             : 
    1071             : 
    1072           0 : void OOJSReportWarning(JSContext *context, NSString *format, ...)
    1073             : {
    1074             :         va_list                                 args;
    1075             :         
    1076             :         va_start(args, format);
    1077             :         OOJSReportWarningWithArguments(context, format, args);
    1078             :         va_end(args);
    1079             : }
    1080             : 
    1081             : 
    1082           0 : void OOJSReportWarningForCaller(JSContext *context, NSString *scriptClass, NSString *function, NSString *format, ...)
    1083             : {
    1084             :         va_list                                 args;
    1085             :         NSString                                *msg = nil;
    1086             :         
    1087             :         @try
    1088             :         {
    1089             :                 va_start(args, format);
    1090             :                 msg = [[NSString alloc] initWithFormat:format arguments:args];
    1091             :                 va_end(args);
    1092             :                 
    1093             :                 OOJSReportWarning(context, @"%@%@", CallerPrefix(scriptClass, function), msg);
    1094             :         }
    1095             :         @catch (id exception)
    1096             :         {
    1097             :                 // Squash any secondary errors during error handling.
    1098             :         }
    1099             :         [msg release];
    1100             : }
    1101             : 
    1102             : 
    1103           0 : void OOJSReportWarningWithArguments(JSContext *context, NSString *format, va_list args)
    1104             : {
    1105             :         NSString                                *msg = nil;
    1106             :         
    1107             :         @try
    1108             :         {
    1109             :                 msg = [[NSString alloc] initWithFormat:format arguments:args];
    1110             :                 JS_ReportWarning(context, "%s", [msg UTF8String]);
    1111             :         }
    1112             :         @catch (id exception)
    1113             :         {
    1114             :                 // Squash any secondary errors during error handling.
    1115             :         }
    1116             :         [msg release];
    1117             : }
    1118             : 
    1119             : 
    1120           0 : void OOJSReportBadPropertySelector(JSContext *context, JSObject *thisObj, jsid propID, JSPropertySpec *propertySpec)
    1121             : {
    1122             :         NSString        *propName = OOStringFromJSPropertyIDAndSpec(context, propID, propertySpec);
    1123             :         const char      *className = OOJSGetClass(context, thisObj)->name;
    1124             :         
    1125             :         OOJSReportError(context, @"Invalid property identifier %@ for instance of %s.", propName, className);
    1126             : }
    1127             : 
    1128             : 
    1129           0 : void OOJSReportBadPropertyValue(JSContext *context, JSObject *thisObj, jsid propID, JSPropertySpec *propertySpec, jsval value)
    1130             : {
    1131             :         NSString        *propName = OOStringFromJSPropertyIDAndSpec(context, propID, propertySpec);
    1132             :         const char      *className = OOJSGetClass(context, thisObj)->name;
    1133             :         NSString        *valueDesc = OOJSDescribeValue(context, value, YES);
    1134             :         
    1135             :         OOJSReportError(context, @"Cannot set property %@ of instance of %s to invalid value %@.", propName, className, valueDesc);
    1136             : }
    1137             : 
    1138             : 
    1139           0 : void OOJSReportBadArguments(JSContext *context, NSString *scriptClass, NSString *function, uintN argc, jsval *argv, NSString *message, NSString *expectedArgsDescription)
    1140             : {
    1141             :         @try
    1142             :         {
    1143             :                 if (message == nil)  message = @"Invalid arguments";
    1144             :                 message = [NSString stringWithFormat:@"%@ %@", message, [NSString stringWithJavaScriptParameters:argv count:argc inContext:context]];
    1145             :                 if (expectedArgsDescription != nil)  message = [NSString stringWithFormat:@"%@ -- expected %@", message, expectedArgsDescription];
    1146             :                 
    1147             :                 OOJSReportErrorForCaller(context, scriptClass, function, @"%@.", message);
    1148             :         }
    1149             :         @catch (id exception)
    1150             :         {
    1151             :                 // Squash any secondary errors during error handling.
    1152             :         }
    1153             : }
    1154             : 
    1155             : 
    1156           0 : void OOJSSetWarningOrErrorStackSkip(unsigned skip)
    1157             : {
    1158             :         sErrorHandlerStackSkip = skip;
    1159             : }
    1160             : 
    1161             : 
    1162           0 : BOOL OOJSArgumentListGetNumber(JSContext *context, NSString *scriptClass, NSString *function, uintN argc, jsval *argv, double *outNumber, uintN *outConsumed)
    1163             : {
    1164             :         if (OOJSArgumentListGetNumberNoError(context, argc, argv, outNumber, outConsumed))
    1165             :         {
    1166             :                 return YES;
    1167             :         }
    1168             :         else
    1169             :         {
    1170             :                 OOJSReportBadArguments(context, scriptClass, function, argc, argv,
    1171             :                                                                            @"Expected number, got", NULL);
    1172             :                 return NO;
    1173             :         }
    1174             : }
    1175             : 
    1176             : 
    1177           0 : BOOL OOJSArgumentListGetNumberNoError(JSContext *context, uintN argc, jsval *argv, double *outNumber, uintN *outConsumed)
    1178             : {
    1179             :         OOJS_PROFILE_ENTER
    1180             :         
    1181             :         double                                  value;
    1182             :         
    1183             :         NSCParameterAssert(context != NULL && (argv != NULL || argc == 0) && outNumber != NULL);
    1184             :         
    1185             :         // Get value, if possible.
    1186             :         if (EXPECT_NOT(!JS_ValueToNumber(context, argv[0], &value) || isnan(value)))
    1187             :         {
    1188             :                 if (outConsumed != NULL)  *outConsumed = 0;
    1189             :                 return NO;
    1190             :         }
    1191             :         
    1192             :         // Success.
    1193             :         *outNumber = value;
    1194             :         if (outConsumed != NULL)  *outConsumed = 1;
    1195             :         return YES;
    1196             :         
    1197             :         OOJS_PROFILE_EXIT
    1198             : }
    1199             : 
    1200             : 
    1201           0 : static JSObject *JSArrayFromNSArray(JSContext *context, NSArray *array)
    1202             : {
    1203             :         OOJS_PROFILE_ENTER
    1204             :         
    1205             :         JSObject                                *result = NULL;
    1206             :         
    1207             :         if (array == nil)  return NULL;
    1208             :         
    1209             :         @try
    1210             :         {
    1211             :                 NSUInteger fullCount = [array count];
    1212             :                 if (EXPECT_NOT(fullCount > INT32_MAX))
    1213             :                 {
    1214             :                         return NULL;
    1215             :                 }
    1216             :                 
    1217             :                 uint32_t i, count = (int32_t)fullCount;
    1218             :                 
    1219             :                 result = JS_NewArrayObject(context, 0, NULL);
    1220             :                 if (result != NULL)
    1221             :                 {
    1222             :                         for (i = 0; i != count; ++i)
    1223             :                         {
    1224             :                                 jsval value = [[array objectAtIndex:i] oo_jsValueInContext:context];
    1225             :                                 BOOL OK = JS_SetElement(context, result, i, &value);
    1226             :                                 
    1227             :                                 if (EXPECT_NOT(!OK))
    1228             :                                 {
    1229             :                                         result = NULL;
    1230             :                                         break;
    1231             :                                 }
    1232             :                         }
    1233             :                 }
    1234             :         }
    1235             :         @catch (id ex)
    1236             :         {
    1237             :                 result = NULL;
    1238             :         }
    1239             :         
    1240             :         return (JSObject *)result;
    1241             :         
    1242             :         OOJS_PROFILE_EXIT
    1243             : }
    1244             : 
    1245             : 
    1246           0 : static BOOL JSNewNSArrayValue(JSContext *context, NSArray *array, jsval *value)
    1247             : {
    1248             :         OOJS_PROFILE_ENTER
    1249             :         
    1250             :         JSObject                                *object = NULL;
    1251             :         BOOL                                    OK = YES;
    1252             :         
    1253             :         if (value == NULL)  return NO;
    1254             :         
    1255             :         // NOTE: should be called within a local root scope or have *value be a set root for GC reasons.
    1256             :         if (!JS_EnterLocalRootScope(context))  return NO;
    1257             :         
    1258             :         object = JSArrayFromNSArray(context, array);
    1259             :         if (object == NULL)
    1260             :         {
    1261             :                 *value = JSVAL_VOID;
    1262             :                 OK = NO;
    1263             :         }
    1264             :         else
    1265             :         {
    1266             :                 *value = OBJECT_TO_JSVAL(object);
    1267             :         }
    1268             :         
    1269             :         JS_LeaveLocalRootScopeWithResult(context, *value);
    1270             :         return OK;
    1271             :         
    1272             :         OOJS_PROFILE_EXIT
    1273             : }
    1274             : 
    1275             : 
    1276             : /*      Convert an NSDictionary to a JavaScript Object.
    1277             :         Only properties whose keys are either strings or non-negative NSNumbers,
    1278             :         and     whose values have a non-void JS representation, are converted.
    1279             : */
    1280           0 : static JSObject *JSObjectFromNSDictionary(JSContext *context, NSDictionary *dictionary)
    1281             : {
    1282             :         OOJS_PROFILE_ENTER
    1283             :         
    1284             :         JSObject                                *result = NULL;
    1285             :         BOOL                                    OK = YES;
    1286             :         NSEnumerator                    *keyEnum = nil;
    1287             :         id                                              key = nil;
    1288             :         jsval                                   value;
    1289             :         jsint                                   index;
    1290             :         
    1291             :         if (dictionary == nil)  return NULL;
    1292             :         
    1293             :         @try
    1294             :         {
    1295             :                 result = JS_NewObject(context, NULL, NULL, NULL);       // create object of class Object
    1296             :                 if (result != NULL)
    1297             :                 {
    1298             :                         for (keyEnum = [dictionary keyEnumerator]; (key = [keyEnum nextObject]); )
    1299             :                         {
    1300             :                                 if ([key isKindOfClass:[NSString class]] && [key length] != 0)
    1301             :                                 {
    1302             : #ifndef __GNUC__
    1303             :                                         value = [[dictionary objectForKey:key] oo_jsValueInContext:context];
    1304             : #else
    1305             : #if __GNUC__ > 4 || __GNUC_MINOR__ > 6
    1306             :                                         value = [[dictionary objectForKey:key] oo_jsValueInContext:context];
    1307             : #else
    1308             :                                         // GCC before 4.7 seems to have problems with this
    1309             :                                         // bit if the object is a weakref, causing crashes
    1310             :                                         // in docking code.
    1311             :                                         id tmp = [dictionary objectForKey:key];
    1312             :                                         if ([tmp respondsToSelector:@selector(weakRefUnderlyingObject)])
    1313             :                                         {
    1314             :                                                 tmp = [tmp weakRefUnderlyingObject];
    1315             :                                         }
    1316             :                                         value = [tmp oo_jsValueInContext:context];
    1317             : #endif
    1318             : #endif
    1319             :                                         if (!JSVAL_IS_VOID(value))
    1320             :                                         {
    1321             :                                                 OK = JS_SetPropertyById(context, result, OOJSIDFromString(key), &value);
    1322             :                                                 if (EXPECT_NOT(!OK))  break;
    1323             :                                         }
    1324             :                                 }
    1325             :                                 else if ([key isKindOfClass:[NSNumber class]])
    1326             :                                 {
    1327             :                                         index = [key intValue];
    1328             :                                         if (0 < index)
    1329             :                                         {
    1330             :                                                 value = [[dictionary objectForKey:key] oo_jsValueInContext:context];
    1331             :                                                 if (!JSVAL_IS_VOID(value))
    1332             :                                                 {
    1333             :                                                         OK = JS_SetElement(context, (JSObject *)result, index, &value);
    1334             :                                                         if (EXPECT_NOT(!OK))  break;
    1335             :                                                 }
    1336             :                                         }
    1337             :                                 }
    1338             :                                 
    1339             :                                 if (EXPECT_NOT(!OK))  break;
    1340             :                         }
    1341             :                 }
    1342             :         }
    1343             :         @catch (id exception)
    1344             :         {
    1345             :                 OK = NO;
    1346             :         }
    1347             :         
    1348             :         if (EXPECT_NOT(!OK))
    1349             :         {
    1350             :                 result = NULL;
    1351             :         }
    1352             :         
    1353             :         return (JSObject *)result;
    1354             :         
    1355             :         OOJS_PROFILE_EXIT
    1356             : }
    1357             : 
    1358             : 
    1359           0 : static BOOL JSNewNSDictionaryValue(JSContext *context, NSDictionary *dictionary, jsval *value)
    1360             : {
    1361             :         OOJS_PROFILE_ENTER
    1362             :         
    1363             :         JSObject                                *object = NULL;
    1364             :         BOOL                                    OK = YES;
    1365             :         
    1366             :         if (value == NULL)  return NO;
    1367             :         
    1368             :         // NOTE: should be called within a local root scope or have *value be a set root for GC reasons.
    1369             :         if (!JS_EnterLocalRootScope(context))  return NO;
    1370             :         
    1371             :         object = JSObjectFromNSDictionary(context, dictionary);
    1372             :         if (object == NULL)
    1373             :         {
    1374             :                 *value = JSVAL_VOID;
    1375             :                 OK = NO;
    1376             :         }
    1377             :         else
    1378             :         {
    1379             :                 *value = OBJECT_TO_JSVAL(object);
    1380             :         }
    1381             :         
    1382             :         JS_LeaveLocalRootScopeWithResult(context, *value);
    1383             :         return OK;
    1384             :         
    1385             :         OOJS_PROFILE_EXIT
    1386             : }
    1387             : 
    1388             : 
    1389             : @implementation NSObject (OOJavaScriptConversion)
    1390             : 
    1391           0 : - (jsval) oo_jsValueInContext:(JSContext *)context
    1392             : {
    1393             :         return JSVAL_VOID;
    1394             : }
    1395             : 
    1396             : 
    1397           0 : - (NSString *) oo_jsClassName
    1398             : {
    1399             :         return nil;
    1400             : }
    1401             : 
    1402             : 
    1403           0 : - (NSString *) oo_jsDescription
    1404             : {
    1405             :         return [self oo_jsDescriptionWithClassName:[self oo_jsClassName]];
    1406             : }
    1407             : 
    1408             : 
    1409           0 : - (NSString *) oo_jsDescriptionWithClassName:(NSString *)className
    1410             : {
    1411             :         OOJS_PROFILE_ENTER
    1412             :         
    1413             :         NSString                                *components = nil;
    1414             :         NSString                                *description = nil;
    1415             :         
    1416             :         components = [self descriptionComponents];
    1417             :         if (className == nil)  className = [[self class] description];
    1418             :         
    1419             :         if (components != nil)
    1420             :         {
    1421             :                 description = [NSString stringWithFormat:@"[%@ %@]", className, components];
    1422             :         }
    1423             :         else
    1424             :         {
    1425             :                 description = [NSString stringWithFormat:@"[object %@]", className];
    1426             :         }
    1427             :         
    1428             :         return description;
    1429             :         
    1430             :         OOJS_PROFILE_EXIT
    1431             : }
    1432             : 
    1433             : 
    1434           0 : - (void) oo_clearJSSelf:(JSObject *)selfVal
    1435             : {
    1436             :         
    1437             : }
    1438             : 
    1439             : @end
    1440             : 
    1441             : 
    1442           0 : JSObject *OOJSObjectFromNativeObject(JSContext *context, id object)
    1443             : {
    1444             :         jsval value = OOJSValueFromNativeObject(context, object);
    1445             :         JSObject *result = NULL;
    1446             :         if (JS_ValueToObject(context, value, &result))  return result;
    1447             :         return NULL;
    1448             : }
    1449             : 
    1450             : 
    1451             : @implementation OOJSValue
    1452             : 
    1453             : + (id) valueWithJSValue:(jsval)value inContext:(JSContext *)context
    1454             : {
    1455             :         OOJS_PROFILE_ENTER
    1456             :         
    1457             :         return [[[self alloc] initWithJSValue:value inContext:context] autorelease];
    1458             :         
    1459             :         OOJS_PROFILE_EXIT
    1460             : }
    1461             : 
    1462             : 
    1463             : + (id) valueWithJSObject:(JSObject *)object inContext:(JSContext *)context
    1464             : {
    1465             :         OOJS_PROFILE_ENTER
    1466             :         
    1467             :         return [[[self alloc] initWithJSObject:object inContext:context] autorelease];
    1468             :         
    1469             :         OOJS_PROFILE_EXIT
    1470             : }
    1471             : 
    1472             : 
    1473             : - (id) initWithJSValue:(jsval)value inContext:(JSContext *)context
    1474             : {
    1475             :         OOJS_PROFILE_ENTER
    1476             :         
    1477             :         self = [super init];
    1478             :         if (self != nil)
    1479             :         {
    1480             :                 BOOL tempCtxt = NO;
    1481             :                 if (context == NULL)
    1482             :                 {
    1483             :                         context = OOJSAcquireContext();
    1484             :                         tempCtxt = YES;
    1485             :                 }
    1486             :                 
    1487             :                 _val = value;
    1488             :                 if (!JSVAL_IS_VOID(_val))
    1489             :                 {
    1490             :                         JS_AddNamedValueRoot(context, &_val, "OOJSValue");
    1491             :                         
    1492             :                         [[NSNotificationCenter defaultCenter] addObserver:self
    1493             :                                                                                                          selector:@selector(deleteJSValue)
    1494             :                                                                                                                  name:kOOJavaScriptEngineWillResetNotification
    1495             :                                                                                                            object:[OOJavaScriptEngine sharedEngine]];
    1496             :                 }
    1497             :                 
    1498             :                 if (tempCtxt)  OOJSRelinquishContext(context);
    1499             :         }
    1500             :         return self;
    1501             :         
    1502             :         OOJS_PROFILE_EXIT
    1503             : }
    1504             : 
    1505             : 
    1506             : - (id) initWithJSObject:(JSObject *)object inContext:(JSContext *)context
    1507             : {
    1508             :         return [self initWithJSValue:OBJECT_TO_JSVAL(object) inContext:context];
    1509             : }
    1510             : 
    1511             : 
    1512           0 : - (void) deleteJSValue
    1513             : {
    1514             :         if (!JSVAL_IS_VOID(_val))
    1515             :         {
    1516             :                 JSContext *context = OOJSAcquireContext();
    1517             :                 JS_RemoveValueRoot(context, &_val);
    1518             :                 OOJSRelinquishContext(context);
    1519             :                 
    1520             :                 _val = JSVAL_VOID;
    1521             :                 [[NSNotificationCenter defaultCenter] removeObserver:self
    1522             :                                                                                                                 name:kOOJavaScriptEngineWillResetNotification
    1523             :                                                                                                           object:[OOJavaScriptEngine sharedEngine]];
    1524             :         }
    1525             : }
    1526             : 
    1527             : 
    1528           0 : - (void) dealloc
    1529             : {
    1530             :         [self deleteJSValue];
    1531             :         [super dealloc];
    1532             : }
    1533             : 
    1534             : 
    1535           0 : - (jsval) oo_jsValueInContext:(JSContext *)context
    1536             : {
    1537             :         return _val;
    1538             : }
    1539             : 
    1540             : @end
    1541             : 
    1542             : 
    1543           0 : void OOJSStrLiteralCachePRIVATE(const char *string, jsval *strCache, BOOL *inited)
    1544             : {
    1545             :         NSCParameterAssert(string != NULL && strCache != NULL && inited != NULL && !*inited);
    1546             :         
    1547             :         JSContext *context = OOJSAcquireContext();
    1548             :         
    1549             :         JSString *jsString = JS_InternString(context, string);
    1550             :         if (EXPECT_NOT(string == NULL))
    1551             :         {
    1552             :                 [NSException raise:NSGenericException format:@"Failed to initialize JavaScript string literal cache for \"%@\".", [[NSString stringWithUTF8String:string] escapedForJavaScriptLiteral]];
    1553             :         }
    1554             :         
    1555             :         *strCache = STRING_TO_JSVAL(jsString);
    1556             :         *inited = YES;
    1557             :         
    1558             :         OOJSRelinquishContext(context);
    1559             : }
    1560             : 
    1561             : 
    1562           0 : NSString *OOStringFromJSString(JSContext *context, JSString *string)
    1563             : {
    1564             :         OOJS_PROFILE_ENTER
    1565             :         
    1566             :         if (EXPECT_NOT(string == NULL))  return nil;
    1567             :         
    1568             :         size_t length;
    1569             :         const jschar *chars = JS_GetStringCharsAndLength(context, string, &length);
    1570             :         
    1571             :         if (EXPECT(chars != NULL))
    1572             :         {
    1573             :                 return [NSString stringWithCharacters:chars length:length];
    1574             :         }
    1575             :         else
    1576             :         {
    1577             :                 return nil;
    1578             :         }
    1579             :         
    1580             :         OOJS_PROFILE_EXIT
    1581             : }
    1582             : 
    1583             : 
    1584           0 : NSString *OOStringFromJSValueEvenIfNull(JSContext *context, jsval value)
    1585             : {
    1586             :         OOJS_PROFILE_ENTER
    1587             :         
    1588             :         NSCParameterAssert(context != NULL && JS_IsInRequest(context));
    1589             :         
    1590             :         JSString *string = JS_ValueToString(context, value);    // Calls the value's toString method if needed.
    1591             :         return OOStringFromJSString(context, string);
    1592             :         
    1593             :         OOJS_PROFILE_EXIT
    1594             : }
    1595             : 
    1596             : 
    1597           0 : NSString *OOStringFromJSValue(JSContext *context, jsval value)
    1598             : {
    1599             :         OOJS_PROFILE_ENTER
    1600             :         
    1601             :         if (EXPECT(!JSVAL_IS_NULL(value) && !JSVAL_IS_VOID(value)))
    1602             :         {
    1603             :                 return OOStringFromJSValueEvenIfNull(context, value);
    1604             :         }
    1605             :         return nil;
    1606             :         
    1607             :         OOJS_PROFILE_EXIT
    1608             : }
    1609             : 
    1610             : 
    1611           0 : NSString *OOStringFromJSPropertyIDAndSpec(JSContext *context, jsid propID, JSPropertySpec *propertySpec)
    1612             : {
    1613             :         if (JSID_IS_STRING(propID))
    1614             :         {
    1615             :                 return OOStringFromJSString(context, JSID_TO_STRING(propID));
    1616             :         }
    1617             :         else if (JSID_IS_INT(propID) && propertySpec != NULL)
    1618             :         {
    1619             :                 int tinyid = JSID_TO_INT(propID);
    1620             :                 
    1621             :                 while (propertySpec->name != NULL)
    1622             :                 {
    1623             :                         if (propertySpec->tinyid == tinyid)  return [NSString stringWithUTF8String:propertySpec->name];
    1624             :                         propertySpec++;
    1625             :                 }
    1626             :         }
    1627             :         
    1628             :         jsval value;
    1629             :         if (!JS_IdToValue(context, propID, &value))  return @"unknown";
    1630             :         return OOStringFromJSString(context, JS_ValueToString(context, value));
    1631             : }
    1632             : 
    1633             : 
    1634           0 : static NSString *DescribeValue(JSContext *context, jsval value, BOOL abbreviateObjects, BOOL recursing)
    1635             : {
    1636             :         OOJS_PROFILE_ENTER
    1637             :         
    1638             :         NSCParameterAssert(context != NULL && JS_IsInRequest(context));
    1639             :         
    1640             :         if (OOJSValueIsFunction(context, value))
    1641             :         {
    1642             :                 JSString *name = JS_GetFunctionId(JS_ValueToFunction(context, value));
    1643             :                 if (name != NULL)  return [NSString stringWithFormat:@"function %@", OOStringFromJSString(context, name)];
    1644             :                 else  return @"function";
    1645             :         }
    1646             :         
    1647             :         NSString                        *result = nil;
    1648             :         JSClass                         *class = NULL;
    1649             :         OOJavaScriptEngine      *jsEng = [OOJavaScriptEngine sharedEngine];
    1650             :         
    1651             :         if (JSVAL_IS_OBJECT(value) && !JSVAL_IS_NULL(value))
    1652             :         {
    1653             :                 class = OOJSGetClass(context, JSVAL_TO_OBJECT(value));
    1654             :         }
    1655             :         
    1656             :         // Convert String objects to strings.
    1657             :         if (class == [jsEng stringClass])
    1658             :         {
    1659             :                 value = STRING_TO_JSVAL(JS_ValueToString(context, value));
    1660             :         }
    1661             :         
    1662             :         if (JSVAL_IS_STRING(value))
    1663             :         {
    1664             :                 enum { kMaxLength = 200 };
    1665             :                 
    1666             :                 JSString *string = JSVAL_TO_STRING(value);
    1667             :                 size_t length;
    1668             :                 const jschar *chars = JS_GetStringCharsAndLength(context, string, &length);
    1669             :                 
    1670             :                 result = [NSString stringWithCharacters:chars length:MIN(length, (size_t)kMaxLength)];
    1671             :                 result = [NSString stringWithFormat:@"\"%@%@\"", [result escapedForJavaScriptLiteral], (length > kMaxLength) ? @"..." : @""];
    1672             :         }
    1673             :         else if (class == [jsEng arrayClass])
    1674             :         {
    1675             :                 // Descibe up to four elements of an array.
    1676             :                 jsuint count;
    1677             :                 JSObject *obj = JSVAL_TO_OBJECT(value);
    1678             :                 if (JS_GetArrayLength(context, obj, &count))
    1679             :                 {
    1680             :                         if (!recursing)
    1681             :                         {
    1682             :                                 NSMutableString *arrayDesc = [NSMutableString stringWithString:@"["];
    1683             :                                 jsuint i, effectiveCount = MIN(count, (jsuint)4);
    1684             :                                 for (i = 0; i < effectiveCount; i++)
    1685             :                                 {
    1686             :                                         jsval item;
    1687             :                                         NSString *itemDesc = @"?";
    1688             :                                         if (JS_GetElement(context, obj, i, &item))
    1689             :                                         {
    1690             :                                                 itemDesc = DescribeValue(context, item, YES /* always abbreviate objects in arrays */, YES);
    1691             :                                         }
    1692             :                                         if (i != 0)  [arrayDesc appendString:@", "];
    1693             :                                         [arrayDesc appendString:itemDesc];
    1694             :                                 }
    1695             :                                 if (effectiveCount != count)
    1696             :                                 {
    1697             :                                         [arrayDesc appendFormat:@", ... <%u items total>]", count];
    1698             :                                 }
    1699             :                                 else
    1700             :                                 {
    1701             :                                         [arrayDesc appendString:@"]"];
    1702             :                                 }
    1703             :                                 
    1704             :                                 result = arrayDesc;
    1705             :                         }
    1706             :                         else
    1707             :                         {
    1708             :                                 result = [NSString stringWithFormat:@"[<%u items>]", count];
    1709             :                         }
    1710             :                 }
    1711             :                 else
    1712             :                 {
    1713             :                         result = @"[...]";
    1714             :                 }
    1715             : 
    1716             :         }
    1717             :         
    1718             :         if (result == nil)
    1719             :         {
    1720             :                 result = OOStringFromJSValueEvenIfNull(context, value);
    1721             :                 
    1722             :                 if (abbreviateObjects && class == [jsEng objectClass] && [result isEqualToString:@"[object Object]"])
    1723             :                 {
    1724             :                         result = @"{...}";
    1725             :                 }
    1726             :                 
    1727             :                 if (result == nil)  result = @"?";
    1728             :         }
    1729             :         
    1730             :         return result;
    1731             :         
    1732             :         OOJS_PROFILE_EXIT
    1733             : }
    1734             : 
    1735             : 
    1736           0 : NSString *OOJSDescribeValue(JSContext *context, jsval value, BOOL abbreviateObjects)
    1737             : {
    1738             :         return DescribeValue(context, value, abbreviateObjects, NO);
    1739             : }
    1740             : 
    1741             : 
    1742             : @implementation NSString (OOJavaScriptExtensions)
    1743             : 
    1744             : + (NSString *) stringWithJavaScriptParameters:(jsval *)params count:(uintN)count inContext:(JSContext *)context
    1745             : {
    1746             :         OOJS_PROFILE_ENTER
    1747             :         
    1748             :         if (params == NULL && count != 0) return nil;
    1749             :         
    1750             :         uintN                                   i;
    1751             :         NSMutableString                 *result = [NSMutableString stringWithString:@"("];
    1752             :         
    1753             :         for (i = 0; i < count; ++i)
    1754             :         {
    1755             :                 if (i != 0)  [result appendString:@", "];
    1756             :                 [result appendString:OOJSDescribeValue(context, params[i], NO)];
    1757             :         }
    1758             :         
    1759             :         [result appendString:@")"];
    1760             :         return result;
    1761             :         
    1762             :         OOJS_PROFILE_EXIT
    1763             : }
    1764             : 
    1765             : 
    1766           0 : - (jsval) oo_jsValueInContext:(JSContext *)context
    1767             : {
    1768             :         OOJS_PROFILE_ENTER
    1769             :         
    1770             :         size_t                                  length = [self length];
    1771             :         unichar                                 *buffer = NULL;
    1772             :         JSString                                *string = NULL;
    1773             :         
    1774             :         if (length == 0)
    1775             :         {
    1776             :                 jsval result = JS_GetEmptyStringValue(context);
    1777             :                 return result;
    1778             :         }
    1779             :         else
    1780             :         {
    1781             :                 buffer = malloc(length * sizeof *buffer);
    1782             :                 if (buffer == NULL) return JSVAL_VOID;
    1783             :                 
    1784             :                 [self getCharacters:buffer];
    1785             :                 
    1786             :                 string = JS_NewUCStringCopyN(context, buffer, length);
    1787             :                 
    1788             :                 free(buffer);
    1789             :                 return STRING_TO_JSVAL(string);
    1790             :         }
    1791             :         
    1792             :         OOJS_PROFILE_EXIT_JSVAL
    1793             : }
    1794             : 
    1795             : 
    1796             : + (NSString *) concatenationOfStringsFromJavaScriptValues:(jsval *)values count:(size_t)count separator:(NSString *)separator inContext:(JSContext *)context
    1797             : {
    1798             :         OOJS_PROFILE_ENTER
    1799             :         
    1800             :         size_t                                  i;
    1801             :         NSMutableString                 *result = nil;
    1802             :         NSString                                *element = nil;
    1803             :         
    1804             :         if (count < 1) return nil;
    1805             :         if (values == NULL) return NULL;
    1806             :         
    1807             :         for (i = 0; i != count; ++i)
    1808             :         {
    1809             :                 element = OOStringFromJSValueEvenIfNull(context, values[i]);
    1810             :                 if (result == nil)  result = [[element mutableCopy] autorelease];
    1811             :                 else
    1812             :                 {
    1813             :                         if (separator != nil)  [result appendString:separator];
    1814             :                         [result appendString:element];
    1815             :                 }
    1816             :         }
    1817             :         
    1818             :         return result;
    1819             :         
    1820             :         OOJS_PROFILE_EXIT
    1821             : }
    1822             : 
    1823             : 
    1824             : - (NSString *)escapedForJavaScriptLiteral
    1825             : {
    1826             :         OOJS_PROFILE_ENTER
    1827             :         
    1828             :         NSMutableString                 *result = nil;
    1829             :         NSUInteger                              i, length;
    1830             :         unichar                                 c;
    1831             :         NSAutoreleasePool               *pool = nil;
    1832             :         
    1833             :         length = [self length];
    1834             :         result = [NSMutableString stringWithCapacity:length];
    1835             :         
    1836             :         // Not hugely efficient.
    1837             :         pool = [[NSAutoreleasePool alloc] init];
    1838             :         for (i = 0; i != length; ++i)
    1839             :         {
    1840             :                 c = [self characterAtIndex:i];
    1841             :                 switch (c)
    1842             :                 {
    1843             :                         case '\\':
    1844             :                                 [result appendString:@"\\\\"];
    1845             :                                 break;
    1846             :                                 
    1847             :                         case '\b':
    1848             :                                 [result appendString:@"\\b"];
    1849             :                                 break;
    1850             :                                 
    1851             :                         case '\f':
    1852             :                                 [result appendString:@"\\f"];
    1853             :                                 break;
    1854             :                                 
    1855             :                         case '\n':
    1856             :                                 [result appendString:@"\\n"];
    1857             :                                 break;
    1858             :                                 
    1859             :                         case '\r':
    1860             :                                 [result appendString:@"\\r"];
    1861             :                                 break;
    1862             :                                 
    1863             :                         case '\t':
    1864             :                                 [result appendString:@"\\t"];
    1865             :                                 break;
    1866             :                                 
    1867             :                         case '\v':
    1868             :                                 [result appendString:@"\\v"];
    1869             :                                 break;
    1870             :                                 
    1871             :                         case '\'':
    1872             :                                 [result appendString:@"\\\'"];
    1873             :                                 break;
    1874             :                                 
    1875             :                         case '\"':
    1876             :                                 [result appendString:@"\\\""];
    1877             :                                 break;
    1878             :                         
    1879             :                         default:
    1880             :                                 [result appendString:[NSString stringWithCharacters:&c length:1]];
    1881             :                 }
    1882             :         }
    1883             :         [pool release];
    1884             :         return result;
    1885             :         
    1886             :         OOJS_PROFILE_EXIT
    1887             : }
    1888             : 
    1889             : 
    1890           0 : - (NSString *) oo_jsClassName
    1891             : {
    1892             :         return @"String";
    1893             : }
    1894             : 
    1895             : @end
    1896             : 
    1897             : 
    1898             : @implementation NSArray (OOJavaScriptConversion)
    1899             : 
    1900           0 : - (jsval)oo_jsValueInContext:(JSContext *)context
    1901             : {
    1902             :         jsval value = JSVAL_VOID;
    1903             :         JSNewNSArrayValue(context, self, &value);
    1904             :         return value;
    1905             : }
    1906             : 
    1907             : @end
    1908             : 
    1909             : @implementation OONativeVector (OOJavaScriptConversion)
    1910             : 
    1911           0 : - (jsval)oo_jsValueInContext:(JSContext *)context
    1912             : {
    1913             :         jsval value = JSVAL_VOID;
    1914             :         VectorToJSValue(context, v, &value);
    1915             :         return value;
    1916             : }
    1917             : 
    1918             : @end
    1919             : 
    1920             : 
    1921             : @implementation NSDictionary (OOJavaScriptConversion)
    1922             : 
    1923           0 : - (jsval)oo_jsValueInContext:(JSContext *)context
    1924             : {
    1925             :         jsval value = JSVAL_VOID;
    1926             :         JSNewNSDictionaryValue(context, self, &value);
    1927             :         return value;
    1928             : }
    1929             : 
    1930             : @end
    1931             : 
    1932             : 
    1933             : @implementation NSNumber (OOJavaScriptConversion)
    1934             : 
    1935           0 : - (jsval)oo_jsValueInContext:(JSContext *)context
    1936             : {
    1937             :         OOJS_PROFILE_ENTER
    1938             :         
    1939             :         jsval                                   result;
    1940             :         BOOL                                    isFloat = NO;
    1941             :         long long                               longLongValue;
    1942             :         
    1943             :         isFloat = [self oo_isFloatingPointNumber];
    1944             :         if (!isFloat)
    1945             :         {
    1946             :                 longLongValue = [self longLongValue];
    1947             :                 if (longLongValue < (long long)JSVAL_INT_MIN || (long long)JSVAL_INT_MAX < longLongValue)
    1948             :                 {
    1949             :                         // values outside JSVAL_INT range are returned as doubles.
    1950             :                         isFloat = YES;
    1951             :                 }
    1952             :         }
    1953             :         
    1954             :         if (isFloat)
    1955             :         {
    1956             :                 if (!JS_NewNumberValue(context, [self doubleValue], &result)) result = JSVAL_VOID;
    1957             :         }
    1958             :         else
    1959             :         {
    1960             :                 result = INT_TO_JSVAL((int32_t)longLongValue);
    1961             :         }
    1962             :         
    1963             :         return result;
    1964             :         
    1965             :         OOJS_PROFILE_EXIT_JSVAL
    1966             : }
    1967             : 
    1968             : 
    1969           0 : - (NSString *) oo_jsClassName
    1970             : {
    1971             :         return @"Number";
    1972             : }
    1973             : 
    1974             : @end
    1975             : 
    1976             : 
    1977             : @implementation NSNull (OOJavaScriptConversion)
    1978             : 
    1979           0 : - (jsval)oo_jsValueInContext:(JSContext *)context
    1980             : {
    1981             :         return JSVAL_NULL;
    1982             : }
    1983             : 
    1984             : @end
    1985             : 
    1986             : 
    1987           0 : JSBool OOJSUnconstructableConstruct(JSContext *context, uintN argc, jsval *vp)
    1988             : {
    1989             :         OOJS_NATIVE_ENTER(context)
    1990             :         
    1991             :         JSFunction *function = JS_ValueToFunction(context, JS_CALLEE(context, vp));
    1992             :         NSString *name = OOStringFromJSString(context, JS_GetFunctionId(function));
    1993             :         
    1994             :         OOJSReportError(context, @"%@ cannot be used as a constructor.", name);
    1995             :         return NO;
    1996             :         
    1997             :         OOJS_NATIVE_EXIT
    1998             : }
    1999             : 
    2000             : 
    2001           0 : void OOJSObjectWrapperFinalize(JSContext *context, JSObject *this)
    2002             : {
    2003             :         OOJS_PROFILE_ENTER
    2004             :         
    2005             :         id object = JS_GetPrivate(context, this);
    2006             :         if (object != nil)
    2007             :         {
    2008             :                 [[object weakRefUnderlyingObject] oo_clearJSSelf:this];
    2009             :                 [object release];
    2010             :                 JS_SetPrivate(context, this, nil);
    2011             :         }
    2012             :         
    2013             :         OOJS_PROFILE_EXIT_VOID
    2014             : }
    2015             : 
    2016             : 
    2017           0 : JSBool OOJSObjectWrapperToString(JSContext *context, uintN argc, jsval *vp)
    2018             : {
    2019             :         OOJS_NATIVE_ENTER(context)
    2020             :         
    2021             :         id                                              object = nil;
    2022             :         NSString                                *description = nil;
    2023             :         JSClass                                 *jsClass = NULL;
    2024             :         
    2025             :         object = OOJSNativeObjectFromJSObject(context, OOJS_THIS);
    2026             :         if (object != nil)
    2027             :         {
    2028             :                 description = [object oo_jsDescription];
    2029             :                 if (description == nil)  description = [object description];
    2030             :         }
    2031             :         if (description == nil)
    2032             :         {
    2033             :                 jsClass = OOJSGetClass(context, OOJS_THIS);
    2034             :                 if (jsClass != NULL)
    2035             :                 {
    2036             :                         description = [NSString stringWithFormat:@"[object %@]", [NSString stringWithUTF8String:jsClass->name]];
    2037             :                 }
    2038             :         }
    2039             :         if (description == nil)  description = @"[object]";
    2040             :         
    2041             :         OOJS_RETURN_OBJECT(description);
    2042             :         
    2043             :         OOJS_NATIVE_EXIT
    2044             : }
    2045             : 
    2046             : 
    2047           0 : BOOL JSFunctionPredicate(Entity *entity, void *parameter)
    2048             : {
    2049             :         OOJS_PROFILE_ENTER
    2050             :         
    2051             :         JSFunctionPredicateParameter    *param = parameter;
    2052             :         jsval                                                   args[1];
    2053             :         jsval                                                   rval = JSVAL_VOID;
    2054             :         JSBool                                                  result = NO;
    2055             :         
    2056             :         NSCParameterAssert(entity != nil && param != NULL);
    2057             :         NSCParameterAssert(param->context != NULL && JS_IsInRequest(param->context));
    2058             :         NSCParameterAssert(OOJSValueIsFunction(param->context, param->function));
    2059             :         
    2060             :         if (EXPECT_NOT(param->errorFlag))  return NO;
    2061             :         
    2062             :         args[0] = [entity oo_jsValueInContext:param->context];       // entity is required to be non-nil (asserted above), so oo_jsValueInContext: is safe.
    2063             :         
    2064             :         OOJSStartTimeLimiter();
    2065             :         OOJSResumeTimeLimiter();
    2066             :         BOOL success = JS_CallFunctionValue(param->context, param->jsThis, param->function, 1, args, &rval);
    2067             :         OOJSPauseTimeLimiter();
    2068             :         OOJSStopTimeLimiter();
    2069             :         
    2070             :         if (success)
    2071             :         {
    2072             :                 if (!JS_ValueToBoolean(param->context, rval, &result))  result = NO;
    2073             :                 if (JS_IsExceptionPending(param->context))
    2074             :                 {
    2075             :                         JS_ReportPendingException(param->context);
    2076             :                         param->errorFlag = YES;
    2077             :                 }
    2078             :         }
    2079             :         else
    2080             :         {
    2081             :                 param->errorFlag = YES;
    2082             :         }
    2083             :         
    2084             :         return result;
    2085             :         
    2086             :         OOJS_PROFILE_EXIT
    2087             : }
    2088             : 
    2089             : 
    2090           0 : BOOL JSEntityIsJavaScriptVisiblePredicate(Entity *entity, void *parameter)
    2091             : {
    2092             :         OOJS_PROFILE_ENTER
    2093             :         
    2094             :         return [entity isVisibleToScripts];
    2095             :         
    2096             :         OOJS_PROFILE_EXIT
    2097             : }
    2098             : 
    2099             : 
    2100           0 : BOOL JSEntityIsJavaScriptSearchablePredicate(Entity *entity, void *parameter)
    2101             : {
    2102             :         OOJS_PROFILE_ENTER
    2103             :         
    2104             :         if (![entity isVisibleToScripts])  return NO;
    2105             :         if ([entity isShip])
    2106             :         {
    2107             :                 if ([entity isSubEntity])  return NO;
    2108             :                 if ([entity status] == STATUS_COCKPIT_DISPLAY)  return NO;      // Demo ship
    2109             :                 return YES;
    2110             :         }
    2111             :         else if ([entity isPlanet])
    2112             :         {
    2113             :                 switch ([(OOPlanetEntity *)entity planetType])
    2114             :                 {
    2115             :                         case STELLAR_TYPE_MOON:
    2116             :                         case STELLAR_TYPE_NORMAL_PLANET:
    2117             :                         case STELLAR_TYPE_SUN:
    2118             :                                 return YES;
    2119             :                                 
    2120             : #if !NEW_PLANETS
    2121             :                         case STELLAR_TYPE_ATMOSPHERE:
    2122             : #endif
    2123             :                         case STELLAR_TYPE_MINIATURE:
    2124             :                                 return NO;
    2125             :                 }
    2126             :         }
    2127             :         
    2128             :         return YES;     // would happen if we added a new script-visible class
    2129             :         
    2130             :         OOJS_PROFILE_EXIT
    2131             : }
    2132             : 
    2133             : 
    2134           0 : BOOL JSEntityIsDemoShipPredicate(Entity *entity, void *parameter)
    2135             : {
    2136             :         return ([entity isVisibleToScripts] && [entity isShip] && [entity status] == STATUS_COCKPIT_DISPLAY && ![entity isSubEntity]);
    2137             : }
    2138             : 
    2139           0 : static NSMapTable *sRegisteredSubClasses;
    2140             : 
    2141           0 : void OOJSRegisterSubclass(JSClass *subclass, JSClass *superclass)
    2142             : {
    2143             :         NSCParameterAssert(subclass != NULL && superclass != NULL);
    2144             :         
    2145             :         if (sRegisteredSubClasses == NULL)
    2146             :         {
    2147             :                 sRegisteredSubClasses = NSCreateMapTable(NSNonOwnedPointerMapKeyCallBacks, NSNonOwnedPointerMapValueCallBacks, 0);
    2148             :         }
    2149             :         
    2150             :         NSCAssert(NSMapGet(sRegisteredSubClasses, subclass) == NULL, @"A JS class cannot be registered as a subclass of multiple classes.");
    2151             :         
    2152             :         NSMapInsertKnownAbsent(sRegisteredSubClasses, subclass, superclass);
    2153             : }
    2154             : 
    2155             : 
    2156           0 : static void UnregisterSubclasses(void)
    2157             : {
    2158             :         NSFreeMapTable(sRegisteredSubClasses);
    2159             :         sRegisteredSubClasses = NULL;
    2160             : }
    2161             : 
    2162             : 
    2163           0 : BOOL OOJSIsSubclass(JSClass *putativeSubclass, JSClass *superclass)
    2164             : {
    2165             :         NSCParameterAssert(putativeSubclass != NULL && superclass != NULL);
    2166             :         NSCAssert(sRegisteredSubClasses != NULL, @"OOJSIsSubclass() called before any subclasses registered (disallowed for hot path efficiency).");
    2167             :         
    2168             :         do
    2169             :         {
    2170             :                 if (putativeSubclass == superclass)  return YES;
    2171             :                 
    2172             :                 putativeSubclass = NSMapGet(sRegisteredSubClasses, putativeSubclass);
    2173             :         }
    2174             :         while (putativeSubclass != NULL);
    2175             :         
    2176             :         return NO;
    2177             : }
    2178             : 
    2179             : 
    2180           0 : BOOL OOJSObjectGetterImplPRIVATE(JSContext *context, JSObject *object, JSClass *requiredJSClass,
    2181             : #ifndef NDEBUG
    2182             :         Class requiredObjCClass, const char *name,
    2183             : #endif
    2184             :         id *outObject)
    2185             : {
    2186             : #ifndef NDEBUG
    2187             :         OOJS_PROFILE_ENTER_NAMED(name)
    2188             :         NSCParameterAssert(requiredObjCClass != Nil);
    2189             :         NSCParameterAssert(context != NULL && object != NULL && requiredJSClass != NULL && outObject != NULL);
    2190             : #else
    2191             :         OOJS_PROFILE_ENTER
    2192             : #endif
    2193             :         
    2194             :         /*
    2195             :                 Ensure it's a valid type of JS object. This is absolutely necessary,
    2196             :                 because if we don't check it we'll crash trying to get the private
    2197             :                 field of something that isn't an ObjC object wrapper - for example,
    2198             :                 Ship.setAI.call(new Vector3D, "") is valid JavaScript.
    2199             :                 
    2200             :                 Alternatively, we could abuse JSCLASS_PRIVATE_IS_NSISUPPORTS as a
    2201             :                 flag for ObjC object wrappers (SpiderMonkey only uses it internally
    2202             :                 in a debug function we don't use), but we'd still need to do an
    2203             :                 Objective-C class test, and I don't think that's any faster.
    2204             :                 TODO: profile.
    2205             :         */
    2206             :         JSClass *actualClass = OOJSGetClass(context, object);
    2207             :         if (EXPECT_NOT(!OOJSIsSubclass(actualClass, requiredJSClass)))
    2208             :         {
    2209             :                 OOJSReportError(context, @"Native method expected %s, got %@.", requiredJSClass->name, OOStringFromJSValue(context, OBJECT_TO_JSVAL(object)));
    2210             :                 return NO;
    2211             :         }
    2212             :         NSCAssert(actualClass->flags & JSCLASS_HAS_PRIVATE, @"Native object accessor requires JS class with private storage.");
    2213             :         
    2214             :         // Get the underlying object.
    2215             :         *outObject = [(id)JS_GetPrivate(context, object) weakRefUnderlyingObject];
    2216             :         
    2217             : #ifndef NDEBUG
    2218             :         // Double-check that the underlying object is of the expected ObjC class.
    2219             :         if (EXPECT_NOT(*outObject != nil && ![*outObject isKindOfClass:requiredObjCClass]))
    2220             :         {
    2221             :                 OOJSReportError(context, @"Native method expected %@ from %s and got correct JS type but incorrect native object %@", requiredObjCClass, requiredJSClass->name, *outObject);
    2222             :                 return NO;
    2223             :         }
    2224             : #endif
    2225             :         
    2226             :         return YES;
    2227             :         
    2228             :         OOJS_PROFILE_EXIT
    2229             : }
    2230             : 
    2231             : 
    2232           0 : NSDictionary *OOJSDictionaryFromJSValue(JSContext *context, jsval value)
    2233             : {
    2234             :         OOJS_PROFILE_ENTER
    2235             :         
    2236             :         JSObject *object = NULL;
    2237             :         if (EXPECT_NOT(!JS_ValueToObject(context, value, &object) || object == NULL))
    2238             :         {
    2239             :                 return nil;
    2240             :         }
    2241             :         return OOJSDictionaryFromJSObject(context, object);
    2242             :         
    2243             :         OOJS_PROFILE_EXIT
    2244             : }
    2245             : 
    2246             : 
    2247           0 : NSDictionary *OOJSDictionaryFromJSObject(JSContext *context, JSObject *object)
    2248             : {
    2249             :         OOJS_PROFILE_ENTER
    2250             :         
    2251             :         JSIdArray                                       *ids = NULL;
    2252             :         jsint                                           i;
    2253             :         NSMutableDictionary                     *result = nil;
    2254             :         jsval                                           value = JSVAL_VOID;
    2255             :         id                                                      objKey = nil;
    2256             :         id                                                      objValue = nil;
    2257             :         
    2258             :         ids = JS_Enumerate(context, object);
    2259             :         if (EXPECT_NOT(ids == NULL))
    2260             :         {
    2261             :                 return nil;
    2262             :         }
    2263             :         
    2264             :         result = [NSMutableDictionary dictionaryWithCapacity:ids->length];
    2265             :         for (i = 0; i != ids->length; ++i)
    2266             :         {
    2267             :                 jsid thisID = ids->vector[i];
    2268             :                 
    2269             :                 if (JSID_IS_STRING(thisID))
    2270             :                 {
    2271             :                         objKey = OOStringFromJSString(context, JSID_TO_STRING(thisID));
    2272             :                 }
    2273             :                 else if (JSID_IS_INT(thisID))
    2274             :                 {
    2275             :                         /* this causes problems with native functions which expect string keys
    2276             :                          * e.g. in mission.runScreen with the 'choices' parameter
    2277             :                          * should this instead be making the objKey a string?
    2278             :                          * is there anything that relies on the current behaviour?
    2279             :                          * - CIM 15/2/13 */
    2280             :                         objKey = [NSNumber numberWithInt:JSID_TO_INT(thisID)];
    2281             :                 }
    2282             :                 else
    2283             :                 {
    2284             :                         objKey = nil;
    2285             :                 }
    2286             :                 
    2287             :                 value = JSVAL_VOID;
    2288             :                 if (objKey != nil && !JS_LookupPropertyById(context, object, thisID, &value))  value = JSVAL_VOID;
    2289             :                 
    2290             :                 if (objKey != nil && !JSVAL_IS_VOID(value))
    2291             :                 {
    2292             :                         objValue = OOJSNativeObjectFromJSValue(context, value);
    2293             :                         if (objValue != nil)
    2294             :                         {
    2295             :                                 [result setObject:objValue forKey:objKey];
    2296             :                         }
    2297             :                 }
    2298             :         }
    2299             :         
    2300             :         JS_DestroyIdArray(context, ids);
    2301             :         return result;
    2302             :         
    2303             :         OOJS_PROFILE_EXIT
    2304             : }
    2305             : 
    2306             : 
    2307           0 : NSDictionary *OOJSDictionaryFromStringTable(JSContext *context, jsval tableValue)
    2308             : {
    2309             :         OOJS_PROFILE_ENTER
    2310             :         
    2311             :         JSObject                                        *tableObject = NULL;
    2312             :         JSIdArray                                       *ids;
    2313             :         jsint                                           i;
    2314             :         NSMutableDictionary                     *result = nil;
    2315             :         jsval                                           value = JSVAL_VOID;
    2316             :         id                                                      objKey = nil;
    2317             :         id                                                      objValue = nil;
    2318             :         
    2319             :         if (EXPECT_NOT(JSVAL_IS_NULL(tableValue) || !JS_ValueToObject(context, tableValue, &tableObject)))
    2320             :         {
    2321             :                 return nil;
    2322             :         }
    2323             :         
    2324             :         ids = JS_Enumerate(context, tableObject);
    2325             :         if (EXPECT_NOT(ids == NULL))
    2326             :         {
    2327             :                 return nil;
    2328             :         }
    2329             :         
    2330             :         result = [NSMutableDictionary dictionaryWithCapacity:ids->length];
    2331             :         for (i = 0; i != ids->length; ++i)
    2332             :         {
    2333             :                 jsid thisID = ids->vector[i];
    2334             :                 
    2335             :                 if (JSID_IS_STRING(thisID))
    2336             :                 {
    2337             :                         objKey = OOStringFromJSString(context, JSID_TO_STRING(thisID));
    2338             :                 }
    2339             :                 else
    2340             :                 {
    2341             :                         objKey = nil;
    2342             :                 }
    2343             :                 
    2344             :                 value = JSVAL_VOID;
    2345             :                 if (objKey != nil && !JS_LookupPropertyById(context, tableObject, thisID, &value))  value = JSVAL_VOID;
    2346             :                 
    2347             :                 if (objKey != nil && !JSVAL_IS_VOID(value))
    2348             :                 {
    2349             :                         objValue = OOStringFromJSValueEvenIfNull(context, value);
    2350             :                         
    2351             :                         if (objValue != nil)
    2352             :                         {
    2353             :                                 [result setObject:objValue forKey:objKey];
    2354             :                         }
    2355             :                 }
    2356             :         }
    2357             :         
    2358             :         JS_DestroyIdArray(context, ids);
    2359             :         return result;
    2360             :         
    2361             :         OOJS_PROFILE_EXIT
    2362             : }
    2363             : 
    2364             : 
    2365           0 : static NSMutableDictionary *sObjectConverters;
    2366             : 
    2367             : 
    2368           0 : id OOJSNativeObjectFromJSValue(JSContext *context, jsval value)
    2369             : {
    2370             :         OOJS_PROFILE_ENTER
    2371             :         
    2372             :         if (JSVAL_IS_NULL(value) || JSVAL_IS_VOID(value))  return nil;
    2373             :         
    2374             :         if (JSVAL_IS_INT(value))
    2375             :         {
    2376             :                 return [NSNumber numberWithInt:JSVAL_TO_INT(value)];
    2377             :         }
    2378             :         if (JSVAL_IS_DOUBLE(value))
    2379             :         {
    2380             :                 return [NSNumber numberWithDouble:JSVAL_TO_DOUBLE(value)];
    2381             :         }
    2382             :         if (JSVAL_IS_BOOLEAN(value))
    2383             :         {
    2384             :                 return [NSNumber numberWithBool:JSVAL_TO_BOOLEAN(value)];
    2385             :         }
    2386             :         if (JSVAL_IS_STRING(value))
    2387             :         {
    2388             :                 return OOStringFromJSValue(context, value);
    2389             :         }
    2390             :         if (JSVAL_IS_OBJECT(value))
    2391             :         {
    2392             :                 return OOJSNativeObjectFromJSObject(context, JSVAL_TO_OBJECT(value));
    2393             :         }
    2394             :         return nil;
    2395             :         
    2396             :         OOJS_PROFILE_EXIT
    2397             : }
    2398             : 
    2399             : 
    2400           0 : id OOJSNativeObjectFromJSObject(JSContext *context, JSObject *tableObject)
    2401             : {
    2402             :         OOJS_PROFILE_ENTER
    2403             :         
    2404             :         NSValue                                 *wrappedClass = nil;
    2405             :         NSValue                                 *wrappedConverter = nil;
    2406             :         OOJSClassConverterCallback converter = NULL;
    2407             :         JSClass                                 *class = NULL;
    2408             :         
    2409             :         if (tableObject == NULL)  return nil;
    2410             :         
    2411             :         class = OOJSGetClass(context, tableObject);
    2412             :         wrappedClass = [NSValue valueWithPointer:class];
    2413             :         if (wrappedClass != nil)  wrappedConverter = [sObjectConverters objectForKey:wrappedClass];
    2414             :         if (wrappedConverter != nil)
    2415             :         {
    2416             :                 converter = [wrappedConverter pointerValue];
    2417             :                 return converter(context, tableObject);
    2418             :         }
    2419             :         return nil;
    2420             :         
    2421             :         OOJS_PROFILE_EXIT
    2422             : }
    2423             : 
    2424             : 
    2425           0 : id OOJSNativeObjectOfClassFromJSValue(JSContext *context, jsval value, Class requiredClass)
    2426             : {
    2427             :         id result = OOJSNativeObjectFromJSValue(context, value);
    2428             :         if (![result isKindOfClass:requiredClass])  result = nil;
    2429             :         return result;
    2430             : }
    2431             : 
    2432             : 
    2433           0 : id OOJSNativeObjectOfClassFromJSObject(JSContext *context, JSObject *object, Class requiredClass)
    2434             : {
    2435             :         id result = OOJSNativeObjectFromJSObject(context, object);
    2436             :         if (![result isKindOfClass:requiredClass])  result = nil;
    2437             :         return result;
    2438             : }
    2439             : 
    2440             : 
    2441           0 : id OOJSBasicPrivateObjectConverter(JSContext *context, JSObject *object)
    2442             : {
    2443             :         id                                              result;
    2444             :         
    2445             :         /*      This will do the right thing - for non-OOWeakReferences,
    2446             :                 weakRefUnderlyingObject returns the object itself. For nil, of course,
    2447             :                 it returns nil.
    2448             :         */
    2449             :         result = JS_GetPrivate(context, object);
    2450             :         return [result weakRefUnderlyingObject];
    2451             : }
    2452             : 
    2453             : 
    2454           0 : void OOJSRegisterObjectConverter(JSClass *theClass, OOJSClassConverterCallback converter)
    2455             : {
    2456             :         NSValue                                 *wrappedClass = nil;
    2457             :         NSValue                                 *wrappedConverter = nil;
    2458             :         
    2459             :         if (theClass == NULL)  return;
    2460             :         if (sObjectConverters == nil)  sObjectConverters = [[NSMutableDictionary alloc] init];
    2461             :         
    2462             :         wrappedClass = [NSValue valueWithPointer:theClass];
    2463             :         if (converter != NULL)
    2464             :         {
    2465             :                 wrappedConverter = [NSValue valueWithPointer:converter];
    2466             :                 [sObjectConverters setObject:wrappedConverter forKey:wrappedClass];
    2467             :         }
    2468             :         else
    2469             :         {
    2470             :                 [sObjectConverters removeObjectForKey:wrappedClass];
    2471             :         }
    2472             : }
    2473             : 
    2474             : 
    2475           0 : static void UnregisterObjectConverters(void)
    2476             : {
    2477             :         DESTROY(sObjectConverters);
    2478             : }
    2479             : 
    2480             : 
    2481           0 : static id JSArrayConverter(JSContext *context, JSObject *array)
    2482             : {
    2483             :         jsuint                                          i, count;
    2484             :         id                                                      *values = NULL;
    2485             :         jsval                                           value = JSVAL_VOID;
    2486             :         id                                                      object = nil;
    2487             :         NSArray                                         *result = nil;
    2488             :         
    2489             :         // Convert a JS array to an NSArray by calling OOJSNativeObjectFromJSValue() on all its elements.
    2490             :         if (!JS_IsArrayObject(context, array)) return nil;
    2491             :         if (!JS_GetArrayLength(context, array, &count)) return nil;
    2492             :         
    2493             :         if (count == 0)  return [NSArray array];
    2494             :         
    2495             :         values = calloc(count, sizeof *values);
    2496             :         if (values == NULL)  return nil;
    2497             :         
    2498             :         for (i = 0; i != count; ++i)
    2499             :         {
    2500             :                 value = JSVAL_VOID;
    2501             :                 if (!JS_GetElement(context, array, i, &value))  value = JSVAL_VOID;
    2502             :                 
    2503             :                 object = OOJSNativeObjectFromJSValue(context, value);
    2504             :                 if (object == nil)  object = [NSNull null];
    2505             :                 values[i] = object;
    2506             :         }
    2507             :         
    2508             :         result = [NSArray arrayWithObjects:values count:count];
    2509             :         free(values);
    2510             :         return result;
    2511             : }
    2512             : 
    2513             : 
    2514           0 : static id JSStringConverter(JSContext *context, JSObject *object)
    2515             : {
    2516             :         return OOStringFromJSValue(context, OBJECT_TO_JSVAL(object));
    2517             : }
    2518             : 
    2519             : 
    2520           0 : static id JSNumberConverter(JSContext *context, JSObject *object)
    2521             : {
    2522             :         jsdouble value;
    2523             :         if (JS_ValueToNumber(context, OBJECT_TO_JSVAL(object), &value))
    2524             :         {
    2525             :                 return [NSNumber numberWithDouble:value];
    2526             :         }
    2527             :         return nil;
    2528             : }
    2529             : 
    2530             : 
    2531           0 : static id JSBooleanConverter(JSContext *context, JSObject *object)
    2532             : {
    2533             :         /*      Fun With JavaScript: Boolean(false) is a truthy value, since it's a
    2534             :                 non-null object. JS_ValueToBoolean() therefore reports true.
    2535             :                 However, Boolean objects are transformed to numbers sanely, so this
    2536             :                 works.
    2537             :         */
    2538             :         jsdouble value;
    2539             :         if (JS_ValueToNumber(context, OBJECT_TO_JSVAL(object), &value))
    2540             :         {
    2541             :                 return [NSNumber numberWithBool:(value != 0)];
    2542             :         }
    2543             :         return nil;
    2544             : }

Generated by: LCOV version 1.14