組込み向けJSインタプリタEspruinoをRaspberryPi上で試す

組込みCPUでも動作するJavaScriptインタプリタEspruinoを触ってみます。
ECMAScriptにどれくらい対応してるのか実際に動かしてチェックしてみます。

Espruino公式ではEspruino対応しているボード等色々あります
http://www.espruino.com/

前半はRPiターゲットにコンパイルしてES対応チェック
後半で組込みCPUターゲットにコンパイルしてバイナリサイズを確認してみます。

Espruino version : 2v05.118

# RPiターゲットでコンパイル

RPiターゲットでコンパイルします

```console
$ git clone git@github.com:espruino/Espruino.git
$ cd Espruino
$ BOARD=RASPBERRYPI make
```

ファイルサイズは3.4MBくらいになりました。RPi上で実行してみます。


```console
$ ./espruino
espruino: Interactive mode.
espruino: Size of JsVar is now 28 bytes
espruino: Size of JsVarRef is now 4 bytes
espruino: Added SIGINT hook
espruino: Added SIGHUP hook
espruino: Added SIGTERM hook

 ____                 _
|  __|___ ___ ___ _ _|_|___ ___
|  __|_ -| . |  _| | | |   | . |
|____|___|  _|_| |___|_|_|_|___|
         |_| espruino.com
 2v05.118 (c) 2019 G.Williams

Espruino is Open Source. Our work is supported
only by sales of official boards and donations:
http://espruino.com/Donate

>
```

問題なく動作

# ESの対応状況

Espruino公式が出しているES対応表はこちら
http://www.espruino.com/Features

基本的にはES5、ES6の一部対応してるようす


## アロー関数

アロー関数、 const 、let は問題なくいけます

```js:hello.js
const hello = () => { console.log('hello'); }
let world = () => { console.log('world'); }

hello();
world();
```

```console
$ ./espruino hello.js

 ____                 _
|  __|___ ___ ___ _ _|_|___ ___
|  __|_ -| . |  _| | | |   | . |
|____|___|  _|_| |___|_|_|_|___|
         |_| espruino.com
 2v05.118 (c) 2019 G.Williams

Espruino is Open Source. Our work is supported
only by sales of official boards and donations:
http://espruino.com/Donate

hello
world
```

※実行すると毎回キャプションが出ますが以下カット


## Promise

Promiseも対応OK
finallyはES9仕様ですがついでに対応してないかなと確認してみたところ、finallyは未対応でした

```js:promise.js
function pr(err) {
  return new Promise((resolve, reject) => {
    if(err) { reject(new Error('!!!error!!!')); }
    else { resolve('hello'); }
  });
}

pr(false)
  .then( res => { console.log(res); } )
  .catch( e => { console.log('error : '+e.message); } );

pr(true)
  .then( res => { console.log(res); } )
  .catch( e => { console.log('error : '+e.message); } );
```

```console
$ ./espruino promise.js
hello
error : !!!error!!!
```

## async/await

async/awaitは未対応

```js:async.js
function pr(err) {
  return new Promise((resolve, reject) => {
    if(err) { reject(new Error('!!!error!!!')); }
    else { resolve('hello'); }
  });
}

async function foo() {
  const ret = await pr(false);
  console.log(ret);
}

foo();
```

```console
$ ./espruino async.js
Uncaught ReferenceError: "async" is not defined
```

## class
classは対応OK

```js:cl.js
class cl {
  constructor() {
    console.log('cl constructor');
  }

  pr(err) {
    return new Promise((resolve, reject) => {
      if(err) { reject(new Error('!!!error!!!')); }
      else { resolve('hello'); }
    });
  }
}

const c = new cl();

c.pr(false)
  .then( res => { console.log(res); } );
```

```console
$ ./espruino cl.js
cl constructor
hello
```

## EventEmitter

Node.jsのノリで `require('events') ` すると、Espruinoでは「そんなモジュールない！」と怒られます


```js:emit.js
const EventEmitter = require('events');
const em = new EventEmitter();

em.on('foo', (val) => { console.log('hello', val); });
em.emit('foo', 'world');
```

```console
$ ./espruino emit.js
Uncaught Error: Module events not found
```

公式を調べてみると、on/emitはObjectクラスの組込み関数として扱われている様子
https://www.espruino.com/Reference#Object

```js:emit-espruino.js
Object.on('foo', (val) => { console.log('hello', val); });
Object.emit('foo', 'world');
```

```
$ ./espruino emit-espruino.js
hello world
```

## requireでファイルロード

組込み系は基本的にはファイルシステムの概念がないので
Node.jsのようにrequireで自前のファイルをロードするという方法はEspruinoでは取っておらず
基本的に1ファイルにフラット化して実行しましょうというスタンスらしい

例えば以下2ファイルを用意したとき

```js:exp.js
class exp {
  constructor() {
    console.log('exp constructor');
  }

  hello() {
    console.log('hello');
  }
}

module.exports = exp;
```

```js:req.js
const exp = require('./exp');
const e = new exp();
e.hello();
```

Node.jsでは問題なく実行できるが

```console:Node.js
$ node req.js
exp constructor
hello
```

espruinoではexpファイルなんてものはないと怒られる

```console:espruino
$ ./espruino req.js
Uncaught Error: Module ./exp not found
```

そこで、requireのファイル依存関係をなくすためにbrowserifyを使います

```console
$ sudo npm install -g browserify
$ browserify req.js -o req.bundle.js
```

browserify された req.bundle.js を espruino に流すと実行できるようになりました

```console
$ ./espruino req.bundle.js
exp constructor
hello
```

browserifyで自動生成された req.bundle.js のファイルの中身

```js:req.bundle.js
(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
class exp {
  constructor() {
    console.log('exp constructor');
  }

  hello() {
    console.log('hello');
  }
}

module.exports = exp;


},{}],2:[function(require,module,exports){
const exp = require('./exp');
const e = new exp();
e.hello();


},{"./exp":1}]},{},[2]);
```

# 組込みCPUターゲット時のバイナリサイズ

最後に、組込みCPUをターゲットにしたときのバイナリサイズはどれくらいになるのかが気になったので確認しました。


評価ボード [NUCLEO-F401RE](https://www.st.com/en/evaluation-tools/nucleo-f401re.html#overview) をターゲットにしたときのバイナリサイズを確認。
組込みCPU [STM32F401RE](https://www.st.com/en/microcontrollers-microprocessors/stm32f401re.html) の**Flash 512KB** に対して **Espruino 356KB** とかなり容量取られてます。
ドライバ全部入りになってるはずなので削ることができればもう少し減らせるかも。

```
$ sudo apt install -y binutils-arm-none-eabi gcc-arm-none-eabi
$ make clean
$ BOARD=NUCLEOF401RE make
$ ls -lh
-rwxr-xr-x 1 pi pi 356K May 30 15:45 espruino_2v05.118_nucleof401re.bin
```