From 76bafb9634376067ed2cc9a1a3419a9e2c713ae4 Mon Sep 17 00:00:00 2001 From: Zalim Bashorov Date: Tue, 2 Dec 2014 23:53:37 +0300 Subject: [PATCH] Added spec about JavaScript interop. --- spec-docs/javascript-interop.md | 226 ++++++++++++++++++++++++++++++++ 1 file changed, 226 insertions(+) create mode 100644 spec-docs/javascript-interop.md diff --git a/spec-docs/javascript-interop.md b/spec-docs/javascript-interop.md new file mode 100644 index 00000000000..fe6539a950b --- /dev/null +++ b/spec-docs/javascript-interop.md @@ -0,0 +1,226 @@ +# 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) \ No newline at end of file