wren-lang / wren

The Wren Programming Language. Wren is a small, fast, class-based concurrent scripting language.
http://wren.io
MIT License
6.86k stars 550 forks source link

How does the host application obtain the name of the called constructor for foreign classes? #1073

Closed gkjpettet closed 2 years ago

gkjpettet commented 2 years ago

Consider this foreign Wren class:

foreign class Unicorn {
  construct new(name) {}
  construct brown(name) {}
}

The docs say:

When you create an instance of a foreign class by calling one its constructors, Wren invokes the allocate callback you gave it when binding the foreign class. Your primary responsibility in that callback is to tell Wren how many bytes of raw memory you need. You do that by calling:

void* wrenSetSlotNewForeign(WrenVM* vm,
int slot, int classSlot, size_t size);

I don't see any way to get the name of the constructor being called though. For example (given the script above), in the host application, how do I know that the script is calling the new or the brown constructor above so I can perform the appropriate initialisation?

minirop commented 2 years ago

You don't. Your class shouldn't depend on the constructor called to know how much memory it should take, otherwise you have an issue (can't find my words) and should probably split the class (e.g. having a field that is of type ClassA is new and ClassB is brown).

gkjpettet commented 2 years ago

Thank you for clarifying.

I'm in the process of making bindings for Wren for an obscure language (Xojo), not C, so I've been using the memory passed to the VM in the class allocator as a pointer to the instance in the host application not the number of bytes the instance will use.

I think what I'll do is when I design the Wren API for my app I will just use the same name for all constructors (e.g: new).

Presumably I'm able to get access to the number of parameters passed to the constructor or is that not possible either?

mhermier commented 2 years ago

If I remember well you have access to the parameters and so their counts, thought you MUST not alter the stack more than returning the newly created instance. That said there is a way to circumvent the limitation, if you really need to create objects of different size with the same object interface. Thought I would not recommend it.

gkjpettet commented 2 years ago

If I remember well you have access to the parameters and so their counts, thought you MUST not alter the stack more than returning the newly created instance.

That's great to hear.

That said there is a way to circumvent the limitation

Do tell...

mhermier commented 2 years ago
foreign class Unicorn {
  static new(name) {
     var instance = new_(0, name)
     // finish to initialize instance here
     return instance
  }
  static brown(name) {
     var instance = new_(1, name)
     // finish to initialize instance here
     return instance
  }
  construct new_(id, name) {
    this.name = name
  }
  foreign name=(newName)
}

That way you can change your allocation based on the identifier passed, I used a number for simplicity, but can be anything you can retrieve from the VM without modifying the stack (like a string).

mhermier commented 2 years ago

There is a limitation I forgot, you can't have member variables in vanilla wren. but you still have the possibility to use getter/setters.

gkjpettet commented 2 years ago

Thanks @mhermier, I think this is the solution.