Cleaned up some invalid JS attributes. Fixed JS namespace pollution with call() and inspect(), and renamed call() to callObjC(). Mission variable names may no longer begin with underscores. Script names may no longer begin or end with underscores or whitespace. The missionVariables object may now be enumerated with for-in.

git-svn-id: http://svn.berlios.de/svnroot/repos/oolite-linux/trunk@3636 127b21dd-08f5-0310-b4b7-95ae10353056
This commit is contained in:
Jens Ayton 2010-06-27 23:52:37 +00:00
parent b775065f76
commit 3c88ad59f5
8 changed files with 124 additions and 26 deletions

View File

@ -491,11 +491,10 @@ function consoleMessage()
// Add inspect() method to all entities, to show inspector palette (Mac OS X only; no effect on other platforms).
Object.getPrototypeOf(Entity).inspect = function inspect()
Entity.inspect = function inspect()
{
debugConsole.inspectEntity(this);
}
// Add call() method to all entities (calls an Objective-C method directly), now only available with debug OXP to avoid abuse.
Object.getPrototypeOf(Entity).call = debugConsole.__callObjCMethod;
debugConsole.__setUpCallObjC(Object.prototype);

View File

@ -71,6 +71,7 @@ static JSBool ConsoleClearConsole(JSContext *context, JSObject *this, uintN argc
static JSBool ConsoleScriptStack(JSContext *context, JSObject *this, uintN argc, jsval *argv, jsval *outResult);
static JSBool ConsoleInspectEntity(JSContext *context, JSObject *this, uintN argc, jsval *argv, jsval *outResult);
static JSBool ConsoleCallObjCMethod(JSContext *context, JSObject *this, uintN argc, jsval *argv, jsval *outResult);
static JSBool ConsoleSetUpCallObjC(JSContext *context, JSObject *this, uintN argc, jsval *argv, jsval *outResult);
static JSBool ConsoleIsExecutableJavaScript(JSContext *context, JSObject *this, uintN argc, jsval *argv, jsval *outResult);
static JSBool ConsoleDisplayMessagesInClass(JSContext *context, JSObject *this, uintN argc, jsval *argv, jsval *outResult);
static JSBool ConsoleSetDisplayMessagesInClass(JSContext *context, JSObject *this, uintN argc, jsval *argv, jsval *outResult);
@ -93,7 +94,7 @@ static JSBool PerformProfiling(JSContext *context, NSString *nominalFunction, ui
static JSClass sConsoleClass =
{
"Console",
JSCLASS_HAS_PRIVATE | JSCLASS_IS_ANONYMOUS,
JSCLASS_HAS_PRIVATE,
JS_PropertyStub, // addProperty
JS_PropertyStub, // delProperty
@ -178,7 +179,7 @@ static JSFunctionSpec sConsoleMethods[] =
{ "clearConsole", ConsoleClearConsole, 0 },
{ "scriptStack", ConsoleScriptStack, 0 },
{ "inspectEntity", ConsoleInspectEntity, 1 },
{ "__callObjCMethod", ConsoleCallObjCMethod, 1 },
{ "__setUpCallObjC", ConsoleSetUpCallObjC, 1, JSPROP_READONLY },
{ "isExecutableJavaScript", ConsoleIsExecutableJavaScript, 2 },
{ "displayMessagesInClass", ConsoleDisplayMessagesInClass, 1 },
{ "setDisplayMessagesInClass", ConsoleSetDisplayMessagesInClass, 2 },
@ -667,7 +668,7 @@ static JSBool ConsoleInspectEntity(JSContext *context, JSObject *this, uintN arg
}
// function __callObjCMethod(selector : String [, ...]) : Object
// function callObjC(selector : String [, ...]) : Object
static JSBool ConsoleCallObjCMethod(JSContext *context, JSObject *this, uintN argc, jsval *argv, jsval *outResult)
{
OOJS_NATIVE_ENTER(context)
@ -691,6 +692,25 @@ static JSBool ConsoleCallObjCMethod(JSContext *context, JSObject *this, uintN ar
}
// function __setUpCallObjC(object) -- object is expected to be Object.prototye.
static JSBool ConsoleSetUpCallObjC(JSContext *context, JSObject *this, uintN argc, jsval *argv, jsval *outResult)
{
OOJS_NATIVE_ENTER(context)
if (EXPECT_NOT(!JSVAL_IS_OBJECT(argv[0])))
{
OOReportJSBadArguments(context, @"Console", @"__setUpCallObjC", argc, argv, nil, @"Object.prototype");
return NO;
}
JSObject *obj = JSVAL_TO_OBJECT(argv[0]);
JS_DefineFunction(context, obj, "callObjC", ConsoleCallObjCMethod, 1, JSPROP_PERMANENT | JSPROP_READONLY);
return YES;
OOJS_NATIVE_EXIT
}
// function isExecutableJavaScript(this : Object, string : String) : Boolean
static JSBool ConsoleIsExecutableJavaScript(JSContext *context, JSObject *this, uintN argc, jsval *argv, jsval *outResult)
{

View File

@ -291,6 +291,8 @@ static JSBool MissionRunScreen(JSContext *context, JSObject *this, uintN argc, j
return NO;
}
OOJSPauseTimeLimiter();
if (function != JSVAL_NULL)
{
sCallbackScript = [[[OOJSScript currentlyRunningScript] weakRefUnderlyingObject] retain];
@ -358,6 +360,8 @@ static JSBool MissionRunScreen(JSContext *context, JSObject *this, uintN argc, j
[player setMissionTitle:nil];
[player setMissionMusic:nil];
OOJSResumeTimeLimiter();
*outResult = JSVAL_TRUE;
return YES;

View File

@ -34,34 +34,47 @@ MA 02110-1301, USA.
static NSString *KeyForName(JSContext *context, jsval name)
{
return [@"mission_" stringByAppendingString:[NSString stringWithJavaScriptValue:name inContext:context]];
NSCParameterAssert(JSVAL_IS_STRING(name));
NSString *key = [NSString stringWithJavaScriptString:JSVAL_TO_STRING(name)];
if ([key hasPrefix:@"_"]) return nil;
return [@"mission_" stringByAppendingString:key];
}
static JSBool MissionVariablesDeleteProperty(JSContext *context, JSObject *this, jsval name, jsval *outValue);
static JSBool MissionVariablesGetProperty(JSContext *context, JSObject *this, jsval name, jsval *outValue);
static JSBool MissionVariablesSetProperty(JSContext *context, JSObject *this, jsval name, jsval *value);
static JSBool MissionVariablesEnumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op, jsval *statep, jsid *idp);
static JSClass sMissionVariablesClass =
static JSExtendedClass sMissionVariablesClass =
{
{
"MissionVariables",
JSCLASS_IS_ANONYMOUS,
JSCLASS_NEW_ENUMERATE | JSCLASS_IS_EXTENDED,
JS_PropertyStub,
MissionVariablesDeleteProperty,
MissionVariablesGetProperty,
MissionVariablesSetProperty,
JS_EnumerateStub,
(JSEnumerateOp)MissionVariablesEnumerate,
JS_ResolveStub,
JS_ConvertStub,
JS_FinalizeStub
},
NULL,
NULL,
NULL,
JSCLASS_NO_RESERVED_MEMBERS
};
void InitOOJSMissionVariables(JSContext *context, JSObject *global)
{
JS_DefineObject(context, global, "missionVariables", &sMissionVariablesClass, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
JS_DefineObject(context, global, "missionVariables", &sMissionVariablesClass.base, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
}
@ -91,6 +104,8 @@ static JSBool MissionVariablesGetProperty(JSContext *context, JSObject *this, js
if (JSVAL_IS_STRING(name))
{
NSString *key = KeyForName(context, name);
if (key == nil) return YES;
id value = [player missionVariableForKey:key];
*outValue = JSVAL_VOID;
@ -142,3 +157,52 @@ static JSBool MissionVariablesSetProperty(JSContext *context, JSObject *this, js
OOJS_NATIVE_EXIT
}
static JSBool MissionVariablesEnumerate(JSContext *context, JSObject *object, JSIterateOp enumOp, jsval *state, jsid *idp)
{
OOJS_NATIVE_ENTER(context)
NSEnumerator *mvarEnumerator = JSVAL_TO_PRIVATE(*state);
switch (enumOp)
{
case JSENUMERATE_INIT:
{
// -allKeys implicitly makes a copy, which is good since the enumerating code might mutate.
NSArray *mvars = [[[PlayerEntity sharedPlayer] missionVariables] allKeys];
mvarEnumerator = [[mvars objectEnumerator] retain];
*state = PRIVATE_TO_JSVAL(mvarEnumerator);
if (idp != NULL)
{
*idp = INT_TO_JSVAL([mvars count]);
}
return YES;
}
case JSENUMERATE_NEXT:
{
id next = [mvarEnumerator nextObject];
if (next != nil)
{
NSCAssert1([next hasPrefix:@"mission_"] || next == nil, @"Mission variable key without \"mission_\" prefix: %@.", next);
next = [next substringFromIndex:8];
jsval val = [next javaScriptValueInContext:context];
return JS_ValueToId(context, val, idp);
}
// else:
*state = JSVAL_NULL;
// Fall through.
}
case JSENUMERATE_DESTROY:
{
[mvarEnumerator release];
if (idp != NULL) return JS_ValueToId(context, JSVAL_VOID, idp);
return YES;
}
}
OOJS_NATIVE_EXIT
}

View File

@ -69,6 +69,8 @@ static NSData *CompiledScriptData(JSContext *context, JSScript *script);
static JSScript *ScriptWithCompiledData(JSContext *context, NSData *data);
#endif
static NSString *StrippedName(NSString *string);
static JSClass sScriptClass =
{
@ -202,7 +204,7 @@ static JSFunctionSpec sScriptMethods[] =
{
// Get display attributes from script
DESTROY(name);
name = [[[self propertyNamed:@"name"] description] copy];
name = [StrippedName([[self propertyNamed:@"name"] description]) copy];
if (name == nil)
{
name = [[self scriptNameFromPath:path] retain];
@ -582,7 +584,7 @@ static JSFunctionSpec sScriptMethods[] =
if (0 == [theName length]) theName = path;
return [theName stringByAppendingString:@".anon-script"];
return StrippedName([theName stringByAppendingString:@".anon-script"]);
}
@end
@ -708,3 +710,12 @@ static JSScript *ScriptWithCompiledData(JSContext *context, NSData *data)
return result;
}
#endif
static NSString *StrippedName(NSString *string)
{
static NSCharacterSet *invalidSet = nil;
if (invalidSet == nil) invalidSet = [[NSCharacterSet characterSetWithCharactersInString:@"_ \t\n\r\v"] retain];
return [string stringByTrimmingCharactersInSet:invalidSet];
}

View File

@ -50,7 +50,7 @@ static JSExtendedClass sSystemInfoClass =
{
{
"SystemInfo",
JSCLASS_IS_ANONYMOUS | JSCLASS_HAS_PRIVATE | JSCLASS_IS_EXTENDED,
JSCLASS_HAS_PRIVATE | JSCLASS_IS_EXTENDED,
JS_PropertyStub,
SystemInfoDeleteProperty,

View File

@ -38,7 +38,7 @@ static JSBool GetWorldScriptNames(JSContext *context, JSObject *this, jsval name
static JSClass sWorldScriptsClass =
{
"WorldScripts",
JSCLASS_IS_ANONYMOUS,
0,
JS_PropertyStub,
JS_PropertyStub,