Skip to content

Instantly share code, notes, and snippets.

@dannote
Created May 11, 2015 18:40
Show Gist options
  • Save dannote/ec83a263188e04f84a9b to your computer and use it in GitHub Desktop.
Save dannote/ec83a263188e04f84a9b to your computer and use it in GitHub Desktop.
JavaScriptCore Lua bindings
local ffi = require "ffi"
local lgi = require "lgi"
local WebKit = lgi.WebKit
ffi.cdef [[
typedef struct OpaqueJSValue* JSObjectRef;
typedef struct OpaqueJSValue* JSValueRef;
typedef struct OpaqueJSString* JSStringRef;
typedef struct OpaqueJSClass* JSClassRef;
typedef struct OpaqueJSContext* JSContextRef;
typedef struct OpaqueJSContext* JSGlobalContextRef;
typedef enum {
kJSTypeUndefined,
kJSTypeNull,
kJSTypeBoolean,
kJSTypeNumber,
kJSTypeString,
kJSTypeObject
} JSType;
]]
local Context = {}
Context.__index = Context
ffi.cdef [[
typedef struct GObjectInternal WebKitWebFrame;
JSGlobalContextRef webkit_web_frame_get_global_context(WebKitWebFrame *frame);
]]
function Context:new(web_view)
local instance = {}
instance.context = ffi.C.webkit_web_frame_get_global_context(web_view:get_main_frame()._native)
setmetatable(instance, self)
return instance
end
ffi.cdef [[
JSValueRef JSEvaluateScript(JSContextRef ctx, JSStringRef script, JSObjectRef thisObject,
JSStringRef sourceURL, int startingLineNumber, JSValueRef* exception)
]]
function Context:eval(script, url, line)
local script = Context.make_string(script)
local url = url and Context.make_string(url)
local line = line or 0
local exception = ffi.new("JSValueRef[1]")
local result = self:get_value(ffi.C.JSEvaluateScript(self.context, script, nil, url, line, exception))
if exception[0] ~= nil then
return result, self:get_value(exception[0])
end
return result
end
ffi.cdef "JSStringRef JSStringCreateWithUTF8CString(const char* string)"
function Context.make_string(str)
return ffi.gc(ffi.C.JSStringCreateWithUTF8CString(str), ffi.C.JSStringRelease)
end
ffi.cdef [[
JSValueRef JSValueMakeNull(JSContextRef ctx);
JSValueRef JSValueMakeBoolean(JSContextRef ctx, bool value);
JSValueRef JSValueMakeNumber(JSContextRef ctx, double value);
JSValueRef JSValueMakeString(JSContextRef ctx, JSStringRef string);
]]
function Context:make_value(obj)
if type(obj) == "nil" then
return ffi.C.JSValueMakeNull(self.context)
elseif type(obj) == "boolean" then
return ffi.C.JSValueMakeBoolean(self.context, obj)
elseif type(obj) == "number" then
return ffi.C.JSValueMakeNumber(self.context, obj)
elseif type(obj) == "string" then
return ffi.C.JSValueMakeNumber(self.context, Context.make_string(obj))
end
-- table is not supported
end
ffi.cdef "JSValueRef JSValueMakeFromJSONString(JSContextRef ctx, JSStringRef string)"
function Context:make_object(json)
return ffi.C.JSValueMakeFromJSONString(self.context, Context.make_string(json))
end
ffi.cdef "JSType JSValueGetType(JSContextRef ctx, JSValueRef value)"
function Context:value_type(value)
local type = ffi.C.JSValueGetType(self.context, value)
if type == ffi.C.kJSTypeUndefined then
return "undefined"
elseif type == ffi.C.kJSTypeNull then
return "nil"
elseif type == ffi.C.kJSTypeBoolean then
return "boolean"
elseif type == ffi.C.kJSTypeNumber then
return "number"
elseif type == ffi.C.kJSTypeString then
return "string"
elseif type == ffi.C.kJSTypeObject then
return "object"
end
end
ffi.cdef [[
size_t JSStringGetMaximumUTF8CStringSize(JSStringRef string);
size_t JSStringGetUTF8CString(JSStringRef string, char* buffer, size_t bufferSize);
void JSStringRelease(JSStringRef string);
]]
function Context.get_string(jsstr)
local len = ffi.C.JSStringGetMaximumUTF8CStringSize(jsstr)
local str = ffi.new("char[?]", len)
ffi.C.JSStringGetUTF8CString(jsstr, str, len)
return ffi.string(str, len)
end
ffi.cdef [[
bool JSValueToBoolean(JSContextRef ctx, JSValueRef value);
double JSValueToNumber(JSContextRef ctx, JSValueRef value, JSValueRef* exception);
JSStringRef JSValueToStringCopy(JSContextRef ctx, JSValueRef value, JSValueRef* exception);
]]
function Context:get_value(value)
local type = ffi.C.JSValueGetType(self.context, value)
local exception = ffi.new("JSValueRef[1]")
local result
if type == ffi.C.kJSTypeUndefined or type == ffi.C.kJSTypeNull then
result = nil
elseif type == ffi.C.kJSTypeBoolean then
result = ffi.C.JSValueToBoolean(self.context, value)
elseif type == ffi.C.kJSTypeNumber then
result = ffi.C.JSValueToNumber(self.context, value, exception)
elseif type == ffi.C.kJSTypeString or type == ffi.C.kJSTypeObject then
local jsstr = ffi.gc(ffi.C.JSValueToStringCopy(self.context, value, exception),
ffi.C.JSStringRelease)
result = Context.get_string(jsstr)
end
if exception[0] ~= nil then
return result, self:get_value(exception[0])
end
return result
end
local jscore = {}
jscore.Context = Context
return jscore
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment