| #!mojo mojo:js_content_handler |
| // Copyright 2015 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| // Simple JavaScript REPL example, using TerminalClient. |
| // |
| // To run this, do something like: |
| // |
| // $ sky/tools/mojodb start out/Debug mojo:moterm_example_app |
| // |
| // At the moterm_example_app prompt: |
| // |
| // :) file:///absolute/path/to/repl.js |
| // |
| // Then, at the repl.js prompt, enter JavaScript, e.g.: |
| // |
| // > 1 + 2 |
| // 3 |
| // > function f(x, y) { return x + y; } |
| // undefined |
| // > f("abc", 123) |
| // "abc123" |
| // |
| // (Note that multiple simultaneous REPLs will share the same global state, that |
| // the global state will persist, etc. This is really just intended as a demo!) |
| |
| define("main", [ |
| "mojo/services/public/js/application", |
| "mojo/services/files/interfaces/types.mojom", |
| "mojo/services/terminal/interfaces/terminal_client.mojom", |
| ], function(application, files_types, terminal_client) { |
| const Application = application.Application; |
| const TerminalClient = terminal_client.TerminalClient; |
| const Whence = files_types.Whence; |
| |
| function stringToBytes(s) { |
| return s.split("").map(function (c) { return c.charCodeAt(0); }); |
| } |
| |
| class Repl { |
| constructor(terminal) { |
| this.terminal_ = terminal; |
| } |
| |
| start() { |
| // TODO(vtl): Handle errors. |
| var self = this; |
| this.terminal_.write(stringToBytes("> "), 0, Whence.FROM_CURRENT) |
| .then(function (result) { self.didWritePrompt_(result); }); |
| } |
| |
| didWritePrompt_(result) { |
| // TODO(vtl): Handle errors. |
| var self = this; |
| this.terminal_.read(1000, 0, Whence.FROM_CURRENT) |
| .then(function (result) { self.didRead_(result); }); |
| } |
| |
| didRead_(result) { |
| // TODO(vtl): Handle errors. |
| var s = String.fromCharCode.apply(String, result.bytes_read).trim(); |
| var r; |
| try { |
| // Note: Indirectly calling eval as follows leads to the evaluation |
| // working at global scope (as of ECMAScript 5). (This allows us to |
| // persist state across evals without doing any work.) |
| var geval = eval; |
| r = geval(s); |
| } catch (e) { |
| r = e; |
| } |
| var resultString; |
| try { |
| if (typeof(r) == "string") { |
| resultString = "\"" + r + "\""; |
| } else if (Array.isArray(r)) { |
| resultString = "[" + r + "]"; |
| } else { |
| resultString = "" + r; |
| } |
| } catch (e) { |
| resultString = "Oops!"; |
| } |
| var self = this; |
| this.terminal_.write(stringToBytes(resultString + "\n"), 0, |
| Whence.FROM_CURRENT) |
| .then(function (result) { self.didWriteResult_(result); }); |
| } |
| |
| didWriteResult_(result) { |
| this.start(); |
| } |
| } |
| |
| class TerminalClientImpl { |
| connectToTerminal(terminal) { |
| (new Repl(terminal)).start(); |
| } |
| } |
| |
| class ReplApp extends Application { |
| acceptConnection(initiatorURL, initiatorServiceExchange) { |
| initiatorServiceExchange.provideService(TerminalClient, |
| TerminalClientImpl); |
| } |
| } |
| |
| return ReplApp; |
| }); |