Cleaned up some JavaScript object coersion stuff, and fixed a crasher in callObjC(). Oddly enough, this is work on bug #17201.

git-svn-id: http://svn.berlios.de/svnroot/repos/oolite-linux/trunk@4123 127b21dd-08f5-0310-b4b7-95ae10353056
This commit is contained in:
Jens Ayton 2011-01-23 14:39:27 +00:00
parent 027dbea907
commit 76630d9f2b
4 changed files with 276 additions and 139 deletions

View File

@ -758,10 +758,10 @@ static JSBool ConsoleCallObjCMethod(OOJS_NATIVE_ARGS)
return NO;
}
OOJS_BEGIN_FULL_NATIVE(context)
OOJSPauseTimeLimiter();
result = JSVAL_VOID;
OK = OOJSCallObjCObjectMethod(context, object, [object oo_jsClassName], argc, OOJS_ARGV, &result);
OOJS_END_FULL_NATIVE
OOJSResumeTimeLimiter();
OOJS_SET_RVAL(result);
return OK;

View File

@ -271,7 +271,7 @@ static JSBool MissionSetInstructionsInternal(OOJS_NATIVE_ARGS, BOOL isKey)
}
OOINLINE NSString *GetParameterString(JSContext *context, JSObject *object, const char *key)
static NSString *GetParameterString(JSContext *context, JSObject *object, const char *key)
{
jsval value = JSVAL_NULL;
if (JS_GetProperty(context, object, key, &value))
@ -281,6 +281,31 @@ OOINLINE NSString *GetParameterString(JSContext *context, JSObject *object, cons
return nil;
}
/*
static NSDictionary *GetParameterImageDescriptor(JSContext *context, JSObject *object, const char *key)
{
jsval value = JSVAL_NULL;
if (JS_GetProperty(context, object, key, &value))
{
if (JSVAL_IS_OBJECT(value))
{
if (JSVAL_IS_NULL(value)) return nil;
if (OOJSGetClass(context, object) != [[OOJavaScriptEngine sharedEngine] stringClass])
{
return OOJSDictionaryFromJSObject(context, object);
}
}
NSString *stringValue = OOStringFromJSValue(context, value);
if (stringValue != nil)
{
return [NSDictionary dictionaryWithObject:stringValue forKey:@"name"];
}
}
return nil;
}
*/
// runScreen(params: dict, callBack:function) - if the callback function is null, emulate the old style runMissionScreen
static JSBool MissionRunScreen(OOJS_NATIVE_ARGS)

View File

@ -58,6 +58,13 @@ enum
uint8_t mainContextInUse;
JSObject *globalObject;
BOOL _showErrorLocations;
JSClass *_objectClass;
JSClass *_stringClass;
JSClass *_arrayClass;
JSClass *_numberClass;
JSClass *_booleanClass;
#ifndef NDEBUG
BOOL _dumpStackForErrors;
BOOL _dumpStackForWarnings;
@ -95,6 +102,12 @@ enum
- (BOOL) showErrorLocations;
- (void) setShowErrorLocations:(BOOL)value;
- (JSClass *) objectClass;
- (JSClass *) stringClass;
- (JSClass *) arrayClass;
- (JSClass *) numberClass;
- (JSClass *) booleanClass;
#ifndef NDEBUG
- (BOOL) dumpStackForErrors;
- (void) setDumpStackForErrors:(BOOL)value;
@ -353,7 +366,22 @@ OOINLINE BOOL OOJSValueIsArray(JSContext *context, jsval value)
}
/* OOJSDictionaryFromStringTable(context, value);
/* OOJSDictionaryFromJSValue(context, value)
OOJSDictionaryFromJSObject(context, object)
Converts a JavaScript value to a dictionary by calling
OOJSNativeObjectFromJSValue() on each of its values.
Only enumerable own (i.e., not inherited) properties with string keys are
included.
Requires a request on context.
*/
NSDictionary *OOJSDictionaryFromJSValue(JSContext *context, jsval value);
NSDictionary *OOJSDictionaryFromJSObject(JSContext *context, JSObject *object);
/* OOJSDictionaryFromStringTable(context, value)
Treat an arbitrary JavaScript object as a dictionary mapping strings to
strings, and convert to a corresponding NSDictionary. The values are

View File

@ -115,14 +115,20 @@ static unsigned sErrorHandlerStackSkip = 0;
#endif
@interface OOJavaScriptEngine (Private)
- (BOOL) lookUpStandardClassPointers;
- (void) registerStandardObjectConverters;
@end
static void ReportJSError(JSContext *context, const char *message, JSErrorReport *report);
static void RegisterStandardObjectConverters(JSContext *context);
static id JSArrayConverter(JSContext *context, JSObject *object);
static id JSGenericObjectConverter(JSContext *context, JSObject *object);
static id JSStringConverter(JSContext *context, JSObject *object);
static id JSNumberConverter(JSContext *context, JSObject *object);
static id JSBooleanConverter(JSContext *context, JSObject *object);
static void ReportJSError(JSContext *context, const char *message, JSErrorReport *report)
@ -320,7 +326,12 @@ static void ReportJSError(JSContext *context, const char *message, JSErrorReport
// Initialize the built-in JS objects and the global object.
JS_InitStandardClasses(mainContext, globalObject);
RegisterStandardObjectConverters(mainContext);
if (![self lookUpStandardClassPointers])
{
OOLog(@"script.javaScript.init.error", @"***** FATAL ERROR: failed to look up standard JavaScript classes.");
exit(1);
}
[self registerStandardObjectConverters];
SetUpOOJSGlobal(mainContext, globalObject);
OOConstToJSStringInit(mainContext);
@ -522,6 +533,71 @@ static void ReportJSError(JSContext *context, const char *message, JSErrorReport
}
- (JSClass *) objectClass
{
return _objectClass;
}
- (JSClass *) stringClass
{
return _stringClass;
}
- (JSClass *) arrayClass
{
return _arrayClass;
}
- (JSClass *) numberClass
{
return _numberClass;
}
- (JSClass *) booleanClass
{
return _booleanClass;
}
- (BOOL) lookUpStandardClassPointers
{
JSObject *templateObject = NULL;
templateObject = JS_NewObject(mainContext, NULL, NULL, NULL);
if (EXPECT_NOT(templateObject == NULL)) return NO;
_objectClass = OOJSGetClass(mainContext, templateObject);
if (EXPECT_NOT(!JS_ValueToObject(mainContext, JS_GetEmptyStringValue(mainContext), &templateObject))) return NO;
_stringClass = OOJSGetClass(mainContext, templateObject);
templateObject = JS_NewArrayObject(mainContext, 0, NULL);
if (EXPECT_NOT(templateObject == NULL)) return NO;
_arrayClass = OOJSGetClass(mainContext, templateObject);
if (EXPECT_NOT(!JS_ValueToObject(mainContext, INT_TO_JSVAL(0), &templateObject))) return NO;
_numberClass = OOJSGetClass(mainContext, templateObject);
if (EXPECT_NOT(!JS_ValueToObject(mainContext, JSVAL_FALSE, &templateObject))) return NO;
_booleanClass = OOJSGetClass(mainContext, templateObject);
return YES;
}
- (void) registerStandardObjectConverters
{
OOJSRegisterObjectConverter([self objectClass], OOJSDictionaryFromJSObject);
OOJSRegisterObjectConverter([self stringClass], JSStringConverter);
OOJSRegisterObjectConverter([self arrayClass], JSArrayConverter);
OOJSRegisterObjectConverter([self numberClass], JSNumberConverter);
OOJSRegisterObjectConverter([self booleanClass], JSBooleanConverter);
}
#ifndef NDEBUG
static JSTrapStatus DebuggerHook(JSContext *context, JSScript *script, jsbytecode *pc, jsval *rval, void *closure)
{
@ -2017,8 +2093,109 @@ BOOL OOJSObjectGetterImpl(JSContext *context, JSObject *object, JSClass *require
}
NSDictionary *OOJSDictionaryFromJSValue(JSContext *context, jsval value)
{
OOJS_PROFILE_ENTER
JSObject *object = NULL;
if (EXPECT_NOT(!JS_ValueToObject(context, value, &object) || object == NULL))
{
return nil;
}
return OOJSDictionaryFromJSObject(context, object);
OOJS_PROFILE_EXIT
}
NSDictionary *OOJSDictionaryFromJSObject(JSContext *context, JSObject *object)
{
OOJS_PROFILE_ENTER
JSObject *object = NULL;
JSIdArray *ids = NULL;
jsint i;
NSMutableDictionary *result = nil;
jsval value = JSVAL_VOID;
id objKey = nil;
id objValue = nil;
ids = JS_Enumerate(context, object);
if (EXPECT_NOT(ids == NULL))
{
return nil;
}
result = [NSMutableDictionary dictionaryWithCapacity:ids->length];
for (i = 0; i != ids->length; ++i)
{
jsid thisID = ids->vector[i];
#if OO_NEW_JS
if (JSID_IS_STRING(thisID))
{
objKey = OOStringFromJSString(context, JSID_TO_STRING(thisID));
}
else if (JSID_IS_INT(thisID))
{
objKey = [NSNumber numberWithInt:JSID_TO_INT(thisID)];
}
else
{
objKey = nil;
}
value = JSVAL_VOID;
if (objKey != nil && !JS_LookupPropertyById(context, object, thisID, &value)) value = JSVAL_VOID;
#else
jsval propKey = value = JSVAL_VOID;
objKey = nil;
if (JS_IdToValue(context, thisID, &propKey))
{
// Properties with string keys.
if (JSVAL_IS_STRING(propKey))
{
JSString *stringKey = JSVAL_TO_STRING(propKey);
if (JS_LookupProperty(context, object, JS_GetStringBytes(stringKey), &value))
{
objKey = OOStringFromJSString(context, stringKey);
}
}
// Properties with int keys.
else if (JSVAL_IS_INT(propKey))
{
jsint intKey = JSVAL_TO_INT(propKey);
if (JS_GetElement(context, object, intKey, &value))
{
objKey = [NSNumber numberWithInt:intKey];
}
}
}
#endif
if (objKey != nil && !JSVAL_IS_VOID(value))
{
objValue = OOJSNativeObjectFromJSValue(context, value);
if (objValue != nil)
{
[result setObject:objValue forKey:objKey];
}
}
}
JS_DestroyIdArray(context, ids);
return result;
OOJS_PROFILE_EXIT
}
NSDictionary *OOJSDictionaryFromStringTable(JSContext *context, jsval tableValue)
{
OOJS_PROFILE_ENTER
JSObject *tableObject = NULL;
JSIdArray *ids;
jsint i;
@ -2087,6 +2264,8 @@ NSDictionary *OOJSDictionaryFromStringTable(JSContext *context, jsval tableValue
JS_DestroyIdArray(context, ids);
return result;
OOJS_PROFILE_EXIT
}
@ -2095,6 +2274,8 @@ static NSMutableDictionary *sObjectConverters;
id OOJSNativeObjectFromJSValue(JSContext *context, jsval value)
{
OOJS_PROFILE_ENTER
if (JSVAL_IS_NULL(value) || JSVAL_IS_VOID(value)) return nil;
if (JSVAL_IS_INT(value))
@ -2118,11 +2299,15 @@ id OOJSNativeObjectFromJSValue(JSContext *context, jsval value)
return OOJSNativeObjectFromJSObject(context, JSVAL_TO_OBJECT(value));
}
return nil;
OOJS_PROFILE_EXIT
}
id OOJSNativeObjectFromJSObject(JSContext *context, JSObject *tableObject)
{
OOJS_PROFILE_ENTER
NSValue *wrappedClass = nil;
NSValue *wrappedConverter = nil;
OOJSClassConverterCallback converter = NULL;
@ -2139,6 +2324,8 @@ id OOJSNativeObjectFromJSObject(JSContext *context, JSObject *tableObject)
return converter(context, tableObject);
}
return nil;
OOJS_PROFILE_EXIT
}
@ -2150,15 +2337,15 @@ id OOJSNativeObjectOfClassFromJSValue(JSContext *context, jsval value, Class req
}
id OOJSNativeObjectOfClassFromJSObject(JSContext *context, JSObject *tableObject, Class requiredClass)
id OOJSNativeObjectOfClassFromJSObject(JSContext *context, JSObject *object, Class requiredClass)
{
id result = OOJSNativeObjectFromJSObject(context, tableObject);
id result = OOJSNativeObjectFromJSObject(context, object);
if (![result isKindOfClass:requiredClass]) result = nil;
return result;
}
id OOJSBasicPrivateObjectConverter(JSContext *context, JSObject *tableObject)
id OOJSBasicPrivateObjectConverter(JSContext *context, JSObject *object)
{
id result;
@ -2166,7 +2353,7 @@ id OOJSBasicPrivateObjectConverter(JSContext *context, JSObject *tableObject)
weakRefUnderlyingObject returns the object itself. For nil, of course,
it returns nil.
*/
result = JS_GetPrivate(context, tableObject);
result = JS_GetPrivate(context, object);
return [result weakRefUnderlyingObject];
}
@ -2192,43 +2379,12 @@ void OOJSRegisterObjectConverter(JSClass *theClass, OOJSClassConverterCallback c
}
static void RegisterStandardObjectConverters(JSContext *context)
{
JSObject *templateObject = NULL;
JSClass *class = NULL;
// Create an array in order to get array class.
templateObject = JS_NewArrayObject(context, 0, NULL);
class = OOJSGetClass(context, templateObject);
OOJSRegisterObjectConverter(class, JSArrayConverter);
// Likewise, create a blank object to get its class.
// This is not documented (not much is) but JS_NewObject falls back to Object if passed a NULL class.
templateObject = JS_NewObject(context, NULL, NULL, NULL);
class = OOJSGetClass(context, templateObject);
OOJSRegisterObjectConverter(class, JSGenericObjectConverter);
// String object wrappers.
if (JS_ValueToObject(context, JS_GetEmptyStringValue(context), &templateObject))
{
class = OOJSGetClass(context, templateObject);
OOJSRegisterObjectConverter(class, JSStringConverter);
}
// Number object wrappers.
if (JS_ValueToObject(context, INT_TO_JSVAL(0), &templateObject))
{
class = OOJSGetClass(context, templateObject);
OOJSRegisterObjectConverter(class, JSNumberConverter);
}
}
static id JSArrayConverter(JSContext *context, JSObject *array)
{
jsuint i, count;
id *values = NULL;
jsval value = JSVAL_VOID;
id tableObject = nil;
id object = nil;
NSArray *result = nil;
// Convert a JS array to an NSArray by calling OOJSNativeObjectFromJSValue() on all its elements.
@ -2245,9 +2401,9 @@ static id JSArrayConverter(JSContext *context, JSObject *array)
value = JSVAL_VOID;
if (!JS_GetElement(context, array, i, &value)) value = JSVAL_VOID;
tableObject = OOJSNativeObjectFromJSValue(context, value);
if (tableObject == nil) tableObject = [NSNull null];
values[i] = tableObject;
object = OOJSNativeObjectFromJSValue(context, value);
if (object == nil) object = [NSNull null];
values[i] = object;
}
result = [NSArray arrayWithObjects:values count:count];
@ -2256,106 +2412,34 @@ static id JSArrayConverter(JSContext *context, JSObject *array)
}
static id JSGenericObjectConverter(JSContext *context, JSObject *tableObject)
static id JSStringConverter(JSContext *context, JSObject *object)
{
JSIdArray *ids;
jsint i;
NSMutableDictionary *result = nil;
jsval value = JSVAL_VOID;
id objKey = nil;
id objValue = nil;
/* Convert a JS Object to an NSDictionary by calling
OOJSNativeObjectFromJSValue() on all its enumerable properties. This is desireable
because it allows objects declared with JavaScript property list
syntax to be converted to native property lists.
This won't convert all objects, since JS has no concept of a class
heirarchy. Also, note that prototype properties are not included.
*/
ids = JS_Enumerate(context, tableObject);
if (EXPECT_NOT(ids == NULL))
{
return nil;
}
result = [NSMutableDictionary dictionaryWithCapacity:ids->length];
for (i = 0; i != ids->length; ++i)
{
jsid thisID = ids->vector[i];
#if OO_NEW_JS
if (JSID_IS_STRING(thisID))
{
objKey = OOStringFromJSString(context, JSID_TO_STRING(thisID));
}
else if (JSID_IS_INT(thisID))
{
objKey = [NSNumber numberWithInt:JSID_TO_INT(thisID)];
}
else
{
objKey = nil;
}
value = JSVAL_VOID;
if (objKey != nil && !JS_LookupPropertyById(context, tableObject, thisID, &value)) value = JSVAL_VOID;
#else
jsval propKey = value = JSVAL_VOID;
objKey = nil;
if (JS_IdToValue(context, thisID, &propKey))
{
// Properties with string keys.
if (JSVAL_IS_STRING(propKey))
{
JSString *stringKey = JSVAL_TO_STRING(propKey);
if (JS_LookupProperty(context, tableObject, JS_GetStringBytes(stringKey), &value))
{
objKey = OOStringFromJSString(context, stringKey);
}
}
// Properties with int keys.
else if (JSVAL_IS_INT(propKey))
{
jsint intKey = JSVAL_TO_INT(propKey);
if (JS_GetElement(context, tableObject, intKey, &value))
{
objKey = [NSNumber numberWithInt:intKey];
}
}
}
#endif
if (objKey != nil && !JSVAL_IS_VOID(value))
{
objValue = OOJSNativeObjectFromJSValue(context, value);
if (objValue != nil)
{
[result setObject:objValue forKey:objKey];
}
}
}
JS_DestroyIdArray(context, ids);
return result;
return [NSString stringOrNilWithJavaScriptValue:OBJECT_TO_JSVAL(object) inContext:context];
}
static id JSStringConverter(JSContext *context, JSObject *tableObject)
{
return [NSString stringOrNilWithJavaScriptValue:OBJECT_TO_JSVAL(tableObject) inContext:context];
}
static id JSNumberConverter(JSContext *context, JSObject *tableObject)
static id JSNumberConverter(JSContext *context, JSObject *object)
{
jsdouble value;
if (JS_ValueToNumber(context, OBJECT_TO_JSVAL(tableObject), &value))
if (JS_ValueToNumber(context, OBJECT_TO_JSVAL(object), &value))
{
return [NSNumber numberWithDouble:value];
}
return nil;
}
static id JSBooleanConverter(JSContext *context, JSObject *object)
{
/* Fun With JavaScript: Boolean(false) is a truthy value, since it's a
non-null object. JS_ValueToBoolean() therefore reports true.
However, Boolean objects are transformed to numbers sanely, so this
works.
*/
jsdouble value;
if (JS_ValueToNumber(context, OBJECT_TO_JSVAL(object), &value))
{
return [NSNumber numberWithBool:(value != 0)];
}
return nil;
}