genAdapt
genAdapt
例えば genAdapt でこんなコード書いてる
code:scala
case (_, AnyType) =>
val tmp = addSyntheticLocal(watpe.RefType.anyref)
fb += wa.LocalSet(tmp)
fb.block(watpe.RefType.anyref) { labelDone =>
fb += wa.LocalGet(tmp)
fb += wa.BrOnCastFail(
labelDone,
watpe.RefType.anyref,
watpe.RefType(genTypeID.i16Array)
)
fb += wa.Call(genFunctionID.createStringFromData)
}
expected type が AnyType の場合は、string を JS string に変換する処理を挟んでる。なぜなら JS Interop なんかで、JS string が欲しいときに genTree(..., Any) みたいな処理結構やってるから。
しかし 単純に`"文字列".asInstanceOf Any] すると JS 文字列に変換されちゃうのってどうなの?
そういう JS interop 向けには genJSTree みたいなの作って、case (_, AnyType) => の処理はそっちに持っていったほうが良いのでは? そうすれば "文字列".asInstanceOf[Any] が意図せず JS 文字列に変換されることもない。
そういうわけにもいかなくて、js.Any#fromString が Any 型に変換するだけで JS String として文字列をみなすみたいなコード書いている。そしてこれは JS interop に関わるところではないの genJSTree は呼び出されないでしょう
code:scala
@inline implicit def fromString(s: String): js.Any =
こうなる
code:lisp
(func $f.scala.scalajs.js.Any$.fromString_Ljava.lang.String_Lscala.scalajs.js.Any (type $365)
(param $this (ref $c.scala.scalajs.js.Any$)) (param $s (ref null $i16Array)) (result anyref)
(local $1 anyref)
local.get $s
local.set $1
block $1 (result anyref)
local.get $1
br_on_cast_fail $1 anyref (ref $i16Array)
call $createStringFromData
end)
あれ、なんか (StringType, AnyType) のときだけ こういう処理挟めばよいのでは?
と思ったけど、"foo".isInstanceOf[...] すると isInstanceOf は Any のメソッドなのでか expectedType が Any になっちゃう。その結果別に i16array を渡してくれればいいのに JS string に変換されてしまう
そもそも genAdapt で JS String に変換しなかった場合どうなる?
code:sh
TypeError: Cannot convert object to primitive value
at jsMethodApply (file:///Users/tanishiking/ghq/github.com/tanishiking/scala-js/examples/helloworld/.2.12/target/scala-2.12/helloworld-fastopt/__loader.js:166:35)
at f.java.lang.String.substring;I;I;Ljava.lang.String (wasm://wasm/0008daa6:1:57241)
at f.java.lang.JSConsoleBasedPrintStream.java$lang$JSConsoleBasedPrintStream$$printString;Ljava.lang.String;V (wasm://wasm/0008daa6:1:60115)
at f.java.lang.JSConsoleBasedPrintStream.println;Ljava.lang.Object;V (wasm://wasm/0008daa6:1:59985)
at f.scala.Console$.println;Ljava.lang.Object;V (wasm://wasm/0008daa6:1:50917)
at f.scala.Predef$.println;Ljava.lang.Object;V (wasm://wasm/0008daa6:1:51095)
at f.helloworld.HelloWorld$.main;[Ljava.lang.String;V (wasm://wasm/0008daa6:1:32029)
at s.helloworld.HelloWorld.main;[Ljava.lang.String;V (wasm://wasm/0008daa6:1:31997)
at start (wasm://wasm/0008daa6:1:72377)
とりあえず こんな
code:diff
diff --git a/linker/shared/src/main/scala/org/scalajs/linker/backend/wasmemitter/FunctionEmitter.scala b/linker/shared/src/main/scala/org/scalajs/linker/backend/wasmemitter/FunctionEmitter.scala
index 34e362104..8748bbe5f 100644
--- a/linker/shared/src/main/scala/org/scalajs/linker/backend/wasmemitter/FunctionEmitter.scala
+++ b/linker/shared/src/main/scala/org/scalajs/linker/backend/wasmemitter/FunctionEmitter.scala
@@ -333,7 +333,27 @@ private class FunctionEmitter private (
def genTreeAuto(tree: Tree): Unit =
genTree(tree, tree.tpe)
+ def genJSTree(tree: Tree, expectedType: Type): Unit = {
+ val generatedType = genTree0(tree, expectedType)
+ val tmp = addSyntheticLocal(watpe.RefType.anyref)
+ fb += wa.LocalSet(tmp)
+ fb.block(watpe.RefType.anyref) { labelDone =>
+ fb += wa.LocalGet(tmp)
+ fb += wa.BrOnCastFail(
+ labelDone,
+ watpe.RefType.anyref,
+ watpe.RefType(genTypeID.i16Array)
+ )
+ fb += wa.Call(genFunctionID.createStringFromData)
+ }
+ }
+
def genTree(tree: Tree, expectedType: Type): Unit = {
+ val generatedType = genTree0(tree, expectedType)
+ genAdapt(generatedType, expectedType)
+ }
+
+ def genTree0(tree: Tree, expectedType: Type): Type = {
val generatedType: Type = tree match {
case t: Literal => genLiteral(t, expectedType)
case t: UnaryOp => genUnaryOp(t)
@@ -408,7 +428,8 @@ private class FunctionEmitter private (
throw new AssertionError(s"Invalid tree: $tree")
}
- genAdapt(generatedType, expectedType)
+ generatedType
+ // genAdapt(generatedType, expectedType)
}
private def genAdapt(generatedType: Type, expectedType: Type): Unit = {
@@ -444,18 +465,18 @@ private class FunctionEmitter private (
*/
fb += wa.Call(genFunctionID.box(primType.primRef))
}
- case (_, AnyType) =>
- val tmp = addSyntheticLocal(watpe.RefType.anyref)
- fb += wa.LocalSet(tmp)
- fb.block(watpe.RefType.anyref) { labelDone =>
- fb += wa.LocalGet(tmp)
- fb += wa.BrOnCastFail(
- labelDone,
- watpe.RefType.anyref,
- watpe.RefType(genTypeID.i16Array)
- )
- fb += wa.Call(genFunctionID.createStringFromData)
- }
+ // case (_, AnyType) =>
+ // val tmp = addSyntheticLocal(watpe.RefType.anyref)
+ // fb += wa.LocalSet(tmp)
+ // fb.block(watpe.RefType.anyref) { labelDone =>
+ // fb += wa.LocalGet(tmp)
+ // fb += wa.BrOnCastFail(
+ // labelDone,
+ // watpe.RefType.anyref,
+ // watpe.RefType(genTypeID.i16Array)
+ // )
+ // fb += wa.Call(genFunctionID.createStringFromData)
+ // }
case _ =>
()
@@ -2293,8 +2314,8 @@ private class FunctionEmitter private (
private def genJSMethodApply(tree: JSMethodApply): Type = {
val JSMethodApply(receiver, method, args) = tree
- genTree(receiver, AnyType)
- genTree(method, AnyType)
+ genJSTree(receiver, AnyType)
+ genJSTree(method, AnyType)
genJSArgsArray(args)
markPosition(tree)
fb += wa.Call(genFunctionID.jsMethodApply)
@@ -2455,10 +2476,10 @@ private class FunctionEmitter private (
for (arg <- args) {
arg match {
case arg: Tree =>
- genTree(arg, AnyType)
+ genJSTree(arg, AnyType)
fb += wa.Call(genFunctionID.jsArrayPush)
case JSSpread(items) =>
- genTree(items, AnyType)
+ genJSTree(items, AnyType)
fb += wa.Call(genFunctionID.jsArraySpreadPush)
}
}
どうなる?
ああ、そもそも boxing やらなくなっちゃったから...
こうしてみたら
code:scala
def genJSTree(tree: Tree, expectedType: Type): Unit = {
val generatedType = genTree0(tree, expectedType)
if (generatedType == StringType) {
val tmp = addSyntheticLocal(watpe.RefType.anyref)
fb += wa.LocalSet(tmp)
fb.block(watpe.RefType.anyref) { labelDone =>
fb += wa.LocalGet(tmp)
fb += wa.BrOnCastFail(
labelDone,
watpe.RefType.anyref,
watpe.RefType(genTypeID.i16Array)
)
fb += wa.Call(genFunctionID.createStringFromData)
}
} else {
genAdapt(generatedType, expectedType)
}
}
code:sh
TypeError: om is not a function at jsMethodApply (file:///Users/tanishiking/ghq/github.com/tanishiking/scala-js/examples/helloworld/.2.12/target/scala-2.12/helloworld-fastopt/__loader.js:166:38)
at f.java.lang.String.substring;I;I;Ljava.lang.String (wasm://wasm/0008dd0a:1:57360)
at f.java.lang.JSConsoleBasedPrintStream.java$lang$JSConsoleBasedPrintStream$$printString;Ljava.lang.String;V (wasm://wasm/0008dd0a:1:60234)
at f.java.lang.JSConsoleBasedPrintStream.println;Ljava.lang.Object;V (wasm://wasm/0008dd0a:1:60104)
at f.scala.Console$.println;Ljava.lang.Object;V (wasm://wasm/0008dd0a:1:51002)
at f.scala.Predef$.println;Ljava.lang.Object;V (wasm://wasm/0008dd0a:1:51180)
at f.helloworld.HelloWorld$.main;[Ljava.lang.String;V (wasm://wasm/0008dd0a:1:32029)
at s.helloworld.HelloWorld.main;[Ljava.lang.String;V (wasm://wasm/0008dd0a:1:31997)
at start (wasm://wasm/0008dd0a:1:72530)
何?
if (generatedType == StringType || generatedType == ClassType(BoxedStringClass)) { こうしてみたら?
動かなくなった、何故????
なんか止まるのは
code:scala
val aaa = js.Any.fromString("aaa")
println("Hello world!")
println(aaa)
こういう感じで js.Any.fromString が関わってるときっぽいな
aaa は js.Any になるから generatedType がどうしても Any になる? String のときだけ JS string に変換するやり方だと Any に変換した 文字列が 変換をすり抜けてしまう? (だからってなんで stuck する? i16Array を JS にわたそうとしたら opaque なんだから cannnot convert to primitive type だのなんだの言って欲しいが...)
code:scala
def genJSTree(tree: Tree, expectedType: Type): Unit = {
val generatedType = genTree0(tree, expectedType)
val tmp = addSyntheticLocal(watpe.RefType.anyref)
fb += wa.LocalSet(tmp)
fb.block(watpe.RefType.anyref) { labelDone =>
fb.block(watpe.RefType.anyref) { nonWasmString =>
fb += wa.LocalGet(tmp)
fb += wa.BrOnCastFail(
nonWasmString,
watpe.RefType.anyref,
watpe.RefType(genTypeID.i16Array)
)
fb += wa.Call(genFunctionID.createStringFromData)
fb += wa.Br(labelDone)
}
genAdapt(generatedType, expectedType)
}
}
じゃあ実行時の型がi16arrayなら変換して、そうでないならgenAdaptするようにすれば?
code:scala
.FloatingPointBits$.makePowsOf2;I;D;..." failed: local.set0 expected type anyref, found local.get of type i32 @+32362] あーこのエラーって genTree0 の結果がそもそも i32 とかだったりすると、tmp の型とマッチしないんだ。
先に genAdaptする?
これでどうか
code:scala
def genJSTree(tree: Tree, expectedType: Type): Unit = {
val generatedType = genTree0(tree, expectedType)
val tmp = addSyntheticLocal(watpe.RefType.anyref)
fb += wa.LocalSet(tmp)
fb.block(watpe.RefType.anyref) { labelDone =>
fb.block(watpe.RefType.anyref) { nonWasmString =>
fb += wa.LocalGet(tmp)
fb += wa.BrOnCastFail(
nonWasmString,
watpe.RefType.anyref,
watpe.RefType(genTypeID.i16Array)
)
fb += wa.Call(genFunctionID.createStringFromData)
fb += wa.Br(labelDone)
}
genAdapt(generatedType, expectedType)
}
}
code:scala
TypeError: Cannot convert object to primitive value
at jsSelect (file:///Users/tanishiking/ghq/github.com/tanishiking/scala-js/examples/helloworld/.2.12/target/scala-2.12/helloworld-fastopt/__loader.js:162:24)
at f.java.lang.Class.isPrimitive;Z (wasm://wasm/0008e39a:1:51361)
at f.helloworld.HelloWorld$.main;[Ljava.lang.String;V (wasm://wasm/0008e39a:1:32000)
at s.helloworld.HelloWorld.main;[Ljava.lang.String;V (wasm://wasm/0008e39a:1:31976)
at start (wasm://wasm/0008e39a:1:73023)
はい。まあ
code:scala
val a = classOfInt.isPrimitive() val b = "foo".isInstanceOfString val c = classOfString.isInstance("foo") val d = "foo".asInstanceOfString == "foo" こっちに対してのエラー、まだjsSelectとかはやってないもんね
code:scala
val aaa = js.Any.fromString("aaa")
println("Hello world!")
println(aaa)
これを実行
HelloWorldのあと止まる。なんやねん
code:wasm
(func $f.helloworld.HelloWorld$.main__Ljava.lang.String_V (type $141)
(param $this (ref $c.helloworld.HelloWorld$)) (param $args (ref null $ObjectArray))
(local $aaa anyref) (local $a i32) (local $b i32) (local $c i32) (local $d i32)
call $m.scala.scalajs.js.Any$
ref.as_non_null
i32.const 556
i32.const 3
i32.const 26
call $stringLiteral
call $f.scala.scalajs.js.Any$.fromString_Ljava.lang.String_Lscala.scalajs.js.Any
local.set $aaa ;; ref null i16array だけど anyref
;; println("Hello World")
call $m.scala.Predef$
ref.as_non_null
local.get $aaa
call $f.scala.Predef$.println_Ljava.lang.Object_V
...
code:wasm
(func $f.scala.scalajs.js.Any$.fromString_Ljava.lang.String_Lscala.scalajs.js.Any (type $363)
(param $this (ref $c.scala.scalajs.js.Any$)) (param $s (ref null $i16Array)) (result anyref)
local.get $s)
code:scala
(func $f.scala.Predef$.println_Ljava.lang.Object_V (type $334)
(param $this (ref $c.scala.Predef$)) (param $x anyref)
call $m.scala.Console$
ref.as_non_null
local.get $x
call $f.scala.Console$.println_Ljava.lang.Object_V)
code:scala
(func $f.scala.Console$.println_Ljava.lang.Object_V (type $331)
(param $this (ref $c.scala.Console$)) (param $x anyref)
(local $1 (ref $c.java.io.PrintStream))
local.get $this
ref.as_non_null
call $f.scala.Console$.out_Ljava.io.PrintStream
ref.as_non_null
local.tee $1
local.get $x
local.get $1
struct.get $c.java.io.PrintStream $vtable
struct.get $v.java.io.PrintStream $m.java.io.PrintStream.println_Ljava.lang.Object_V
call_ref $56)
$obj は i16array
code:lisp
(func $f.java.lang.JSConsoleBasedPrintStream.println_Ljava.lang.Object_V (type $526)
(param $this (ref $c.java.lang.JSConsoleBasedPrintStream)) (param $obj anyref)
(local $1 (ref $c.java.lang.Object))
local.get $this
ref.as_non_null
block $1 (result (ref $i16Array))
block $2 (result anyref)
block $3 (result anyref)
local.get $obj
br_on_cast_fail $3 anyref (ref $c.java.lang.Object) ;; fail するはず
local.tee $1
local.get $1
struct.get $c.java.lang.Object $vtable
struct.get $v.java.lang.Object $m.java.lang.Object.toString_Ljava.lang.String
call_ref $3
br_on_non_null $1
ref.null any
end ;; ここに飛ぶ
br_on_cast_fail $2 anyref (ref $i16Array) ;; これ通ると思うんだけどどうなんだろう? ref null i16array じゃないと cast できない?
;; べつに wasmStringConcat は nullable も受け付けるから ref i16array が non-null である保証はここでは不要
;; まあでもこっちは問題なさそう (なぜなら下記で問題が発生したから
br $1
end
;; もし cast fail したらこっちに飛んで...
call $jsValueToStringForConcat
call $createArrayFromJSString
ref.as_non_null
end
i32.const 6166
i32.const 1
i32.const 168
call $stringLiteral
call $wasmStringConcat ;; 怪しさはある、開始と終わりにdebug print仕込んでみよう
call $f.java.lang.JSConsoleBasedPrintStream.java$lang$JSConsoleBasedPrintStream$$printString_Ljava.lang.String_V)
こう書き換えて実行
code:diff
diff --git a/linker/shared/src/main/scala/org/scalajs/linker/backend/wasmemitter/CoreWasmLib.scala b/linker/shared/src/main/scala/org/scalajs/linker/backend/wasmemitter/CoreWasmLib.scala
index 4b8bc2a28..818e4d548 100644
--- a/linker/shared/src/main/scala/org/scalajs/linker/backend/wasmemitter/CoreWasmLib.scala
+++ b/linker/shared/src/main/scala/org/scalajs/linker/backend/wasmemitter/CoreWasmLib.scala
@@ -2330,6 +2330,9 @@ object CoreWasmLib {
val dest = fb.addLocal("result", RefType(i16Array))
fb.setResultType(RefType(i16Array))
+ fb ++= ctx.stringPool.getConstantJSStringInstr("start")
+ fb += Call(genFunctionID.print)
+
fb += LocalGet(arr1Param)
fb += RefIsNull
fb.ifThenElse(RefType(i16Array)) {
@@ -2378,6 +2381,9 @@ object CoreWasmLib {
fb += LocalGet(dest)
+ fb ++= ctx.stringPool.getConstantJSStringInstr("end")
+ fb += Call(genFunctionID.print)
+
fb.buildAndAddToModule()
}
}
う、うわー無限に 呼び出されている!
なんか実装が無限に呼び出されてるのかと思って別な実装用意してみた
code:wasm
private def genWasmStringConcat()(implicit ctx: WasmContext): Unit = {
import VarGen.genTypeID.i16Array
val fb = newFunctionBuilder(genFunctionID.wasmStringConcat)
val arr1Param = fb.addParam("arr1param", RefType.nullable(i16Array))
val arr2Param = fb.addParam("arr2param", RefType.nullable(i16Array))
val arr1 = fb.addLocal("arr1", RefType(i16Array))
val arr2 = fb.addLocal("arr2", RefType(i16Array))
// val dest = fb.addLocal("result", RefType(i16Array))
fb.setResultType(RefType(i16Array))
fb ++= ctx.stringPool.getConstantJSStringInstr("start")
fb += Call(genFunctionID.print)
fb += LocalGet(arr1Param)
fb += RefIsNull
fb.ifThenElse(RefType(i16Array)) {
fb ++= ctx.stringPool.getConstantStringInstr("null")
} {
fb += LocalGet(arr1Param)
fb += RefAsNonNull
}
fb += LocalSet(arr1)
fb += LocalGet(arr2Param)
fb += RefIsNull
fb.ifThenElse(RefType(i16Array)) {
fb ++= ctx.stringPool.getConstantStringInstr("null")
} {
fb += LocalGet(arr2Param)
fb += RefAsNonNull
}
fb += LocalSet(arr2)
fb += LocalGet(arr1)
fb += Call(genFunctionID.createJSStringFromArray)
fb += LocalGet(arr2)
fb += Call(genFunctionID.createJSStringFromArray)
fb += Call(genFunctionID.stringConcat)
fb += Call(genFunctionID.createArrayFromJSString)
fb += RefAsNonNull
fb ++= ctx.stringPool.getConstantJSStringInstr("end")
fb += Call(genFunctionID.print)
fb.buildAndAddToModule()
}
けど結果は一緒で無限に呼び出されている
そうせ wasmStringConcat 呼び出してるの一家っしょなんで、なにの文字列を結合で無限ループしてるのか
code:scala
private def genStringConcat(tree: BinaryOp): Type = {
val BinaryOp(op, lhs, rhs) = tree
assert(op == BinaryOp.String_+)
lhs match {
case StringLiteral("") =>
// Common case where we don't actually need a concatenation
genToStringForConcat(rhs)
case _ =>
val lhs0 = addSyntheticLocal(watpe.RefType.nullable(genTypeID.i16Array))
val rhs0 = addSyntheticLocal(watpe.RefType.nullable(genTypeID.i16Array))
genToStringForConcat(lhs)
fb += wa.LocalTee(lhs0)
genToStringForConcat(rhs)
fb += wa.LocalTee(rhs0)
markPosition(tree)
fb += wa.LocalGet(lhs0)
fb += RefAsNonNull
fb += wa.Call(genFunctionID.createJSStringFromArray)
fb += wa.Call(genFunctionID.print)
fb += wa.LocalGet(rhs0)
fb += RefAsNonNull
fb += wa.Call(genFunctionID.createJSStringFromArray)
fb += wa.Call(genFunctionID.print)
fb += wa.Call(genFunctionID.wasmStringConcat)
}
code:sh
"Hello world!"
"\n"
"start"
"end"
""
"Hello world!"
"start"
"end"
Hello world!
""
""
"start"
"end"
...
空文字のループ
なんかおかしくないか? HelloWorld の次は "aaa" と "\n" の結合になるはずでは?
なんか Any.fromString は関係なくて 普通に println("aaa") でも無限ループするね
この先 $f.java.lang.JSConsoleBasedPrintStream.java$lang$JSConsoleBasedPrintStream$$printString_Ljava.lang.String_V を見てみよう
本のコードはこれ
code:scala
private def printString(s: String): Unit = {
var rest: String = s
while (rest != "") {
val nlPos = rest.indexOf("\n")
if (nlPos < 0) {
buffer += rest
flushed = false
rest = ""
} else {
doWriteLine(buffer + rest.substring(0, nlPos))
buffer = ""
flushed = true
rest = rest.substring(nlPos+1)
}
}
}
code:lisp
(func $f.java.lang.JSConsoleBasedPrintStream.java$lang$JSConsoleBasedPrintStream$$printString_Ljava.lang.String_V (type $518)
(param $this (ref $c.java.lang.JSConsoleBasedPrintStream)) (param $s (ref null $i16Array))
(local $rest (ref null $i16Array)) (local $nlPos i32) (local $1 (ref null $i16Array)) (local $2 (ref null $i16Array)) (local $3 (ref null $i16Array)) (local $4 (ref null $i16Array)) (local $5 (ref null $i16Array)) (local $6 (ref null $i16Array)) (local $7 (ref null $i16Array)) (local $8 (ref null $i16Array))
local.get $s
local.set $rest ;; "aaa\n"
loop $1
local.get $rest
i32.const 2396
i32.const 0 ;; "" か?
i32.const 82
call $stringLiteral
call $is ;; なんですかこれは!!!! true (1) になるわけない
i32.eqz ;; rest != "" ですね
if ;; 当然こっち
local.get $rest
i32.const 6110
i32.const 1
i32.const 167
call $stringLiteral
call $f.java.lang.String.indexOf_Ljava.lang.String_I
local.set $nlPos
local.get $nlPos
i32.const 0
i32.lt_s
if
local.get $this
ref.as_non_null
local.get $this
ref.as_non_null
call $p.java.lang.JSConsoleBasedPrintStream.buffer_Ljava.lang.String
local.tee $3
local.get $3
ref.as_non_null
call $createJSStringFromArray
call $print
local.tee $1
local.get $rest
local.tee $4
local.get $4
ref.as_non_null
call $createJSStringFromArray
call $print
local.tee $2
local.get $1
ref.as_non_null
call $createJSStringFromArray
call $print
local.get $2
ref.as_non_null
call $createJSStringFromArray
call $print
call $wasmStringConcat
call $p.java.lang.JSConsoleBasedPrintStream.buffer_$eq_Ljava.lang.String_V
local.get $this
ref.as_non_null
i32.const 0
call $p.java.lang.JSConsoleBasedPrintStream.flushed_$eq_Z_V
i32.const 2396
i32.const 0
i32.const 82
call $stringLiteral
local.set $rest
else
local.get $this
ref.as_non_null
local.get $this
ref.as_non_null
call $p.java.lang.JSConsoleBasedPrintStream.buffer_Ljava.lang.String
local.tee $7
local.get $7
ref.as_non_null
call $createJSStringFromArray
call $print
local.tee $5
local.get $rest
i32.const 0
local.get $nlPos
call $f.java.lang.String.substring_I_I_Ljava.lang.String
local.tee $8
local.get $8
ref.as_non_null
call $createJSStringFromArray
call $print
local.tee $6
local.get $5
ref.as_non_null
call $createJSStringFromArray
call $print
local.get $6
ref.as_non_null
call $createJSStringFromArray
call $print
call $wasmStringConcat
call $p.java.lang.JSConsoleBasedPrintStream.doWriteLine_Ljava.lang.String_V
local.get $this
ref.as_non_null
i32.const 2396
i32.const 0
i32.const 82
call $stringLiteral
call $p.java.lang.JSConsoleBasedPrintStream.buffer_$eq_Ljava.lang.String_V
local.get $this
ref.as_non_null
i32.const 1
call $p.java.lang.JSConsoleBasedPrintStream.flushed_$eq_Z_V
local.get $rest
local.get $nlPos
i32.const 1
i32.add
call $f.java.lang.String.substring_I_Ljava.lang.String
local.set $rest
end
br $1
end
end)
ああ、通りで無限ループするわけですね...
Object.is が生成されるのは
genEq
genMatch
genApplyWithDispatch の
code:scala
/* It must be a method of j.l.Object and it can be any value.
* hashCode() and equals() are overridden in all hijacked classes.
* We use identityHashCode for hashCode and Object.is for equals,
* as they coincide with the respective specifications (on purpose).
* The other methods are never overridden and can be statically
* resolved to j.l.Object.
*/
pushArgs(argsLocals)
methodName match {
case SpecialNames.hashCodeMethodName =>
fb += wa.Call(genFunctionID.identityHashCode)
case equalsMethodName =>
fb += wa.Call(genFunctionID.is)
case _ =>
genHijackedClassCall(ObjectClass)
}
とりあえず genEq をこういう感じに変更
code:scala
private def genEq(tree: BinaryOp): Type = {
import BinaryOp.{===, !==}
val BinaryOp(op, lhs, rhs) = tree
assert(op == === || op == !==)
// TODO Optimize this when the operands have a better type than any
if (tree.lhs.tpe == StringType || tree.lhs.tpe == ClassType(BoxedStringClass)) {
genJSTree(lhs, AnyType)
genJSTree(rhs, AnyType)
markPosition(tree)
fb += wa.Call(genFunctionID.is)
} else {
genTree(lhs, AnyType)
genTree(rhs, AnyType)
markPosition(tree)
fb += wa.Call(genFunctionID.is)
}
if (op == !==)
fb += wa.I32Eqz
BooleanType
}
hr.icon
もともと block の return type を anyref にしていたけれど、それだと実際に block が返すべきだった型が anyref に upcast されてしまい、型チェック失敗
なので block の return type を 生成された型に合わせてみた...
しかし今度は ref null i16array の場合に 方は createStringFromData によって anyref になっちゃう
generatedType が i16array のときだけこの変換処理挟めば良かったりする?
ていうかもしそうなら実行時型検査いらなくない? なんでやってるんだっけ...
もともと expectedType == AnyType のときに i16 array を js string に変換したかった。
なぜか generatedType の型を見るんじゃなくて、実行時検査している。何故?
あ〜実体は string だけど Any に upcast した場合とか?
なぜなら js.Any.froString など? なんか問題にならなそう??? だし、別にコンパイル時の肩でいいんじゃないか?
なんか console.log の log の部分が JS 文字列にならないんだが~?
単に refType == watpe.RefType(genTypeID.i16Array) がだめで
case refType @ watpe.Reftype(true, watpe.HeapType.Type(genTypeID.i16Array)) がokだった
code:scala
def genJSTree(tree: Tree, expectedType: Type): Unit = {
val generatedType = genTree0(tree, expectedType)
val newGeneratedType = genAdapt(generatedType, expectedType)
TypeTransformer.transformType(newGeneratedType) match {
case refType @ watpe.RefType(true, watpe.HeapType.Type(genTypeID.i16Array)) =>
fb += wa.RefAsNonNull
fb += wa.Call(genFunctionID.createStringFromData)
case _: watpe.SimpleType => // do nothing, it shouldn't be a JS string
case _ =>
}
}
hr.icon
js.Object のフィールドに現れるString型はどうする?
code:scala
def makeLocalJSClass(yyy: String): js.Dynamic = {
class LocalJSClass(abc: String) extends js.Object {
val zzz: String = xxx + yyy + abc
def foo(a: String): String = xxx + yyy + abc + a
}
}
makeLocalJS
code:lisp
(func $f.helloworld.NestedJS$ScalaClassContainer.makeLocalJSClass_Ljava.lang.String_Lscala.scalajs.js.Dynamic (type $161)
(param $this (ref $c.helloworld.NestedJS$ScalaClassContainer)) (param $yyy (ref null $i16Array)) (result anyref)
(local $LocalJSClass$jsname1 anyref)
i32.const 1454
i32.const 6
i32.const 62
call $stringLiteral ;; i16Arreay
call $createJSStringFromArray ;; ref any
call $jsGlobalRefGet ;; (ref any) -> anyref / anyref local.get $this ;; (ref container)
local.get $yyy ;; ref null i16array
ref.as_non_null ;; ref i16Array
call $createStringFromData ;; ref any
;; arguments は refany になってる
;; けど js.Object のコンストラクタの引数は i16array なんだよなぁ
call $c.helloworld.NestedJS$ScalaClassContainer$LocalJSClass$1 ;; anyref, ref null container, ref null i16array
local.set $LocalJSClass$jsname1
local.get $LocalJSClass$jsname1)
理想的には js.Object のフィールドの型も anyref になってるべきか (not i16array)
フィールドは
code:scala
private def genJSClass(clazz: LinkedClass)(implicit ctx: WasmContext): Unit = {
assert(clazz.kind.isJSClass)
// Define the globals holding the Symbols of private fields
for (fieldDef <- clazz.fields) {
fieldDef match {
case FieldDef(flags, name, _, _) if !flags.namespace.isStatic =>
ctx.addGlobal(
wamod.Global(
genGlobalID.forJSPrivateField(name.name),
makeDebugName(ns.PrivateJSField, name.name),
isMutable = true,
watpe.RefType.anyref,
wa.Expr(List(wa.RefNull(watpe.HeapType.Any)))
)
)
case _ =>
()
}
}
フィールドの型はなぁ大丈夫なんだよmなぁ
ClassEmitter.scala genCreateJSClassFunction
FunctionEmitter.scala emitJSConstructorFunctions
このなかで普通に prepareEmitter やってるけど、これだとコンストラクタのパラメータは普通の処理されますね〜
JS Class だったら String は i16Array じゃなくて、anyref になってもらわないと
なぜなら array は JS に対して opaque だから
TypeTransoformer を JS 版 (String -> anyref) と Wasm版 (String -> i16array) を実装
JSClass は JS版を使うようにした
今度は String Concat などの実装を array だった場合と JS string だった場合とで分岐しないといけない...辛い
そもそも文字列の representation が複数あるのつらすぎる
hr.icon
そもそも JS interoperability と JS String の共存は何が目的なんだったっけ
現状の JS interop 実装に JS string がめちゃ使われているので、JS String を削除する = JS interop が使えなくなる
しかし、そうなると大部分のプログラムが動かなくなって厳しい、test-suite の大部分も使えなくなるのは辛いって
そうなの?
JS interop なかったときにどこまで動くのかまず調べたほうがいいんじゃない?
jsSelect がなくなった時点で linkingInfo.esVersion とかが使えなくなるので厳しい...
うーん js.Object だけ廃止とかできる? 厳しそうだ
やっぱり複数の string representation もちつつちょっとずつ wasm 実装に寄せていくのが良いのかな?