Skip to content

Instantly share code, notes, and snippets.

@MCJack123
Created July 20, 2021 03:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save MCJack123/7383b9ecdff625ef04d8fd8a147b2004 to your computer and use it in GitHub Desktop.
Save MCJack123/7383b9ecdff625ef04d8fd8a147b2004 to your computer and use it in GitHub Desktop.
JavaScriptCore integration for CraftOS-PC (bad code)
#import <JavaScriptCore/JavaScriptCore.h>
#include <CraftOS-PC.hpp>
#include <math.h>
#include <float.h>
#include <stdarg.h>
static JSContext * ctx;
static JSValue * Function;
static int jsc_call(lua_State *L);
static void ObjCToLua(lua_State *L, id obj) {
if (obj == nil || [obj isKindOfClass:[NSNull class]]) lua_pushnil(L);
else if ([obj isKindOfClass:[NSString class]]) lua_pushlstring(L, [obj UTF8String], [obj length]);
else if ([obj isKindOfClass:[NSNumber class]]) lua_pushnumber(L, [obj doubleValue]);
else if ([obj isKindOfClass:[NSDictionary class]]) {
lua_newtable(L);
[obj enumerateKeysAndObjectsUsingBlock:^(id key, id val, BOOL *stop) {
ObjCToLua(L, key);
ObjCToLua(L, val);
lua_settable(L, -3);
}];
} else if ([obj isKindOfClass:[NSArray class]]) {
lua_newtable(L);
for (NSUInteger i = 0; i < [obj count]; i++) {
ObjCToLua(L, [obj objectAtIndex:i]);
lua_rawseti(L, -2, i + 1);
}
} else {
NSString * name = [obj className];
lua_pushliteral(L, "[");
lua_pushlstring(L, [name UTF8String], [name length]);
lua_pushliteral(L, "]");
lua_concat(L, 3);
[name release];
}
}
static int jsc_fn_free(lua_State *L) {
[*(JSValue**)lua_touserdata(L, 1) release];
return 0;
}
static bool JSValueToLua(lua_State *L, JSValue * val) {
if ([val isUndefined] || [val isNull]) lua_pushnil(L);
else if ([val isBoolean]) lua_pushboolean(L, [val toBool]);
else if ([val isNumber]) lua_pushnumber(L, [val toDouble]);
else if ([val isObject]) {
if ([val isInstanceOf:Function]) {
*(JSValue**)lua_newuserdata(L, sizeof(val)) = val;
lua_createtable(L, 0, 1);
lua_pushcfunction(L, jsc_fn_free);
lua_setfield(L, -2, "__gc");
lua_setmetatable(L, -2);
lua_pushcclosure(L, jsc_call, 1);
return false;
}
NSDictionary * dict = [val toDictionary];
ObjCToLua(L, dict);
[dict release];
} else if ([val isArray]) {
NSArray * arr = [val toArray];
ObjCToLua(L, arr);
[arr release];
} else {
NSString * strval = [val toString];
lua_pushlstring(L, strval.UTF8String, strval.length);
[strval release];
}
return true;
}
static JSValue * LuaToJSValue(lua_State *L, int idx, JSContext * ctx) {
switch (lua_type(L, idx)) {
case LUA_TNIL: return [JSValue valueWithNullInContext:ctx];
case LUA_TBOOLEAN: return [JSValue valueWithBool:lua_toboolean(L, idx) inContext:ctx];
case LUA_TNUMBER: return [JSValue valueWithDouble:lua_tonumber(L, idx) inContext:ctx];
case LUA_TSTRING: {
size_t len = 0;
const char * str = lua_tolstring(L, idx, &len);
return [JSValue valueWithObject:[[NSString alloc] initWithBytes:str length:len encoding:NSISOLatin1StringEncoding] inContext:ctx];
}
case LUA_TTABLE: {
NSMutableArray * keys = [NSMutableArray array];
NSMutableArray * values = [NSMutableArray array];
int i = 0;
lua_Integer arraySize = 0;
lua_pushnil(L);
while (lua_next(L, idx) != 0) {
if (arraySize >= 0) {
if (lua_type(L, -2) != LUA_TNUMBER) arraySize = -1;
else {
lua_Number num = lua_tonumber(L, -2);
if (fmod(num, 1) > DBL_MIN) arraySize = -1;
else if (arraySize < num) arraySize = (lua_Integer)num;
}
}
keys[i] = LuaToJSValue(L, lua_gettop(L) - 1, ctx);
values[i++] = LuaToJSValue(L, lua_gettop(L), ctx);
lua_pop(L, 1);
}
if (arraySize > 0) {
NSMutableArray * arr = [NSMutableArray arrayWithCapacity:arraySize];
for (int j = 0; j < i; j++) arr[[(NSNumber*)keys[j] integerValue]] = values[j];
[keys release];
[values release];
return [JSValue valueWithObject:arr inContext:ctx];
} else {
NSDictionary * dict = [NSDictionary dictionaryWithObjects:values forKeys:keys];
[keys release];
[values release];
return [JSValue valueWithObject:dict inContext:ctx];
}
}
case LUA_TLIGHTUSERDATA: case LUA_TUSERDATA: case LUA_TTHREAD: return [JSValue valueWithNullInContext:ctx]; // ?
case LUA_TFUNCTION: {
void * ptr = (void*)lua_topointer(L, idx);
lua_getfield(L, LUA_REGISTRYINDEX, "jsc_stack");
lua_State *stack = lua_tothread(L, -1);
lua_getfield(L, LUA_REGISTRYINDEX, "jsc_functions");
lua_pushlightuserdata(L, ptr);
lua_pushvalue(L, idx);
lua_settable(L, -3);
lua_pop(L, 2);
return [JSValue valueWithObject:^JSValue *() {
NSArray * args = [JSContext currentArguments];
lua_getfield(stack, LUA_REGISTRYINDEX, "jsc_functions");
lua_pushlightuserdata(stack, ptr);
lua_gettable(stack, -2);
lua_remove(stack, -2);
for (int i = 0; i < args.count; i++) JSValueToLua(stack, args[i]);
int status = lua_pcall(stack, args.count, 1, 0);
if (status != 0) {
ctx.exception = [JSValue valueWithNewErrorFromMessage:[NSString stringWithCString:lua_tostring(stack, -1) encoding:NSISOLatin1StringEncoding] inContext:ctx];
lua_pop(stack, 1);
return [JSValue valueWithNullInContext:ctx];
}
JSValue * retval = LuaToJSValue(stack, lua_gettop(stack), ctx);
lua_pop(stack, 1);
return retval;
} inContext:ctx];
}
default: return nil;
}
}
static int jsc_call(lua_State *L) {
JSValue * fn = *(JSValue**)lua_touserdata(L, lua_upvalueindex(1));
NSMutableArray * args = [NSMutableArray arrayWithCapacity:lua_gettop(L)];
for (int i = 1; i <= lua_gettop(L); i++) args[i-1] = LuaToJSValue(L, i, ctx);
ctx.exceptionHandler = ^void(JSContext *context, JSValue *exception) {
NSString * err = [exception toString];
lua_pushlstring(L, err.UTF8String, err.length);
[err release];
lua_error(L);
};
JSValue * retval = [fn callWithArguments:args];
JSValueToLua(L, retval);
[retval release];
return 1;
}
static int jsc_exec(lua_State *L) {
size_t len = 0;
const char * str = luaL_checklstring(L, 1, &len);
NSString * code = [[NSString alloc] initWithBytes:str length:len encoding:NSISOLatin1StringEncoding];
ctx.exceptionHandler = ^void(JSContext *context, JSValue *exception) {
NSString * err = [exception toString];
lua_pushlstring(L, err.UTF8String, err.length);
[err release];
lua_error(L);
};
JSValue * val = [ctx evaluateScript:code];
if (JSValueToLua(L, val)) [val release];
[code release];
return 1;
}
static int jsc_window_index(lua_State *L) {
if (lua_isnumber(L, 2) && fmod(lua_tonumber(L, 2), 1) <= DBL_MIN) JSValueToLua(L, [ctx.globalObject valueAtIndex:lua_tointeger(L, 2)]);
else if (lua_isstring(L, 2)) {
NSString * key = [NSString stringWithCString:lua_tostring(L, 2) encoding:NSISOLatin1StringEncoding];
JSValueToLua(L, [ctx.globalObject valueForProperty:key]);
[key release];
} else lua_pushnil(L);
return 1;
}
static int jsc_window_newindex(lua_State *L) {
id val = LuaToJSValue(L, 3, ctx);
if (lua_isnumber(L, 2) && fmod(lua_tonumber(L, 2), 1) <= DBL_MIN) [ctx.globalObject setValue:val atIndex:lua_tointeger(L, 2)];
else if (lua_isstring(L, 2)) {
NSString * key = [NSString stringWithCString:lua_tostring(L, 2) encoding:NSISOLatin1StringEncoding];
[ctx.globalObject setValue:val forProperty:key];
[key release];
}
[val release];
return 0;
}
static luaL_Reg jsc_lib[] = {
{"exec", jsc_exec},
{NULL, NULL}
};
static PluginInfo info("jsc");
extern "C" {
PluginInfo * plugin_init(const PluginFunctions * func, const path_t& path) {
ctx = [[JSContext alloc] init];
Function = [ctx evaluateScript:@"Function"];
return &info;
}
int luaopen_jsc(lua_State *L) {
lua_newtable(L);
lua_setfield(L, LUA_REGISTRYINDEX, "jsc_functions");
lua_newthread(L);
lua_setfield(L, LUA_REGISTRYINDEX, "jsc_stack");
luaL_register(L, "jsc", jsc_lib);
lua_newtable(L);
lua_createtable(L, 0, 2);
lua_pushcfunction(L, jsc_window_index);
lua_setfield(L, -2, "__index");
lua_pushcfunction(L, jsc_window_newindex);
lua_setfield(L, -2, "__newindex");
lua_setmetatable(L, -2);
lua_setfield(L, -2, "window");
return 1;
}
void plugin_deinit(PluginInfo * info) {
[ctx release];
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment