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

          Line data    Source code
       1           0 : /*
       2             : 
       3             : OOJSTimer.m
       4             : 
       5             : 
       6             : Oolite
       7             : Copyright (C) 2004-2013 Giles C Williams and contributors
       8             : 
       9             : This program is free software; you can redistribute it and/or
      10             : modify it under the terms of the GNU General Public License
      11             : as published by the Free Software Foundation; either version 2
      12             : of the License, or (at your option) any later version.
      13             : 
      14             : This program is distributed in the hope that it will be useful,
      15             : but WITHOUT ANY WARRANTY; without even the implied warranty of
      16             : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      17             : GNU General Public License for more details.
      18             : 
      19             : You should have received a copy of the GNU General Public License
      20             : along with this program; if not, write to the Free Software
      21             : Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
      22             : MA 02110-1301, USA.
      23             : 
      24             : */
      25             : 
      26             : #import "OOJSTimer.h"
      27             : #import "OOJavaScriptEngine.h"
      28             : #import "Universe.h"
      29             : 
      30             : 
      31             : // Minimum allowable interval for repeating timers.
      32           0 : #define kMinInterval 0.25
      33             : 
      34             : 
      35           0 : static JSObject *sTimerPrototype;
      36           0 : static JSClass sTimerClass;
      37             : 
      38             : 
      39             : @interface OOJSTimer (Private)
      40             : 
      41           0 : - (id) initWithDelay:(OOTimeAbsolute)delay
      42             :                         interval:(OOTimeDelta)interval
      43             :                          context:(JSContext *)context
      44             :                         function:(jsval)function
      45             :                                 this:(JSObject *)jsThis;
      46             : 
      47             : @end
      48             : 
      49             : 
      50             : @implementation OOJSTimer
      51             : 
      52           0 : - (id) initWithDelay:(OOTimeAbsolute)delay
      53             :                         interval:(OOTimeDelta)interval
      54             :                          context:(JSContext *)context
      55             :                         function:(jsval)function
      56             :                                 this:(JSObject *)jsThis
      57             : {
      58             :         self = [super initWithNextTime:[UNIVERSE getTime] + delay interval:interval];
      59             :         if (self != nil)
      60             :         {
      61             :                 NSAssert(OOJSValueIsFunction(context, function), @"Attempt to init OOJSTimer with a function that isn't.");
      62             :                 
      63             :                 _jsThis = jsThis;
      64             :                 OOJSAddGCObjectRoot(context, &_jsThis, "OOJSTimer this");
      65             :                 
      66             :                 _function = function;
      67             :                 OOJSAddGCValueRoot(context, &_function, "OOJSTimer function");
      68             :                 
      69             :                 _jsSelf = JS_NewObject(context, &sTimerClass, sTimerPrototype, NULL);
      70             :                 if (_jsSelf != NULL)
      71             :                 {
      72             :                         if (!JS_SetPrivate(context, _jsSelf, [self retain]))  _jsSelf = NULL;
      73             :                 }
      74             :                 if (_jsSelf == NULL)
      75             :                 {
      76             :                         [self release];
      77             :                         return nil;
      78             :                 }
      79             :                 
      80             :                 _owningScript = [[OOJSScript currentlyRunningScript] weakRetain];
      81             :                 
      82             :                 [[NSNotificationCenter defaultCenter] addObserver:self
      83             :                                                                                                  selector:@selector(deleteJSPointers)
      84             :                                                                                                          name:kOOJavaScriptEngineWillResetNotification
      85             :                                                                                                    object:[OOJavaScriptEngine sharedEngine]];
      86             :         }
      87             :         
      88             :         return self;
      89             : }
      90             : 
      91             : 
      92           0 : - (void) deleteJSPointers
      93             : {
      94             :         [self unscheduleTimer];
      95             :         
      96             :         if (_jsThis != NULL)
      97             :         {
      98             :                 _jsThis = NULL;
      99             :                 _function = JSVAL_VOID;
     100             :                 
     101             :                 JSContext *context = OOJSAcquireContext();
     102             :                 JS_RemoveObjectRoot(context, &_jsThis);
     103             :                 JS_RemoveValueRoot(context, &_function);
     104             :                 OOJSRelinquishContext(context);
     105             :                 
     106             :                 [[NSNotificationCenter defaultCenter] removeObserver:self
     107             :                                                                                                                 name:kOOJavaScriptEngineWillResetNotification
     108             :                                                                                                           object:[OOJavaScriptEngine sharedEngine]];
     109             :         }
     110             : }
     111             : 
     112             : 
     113           0 : - (void) dealloc
     114             : {
     115             :         [_owningScript release];
     116             :         
     117             :         [self deleteJSPointers];
     118             :         
     119             :         [super dealloc];
     120             : }
     121             : 
     122             : 
     123           0 : - (NSString *) descriptionComponents
     124             : {
     125             :         NSString                                *funcName = nil;
     126             :         JSContext                               *context = NULL;
     127             :         
     128             :         if (JSVAL_IS_VOID(_function) || JSVAL_IS_NULL(_function))
     129             :         {
     130             :                 return @"invalid";
     131             :         }
     132             :         
     133             :         context = OOJSAcquireContext();
     134             :         funcName = OOStringFromJSString(context, JS_GetFunctionId(JS_ValueToFunction(context, _function)));
     135             :         OOJSRelinquishContext(context);
     136             :         
     137             :         if (funcName == nil)
     138             :         {
     139             :                 funcName = @"anonymous";
     140             :         }
     141             :         
     142             :         return [NSString stringWithFormat:@"%@, function: %@", [super descriptionComponents], funcName];
     143             : }
     144             : 
     145             : 
     146           0 : - (NSString *) oo_jsClassName
     147             : {
     148             :         return @"Timer";
     149             : }
     150             : 
     151             : 
     152           0 : - (void) timerFired
     153             : {
     154             :         jsval                                   rval = JSVAL_VOID;
     155             :         NSString                                *description = nil;
     156             :         
     157             :         OOJavaScriptEngine *engine = [OOJavaScriptEngine sharedEngine];
     158             :         JSContext *context = OOJSAcquireContext();
     159             :         
     160             :         // stop and remove the timer if _jsThis (the first parameter in the constructor) dies.
     161             :         id object = OOJSNativeObjectFromJSObject(context, _jsThis);
     162             :         if (object != nil)
     163             :         {
     164             :                 description = [object oo_jsDescription];
     165             :                 if (description == nil)  description = [object description];
     166             :         }
     167             :         
     168             :         if (description == nil)
     169             :         {
     170             :                 [self unscheduleTimer];
     171             :                 OOJSRelinquishContext(context);
     172             :                 return;
     173             :         }
     174             :         
     175             :         [OOJSScript pushScript:_owningScript];
     176             :         [engine callJSFunction:_function
     177             :                                  forObject:_jsThis
     178             :                                           argc:0
     179             :                                           argv:NULL
     180             :                                         result:&rval];
     181             :         [OOJSScript popScript:_owningScript];
     182             :         
     183             :         OOJSRelinquishContext(context);
     184             : }
     185             : 
     186             : 
     187           0 : - (jsval) oo_jsValueInContext:(JSContext *)context
     188             : {
     189             :         return OBJECT_TO_JSVAL(_jsSelf);
     190             : }
     191             : 
     192             : @end
     193             : 
     194             : 
     195             : static JSBool TimerGetProperty(JSContext *context, JSObject *this, jsid propID, jsval *value);
     196             : static JSBool TimerSetProperty(JSContext *context, JSObject *this, jsid propID, JSBool strict, jsval *value);
     197             : static void TimerFinalize(JSContext *context, JSObject *this);
     198             : static JSBool TimerConstruct(JSContext *context, uintN argc, jsval *vp);
     199             : 
     200             : // Methods
     201             : static JSBool TimerStart(JSContext *context, uintN argc, jsval *vp);
     202             : static JSBool TimerStop(JSContext *context, uintN argc, jsval *vp);
     203             : 
     204             : 
     205             : static JSClass sTimerClass =
     206             : {
     207             :         "Timer",
     208             :         JSCLASS_HAS_PRIVATE,
     209             :         
     210             :         JS_PropertyStub,                // addProperty
     211             :         JS_PropertyStub,                // delProperty
     212             :         TimerGetProperty,               // getProperty
     213             :         TimerSetProperty,               // setProperty
     214             :         JS_EnumerateStub,               // enumerate
     215             :         JS_ResolveStub,                 // resolve
     216             :         JS_ConvertStub,                 // convert
     217             :         TimerFinalize,                  // finalize
     218             :         JSCLASS_NO_OPTIONAL_MEMBERS
     219             : };
     220             : 
     221             : 
     222           0 : enum
     223             : {
     224             :         // Property IDs
     225             :         kTimer_nextTime,                        // next fire time, double, read/write
     226             :         kTimer_interval,                        // interval, double, read/write
     227             :         kTimer_isRunning                        // is scheduled, boolean, read-only
     228             : };
     229             : 
     230             : 
     231           0 : static JSPropertySpec sTimerProperties[] =
     232             : {
     233             :         // JS name                                      ID                                                      flags
     234             :         { "interval",                         kTimer_interval,                        OOJS_PROP_READWRITE_CB },
     235             :         { "isRunning",                                kTimer_isRunning,                       OOJS_PROP_READONLY_CB },
     236             :         { "nextTime",                         kTimer_nextTime,                        OOJS_PROP_READWRITE_CB },
     237             :         { 0 }
     238             : };
     239             : 
     240             : 
     241           0 : static JSFunctionSpec sTimerMethods[] =
     242             : {
     243             :         // JS name                                      Function                                        min args
     244             :         { "toString",                         OOJSObjectWrapperToString,      0 },
     245             :         { "start",                                    TimerStart,                                     0 },
     246             :         { "stop",                                     TimerStop,                                      0 },
     247             :         { 0 }
     248             : };
     249             : 
     250             : 
     251           0 : DEFINE_JS_OBJECT_GETTER(JSTimerGetTimer, &sTimerClass, sTimerPrototype, OOJSTimer);
     252             : 
     253             : 
     254           0 : void InitOOJSTimer(JSContext *context, JSObject *global)
     255             : {
     256             :         sTimerPrototype = JS_InitClass(context, global, NULL, &sTimerClass, TimerConstruct, 0, sTimerProperties, sTimerMethods, NULL, NULL);
     257             :         OOJSRegisterObjectConverter(&sTimerClass, OOJSBasicPrivateObjectConverter);
     258             : }
     259             : 
     260             : 
     261           0 : static JSBool TimerGetProperty(JSContext *context, JSObject *this, jsid propID, jsval *value)
     262             : {
     263             :         if (!JSID_IS_INT(propID))  return YES;
     264             :         
     265             :         OOJS_NATIVE_ENTER(context)
     266             :         
     267             :         OOJSTimer                               *timer = nil;
     268             :         
     269             :         if (EXPECT_NOT(!JSTimerGetTimer(context, this, &timer))) return NO;
     270             :         
     271             :         switch (JSID_TO_INT(propID))
     272             :         {
     273             :                 case kTimer_nextTime:
     274             :                         return JS_NewNumberValue(context, [timer nextTime], value);
     275             :                         
     276             :                 case kTimer_interval:
     277             :                         return JS_NewNumberValue(context, [timer interval], value);
     278             :                         
     279             :                 case kTimer_isRunning:
     280             :                         *value = OOJSValueFromBOOL([timer isScheduled]);
     281             :                         return YES;
     282             :                         
     283             :                 default:
     284             :                         OOJSReportBadPropertySelector(context, this, propID, sTimerProperties);
     285             :                         return NO;
     286             :         }
     287             :         
     288             :         OOJS_NATIVE_EXIT
     289             : }
     290             : 
     291             : 
     292           0 : static JSBool TimerSetProperty(JSContext *context, JSObject *this, jsid propID, JSBool strict, jsval *value)
     293             : {
     294             :         if (!JSID_IS_INT(propID))  return YES;
     295             :         
     296             :         OOJS_NATIVE_ENTER(context)
     297             :         
     298             :         OOJSTimer                               *timer = nil;
     299             :         double                                  fValue;
     300             :         
     301             :         if (EXPECT_NOT(!JSTimerGetTimer(context, this, &timer))) return NO;
     302             :         
     303             :         switch (JSID_TO_INT(propID))
     304             :         {
     305             :                 case kTimer_nextTime:
     306             :                         if (JS_ValueToNumber(context, *value, &fValue))
     307             :                         {
     308             :                                 if (![timer setNextTime:fValue])
     309             :                                 {
     310             :                                         OOJSReportWarning(context, @"Ignoring attempt to change next fire time for running timer %@.", timer);
     311             :                                 }
     312             :                                 return YES;
     313             :                         }
     314             :                         break;
     315             :                         
     316             :                 case kTimer_interval:
     317             :                         if (JS_ValueToNumber(context, *value, &fValue))
     318             :                         {
     319             :                                 [timer setInterval:fValue];
     320             :                                 return YES;
     321             :                         }
     322             :                         break;
     323             :                         
     324             :                 default:
     325             :                         OOJSReportBadPropertySelector(context, this, propID, sTimerProperties);
     326             :                         return NO;
     327             :         }
     328             :         
     329             :         OOJSReportBadPropertyValue(context, this, propID, sTimerProperties, *value);
     330             :         return NO;
     331             :         
     332             :         OOJS_NATIVE_EXIT
     333             : }
     334             : 
     335             : 
     336           0 : static void TimerFinalize(JSContext *context, JSObject *this)
     337             : {
     338             :         OOJS_PROFILE_ENTER
     339             :         
     340             :         // Can't use JSTimerGetTimer() here - potential chicken-and-egg problem manifesting as a crash.
     341             :         OOJSTimer *timer = (OOJSTimer *)JS_GetPrivate(context, this);
     342             :         
     343             :         if (timer != nil)
     344             :         {
     345             :                 if ([timer isScheduled])
     346             :                 {
     347             :                         OOLogWARN(@"script.javaScript.unrootedTimer", @"Timer %@ is being garbage-collected while still running. You must keep a reference to all running timers, or they will stop unpredictably!", timer);
     348             :                 }
     349             :                 [timer release];
     350             :                 JS_SetPrivate(context, this, NULL);
     351             :         }
     352             :         
     353             :         OOJS_PROFILE_EXIT_VOID
     354             : }
     355             : 
     356             : 
     357             : // new Timer(this : Object, function : Function, delay : Number [, interval : Number]) : Timer
     358           0 : static JSBool TimerConstruct(JSContext *context, uintN argc, jsval *vp)
     359             : {
     360             :         OOJS_NATIVE_ENTER(context)
     361             :         
     362             :         jsval                                   function = JSVAL_VOID;
     363             :         double                                  delay;
     364             :         double                                  interval = -1.0;
     365             :         OOJSTimer                               *timer = nil;
     366             :         JSObject                                *callbackThis = NULL;
     367             :         
     368             :         if (EXPECT_NOT(!JS_IsConstructing(context, vp)))
     369             :         {
     370             :                 OOJSReportError(context, @"Timer() cannot be called as a function, it must be used as a constructor (as in new Timer(...)).");
     371             :                 return NO;
     372             :         }
     373             :         
     374             :         if (argc < 3)
     375             :         {
     376             :                 OOJSReportBadArguments(context, nil, @"Timer", argc, OOJS_ARGV, @"Invalid arguments in constructor", @"(object, function, number [, number])");
     377             :                 return NO;
     378             :         }
     379             :         
     380             :         if (!JSVAL_IS_NULL(OOJS_ARGV[0]) && !JSVAL_IS_VOID(OOJS_ARGV[0]))
     381             :         {
     382             :                 if (!JS_ValueToObject(context, OOJS_ARGV[0], &callbackThis))
     383             :                 {
     384             :                         OOJSReportBadArguments(context, nil, @"Timer", 1, OOJS_ARGV, @"Invalid argument in constructor", @"object");
     385             :                         return NO;
     386             :                 }
     387             :         }
     388             :         
     389             :         function = OOJS_ARGV[1];
     390             :         if (JS_ValueToFunction(context, function) == NULL)
     391             :         {
     392             :                 OOJSReportBadArguments(context, nil, @"Timer", 1, OOJS_ARGV + 1, @"Invalid argument in constructor", @"function");
     393             :                 return NO;
     394             :         }
     395             :         
     396             :         if (!JS_ValueToNumber(context, OOJS_ARGV[2], &delay) || isnan(delay))
     397             :         {
     398             :                 OOJSReportBadArguments(context, nil, @"Timer", 1, OOJS_ARGV + 2, @"Invalid argument in constructor", @"number");
     399             :                 return NO;
     400             :         }
     401             :         
     402             :         // Fourth argument is optional.
     403             :         if (3 < argc && !JS_ValueToNumber(context, OOJS_ARGV[3], &interval))  interval = -1;
     404             :         
     405             :         // Ensure interval is not too small.
     406             :         if (0.0 < interval && interval < kMinInterval)  interval = kMinInterval;
     407             :         
     408             :         timer = [[OOJSTimer alloc] initWithDelay:delay
     409             :                                                                         interval:interval
     410             :                                                                          context:context
     411             :                                                                         function:function
     412             :                                                                                 this:callbackThis];
     413             :         if (EXPECT_NOT(!timer))  return NO;
     414             :         
     415             :         if (delay >= 0)      // Leave in stopped state if delay is negative
     416             :         {
     417             :                 [timer scheduleTimer];
     418             :         }
     419             :         [timer autorelease];
     420             :         OOJS_RETURN_OBJECT(timer);
     421             :         
     422             :         OOJS_NATIVE_EXIT
     423             : }
     424             : 
     425             : 
     426             : // *** Methods ***
     427             : 
     428             : // start() : Boolean
     429           0 : static JSBool TimerStart(JSContext *context, uintN argc, jsval *vp)
     430             : {
     431             :         OOJS_NATIVE_ENTER(context)
     432             :         
     433             :         OOJSTimer                                       *thisTimer = nil;
     434             :         
     435             :         if (EXPECT_NOT(!JSTimerGetTimer(context, OOJS_THIS, &thisTimer)))  return NO;
     436             :         
     437             :         OOJS_RETURN_BOOL([thisTimer scheduleTimer]);
     438             :         
     439             :         OOJS_NATIVE_EXIT
     440             : }
     441             : 
     442             : 
     443             : // stop()
     444           0 : static JSBool TimerStop(JSContext *context, uintN argc, jsval *vp)
     445             : {
     446             :         OOJS_NATIVE_ENTER(context)
     447             :         
     448             :         OOJSTimer                                       *thisTimer = nil;
     449             :         
     450             :         if (EXPECT_NOT(!JSTimerGetTimer(context, OOJS_THIS, &thisTimer)))  return NO;
     451             :         
     452             :         [thisTimer unscheduleTimer];
     453             :         OOJS_RETURN_VOID;
     454             :         
     455             :         OOJS_NATIVE_EXIT
     456             : }

Generated by: LCOV version 1.14