諸般の事情あって Scala.js の出力する js 用の型を取り出したくなったので、とりまということで .sjsir の情報を出力するツール scalajsp の出力を観賞してみました。この記事は観賞するだけですのでそれ以上の有益な分析結果などはありません。あしからず。
Scala のバージョンは 2.12.0, Scala.js のバージョンは 0.6.13 です。
.sjsir と scalajsp
.sjsir は、Scala.js の出力する .class に対応する中間バイナリファイルです。これを Scala.js のリンカが結合した上でゴニョゴニョして最終的な js を出力します。.sjsir にどんな情報が入っているかがちゃんと書いてあるところは見当たらなかったのですが、 scalajsp を使うと見られるらしいので使ってみました。なお、scalajsp はスタンドアローン版 Scala.js をインストールするか、sbt で scalajsp
タスクを実行することで使用できます。ちなみに Scala.js のソースコードでいうとこのあたりに相当する内容が出力されます。
観賞用コード
class
まずクラスです。
@JSExport
class Point(_x: Double, _y: Double) {
@JSExport
val x: Double = _x
@JSExport
var y: Double = _y
@JSExport
def abs: Double = Math.sqrt(x*x + y*y)
@JSExport
def sum: Double = x + y
@JSExport
def sum_=(v: Double): Unit = y = v - x
}
class LPoint extends O {
val x$1: double
var y$1: double
def x__D(): double = {
this.x$1
}
def y__D(): double = {
this.y$1
}
def y$und$eq__D__V(x$1: double) {
this.y$1 = x$1
}
def abs__D(): double = {
mod:jl_Math$.sqrt__D__D(((this.x__D() *[double] this.x__D()) +[double] (this.y__D() *[double] this.y__D())))
}
def sum__D(): double = {
(this.x__D() +[double] this.y__D())
}
def sum$und$eq__D__V(v: double) {
this.y$und$eq__D__V((v -[double] this.x__D()))
}
def $$js$exported$prop$x__O(): any = {
this.x__D()
}
def $$js$exported$prop$y__O(): any = {
this.y__D()
}
def $$js$exported$prop$y__D__O(x$1: double): any = {
this.y$und$eq__D__V(x$1);
(void 0)
}
def $$js$exported$prop$abs__O(): any = {
this.abs__D()
}
def $$js$exported$prop$sum__O(): any = {
this.sum__D()
}
def $$js$exported$prop$sum__D__O(v: double): any = {
this.sum$und$eq__D__V(v);
(void 0)
}
def init___D__D(_x: double, _y: double) {
this.O::init___();
this.x$1 = _x;
this.y$1 = _y
}
get "sum"(): any = {
this.$$js$exported$prop$sum__O()
}
set "sum"(arg$1: any) {
val prep0: double = arg$1.asInstanceOf[D];
this.$$js$exported$prop$sum__D__O(prep0)
}
get "abs"(): any = {
this.$$js$exported$prop$abs__O()
}
get "y"(): any = {
this.$$js$exported$prop$y__O()
}
set "y"(arg$1: any) {
val prep0: double = arg$1.asInstanceOf[D];
this.$$js$exported$prop$y__D__O(prep0)
}
get "x"(): any = {
this.$$js$exported$prop$x__O()
}
export "Point"(arg$1: any, arg$2: any) {
val prep0: double = arg$1.asInstanceOf[D];
val prep1: double = arg$2.asInstanceOf[D];
this.LPoint::init___D__D(prep0, prep1)
}
}
なんかjsっぽさを含んだScalaベースの謎言語のコードが出力されています。最初型情報がほしいと思って見てみたわけですが、直接 export されるメソッドの方は引数も返り値も any になってしまって情報が失われています。丸投げ先のメソッドには残っているようなのでそちらを見ればいいということがわかりました。
ところで今回はソースコードもどきが出力されましたが、 -i
をつけると以下のように上の出力には含まれていないようなメタ情報を見ることができます。
encodedName: LPoint
isExported: true
kind: Class
superClass: Some(O)
methods:
x__D:
y__D:
y$und$eq__D__V:
abs__D:
methodsCalled:
LPoint: [y__D, x__D]
jl_Math$: [sqrt__D__D]
accessedModules: [jl_Math$]
sum__D:
methodsCalled:
LPoint: [y__D, x__D]
sum$und$eq__D__V:
methodsCalled:
LPoint: [y$und$eq__D__V, x__D]
$$js$exported$prop$x__O:
methodsCalled:
LPoint: [x__D]
$$js$exported$prop$y__O:
methodsCalled:
LPoint: [y__D]
$$js$exported$prop$y__D__O:
methodsCalled:
LPoint: [y$und$eq__D__V]
$$js$exported$prop$abs__O:
methodsCalled:
LPoint: [abs__D]
$$js$exported$prop$sum__O:
methodsCalled:
LPoint: [sum__D]
$$js$exported$prop$sum__D__O:
methodsCalled:
LPoint: [sum$und$eq__D__V]
init___D__D:
methodsCalledStatically:
O: [init___]
sum:
isExported: true
methodsCalled:
LPoint: [$$js$exported$prop$sum__D__O, $$js$exported$prop$sum__O]
abs:
isExported: true
methodsCalled:
LPoint: [$$js$exported$prop$abs__O]
y:
isExported: true
methodsCalled:
LPoint: [$$js$exported$prop$y__O, $$js$exported$prop$y__D__O]
x:
isExported: true
methodsCalled:
LPoint: [$$js$exported$prop$x__O]
__exportedInits:
isExported: true
methodsCalledStatically:
LPoint: [init___D__D]
object
オブジェクトです。
@JSExport
object HelloWorld {
@JSExport
def main(): Unit = {
println("Hello world!")
}
}
module class LHelloWorld$ extends O {
def main__V() {
mod:s_Predef$.println__O__V("Hello world!")
}
def $$js$exported$meth$main__O(): any = {
this.main__V();
(void 0)
}
def init___() {
this.O::init___();
mod:LHelloWorld$<-this
}
def "main"(): any = {
this.$$js$exported$meth$main__O()
}
export module "HelloWorld"
}
object
ではなく module class
という扱いのようです。Scalac の内部的に module と呼ばれることがあるからか、それとも js の module から来ているのかちょっとだけ興味が湧くところです。
trait
トレイトです。
trait PointT {
val _x: Double
val _y: Double
@JSExport
val x: Double = _x
@JSExport
var y: Double = _y
@JSExport
def abs: Double = Math.sqrt(x*x + y*y)
@JSExport
def sum: Double = x + y
@JSExport
def sum_=(v: Double): Unit = y = v - x
}
interface LPointT {
def PointT$$undsetter$und$x$und$eq__D__V(x$1: double) <abstract>
def $$undx__D(): double = <abstract>
def $$undy__D(): double = <abstract>
def x__D(): double = <abstract>
def y__D(): double = <abstract>
def y$und$eq__D__V(x$1: double) <abstract>
def abs__D(): double = {
mod:jl_Math$.sqrt__D__D(((this.x__D() *[double] this.x__D()) +[double] (this.y__D() *[double] this.y__D())))
}
def sum__D(): double = {
(this.x__D() +[double] this.y__D())
}
def sum$und$eq__D__V(v: double) {
this.y$und$eq__D__V((v -[double] this.x__D()))
}
def $$js$exported$prop$x__O(): any = {
this.x__D()
}
def $$js$exported$prop$y__O(): any = {
this.y__D()
}
def $$js$exported$prop$y__D__O(x$1: double): any = {
this.y$und$eq__D__V(x$1);
(void 0)
}
def $$js$exported$prop$abs__O(): any = {
this.abs__D()
}
def $$js$exported$prop$sum__O(): any = {
this.sum__D()
}
def $$js$exported$prop$sum__D__O(v: double): any = {
this.sum$und$eq__D__V(v);
(void 0)
}
def $$init$__V() {
this.PointT$$undsetter$und$x$und$eq__D__V(this.$$undx__D());
this.y$und$eq__D__V(this.$$undy__D())
}
}
トレイトは interface という名前になるようです。
@ScalaJSDefined
@JSExport
以外に @ScalaJSDefined
というアノテーションを使うことでより直接的に js のものを定義できる機能もあるのでそれの出力も見てみます。
@ScalaJSDefined
class Foo extends js.Object {
val x: Int = 4
def bar(x: Int): Int = x + 1
}
js class LFoo extends sjs_js_Object {
val "x": int
def "constructor"() {
super();
this["x"] = 4
}
def "bar"(arg$1: any): any = {
val prep0: int = arg$1.asInstanceOf[I];
LFoo::bar__LFoo__I__I(this, prep0)
}
static def bar__LFoo__I__I($this: any, x: int): int = {
(x +[int] 1)
}
}
これは js class
という扱いになるようです。扱いがかなり特殊になっているようです。また、直接的にconstructor
メソッドが作られていたり、丸投げ先のメソッドがstaticになっているなど興味深い点がいくつもあるようです。