Spaces:
Running
Running
| // This is a SqueakJS VM for use with node | |
| // | |
| // To start an image use: node squeak_node.js [-ignoreQuit] <image filename> | |
| // | |
| // To start the minimal headless image present in the folder "headless" use: | |
| // node squeak_node.js headless/headless.image | |
| // | |
| // Option "-ignoreQuit" is present to prevent some images from quiting when | |
| // no GUI (support) is found. The image will not be able to quit from within | |
| // the image and needs to be quit by stopping the process itself. | |
| // In some situations adding "-ignoreQuit" can make some minimal images crash | |
| // when no more processes are running (ie when no bytecode is left to execute). | |
| // | |
| // A special ConsolePlugin is loaded which allows sending messages to the console. | |
| // Add the following method to the Smalltalk image (to Object for example): | |
| // | |
| // primLog: messageString level: levelString | |
| // | |
| // "Log messageString to the console. The specified level should be one of: | |
| // 'log' | |
| // 'info' | |
| // 'warn' | |
| // 'error' | |
| // " | |
| // | |
| // <primitive: 'primitiveLog:level:' module: 'ConsolePlugin'> | |
| // ^ self | |
| // | |
| // The VM will try to load plugins when named primitives are used for the first time. | |
| // These plugins do not need to be imported up front. | |
| var os = require("os"); | |
| var fs = require("fs"); | |
| var process = require("process"); | |
| var path = require("path"); | |
| // Retrieve image name and parameters from command line | |
| var processArgs = process.argv.slice(2); | |
| var ignoreQuit = processArgs[0] === "-ignoreQuit"; | |
| if (ignoreQuit) { | |
| processArgs = processArgs.slice(1); | |
| } | |
| var fullName = processArgs[0]; | |
| if (!fullName) { | |
| console.error("No image name specified."); | |
| console.log("Usage (simplified): " + path.basename(process.argv0) + path.basename(process.argv[1]) + " [-ignoreQuit] <image filename>"); | |
| process.exit(1); | |
| } | |
| var root = path.dirname(fullName) + path.sep; | |
| var imageName = path.basename(fullName, ".image"); | |
| // Create global 'self' resembling the global scope in the browser DOM | |
| Object.assign(global, { | |
| // Add browser element 'self' for platform consistency | |
| self: new Proxy({}, { | |
| get: function(obj, prop) { | |
| return global[prop]; | |
| }, | |
| set: function(obj, prop, value) { | |
| global[prop] = value; | |
| return true; | |
| } | |
| }) | |
| }); | |
| // Extend the new global scope with a few browser/DOM classes and methods | |
| Object.assign(self, { | |
| localStorage: {}, | |
| WebSocket: typeof WebSocket === "undefined" ? require("./lib_node/WebSocket") : WebSocket, | |
| sha1: require("./lib/sha1"), | |
| btoa: function(string) { | |
| return Buffer.from(string, 'ascii').toString('base64'); | |
| }, | |
| atob: function(string) { | |
| return Buffer.from(string, 'base64').toString('ascii'); | |
| } | |
| }); | |
| // Load VM and the internal plugins | |
| require("./globals.js"); | |
| require("./vm.js"); | |
| require("./vm.object.js"); | |
| require("./vm.object.spur.js"); | |
| require("./vm.image.js"); | |
| require("./vm.interpreter.js"); | |
| require("./vm.interpreter.proxy.js"); | |
| require("./vm.instruction.stream.js"); | |
| require("./vm.instruction.stream.sista.js"); | |
| require("./vm.instruction.printer.js"); | |
| require("./vm.primitives.js"); | |
| require("./jit.js"); | |
| require("./vm.display.js"); | |
| require("./vm.display.headless.js"); // use headless display to prevent image crashing/becoming unresponsive | |
| require("./vm.input.js"); | |
| require("./vm.input.headless.js"); // use headless input to prevent image crashing/becoming unresponsive | |
| require("./vm.plugins.js"); | |
| require("./vm.plugins.file.node"); | |
| // Set the appropriate VM and platform values | |
| Object.extend(Squeak, { | |
| vmPath: process.cwd() + path.sep, | |
| platformSubtype: "Node.js", | |
| osVersion: process.version + " " + os.platform() + " " + os.release() + " " + os.arch(), | |
| windowSystem: "none", | |
| }); | |
| // Extend the Squeak primitives with ability to load modules dynamically | |
| Object.extend(Squeak.Primitives.prototype, { | |
| loadModuleDynamically: function(modName) { | |
| try { | |
| require("./plugins/" + modName); | |
| // Modules register themselves, should be available now | |
| return Squeak.externalModules[modName]; | |
| } catch(e) { | |
| console.error("Plugin " + modName + " could not be loaded"); | |
| } | |
| return undefined; | |
| } | |
| }); | |
| // Read raw image | |
| fs.readFile(root + imageName + ".image", function(error, data) { | |
| if (error) { | |
| console.error("Failed to read image", error); | |
| return; | |
| } | |
| // Create Squeak image from raw data | |
| var image = new Squeak.Image(root + imageName); | |
| image.readFromBuffer(data.buffer, function startRunning() { | |
| // Create fake display and create interpreter | |
| var display = { vmOptions: [ "-vm-display-null", "-nodisplay" ] }; | |
| var vm = new Squeak.Interpreter(image, display); | |
| function run() { | |
| try { | |
| vm.interpret(200, function runAgain(ms) { | |
| // Ignore display.quitFlag when requested. | |
| // Some Smalltalk images quit when no display is found. | |
| if (ignoreQuit || !display.quitFlag) { | |
| setTimeout(run, ms === "sleep" ? 10 : ms); | |
| } | |
| }); | |
| } catch(e) { | |
| console.error("Failure during Squeak run: ", e); | |
| } | |
| } | |
| // Start the interpreter | |
| run(); | |
| }); | |
| }); | |