81#define OOJSENGINE_JSVERSION JSVERSION_ECMA_5
85#define JIT_OPTIONS JSOPTION_JIT | JSOPTION_METHODJIT | JSOPTION_PROFILING
87#define OOJSENGINE_CONTEXT_OPTIONS JSOPTION_VAROBJFIX | JSOPTION_RELIMIT | JSOPTION_ANONFUNFIX | JIT_OPTIONS
90#define OOJS_STACK_SIZE 8192
91#define OOJS_RUNTIME_SIZE_MiB 256
104#if OOJSENGINE_MONITOR_SUPPORT
106@interface OOJavaScriptEngine (OOMonitorSupportInternal)
108- (void)sendMonitorError:(JSErrorReport *)errorReport
109 withMessage:(NSString *)message
110 inContext:(JSContext *)context;
112- (void)sendMonitorLogMessage:(NSString *)message
113 withMessageClass:(NSString *)messageClass
114 inContext:(JSContext *)context;
121@interface OOJavaScriptEngine (Private)
123- (BOOL) lookUpStandardClassPointers;
124- (void) registerStandardObjectConverters;
126- (void) createMainThreadContext;
127- (void) destroyMainThreadContext;
132static void ReportJSError(JSContext *context,
const char *message, JSErrorReport *report);
144static void ReportJSError(JSContext *context,
const char *message, JSErrorReport *report)
146 NSString *severity =
@"error";
147 NSString *messageText =
nil;
148 NSString *lineBuf =
nil;
149 NSString *messageClass =
nil;
150 NSString *highlight =
@"*****";
151 NSString *activeScript =
nil;
158 jschar empty[1] = { 0 };
159 JSErrorReport blankReport =
161 .filename =
"<unspecified file>",
167 if (
EXPECT_NOT(report == NULL)) report = &blankReport;
168 if (
EXPECT_NOT(message == NULL || *message ==
'\0')) message =
"<unspecified error>";
171 if (report->flags & JSREPORT_EXCEPTION) severity =
@"exception";
172 else if (report->flags & JSREPORT_WARNING)
174 severity =
@"warning";
175 highlight =
@"-----";
179 messageText = [NSString stringWithUTF8String:message];
182 lineBuf = [NSString stringWithUTF16String:report->uclinebuf];
183 while ([lineBuf hasSuffix:
@"\n"] || [lineBuf hasSuffix:@"\r"]) lineBuf = [lineBuf substringToIndex:[lineBuf length] - 1];
187 NSString *errorNumberStr = [NSString stringWithFormat:@"%u", report->errorNumber];
188 NSString *errorName = [errorNames oo_stringForKey:errorNumberStr];
189 if (errorName ==
nil) errorName = errorNumberStr;
192 messageClass = [NSString stringWithFormat:@"script.javaScript.%@.%@", severity, errorName];
200 activeScript = [[thisScript weakRefUnderlyingObject] displayName];
201 [thisScript release];
203 if (activeScript ==
nil) activeScript =
@"<unidentified script>";
204 OOLog(messageClass,
@"%@ JavaScript %@ (%@): %@", highlight, severity, activeScript, messageText);
209 if ([lineBuf length] != 0)
211 OOLog(messageClass,
@" %s, line %d: %@", report->filename, report->lineno, lineBuf);
215 OOLog(messageClass,
@" %s, line %d.", report->filename, report->lineno);
226#if OOJSENGINE_MONITOR_SUPPORT
227 JSExceptionState *exState = JS_SaveExceptionState(context);
229 withMessage:messageText
231 JS_RestoreExceptionState(context, exState);
253- (void) runMissionCallback
261 NSAssert(
sSharedEngine ==
nil,
@"Attempt to create multiple OOJavaScriptEngines.");
263 if (!(
self = [super init]))
return nil;
266 JS_SetCStringsAreUTF8();
268 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
275 [
self setDumpStackForErrors:[defaults boolForKey:@"dump-stack-for-errors"]];
276 [
self setDumpStackForWarnings:[defaults boolForKey:@"dump-stack-for-warnings"]];
279 assert(
sizeof(jschar) ==
sizeof(unichar));
282 uint32_t jsRuntimeInMiB = [defaults oo_intForKey:@"jsruntime-size-mib" defaultValue:OOJS_RUNTIME_SIZE_MiB];
283 _runtime = JS_NewRuntime(jsRuntimeInMiB * 1024L * 1024L);
286 if (_runtime == NULL)
288 OOLog(
@"script.javaScript.init.error",
@"***** FATAL ERROR: failed to create JavaScript runtime with size %uMiB.", jsRuntimeInMiB);
295 [
self createMainThreadContext];
301- (void) createMainThreadContext
303 NSAssert(
gOOJSMainThreadContext == NULL,
@"-[OOJavaScriptEngine createMainThreadContext] called while the main thread context exists.");
311 OOLog(
@"script.javaScript.init.error",
@"%@",
@"***** FATAL ERROR: failed to create JavaScript context.");
321 uint8_t gcZeal = [[NSUserDefaults standardUserDefaults] oo_unsignedCharForKey:@"js-gc-zeal"];
325 OOLog(
@"script.javaScript.debug.gcZeal",
@"Setting JavaScript garbage collector zeal to %u.", gcZeal);
337 if (![
self lookUpStandardClassPointers])
339 OOLog(
@"script.javaScript.init.error",
@"%@",
@"***** FATAL ERROR: failed to look up standard JavaScript classes.");
342 [
self registerStandardObjectConverters];
383 properties:[NSDictionary dictionaryWithObject:JSSpecialFunctionsObjectWrapper(gOOJSMainThreadContext)
388 OOLog(
@"script.javaScript.init.success",
@"%@",
@"Set up JavaScript context.");
392- (void) destroyMainThreadContext
399 _globalObject = NULL;
404 _booleanClass = NULL;
412 _globalObject = NULL;
427 static int counter = 3;
428 if (counter-- == 0) {
430 OOLog(
@"script.javascript.init.error",
@"%@",
@"JavaScript processes still pending. Can't reset JavaScript engine.");
435 OOLog(
@"script.javascript.init",
@"%@",
@"JavaScript reset successful.");
445 OOLog(
@"script.javascript.init.error",
@"%@",
@"JavaScript processes still pending. Can't reset JavaScript engine.");
450 OOLog(
@"script.javascript.init",
@"%@",
@"JavaScript reset successful.");
455 [[NSNotificationCenter defaultCenter] postNotificationName:kOOJavaScriptEngineWillResetNotification object:self];
458 [
self destroyMainThreadContext];
459 [
self createMainThreadContext];
462 [[NSNotificationCenter defaultCenter] postNotificationName:kOOJavaScriptEngineDidResetNotification object:self];
465 [
self garbageCollectionOpportunity:YES];
476 [
self destroyMainThreadContext];
477 JS_DestroyRuntime(_runtime);
483- (JSObject *) globalObject
485 return _globalObject;
489- (BOOL) callJSFunction:(jsval)function
490 forObject:(JSObject *)jsThis
493 result:(jsval *)outResult
495 JSContext *context = NULL;
503 result = JS_CallFunctionValue(context, jsThis,
function, argc, argv, outResult);
506 JS_ReportPendingException(context);
513- (void) removeGCObjectRoot:(JSObject **)rootPtr
516 JS_RemoveObjectRoot(context, rootPtr);
521- (void) removeGCValueRoot:(jsval *)rootPtr
524 JS_RemoveValueRoot(context, rootPtr);
529- (void) garbageCollectionOpportunity:(BOOL)force
544- (BOOL) showErrorLocations
546 return _showErrorLocations;
550- (void) setShowErrorLocations:(BOOL)value
552 _showErrorLocations = !!value;
556- (JSClass *) objectClass
562- (JSClass *) stringClass
568- (JSClass *) arrayClass
574- (JSClass *) numberClass
580- (JSClass *) booleanClass
582 return _booleanClass;
586- (BOOL) lookUpStandardClassPointers
588 JSObject *templateObject = NULL;
591 if (
EXPECT_NOT(templateObject == NULL))
return NO;
598 if (
EXPECT_NOT(templateObject == NULL))
return NO;
611- (void) registerStandardObjectConverters
622static JSTrapStatus DebuggerHook(JSContext *context, JSScript *script, jsbytecode *pc, jsval *rval,
void *closure)
626 OOLog(
@"script.javaScript.debugger",
@"debugger invoked during %@:", [[
OOJSScript currentlyRunningScript] displayName]);
631 return JSTRAP_CONTINUE;
635- (BOOL) dumpStackForErrors
637 return _dumpStackForErrors;
641- (void) setDumpStackForErrors:(BOOL)value
643 _dumpStackForErrors = !!value;
647- (BOOL) dumpStackForWarnings
649 return _dumpStackForWarnings;
653- (void) setDumpStackForWarnings:(BOOL)value
655 _dumpStackForWarnings = !!value;
659- (void) enableDebuggerStatement
661 JS_SetDebuggerHandler(_runtime, DebuggerHook,
self);
668#if OOJSENGINE_MONITOR_SUPPORT
670@implementation OOJavaScriptEngine (OOMonitorSupport)
672- (void) setMonitor:(
id<OOJavaScriptEngineMonitor>)inMonitor
674 [_monitor autorelease];
675 _monitor = [inMonitor retain];
681@implementation OOJavaScriptEngine (OOMonitorSupportInternal)
683- (void) sendMonitorError:(JSErrorReport *)errorReport
684 withMessage:(NSString *)message
685 inContext:(JSContext *)theContext
687 if ([_monitor respondsToSelector:@selector(jsEngine:context:error:stackSkip:showingLocation:withMessage:)])
689 [_monitor jsEngine:self context:theContext error:errorReport stackSkip:sErrorHandlerStackSkip showingLocation:[
self showErrorLocations] withMessage:message];
694- (void) sendMonitorLogMessage:(NSString *)message
695 withMessageClass:(NSString *)messageClass
696 inContext:(JSContext *)theContext
698 if ([_monitor respondsToSelector:@selector(jsEngine:context:logMessage:ofClass:)])
700 [_monitor jsEngine:self context:theContext logMessage:message ofClass:messageClass];
718 kInterestingFlags = ~(JSPD_ENUMERATE | JSPD_PERMANENT | JSPD_VARIABLE | JSPD_ARGUMENT)
721 NSString *flagStr =
@"";
722 if ((prop->flags & kInterestingFlags) != 0)
724 NSMutableArray *flags = [NSMutableArray array];
725 if (prop->flags & JSPD_READONLY) [flags addObject:@"read-only"];
726 if (prop->flags & JSPD_ALIAS) [flags addObject:[NSString stringWithFormat:@"alias (%@)", OOJSDescribeValue(context, prop->alias, YES)]];
727 if (prop->flags & JSPD_EXCEPTION) [flags addObject:@"exception"];
728 if (prop->flags & JSPD_ERROR) [flags addObject:@"error"];
730 flagStr = [NSString stringWithFormat:@" [%@]", [flags componentsJoinedByString:@", "]];
733 OOLog(
@"script.javaScript.stackTrace",
@" %@: %@%@", name, value, flagStr);
739 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
743 JSStackFrame *frame = NULL;
747 while (JS_FrameIterator(context, &frame) != NULL)
749 JSScript *script = JS_GetFrameScript(context, frame);
750 NSString *desc =
nil;
751 JSPropertyDescArray properties = { 0 , NULL };
752 BOOL gotProperties = NO;
756 if (!JS_IsScriptFrame(context, frame))
770 JSObject *scope = JS_GetFrameScopeChain(context, frame);
772 if (scope != NULL) gotProperties = JS_GetPropertyDescArray(context, scope, &properties);
774 NSString *funcDesc =
nil;
775 JSFunction *
function = JS_GetFrameFunction(context, frame);
776 if (
function != NULL)
778 JSString *funcName = JS_GetFunctionId(
function);
779 if (funcName != NULL)
782 if (!JS_IsConstructorFrame(context, frame))
784 funcDesc = [funcDesc stringByAppendingString:@"()"];
788 funcDesc = [NSString stringWithFormat:@"new %@()", funcDesc];
794 funcDesc =
@"<anonymous function>";
799 funcDesc =
@"<not a function frame>";
802 desc = [NSString stringWithFormat:@"(%@) %@", location, funcDesc];
804 else if (JS_IsDebuggerFrame(context, frame))
806 desc =
@"<debugger frame>";
810 desc =
@"<Oolite native>";
813 OOLog(
@"script.javaScript.stackTrace",
@"%2u %@", idx - 1, desc);
818 if (JS_GetFrameThis(context, frame, &
this))
820 static BOOL haveThis = NO;
821 static jsval thisAtom;
824 thisAtom = STRING_TO_JSVAL(JS_InternString(context,
"this"));
827 JSPropertyDesc thisDesc = { .id = thisAtom, .value =
this };
833 for (i = 0; i < properties.length; i++)
835 JSPropertyDesc *prop = &properties.array[i];
836 if (prop->flags & JSPD_ARGUMENT)
DumpVariable(context, prop);
840 for (i = 0; i < properties.length; i++)
842 JSPropertyDesc *prop = &properties.array[i];
843 if (prop->flags & JSPD_VARIABLE)
DumpVariable(context, prop);
847 for (i = 0; i < properties.length; i++)
849 JSPropertyDesc *prop = &properties.array[i];
850 if (!(prop->flags & (JSPD_ARGUMENT | JSPD_VARIABLE)))
DumpVariable(context, prop);
853 JS_PutPropertyDescArray(context, &properties);
857 @catch (NSException *exception)
859 OOLog(
kOOLogException,
@"Exception during JavaScript stack trace: %@:%@", [exception name], [exception reason]);
872 NSCParameterAssert(context != NULL && stackFrame != NULL && name != NULL && line != NULL);
877 JSScript *script = JS_GetFrameScript(context, stackFrame);
880 *name = JS_GetScriptFilename(context, script);
883 jsbytecode *PC = JS_GetFramePC(context, stackFrame);
884 *line = JS_PCToLineNumber(context, script, PC);
887 else if (JS_IsDebuggerFrame(context, stackFrame))
889 *name =
"<debugger frame>";
896 NSCParameterAssert(context != NULL && stackFrame != NULL);
898 const char *fileName;
901 if (fileName == NULL)
return nil;
907 NSString *fileNameObj = [NSString stringWithUTF8String:fileName];
908 if (fileNameObj ==
nil) fileNameObj = [NSString stringWithCString:fileName encoding:NSISOLatin1StringEncoding];
909 if (fileNameObj ==
nil)
return nil;
911 NSString *shortFileName = [fileNameObj lastPathComponent];
912 if (![[shortFileName lowercaseString] isEqualToString:
@"script.js"]) fileNameObj = shortFileName;
914 return [NSString stringWithFormat:@"%@:%llu", fileNameObj, lineNo];
927 NSCParameterAssert(name != NULL && name[0] !=
'\0' && idCache != NULL);
931 JSString *
string = JS_InternString(context, name);
934 [NSException raise:NSGenericException format:@"Failed to initialize JS ID cache for \"%s\".", name];
937 *idCache = INTERNED_STRING_TO_JSID(
string);
949 enum { kStackBufSize = 1024 };
950 unichar stackBuf[kStackBufSize];
952 size_t length = [string length];
953 if (length < kStackBufSize)
959 buffer = malloc(
sizeof (unichar) * length);
960 if (
EXPECT_NOT(buffer == NULL))
return JSID_VOID;
962 [string getCharacters:buffer];
964 JSString *jsString = JS_InternUCStringN(context, buffer, length);
966 if (
EXPECT_NOT(buffer != stackBuf)) free(buffer);
970 if (
EXPECT(jsString != NULL))
return INTERNED_STRING_TO_JSID(jsString);
971 else return JSID_VOID;
980 NSString *result =
nil;
981 if (JS_IdToValue(context, propID, &value))
992static NSString *
CallerPrefix(NSString *scriptClass, NSString *
function)
994 if (
function ==
nil)
return @"";
995 if (scriptClass ==
nil)
return [function stringByAppendingString:@": "];
996 return [NSString stringWithFormat:@"%@.%@: ", scriptClass, function];
1004 va_start(args, format);
1013 NSString *msg =
nil;
1017 va_start(args, format);
1018 msg = [[NSString alloc] initWithFormat:format arguments:args];
1023 @catch (
id exception)
1033 NSString *msg =
nil;
1039 msg = [[NSString alloc] initWithFormat:format arguments:args];
1040 JS_ReportError(context,
"%s", [msg UTF8String]);
1042 @catch (
id exception)
1052 if (!JS_IsExceptionPending(context))
1054 if ([exception isKindOfClass:[NSException class]])
OOJSReportError(context,
@"Native exception: %@", [exception reason]);
1065 OOLog(
@"fatal.unreachable",
@"Supposedly unreachable statement reached in %s (%@:%u) -- terminating.",
function,
OOLogAbbreviatedFileName(file), line);
1076 va_start(args, format);
1085 NSString *msg =
nil;
1089 va_start(args, format);
1090 msg = [[NSString alloc] initWithFormat:format arguments:args];
1095 @catch (
id exception)
1105 NSString *msg =
nil;
1109 msg = [[NSString alloc] initWithFormat:format arguments:args];
1110 JS_ReportWarning(context,
"%s", [msg UTF8String]);
1112 @catch (
id exception)
1123 const char *className =
OOJSGetClass(context, thisObj)->name;
1125 OOJSReportError(context,
@"Invalid property identifier %@ for instance of %s.", propName, className);
1132 const char *className =
OOJSGetClass(context, thisObj)->name;
1135 OOJSReportError(context,
@"Cannot set property %@ of instance of %s to invalid value %@.", propName, className, valueDesc);
1139void OOJSReportBadArguments(JSContext *context, NSString *scriptClass, NSString *
function, uintN argc, jsval *argv, NSString *message, NSString *expectedArgsDescription)
1143 if (message ==
nil) message =
@"Invalid arguments";
1144 message = [NSString stringWithFormat:@"%@ %@", message, [NSString stringWithJavaScriptParameters:argv count:argc inContext:context]];
1145 if (expectedArgsDescription !=
nil) message = [NSString stringWithFormat:@"%@ -- expected %@", message, expectedArgsDescription];
1149 @catch (
id exception)
1162BOOL
OOJSArgumentListGetNumber(JSContext *context, NSString *scriptClass, NSString *
function, uintN argc, jsval *argv,
double *outNumber, uintN *outConsumed)
1171 @"Expected number, got", NULL);
1183 NSCParameterAssert(context != NULL && (argv != NULL || argc == 0) && outNumber != NULL);
1186 if (
EXPECT_NOT(!JS_ValueToNumber(context, argv[0], &value) || isnan(value)))
1188 if (outConsumed != NULL) *outConsumed = 0;
1194 if (outConsumed != NULL) *outConsumed = 1;
1205 JSObject *result = NULL;
1207 if (array ==
nil)
return NULL;
1211 NSUInteger fullCount = [array count];
1217 uint32_t i,
count = (int32_t)fullCount;
1219 result = JS_NewArrayObject(context, 0, NULL);
1222 for (i = 0; i !=
count; ++i)
1224 jsval value = [[array objectAtIndex:i] oo_jsValueInContext:context];
1225 BOOL OK = JS_SetElement(context, result, i, &value);
1240 return (JSObject *)result;
1250 JSObject *
object = NULL;
1253 if (value == NULL)
return NO;
1256 if (!JS_EnterLocalRootScope(context))
return NO;
1261 *value = JSVAL_VOID;
1266 *value = OBJECT_TO_JSVAL(
object);
1269 JS_LeaveLocalRootScopeWithResult(context, *value);
1284 JSObject *result = NULL;
1290 if (dictionary ==
nil)
return NULL;
1294 result = JS_NewObject(context, NULL, NULL, NULL);
1299 if ([key isKindOfClass:[NSString class]] && [key length] != 0)
1302 value = [[dictionary objectForKey:key] oo_jsValueInContext:context];
1304#if __GNUC__ > 4 || __GNUC_MINOR__ > 6
1305 value = [[dictionary objectForKey:key] oo_jsValueInContext:context];
1310 id tmp = [dictionary objectForKey:key];
1311 if ([tmp respondsToSelector:
@selector(weakRefUnderlyingObject)])
1313 tmp = [tmp weakRefUnderlyingObject];
1315 value = [tmp oo_jsValueInContext:context];
1318 if (!JSVAL_IS_VOID(value))
1324 else if ([key isKindOfClass:[NSNumber class]])
1326 index = [key intValue];
1329 value = [[dictionary objectForKey:key] oo_jsValueInContext:context];
1330 if (!JSVAL_IS_VOID(value))
1332 OK = JS_SetElement(context, (JSObject *)result, index, &value);
1342 @catch (
id exception)
1352 return (JSObject *)result;
1362 JSObject *
object = NULL;
1365 if (value == NULL)
return NO;
1368 if (!JS_EnterLocalRootScope(context))
return NO;
1373 *value = JSVAL_VOID;
1378 *value = OBJECT_TO_JSVAL(
object);
1381 JS_LeaveLocalRootScopeWithResult(context, *value);
1388@implementation NSObject (OOJavaScriptConversion)
1390- (jsval) oo_jsValueInContext:(JSContext *)context
1396- (NSString *) oo_jsClassName
1402- (NSString *) oo_jsDescription
1404 return [
self oo_jsDescriptionWithClassName:[
self oo_jsClassName]];
1408- (NSString *) oo_jsDescriptionWithClassName:(NSString *)className
1412 NSString *components =
nil;
1413 NSString *description =
nil;
1415 components = [
self descriptionComponents];
1416 if (className ==
nil) className = [[
self class] description];
1418 if (components !=
nil)
1420 description = [NSString stringWithFormat:@"[%@ %@]", className, components];
1424 description = [NSString stringWithFormat:@"[object %@]", className];
1433- (void) oo_clearJSSelf:(JSObject *)selfVal
1444 JSObject *result = NULL;
1445 if (JS_ValueToObject(context, value, &result))
return result;
1452+ (id) valueWithJSValue:(jsval)value inContext:(JSContext *)context
1456 return [[[
self alloc] initWithJSValue:value inContext:context] autorelease];
1462+ (id) valueWithJSObject:(JSObject *)object inContext:(JSContext *)context
1466 return [[[
self alloc] initWithJSObject:object inContext:context] autorelease];
1472- (id) initWithJSValue:(jsval)value inContext:(JSContext *)context
1476 self = [
super init];
1480 if (context == NULL)
1487 if (!JSVAL_IS_VOID(_val))
1489 JS_AddNamedValueRoot(context, &_val,
"OOJSValue");
1491 [[NSNotificationCenter defaultCenter] addObserver:self
1492 selector:@selector(deleteJSValue)
1493 name:kOOJavaScriptEngineWillResetNotification
1505- (id) initWithJSObject:(JSObject *)object inContext:(JSContext *)context
1507 return [
self initWithJSValue:OBJECT_TO_JSVAL(object) inContext:context];
1511- (void) deleteJSValue
1513 if (!JSVAL_IS_VOID(_val))
1516 JS_RemoveValueRoot(context, &_val);
1520 [[NSNotificationCenter defaultCenter] removeObserver:self
1521 name:kOOJavaScriptEngineWillResetNotification
1529 [
self deleteJSValue];
1534- (jsval) oo_jsValueInContext:(JSContext *)context
1544 NSCParameterAssert(
string != NULL && strCache != NULL && inited != NULL && !*inited);
1548 JSString *jsString = JS_InternString(context,
string);
1551 [NSException raise:NSGenericException format:@"Failed to initialize JavaScript string literal cache for \"%@\".", [[NSString stringWithUTF8String:string] escapedForJavaScriptLiteral]];
1554 *strCache = STRING_TO_JSVAL(jsString);
1568 const jschar *chars = JS_GetStringCharsAndLength(context,
string, &length);
1570 if (
EXPECT(chars != NULL))
1572 return [NSString stringWithCharacters:chars length:length];
1589 JSString *
string = JS_ValueToString(context, value);
1600 if (
EXPECT(!JSVAL_IS_NULL(value) && !JSVAL_IS_VOID(value)))
1612 if (JSID_IS_STRING(propID))
1616 else if (JSID_IS_INT(propID) && propertySpec != NULL)
1618 int tinyid = JSID_TO_INT(propID);
1620 while (propertySpec->name != NULL)
1622 if (propertySpec->tinyid == tinyid)
return [NSString stringWithUTF8String:propertySpec->name];
1628 if (!JS_IdToValue(context, propID, &value))
return @"unknown";
1633static NSString *
DescribeValue(JSContext *context, jsval value, BOOL abbreviateObjects, BOOL recursing)
1641 JSString *name = JS_GetFunctionId(JS_ValueToFunction(context, value));
1642 if (name != NULL)
return [NSString stringWithFormat:@"function %@", OOStringFromJSString(context, name)];
1643 else return @"function";
1646 NSString *result =
nil;
1647 JSClass *
class = NULL;
1650 if (JSVAL_IS_OBJECT(value) && !JSVAL_IS_NULL(value))
1656 if (
class == [jsEng stringClass])
1658 value = STRING_TO_JSVAL(JS_ValueToString(context, value));
1661 if (JSVAL_IS_STRING(value))
1663 enum { kMaxLength = 200 };
1665 JSString *
string = JSVAL_TO_STRING(value);
1667 const jschar *chars = JS_GetStringCharsAndLength(context,
string, &length);
1669 result = [NSString stringWithCharacters:chars length:MIN(length, (size_t)kMaxLength)];
1670 result = [NSString stringWithFormat:@"\"%@%@\"", [result escapedForJavaScriptLiteral], (length > kMaxLength) ? @"..." : @""];
1672 else if (
class == [jsEng arrayClass])
1676 JSObject *obj = JSVAL_TO_OBJECT(value);
1677 if (JS_GetArrayLength(context, obj, &
count))
1681 NSMutableString *arrayDesc = [NSMutableString stringWithString:@"["];
1682 jsuint i, effectiveCount =
MIN(
count, (jsuint)4);
1683 for (i = 0; i < effectiveCount; i++)
1686 NSString *itemDesc =
@"?";
1687 if (JS_GetElement(context, obj, i, &item))
1691 if (i != 0) [arrayDesc appendString:@", "];
1692 [arrayDesc appendString:itemDesc];
1694 if (effectiveCount !=
count)
1696 [arrayDesc appendFormat:@", ... <%u items total>]", count];
1700 [arrayDesc appendString:@"]"];
1707 result = [NSString stringWithFormat:@"[<%u items>]", count];
1721 if (abbreviateObjects &&
class == [jsEng objectClass] && [result isEqualToString:
@"[object Object]"])
1726 if (result ==
nil) result =
@"?";
1737 return DescribeValue(context, value, abbreviateObjects, NO);
1741@implementation NSString (OOJavaScriptExtensions)
1743+ (NSString *) stringWithJavaScriptParameters:(jsval *)params count:(uintN)count inContext:(JSContext *)context
1747 if (params == NULL &&
count != 0)
return nil;
1750 NSMutableString *result = [NSMutableString stringWithString:@"("];
1752 for (i = 0; i <
count; ++i)
1754 if (i != 0) [result appendString:
@", "];
1755 [result appendString:OOJSDescribeValue(context, params[i], NO)];
1758 [result appendString:@")"];
1765- (jsval) oo_jsValueInContext:(JSContext *)context
1769 size_t length = [
self length];
1770 unichar *buffer = NULL;
1771 JSString *
string = NULL;
1775 jsval result = JS_GetEmptyStringValue(context);
1780 buffer = malloc(length *
sizeof *buffer);
1781 if (buffer == NULL)
return JSVAL_VOID;
1783 [
self getCharacters:buffer];
1785 string = JS_NewUCStringCopyN(context, buffer, length);
1788 return STRING_TO_JSVAL(
string);
1795+ (NSString *) concatenationOfStringsFromJavaScriptValues:(jsval *)values count:(
size_t)count separator:(NSString *)separator inContext:(JSContext *)context
1800 NSMutableString *result =
nil;
1801 NSString *element =
nil;
1804 if (values == NULL)
return NULL;
1806 for (i = 0; i !=
count; ++i)
1809 if (result ==
nil) result = [[element mutableCopy] autorelease];
1812 if (separator !=
nil) [result appendString:separator];
1813 [result appendString:element];
1823- (NSString *)escapedForJavaScriptLiteral
1827 NSMutableString *result =
nil;
1828 NSUInteger i, length;
1830 NSAutoreleasePool *pool =
nil;
1832 length = [
self length];
1833 result = [NSMutableString stringWithCapacity:length];
1836 pool = [[NSAutoreleasePool alloc] init];
1837 for (i = 0; i != length; ++i)
1839 c = [
self characterAtIndex:i];
1843 [result appendString:@"\\\\"];
1847 [result appendString:@"\\b"];
1851 [result appendString:@"\\f"];
1855 [result appendString:@"\\n"];
1859 [result appendString:@"\\r"];
1863 [result appendString:@"\\t"];
1867 [result appendString:@"\\v"];
1871 [result appendString:@"\\\'"];
1875 [result appendString:@"\\\""];
1879 [result appendString:[NSString stringWithCharacters:&c length:1]];
1889- (NSString *) oo_jsClassName
1897@implementation NSArray (OOJavaScriptConversion)
1899- (jsval)oo_jsValueInContext:(JSContext *)context
1901 jsval value = JSVAL_VOID;
1908@implementation OONativeVector (OOJavaScriptConversion)
1910- (jsval)oo_jsValueInContext:(JSContext *)context
1912 jsval value = JSVAL_VOID;
1920@implementation NSDictionary (OOJavaScriptConversion)
1922- (jsval)oo_jsValueInContext:(JSContext *)context
1924 jsval value = JSVAL_VOID;
1932@implementation NSNumber (OOJavaScriptConversion)
1934- (jsval)oo_jsValueInContext:(JSContext *)context
1940 long long longLongValue;
1942 isFloat = [
self oo_isFloatingPointNumber];
1945 longLongValue = [
self longLongValue];
1946 if (longLongValue < (
long long)JSVAL_INT_MIN || (
long long)JSVAL_INT_MAX < longLongValue)
1955 if (!JS_NewNumberValue(context, [
self doubleValue], &result)) result = JSVAL_VOID;
1959 result = INT_TO_JSVAL((int32_t)longLongValue);
1968- (NSString *) oo_jsClassName
1976@implementation NSNull (OOJavaScriptConversion)
1978- (jsval)oo_jsValueInContext:(JSContext *)context
1990 JSFunction *
function = JS_ValueToFunction(context, JS_CALLEE(context, vp));
1993 OOJSReportError(context,
@"%@ cannot be used as a constructor.", name);
2004 id object = JS_GetPrivate(context,
this);
2007 [[object weakRefUnderlyingObject] oo_clearJSSelf:this];
2009 JS_SetPrivate(context,
this,
nil);
2021 NSString *description =
nil;
2022 JSClass *jsClass = NULL;
2027 description = [object oo_jsDescription];
2028 if (description ==
nil) description = [object description];
2030 if (description ==
nil)
2033 if (jsClass != NULL)
2035 description = [NSString stringWithFormat:@"[object %@]", [NSString stringWithUTF8String:jsClass->name]];
2038 if (description ==
nil) description =
@"[object]";
2052 jsval rval = JSVAL_VOID;
2055 NSCParameterAssert(entity !=
nil && param != NULL);
2056 NSCParameterAssert(param->context != NULL &&
JS_IsInRequest(param->context));
2065 BOOL success = JS_CallFunctionValue(param->context, param->jsThis, param->function, 1, args, &rval);
2071 if (!JS_ValueToBoolean(param->context, rval, &result)) result = NO;
2072 if (JS_IsExceptionPending(param->context))
2074 JS_ReportPendingException(param->context);
2075 param->errorFlag = YES;
2080 param->errorFlag = YES;
2103 if (![entity isVisibleToScripts])
return NO;
2104 if ([entity isShip])
2106 if ([entity isSubEntity])
return NO;
2107 if ([entity status] == STATUS_COCKPIT_DISPLAY)
return NO;
2110 else if ([entity isPlanet])
2135 return ([entity isVisibleToScripts] && [entity isShip] && [entity status] == STATUS_COCKPIT_DISPLAY && ![entity isSubEntity]);
2142 NSCParameterAssert(subclass != NULL && superclass != NULL);
2146 sRegisteredSubClasses = NSCreateMapTable(NSNonOwnedPointerMapKeyCallBacks, NSNonOwnedPointerMapValueCallBacks, 0);
2149 NSCAssert(NSMapGet(
sRegisteredSubClasses, subclass) == NULL,
@"A JS class cannot be registered as a subclass of multiple classes.");
2164 NSCParameterAssert(putativeSubclass != NULL && superclass != NULL);
2165 NSCAssert(
sRegisteredSubClasses != NULL,
@"OOJSIsSubclass() called before any subclasses registered (disallowed for hot path efficiency).");
2169 if (putativeSubclass == superclass)
return YES;
2173 while (putativeSubclass != NULL);
2181 Class requiredObjCClass,
const char *name,
2186 OOJS_PROFILE_ENTER_NAMED(name)
2187 NSCParameterAssert(requiredObjCClass != Nil);
2188 NSCParameterAssert(context != NULL &&
object != NULL && requiredJSClass != NULL && outObject != NULL);
2211 NSCAssert(actualClass->flags & JSCLASS_HAS_PRIVATE,
@"Native object accessor requires JS class with private storage.");
2214 *outObject = [(id)JS_GetPrivate(context, object) weakRefUnderlyingObject];
2218 if (
EXPECT_NOT(*outObject !=
nil && ![*outObject isKindOfClass:requiredObjCClass]))
2220 OOJSReportError(context,
@"Native method expected %@ from %s and got correct JS type but incorrect native object %@", requiredObjCClass, requiredJSClass->name, *outObject);
2235 JSObject *
object = NULL;
2236 if (
EXPECT_NOT(!JS_ValueToObject(context, value, &
object) ||
object == NULL))
2250 JSIdArray *ids = NULL;
2252 NSMutableDictionary *result =
nil;
2253 jsval value = JSVAL_VOID;
2257 ids = JS_Enumerate(context,
object);
2263 result = [NSMutableDictionary dictionaryWithCapacity:ids->length];
2264 for (i = 0; i != ids->length; ++i)
2266 jsid thisID = ids->vector[i];
2268 if (JSID_IS_STRING(thisID))
2272 else if (JSID_IS_INT(thisID))
2279 objKey = [NSNumber numberWithInt:JSID_TO_INT(thisID)];
2287 if (objKey !=
nil && !JS_LookupPropertyById(context,
object, thisID, &value)) value = JSVAL_VOID;
2289 if (objKey !=
nil && !JSVAL_IS_VOID(value))
2292 if (objValue !=
nil)
2294 [result setObject:objValue forKey:objKey];
2299 JS_DestroyIdArray(context, ids);
2310 JSObject *tableObject = NULL;
2313 NSMutableDictionary *result =
nil;
2314 jsval value = JSVAL_VOID;
2318 if (
EXPECT_NOT(JSVAL_IS_NULL(tableValue) || !JS_ValueToObject(context, tableValue, &tableObject)))
2323 ids = JS_Enumerate(context, tableObject);
2329 result = [NSMutableDictionary dictionaryWithCapacity:ids->length];
2330 for (i = 0; i != ids->length; ++i)
2332 jsid thisID = ids->vector[i];
2334 if (JSID_IS_STRING(thisID))
2344 if (objKey !=
nil && !JS_LookupPropertyById(context, tableObject, thisID, &value)) value = JSVAL_VOID;
2346 if (objKey !=
nil && !JSVAL_IS_VOID(value))
2350 if (objValue !=
nil)
2352 [result setObject:objValue forKey:objKey];
2357 JS_DestroyIdArray(context, ids);
2371 if (JSVAL_IS_NULL(value) || JSVAL_IS_VOID(value))
return nil;
2373 if (JSVAL_IS_INT(value))
2375 return [NSNumber numberWithInt:JSVAL_TO_INT(value)];
2377 if (JSVAL_IS_DOUBLE(value))
2379 return [NSNumber numberWithDouble:JSVAL_TO_DOUBLE(value)];
2381 if (JSVAL_IS_BOOLEAN(value))
2383 return [NSNumber numberWithBool:JSVAL_TO_BOOLEAN(value)];
2385 if (JSVAL_IS_STRING(value))
2389 if (JSVAL_IS_OBJECT(value))
2403 NSValue *wrappedClass =
nil;
2404 NSValue *wrappedConverter =
nil;
2406 JSClass *
class = NULL;
2408 if (tableObject == NULL)
return nil;
2411 wrappedClass = [NSValue valueWithPointer:class];
2412 if (wrappedClass !=
nil) wrappedConverter = [sObjectConverters objectForKey:wrappedClass];
2413 if (wrappedConverter !=
nil)
2415 converter = [wrappedConverter pointerValue];
2416 return converter(context, tableObject);
2427 if (![result isKindOfClass:requiredClass]) result =
nil;
2435 if (![result isKindOfClass:requiredClass]) result =
nil;
2448 result = JS_GetPrivate(context,
object);
2449 return [result weakRefUnderlyingObject];
2455 NSValue *wrappedClass =
nil;
2456 NSValue *wrappedConverter =
nil;
2458 if (theClass == NULL)
return;
2461 wrappedClass = [NSValue valueWithPointer:theClass];
2462 if (converter != NULL)
2464 wrappedConverter = [NSValue valueWithPointer:converter];
2465 [sObjectConverters setObject:wrappedConverter forKey:wrappedClass];
2469 [sObjectConverters removeObjectForKey:wrappedClass];
2484 jsval value = JSVAL_VOID;
2486 NSArray *result =
nil;
2489 if (!JS_IsArrayObject(context, array))
return nil;
2490 if (!JS_GetArrayLength(context, array, &
count))
return nil;
2492 if (
count == 0)
return [NSArray array];
2494 values = calloc(
count,
sizeof *values);
2495 if (values == NULL)
return nil;
2497 for (i = 0; i !=
count; ++i)
2500 if (!JS_GetElement(context, array, i, &value)) value = JSVAL_VOID;
2503 if (
object ==
nil)
object = [NSNull null];
2507 result = [NSArray arrayWithObjects:values count:count];
2522 if (JS_ValueToNumber(context, OBJECT_TO_JSVAL(
object), &value))
2524 return [NSNumber numberWithDouble:value];
2538 if (JS_ValueToNumber(context, OBJECT_TO_JSVAL(
object), &value))
2540 return [NSNumber numberWithBool:(value != 0)];
#define foreachkey(VAR, DICT)
void OOConstToJSStringDestroy(void)
void OOConstToJSStringInit(JSContext *context)
void InitOOJSClock(JSContext *context, JSObject *global)
void InitOOJSDock(JSContext *context, JSObject *global)
#define OOJS_PROFILE_EXIT
#define OOJS_PROFILE_EXIT_VOID
#define OOJS_NATIVE_ENTER(cx)
#define OOJS_PROFILE_ENTER
#define OOJS_PROFILE_EXIT_JSVAL
#define OOJSStopTimeLimiter()
#define OOJSStartTimeLimiter()
void OOJSTimeManagementInit(OOJavaScriptEngine *engine, JSRuntime *runtime)
void InitOOJSEntity(JSContext *context, JSObject *global)
void InitOOJSEquipmentInfo(JSContext *context, JSObject *global)
void InitOOJSExhaustPlume(JSContext *context, JSObject *global)
void InitOOJSFlasher(JSContext *context, JSObject *global)
void InitOOJSFont(JSContext *context, JSObject *global)
void InitOOJSFrameCallbacks(JSContext *context, JSObject *global)
void OOJSFrameCallbacksRemoveAll(void)
void CreateOOJSGlobal(JSContext *context, JSObject **outGlobal)
void SetUpOOJSGlobal(JSContext *context, JSObject *global)
void InitOOJSManifest(JSContext *context, JSObject *global)
void InitOOJSMissionVariables(JSContext *context, JSObject *global)
void MissionRunCallback(void)
void InitOOJSMission(JSContext *context, JSObject *global)
void InitOOJSOolite(JSContext *context, JSObject *global)
void InitOOJSPlanet(JSContext *context, JSObject *global)
void InitOOJSPlayerShip(JSContext *context, JSObject *global)
void InitOOJSPlayer(JSContext *context, JSObject *global)
void InitOOJSQuaternion(JSContext *context, JSObject *global)
void InitOOJSScript(JSContext *context, JSObject *global)
void InitOOJSShipGroup(JSContext *context, JSObject *global)
void InitOOJSShip(JSContext *context, JSObject *global)
void InitOOJSSoundSource(JSContext *context, JSObject *global)
void InitOOJSSound(JSContext *context, JSObject *global)
void InitOOJSSpecialFunctions(JSContext *context, JSObject *global)
void InitOOJSStation(JSContext *context, JSObject *global)
void InitOOJSSun(JSContext *context, JSObject *global)
void InitOOJSSystemInfo(JSContext *context, JSObject *global)
void InitOOJSSystem(JSContext *context, JSObject *global)
void InitOOJSTimer(JSContext *context, JSObject *global)
void InitOOJSVector(JSContext *context, JSObject *global)
BOOL VectorToJSValue(JSContext *context, Vector vector, jsval *outValue) NONNULL_FUNC
void InitOOJSVisualEffect(JSContext *context, JSObject *global)
void InitOOJSWaypoint(JSContext *context, JSObject *global)
void InitOOJSWorldScripts(JSContext *context, JSObject *global)
void InitOOJSWormhole(JSContext *context, JSObject *global)
void OOJSPauseTimeLimiter(void)
id OOJSNativeObjectFromJSValue(JSContext *context, jsval value)
jsid OOJSIDFromString(NSString *string)
BOOL JSEntityIsDemoShipPredicate(Entity *entity, void *parameter)
BOOL OOJSArgumentListGetNumberNoError(JSContext *context, uintN argc, jsval *argv, double *outNumber, uintN *outConsumed)
void OOJSReportWarning(JSContext *context, NSString *format,...)
BOOL OOJSArgumentListGetNumber(JSContext *context, NSString *scriptClass, NSString *function, uintN argc, jsval *argv, double *outNumber, uintN *outConsumed)
JSBool OOJSObjectWrapperToString(JSContext *context, uintN argc, jsval *vp)
BOOL OOJSObjectGetterImplPRIVATE(JSContext *context, JSObject *object, JSClass *requiredJSClass, Class requiredObjCClass, const char *name, id *outObject)
BOOL JSEntityIsJavaScriptSearchablePredicate(Entity *entity, void *parameter)
void OOJSReportWarningWithArguments(JSContext *context, NSString *format, va_list args)
NSString * OOStringFromJSPropertyIDAndSpec(JSContext *context, jsid propID, JSPropertySpec *propertySpec)
#define JS_IsInRequest(context)
NSString * OOStringFromJSID(jsid propID)
void OOJSRegisterObjectConverter(JSClass *theClass, OOJSClassConverterCallback converter)
void OOJSSetWarningOrErrorStackSkip(unsigned skip)
BOOL JSFunctionPredicate(Entity *entity, void *parameter)
OOINLINE jsval OOJSValueFromNativeObject(JSContext *context, id object)
id OOJSNativeObjectFromJSObject(JSContext *context, JSObject *object)
void OOJSObjectWrapperFinalize(JSContext *context, JSObject *this)
#define OOJS_RETURN_OBJECT(o)
void OOJSReportBadPropertySelector(JSContext *context, JSObject *thisObj, jsid propID, JSPropertySpec *propertySpec)
NSDictionary * OOJSDictionaryFromJSValue(JSContext *context, jsval value)
void OOJSReportWarningForCaller(JSContext *context, NSString *scriptClass, NSString *function, NSString *format,...)
void OOJSReportErrorForCaller(JSContext *context, NSString *scriptClass, NSString *function, NSString *format,...)
NSDictionary * OOJSDictionaryFromJSObject(JSContext *context, JSObject *object)
void OOJSMarkConsoleEvalLocation(JSContext *context, JSStackFrame *stackFrame)
id OOJSNativeObjectOfClassFromJSValue(JSContext *context, jsval value, Class requiredClass)
NSString * OOStringFromJSValue(JSContext *context, jsval value)
JSBool OOJSUnconstructableConstruct(JSContext *context, uintN argc, jsval *vp)
OOINLINE BOOL OOJSValueIsFunction(JSContext *context, jsval value)
void OOJSRegisterSubclass(JSClass *subclass, JSClass *superclass)
JSObject * OOJSObjectFromNativeObject(JSContext *context, id object)
OOINLINE JSContext * OOJSAcquireContext(void)
NSString * OOJSDescribeValue(JSContext *context, jsval value, BOOL abbreviateObjects)
void OOJSReportError(JSContext *context, NSString *format,...)
#define JS_BeginRequest(context)
NSString * OOStringFromJSValueEvenIfNull(JSContext *context, jsval value)
OOINLINE void OOJSRelinquishContext(JSContext *context)
id(* OOJSClassConverterCallback)(JSContext *context, JSObject *object)
void OOJSReportBadPropertyValue(JSContext *context, JSObject *thisObj, jsid propID, JSPropertySpec *propertySpec, jsval value)
id OOJSBasicPrivateObjectConverter(JSContext *context, JSObject *object)
void OOJSReportBadArguments(JSContext *context, NSString *scriptClass, NSString *function, uintN argc, jsval *argv, NSString *message, NSString *expectedArgsDescription)
#define JS_EndRequest(context)
NSDictionary * OOJSDictionaryFromStringTable(JSContext *context, jsval value)
void OOJSReportErrorWithArguments(JSContext *context, NSString *format, va_list args)
void OOJSResumeTimeLimiter(void)
BOOL JSEntityIsJavaScriptVisiblePredicate(Entity *entity, void *parameter)
NSString * OOJSDescribeLocation(JSContext *context, JSStackFrame *stackFrame)
BOOL OOJSIsSubclass(JSClass *putativeSubclass, JSClass *superclass)
void OOJSDumpStack(JSContext *context)
NSString * OOStringFromJSString(JSContext *context, JSString *string)
OOINLINE JSClass * OOJSGetClass(JSContext *cx, JSObject *obj) ALWAYS_INLINE_FUNC
void OOJSStrLiteralCachePRIVATE(const char *string, jsval *strCache, BOOL *inited)
id OOJSNativeObjectOfClassFromJSObject(JSContext *context, JSObject *object, Class requiredClass)
static id JSNumberConverter(JSContext *context, JSObject *object)
static NSUInteger sConsoleEvalLineNo
static NSString * CallerPrefix(NSString *scriptClass, NSString *function)
static const char * sConsoleScriptName
static id JSStringConverter(JSContext *context, JSObject *object)
static void DumpVariable(JSContext *context, JSPropertyDesc *prop)
NSString *const kOOJavaScriptEngineWillResetNotification
void OOJSReportWrappedException(JSContext *context, id exception)
NSString *const kOOJavaScriptEngineDidResetNotification
#define OOJSENGINE_JSVERSION
static OOJavaScriptEngine * sSharedEngine
static id JSArrayConverter(JSContext *context, JSObject *object)
static void ReportJSError(JSContext *context, const char *message, JSErrorReport *report)
#define OOJSENGINE_CONTEXT_OPTIONS
void OOJSUnreachable(const char *function, const char *file, unsigned line)
static NSMapTable * sRegisteredSubClasses
static NSString * DescribeValue(JSContext *context, jsval value, BOOL abbreviateObjects, BOOL recursing)
static void UnregisterSubclasses(void)
static id JSBooleanConverter(JSContext *context, JSObject *object)
static JSObject * JSArrayFromNSArray(JSContext *context, NSArray *array)
static JSObject * JSObjectFromNSDictionary(JSContext *context, NSDictionary *dictionary)
static void GetLocationNameAndLine(JSContext *context, JSStackFrame *stackFrame, const char **name, NSUInteger *line)
JSContext * gOOJSMainThreadContext
static NSMutableDictionary * sObjectConverters
static BOOL JSNewNSArrayValue(JSContext *context, NSArray *array, jsval *value)
static unsigned sErrorHandlerStackSkip
static BOOL JSNewNSDictionaryValue(JSContext *context, NSDictionary *dictionary, jsval *value)
static void UnregisterObjectConverters(void)
void OOJSInitJSIDCachePRIVATE(const char *name, jsid *idCache)
NSString *const kOOLogException
BOOL OOLogWillDisplayMessagesInClass(NSString *inMessageClass)
#define OOLog(class, format,...)
NSString * OOLogAbbreviatedFileName(const char *inName)
@ STELLAR_TYPE_ATMOSPHERE
@ STELLAR_TYPE_NORMAL_PLANET
BOOL isVisibleToScripts()
jsval oo_jsValueInContext:(JSContext *context)
OOJSScript * currentlyRunningScript()
BOOL dumpStackForErrors()
BOOL dumpStackForWarnings()
BOOL showErrorLocations()
OOJavaScriptEngine * sharedEngine()
id jsScriptFromFileNamed:properties:(NSString *fileName,[properties] NSDictionary *properties)
NSDictionary * dictionaryFromFilesNamed:inFolder:andMerge:(NSString *fileName,[inFolder] NSString *folderName,[andMerge] BOOL mergeFiles)