codefrau / SqueakJS

A Squeak Smalltalk VM in Javascript
https://squeak.js.org
MIT License
365 stars 75 forks source link

Question: perform Smalltalk method from Javascript plugin #92

Closed ErikOnBike closed 4 years ago

ErikOnBike commented 4 years ago

I'd like to call a Smalltalk method from within my Javascript plugin (instead of using Semaphore to signal some event). I tried something like shown below, but I end up in a debugger showing me a DNU for a method on an object/class I am not referring to directly.

// Trying to do a sort of #perform: with the argument of the named primitive within the plugin
primitiveTest: function(argCount) {
  if (argCount !== 1) return false;
  var selector = this.interpreterProxy.vm.stackValue(0);
  this.interpreterProxy.vm.push(this.interpreterProxy.vm.receiver);
  this.interpreterProxy.vm.send(selector, 0, false);
  // ...probably need to remove the pushed receiver, but popping here does not give correct result

  // Normal behavior for popping the arguments and setting the result of named primitive function
  this.interpreterProxy.popthenPush(argCount, this.interpreterProxy.nilObject());
  return true;
}

The selector in the above has the correct Symbol. The DNU is for a message which IS send from the method belonging to the selector I'm trying to perform. So it seems as if 'self' within the performed method is not tied to 'receiver' but to the outer context (the instance performing the method which invoked the primitive). The following method is called, but a DNU for #log: is reported on some other instance (not the receiver).

mySelector

    self log: (3 + 4) printString

Could not find any similar situations in the existing code base. Any advice would be highly appreciated! Is this approach not possible? Is using a semaphore to trigger some Smalltalk code for doing the #perform the regular/right way of doing this?

ccrraaiigg commented 4 years ago

That's how I do it. To do it the way you're attempting, I think you'd also want to create a process in which the message-send occurs. I.e., create a block closure that executes the code you want to run, and schedule a process forked from it. It's just much less work to collaborate with an already-existing process through a semaphore.

codefrau commented 4 years ago

Primitives are atomic, you cannot call Smalltalk methods from within. Registering a handler in the image that is acting on a semaphore is the right way to do this, yes.

Btw, vm.send does not execute the method, it just activates a new context to execute the method. You would have to call vm.interpretOne() in a loop until that method returns. But that method activate other methods which could lead to process switches etc pp. At least it would invoke other primitives so the primitive return value value and primitive error code (which is global in many VMs) would be clobbered. To support this kind of recursion the interpreter would have to be re-entrant. It is not, nor do any of the other Squeak VMs support this.

ErikOnBike commented 4 years ago

Thanks Craig and Bert. I'll use the semaphore approach then.