creating an object from a ctype.c_void_pointer

1k views Asked by At

I am doing the following in python

import ctypes, ctypes.util
from gi.repository import WebKit, JSCore, GLib, Gtk
import sys

webkit = ctypes.CDLL(ctypes.util.find_library('webkitgtk-3.0'))
jscore = ctypes.CDLL(ctypes.util.find_library('javascriptcoregtk-3.0'))

def inject_js(view, frame):
    """
    void
    evalscript(WebKitWebFrame *frame, JSContextRef js, char *script, char* scriptname) { 
        JSStringRef jsscript, jsscriptname; 
        JSValueRef exception = NULL; 
        jsscript = JSStringCreateWithUTF8CString(script); 
        jsscriptname = JSStringCreateWithUTF8CString(scriptname); 
        JSEvaluateScript(js, jsscript, JSContextGetGlobalObject(js), jsscriptname, 0, &exception); 
        JSStringRelease(jsscript); 
        JSStringRelease(jsscriptname); 
    } 
    """

    offset = sys.getsizeof(object())
    frame = ctypes.POINTER(ctypes.c_void_p).from_address(id(frame) + offset)

    adr = webkit.webkit_web_frame_get_global_context(c_frame)
    js = ctypes.cast(js_ctx_adr, ctypes.c_void_p)

    js_objref_adr = jscore.JSContextGetGlobalObject(js_ctx_ref) #segfaults here


window = Gtk.Window()
view = WebKit.WebView()
window.add(view)
window.show_all()

view.connect('document-load-finished', inject_js)
view.load_uri("http://google.com")
mainloop = GLib.MainLoop()
mainloop.run()

I am trying to use ctypes to access a non-introspectable method, so far I am successful in creating a pointer to gtk/gobject stuff. However the js intance I am trying to cast should not be a pointer but rather the object itself, or something similar. ==> WebKitWebFrame *frame, JSContextRef js (not a pointer) How can I do that. Right now it just segfaults

1

There are 1 answers

0
Simon Feltman On

The argument types and return type need to be setup explicitly on ctypes functions. ctypes uses a default return type of "C int", which is likely why the segfault is occurring. See: Specifying the required argument types

jscore.JSContextGetGlobalObject.argtypes = [ctypes.c_void_p]
jscore.JSContextGetGlobalObject.restype = ctypes.c_void_p

webkit.webkit_web_frame_get_global_context.argtypes = [ctypes.c_void_p]
webkit.webkit_web_frame_get_global_context.restype = ctypes.c_void_p

JSContextRef and JSGlobalContextRef are typedefs to opaque struct pointers, so using c_void_p can work as the argument type: JavaScriptCore/API/JSBase.h

I think the use of sys.getsizeof(object()) and from_address is ok. It is used in the PyGObject unittests because it ensures the code will run correctly with debug builds of Python (where the PyObject struct has some extra fields and is of a different size). See: git.gnome.org/browse/pygobject/tree/tests/test_everything.py?id=3.9.90#n36

As a side note, PyGObject exposes a pointer to the underlying GObject as a PyCapsule via the attribute "__gpointer__". Unfortunately, this is not very useful because ctypes does not marshal the pointer held in PyCapsules automatically, nor does the pointer address seem accessible on the PyCapsule in Python.

With the argtypes/restype setup mentioned (and variable name fixes), the callback no longer segfaults:

def inject_js(view, frame):
    offset = sys.getsizeof(object())
    c_frame = ctypes.c_void_p.from_address(id(frame) + offset)

    js_ctx_ptr = webkit.webkit_web_frame_get_global_context(c_frame)
    js_obj_ptr = jscore.JSContextGetGlobalObject(js_ctx_ptr)