Simplifed OOJSScript so there's now One True method call interface.

git-svn-id: http://svn.berlios.de/svnroot/repos/oolite-linux/trunk@4150 127b21dd-08f5-0310-b4b7-95ae10353056
This commit is contained in:
Jens Ayton 2011-01-24 21:40:15 +00:00
parent c73749773e
commit 1e37e0961a
11 changed files with 78 additions and 202 deletions

View File

@ -201,9 +201,12 @@ static OODebugMonitor *sSingleton = nil;
- (oneway void)performJSConsoleCommand:(in NSString *)command
{
JSContext *context = OOJSAcquireContext();
jsval commandVal = OOJSValueFromNativeObject(context, command);
OOJSStartTimeLimiterWithTimeLimit(kOOJSLongTimeLimit);
[_script doEvent:OOJSID("consolePerformJSCommand") withArguments:[NSArray arrayWithObject:command]];
[_script callMethod:OOJSID("consolePerformJSCommand") inContext:context withArguments:&commandVal count:1 result:NULL];
OOJSStopTimeLimiter();
OOJSRelinquishContext(context);
}

View File

@ -808,7 +808,7 @@ typedef enum
// In general, script events should be sent through doScriptEvent:..., which
// will forward to the world scripts.
//- (void) doWorldScriptEvent:(OOJSPropID)message withArguments:(NSArray *)arguments timeLimit:(OOTimeDelta)limit;
- (BOOL) doWorldEventUntilMissionScreen:(NSString *)message;
- (BOOL) doWorldEventUntilMissionScreen:(OOJSPropID)message;
- (void) doWorldScriptEvent:(OOJSPropID)message inContext:(JSContext *)context withArguments:(jsval *)argv count:(uintN)argc timeLimit:(OOTimeDelta)limit;
- (BOOL)showInfoFlag;

View File

@ -4623,7 +4623,7 @@ static bool minShieldLevelPercentageInitialised = false;
[[OOJavaScriptEngine sharedEngine] garbageCollectionOpportunity];
// When a mission screen is started, any on-screen message is removed immediately.
[self doWorldEventUntilMissionScreen:@"missionScreenOpportunity"]; // also displays docking reports first.
[self doWorldEventUntilMissionScreen:OOJSID("missionScreenOpportunity")]; // also displays docking reports first.
}
@ -4642,7 +4642,7 @@ static bool minShieldLevelPercentageInitialised = false;
[self doMissionCallback];
}
// notify older scripts, but do not trigger missionScreenOpportunity.
[self doWorldEventUntilMissionScreen:@"missionScreenEnded"];
[self doWorldEventUntilMissionScreen:OOJSID("missionScreenEnded")];
}
if (station == [UNIVERSE station])
@ -8071,47 +8071,6 @@ static NSString *last_outfitting_key=nil;
}
#if 0
- (void) doScriptEvent:(OOJSPropID)message withArguments:(NSArray *)arguments
{
JSContext *context = OOJSAcquireContext();
uintN i, argc;
jsval *argv = NULL;
// Convert arguments to JS values and make them temporarily un-garbage-collectable.
argc = [arguments count];
if (argc != 0)
{
argv = malloc(sizeof *argv * argc);
if (argv != NULL)
{
for (i = 0; i != argc; ++i)
{
argv[i] = [[arguments objectAtIndex:i] oo_jsValueInContext:context];
OOJSAddGCValueRoot(context, &argv[i], "JSScript event parameter");
}
}
else argc = 0;
}
[super doScriptEvent:message inContext:context withArguments:argv count:argc];
[self doWorldScriptEvent:message inContext:context withArguments:argv count:argc timeLimit:0.0];
// Re-garbage-collectibalize the arguments and free the array.
if (argv != NULL)
{
for (i = 0; i != argc; ++i)
{
JS_RemoveValueRoot(context, &argv[i]);
}
free(argv);
}
OOJSRelinquishContext(context);
}
#endif
- (void) doScriptEvent:(OOJSPropID)message inContext:(JSContext *)context withArguments:(jsval *)argv count:(uintN)argc
{
[super doScriptEvent:message inContext:context withArguments:argv count:argc];
@ -8119,11 +8078,10 @@ static NSString *last_outfitting_key=nil;
}
- (BOOL) doWorldEventUntilMissionScreen:(NSString *)message
- (BOOL) doWorldEventUntilMissionScreen:(OOJSPropID)message
{
NSEnumerator *scriptEnum = [worldScripts objectEnumerator];
OOScript *theScript;
OOJSPropID messageID = OOJSPropIDFromString(message);
// Check for the pressence of report messages first.
if (gui_screen != GUI_SCREEN_MISSION && [dockingReport length] > 0 && [self isDocked] && ![dockedStation suppressArrivalReports])
@ -8132,12 +8090,13 @@ static NSString *last_outfitting_key=nil;
[[UNIVERSE message_gui] clear];
return YES;
}
// FIXME: does this work ok in all situations? Needs fixing if not.
JSContext *context = OOJSAcquireContext();
while ((theScript = [scriptEnum nextObject]) && gui_screen != GUI_SCREEN_MISSION && [self isDocked])
{
[theScript doEvent:messageID withArguments:nil];
[theScript callMethod:message inContext:context withArguments:NULL count:0 result:NULL];
}
OOJSRelinquishContext(context);
if (gui_screen == GUI_SCREEN_MISSION)
{
@ -8160,7 +8119,7 @@ static NSString *last_outfitting_key=nil;
for (scriptEnum = [worldScripts objectEnumerator]; (theScript = [scriptEnum nextObject]); )
{
OOJSStartTimeLimiterWithTimeLimit(limit);
[theScript doEvent:message inContext:context withArguments:argv count:argc];
[theScript callMethod:message inContext:context withArguments:argv count:argc result:NULL];
OOJSStopTimeLimiter();
}
}

