diff --git a/experiments/modern/src/maki-interpreter/Emitter.js b/experiments/modern/src/maki-interpreter/Emitter.js new file mode 100644 index 00000000..931ddffb --- /dev/null +++ b/experiments/modern/src/maki-interpreter/Emitter.js @@ -0,0 +1,44 @@ +class Emitter { + constructor() { + this._hooks = {}; + // TODO: Rename this property + this._globalHooks = []; + } + + listen(eventName, cb) { + if (this._hooks[eventName] == null) { + this._hooks[eventName] = []; + } + this._hooks[eventName].push(cb); + return () => { + this._hooks[eventName] = this._hooks[eventName].filter( + hookCb => hookCb !== cb + ); + }; + } + + trigger(eventName, ...args) { + this._globalHooks.forEach(cb => cb(eventName, args)); + if (this._hooks[eventName] == null) { + return; + } + this._hooks[eventName].forEach(cb => { + cb(...args); + }); + } + + listenToAll(cb) { + this._globalHooks.push(cb); + return () => { + this._globalHooks = this._globalHooks.filter(hookCb => hookCb !== cb); + }; + } + + dispose() { + // Note: This will cause any future trigger or hook to cause a runtime error. + this._hooks = null; + this._globalHooks = null; + } +} + +module.exports = Emitter; diff --git a/experiments/modern/src/maki-interpreter/interpreter.js b/experiments/modern/src/maki-interpreter/interpreter.js index 5161a751..bc7ee811 100644 --- a/experiments/modern/src/maki-interpreter/interpreter.js +++ b/experiments/modern/src/maki-interpreter/interpreter.js @@ -29,20 +29,13 @@ function main({ runtime, data, system, log }) { return interpret(binding.commandOffset, program, { log }); }; - // For now we only know how to handle System handlers. - if (binding.variableOffset === 0) { - const obj = program.variables[binding.variableOffset].getValue(); - const method = program.methods[binding.methodOffset]; - obj[method.name](handler); - } else { - console.warn( - "Not Implemented: Not binding to non-system events", - binding - ); - } + const variable = program.variables[binding.variableOffset]; + + const method = program.methods[binding.methodOffset]; + variable.hook(method.name, handler); }); - system._start(); + system.js_start(); } module.exports = main; diff --git a/experiments/modern/src/maki-interpreter/runtime/MakiObject.js b/experiments/modern/src/maki-interpreter/runtime/MakiObject.js index a4776d43..41a34807 100644 --- a/experiments/modern/src/maki-interpreter/runtime/MakiObject.js +++ b/experiments/modern/src/maki-interpreter/runtime/MakiObject.js @@ -1,4 +1,22 @@ +const Emitter = require("../Emitter"); + class MakiObject { + constructor() { + this._emitter = new Emitter(); + } + + js_trigger(eventName, ...args) { + this._emitter.trigger(eventName, args); + } + + js_listenToAll(cb) { + return this._emitter.listenToAll(cb); + } + + js_dispose() { + this._emitter.dispose(); + } + /** * getClassName() * diff --git a/experiments/modern/src/maki-interpreter/runtime/System.js b/experiments/modern/src/maki-interpreter/runtime/System.js index 17e1219b..27330ca6 100644 --- a/experiments/modern/src/maki-interpreter/runtime/System.js +++ b/experiments/modern/src/maki-interpreter/runtime/System.js @@ -12,23 +12,10 @@ class System extends MakiObject { return "System"; } - constructor() { - super(); - this._onScriptLoadedCallbacks = []; - this._onSetXuiParamCallbacks = []; + js_start() { + this.js_trigger("onScriptLoaded"); } - _start() { - this._onScriptLoadedCallbacks.forEach(cb => { - cb(); - }); - } - onScriptLoaded(cb) { - this._onScriptLoadedCallbacks.push(cb); - } - onSetXuiParam(cb) { - this._onSetXuiParamCallbacks.push(cb); - } getScriptGroup() { return new Group(); } diff --git a/experiments/modern/src/maki-interpreter/variable.js b/experiments/modern/src/maki-interpreter/variable.js index ca9fb635..75be3fda 100644 --- a/experiments/modern/src/maki-interpreter/variable.js +++ b/experiments/modern/src/maki-interpreter/variable.js @@ -1,7 +1,11 @@ +const Emitter = require("./Emitter"); + class Variable { constructor({ type, typeName }) { this.type = type; this.typeName = typeName; + this._emitter = new Emitter(); + this._unsubscribeFromValue = null; } getValue() { @@ -9,8 +13,27 @@ class Variable { } setValue(value) { + if (this._unsubscribeFromValue != null) { + this._unsubscribeFromValue(); + } + if (this.typeName === "OBJECT") { + this._unsubscribeFromValue = value.js_listenToAll((eventName, args) => { + this._emitter.trigger(eventName, args); + }); + } this._value = value; } + + hook(eventName, cb) { + this._emitter.listen(eventName, cb); + } + + dispose() { + if (this._unsubscribeFromValue) { + this._unsubscribeFromValue(); + } + this._emitter.dispose(); + } } module.exports = Variable;