[JS IR BE] support more constructions:

* generate declaration for top level properties
* basic support for calls
* break
* continue
* throw
* generate declaration for local vals and vars
This commit is contained in:
Zalim Bashorov
2018-03-19 17:06:22 +03:00
parent 560e35174b
commit 68457a55a8
9 changed files with 124 additions and 63 deletions
@@ -6,7 +6,7 @@
package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs
import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.backend.js.TODO import org.jetbrains.kotlin.ir.backend.js.utils.TODO
import org.jetbrains.kotlin.ir.declarations.IrTypeAlias import org.jetbrains.kotlin.ir.declarations.IrTypeAlias
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
import org.jetbrains.kotlin.js.backend.ast.JsEmpty import org.jetbrains.kotlin.js.backend.ast.JsEmpty
@@ -5,10 +5,15 @@
package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs
import org.jetbrains.kotlin.ir.declarations.IrProperty
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.js.backend.ast.* import org.jetbrains.kotlin.js.backend.ast.*
class IrDeclarationToJsTransformer : BaseIrElementToJsNodeTransformer<JsStatement, Nothing?> { class IrDeclarationToJsTransformer : BaseIrElementToJsNodeTransformer<JsStatement, Nothing?> {
override fun visitProperty(declaration: IrProperty, data: Nothing?): JsStatement {
return jsVar(declaration.name, declaration.backingField?.initializer?.expression)
}
override fun visitSimpleFunction(declaration: IrSimpleFunction, data: Nothing?): JsStatement { override fun visitSimpleFunction(declaration: IrSimpleFunction, data: Nothing?): JsStatement {
return JsExpressionStatement(transformIrFunctionToJsFunction(declaration)) return JsExpressionStatement(transformIrFunctionToJsFunction(declaration))
} }
@@ -16,9 +21,9 @@ class IrDeclarationToJsTransformer : BaseIrElementToJsNodeTransformer<JsStatemen
private fun transformIrFunctionToJsFunction(declaration: IrSimpleFunction): JsFunction { private fun transformIrFunctionToJsFunction(declaration: IrSimpleFunction): JsFunction {
val funName = declaration.name.asString() val funName = declaration.name.asString()
val body = declaration.body?.accept(IrElementToJsStatementTransformer(), null) as? JsBlock ?: JsBlock() val body = declaration.body?.accept(IrElementToJsStatementTransformer(), null) as? JsBlock ?: JsBlock()
val function = JsFunction(JsFunctionScope(JsDynamicScope, "scope for $funName"), body, "function $funName") val function = JsFunction(JsFunctionScope(dummyScope, "scope for $funName"), body, "function $funName")
function.name = JsDynamicScope.declareName(funName) function.name = declaration.name.toJsName()
fun JsFunction.addParameter(parameterName: String) { fun JsFunction.addParameter(parameterName: String) {
val parameter = function.scope.declareName(parameterName) val parameter = function.scope.declareName(parameterName)
@@ -5,12 +5,60 @@
package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs
import org.jetbrains.kotlin.ir.backend.js.utils.isPrimary
import org.jetbrains.kotlin.ir.backend.js.utils.parameterCount
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrConst import org.jetbrains.kotlin.ir.expressions.IrConst
import org.jetbrains.kotlin.js.backend.ast.JsExpression import org.jetbrains.kotlin.ir.expressions.IrExpressionBody
import org.jetbrains.kotlin.js.backend.ast.JsStringLiteral import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol
import org.jetbrains.kotlin.js.backend.ast.*
class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsExpression, Nothing?> { class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsExpression, Nothing?> {
override fun visitExpressionBody(body: IrExpressionBody, data: Nothing?): JsExpression {
return body.expression.accept(this, data)
}
override fun <T> visitConst(expression: IrConst<T>, data: Nothing?): JsExpression { override fun <T> visitConst(expression: IrConst<T>, data: Nothing?): JsExpression {
// TODO: support all cases
return JsStringLiteral(expression.value.toString()) return JsStringLiteral(expression.value.toString())
} }
override fun visitCall(expression: IrCall, data: Nothing?): JsExpression {
// TODO rewrite more accurately, right now it just copy-pasted and adopted from old version
// TODO support:
// * ir intrinsics
// * js be intrinsics
// * js function
// * getters and setters
// * binary and unary operations
val symbol = expression.symbol
val dispatchReceiver = expression.dispatchReceiver?.accept(this, data)
val extensionReceiver = expression.extensionReceiver?.accept(this, data)
// TODO sanitize name
val symbolName = (symbol.owner as IrSimpleFunction).name.asString()
val ref = if (dispatchReceiver != null) JsNameRef(symbolName, dispatchReceiver) else JsNameRef(symbolName)
val arguments =
// TODO mapTo?
(0 until expression.symbol.parameterCount).map {
val argument = expression.getValueArgument(it)
if (argument != null) {
argument.accept(this, data)
} else {
JsPrefixOperation(JsUnaryOperator.VOID, JsIntLiteral(1))
}
}
if (symbol is IrConstructorSymbol && symbol.isPrimary) {
return JsNew(JsNameRef((symbol.owner.parent as IrClass).name.asString()), arguments)
}
return JsInvocation(ref, extensionReceiver?.let { listOf(extensionReceiver) + arguments } ?: arguments)
}
} }
@@ -5,13 +5,9 @@
package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs
import org.jetbrains.kotlin.ir.expressions.IrBlockBody import org.jetbrains.kotlin.ir.declarations.IrVariable
import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.IrReturn import org.jetbrains.kotlin.js.backend.ast.*
import org.jetbrains.kotlin.js.backend.ast.JsBlock
import org.jetbrains.kotlin.js.backend.ast.JsExpressionStatement
import org.jetbrains.kotlin.js.backend.ast.JsReturn
import org.jetbrains.kotlin.js.backend.ast.JsStatement
class IrElementToJsStatementTransformer : BaseIrElementToJsNodeTransformer<JsStatement, Nothing?> { class IrElementToJsStatementTransformer : BaseIrElementToJsNodeTransformer<JsStatement, Nothing?> {
override fun visitBlockBody(body: IrBlockBody, data: Nothing?): JsStatement { override fun visitBlockBody(body: IrBlockBody, data: Nothing?): JsStatement {
@@ -22,7 +18,23 @@ class IrElementToJsStatementTransformer : BaseIrElementToJsNodeTransformer<JsSta
return JsExpressionStatement(expression.accept(IrElementToJsExpressionTransformer(), data)) return JsExpressionStatement(expression.accept(IrElementToJsExpressionTransformer(), data))
} }
override fun visitBreak(jump: IrBreak, data: Nothing?): JsStatement {
return JsBreak(jump.label?.let(::JsNameRef))
}
override fun visitContinue(jump: IrContinue, data: Nothing?): JsStatement {
return JsContinue(jump.label?.let(::JsNameRef))
}
override fun visitReturn(expression: IrReturn, data: Nothing?): JsStatement { override fun visitReturn(expression: IrReturn, data: Nothing?): JsStatement {
return JsReturn(expression.value.accept(IrElementToJsExpressionTransformer(), null)) return JsReturn(expression.value.accept(IrElementToJsExpressionTransformer(), data))
}
override fun visitThrow(expression: IrThrow, data: Nothing?): JsStatement {
return JsThrow(expression.value.accept(IrElementToJsExpressionTransformer(), data))
}
override fun visitVariable(declaration: IrVariable, data: Nothing?): JsStatement {
return jsVar(declaration.name, declaration.initializer)
} }
} }
@@ -0,0 +1,23 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.js.backend.ast.JsDynamicScope
import org.jetbrains.kotlin.js.backend.ast.JsVars
import org.jetbrains.kotlin.name.Name
// TODO don't use JsDynamicScope
val dummyScope = JsDynamicScope
fun Name.toJsName() =
// TODO sanitize
dummyScope.declareName(asString())
fun jsVar(name: Name, initializer: IrExpression?): JsVars {
val jsInitializer = initializer?.accept(IrElementToJsExpressionTransformer(), null)
return JsVars(JsVars.JsVar(name.toJsName(), jsInitializer))
}
@@ -0,0 +1,13 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.ir.backend.js.utils
import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
val IrFunctionSymbol.parameterCount get() = descriptor.valueParameters.size
val IrConstructorSymbol.isPrimary get() = descriptor.isPrimary
@@ -3,7 +3,7 @@
* that can be found in the license/LICENSE.txt file. * that can be found in the license/LICENSE.txt file.
*/ */
package org.jetbrains.kotlin.ir.backend.js package org.jetbrains.kotlin.ir.backend.js.utils
import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.IrElement
@@ -9,5 +9,9 @@ import java.io.File
val JS_IR_BACKEND_TEST_WHITELIST = listOf( val JS_IR_BACKEND_TEST_WHITELIST = listOf(
"js/js.translator/testData/box/package/nestedPackage.kt", "js/js.translator/testData/box/package/nestedPackage.kt",
"js/js.translator/testData/box/package/deeplyNestedPackage.kt" "js/js.translator/testData/box/package/deeplyNestedPackage.kt",
"js/js.translator/testData/box/package/deeplyNestedPackageFunctionCalled.kt",
"js/js.translator/testData/box/multiPackage/nestedPackageFunctionCalledFromOtherPackage.kt",
"js/js.translator/testData/box/expression/identifierClash/useVariableOfNameOfFunction.kt",
"js/js.translator/testData/box/expression/stringClass/stringConstant.kt"
).map { File(it) } ).map { File(it) }
@@ -7383,18 +7383,7 @@ public class IrBoxJsTestGenerated extends AbstractIrBoxJsTest {
@TestMetadata("useVariableOfNameOfFunction.kt") @TestMetadata("useVariableOfNameOfFunction.kt")
public void testUseVariableOfNameOfFunction() throws Exception { public void testUseVariableOfNameOfFunction() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/expression/identifierClash/useVariableOfNameOfFunction.kt"); String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/expression/identifierClash/useVariableOfNameOfFunction.kt");
if (KotlinTestUtils.RUN_IGNORED_TESTS_AS_REGULAR) { doTest(fileName);
doTest(fileName);
return;
}
try {
doTest(fileName);
}
catch (Throwable ignore) {
ignore.printStackTrace();
return;
}
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive or add it to whitelist for that.");
} }
} }
@@ -8618,18 +8607,7 @@ public class IrBoxJsTestGenerated extends AbstractIrBoxJsTest {
@TestMetadata("stringConstant.kt") @TestMetadata("stringConstant.kt")
public void testStringConstant() throws Exception { public void testStringConstant() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/expression/stringClass/stringConstant.kt"); String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/expression/stringClass/stringConstant.kt");
if (KotlinTestUtils.RUN_IGNORED_TESTS_AS_REGULAR) { doTest(fileName);
doTest(fileName);
return;
}
try {
doTest(fileName);
}
catch (Throwable ignore) {
ignore.printStackTrace();
return;
}
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive or add it to whitelist for that.");
} }
@TestMetadata("stringInTemplate.kt") @TestMetadata("stringInTemplate.kt")
@@ -16617,18 +16595,7 @@ public class IrBoxJsTestGenerated extends AbstractIrBoxJsTest {
@TestMetadata("nestedPackageFunctionCalledFromOtherPackage.kt") @TestMetadata("nestedPackageFunctionCalledFromOtherPackage.kt")
public void testNestedPackageFunctionCalledFromOtherPackage() throws Exception { public void testNestedPackageFunctionCalledFromOtherPackage() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/multiPackage/nestedPackageFunctionCalledFromOtherPackage.kt"); String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/multiPackage/nestedPackageFunctionCalledFromOtherPackage.kt");
if (KotlinTestUtils.RUN_IGNORED_TESTS_AS_REGULAR) { doTest(fileName);
doTest(fileName);
return;
}
try {
doTest(fileName);
}
catch (Throwable ignore) {
ignore.printStackTrace();
return;
}
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive or add it to whitelist for that.");
} }
@TestMetadata("packageVariableVisibleFromOtherPackage.kt") @TestMetadata("packageVariableVisibleFromOtherPackage.kt")
@@ -19177,18 +19144,7 @@ public class IrBoxJsTestGenerated extends AbstractIrBoxJsTest {
@TestMetadata("deeplyNestedPackageFunctionCalled.kt") @TestMetadata("deeplyNestedPackageFunctionCalled.kt")
public void testDeeplyNestedPackageFunctionCalled() throws Exception { public void testDeeplyNestedPackageFunctionCalled() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/package/deeplyNestedPackageFunctionCalled.kt"); String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/package/deeplyNestedPackageFunctionCalled.kt");
if (KotlinTestUtils.RUN_IGNORED_TESTS_AS_REGULAR) { doTest(fileName);
doTest(fileName);
return;
}
try {
doTest(fileName);
}
catch (Throwable ignore) {
ignore.printStackTrace();
return;
}
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive or add it to whitelist for that.");
} }
@TestMetadata("initializersOfNestedPackagesExecute.kt") @TestMetadata("initializersOfNestedPackagesExecute.kt")