Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support pony bare functions to be reachable externally. #4569

Open
redvers opened this issue Dec 7, 2024 · 12 comments
Open

Support pony bare functions to be reachable externally. #4569

redvers opened this issue Dec 7, 2024 · 12 comments
Labels
discuss during sync Should be discussed during an upcoming sync

Comments

@redvers
Copy link
Contributor

redvers commented Dec 7, 2024

As per zulip discussion: https://ponylang.zulipchat.com/#narrow/channel/189985-beginner-help/topic/Hooking.20up.20pony.20bare.20functions.20via.20XML

The additional compile flags that I added to src/libponyc/codegen/genexe.c were:

   program_lib_build_args(program, c->opt, "-L", "-Wl,-rpath,",
-    "-Wl,--start-group ", "-Wl,--end-group ", "-l", "");
+    "-Wl,--start-group ", "-Wl,--end-group -Wl,--export-dynamic", "-l", "");
   const char* lib_args = program_lib_args(program);

I tested with --extfun, and ponyc (or I guess llvm), still removed the function.

In order for the function to not be removed by dead code removal, I assigned it to a variable like so:

let x: @{(): None} = Callbacks~test_extfun()

 
Thanks,

Red

@ponylang-main ponylang-main added the discuss during sync Should be discussed during an upcoming sync label Dec 7, 2024
@SeanTAllen
Copy link
Member

I believe --extfun is supposed to do what "-export-dynamic" does.

Did you try --extfun while assigning the callback to a variable?

@redvers
Copy link
Contributor Author

redvers commented Dec 7, 2024

Ah, okay - I need to remove my patch, recompile ponyc, and test that way. One moment caller...

@SeanTAllen
Copy link
Member

SeanTAllen commented Dec 7, 2024

Or use a prebuilt ponyc with the --extfun command.

@redvers
Copy link
Contributor Author

redvers commented Dec 7, 2024

It does not work with --extfun

@redvers
Copy link
Contributor Author

redvers commented Dec 7, 2024

Proof:

red@panic:~/projects/gtk4-pony$ ponyc --extfun --pass=obj
Building builtin -> /usr/local/lib/pony/0.58.6-d5f63217f/packages/builtin
Building . -> /home/red/projects/gtk4-pony
Building collections -> /usr/local/lib/pony/0.58.6-d5f63217f/packages/collections
Building pony_test -> /usr/local/lib/pony/0.58.6-d5f63217f/packages/pony_test
Building time -> /usr/local/lib/pony/0.58.6-d5f63217f/packages/time
Building random -> /usr/local/lib/pony/0.58.6-d5f63217f/packages/random
Building actor_pinning -> /usr/local/lib/pony/0.58.6-d5f63217f/packages/actor_pinning
Building Gtk -> /home/red/projects/gtk4-pony/Gtk
Building Gtk/Builder -> /home/red/projects/gtk4-pony/Gtk/Builder
Building ../../GObject/Object -> /home/red/projects/gtk4-pony/GObject/Object
Building debug -> /usr/local/lib/pony/0.58.6-d5f63217f/packages/debug
Building ../../GLib/Resource -> /home/red/projects/gtk4-pony/GLib/Resource
Building .. -> /home/red/projects/gtk4-pony/GLib
Building Gtk/Button -> /home/red/projects/gtk4-pony/Gtk/Button
Building ../Widget -> /home/red/projects/gtk4-pony/Gtk/Widget
Building Gtk/Window -> /home/red/projects/gtk4-pony/Gtk/Window
Building Gtk/ScrolledWindow -> /home/red/projects/gtk4-pony/Gtk/ScrolledWindow
Building GObject/Value -> /home/red/projects/gtk4-pony/GObject/Value
Building Webkit/WebView -> /home/red/projects/gtk4-pony/Webkit/WebView
Generating
 Reachability
 Selector painting
 Data prototypes
 Data types
 Function prototypes
 Functions
 Descriptors
