Git Product home page Git Product logo

squeakjs's Introduction

SqueakJS: A Squeak VM for the Web and Node.js

SqueakJS is an HTML5 runtime engine for Squeak Smalltalk written in pure JavaScript. It also works for many other OpenSmalltalk-compatible images.

Embedding a Smalltalk application in your webpage can be as simple as:

SqueakJS.runSqueak(imageUrl, canvas);

The interpreter core is divided in a number of vm.*.js modules, internal plugins in vm.plugins.*.js modules and external plugins in the "plugins" directory. The Just-in-Time compiler is optional ("jit.js") and can be easily replaced with your own.

There are a number of interfaces:

  • browser: the regular HTML interface lets you use SqueakJS on your own web page. Just include "squeak.js".
  • headless browser: a headless VM. It lets you use SqueakJS in your browser without a direct UI (you can create your own UI with a plugin). Include "squeak_headless.js" and add an "imageName" parameter to your website URL (eg. https://example.com/my/page.html?imageName=./example.image) or call the Javascript function "fetchImageAndRun('https://example.com/my/example.image')" to start the specified image.
  • Node.js: another headless VM. It lets you use SqueakJS as a Node.js application. Just run "node squeak_node.js ".

For discussions, please use the vm-dev mailing list. Also, please visit the project home page!

Running it

Simplest

  • Run a minimal image. This is the simple demo included in this repo.
  • Or run Etoys. Everything except the image and template files is in this repo.
  • Or similarly, Scratch, also in here.

Run your own Squeak image in the browser

  • Drag an image from your local files into the launcher.
  • ... and all the other demo pages (see above) accept dropped images, too.

Run your own Squeak image from the command line

  • Install a recent version of Node.js
  • Run example image: node squeak_node.js headless/headless.image

Run an interactive shell based on WebSocket communication with Cuis image

  • Install a recent version of Node.js
  • Go to ws and execute start_server.sh in a first shell and start_client.sh in a second shell.
  • After initialization it should be possible to issue Smalltalk statements which will be executed in the Smalltalk image.
  • Try commands like: Object allSubclasses size 1837468731248764723 * 321653125376153761 Collection allSubclasses collect: [ :c | c name ]

Which Browser

All modern browsers should work (Chrome, Safari, IE, FireFox), though Chrome performs best currently. Safari on iPad works somewhat. YMMV. Fixes to improve browser compatibility are highly welcome!

If your browser does not support ES6 modules try the full or headless SqueakJS VM as a single file (aka bundle) in the Distribution directory.

Installing locally

  • clone the github repo:

    git clone https://github.com/codefrau/SqueakJS.git
    

    or download and unpack the ZIP archive

  • serve the SqueakJS directory using a local web server.

    TIP: If you have Node.js, try

    cd SqueakJS
    npx serve
    

    which will run a webserver on port 3000.

  • in a web browser, open http://localhost:3000/run/ and pick one of the images, or drag and drop your own

Now Squeak should be running. The reason for having to run from a web server is because the image is loaded with an XMLHttpRequest which does not work with a file URL. Alternatively, you could just open SqueakJS/run/index.html and drop in a local image.

Using (self contained) bundled files

  • select your preferred type of interface (browser or headless)
  • use the appropriate file (squeak_bundle.js resp. squeak_headless_bundle.js) from the Distribution directory
  • you can also build minified bundles using npm run build

How to modify it

  • use any text editor
  • you have to reload the page for your changes to take effect

How to share your changes

  • easiest for me is if you create a pull request
  • otherwise, send me patches

Contributions are very welcome!

Things to work on

SqueakJS is intended to run any Squeak image. It can already load any image from the original 1996 Squeak release to the latest Cog-Spur release, including 64-bit and Sista variants. But various pieces (primitives in various plugins) are still missing, in particular 3D graphics and networking (however, see Croquet which supports both, but should be generalized). Also, we should make pre-Spur 64 bit images load. And, it would be nice to make it work on as many browsers as possible, especially on mobile touch devices.

As for optimizing I think the way to go is an optimizing JIT compiler. The current JIT is very simple and does not optimize at all. Since we can't access or manipulate the JavaScript stack, we might want that compiler to inline as much as possible, but keep the call sequence flat so we can return to the browser at any time. Even better (but potentially more complicated) is actually using the JavaScript stack, just like Eliot's Stack VM uses the C stack. I have done some advanced JIT mockups. To make BitBlt fast, we could probably use WASM or even WebGL.

To make SqueakJS useful beyond running existing Squeak images, we should use the JavaScript bridge to write a native HTML UI which would certainly be much faster than BitBlt.

Better Networking would be interesting, too. The SocketPlugin currently does allows HTTP(S) requests and WebSockets. How about implementing low level Socket support based on HTTP-tunneling? The VM can run in a WebWorker. How about parallelizing the VM with WebWorkers?

There's a gazillion exciting things to do :)

-- Vanessa Freudenberg (codefrau)

Changelog

2024-06-22: 1.2.2 make copy/paste work on mobile
2024-05-27: 1.2.1 add virtual cmd button, fix touch events
2024-03-25: 1.2.0 add FFI and MIDI plugins, JIT for Sista bytecodes, JPEG write prim, fix keyboard input, copy/paste, scroll wheel, highdpi, allow ES6 in source
2023-11-24: 1.1.2 fixed BitBlt bug (symptom reported 9 years ago, thanks to Agustin Martinez for narrowing it down), add object pinning, support keyboard in ancient Scratch images
2023-10-24: 1.1.1 workarounds for Cuis 6
2023-10-23: 1.1.0 implement Etoys project saving (image segment export), drag-n-drop directories
2023-09-30: 1.0.6 fixes
2022-11-19: 1.0.5 fixes, add highdpi mode, add image format for Squeak 6
2021-05-31: 1.0.4 fixes
2021-03-21: 1.0.3 headless fixes (Erik Stel); fixes object-as-method
2021-02-07: 1.0.2 new one-way become prim (Christoph Tiede); JIT-compile Array at:/at:put:
2021-01-05: 1.0.1 fixes some primitives to properly pop the stack
2020-12-20: 1.0 supports 64 bits and Sista
2020-06-20: renamed "master" branch to "main"
2020-06-20: 0.9.9 JSBridge additions (Bill Burdick), fixes
2020-04-08: renamed github account to "codefrau"
2020-01-26: 0.9.8 split into modules (Erik Stel), fixes
2019-01-03: 0.9.7 minor fixes
2018-03-13: 0.9.6 minor fixes
2016-11-08: 0.9.5 more fixes
2016-10-20: 0.9.4 fixes
2016-09-08: 0.9.3 add partial GC (5x faster become / allInstances)
2016-08-25: 0.9.2 add keyboard on iOS
2016-08-03: 0.9.1 fixes
2016-07-29: 0.9 Spur support, stdout, SpeechPlugin, zipped images
2016-06-28: 0.8.3 add SocketPlugin for http/https connections
2016-04-07: 0.8.2 better touch handling, debugging, CORS, lint
2016-01-08: 0.8.1 windows keyboard fixes, 'new' operator fixed
2015-11-24: 0.8 minor fixes
2015-08-13: 0.7.9 make work on iOS again
2015-07-18: 0.7.8 fix keyboard
2015-06-09: 0.7.7 fix thisContext
2015-04-27: 0.7.6 revert JIT, minor fixes
2015-04-14: 0.7.5 JIT optimizations by HPI students (reverted in 0.7.6)
2015-02-18: 0.7.4 make pre-release image work
2015-01-30: 0.7.3 JSBridge: fix closure callbacks
2015-01-25: 0.7.2 JSBridge: add asJSObject
2014-12-22: 0.7.1 cursor shapes
2014-12-04: 0.7 support finalization of weak references
2014-11-28: 0.6.8 JSBridge with callbacks
2014-11-20: 0.6.7 implement JavaScriptPlugin
2014-11-18: 0.6.6 implement DropPlugin
2014-11-14: 0.6.5 add generated Balloon2D plugin
2014-11-06: 0.6.4 add generic run page
2014-10-28: 0.6.3 pass options via URL
2014-10-27: add JPEG plugin
2014-10-25: add template files
2014-10-23: 0.6.2 fixes
2014-10-21: 0.6.1 add image segment loading
2014-10-18: 0.6 move squeak.js out of lib dir
2014-10-13: 0.5.9 microphone support
2014-10-09: 0.5.8 fixes
2014-10-07: 0.5.7 even more plugins generated
2014-10-07: 0.5.6 add quitSqueak and onQuit
2014-10-07: 0.5.5 generated ScratchPlugin
2014-10-06: 0.5.4 replace BitBltPlugin by generated
2014-10-06: 0.5.3 SoundGenerationPlugin, Matrix2x3Plugin, FloatArrayPlugin
2014-10-05: ZipPlugin
2014-10-04: MiscPrimitivePlugin
2014-10-03: VMMakerJS generates LargeIntegersPlugin
2014-09-30: 0.5.2 more JIT
2014-09-28: 0.5.1 JIT fixes
2014-09-26: 0.5 add JIT compiler
2014-09-22: v8 optimizations
2014-09-20: 0.4.6 sound output support
2014-09-13: 0.4.5 clipboartd fixes
2014-09-12: 0.4.4 cut/copy/paste in stand-alone
2014-09-09: 0.4.3 some scratch prims
2014-09-09: 0.4.2 idle fixes
2014-09-05: 0.4.1 scratch fixes
2014-09-04: 0.4.0 runs scratch
2014-08-31: switch old/new primitives
2014-08-27: event-based input
2014-08-21: exception handling
2014-07-25: 0.3.3 fullscreen support
2014-07-18: 0.3.2 benchmarking (timfel)
2014-07-18: 0.3.1 deferred display
2014-07-16: 0.3.0 closure support
2014-07-14: 0.2.3 IE optimization (timfel)
2014-07-11: 0.2.2 drag-n-drop
2014-07-07: 0.2.1 fixes for IE11 (timfel)
2014-07-04: 0.2 runs Etoys
2014-06-27: Balloon2D (krono)
2014-06-03: stand-alone version
2014-05-29: 0.1 added version number
2014-05-27: WarpBlt
2014-05-07: image saving
2014-04-23: file support
2013-12-20: public release
2013-12-14: colored bitblt
2013-12-03: first pixels on screen
2013-11-29: GC
2013-11-22: runs 43 byte codes and 8 sends successfully
2013-11-07: initial commit

squeakjs's People

Contributors

abstraktor avatar aranlunzer avatar ccrraaiigg avatar codefrau avatar dependabot[bot] avatar erikonbike avatar fniephaus avatar jsparkes avatar krono avatar linqlover avatar luque avatar nicolaihess avatar nilos avatar pavel-krivanek avatar timfel avatar tonyg avatar zot avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

squeakjs's Issues

Implement SocketPlugin

So far, there is no networking support (except via the JSBridge). It would be awesome if socket connections could be opened, but it's not quite clear how this could work. WebSockets are a possibility but are hampered by the browser's security restriction. Possibly a proxy server is needed.

extend Locale plugin (stubs)

We are missing the better LocalePlugin implementation for:

locale_primitiveTimezoneOffset
locale_primitiveTimezoneOffset

I used these stubs for now:

    locale_primitiveTimezoneOffset: function(argCount) {
        this.vm.popNandPush(argCount, 0);
        return true;
    },
    locale_primitiveTimezoneOffset: function(argCount) {
        this.vm.popNandPush(argCount, 0);
        return true;
    },

VM parameters for better Pharo support

I used these VM parameters implementation for Pharo that tries to read them

          // 46   size of machine code zone, in bytes (stored in image file header; Cog JIT VM only, otherwise nil)
           case 46: return 0;

           // 49   the size of the external semaphore table (read-write; Cog VMs only)
           case 49: return null;

           // 65   In newer Cog VMs a set of flags describing VM features,
            //      if non-zero bit 0 implies multiple bytecode set support;
            //      if non-zero bit 0 implies read-only object support
            //      (read-only; Cog VMs only; nil in older Cog VMs, a boolean answering multiple bytecode support in not so old Cog VMs)
            case 65: return 0;

Test SqueakJS components

It'd be great to have a testing setup for SqueakJS.
This testing setup could run SUnit tests and/or other JavaScript tests.
Probably using Travis CI to be able to test pull requests in advance.

Launcher: Mixed Content

When visiting https://bertfreudenberg.github.io/SqueakJS/run/, the browser refuses to load images via http://.

Mixed Content: The page at 'https://bertfreudenberg.github.io/SqueakJS/run/#url=http://freudenbergs.de/…kjs&files=[Squeak4.5-13680.image,Squeak4.5-13680.changes,SqueakV41.sources]' was loaded over HTTPS, but requested an insecure XMLHttpRequest endpoint 'http://freudenbergs.de/bert/squeakjs/Squeak4.5-13680.image'. This request has been blocked; the content must be served over HTTPS.

Providing images through https:// will solve the problem.

Allow image to be passed in zip

Right now all files need to be served separately over http. It would be nice to be able to specify a zip file instead. It would have to be decoded using some JS zip library.

This would allow to cut down on the bandwidth (e.g. for the initial Etoys and Scratch downloads) and we could link to zipped Squeak images directly (e.g. http://files.squeak.org/5.0/).

Scratch buttons are the wrong size

As a long-time Scratch user, it was rather weird seeing smaller unpadded buttons. :-)
screen shot 2014-10-01 at 4 36 51 pm

P.S. This is very cool stuff! Have you seen Amber? How about Morphic.js?

mustBeBoolean sent to wrong object

Reported by Klaus D. Witzel:

jumpIfTrue and jumpIfFalse both send SelectorMustBeBoolean to the wrong object on the stack, they assume mustBeBoolean has 1 argument.

In the example, 'hello' zork: (nil ifTrue: []), which is intended to fail, the VM should send to nil but sends to the string.

asm.js

hi,
what do you think about compile some parts of squeakjs in asm.js. For example the canvas rendering code.
I don't konw if this ideas has been explored. What do you think about?

SocketPlugin: support incremental downloads

For now we download the whole file first before passing it on to the image. We should start delivering data as soon as it arrives. This would allow the image to display proper progress, and potentially reduce memory consumption.

Since we're dealing with binary data we might have to use the fetch api, which isn't widely supported yet. So this needs to be optional.

MessageTally not working as expected

E.g. in a 4.5 image running

MessageTally spyOn: [10 timesRepeat: [(2 raisedTo: 100000000) basicSize]]

gives

100.0% {3441ms} EventSensor>>eventTickler
  100.0% {3441ms} Delay>>wait

Perhaps there is something wrong with process switching?

Inputs not working on Pharo 7

Pharo on vanilla SqueakJS has no active inputs. The image is not responsive to events. The Craig's version does some changes to the method primitiveInputSemaphore:

    primitiveInputSemaphore: function(argCount) {
        var semaIndex = this.stackInteger(0);
        if (!this.success) return false;
        this.inputEventSemaIndex = semaIndex;
        window.interpreter = this;
        this.display.signalInputEvent = function() {
            this.signalSemaphoreWithIndex(this.inputEventSemaIndex);
        }.bind(this);
        this.display.signalInputEvent();
        return true;
    },

The problem is that in recordMouseEvent, the eventQueue is not set and so signalInputEvent is never executed

Detect when an image/changes ZIP is updated.

Currently, if an image/changes pair is delivered via ZIP, and then updated, the new ZIP is not downloaded and unpacked, and the old image/changes are used (tested with Chrome 54).

Add option to force a reload of files

If SqueakJS is invoked with a set of files as specified in the URL, those files will not be downloaded/updated if the browser cached them already:

https://bertfreudenberg.github.io/SqueakJS/run#url=<url-to-files>&files=[<some-files>]

It would be nice to provide an additional option to force a reload. Maybe this: &forceReload=true

Splitting of source files?

Do you think that it would make sense to split the source files into smaller pieces for easier source code management and organization? I tried to extract the internal plugins, interpreter, primitives etc. into standalone files as you may see here:

https://github.com/pavel-krivanek/pavel-krivanek.github.io/tree/master/pharo8/js/squeakjs

I guess it would make the managing of the code harder with Lively-Kernel but it seems that it is unfortunately dead these days and the next Lively versions like lively4-core are very different. So the better support for standard JS tools should be considered.

primitiveFetchMourner (172) missing in new primitives

We do not implement primitiveFetchMourner (172) in the new primitives. I use this workaround to do not let it fail in Pharo:

case 172: if (this.oldPrims) return this.namedPrimitive('SoundPlugin', 'primitiveSoundStop', argCount);
                else return this.popNandPushIfOK(argCount, this.vm.nilObj); //primitiveFetchMourner

Add to cdnjs.com?

Adding the library to http://cdnjs.com would make it much easier for developers to include it in their websites since they wouldn't have to host and update it themselves. This would be great since SqueakJS seems to be updated rather quickly.

sockets: simple requests working but not MC

This is working fine:
HTTPSocket httpGet: 'http://source.squeak.org:80/squeak45/
but this fails:
HTTPSocket httpGet: 'http://source.squeak.org/squeak45/' args: nil user: '' passwd: ''

This is with the https://crossorigin.me/ proxy and using the fetch API.

The proxy does not grant the requested Access-Control-Request-Headers: authorization, it sends back Access-Control-Allow-Headers: Content-Type instead. This appears to be ignored by XMLHttpRequest but fetch reports an error.

SqueakJS on node

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!

Primitive 100 should have two variants

There are two possible calling conventions of the <primitive 100> that have different arguments count:

Object>>#perform:withArguments:inSuperclass:
Context>>#object:perform:withArguments:inClass: (ContextPart)

The SqueakJS knows only the first one

JSBridge callback does not work in 4.5

Reported by Jerry Bell at http://lists.squeakfoundation.org/pipermail/vm-dev/2015-January/017585.html

I'm trying to run the JSBridge callback example from a 4.5 image. JSBridge.st is loaded and I'm running under Chrome on Windows. I get an error: "Error:primReturnFromCallback: failed. " Digging a little in the debugger, I find in JSObjectProxy>>handleCallback, result is : "Error: Error: This block accepts 2 arguments, but was called with 0 arguments".

Modifying the example with a callback block with 0 arguments works.

primitiveHashMultiply (159) missing

The primitive primitiveHashMultiply from the set of new primitives is missing. It should at least fail. I use this workaround:

            case 159: if (this.oldPrims) return this.primitiveFileRename(argCount);
                else return this.primitiveHashMultiply(argCount);

where this funciton is defined behind primitiveNotEqualLargeIntegers as:

    primitiveHashMultiply: function(numArgs) {
        false;
    },

Modifier keys stuck

When you cmd-tab away from Squeak, it gets the cmd-keydown event but not the cmd-keyup. So when returning it still thinks the cmd key is pressed, so you have to press cmd again.

I guess we need to also fetch the modifier key state from the event, not just look for keydown/keyup.

Add file backup to server (e.g. Dropbox)

E.g. using https://github.com/dropbox/dropbox-js

The web browser wipes out our "persistent" files if the disk space goes low. Also, if we had a way to "log in" our files could be shared across different browsers/devices.

Implementation-wise, files should be uploaded in the background. File download should happen on-demand, perhaps using the existing "templates" code. That would mean directory information would have to be fetched on startup.

Make the Mini image a little less minimal

Amazing project! It would be nice if the Mini image contained more of the functionality needed to work through the classic "Smalltalk-80: The Language" by Goldberg & Robson. For example I tried some of the Polygon examples and the Polygon class was missing. Also Pen did not respond to location etc.

SocketPlugin: keep CORS flag per server

Currently we always try a direct request first, and the proxied one second. In some situations this leads to considerable delay (e.g. 2 seconds on source.squeak.org).

We might want to keep a table of hosts and their need-proxy flag. Whenever a request succeeds we put a flag for that host in the table. When another request comes in and we have a flag already then we use the order (proxy/direct first) given by the flag, otherwise we try direct first.

Question: perform Smalltalk method from Javascript plugin

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?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.