| # Interop with native JavaScript | |
| ## Goal | |
| Provide the ways to interact with native JavaScript. | |
| ## Type-safe Declarations | |
| ### Annotation `native` | |
| TODO | |
| ### Annotation `nativeInvoke` | |
| Calls to functions annotated by `nativeInvoke` will be translated to calls of receiver with the arguments provided for original call. | |
| Applicable to: | |
| * member functions of native declarations | |
| * non-member extension functions | |
| Example: | |
| ```kotlin | |
| native | |
| class A { | |
| nativeInvoke | |
| fun invoke(): String = noImpl | |
| nativeInvoke | |
| fun foo(a: Int): Int = noImpl | |
| } | |
| fun A.bar(a: String): Int = noImpl | |
| fun test(baz: A) { | |
| baz() | |
| baz.invoke() | |
| baz.foo(1) | |
| baz.bar("str") | |
| } | |
| ``` | |
| Function `test` will be translated to: | |
| ```js | |
| ... | |
| test: function (baz) { | |
| foo() | |
| foo() | |
| foo(1) | |
| foo("str") | |
| } | |
| ... | |
| ``` | |
| ### Annotation `nativeGetter` | |
| Calls to functions annotated by `nativeGetter` will be translated to square/index operation on the receiver with the argument provided for original call. | |
| Applicable to: | |
| * member functions of native declarations | |
| * non-member extension functions | |
| Requirements: | |
| * must have exactly one argument | |
| * type of the argument must be `String` or subtype of `Number` | |
| * default values are prohibited | |
| * return type must be nullable | |
| Example: | |
| ```kotlin | |
| native | |
| class A { | |
| nativeGetter | |
| fun get(a: String): String? = noImpl | |
| nativeGetter | |
| fun foo(a: Int): Int? = noImpl | |
| } | |
| class B | |
| nativeGetter | |
| fun B.get(a: String): Int? = noImpl | |
| nativeGetter | |
| fun B.bar(a: Int): Int? = noImpl | |
| fun test(a: A, b: B) { | |
| a["foo"] | |
| a.get("bar") | |
| a.foo(1) | |
| b["foo"] | |
| b.get("bar") | |
| b.bar(1) | |
| } | |
| ``` | |
| Function `test` will be translated to: | |
| ```js | |
| ... | |
| test: function (a, b) { | |
| a["foo"] | |
| a["bar"] | |
| a[1] | |
| b["foo"] | |
| b["bar"] | |
| b[1] | |
| } | |
| ... | |
| ``` | |
| ### Annotation `nativeSetter` | |
| Calls of functions annotated by `nativeSetter` will be translated to assignment of the second argument to the receiver | |
| indexed (with square/index operation) with the first argument. | |
| Applicable to: | |
| * member functions of native declarations | |
| * non-member extension functions | |
| Requirements: | |
| * must have exactly two arguments | |
| * type of the first argument must be `String` or subtype of `Number` | |
| * default values are prohibited | |
| * the return type is either `Unit` or a supertype of the second parameter's type | |
| Example: | |
| ```kotlin | |
| native | |
| class A { | |
| nativeSetter | |
| fun set(a: String, v: Any) {} | |
| nativeSetter | |
| fun foo(a: Int, v: A) {} | |
| } | |
| class B | |
| nativeSetter | |
| fun B.set(a: String, v: B) {} | |
| nativeSetter | |
| fun B.bar(a: String, v: B?) {} | |
| fun test(a: A, b: B) { | |
| a["foo"] = "text" | |
| a.set("bar", "value") | |
| a.foo(1, A()) | |
| b["foo"] = B() | |
| b.set("bar", b) | |
| b.bar("a", null) | |
| } | |
| ``` | |
| Function `test` will be translated to: | |
| ```js | |
| ... | |
| test: function (a, b) { | |
| a["foo"] = "text" | |
| a["bar"] = "value" | |
| a[1] = A() | |
| b["foo"] = B() | |
| b["bar"] = b | |
| b["a"] = null | |
| } | |
| ... | |
| ``` | |
| ## Function `js` | |
| Argument of `js` function is parsed as JavaScript code and injected directly into the JavaScript code generated by the compiler. | |
| Requirements: | |
| * the argument should be a compile time constant of type `String` | |
| Example: | |
| ```kotlin | |
| fun test1() { | |
| js("console.log('Hello')") | |
| } | |
| fun test2(a: String) = js(""" | |
| var r = foo(a); | |
| return r; | |
| """) | |
| ``` | |
| is translated to: | |
| ```js | |
| function test1() { | |
| console.log('Hello') | |
| } | |
| function test2(a) { | |
| var r = foo(a); | |
| return r; | |
| } | |
| ``` | |
| ## Dynamic types | |
| All dynamic calls with explicit names (regular and infix function calls, and property calls) are translated "as is", without mangling. | |
| Additionally, many operations when applied to a receiver of type `dynamic` are translated "as is", instead of by convention. | |
| Operations translated "as is" to JavaScript: | |
| * binary: `+`, `-`, `*`, `/`, `%`, `>`, `<` `>=`, `<=`, `==`, `!=`, `===`, `!==`, `&&`, `||` | |
| * unary | |
| * prefix: `-`, `+`, `!` | |
| * prefix and postfix: `++`, `--` | |
| * assignments: `+=`, `-=`, `*=`, `/=`, `%=` | |
| * indexed access: | |
| * read: `d[a]`, more than one argument is an error | |
| * write: `d[a1] = a2`, more than one argument in `[]` is an error | |
| * `in` and `!in` are forbidden, an error is reported | |
| Note: | |
| * `..` is translated to a call to `rangeTo` | |
| * `~`, `|`, `&` and `^` are not supported (there's no Kotlin code that translates to these operations) |