Verifying
Writing ./gtk4-pony.o
red@panic:~/projects/gtk4-pony$ /usr/bin/clang -o ./gtk4-pony -O3 -march=native -mcx16  -fuse-ld=gold ./gtk4-pony.o  -lpthread -L"/usr/local/lib/pony/0.58.6-d5f63217f/bin/" -Wl,-rpath,"/usr/local/lib/pony/0.58.6-d5f63217f/bin/" -L"/usr/local/lib/pony/0.58.6-d5f63217f/bin/../lib/native" -Wl,-rpath,"/usr/local/lib/pony/0.58.6-d5f63217f/bin/../lib/native" -L"/usr/local/lib/pony/0.58.6-d5f63217f/bin/../packages" -Wl,-rpath,"/usr/local/lib/pony/0.58.6-d5f63217f/bin/../packages" -L"/usr/local/lib" -Wl,-rpath,"/usr/local/lib" -Wl,--start-group -l"rt" -l"gtk-4" -l"gobject-2.0" -l"gio-2.0" -l"glib-2.0" -l"webkitgtk-6.0" -l"gmodule-2.0" -Wl,--end-group   -lponyrt-pic -lm -ldl  -latomic
red@panic:~/projects/gtk4-pony$ ./gtk4-pony 
We are not pinned
We are pinned

(process:12921): Gtk-ERROR **: 15:54:24.809: failed to add UI from file gtk4-pony-test-ui.ui: No function named `Callbacks_test_extfun_o`.
Trace/breakpoint trap (core dumped)
red@panic:~/projects/gtk4-pony$ /usr/bin/clang -o ./gtk4-pony -O3 -march=native -mcx16  -fuse-ld=gold ./gtk4-pony.o  -lpthread -L"/usr/local/lib/pony/0.58.6-d5f63217f/bin/" -Wl,-rpath,"/usr/local/lib/pony/0.58.6-d5f63217f/bin/" -L"/usr/local/lib/pony/0.58.6-d5f63217f/bin/../lib/native" -Wl,-rpath,"/usr/local/lib/pony/0.58.6-d5f63217f/bin/../lib/native" -L"/usr/local/lib/pony/0.58.6-d5f63217f/bin/../packages" -Wl,-rpath,"/usr/local/lib/pony/0.58.6-d5f63217f/bin/../packages" -L"/usr/local/lib" -Wl,-rpath,"/usr/local/lib" -Wl,--start-group -l"rt" -l"gtk-4" -l"gobject-2.0" -l"gio-2.0" -l"glib-2.0" -l"webkitgtk-6.0" -l"gmodule-2.0" -Wl,--end-group   -lponyrt-pic -lm -ldl  -latomic -Wl,--export-dynamic
red@panic:~/projects/gtk4-pony$ 
red@panic:~/projects/gtk4-pony$ ./gtk4-pony 
We are not pinned
We are pinned

The only difference between the two command lines is the additional -Wl,--export-dynamic.

@redvers
Copy link
Contributor Author

redvers commented Dec 7, 2024

(For the record, I did it using just ponyc and --extfun and letting it complete the execution build without the --pass=obj)

@redvers
Copy link
Contributor Author

redvers commented Dec 7, 2024

Let me build a minimal example to add to this PR.

@redvers
Copy link
Contributor Author

redvers commented Dec 8, 2024

use "lib:gtk-4"
use "lib:glib-2.0"
use "lib:gobject-2.0"
use "actor_pinning"

use @gtk_init[None]()
use @gtk_builder_new_from_string[Pointer[GObject] tag](str: Pointer[U8] tag, length: I64)
use @gtk_builder_get_object[Pointer[GObject] tag](gobj: Pointer[GObject] tag, str: Pointer[U8] tag)
use @gtk_widget_set_visible[None](widget: Pointer[GObject] tag, visible: I32)
use @gtk_window_set_interactive_debugging[None](enable: I32)
use @g_main_context_default[Pointer[GMainContext]]()
use @g_main_context_iteration[I32](context: Pointer[GMainContext], mayblock: I32)
use @g_object_ref_sink[Pointer[GObject] tag](gobj: Pointer[GObject] tag)
use @g_object_ref[None](gobj: Pointer[GObject] tag)
use @printf[U32](fmt: Pointer[U8] tag, ...)

