Experimental support for caching of compiled JavaScript (can be disabled with -DOO_CACHE_JS_SCRIPTS=0). Implemented JS Vector.rotateBy(quaternionExpression).
git-svn-id: http://svn.berlios.de/svnroot/repos/oolite-linux/trunk@1181 127b21dd-08f5-0310-b4b7-95ae10353056
This commit is contained in:
parent
a677bc3744
commit
7d99e403a5
@ -97,6 +97,9 @@ NSString *QuaternionDescription(Quaternion quaternion); // @"(w + xi + yj + zk)"
|
||||
#endif
|
||||
|
||||
|
||||
Vector quaternion_rotate_vector(Quaternion q, Vector vector) CONST_FUNC;
|
||||
|
||||
|
||||
|
||||
/*** Only inline definitions beyond this point ***/
|
||||
|
||||
|
@ -375,3 +375,12 @@ NSString *QuaternionDescription(Quaternion quaternion)
|
||||
|
||||
return [NSString stringWithFormat:@"(%g %c %gi %c %gj %c %gk)", quaternion.w, xs, x, ys, y, zs, z];
|
||||
}
|
||||
|
||||
|
||||
Vector quaternion_rotate_vector(Quaternion q, Vector vector)
|
||||
{
|
||||
Vector u, v, w;
|
||||
|
||||
basis_vectors_from_quaternion(q, &u, &v, &w);
|
||||
return make_vector(dot_product(u, vector), dot_product(v, vector), dot_product(w, vector));
|
||||
}
|
||||
|
@ -41,7 +41,6 @@ static JSBool EntityGetProperty(JSContext *context, JSObject *this, jsval name,
|
||||
static JSBool EntitySetProperty(JSContext *context, JSObject *this, jsval name, jsval *value);
|
||||
|
||||
// Methods
|
||||
static JSBool EntityToString(JSContext *context, JSObject *this, uintN argc, jsval *argv, jsval *outResult);
|
||||
static JSBool EntitySetPosition(JSContext *context, JSObject *this, uintN argc, jsval *argv, jsval *outResult);
|
||||
static JSBool EntitySetOrientation(JSContext *context, JSObject *this, uintN argc, jsval *argv, jsval *outResult);
|
||||
static JSBool EntityValid(JSContext *context, JSObject *this, uintN argc, jsval *argv, jsval *outResult);
|
||||
@ -126,7 +125,7 @@ static JSPropertySpec sEntityProperties[] =
|
||||
static JSFunctionSpec sEntityMethods[] =
|
||||
{
|
||||
// JS name Function min args
|
||||
{ "toString", EntityToString, 0 },
|
||||
{ "toString", JSObjectWrapperToString, 0 },
|
||||
{ "setPosition", EntitySetPosition, 1 },
|
||||
{ "setOrientation", EntitySetOrientation, 1 },
|
||||
{ "valid", EntityValid, 0 },
|
||||
@ -374,22 +373,6 @@ static JSBool EntitySetProperty(JSContext *context, JSObject *this, jsval name,
|
||||
|
||||
// *** Methods ***
|
||||
|
||||
static JSBool EntityToString(JSContext *context, JSObject *this, uintN argc, jsval *argv, jsval *outResult)
|
||||
{
|
||||
Entity *thisEnt = nil;
|
||||
|
||||
if (JSEntityGetEntity(context, this, &thisEnt))
|
||||
{
|
||||
*outResult = [[thisEnt description] javaScriptValueInContext:context];
|
||||
}
|
||||
else
|
||||
{
|
||||
*outResult = STRING_TO_JSVAL(JS_InternString(context, "[stale Entity]"));
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
|
||||
static JSBool EntitySetPosition(JSContext *context, JSObject *this, uintN argc, jsval *argv, jsval *outResult)
|
||||
{
|
||||
Entity *thisEnt;
|
||||
|
@ -32,6 +32,7 @@ MA 02110-1301, USA.
|
||||
{
|
||||
JSContext *context;
|
||||
JSObject *object;
|
||||
JSObject *scriptObject;
|
||||
|
||||
NSString *name;
|
||||
NSString *description;
|
||||
|
@ -22,6 +22,11 @@ MA 02110-1301, USA.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef OO_CACHE_JS_SCRIPTS
|
||||
#define OO_CACHE_JS_SCRIPTS 1
|
||||
#endif
|
||||
|
||||
|
||||
#import "OOJSScript.h"
|
||||
#import "OOLogging.h"
|
||||
#import "OOConstToString.h"
|
||||
@ -30,6 +35,11 @@ MA 02110-1301, USA.
|
||||
#import "NSStringOOExtensions.h"
|
||||
#import "EntityOOJavaScriptExtensions.h"
|
||||
|
||||
#if OO_CACHE_JS_SCRIPTS
|
||||
#import <jsxdrapi.h>
|
||||
#import "OOCacheManager.h"
|
||||
#endif
|
||||
|
||||
|
||||
typedef struct RunningStack RunningStack;
|
||||
struct RunningStack
|
||||
@ -47,6 +57,13 @@ static void JSScriptFinalize(JSContext *context, JSObject *this);
|
||||
|
||||
static void AddStackToArrayReversed(NSMutableArray *array, RunningStack *stack);
|
||||
|
||||
static JSScript *LoadScriptWithName(JSContext *context, NSString *name, JSObject *object, NSString **outErrorMessage);
|
||||
|
||||
#if OO_CACHE_JS_SCRIPTS
|
||||
static NSData *CompiledScriptData(JSContext *context, JSScript *script);
|
||||
static JSScript *ScriptWithCompiledData(JSContext *context, NSData *data);
|
||||
#endif
|
||||
|
||||
|
||||
static JSClass sScriptClass =
|
||||
{
|
||||
@ -96,8 +113,6 @@ static JSFunctionSpec sScriptMethods[] =
|
||||
- (id)initWithPath:(NSString *)path properties:(NSDictionary *)properties context:(JSContext *)inContext
|
||||
{
|
||||
NSString *problem = nil; // Acts as error flag.
|
||||
NSString *fileContents = nil;
|
||||
NSData *data = nil;
|
||||
JSScript *script = NULL;
|
||||
jsval returnValue;
|
||||
NSEnumerator *keyEnum = nil;
|
||||
@ -112,7 +127,7 @@ static JSFunctionSpec sScriptMethods[] =
|
||||
{
|
||||
context = inContext;
|
||||
// Do we actually want parent to be the global object here?
|
||||
object = JS_NewObject(context, &sScriptClass, sScriptPrototype, JS_GetGlobalObject(context));
|
||||
object = JS_NewObject(context, &sScriptClass, sScriptPrototype, NULL /*JS_GetGlobalObject(context)*/);
|
||||
if (object == NULL) problem = @"allocation failure";
|
||||
}
|
||||
|
||||
@ -131,16 +146,15 @@ static JSFunctionSpec sScriptMethods[] =
|
||||
|
||||
if (!problem)
|
||||
{
|
||||
fileContents = [NSString stringWithContentsOfUnicodeFile:path];
|
||||
if (fileContents != nil) data = [fileContents utf16DataWithBOM:NO];
|
||||
if (data == nil) problem = @"could not load file";
|
||||
}
|
||||
|
||||
// Compile
|
||||
if (!problem)
|
||||
{
|
||||
script = JS_CompileUCScript(context, object, [data bytes], [data length] / sizeof(unichar), [path UTF8String], 1);
|
||||
if (script == NULL) problem = @"compilation failed";
|
||||
script = LoadScriptWithName(context, path, object, &problem);
|
||||
if (JS_GetScriptObject(script) == NULL)
|
||||
{
|
||||
/*scriptObject = JS_NewScriptObject(context, script);
|
||||
if (!JS_AddNamedRoot(context, &scriptObject, "Cached script reference object"))
|
||||
{
|
||||
problem = @"could not add JavaScript root object";
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
// Set properties.
|
||||
@ -202,6 +216,7 @@ static JSFunctionSpec sScriptMethods[] =
|
||||
[version release];
|
||||
[weakSelf weakRefDrop];
|
||||
JS_RemoveRoot(context, &object);
|
||||
// if (scriptObject != NULL) JS_RemoveRoot(context, &scriptObject);
|
||||
|
||||
[super dealloc];
|
||||
}
|
||||
@ -377,54 +392,6 @@ static JSFunctionSpec sScriptMethods[] =
|
||||
}
|
||||
|
||||
|
||||
/* Generate default name for script which doesn't set its name property when
|
||||
first run.
|
||||
|
||||
The generated name is <name>.anon-script, where <name> is selected as
|
||||
follows:
|
||||
* If path is nil (futureproofing), use the address of the script object.
|
||||
* If the file's name is something other than script.*, use the file name.
|
||||
* If the containing directory is something other than Config, use the
|
||||
containing directory's name.
|
||||
* Otherwise, use the containing directory's parent (which will generally
|
||||
be an OXP root directory).
|
||||
* If either of the two previous steps results in an empty string, fall
|
||||
back on the full path.
|
||||
*/
|
||||
- (NSString *)scriptNameFromPath:(NSString *)path
|
||||
{
|
||||
NSString *lastComponent = nil;
|
||||
NSString *truncatedPath = nil;
|
||||
NSString *theName = nil;
|
||||
|
||||
if (path == nil) theName = [NSString stringWithFormat:@"%p", self];
|
||||
else
|
||||
{
|
||||
lastComponent = [path lastPathComponent];
|
||||
if (![lastComponent hasPrefix:@"script."]) theName = lastComponent;
|
||||
else
|
||||
{
|
||||
truncatedPath = [path stringByDeletingLastPathComponent];
|
||||
if (NSOrderedSame == [[truncatedPath lastPathComponent] caseInsensitiveCompare:@"Config"])
|
||||
{
|
||||
truncatedPath = [truncatedPath stringByDeletingLastPathComponent];
|
||||
}
|
||||
if (NSOrderedSame == [[truncatedPath pathExtension] caseInsensitiveCompare:@"oxp"])
|
||||
{
|
||||
truncatedPath = [truncatedPath stringByDeletingPathExtension];
|
||||
}
|
||||
|
||||
lastComponent = [truncatedPath lastPathComponent];
|
||||
theName = lastComponent;
|
||||
}
|
||||
}
|
||||
|
||||
if (0 == [theName length]) theName = path;
|
||||
|
||||
return [theName stringByAppendingString:@".anon-script"];
|
||||
}
|
||||
|
||||
|
||||
- (jsval)javaScriptValueInContext:(JSContext *)context
|
||||
{
|
||||
return OBJECT_TO_JSVAL(object);
|
||||
@ -461,7 +428,61 @@ static JSFunctionSpec sScriptMethods[] =
|
||||
@end
|
||||
|
||||
|
||||
@implementation OOScript(OOJavaScriptConversion)
|
||||
@implementation OOJSScript (OOPrivate)
|
||||
|
||||
|
||||
|
||||
/* Generate default name for script which doesn't set its name property when
|
||||
first run.
|
||||
|
||||
The generated name is <name>.anon-script, where <name> is selected as
|
||||
follows:
|
||||
* If path is nil (futureproofing), use the address of the script object.
|
||||
* If the file's name is something other than script.*, use the file name.
|
||||
* If the containing directory is something other than Config, use the
|
||||
containing directory's name.
|
||||
* Otherwise, use the containing directory's parent (which will generally
|
||||
be an OXP root directory).
|
||||
* If either of the two previous steps results in an empty string, fall
|
||||
back on the full path.
|
||||
*/
|
||||
- (NSString *)scriptNameFromPath:(NSString *)path
|
||||
{
|
||||
NSString *lastComponent = nil;
|
||||
NSString *truncatedPath = nil;
|
||||
NSString *theName = nil;
|
||||
|
||||
if (path == nil) theName = [NSString stringWithFormat:@"%p", self];
|
||||
else
|
||||
{
|
||||
lastComponent = [path lastPathComponent];
|
||||
if (![lastComponent hasPrefix:@"script."]) theName = lastComponent;
|
||||
else
|
||||
{
|
||||
truncatedPath = [path stringByDeletingLastPathComponent];
|
||||
if (NSOrderedSame == [[truncatedPath lastPathComponent] caseInsensitiveCompare:@"Config"])
|
||||
{
|
||||
truncatedPath = [truncatedPath stringByDeletingLastPathComponent];
|
||||
}
|
||||
if (NSOrderedSame == [[truncatedPath pathExtension] caseInsensitiveCompare:@"oxp"])
|
||||
{
|
||||
truncatedPath = [truncatedPath stringByDeletingPathExtension];
|
||||
}
|
||||
|
||||
lastComponent = [truncatedPath lastPathComponent];
|
||||
theName = lastComponent;
|
||||
}
|
||||
}
|
||||
|
||||
if (0 == [theName length]) theName = path;
|
||||
|
||||
return [theName stringByAppendingString:@".anon-script"];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation OOScript (OOJavaScriptConversion)
|
||||
|
||||
- (jsval)javaScriptValueInContext:(JSContext *)context
|
||||
{
|
||||
@ -493,3 +514,98 @@ static void AddStackToArrayReversed(NSMutableArray *array, RunningStack *stack)
|
||||
[array addObject:stack->current];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static JSScript *LoadScriptWithName(JSContext *context, NSString *path, JSObject *object, NSString **outErrorMessage)
|
||||
{
|
||||
#if OO_CACHE_JS_SCRIPTS
|
||||
OOCacheManager *cache = nil;
|
||||
#endif
|
||||
NSString *fileContents = nil;
|
||||
NSData *data = nil;
|
||||
JSScript *script = NULL;
|
||||
|
||||
assert(outErrorMessage != NULL);
|
||||
*outErrorMessage = nil;
|
||||
|
||||
#if OO_CACHE_JS_SCRIPTS
|
||||
// Look for cached compiled script
|
||||
cache = [OOCacheManager sharedCache];
|
||||
data = [cache objectForKey:path inCache:@"compiled JavaScript scripts"];
|
||||
if (data != nil)
|
||||
{
|
||||
script = ScriptWithCompiledData(context, data);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (script == NULL)
|
||||
{
|
||||
fileContents = [NSString stringWithContentsOfUnicodeFile:path];
|
||||
if (fileContents != nil) data = [fileContents utf16DataWithBOM:NO];
|
||||
if (data == nil) *outErrorMessage = @"could not load file";
|
||||
else
|
||||
{
|
||||
script = JS_CompileUCScript(context, object, [data bytes], [data length] / sizeof(unichar), [path UTF8String], 1);
|
||||
if (script == NULL) *outErrorMessage = @"compilation failed";
|
||||
}
|
||||
|
||||
#if OO_CACHE_JS_SCRIPTS
|
||||
if (script != NULL)
|
||||
{
|
||||
// Write compiled script to cache
|
||||
data = CompiledScriptData(context, script);
|
||||
[cache setObject:data forKey:path inCache:@"compiled JavaScript scripts"];
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
return script;
|
||||
}
|
||||
|
||||
|
||||
#if OO_CACHE_JS_SCRIPTS
|
||||
static NSData *CompiledScriptData(JSContext *context, JSScript *script)
|
||||
{
|
||||
JSXDRState *xdr = NULL;
|
||||
NSData *result = nil;
|
||||
uint32 length;
|
||||
void *bytes = NULL;
|
||||
|
||||
xdr = JS_XDRNewMem(context, JSXDR_ENCODE);
|
||||
if (xdr != NULL)
|
||||
{
|
||||
if (JS_XDRScript(xdr, &script))
|
||||
{
|
||||
bytes = JS_XDRMemGetData(xdr, &length);
|
||||
if (bytes != NULL)
|
||||
{
|
||||
result = [NSData dataWithBytes:bytes length:length];
|
||||
}
|
||||
}
|
||||
JS_XDRDestroy(xdr);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
static JSScript *ScriptWithCompiledData(JSContext *context, NSData *data)
|
||||
{
|
||||
JSXDRState *xdr = NULL;
|
||||
JSScript *result = NULL;
|
||||
|
||||
if (data == nil) return NULL;
|
||||
|
||||
xdr = JS_XDRNewMem(context, JSXDR_DECODE);
|
||||
if (xdr != NULL)
|
||||
{
|
||||
JS_XDRMemSetData(xdr, (void *)[data bytes], [data length]);
|
||||
if (!JS_XDRScript(xdr, &result)) result = NULL;
|
||||
|
||||
JS_XDRMemSetData(xdr, NULL, 0); // Don't let it be freed by XDRDestroy
|
||||
JS_XDRDestroy(xdr);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
@ -105,6 +105,7 @@ enum
|
||||
kShip_maxSpeed, // maximum flight speed, double, read-only
|
||||
kShip_script, // script, Script, read-only
|
||||
kShip_isPirate, // is pirate, boolean, read-only
|
||||
kShip_isPlayer, // is player, boolean, read-only
|
||||
kShip_isPolice, // is police, boolean, read-only
|
||||
kShip_isThargoid, // is thargoid, boolean, read-only
|
||||
kShip_isTrader, // is trader, boolean, read-only
|
||||
@ -147,6 +148,7 @@ static JSPropertySpec sShipProperties[] =
|
||||
{ "maxSpeed", kShip_maxSpeed, JSPROP_PERMANENT | JSPROP_ENUMERATE | JSPROP_READONLY },
|
||||
{ "script", kShip_script, JSPROP_PERMANENT | JSPROP_ENUMERATE | JSPROP_READONLY },
|
||||
{ "isPirate", kShip_isPirate, JSPROP_PERMANENT | JSPROP_ENUMERATE | JSPROP_READONLY },
|
||||
{ "isPlayer", kShip_isPlayer, JSPROP_PERMANENT | JSPROP_ENUMERATE | JSPROP_READONLY },
|
||||
{ "isPolice", kShip_isPolice, JSPROP_PERMANENT | JSPROP_ENUMERATE | JSPROP_READONLY },
|
||||
{ "isThargoid", kShip_isThargoid, JSPROP_PERMANENT | JSPROP_ENUMERATE | JSPROP_READONLY },
|
||||
{ "isTrader", kShip_isTrader, JSPROP_PERMANENT | JSPROP_ENUMERATE | JSPROP_READONLY },
|
||||
@ -343,12 +345,16 @@ static JSBool ShipGetProperty(JSContext *context, JSObject *this, jsval name, js
|
||||
case kShip_script:
|
||||
result = [entity shipScript];
|
||||
if (result == nil) result = [NSNull null];
|
||||
break;
|
||||
break;
|
||||
|
||||
case kShip_isPirate:
|
||||
*outValue = BOOLToJSVal([entity isPirate]);
|
||||
break;
|
||||
|
||||
case kShip_isPlayer:
|
||||
*outValue = BOOLToJSVal([entity isPlayer]);
|
||||
break;
|
||||
|
||||
case kShip_isPolice:
|
||||
*outValue = BOOLToJSVal([entity isPolice]);
|
||||
break;
|
||||
|
@ -351,7 +351,7 @@ static JSBool SystemToString(JSContext *context, JSObject *this, uintN argc, jsv
|
||||
PlayerEntity *player = nil;
|
||||
|
||||
player = [PlayerEntity sharedPlayer];
|
||||
systemDesc = [NSString stringWithFormat:@"<System %u:%u \"%@\">", [player currentGalaxyID], [player currentSystemID], [[UNIVERSE currentSystemData] objectForKey:KEY_NAME]];
|
||||
systemDesc = [NSString stringWithFormat:@"[System %u:%u \"%@\"]", [player currentGalaxyID], [player currentSystemID], [[UNIVERSE currentSystemData] objectForKey:KEY_NAME]];
|
||||
*outResult = [systemDesc javaScriptValueInContext:context];
|
||||
return YES;
|
||||
}
|
||||
|
@ -60,6 +60,7 @@ static JSBool VectorDirection(JSContext *context, JSObject *this, uintN argc, js
|
||||
static JSBool VectorMagnitude(JSContext *context, JSObject *this, uintN argc, jsval *argv, jsval *outResult);
|
||||
static JSBool VectorSquaredMagnitude(JSContext *context, JSObject *this, uintN argc, jsval *argv, jsval *outResult);
|
||||
static JSBool VectorRotationTo(JSContext *context, JSObject *this, uintN argc, jsval *argv, jsval *outResult);
|
||||
static JSBool VectorRotateBy(JSContext *context, JSObject *this, uintN argc, jsval *argv, jsval *outResult);
|
||||
static JSBool VectorStaticInterpolate(JSContext *context, JSObject *this, uintN argc, jsval *argv, jsval *outResult);
|
||||
|
||||
|
||||
@ -121,7 +122,7 @@ static JSFunctionSpec sVectorMethods[] =
|
||||
{ "direction", VectorDirection, 0, },
|
||||
{ "magnitude", VectorMagnitude, 0, },
|
||||
{ "squaredMagnitude", VectorSquaredMagnitude, 0, },
|
||||
// { "rotateBy", VectorRotateBy, 1, }, // TODO: Vector rotateBy(quaternionExpression)
|
||||
{ "rotateBy", VectorRotateBy, 1, },
|
||||
{ "rotationTo", VectorRotationTo, 1, },
|
||||
{ 0 }
|
||||
};
|
||||
@ -614,6 +615,21 @@ static JSBool VectorRotationTo(JSContext *context, JSObject *this, uintN argc, j
|
||||
}
|
||||
|
||||
|
||||
// rotateBy(q : quaternionExpression) : Vector
|
||||
static JSBool VectorRotateBy(JSContext *context, JSObject *this, uintN argc, jsval *argv, jsval *outResult)
|
||||
{
|
||||
Vector thisv, result;
|
||||
Quaternion q;
|
||||
|
||||
if (!JSVectorGetVector(context, this, &thisv)) return NO;
|
||||
if (!QuaternionFromArgumentList(context, @"Vector", @"rotateBy", argc, argv, &q, NULL)) return YES;
|
||||
|
||||
result = quaternion_rotate_vector(q, thisv);
|
||||
|
||||
return VectorToJSValue(context, result, outResult);
|
||||
}
|
||||
|
||||
|
||||
static JSBool VectorStaticInterpolate(JSContext *context, JSObject *this, uintN argc, jsval *argv, jsval *outResult)
|
||||
{
|
||||
Vector av, bv;
|
||||
|
@ -660,6 +660,8 @@ static BOOL JSNewNSDictionaryValue(JSContext *context, NSDictionary *dictionary,
|
||||
JSString *string = NULL;
|
||||
|
||||
length = [self length];
|
||||
if (length == 0) return JS_GetEmptyStringValue(context);
|
||||
|
||||
buffer = malloc(length * sizeof *buffer);
|
||||
if (buffer == NULL) return JSVAL_VOID;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user