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.

Revisions

  1. dannote created this gist May 11, 2015.
    146 changes: 146 additions & 0 deletions JavaScriptCore.lua
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,146 @@
    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