actor Main
  new create(env': Env) =>
    let q: GtkController = GtkController(env')
    q.initialize_gtk()

actor GtkController
  let env: Env
  let auth: PinUnpinActorAuth
  var builder: Pointer[GObject] tag = Pointer[GObject]
  var window: Pointer[GObject] tag = Pointer[GObject]
  var button: Pointer[GObject] tag = Pointer[GObject]
  var me: GtkController tag
  var active: Bool = true

  // Must be present to stop the function being dead-coded
  let cb: @{(): None} = Callbacks~button_clicked()


  new create(env': Env) =>
    env = env'
    auth = PinUnpinActorAuth(env.root)
    ActorPinning.request_pin(auth)
    me = recover tag this end

  be initialize_gtk() =>
    if ActorPinning.is_successfully_pinned(auth) then
      @printf("We are pinned\n".cstring())
      @gtk_init()
      summon_builder()
    else
      @printf("We are not pinned\n".cstring())
      initialize_gtk()
    end

  be summon_builder() =>
    @printf("summon_builder() called\n".cstring())
    builder = @gtk_builder_new_from_string(UIXML().cstring(), UIXML().size().i64())
    @g_object_ref(builder)
    window = @gtk_builder_get_object(builder, "window".cstring())
    @g_object_ref(window)
    button = @gtk_builder_get_object(builder, "button".cstring())
    @g_object_ref(button)
    @gtk_window_set_interactive_debugging(1)
    @gtk_widget_set_visible(window, 1)
    loop()

  fun loop() =>
    let context: Pointer[GMainContext] = @g_main_context_default()
    if (active) then
      @g_main_context_iteration(context, 0)
      loop()
    else
      @printf("Not In loop\n".cstring())
    end

  fun _final() =>
    @printf("Pony has exited for some reason\n".cstring())


primitive UIXML
  fun apply(): String val =>
    """
<?xml version='1.0' encoding='UTF-8'?>
<!-- Created with Cambalache 0.94.0 -->
<interface>
  <!-- interface-name x.ui -->
  <requires lib="gtk" version="4.12"/>
  <object class="GtkWindow" id="window">
    <child>
      <object class="GtkButton" id="button">
        <property name="label">Click Me!</property>
        <signal name="clicked" handler="Callbacks_button_clicked_o"/>
      </object>
    </child>
  </object>
</interface>
"""

primitive Callbacks
  fun @button_clicked() =>
    @printf("I have been clicked\n".cstring())

primitive GObject
primitive GMainContext

@redvers
Copy link
Contributor Author

redvers commented Dec 8, 2024

(Note - this isn't representative of the final API - this is just raw FFI so we can get the example as short as possible for your testing).

@SeanTAllen
Copy link
Member

@redvers Given that your example requires external dependencies, can you provide a Dockerfile that someone could use to build an image that would allow for testing of your example program.

@redvers
Copy link
Contributor Author

redvers commented Dec 8, 2024

I can try, it is not my area of expertise.

@jemc
Copy link
Member

jemc commented Dec 10, 2024

We discussed in today's sync call.

Both the Callbacks type and the bare function inside Callbacks are being eliminated by the reachability pass (reach.c) before we even reach codegen.c. So the --debug vs release and --extfun vs not aren't making a difference.

I suggested in the sync call that the best path forward here is an RFC for a new annotation that marks a function as "reachable" regardless of whether it's reachable from Pony code. And that annotation would not work for generic types, because we don't know which of the possible reifications of the generic type to mark as reachable.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
discuss during sync Should be discussed during an upcoming sync
Projects
None yet
Development

No branches or pull requests

4 participants