codefrau / SqueakJS

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

SqueakJS on node #91

Closed ErikOnBike closed 4 years ago

ErikOnBike commented 4 years ago

I have put my current version of SqueakJS on node on my Github repo. I have not created a PR, because the changes are quite big and may need some adjustment to be in line with your preferred approach.

You can find it at: https://github.com/ErikOnBike/SqueakJS/tree/nodejs

I also put up a Gist where you can find a Cuis image which will run from SqueakJS on node. It will interact with a Node WebSocketServer which will display a prompt, allowing to send Smalltalk expressions to the Cuis image (in SqueakJS). Pretty cool if I might say so myself :-).

Gist: https://gist.github.com/ErikOnBike/ae10d81ed19500f24f155c086b3d7c09

Let me know what you think!

ccrraaiigg commented 4 years ago

Cool! The node installation on my server doesn't like the spread syntax on line 347 of node/websocket.js. What should I write instead?

Also, the current commit of Caffeine works as a WebWorker.

ErikOnBike commented 4 years ago

I assume lines 342-348 could be written as:

    const opts = Object.assign({
      binary: typeof data !== 'string',
      mask: !this._isServer,
      compress: true,
      fin: true
    }, options || {});

And lines 445-462 could be written as:

  const opts = Object.assign({
    protocolVersion: protocolVersions[1],
    maxPayload: 100 * 1024 * 1024,
    perMessageDeflate: true,
    followRedirects: false,
    maxRedirects: 10
  }, options || {}, {
    createConnection: undefined,
    socketPath: undefined,
    hostname: undefined,
    protocol: undefined,
    timeout: undefined,
    method: undefined,
    auth: undefined,
    host: undefined,
    path: undefined,
    port: undefined
  });

And lines 500-506 could be written as:

  opts.headers = Object.assign({
    'Sec-WebSocket-Version': opts.protocolVersion,
    'Sec-WebSocket-Key': key,
    Connection: 'Upgrade',
    Upgrade: 'websocket'
  }, opts.headers || {});

In the file sender.js you will also have to replace lines 65-72 with:

  const duplex = new Duplex(Object.assign({},
    options || {}, {
    autoDestroy: false,
    emitClose: false,
    objectMode: false,
    readableObjectMode: false,
    writableObjectMode: false
  }));

The position of the assignment of existing options in the second and fourth replacement is relevant, since items might be overwritten by latter ones. Hope this helps (have not tested it).

ErikOnBike commented 4 years ago

With respect to running Caffeine in WebWorker: Cool!!! I was not aware you already did that. It means you also have separated out the DOM access (which is not possible from within a WebWorker). I will check it out. Hopefully we are not doing the same kind of stuff then (that would seem like waste ;-). Added: I think you have limited the changes, because many browser features are also available within WebWorkers. My goal was to create a much leaner SqueakJS that could run in Node. From this leaner version I want to create a 'headless' variant for in the browser. This will not include any file handling and (after some thoughts about it) probably not even WebSocket support in its current format (although I need WebSocket support). There will probably be a WebSocket within a WebWorker which will communicate with the SqueakJS in the 'main' UI thread, because there I want access to the DOM. So the earlier 'headless' refers to the Smalltalk image which does not show its regular UI but can generate any type of UI. Hope this makes sense. I'll probably write something about it, not too long from now.

ccrraaiigg commented 4 years ago

Re: spread syntax changes: Thanks, that works!

Re: WebWorkers: Yeah, I notice a bunch of the same little changes (like using "self" instead of "window" in the plugins). No biggie.

I'm curious: how do you communicate with the object memory when running on Node? Do you use some kind of postMessage API at the JS level? That's how my main-thread memory and worker memory communicate, to do the history-system stuff that replaces the changes and sources files.

Thanks again!

ErikOnBike commented 4 years ago

On node there is a single memory space (like the original SqueakJS in the browser has a single memory space). So no need to communicate. Or did you mean something else?

ccrraaiigg commented 4 years ago

Yes, I meant how do you communicate between the object memory running in Node and other memories elsewhere, in other local Node processes, or on other machines. In the web-worker-in-a-web-browser case, there is the WebWorker-defined way of posting messages between the worker and the main thread. I was wondering if you'd thought about something similar for Node.

ErikOnBike commented 4 years ago

I use WebSockets to communicate between memories. I created some Smalltalk images (Cuis and Squeak) which listened to a fixed port and would just take the message received and perform these (like 'printit') and send the result back over the WebSocket connection. It required a Compiler in the image. My initial idea was to slowly remove all unnecessary classes from the image to get a small Image. Now with Pharo bootstrap I can just use the Pharo Candle source and create a tiny image to start with. That is easier. This small image will also run on SqueakJS. The only thing is, the VM does not seem to be able to find the class names. It looks in the wrong pointer position. Fixed it manually, but I think it is some image format that is not handled or created correctly (ie. not sure whether this is from Bootstrap or SqueakJS).

ErikOnBike commented 4 years ago

@bertfreudenberg are you interested in this SqueakJS on node? As explained in my first post, I have refactored your code to make this work and I was wondering if you would like to receive a PR for this. Or whether you prefer some other coding style or ... (you fill in the blanks ;-).

codefrau commented 4 years ago

Yep, interested. One specific idea would be to make this into a stand-alone version (maybe via SDL or Electron) to be able to run old interpreter images on a 64 bit only system. That would require "native" file and socket plugins for Node, as opposed to the "fake" web versions. So I like the direction this is going.

ErikOnBike commented 4 years ago

The current version (referenced in first post) does use 'native' FS already. See the file https://github.com/ErikOnBike/SqueakJS/blob/nodejs/vm.plugins.file.node.js (haven't tested this thoroughly). I have not implemented native socket plugin.

ErikOnBike commented 4 years ago

With new modularised SqueakJS, this issue can be closed.