TruffleSqueakの
紹介
ライブなPolyglotプログラミング環境を存分に楽しむ
第135回Smalltalk勉強会
合同会社ソフトウメヤ 梅澤真史
TruffleSqueakとは?
● GraalVM上で動くSqueak
○ GraalSqueakという名前の時もあった
○ Polyglotプログラミングが出来るSqueak
● GraalVMとは?
○ https://www.graalvm.org
○ Polyglotプログラミング用JVM
■ 複数言語が同一のランタイムで動作
■ 相互のやりとりも可能
○ OSS版と商用版(Oracle Cloud)が存在
○ AOTコンパイルでネイティブ実行イメージも作成可
Truffle Frameworkとは?
● GraalVMで提供される言語実装フレームワーク
○ https://www.graalvm.org/graalvm-as-a-platform/language-implementation-framework/
○ JavaScript, Ruby, Python, R などをサポート
○ LLVM IR, WebAssemblyもサポート
■ C, C++, Rust, Go
○ ASTインタプリタ
■ 実行時情報を基にツリーを最適化
■ JITコンパイルされる
○ Polyglot APIを提供
Context context = Context.create();
context.eval("js","print('Hello, JavaScript!');");
java
GraalVMのインストール
● Community Editionを入手
○ 現在Java 11ベースのものが安定版
○ GitHubのreleasesから
■ https://github.com/graalvm/graalvm-ce-builds/releases/tag/vm-21.1.0
○ アーカイブを適当なディレクトリに展開
○ 環境変数 GRAALVM_HOME を設定
○ $GRAALVM_HOME/bin以下にPATHを通す
● gu (GraalVM Updater) コマンドが使えるようになる
GraalVMインストールのTips
● Windowsの場合
○ サポート言語が少ない(Java,JS,WebAssemblyのみ)
○ WSL2でUbuntuに入れると良い
● Macの場合
○ /Libraryに入れるための設定が面倒
○ 軽く試すのであればSDKMAN!経由が楽
$ sdk install java 21.1.0.r11-grl
$ sdk current
Using:
java: 11.0.11.hs-adpt
TruffleSqueakのインストール (1)
● GitHubから各プラットフォーム用のjarを取得
○ https://github.com/hpi-swa/trufflesqueak/releases
○ GraalVMとバージョンを合わせること
● JDK11ベースの場合
○ Linux
■ trufflesqueak-installable-java11-linux-amd64-21.1.0.jar
○ Mac
■ trufflesqueak-installable-java11-darwin-amd64-21.1.0.jar
○ Windows
■ trufflesqueak-installable-java11-windows-amd64-21.1.0.jar
TruffleSqueakのインストール (2)
● guコマンドでインストール
$ gu install -L trufflesqueak-installable-java11-linux-amd64-21.1.0.jar
● gu list で確認
ComponentId Version Component name Stability Origin
---------------------------------------------------------------------------------------------------------------------------------
graalvm 21.1.0 GraalVM Core -
js 21.1.0 Graal.js Supported
smalltalk 21.1.0 TruffleSqueak Experimental
その他の言語も追加
● Polyglotプログラミング用に他の言語も追加
$ gu install python
$ gu install ruby
$ gu install R
TruffleSqueakの起動
● trufflesqueak でTruffleSqueak
の画面が立ち上がる
○ Squeak 5.2ベース
PolyglotWorkspaceを開く
● 左上の”PolyglotWorkspace open”のボタンを押下
PolyglotWorkspace open. "do it"
● 右上のメニューボタン(青ボタン)で言語の選択が可能
JavaScriptの実行
● JavaScriptに言語を切り替えて”print it”
○ 関数を定義して選択、実行できる
function createDate() {
return new Date();
}
createDate().toString() //=> 'Fri May 28 2021 22:24:40 GMT+0900 (JST)'
JavaScript
ForeignObject
● 今度はPythonに切り替えlistを”explore it”(Ctrl+Shift+i)
● 他言語の要素はForeignObjectとなる
○ 各種メタ情報を参照できる
○ 下部ペインからメッセージも送ることができる
evalの利用
● 言語を切り替えなくともevalで各言語の実行が可能
Polyglot eval: 'js' string: 'Math.random()'. "=> 0.06826435251134522"
Polyglot eval: 'ruby' string: 'Random.new.rand'. "=> 0.6788512960113738"
Polyglot.eval('smalltalk', 'Random new next'); //=> 0.5022477628512514
Polyglot.eval('smalltalk', 'Random new next') #=> 0.8651843420677447
● 他言語からSmalltalkの実行も可能
○ JavaScript workspaceで
○ Ruby workspaceで
Polyglot Proxy
● evalの結果返ってくるForeignObjectにさらに
メッセージ送信できる
● 引数も渡せる
(Polyglot eval: 'js' string: 'Math') random.
(Polyglot eval: 'ruby' string: 'Random') new rand.
Smalltalk
(Polyglot eval: 'js' string: '[1,2,3]') join: '-'. "=> '1-2-3'"
(Polyglot eval: 'js' string: 'Math') max: (1+2*3) and: (Polyglot eval: 'js' string: '1+2*3').
"=> 9"
((Polyglot eval: 'js' string: '[1,2,3]') concat: (Polyglot eval: 'ruby' string: '[4,5]')) asArray.
"=> #(1 2 3 4 5)"
Smalltalk
exportとimport
● Polyglot>>import:で他言語の変数をインポート
● Polyglot>>export:で他言語へ変数をエクスポート
Polyglot export: 'morph' value: Morph new.
Smalltalk
Polyglot.import("morph").openInHand();
JavaScript
Dictionaryをやりとりしてみる
Polyglot export: 'stDict' value: {'Smalltalk'->1. 'JS'->2} asDictionary. Smalltalk
(Polyglot import: 'jsObj') inspect.
let stDict = Polyglot.import("stDict");
stDict.size(); //2
stDict.values(); //#(1 2)
let jsObj = {};
stDict.keysAndValuesDo((key, value)=>{
jsObj[key] = value**2;
})
JSON.stringify(jsObj); //'{"Smalltalk":1,"JS":4}'
Polyglot.export("jsObj", jsObj);
stDict.inspect();
JavaScript
Smalltalk
Javaを使う
((Java type: 'java.awt.Frame') getFrames at: 1) inspect.
Smalltalk
● Javaとのやりとり用にJavaクラスが用意されている
self setTitle: 'hello'
Smalltalk
● Inspectorから...
Interopクラスの利用
● Proxyを使う通常の書き方
(Java type: 'java.lang.System') out println: 'Hello from Squeak'
Smalltalk
● Interopクラスから低レベルなPolyglot APIの呼び出しが可能
(Polyglot primitiveGetScope: 'java') inspect. Smalltalk
(Interop readMember: self member: 'java.lang.System') inspect.
Smalltalk
Interop invokeMember: self out
member: 'println/(Ljava/lang/String;)V'
arguments: #('Hello from TruffleSqueak!')
Smalltalk
● Inspectorから...
● Inspectorから...
PolyglotNotebookを開く
● 左上の”PolyglotNotebook open”のボタンを押下
PolyglotNotebook open. "do it"
● "Add cell"で各言語実行用のセルを追加できる
○ タイトルバーのクリックで言語切り替え可能
Rubyを追加した場合は...
● TruffleRuby についてはgemでrougeを入れておく
○ シンタックスハイライトでPolyglotNotebookが利用
$ gem install rouge -v 3.2.0
$ ruby --version
truffleruby 21.1.0, like ruby 2.7.2, GraalVM CE Native [x86_64-linux]
bindingsを通じてのやりとり
● 各言語からbindings辞書の読み書きが可能
● 右側のexplorerペインから観察できる
Cellの実行
● タイトルバーメニューで"run cell..."
○ 実行結果がすぐ下に展開される
1: Pythonでrangeからlistの生成
bindings["pyRange"] = range(0, 10)
bindings["pyList"] = list(bindings["pyRange"])
Python
● bindingsに値を格納すると
ForeignObjectとして
他言語から参照可能になる
2: Rubyでlistを配列にしてmap
bindings["rbArray"] =
Truffle::Interop.to_array(bindings["pyList"])
bindings["rbAdded"] = (bindings["rbArray"].map{|e| e + 65 })
Ruby
● Interopを使うとPythonの
list(ForeignObject)をRuby
のArrayに変換可能
3: Smalltalkで文字列に変換
bindings at: 'stArray' put: (bindings at:'rbAdded') asArray.
bindings at: 'stString' put: (bindings at: 'stArray') asByteArray
asString. "=>'ABCDEFGHIJ'"
Smalltalk
● asCollectionでRubyのArrayを
Smalltalkの配列に変換している
● #[65 66 67 68 69 70 71 72 73
74 75] が文字列となって
'ABCDEFGHIJ'
PolyglotNotebookのより高度な例
● "Some Smalltalk with Fabio Niephaus
- Fun with TruffleSqueak on GraalVM"
○ https://www.youtube.com/watch?v=MVblV_ruG28
○ https://gist.github.com/fniephaus/311155612be669061382685bf51b0b68
● 上記Gistに載っているNotebookのサンプルコードを実行してみる
○ conference-contributors-v3-no-text.ipynbをload
1: nokogiriでスクレイピング
require "nokogiri"; require "open-uri"
url = "https://2021.programming-conference.org/people-index"
doc = Nokogiri::HTML(URI.open(url))
bindings["rows"] = doc.css("#results-table .row").map{ | row |
row.css(".pers-affiliation,.pers-country").map(&:content)}
bindings["rows"].size
Ruby
2: pycountryの国データで集計
import pycountry
bindings["countries"] = [c.name for c in pycountry.countries
for row in bindings["rows"] if c.name in str(row[1])]
len(bindings["countries"])
Python
3: ggplot2で視覚化
%ggplot2
values <- data.frame(contributors = bindings["countries"])
data <- aggregate(x = values, by = list(countries =
values$contributors), FUN = length)
print(ggplot(data, aes(x = reorder(countries, +contributors),
contributors)) +
geom_bar(stat = "identity") + xlab("") + ylab("") + coord_flip() +
geom_hline(aes(yintercept = mean(contributors))))
R
サンプルの実行結果
注: 依存パッケージは入れておく
$ gem install rouge nokogiri
$ R -e 'install.packages("ggplot2")'
$ $GRAALVM_HOME/languages/python/lib-python/
$ curl -s -L https://bit.ly/38Ru3Fk | tar xzv
$ mv pycountry-20.7.3/src/pycountry ./3/
$ rm -r pycountry-20.7.3/
その他デモ
CallTargetBrowser
● メソッドが呼ばれている頻度が一目瞭然
CallTargetBrowser fullOnClass:Tetris.
Tetris new openInHand.
RPlotMorph
● SqueakのFormからJavaのGraphics2Dを生成し、
Rにexportして描画させている
RPlotMorph example
$ R -e 'install.packages("ggplot2")'
Benchmark
● 動的に最適化されて速くなっていく!!
Preferences setFlag: #higherPerformance toValue: true.
TruffleSqueakUtilities setUpUIBenchmark.
まとめ
● TruffleSqueakはGraalVM上で動作するSqueak
● Polyglotプログラミングを強力にサポート
○ PolyglotWorkspace
○ PolyglotNotebook
● 高速なSmalltalkの処理系としても面白い
○ GraalVMの動的な最適化
○ native-image化できるとより楽しそう

TruffleSqueakの紹介