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

          Line data    Source code
       1           0 : /*
       2             : 
       3             : OOJSScript.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             : #ifndef OO_CACHE_JS_SCRIPTS
      26           0 : #define OO_CACHE_JS_SCRIPTS             1
      27             : #endif
      28             : 
      29             : 
      30             : #import "OOJSScript.h"
      31             : #import "OOJavaScriptEngine.h"
      32             : #import "OOJSEngineTimeManagement.h"
      33             : 
      34             : #import "OOLogging.h"
      35             : #import "OOConstToString.h"
      36             : #import "Entity.h"
      37             : #import "NSStringOOExtensions.h"
      38             : #import "EntityOOJavaScriptExtensions.h"
      39             : #import "OOConstToJSString.h"
      40             : #import "OOManifestProperties.h"
      41             : #import "OOCollectionExtractors.h"
      42             : #import "OOPListParsing.h"
      43             : #import "OODebugStandards.h"
      44             : 
      45             : #if OO_CACHE_JS_SCRIPTS
      46             : #include <jsxdrapi.h>
      47             : #import "OOCacheManager.h"
      48             : #endif
      49             : 
      50             : 
      51           0 : typedef struct RunningStack RunningStack;
      52           0 : struct RunningStack
      53             : {
      54           0 :         RunningStack            *back;
      55           0 :         OOJSScript                      *current;
      56             : };
      57             : 
      58             : 
      59           0 : static JSObject                 *sScriptPrototype;
      60           0 : static RunningStack             *sRunningStack = NULL;
      61             : 
      62             : 
      63             : static void AddStackToArrayReversed(NSMutableArray *array, RunningStack *stack);
      64             : 
      65             : static JSScript *LoadScriptWithName(JSContext *context, NSString *path, JSObject *object, JSObject **outScriptObject, NSString **outErrorMessage);
      66             : 
      67             : #if OO_CACHE_JS_SCRIPTS
      68             : static NSData *CompiledScriptData(JSContext *context, JSScript *script);
      69             : static JSScript *ScriptWithCompiledData(JSContext *context, NSData *data);
      70             : #endif
      71             : 
      72             : static NSString *StrippedName(NSString *string);
      73             : 
      74             : 
      75             : static JSBool ScriptAddProperty(JSContext *context, JSObject *this, jsid propID, jsval *value);
      76             : 
      77             : 
      78           0 : static JSClass sScriptClass =
      79             : {
      80             :         "Script",
      81             :         JSCLASS_HAS_PRIVATE,
      82             :         
      83             :         ScriptAddProperty,
      84             :         JS_PropertyStub,
      85             :         JS_PropertyStub,
      86             :         JS_StrictPropertyStub,
      87             :         JS_EnumerateStub,
      88             :         JS_ResolveStub,
      89             :         JS_ConvertStub,
      90             :         OOJSObjectWrapperFinalize
      91             : };
      92             : 
      93             : 
      94           0 : static JSFunctionSpec sScriptMethods[] =
      95             : {
      96             :         // JS name                                      Function                                        min args
      97             :         { "toString",                         OOJSObjectWrapperToString,      0, },
      98             :         { 0 }
      99             : };
     100             : 
     101             : 
     102             : @interface OOJSScript (OOPrivate)
     103             : 
     104           0 : - (NSString *)scriptNameFromPath:(NSString *)path;
     105           0 : - (NSDictionary *)defaultPropertiesFromPath:(NSString *)path;
     106             : 
     107             : @end
     108             : 
     109             : 
     110             : @implementation OOJSScript
     111             : 
     112             : + (id) scriptWithPath:(NSString *)path properties:(NSDictionary *)properties
     113             : {
     114             :         return [[[self alloc] initWithPath:path properties:properties] autorelease];
     115             : }
     116             : 
     117             : 
     118             : - (id) initWithPath:(NSString *)path properties:(NSDictionary *)properties
     119             : {
     120             :         JSContext                               *context = NULL;
     121             :         NSString                                *problem = nil;         // Acts as error flag.
     122             :         JSScript                                *script = NULL;
     123             :         JSObject                                *scriptObject = NULL;
     124             :         jsval                                   returnValue = JSVAL_VOID;
     125             :         NSEnumerator                    *keyEnum = nil;
     126             :         NSString                                *key = nil;
     127             :         id                                              property = nil;
     128             :         
     129             :         self = [super init];
     130             :         if (self == nil) problem = @"allocation failure";
     131             :         else
     132             :         {
     133             :                 context = OOJSAcquireContext();
     134             :                 
     135             :                 if (JS_IsExceptionPending(context))
     136             :                 {
     137             :                         JS_ClearPendingException(context);
     138             :                         OOLogERR(@"script.javaScript.load.waitingException", @"Prior to loading script %@, there was a pending JavaScript exception, which has been cleared. This is an internal error, please report it.", path);
     139             :                 }
     140             :                 
     141             :                 // Set up JS object
     142             :                 if (!problem)
     143             :                 {
     144             :                         _jsSelf = JS_NewObject(context, &sScriptClass, sScriptPrototype, NULL);
     145             :                         if (_jsSelf == NULL) problem = @"allocation failure";
     146             :                 }
     147             :                 
     148             :                 if (!problem && !OOJSAddGCObjectRoot(context, &_jsSelf, "Script object"))
     149             :                 {
     150             :                         problem = @"could not add JavaScript root object";
     151             :                 }
     152             :                 
     153             :                 if (!problem && !OOJSAddGCObjectRoot(context, &scriptObject, "Script GC holder"))
     154             :                 {
     155             :                         problem = @"could not add JavaScript root object";
     156             :                 }
     157             :                 
     158             :                 if (!problem)
     159             :                 {
     160             :                         if (!JS_SetPrivate(context, _jsSelf, OOConsumeReference([self weakRetain])))
     161             :                         {
     162             :                                 problem = @"could not set private backreference";
     163             :                         }
     164             :                 }
     165             :                 
     166             :                 // Push self on stack of running scripts.
     167             :                 RunningStack stackElement =
     168             :                 {
     169             :                         .back = sRunningStack,
     170             :                         .current = self
     171             :                 };
     172             :                 sRunningStack = &stackElement;
     173             :                 
     174             :                 filePath = [path retain];
     175             :                 
     176             :                 if (!problem)
     177             :                 {
     178             :                         OOLog(@"script.javaScript.willLoad", @"About to load JavaScript %@", path);
     179             :                         script = LoadScriptWithName(context, path, _jsSelf, &scriptObject, &problem);
     180             :                 }
     181             :                 OOLogIndentIf(@"script.javaScript.willLoad");
     182             :                 
     183             :                 // Set default properties from manifest.plist
     184             :                 NSDictionary *defaultProperties = [self defaultPropertiesFromPath:path];
     185             :                 for (keyEnum = [defaultProperties keyEnumerator]; (key = [keyEnum nextObject]); )
     186             :                 {
     187             :                         if ([key isKindOfClass:[NSString class]])
     188             :                         {
     189             :                                 property = [defaultProperties objectForKey:key];
     190             :                                 if ([key isEqualToString:kLocalManifestProperty])
     191             :                                 {
     192             :                                         // this must not be editable
     193             :                                         [self defineProperty:property named:key];
     194             :                                 }
     195             :                                 else
     196             :                                 {
     197             :                                         // can be overwritten by script itself
     198             :                                         [self setProperty:property named:key];
     199             :                                 }
     200             :                         }
     201             :                 }
     202             : 
     203             :                 // Set properties. (read-only)
     204             :                 if (!problem && properties != nil)
     205             :                 {
     206             :                         for (keyEnum = [properties keyEnumerator]; (key = [keyEnum nextObject]); )
     207             :                         {
     208             :                                 if ([key isKindOfClass:[NSString class]])
     209             :                                 {
     210             :                                         property = [properties objectForKey:key];
     211             :                                         [self defineProperty:property named:key];
     212             :                                 }
     213             :                         }
     214             :                 }
     215             :                 
     216             :                 /*      Set initial name (in case of script error during initial run).
     217             :                         The "name" ivar is not set here, so the property can be fetched from JS
     218             :                         if we fail during setup. However, the "name" ivar is set later so that
     219             :                         the script object can't be renamed after the initial run. This could
     220             :                         probably also be achieved by fiddling with JS property attributes.
     221             :                 */
     222             :                 jsid nameID = OOJSID("name");
     223             :                 [self setProperty:[self scriptNameFromPath:path] withID:nameID inContext:context];
     224             :                 
     225             :                 // Run the script (allowing it to set up the properties we need, as well as setting up those event handlers)
     226             :                 if (!problem)
     227             :                 {
     228             :                         OOJSStartTimeLimiterWithTimeLimit(kOOJSLongTimeLimit);
     229             :                         if (!JS_ExecuteScript(context, _jsSelf, script, &returnValue))
     230             :                         {
     231             :                                 problem = @"could not run script";
     232             :                         }
     233             :                         OOJSStopTimeLimiter();
     234             :                         
     235             :                         // We don't need the script any more - the event handlers hang around as long as the JS object exists.
     236             :                         JS_DestroyScript(context, script);
     237             :                 }
     238             :                 
     239             :                 JS_RemoveObjectRoot(context, &scriptObject);
     240             :                 
     241             :                 sRunningStack = stackElement.back;
     242             :                 
     243             :                 if (!problem)
     244             :                 {
     245             :                         // Get display attributes from script
     246             :                         DESTROY(name);
     247             :                         name = [StrippedName([[self propertyWithID:nameID inContext:context] description]) copy];
     248             :                         if (name == nil)
     249             :                         {
     250             :                                 name = [[self scriptNameFromPath:path] retain];
     251             :                                 [self setProperty:name withID:nameID inContext:context];
     252             :                         }
     253             :                         
     254             :                         version = [[[self propertyWithID:OOJSID("version") inContext:context] description] copy];
     255             :                         description = [[[self propertyWithID:OOJSID("description") inContext:context] description] copy];
     256             :                         
     257             :                         OOLog(@"script.javaScript.load.success", @"Loaded JavaScript: %@ -- %@", [self displayName], description ? description : (NSString *)@"(no description)");
     258             :                 }
     259             :                 
     260             :                 OOLogOutdentIf(@"script.javaScript.willLoad");
     261             :                 
     262             :                 DESTROY(filePath);      // Only used for error reporting during startup.
     263             :         }
     264             :         
     265             :         if (problem)
     266             :         {
     267             :                 OOLog(@"script.javaScript.load.failed", @"***** Error loading JavaScript script %@ -- %@", path, problem);
     268             :                 JS_ReportPendingException(context);
     269             :                 DESTROY(self);
     270             :         }
     271             :         
     272             :         OOJSRelinquishContext(context);
     273             :         
     274             :         if (self != nil)
     275             :         {
     276             :                 [[NSNotificationCenter defaultCenter] addObserver:self
     277             :                                                                                                  selector:@selector(javaScriptEngineWillReset:)
     278             :                                                                                                          name:kOOJavaScriptEngineWillResetNotification
     279             :                                                                                                    object:[OOJavaScriptEngine sharedEngine]];
     280             :         }
     281             :         
     282             :         return self;
     283             : }
     284             : 
     285             : 
     286           0 : - (void) dealloc
     287             : {
     288             :         [[NSNotificationCenter defaultCenter] removeObserver:self
     289             :                                                                                                         name:kOOJavaScriptEngineWillResetNotification
     290             :                                                                                                   object:[OOJavaScriptEngine sharedEngine]];
     291             :         
     292             :         DESTROY(name);
     293             :         DESTROY(description);
     294             :         DESTROY(version);
     295             :         DESTROY(filePath);
     296             :         
     297             :         if (_jsSelf != NULL)
     298             :         {
     299             :                 JSContext *context = OOJSAcquireContext();
     300             :                 
     301             :                 OOJSObjectWrapperFinalize(context, _jsSelf);    // Release weakref to self
     302             :                 JS_RemoveObjectRoot(context, &_jsSelf);                     // Unroot jsSelf
     303             :                 
     304             :                 OOJSRelinquishContext(context);
     305             :         }
     306             :         
     307             :         [weakSelf weakRefDrop];
     308             :         
     309             :         [super dealloc];
     310             : }
     311             : 
     312             : 
     313           0 : - (NSString *) oo_jsClassName
     314             : {
     315             :         return @"Script";
     316             : }
     317             : 
     318             : 
     319           0 : - (NSString *)descriptionComponents
     320             : {
     321             :         if (_jsSelf != NULL)  return [super descriptionComponents];
     322             :         else  return @"invalid script";
     323             : }
     324             : 
     325             : 
     326           0 : - (void) javaScriptEngineWillReset:(NSNotification *)notification
     327             : {
     328             :         // All scripts become invalid when the JS engine resets.
     329             :         if (_jsSelf != NULL)
     330             :         {
     331             :                 _jsSelf = NULL;
     332             :                 JSContext *context = OOJSAcquireContext();
     333             :                 JS_RemoveObjectRoot(context, &_jsSelf);
     334             :                 OOJSRelinquishContext(context);
     335             :         }
     336             : }
     337             : 
     338             : 
     339             : + (OOJSScript *) currentlyRunningScript
     340             : {
     341             :         if (sRunningStack == NULL)  return NULL;
     342             :         return sRunningStack->current;
     343             : }
     344             : 
     345             : 
     346             : + (NSArray *) scriptStack
     347             : {
     348             :         NSMutableArray                  *result = nil;
     349             :         
     350             :         result = [NSMutableArray array];
     351             :         AddStackToArrayReversed(result, sRunningStack);
     352             :         return result;
     353             : }
     354             : 
     355             : 
     356           0 : - (id) weakRetain
     357             : {
     358             :         if (weakSelf == nil)  weakSelf = [OOWeakReference weakRefWithObject:self];
     359             :         return [weakSelf retain];
     360             : }
     361             : 
     362             : 
     363           0 : - (void) weakRefDied:(OOWeakReference *)weakRef
     364             : {
     365             :         if (weakRef == weakSelf)  weakSelf = nil;
     366             : }
     367             : 
     368             : 
     369             : - (NSString *) name
     370             : {
     371             :         if (name == nil)  name = [[self propertyNamed:@"name"] copy];
     372             :         if (name == nil)  return [self scriptNameFromPath:filePath];    // Special case for parse errors during load.
     373             :         return name;
     374             : }
     375             : 
     376             : 
     377           0 : - (NSString *) scriptDescription
     378             : {
     379             :         return description;
     380             : }
     381             : 
     382             : 
     383             : - (NSString *) version
     384             : {
     385             :         return version;
     386             : }
     387             : 
     388             : 
     389           0 : - (void)runWithTarget:(Entity *)target
     390             : {
     391             :         
     392             : }
     393             : 
     394             : 
     395             : - (BOOL) callMethod:(jsid)methodID
     396             :                   inContext:(JSContext *)context
     397             :           withArguments:(jsval *)argv count:(intN)argc
     398             :                          result:(jsval *)outResult
     399             : {
     400             :         NSParameterAssert(name != NULL && (argv != NULL || argc == 0) && context != NULL && JS_IsInRequest(context));
     401             :         if (_jsSelf == NULL)  return NO;
     402             :         
     403             :         JSObject                                *root = NULL;
     404             :         BOOL                                    OK = NO;
     405             :         jsval                                   method;
     406             :         jsval                                   ignoredResult = JSVAL_VOID;
     407             :         
     408             :         if (outResult == NULL)  outResult = &ignoredResult;
     409             :         OOJSAddGCObjectRoot(context, &root, "OOJSScript method root");
     410             :         
     411             :         if (EXPECT(JS_GetMethodById(context, _jsSelf, methodID, &root, &method) && !JSVAL_IS_VOID(method)))
     412             :         {
     413             : #ifndef NDEBUG
     414             :                 if (JS_IsExceptionPending(context))
     415             :                 {
     416             :                         OOLog(@"script.internalBug", @"Exception pending on context before calling method in %s, clearing. This is an internal error, please report it.", __PRETTY_FUNCTION__);
     417             :                         JS_ClearPendingException(context);
     418             :                 }
     419             :                 
     420             :                 OOLog(@"script.javaScript.call", @"Calling [%@].%@()", [self name], OOStringFromJSID(methodID));
     421             :                 OOLogIndentIf(@"script.javaScript.call");
     422             : #endif
     423             :                 
     424             :                 // Push self on stack of running scripts.
     425             :                 RunningStack stackElement =
     426             :                 {
     427             :                         .back = sRunningStack,
     428             :                         .current = self
     429             :                 };
     430             :                 sRunningStack = &stackElement;
     431             :                 
     432             :                 // Call the method.
     433             :                 OOJSStartTimeLimiter();
     434             :                 OK = JS_CallFunctionValue(context, _jsSelf, method, argc, argv, outResult);
     435             :                 OOJSStopTimeLimiter();
     436             :                 
     437             :                 if (JS_IsExceptionPending(context))
     438             :                 {
     439             :                         JS_ReportPendingException(context);
     440             :                         OK = NO;
     441             :                 }
     442             :                 
     443             :                 // Pop running scripts stack
     444             :                 sRunningStack = stackElement.back;
     445             :                 
     446             : #ifndef NDEBUG
     447             :                 OOLogOutdentIf(@"script.javaScript.call");
     448             : #endif
     449             :         }
     450             :         
     451             :         JS_RemoveObjectRoot(context, &root);
     452             :         
     453             :         return OK;
     454             : }
     455             : 
     456             : 
     457             : - (id) propertyWithID:(jsid)propID inContext:(JSContext *)context
     458             : {
     459             :         NSParameterAssert(context != NULL && JS_IsInRequest(context));
     460             :         if (_jsSelf == NULL)  return nil;
     461             :         
     462             :         jsval jsValue = JSVAL_VOID;
     463             :         if (JS_GetPropertyById(context, _jsSelf, propID, &jsValue))
     464             :         {
     465             :                 return OOJSNativeObjectFromJSValue(context, jsValue);
     466             :         }
     467             :         return nil;
     468             : }
     469             : 
     470             : 
     471             : - (BOOL) setProperty:(id)value withID:(jsid)propID inContext:(JSContext *)context
     472             : {
     473             :         NSParameterAssert(context != NULL && JS_IsInRequest(context));
     474             :         if (_jsSelf == NULL)  return NO;
     475             :         
     476             :         jsval jsValue = OOJSValueFromNativeObject(context, value);
     477             :         return JS_SetPropertyById(context, _jsSelf, propID, &jsValue);
     478             : }
     479             : 
     480             : 
     481             : - (BOOL) defineProperty:(id)value withID:(jsid)propID inContext:(JSContext *)context
     482             : {
     483             :         NSParameterAssert(context != NULL && JS_IsInRequest(context));
     484             :         if (_jsSelf == NULL)  return NO;
     485             :         
     486             :         jsval jsValue = OOJSValueFromNativeObject(context, value);
     487             :         return JS_DefinePropertyById(context, _jsSelf, propID, jsValue, NULL, NULL, OOJS_PROP_READONLY);
     488             : }
     489             : 
     490             : 
     491             : - (id) propertyNamed:(NSString *)propName
     492             : {
     493             :         if (propName == nil)  return nil;
     494             :         if (_jsSelf == NULL)  return nil;
     495             :         
     496             :         JSContext *context = OOJSAcquireContext();
     497             :         id result = [self propertyWithID:OOJSIDFromString(propName) inContext:context];
     498             :         OOJSRelinquishContext(context);
     499             :         
     500             :         return result;
     501             : }
     502             : 
     503             : 
     504             : - (BOOL) setProperty:(id)value named:(NSString *)propName
     505             : {
     506             :         if (value == nil || propName == nil)  return NO;
     507             :         if (_jsSelf == NULL)  return NO;
     508             :         
     509             :         JSContext *context = OOJSAcquireContext();
     510             :         BOOL result = [self setProperty:value withID:OOJSIDFromString(propName) inContext:context];
     511             :         OOJSRelinquishContext(context);
     512             :         
     513             :         return result;
     514             : }
     515             : 
     516             : 
     517             : - (BOOL) defineProperty:(id)value named:(NSString *)propName
     518             : {
     519             :         if (value == nil || propName == nil)  return NO;
     520             :         if (_jsSelf == NULL)  return NO;
     521             :         
     522             :         JSContext *context = OOJSAcquireContext();
     523             :         BOOL result = [self defineProperty:value withID:OOJSIDFromString(propName) inContext:context];
     524             :         OOJSRelinquishContext(context);
     525             :         
     526             :         return result;
     527             : }
     528             : 
     529             : 
     530           0 : - (jsval)oo_jsValueInContext:(JSContext *)context
     531             : {
     532             :         if (_jsSelf == NULL)  return JSVAL_VOID;
     533             :         return OBJECT_TO_JSVAL(_jsSelf);
     534             : }
     535             : 
     536             : 
     537             : + (void)pushScript:(OOJSScript *)script
     538             : {
     539             :         RunningStack                    *element = NULL;
     540             :         
     541             :         element = malloc(sizeof *element);
     542             :         if (element == NULL)  exit(EXIT_FAILURE);
     543             :         
     544             :         element->back = sRunningStack;
     545             :         element->current = script;
     546             :         sRunningStack = element;
     547             : }
     548             : 
     549             : 
     550             : + (void)popScript:(OOJSScript *)script
     551             : {
     552             :         RunningStack                    *element = NULL;
     553             :         
     554             :         assert(sRunningStack->current == script);
     555             :         
     556             :         element = sRunningStack;
     557             :         sRunningStack = sRunningStack->back;
     558             :         free(element);
     559             : }
     560             : 
     561             : @end
     562             : 
     563             : 
     564             : @implementation OOJSScript (OOPrivate)
     565             : 
     566             : 
     567             : 
     568             : /*      Generate default name for script which doesn't set its name property when
     569             :         first run.
     570             :  
     571             :         The generated name is <name>.anon-script, where <name> is selected as
     572             :         follows:
     573             :         * If path is nil (futureproofing), use the address of the script object.
     574             :         * If the file's name is something other than script.*, use the file name.
     575             :         * If the containing directory is something other than Config, use the
     576             :         containing directory's name.
     577             :         * Otherwise, use the containing directory's parent (which will generally
     578             :                                                                                                                 be an OXP root directory).
     579             :         * If either of the two previous steps results in an empty string, fall
     580             :         back on the full path.
     581             : */
     582             : - (NSString *)scriptNameFromPath:(NSString *)path
     583             : {
     584             :         NSString                *lastComponent = nil;
     585             :         NSString                *truncatedPath = nil;
     586             :         NSString                *theName = nil;
     587             :         
     588             :         if (path == nil) theName = [NSString stringWithFormat:@"%p", self];
     589             :         else
     590             :         {
     591             :                 lastComponent = [path lastPathComponent];
     592             :                 if (![lastComponent hasPrefix:@"script."]) theName = lastComponent;
     593             :                 else
     594             :                 {
     595             :                         truncatedPath = [path stringByDeletingLastPathComponent];
     596             :                         if (NSOrderedSame == [[truncatedPath lastPathComponent] caseInsensitiveCompare:@"Config"])
     597             :                         {
     598             :                                 truncatedPath = [truncatedPath stringByDeletingLastPathComponent];
     599             :                         }
     600             :                         if (NSOrderedSame == [[truncatedPath pathExtension] caseInsensitiveCompare:@"oxp"])
     601             :                         {
     602             :                                 truncatedPath = [truncatedPath stringByDeletingPathExtension];
     603             :                         }
     604             :                         
     605             :                         lastComponent = [truncatedPath lastPathComponent];
     606             :                         theName = lastComponent;
     607             :                 }
     608             :         }
     609             :         
     610             :         if (0 == [theName length]) theName = path;
     611             :         
     612             :         return StrippedName([theName stringByAppendingString:@".anon-script"]);
     613             : }
     614             : 
     615             : 
     616             : - (NSDictionary *) defaultPropertiesFromPath:(NSString *)path
     617             : {
     618             :         // remove file name, remove OXP subfolder, add manifest.plist
     619             :         NSString *manifestPath = [[[path stringByDeletingLastPathComponent] stringByDeletingLastPathComponent] stringByAppendingPathComponent:@"manifest.plist"];
     620             :         NSDictionary *manifest = OODictionaryFromFile(manifestPath);
     621             :         NSMutableDictionary *properties = [NSMutableDictionary dictionaryWithCapacity:3];
     622             :         /* __oolite.tmp.* is allocated for OXPs without manifests. Its
     623             :          * values are meaningless and shouldn't be used here */
     624             :         if (manifest != nil && ![[manifest oo_stringForKey:kOOManifestIdentifier] hasPrefix:@"__oolite.tmp."])
     625             :         {
     626             :                 if ([manifest objectForKey:kOOManifestVersion] != nil)
     627             :                 {
     628             :                         [properties setObject:[manifest oo_stringForKey:kOOManifestVersion] forKey:@"version"];
     629             :                 }
     630             :                 if ([manifest objectForKey:kOOManifestIdentifier] != nil)
     631             :                 {
     632             :                         // used for system info
     633             :                         [properties setObject:[manifest oo_stringForKey:kOOManifestIdentifier] forKey:kLocalManifestProperty];
     634             :                 }
     635             :                 if ([manifest objectForKey:kOOManifestAuthor] != nil)
     636             :                 {
     637             :                         [properties setObject:[manifest oo_stringForKey:kOOManifestAuthor] forKey:@"author"];
     638             :                 }
     639             :                 if ([manifest objectForKey:kOOManifestLicense] != nil)
     640             :                 {
     641             :                         [properties setObject:[manifest oo_stringForKey:kOOManifestLicense] forKey:@"license"];
     642             :                 }
     643             :         }
     644             :         return properties;
     645             : }
     646             : 
     647             : @end
     648             : 
     649             : 
     650             : @implementation OOScript (JavaScriptEvents)
     651             : 
     652             : - (BOOL) callMethod:(jsid)methodID
     653             :                   inContext:(JSContext *)context
     654             :           withArguments:(jsval *)argv count:(intN)argc
     655             :                          result:(jsval *)outResult
     656             : {
     657             :         return NO;
     658             : }
     659             : 
     660             : @end
     661             : 
     662             : 
     663           0 : void InitOOJSScript(JSContext *context, JSObject *global)
     664             : {
     665             :         sScriptPrototype = JS_InitClass(context, global, NULL, &sScriptClass, OOJSUnconstructableConstruct, 0, NULL, sScriptMethods, NULL, NULL);
     666             :         OOJSRegisterObjectConverter(&sScriptClass, OOJSBasicPrivateObjectConverter);
     667             : }
     668             : 
     669             : 
     670           0 : static JSBool ScriptAddProperty(JSContext *context, JSObject *this, jsid propID, jsval *value)
     671             : {
     672             :         // Complain about attempts to set the property tickle.
     673             :         if (JSID_IS_STRING(propID))
     674             :         {
     675             :                 JSString *propName = JSID_TO_STRING(propID);
     676             :                 JSBool match;
     677             :                 if (JS_StringEqualsAscii(context, propName, "tickle", &match) && match)
     678             :                 {
     679             :                         OOJSScript *thisScript = OOJSNativeObjectOfClassFromJSObject(context, this, [OOJSScript class]);
     680             :                         OOJSReportWarning(context, @"Script %@ appears to use the tickle() event handler, which is no longer supported.", [thisScript name]);
     681             :                 }
     682             :         }
     683             :         
     684             :         return YES;
     685             : }
     686             : 
     687             : 
     688           0 : static void AddStackToArrayReversed(NSMutableArray *array, RunningStack *stack)
     689             : {
     690             :         if (stack != NULL)
     691             :         {
     692             :                 AddStackToArrayReversed(array, stack->back);
     693             :                 [array addObject:stack->current];
     694             :         }
     695             : }
     696             : 
     697             : 
     698           0 : static JSScript *LoadScriptWithName(JSContext *context, NSString *path, JSObject *object, JSObject **outScriptObject, NSString **outErrorMessage)
     699             : {
     700             : #if OO_CACHE_JS_SCRIPTS
     701             :         OOCacheManager                          *cache = nil;
     702             : #endif
     703             :         NSString                                        *fileContents = nil;
     704             :         NSData                                          *data = nil;
     705             :         JSScript                                        *script = NULL;
     706             :         
     707             :         NSCParameterAssert(outScriptObject != NULL && outErrorMessage != NULL);
     708             :         *outErrorMessage = nil;
     709             :         
     710             : #if OO_CACHE_JS_SCRIPTS
     711             :         // Look for cached compiled script
     712             :         cache = [OOCacheManager sharedCache];
     713             :         data = [cache objectForKey:path inCache:@"compiled JavaScript scripts"];
     714             :         if (data != nil)
     715             :         {
     716             :                 script = ScriptWithCompiledData(context, data);
     717             :         }
     718             : #endif
     719             :         
     720             :         if (script == NULL)
     721             :         {
     722             :                 fileContents = [NSString stringWithContentsOfUnicodeFile:path];
     723             : 
     724             :                 if (fileContents != nil) 
     725             :                 {
     726             : #ifndef NDEBUG
     727             :                 /* FIXME: this isn't strictly the right test, since strict
     728             :                  * mode can be enabled with this string within a function
     729             :                  * definition, but it seems unlikely anyone is actually doing
     730             :                  * that here. */
     731             :                 if ([fileContents rangeOfString:@"\"use strict\";"].location == NSNotFound && [fileContents rangeOfString:@"'use strict';"].location == NSNotFound)
     732             :                 {
     733             :                         OOStandardsDeprecated([NSString stringWithFormat:@"Script %@ does not \"use strict\";",path]);
     734             :                         if (OOEnforceStandards())
     735             :                         {
     736             :                                 // prepend it anyway
     737             :                                 // TODO: some time after 1.82, make this required
     738             :                                 fileContents = [@"\"use strict\";\n" stringByAppendingString:fileContents];
     739             :                         }
     740             :                 }
     741             : #endif
     742             :                         data = [fileContents utf16DataWithBOM:NO];
     743             :                 }
     744             :                 if (data == nil)  *outErrorMessage = @"could not load file";
     745             :                 else
     746             :                 {
     747             :                         script = JS_CompileUCScript(context, object, [data bytes], [data length] / sizeof(unichar), [path UTF8String], 1);
     748             :                         if (script != NULL)  *outScriptObject = JS_NewScriptObject(context, script);
     749             :                         else  *outErrorMessage = @"compilation failed";
     750             :                 }
     751             :                 
     752             : #if OO_CACHE_JS_SCRIPTS
     753             :                 if (script != NULL)
     754             :                 {
     755             :                         // Write compiled script to cache
     756             :                         data = CompiledScriptData(context, script);
     757             :                         [cache setObject:data forKey:path inCache:@"compiled JavaScript scripts"];
     758             :                 }
     759             : #endif
     760             :         }
     761             :         
     762             :         return script;
     763             : }
     764             : 
     765             : 
     766             : #if OO_CACHE_JS_SCRIPTS
     767           0 : static NSData *CompiledScriptData(JSContext *context, JSScript *script)
     768             : {
     769             :         JSXDRState                                      *xdr = NULL;
     770             :         NSData                                          *result = nil;
     771             :         uint32                                          length;
     772             :         void                                            *bytes = NULL;
     773             :         
     774             :         xdr = JS_XDRNewMem(context, JSXDR_ENCODE);
     775             :         if (xdr != NULL)
     776             :         {
     777             :                 if (JS_XDRScript(xdr, &script))
     778             :                 {
     779             :                         bytes = JS_XDRMemGetData(xdr, &length);
     780             :                         if (bytes != NULL)
     781             :                         {
     782             :                                 result = [NSData dataWithBytes:bytes length:length];
     783             :                         }
     784             :                 }
     785             :                 JS_XDRDestroy(xdr);
     786             :         }
     787             :         
     788             :         return result;
     789             : }
     790             : 
     791             : 
     792           0 : static JSScript *ScriptWithCompiledData(JSContext *context, NSData *data)
     793             : {
     794             :         JSXDRState                                      *xdr = NULL;
     795             :         JSScript                                        *result = NULL;
     796             :         
     797             :         if (data == nil)  return NULL;
     798             :         
     799             :         xdr = JS_XDRNewMem(context, JSXDR_DECODE);
     800             :         if (xdr != NULL)
     801             :         {
     802             :                 NSUInteger length = [data length];
     803             :                 if (EXPECT_NOT(length > UINT32_MAX))  return NULL;
     804             :                 
     805             :                 JS_XDRMemSetData(xdr, (void *)[data bytes], (uint32_t)length);
     806             :                 if (!JS_XDRScript(xdr, &result))  result = NULL;
     807             :                 
     808             :                 JS_XDRMemSetData(xdr, NULL, 0); // Don't let it be freed by XDRDestroy
     809             :                 JS_XDRDestroy(xdr);
     810             :         }
     811             :         
     812             :         return result;
     813             : }
     814             : #endif
     815             : 
     816             : 
     817           0 : static NSString *StrippedName(NSString *string)
     818             : {
     819             :         static NSCharacterSet *invalidSet = nil;
     820             :         if (invalidSet == nil)  invalidSet = [[NSCharacterSet characterSetWithCharactersInString:@"_ \t\n\r\v"] retain];
     821             :         
     822             :         return [string stringByTrimmingCharactersInSet:invalidSet];
     823             : }

Generated by: LCOV version 1.14