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 32
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);
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:@"%@:%lu", 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;
1286 NSEnumerator *keyEnum =
nil;
1291 if (dictionary ==
nil)
return NULL;
1295 result = JS_NewObject(context, NULL, NULL, NULL);
1298 for (keyEnum = [dictionary keyEnumerator]; (key = [keyEnum nextObject]); )
1300 if ([key isKindOfClass:[NSString
class]] && [key length] != 0)
1303 value = [[dictionary objectForKey:key] oo_jsValueInContext:context];
1305#if __GNUC__ > 4 || __GNUC_MINOR__ > 6
1306 value = [[dictionary objectForKey:key] oo_jsValueInContext:context];
1311 id tmp = [dictionary objectForKey:key];
1312 if ([tmp respondsToSelector:
@selector(weakRefUnderlyingObject)])
1314 tmp = [tmp weakRefUnderlyingObject];
1316 value = [tmp oo_jsValueInContext:context];
1319 if (!JSVAL_IS_VOID(value))
1325 else if ([key isKindOfClass:[NSNumber
class]])
1327 index = [key intValue];
1330 value = [[dictionary objectForKey:key] oo_jsValueInContext:context];
1331 if (!JSVAL_IS_VOID(value))
1333 OK = JS_SetElement(context, (JSObject *)result, index, &value);
1343 @catch (
id exception)
1353 return (JSObject *)result;
1363 JSObject *
object = NULL;
1366 if (value == NULL)
return NO;
1369 if (!JS_EnterLocalRootScope(context))
return NO;
1374 *value = JSVAL_VOID;
1379 *value = OBJECT_TO_JSVAL(
object);
1382 JS_LeaveLocalRootScopeWithResult(context, *value);
1389@implementation NSObject (OOJavaScriptConversion)
1391- (jsval) oo_jsValueInContext:(JSContext *)context
1397- (NSString *) oo_jsClassName
1403- (NSString *) oo_jsDescription
1405 return [
self oo_jsDescriptionWithClassName:[
self oo_jsClassName]];
1409- (NSString *) oo_jsDescriptionWithClassName:(NSString *)className
1413 NSString *components =
nil;
1414 NSString *description =
nil;
1416 components = [
self descriptionComponents];
1417 if (className ==
nil) className = [[
self class] description];
1419 if (components !=
nil)
1421 description = [NSString stringWithFormat:@"[%@ %@]", className, components];
1425 description = [NSString stringWithFormat:@"[object %@]", className];
1434- (void) oo_clearJSSelf:(JSObject *)selfVal
1445 JSObject *result = NULL;
1446 if (JS_ValueToObject(context, value, &result))
return result;
1453+ (id) valueWithJSValue:(jsval)value inContext:(JSContext *)context
1457 return [[[
self alloc] initWithJSValue:value inContext:context] autorelease];
1463+ (id) valueWithJSObject:(JSObject *)object inContext:(JSContext *)context
1467 return [[[
self alloc] initWithJSObject:object inContext:context] autorelease];
1473- (id) initWithJSValue:(jsval)value inContext:(JSContext *)context
1477 self = [
super init];
1481 if (context == NULL)
1488 if (!JSVAL_IS_VOID(_val))
1490 JS_AddNamedValueRoot(context, &_val,
"OOJSValue");
1492 [[NSNotificationCenter defaultCenter] addObserver:self
1493 selector:@selector(deleteJSValue)
1494 name:kOOJavaScriptEngineWillResetNotification
1506- (id) initWithJSObject:(JSObject *)object inContext:(JSContext *)context
1508 return [
self initWithJSValue:OBJECT_TO_JSVAL(object) inContext:context];
1512- (void) deleteJSValue
1514 if (!JSVAL_IS_VOID(_val))
1517 JS_RemoveValueRoot(context, &_val);
1521 [[NSNotificationCenter defaultCenter] removeObserver:self
1522 name:kOOJavaScriptEngineWillResetNotification
1530 [
self deleteJSValue];
1535- (jsval) oo_jsValueInContext:(JSContext *)context
1545 NSCParameterAssert(
string != NULL && strCache != NULL && inited != NULL && !*inited);
1549 JSString *jsString = JS_InternString(context,
string);
1552 [NSException raise:NSGenericException format:@"Failed to initialize JavaScript string literal cache for \"%@\".", [[NSString stringWithUTF8String:string] escapedForJavaScriptLiteral]];
1555 *strCache = STRING_TO_JSVAL(jsString);
1569 const jschar *chars = JS_GetStringCharsAndLength(context,
string, &length);
1571 if (
EXPECT(chars != NULL))
1573 return [NSString stringWithCharacters:chars length:length];
1590 JSString *
string = JS_ValueToString(context, value);
1601 if (
EXPECT(!JSVAL_IS_NULL(value) && !JSVAL_IS_VOID(value)))
1613 if (JSID_IS_STRING(propID))
1617 else if (JSID_IS_INT(propID) && propertySpec != NULL)
1619 int tinyid = JSID_TO_INT(propID);
1621 while (propertySpec->name != NULL)
1623 if (propertySpec->tinyid == tinyid)
return [NSString stringWithUTF8String:propertySpec->name];
1629 if (!JS_IdToValue(context, propID, &value))
return @"unknown";
1634static NSString *
DescribeValue(JSContext *context, jsval value, BOOL abbreviateObjects, BOOL recursing)
1642 JSString *name = JS_GetFunctionId(JS_ValueToFunction(context, value));
1643 if (name != NULL)
return [NSString stringWithFormat:@"function %@", OOStringFromJSString(context, name)];
1644 else return @"function";
1647 NSString *result =
nil;
1648 JSClass *
class = NULL;
1651 if (JSVAL_IS_OBJECT(value) && !JSVAL_IS_NULL(value))
1657 if (
class == [jsEng stringClass])
1659 value = STRING_TO_JSVAL(JS_ValueToString(context, value));
1662 if (JSVAL_IS_STRING(value))
1664 enum { kMaxLength = 200 };
1666 JSString *
string = JSVAL_TO_STRING(value);
1668 const jschar *chars = JS_GetStringCharsAndLength(context,
string, &length);
1670 result = [NSString stringWithCharacters:chars length:MIN(length, (size_t)kMaxLength)];
1671 result = [NSString stringWithFormat:@"\"%@%@\"", [result escapedForJavaScriptLiteral], (length > kMaxLength) ? @"..." : @""];
1673 else if (
class == [jsEng arrayClass])
1677 JSObject *obj = JSVAL_TO_OBJECT(value);
1678 if (JS_GetArrayLength(context, obj, &
count))
1682 NSMutableString *arrayDesc = [NSMutableString stringWithString:@"["];
1683 jsuint i, effectiveCount =
MIN(
count, (jsuint)4);
1684 for (i = 0; i < effectiveCount; i++)
1687 NSString *itemDesc =
@"?";
1688 if (JS_GetElement(context, obj, i, &item))
1692 if (i != 0) [arrayDesc appendString:@", "];
1693 [arrayDesc appendString:itemDesc];
1695 if (effectiveCount !=
count)
1697 [arrayDesc appendFormat:@", ... <%u items total>]", count];
1701 [arrayDesc appendString:@"]"];
1708 result = [NSString stringWithFormat:@"[<%u items>]", count];
1722 if (abbreviateObjects &&
class == [jsEng objectClass] && [result isEqualToString:
@"[object Object]"])
1727 if (result ==
nil) result =
@"?";
1738 return DescribeValue(context, value, abbreviateObjects, NO);
1742@implementation NSString (OOJavaScriptExtensions)
1744+ (NSString *) stringWithJavaScriptParameters:(jsval *)params count:(uintN)count inContext:(JSContext *)context
1748 if (params == NULL &&
count != 0)
return nil;
1751 NSMutableString *result = [NSMutableString stringWithString:@"("];
1753 for (i = 0; i <
count; ++i)
1755 if (i != 0) [result appendString:
@", "];
1756 [result appendString:OOJSDescribeValue(context, params[i], NO)];
1759 [result appendString:@")"];
1766- (jsval) oo_jsValueInContext:(JSContext *)context
1770 size_t length = [
self length];
1771 unichar *buffer = NULL;
1772 JSString *
string = NULL;
1776 jsval result = JS_GetEmptyStringValue(context);
1781 buffer = malloc(length *
sizeof *buffer);
1782 if (buffer == NULL)
return JSVAL_VOID;
1784 [
self getCharacters:buffer];
1786 string = JS_NewUCStringCopyN(context, buffer, length);
1789 return STRING_TO_JSVAL(
string);
1796+ (NSString *) concatenationOfStringsFromJavaScriptValues:(jsval *)values count:(
size_t)count separator:(NSString *)separator inContext:(JSContext *)context
1801 NSMutableString *result =
nil;
1802 NSString *element =
nil;
1805 if (values == NULL)
return NULL;
1807 for (i = 0; i !=
count; ++i)
1810 if (result ==
nil) result = [[element mutableCopy] autorelease];
1813 if (separator !=
nil) [result appendString:separator];
1814 [result appendString:element];
1824- (NSString *)escapedForJavaScriptLiteral
1828 NSMutableString *result =
nil;
1829 NSUInteger i, length;
1831 NSAutoreleasePool *pool =
nil;
1833 length = [
self length];
1834 result = [NSMutableString stringWithCapacity:length];
1837 pool = [[NSAutoreleasePool alloc] init];
1838 for (i = 0; i != length; ++i)
1840 c = [
self characterAtIndex:i];
1844 [result appendString:@"\\\\"];
1848 [result appendString:@"\\b"];
1852 [result appendString:@"\\f"];
1856 [result appendString:@"\\n"];
1860 [result appendString:@"\\r"];
1864 [result appendString:@"\\t"];
1868 [result appendString:@"\\v"];
1872 [result appendString:@"\\\'"];
1876 [result appendString:@"\\\""];
1880 [result appendString:[NSString stringWithCharacters:&c length:1]];
1890- (NSString *) oo_jsClassName
1898@implementation NSArray (OOJavaScriptConversion)
1900- (jsval)oo_jsValueInContext:(JSContext *)context
1902 jsval value = JSVAL_VOID;
1909@implementation OONativeVector (OOJavaScriptConversion)
1911- (jsval)oo_jsValueInContext:(JSContext *)context
1913 jsval value = JSVAL_VOID;
1921@implementation NSDictionary (OOJavaScriptConversion)
1923- (jsval)oo_jsValueInContext:(JSContext *)context
1925 jsval value = JSVAL_VOID;
1933@implementation NSNumber (OOJavaScriptConversion)
1935- (jsval)oo_jsValueInContext:(JSContext *)context
1941 long long longLongValue;
1943 isFloat = [
self oo_isFloatingPointNumber];
1946 longLongValue = [
self longLongValue];
1947 if (longLongValue < (
long long)JSVAL_INT_MIN || (
long long)JSVAL_INT_MAX < longLongValue)
1956 if (!JS_NewNumberValue(context, [
self doubleValue], &result)) result = JSVAL_VOID;
1960 result = INT_TO_JSVAL((int32_t)longLongValue);
1969- (NSString *) oo_jsClassName
1977@implementation NSNull (OOJavaScriptConversion)
1979- (jsval)oo_jsValueInContext:(JSContext *)context
1991 JSFunction *
function = JS_ValueToFunction(context, JS_CALLEE(context, vp));
1994 OOJSReportError(context,
@"%@ cannot be used as a constructor.", name);
2005 id object = JS_GetPrivate(context,
this);
2008 [[object weakRefUnderlyingObject] oo_clearJSSelf:this];
2010 JS_SetPrivate(context,
this,
nil);
2022 NSString *description =
nil;
2023 JSClass *jsClass = NULL;
2028 description = [object oo_jsDescription];
2029 if (description ==
nil) description = [object description];
2031 if (description ==
nil)
2034 if (jsClass != NULL)
2036 description = [NSString stringWithFormat:@"[object %@]", [NSString stringWithUTF8String:jsClass->name]];
2039 if (description ==
nil) description =
@"[object]";
2053 jsval rval = JSVAL_VOID;
2056 NSCParameterAssert(entity !=
nil && param != NULL);
2057 NSCParameterAssert(param->context != NULL &&
JS_IsInRequest(param->context));
2066 BOOL success = JS_CallFunctionValue(param->context, param->jsThis, param->function, 1, args, &rval);
2072 if (!JS_ValueToBoolean(param->context, rval, &result)) result = NO;
2073 if (JS_IsExceptionPending(param->context))
2075 JS_ReportPendingException(param->context);
2076 param->errorFlag = YES;
2081 param->errorFlag = YES;
2104 if (![entity isVisibleToScripts])
return NO;
2105 if ([entity isShip])
2107 if ([entity isSubEntity])
return NO;
2108 if ([entity status] == STATUS_COCKPIT_DISPLAY)
return NO;
2111 else if ([entity isPlanet])
2113 switch ([(OOPlanetEntity *)entity planetType])
2136 return ([entity isVisibleToScripts] && [entity isShip] && [entity status] == STATUS_COCKPIT_DISPLAY && ![entity isSubEntity]);
2143 NSCParameterAssert(subclass != NULL && superclass != NULL);
2147 sRegisteredSubClasses = NSCreateMapTable(NSNonOwnedPointerMapKeyCallBacks, NSNonOwnedPointerMapValueCallBacks, 0);
2150 NSCAssert(NSMapGet(
sRegisteredSubClasses, subclass) == NULL,
@"A JS class cannot be registered as a subclass of multiple classes.");
2165 NSCParameterAssert(putativeSubclass != NULL && superclass != NULL);
2166 NSCAssert(
sRegisteredSubClasses != NULL,
@"OOJSIsSubclass() called before any subclasses registered (disallowed for hot path efficiency).");
2170 if (putativeSubclass == superclass)
return YES;
2174 while (putativeSubclass != NULL);
2182 Class requiredObjCClass,
const char *name,
2187 OOJS_PROFILE_ENTER_NAMED(name)
2188 NSCParameterAssert(requiredObjCClass != Nil);
2189 NSCParameterAssert(context != NULL &&
object != NULL && requiredJSClass != NULL && outObject != NULL);
2212 NSCAssert(actualClass->flags & JSCLASS_HAS_PRIVATE,
@"Native object accessor requires JS class with private storage.");
2215 *outObject = [(id)JS_GetPrivate(context, object) weakRefUnderlyingObject];
2219 if (
EXPECT_NOT(*outObject !=
nil && ![*outObject isKindOfClass:requiredObjCClass]))
2221 OOJSReportError(context,
@"Native method expected %@ from %s and got correct JS type but incorrect native object %@", requiredObjCClass, requiredJSClass->name, *outObject);
2236 JSObject *
object = NULL;
2237 if (
EXPECT_NOT(!JS_ValueToObject(context, value, &
object) ||
object == NULL))
2251 JSIdArray *ids = NULL;
2253 NSMutableDictionary *result =
nil;
2254 jsval value = JSVAL_VOID;
2258 ids = JS_Enumerate(context,
object);
2264 result = [NSMutableDictionary dictionaryWithCapacity:ids->length];
2265 for (i = 0; i != ids->length; ++i)
2267 jsid thisID = ids->vector[i];
2269 if (JSID_IS_STRING(thisID))
2273 else if (JSID_IS_INT(thisID))
2280 objKey = [NSNumber numberWithInt:JSID_TO_INT(thisID)];
2288 if (objKey !=
nil && !JS_LookupPropertyById(context,
object, thisID, &value)) value = JSVAL_VOID;
2290 if (objKey !=
nil && !JSVAL_IS_VOID(value))
2293 if (objValue !=
nil)
2295 [result setObject:objValue forKey:objKey];
2300 JS_DestroyIdArray(context, ids);
2311 JSObject *tableObject = NULL;
2314 NSMutableDictionary *result =
nil;
2315 jsval value = JSVAL_VOID;
2319 if (
EXPECT_NOT(JSVAL_IS_NULL(tableValue) || !JS_ValueToObject(context, tableValue, &tableObject)))
2324 ids = JS_Enumerate(context, tableObject);
2330 result = [NSMutableDictionary dictionaryWithCapacity:ids->length];
2331 for (i = 0; i != ids->length; ++i)
2333 jsid thisID = ids->vector[i];
2335 if (JSID_IS_STRING(thisID))
2345 if (objKey !=
nil && !JS_LookupPropertyById(context, tableObject, thisID, &value)) value = JSVAL_VOID;
2347 if (objKey !=
nil && !JSVAL_IS_VOID(value))
2351 if (objValue !=
nil)
2353 [result setObject:objValue forKey:objKey];
2358 JS_DestroyIdArray(context, ids);
2372 if (JSVAL_IS_NULL(value) || JSVAL_IS_VOID(value))
return nil;
2374 if (JSVAL_IS_INT(value))
2376 return [NSNumber numberWithInt:JSVAL_TO_INT(value)];
2378 if (JSVAL_IS_DOUBLE(value))
2380 return [NSNumber numberWithDouble:JSVAL_TO_DOUBLE(value)];
2382 if (JSVAL_IS_BOOLEAN(value))
2384 return [NSNumber numberWithBool:JSVAL_TO_BOOLEAN(value)];
2386 if (JSVAL_IS_STRING(value))
2390 if (JSVAL_IS_OBJECT(value))
2404 NSValue *wrappedClass =
nil;
2405 NSValue *wrappedConverter =
nil;
2407 JSClass *
class = NULL;
2409 if (tableObject == NULL)
return nil;
2412 wrappedClass = [NSValue valueWithPointer:class];
2414 if (wrappedConverter !=
nil)
2416 converter = [wrappedConverter pointerValue];
2417 return converter(context, tableObject);
2428 if (![result isKindOfClass:requiredClass]) result =
nil;
2436 if (![result isKindOfClass:requiredClass]) result =
nil;
2449 result = JS_GetPrivate(context,
object);
2450 return [result weakRefUnderlyingObject];
2456 NSValue *wrappedClass =
nil;
2457 NSValue *wrappedConverter =
nil;
2459 if (theClass == NULL)
return;
2462 wrappedClass = [NSValue valueWithPointer:theClass];
2463 if (converter != NULL)
2465 wrappedConverter = [NSValue valueWithPointer:converter];
2466 [sObjectConverters setObject:wrappedConverter forKey:wrappedClass];
2470 [sObjectConverters removeObjectForKey:wrappedClass];
2485 jsval value = JSVAL_VOID;
2487 NSArray *result =
nil;
2490 if (!JS_IsArrayObject(context, array))
return nil;
2491 if (!JS_GetArrayLength(context, array, &
count))
return nil;
2493 if (
count == 0)
return [NSArray array];
2495 values = calloc(
count,
sizeof *values);
2496 if (values == NULL)
return nil;
2498 for (i = 0; i !=
count; ++i)
2501 if (!JS_GetElement(context, array, i, &value)) value = JSVAL_VOID;
2504 if (
object ==
nil)
object = [NSNull null];
2508 result = [NSArray arrayWithObjects:values count:count];
2523 if (JS_ValueToNumber(context, OBJECT_TO_JSVAL(
object), &value))
2525 return [NSNumber numberWithDouble:value];
2539 if (JS_ValueToNumber(context, OBJECT_TO_JSVAL(
object), &value))
2541 return [NSNumber numberWithBool:(value != 0)];
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()
void sendMonitorError:withMessage:inContext:(JSErrorReport *errorReport,[withMessage] NSString *message,[inContext] JSContext *context)
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)