graalvm / sulong

Obsolete repository. Moved to oracle/graal.
Other
628 stars 65 forks source link

Polyglot docs missing some detail #867

Open mikehearn opened 5 years ago

mikehearn commented 5 years ago
  1. It's not clear to me if the "convert polyglot value to a struct" trick can handle function pointers inside the struct to emulate OO method calls. The header file says accessing members is equivalent to get/put member, so perhaps not, but it seems an obvious feature to have? Then you can map objects to structs (or perhaps even to C++ classes which would be neat)

  2. The docs go into a lot more detail than the website does and it's annoying to figure out how to get to polyglot.h from the tiny example on graalvm.org - it'd be great to have more thorough examples on the website.

rschatz commented 5 years ago

Hi Mike!

It's not clear to me if the "convert polyglot value to a struct" trick can handle function pointers inside the struct to emulate OO method calls. The header file says accessing members is equivalent to get/put member, so perhaps not, but it seems an obvious feature to have? Then you can map objects to structs...

Having function pointers inside the struct currently doesn't work, but we definitely want to support that in the future. It shouldn't be very hard to get this working.

... (or perhaps even to C++ classes which would be neat)

That's a bit more complex. First there is the question of semantics. I guess a foreign object that is cast to a C++ class should appear as a "subclass" of the C++ class. In terms of implementation, that means we'd need to emulate a vtable. I'm sure it's doable, but it's a bit more work.

See also https://github.com/oracle/graal/issues/609 for a discussion about the reverse feature, exporting C++ classes to other languages.

The docs go into a lot more detail than the website does and it's annoying to figure out how to get to polyglot.h from the tiny example on graalvm.org - it'd be great to have more thorough examples on the website.

I agree.

The inline documentation in polyglot.h is quite detailed. A good first step would be to put that on the webpage. Unfortunately our API doc infrastructure for the rest of the project is built on JavaDoc, we currently don't have a way to publish C API documentation (e.g. via Doxigen). I'll see what I can do to fix this.

rschatz commented 5 years ago

Having function pointers inside the struct currently doesn't work, but we definitely want to support that in the future.

Actually, I was a bit too fast: I just tested it, and it does indeed work, but only one level:

struct Point {
   double (*length)();
   struct Point *(*normalize)();
};

POLYGLOT_DECLARE_STRUCT(Point)

struct Point *pt = polyglot_as_Point(...);
printf("%f\n", pt->length()); // will work fine, but note that this is C, not C++, so no implicit "this" argument
struct Point *n1 = pt->normalize(); // will not quite do what you expect, the result is a "naked" polyglot value
struct Point *n2 = polyglot_as_Point(n1); // you need to do this

The missing feature is to pick up the types from the function pointer signature, and recursively apply them to arguments and return values, like we already do for read and written values.

mikehearn commented 5 years ago

You know you're doing well when your software turns out to have useful features you didn't know were there ;)

We're so close! With knowledge of return types and this, we'd be able to call Java/Ruby/Python/etc objects from C++ in a vaguely OOPified way, with compiler optimisations across the boundaries. That'd be stellar!

rschatz commented 5 years ago

With https://github.com/graalvm/sulong/commit/b24f57a1604c04f5736199e8ef8d83e50a53868c function pointer types should now be fully supported, including recursively on argument and return types.

For example:

C:

#include <stdio.h>
#include <polyglot.h>

struct Point {
   double x;
   double y;
   struct Point *(*add)(struct Point *);
};

POLYGLOT_DECLARE_STRUCT(Point)

void access(void *value) {
   struct Point *p1 = polyglot_as_Point(value);
   struct Point p2 = {3, 5};
   struct Point *sum = p1->add(&p2);
   printf("%f %f\n", sum->x, sum->y);
}

JS:

function createPoint(x, y) {
   var self = {x: x, y: y};
   self.add = function(other) {
      return createPoint(self.x + other.x, self.y + other.y);
   };
   return self;
}

var llvm = Polyglot.evalFile("llvm", "point.bc");
var p = createPoint(1, 2);
llvm.access(p);

Note that this is still C, not C++. There is no implicit this argument, you have to capture self yourself on the JS side. Of course you can now write a C++ wrapper for the C function pointer table.