View File

@ -653,7 +653,7 @@ static NSTimeInterval time_last_frame;
[self doMissionCallback];
}
// notify older scripts, but do not trigger missionScreenOpportunity.
[self doWorldEventUntilMissionScreen:@"missionScreenEnded"];
[self doWorldEventUntilMissionScreen:OOJSID("missionScreenEnded")];
}
}
else if (!paused)
@ -924,7 +924,12 @@ static NSTimeInterval time_last_frame;
{
// primedEquipment == [eqScripts count] means we don't want to activate any equipment.
if(primedEquipment < [eqScripts count])
[(OOScript *)[[eqScripts oo_arrayAtIndex:primedEquipment] objectAtIndex:1] doEvent:OOJSID("activated") withArguments:nil];
{
OOJSScript *eqScript = [[eqScripts oo_arrayAtIndex:primedEquipment] objectAtIndex:1];
JSContext *context = OOJSAcquireContext();
[eqScript callMethod:OOJSID("activated") inContext:context withArguments:NULL count:0 result:NULL];
OOJSRelinquishContext(context);
}
}
activate_equipment_pressed = YES;
}
@ -2022,7 +2027,7 @@ static NSTimeInterval time_last_frame;
{
[self setGuiToStatusScreen];
[self doScriptEvent:OOJSID("reportScreenEnded")]; // last report given. Screen is now free for missionscreens.
[self doWorldEventUntilMissionScreen:@"missionScreenOpportunity"];
[self doWorldEventUntilMissionScreen:OOJSID("missionScreenOpportunity")];
}
else
{
@ -3196,7 +3201,7 @@ static BOOL toggling_music;
[[OOMusicController sharedController] stopThemeMusic];
[[UNIVERSE gameView] supressKeysUntilKeyUp]; // to prevent a missionscreen on the first page from reacting on this keypress.
[self setGuiToStatusScreen];
[self doWorldEventUntilMissionScreen:@"missionScreenOpportunity"]; // trigger missionScreenOpportunity immediately after (re)start
[self doWorldEventUntilMissionScreen:OOJSID("missionScreenOpportunity")]; // trigger missionScreenOpportunity immediately after (re)start
}
if ([gameView isDown:gvArrowKeyLeft]) // '<--'
{
@ -3276,7 +3281,7 @@ static BOOL toggling_music;
if ([self status] != STATUS_DOCKED) // did we launch inside callback? / are we in flight?
{
[self doWorldEventUntilMissionScreen:@"missionScreenEnded"]; // no opportunity events.
[self doWorldEventUntilMissionScreen:OOJSID("missionScreenEnded")]; // no opportunity events.
}
else
{

View File

@ -2325,10 +2325,10 @@ static int scriptRandomSeed = -1; // ensure proper random function
- (void) endMissionScreenAndNoteOpportunity
{
// Older scripts might intercept missionScreenEnded first, and call secondary mission screens.
if(![self doWorldEventUntilMissionScreen:@"missionScreenEnded"])
if(![self doWorldEventUntilMissionScreen:OOJSID("missionScreenEnded")])
{
// if we're here, no mission screen is running. Opportunity! :)
[self doWorldEventUntilMissionScreen:@"missionScreenOpportunity"];
[self doWorldEventUntilMissionScreen:OOJSID("missionScreenOpportunity")];
}
}

View File

@ -530,7 +530,7 @@ static uint16_t PersonalityForCommanderDict(NSDictionary *dict);
[UNIVERSE setGalaxy_seed: galaxy_seed andReinit:YES]; // set overridden planet names on long range map
[[UNIVERSE gameView] supressKeysUntilKeyUp];
[self setGuiToStatusScreen];
if (loadedOK) [self doWorldEventUntilMissionScreen:@"missionScreenOpportunity"]; // trigger missionScreenOpportunity immediately after loading
if (loadedOK) [self doWorldEventUntilMissionScreen:OOJSID("missionScreenOpportunity")]; // trigger missionScreenOpportunity immediately after loading
return loadedOK;
}

View File

@ -9137,10 +9137,10 @@ Vector positionOffsetForShipInRotationToAlignment(ShipEntity* ship, Quaternion q
{
args[0] = INT_TO_JSVAL(i);
OOJSStartTimeLimiter();
OK = [script callMethodNamed:OOJSID("coordinatesForEscortPosition")
withArguments:args count:sizeof args / sizeof *args
inContext:context
gettingResult:&result];
OK = [script callMethod:OOJSID("coordinatesForEscortPosition")
inContext:context
withArguments:args count:sizeof args / sizeof *args
result:&result];
OOJSStopTimeLimiter();
if (OK) OK = JSValueToVector(context, result, &_escortPositions[i]);
@ -9900,7 +9900,6 @@ static BOOL AuthorityPredicate(Entity *entity, void *parameter)
// *** Script event dispatch.
// For ease of overriding, these all go through -doScriptEvent:inContext:withArguments:count:.
- (void) doScriptEvent:(OOJSPropID)message
{
JSContext *context = OOJSAcquireContext();
@ -9949,7 +9948,7 @@ static BOOL AuthorityPredicate(Entity *entity, void *parameter)
for (i = 0; i != argc; ++i)
{
argv[i] = [[arguments objectAtIndex:i] oo_jsValueInContext:context];
OOJSAddGCValueRoot(context, &argv[i], "JSScript event parameter");
OOJSAddGCValueRoot(context, &argv[i], "event parameter");
}
}
else argc = 0;
@ -9973,7 +9972,8 @@ static BOOL AuthorityPredicate(Entity *entity, void *parameter)
- (void) doScriptEvent:(OOJSPropID)message inContext:(JSContext *)context withArguments:(jsval *)argv count:(uintN)argc
{
[script doEvent:message inContext:context withArguments:argv count:argc];
// This method is a bottleneck so that PlayerEntity can override at one point.
[script callMethod:message inContext:context withArguments:argv count:argc result:NULL];
}

View File

@ -478,7 +478,9 @@ MA 02110-1301, USA.
- (void) doScriptEvent:(OOJSPropID)message
{
[_script doEvent:message withArguments:nil];
JSContext *context = OOJSAcquireContext();
[_script callMethod:message inContext:context withArguments:NULL count:0 result:NULL];
OOJSRelinquishContext(context);
}

View File

@ -55,13 +55,14 @@ MA 02110-1301, USA.
+ (void) pushScript:(OOJSScript *)script;
+ (void) popScript:(OOJSScript *)script;
/* Low-level interface to call a JavaScript method.
/* Call a method.
Requires a request on context.
outResult may be NULL.
*/
- (BOOL) callMethodNamed:(OOJSPropID)methodID
withArguments:(jsval *)argv count:(intN)argc
inContext:(JSContext *)context
gettingResult:(jsval *)outResult;
- (BOOL) callMethod:(OOJSPropID)methodID
inContext:(JSContext *)context
withArguments:(jsval *)argv count:(intN)argc
result:(jsval *)outResult;
- (id) propertyWithID:(OOJSPropID)propID inContext:(JSContext *)context;
// Set a property which can be modified or deleted by the script.
@ -78,9 +79,11 @@ MA 02110-1301, USA.
@interface OOScript (JavaScriptEvents)
// These only do anything for JS scripts, but can be safely called on plist scripts too.
- (BOOL) doEvent:(OOJSPropID)eventID withArguments:(NSArray *)arguments;
- (BOOL) doEvent:(OOJSPropID)eventID inContext:(JSContext *)context withArguments:(jsval *)argv count:(uintN)argc;
// For simplicity, calling methods on non-JS scripts works but does nothing.
- (BOOL) callMethod:(OOJSPropID)methodID
inContext:(JSContext *)context
withArguments:(jsval *)argv count:(intN)argc
result:(jsval *)outResult;
@end

View File

@ -36,6 +36,7 @@ MA 02110-1301, USA.
#import "Entity.h"
#import "NSStringOOExtensions.h"
#import "EntityOOJavaScriptExtensions.h"
#import "OOConstToJSString.h"
#if OO_CACHE_JS_SCRIPTS
#import <jsxdrapi.h>
@ -94,7 +95,6 @@ static JSFunctionSpec sScriptMethods[] =
@interface OOJSScript (OOPrivate)
- (NSString *)scriptNameFromPath:(NSString *)path;
- (BOOL) doEvent:(OOJSPropID)eventID withMethod:(jsval)method andArguments:(jsval *)argv count:(uintN)argc inContext:(JSContext *)context;
@end
@ -314,88 +314,33 @@ static JSFunctionSpec sScriptMethods[] =
- (void)runWithTarget:(Entity *)target
{
[self doEvent:OOJSID("tickle") withArguments:[NSArray arrayWithObject:[PLAYER status_string]]];
}
- (BOOL) doEvent:(OOJSPropID)eventID withArguments:(NSArray *)arguments
{
JSContext *context = OOJSAcquireContext();
uintN i, argc;
jsval *argv = NULL;
jsval function;
JSObject *fakeRoot;
BOOL OK = YES;
if (OOJSGetMethod(context, _jsSelf, eventID, &fakeRoot, &function) && !JSVAL_IS_VOID(function))
{
// Convert arguments to JS values and make them temporarily un-garbage-collectable.
argc = [arguments count];
if (argc != 0)
{
argv = malloc(sizeof *argv * argc);
if (argv != NULL)
{
for (i = 0; i != argc; ++i)
{
argv[i] = [[arguments objectAtIndex:i] oo_jsValueInContext:context];
OOJSAddGCValueRoot(context, &argv[i], "JSScript event parameter");
}
}
else argc = 0;
}
OK = [self doEvent:eventID withMethod:function andArguments:argv count:argc inContext:context];
// Re-garbage-collectibalize the arguments and free the array.
if (argv != NULL)
{
for (i = 0; i != argc; ++i)
{
JS_RemoveValueRoot(context, &argv[i]);
}
free(argv);
}
}
JSContext *context = OOJSAcquireContext();
jsval arg = OOJSValueFromEntityStatus(context, [PLAYER status]);
[self callMethod:OOJSID("tickle") inContext:context withArguments:&arg count:1 result:NULL];
OOJSRelinquishContext(context);
return OK;
}
- (BOOL) doEvent:(OOJSPropID)eventID inContext:(JSContext *)context withArguments:(jsval *)argv count:(uintN)argc
- (BOOL) callMethod:(OOJSPropID)methodID
inContext:(JSContext *)context
withArguments:(jsval *)argv count:(intN)argc
result:(jsval *)outResult
{
NSParameterAssert(context != NULL && JS_IsInRequest(context));
NSParameterAssert(name != NULL && (argv != NULL || argc == 0) && context != NULL && JS_IsInRequest(context));
jsval function;
JSObject *fakeRoot;
BOOL OK = YES;
JSObject *root = NULL;
BOOL OK = NO;
jsval method;
jsval ignoredResult = JSVAL_VOID;
if (OOJSGetMethod(context, _jsSelf, eventID, &fakeRoot, &function) && !JSVAL_IS_VOID(function))
{
OK = [self doEvent:eventID withMethod:function andArguments:argv count:argc inContext:context];
}
if (outResult == NULL) outResult = &ignoredResult;
OOJSAddGCObjectRoot(context, &root, "OOJSScript method root");
return OK;
}
- (BOOL) callMethodNamed:(OOJSPropID)methodID
withArguments:(jsval *)argv count:(intN)argc
inContext:(JSContext *)context
gettingResult:(jsval *)outResult
{
NSParameterAssert(name != NULL && (argv != NULL || argc == 0) && context != NULL && JS_IsInRequest(context) && outResult != NULL);
BOOL OK = NO;
JSObject *fakeRoot = NULL;
jsval method;
if (EXPECT(OOJSGetMethod(context, _jsSelf, methodID, &fakeRoot, &method) && !JSVAL_IS_VOID(method)))
if (EXPECT(OOJSGetMethod(context, _jsSelf, methodID, &root, &method) && !JSVAL_IS_VOID(method)))
{
#ifndef NDEBUG
OOLog(@"script.trace.javaScript.callback", @"Calling [%@].%@()", [self name], OOStringFromJSPropID(methodID));
OOLogIndentIf(@"script.trace.javaScript.callback");
OOLog(@"script.trace.javaScript", @"Calling [%@].%@()", [self name], OOStringFromJSPropID(methodID));
OOLogIndentIf(@"script.trace.javaScript");
#endif
// Push self on stack of running scripts.
@ -414,11 +359,17 @@ static JSFunctionSpec sScriptMethods[] =
// Pop running scripts stack
sRunningStack = stackElement.back;
#if !OO_NEW_JS
JS_ClearNewbornRoots(context);
#endif
#ifndef NDEBUG
OOLogOutdentIf(@"script.trace.javaScript.callback");
OOLogOutdentIf(@"script.trace.javaScript");
#endif
}
JS_RemoveObjectRoot(context, &root);
return OK;
}
@ -574,64 +525,17 @@ static JSFunctionSpec sScriptMethods[] =
return StrippedName([theName stringByAppendingString:@".anon-script"]);
}
- (BOOL) doEvent:(OOJSPropID)eventID withMethod:(jsval)method andArguments:(jsval *)argv count:(uintN)argc inContext:(JSContext *)context
{
BOOL OK = YES;
jsval value = JSVAL_VOID;
#ifndef NDEBUG
NSAssert1(OOJSValueIsFunction(context, method), @"Expected function, got %@.", OOStringFromJSValueEvenIfNull(context, method));
OOLog(@"script.trace.javaScript.event", @"Calling [%@].%@()", [self name], OOStringFromJSPropID(eventID));
OOLogIndentIf(@"script.trace.javaScript.event");
#endif
// Push self on stack of running scripts.
RunningStack stackElement =
{
.back = sRunningStack,
.current = self
};
sRunningStack = &stackElement;
// Call the method.
OOJSStartTimeLimiter();
OK = JS_CallFunctionValue(context, _jsSelf, method, argc, argv, &value);
OOJSStopTimeLimiter();
// Pop running scripts stack.
sRunningStack = stackElement.back;
#if !OO_NEW_JS
JS_ClearNewbornRoots(context);
#endif
#ifndef NDEBUG
OOLogOutdentIf(@"script.trace.javaScript.event");
#endif
return OK;
}
@end
@implementation OOScript (JavaScriptEvents)
- (BOOL) doEvent:(OOJSPropID)eventID withArguments:(NSArray *)arguments
- (BOOL) callMethod:(OOJSPropID)methodID
inContext:(JSContext *)context
withArguments:(jsval *)argv count:(intN)argc
result:(jsval *)outResult
{
return YES;
}
- (BOOL) doEvent:(OOJSPropID)eventID inContext:(JSContext *)context withArguments:(jsval *)argv count:(uintN)argc
{
return YES;
}
- (jsval)oo_jsValueInContext:(JSContext *)context
{
return JSVAL_NULL;
return NO;
}
@end

View File

@ -8452,7 +8452,7 @@ Entity *gOOJSPlayerIfStale = nil;
if(!showDemo)
{
[player setGuiToStatusScreen];
[player doWorldEventUntilMissionScreen:@"missionScreenOpportunity"];
[player doWorldEventUntilMissionScreen:OOJSID("missionScreenOpportunity")];
}
no_update = NO;