Implement basic IR -> JS class translation
This commit is contained in:
@@ -1,8 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
class JsIrBackendContext
|
||||
+14
-23
@@ -5,37 +5,28 @@
|
||||
|
||||
package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs
|
||||
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.JsGenerationContext
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.isPrimary
|
||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
import org.jetbrains.kotlin.ir.declarations.IrConstructor
|
||||
import org.jetbrains.kotlin.ir.declarations.IrProperty
|
||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
||||
import org.jetbrains.kotlin.js.backend.ast.*
|
||||
|
||||
class IrDeclarationToJsTransformer : BaseIrElementToJsNodeTransformer<JsStatement, Nothing?> {
|
||||
override fun visitProperty(declaration: IrProperty, data: Nothing?): JsStatement {
|
||||
return jsVar(declaration.name, declaration.backingField?.initializer?.expression)
|
||||
class IrDeclarationToJsTransformer : BaseIrElementToJsNodeTransformer<JsStatement, JsGenerationContext> {
|
||||
override fun visitProperty(declaration: IrProperty, context: JsGenerationContext): JsStatement {
|
||||
return jsVar(declaration.name, declaration.backingField?.initializer?.expression, context)
|
||||
}
|
||||
|
||||
override fun visitSimpleFunction(declaration: IrSimpleFunction, data: Nothing?): JsStatement {
|
||||
return JsExpressionStatement(transformIrFunctionToJsFunction(declaration))
|
||||
override fun visitSimpleFunction(declaration: IrSimpleFunction, context: JsGenerationContext): JsStatement {
|
||||
return declaration.accept(IrFunctionToJsTransformer(), context).makeStmt()
|
||||
}
|
||||
|
||||
private fun transformIrFunctionToJsFunction(declaration: IrSimpleFunction): JsFunction {
|
||||
val funName = declaration.name.asString()
|
||||
val body = declaration.body?.accept(IrElementToJsStatementTransformer(), null) as? JsBlock ?: JsBlock()
|
||||
val function = JsFunction(JsFunctionScope(dummyScope, "scope for $funName"), body, "function $funName")
|
||||
|
||||
function.name = declaration.name.toJsName()
|
||||
|
||||
fun JsFunction.addParameter(parameterName: String) {
|
||||
val parameter = function.scope.declareName(parameterName)
|
||||
parameters.add(JsParameter(parameter))
|
||||
}
|
||||
|
||||
declaration.extensionReceiverParameter?.let { function.addParameter("\$receiver") }
|
||||
declaration.valueParameters.forEach {
|
||||
function.addParameter(it.name.asString())
|
||||
}
|
||||
|
||||
return function
|
||||
override fun visitConstructor(declaration: IrConstructor, context: JsGenerationContext): JsStatement {
|
||||
return declaration.accept(IrFunctionToJsTransformer(), context).makeStmt()
|
||||
}
|
||||
|
||||
override fun visitClass(declaration: IrClass, context: JsGenerationContext): JsStatement {
|
||||
return JsClassGenerator(declaration, context).generate()
|
||||
}
|
||||
}
|
||||
|
||||
+49
-38
@@ -5,21 +5,19 @@
|
||||
|
||||
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.name
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.parameterCount
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.*
|
||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol
|
||||
import org.jetbrains.kotlin.js.backend.ast.*
|
||||
|
||||
class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsExpression, Nothing?> {
|
||||
override fun visitExpressionBody(body: IrExpressionBody, data: Nothing?): JsExpression {
|
||||
return body.expression.accept(this, data)
|
||||
class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsExpression, JsGenerationContext> {
|
||||
override fun visitExpressionBody(body: IrExpressionBody, context: JsGenerationContext): JsExpression {
|
||||
return body.expression.accept(this, context)
|
||||
}
|
||||
|
||||
override fun <T> visitConst(expression: IrConst<T>, data: Nothing?): JsExpression {
|
||||
override fun <T> visitConst(expression: IrConst<T>, context: JsGenerationContext): JsExpression {
|
||||
val kind = expression.kind
|
||||
return when (kind) {
|
||||
is IrConstKind.String -> JsStringLiteral(kind.valueOf(expression))
|
||||
@@ -29,34 +27,57 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsEx
|
||||
is IrConstKind.Short -> JsIntLiteral(kind.valueOf(expression).toInt())
|
||||
is IrConstKind.Int -> JsIntLiteral(kind.valueOf(expression))
|
||||
is IrConstKind.Long,
|
||||
is IrConstKind.Char -> super.visitConst(expression, data)
|
||||
is IrConstKind.Char -> super.visitConst(expression, context)
|
||||
is IrConstKind.Float -> JsDoubleLiteral(kind.valueOf(expression).toDouble())
|
||||
is IrConstKind.Double -> JsDoubleLiteral(kind.valueOf(expression))
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitStringConcatenation(expression: IrStringConcatenation, data: Nothing?): JsExpression {
|
||||
override fun visitStringConcatenation(expression: IrStringConcatenation, context: JsGenerationContext): JsExpression {
|
||||
// TODO revisit
|
||||
return expression.arguments.fold<IrExpression, JsExpression>(JsStringLiteral("")) { jsExpr, irExpr ->
|
||||
JsBinaryOperation(
|
||||
JsBinaryOperator.ADD,
|
||||
jsExpr,
|
||||
irExpr.accept(this, data)
|
||||
irExpr.accept(this, context)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitGetValue(expression: IrGetValue, data: Nothing?): JsExpression {
|
||||
return JsNameRef(expression.symbol.name.toJsName())
|
||||
override fun visitGetField(expression: IrGetField, context: JsGenerationContext): JsExpression {
|
||||
return JsNameRef(expression.symbol.name.asString(), expression.receiver?.accept(this, context))
|
||||
}
|
||||
|
||||
override fun visitSetVariable(expression: IrSetVariable, data: Nothing?): JsExpression {
|
||||
override fun visitGetValue(expression: IrGetValue, context: JsGenerationContext): JsExpression {
|
||||
|
||||
return if (expression.symbol.isSpecial) {
|
||||
context.getSpecialRefForName(expression.symbol.name)
|
||||
} else {
|
||||
JsNameRef(context.getNameForSymbol(expression.symbol))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun visitSetField(expression: IrSetField, context: JsGenerationContext): JsExpression {
|
||||
val dest = JsNameRef(expression.symbol.name.asString(), expression.receiver?.accept(this, context))
|
||||
val source = expression.value.accept(this, context)
|
||||
return jsAssignment(dest, source)
|
||||
}
|
||||
|
||||
override fun visitSetVariable(expression: IrSetVariable, context: JsGenerationContext): JsExpression {
|
||||
val ref = JsNameRef(expression.symbol.name.toJsName())
|
||||
val value = expression.value.accept(this, data)
|
||||
val value = expression.value.accept(this, context)
|
||||
return JsBinaryOperation(JsBinaryOperator.ASG, ref, value)
|
||||
}
|
||||
|
||||
override fun visitCall(expression: IrCall, data: Nothing?): JsExpression {
|
||||
override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall, context: JsGenerationContext): JsExpression {
|
||||
val classNameRef = expression.symbol.owner.descriptor.constructedClass.name.toJsName().makeRef()
|
||||
val callFuncRef = JsNameRef(Namer.CALL_FUNCTION, classNameRef)
|
||||
val arguments = translateCallArguments(expression, expression.symbol.parameterCount, context)
|
||||
return JsInvocation(callFuncRef, listOf(JsThisRef()) + arguments)
|
||||
}
|
||||
|
||||
override fun visitCall(expression: IrCall, context: JsGenerationContext): JsExpression {
|
||||
// TODO rewrite more accurately, right now it just copy-pasted and adopted from old version
|
||||
// TODO support:
|
||||
// * ir intrinsics
|
||||
@@ -67,34 +88,24 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsEx
|
||||
|
||||
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))
|
||||
}
|
||||
}
|
||||
val dispatchReceiver = expression.dispatchReceiver?.accept(this, context)
|
||||
val extensionReceiver = expression.extensionReceiver?.accept(this, context)
|
||||
|
||||
|
||||
if (symbol is IrConstructorSymbol && symbol.isPrimary) {
|
||||
return JsNew(JsNameRef((symbol.owner.parent as IrClass).name.asString()), arguments)
|
||||
val arguments = translateCallArguments(expression, expression.symbol.parameterCount, context)
|
||||
|
||||
return if (symbol is IrConstructorSymbol && symbol.isPrimary) {
|
||||
JsNew(JsNameRef((symbol.owner.parent as IrClass).name.asString()), arguments)
|
||||
} else {
|
||||
// TODO sanitize name
|
||||
val symbolName = context.getNameForSymbol(symbol)
|
||||
val ref = if (dispatchReceiver != null) JsNameRef(symbolName, dispatchReceiver) else JsNameRef(symbolName)
|
||||
JsInvocation(ref, extensionReceiver?.let { listOf(extensionReceiver) + arguments } ?: arguments)
|
||||
}
|
||||
|
||||
return JsInvocation(ref, extensionReceiver?.let { listOf(extensionReceiver) + arguments } ?: arguments)
|
||||
}
|
||||
|
||||
override fun visitWhen(expression: IrWhen, data: Nothing?): JsExpression {
|
||||
override fun visitWhen(expression: IrWhen, context: JsGenerationContext): JsExpression {
|
||||
// TODO check when w/o else branch and empty when
|
||||
return expression.toJsNode(this, data, ::JsConditional)!!
|
||||
return expression.toJsNode(this, context, ::JsConditional)!!
|
||||
}
|
||||
}
|
||||
+38
-23
@@ -5,59 +5,74 @@
|
||||
|
||||
package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs
|
||||
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.*
|
||||
import org.jetbrains.kotlin.ir.declarations.IrVariable
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.js.backend.ast.*
|
||||
|
||||
class IrElementToJsStatementTransformer : BaseIrElementToJsNodeTransformer<JsStatement, Nothing?> {
|
||||
override fun visitBlockBody(body: IrBlockBody, data: Nothing?): JsStatement {
|
||||
return JsBlock(body.statements.map { it.accept(this, data) })
|
||||
class IrElementToJsStatementTransformer : BaseIrElementToJsNodeTransformer<JsStatement, JsGenerationContext> {
|
||||
override fun visitBlockBody(body: IrBlockBody, context: JsGenerationContext): JsStatement {
|
||||
return JsBlock(body.statements.map { it.accept(this, context) })
|
||||
}
|
||||
|
||||
override fun visitBlock(expression: IrBlock, data: Nothing?): JsBlock {
|
||||
return JsBlock(expression.statements.map { it.accept(this, data) })
|
||||
override fun visitBlock(expression: IrBlock, context: JsGenerationContext): JsBlock {
|
||||
return JsBlock(expression.statements.map { it.accept(this, context) })
|
||||
}
|
||||
|
||||
override fun visitComposite(expression: IrComposite, data: Nothing?): JsStatement {
|
||||
override fun visitComposite(expression: IrComposite, context: JsGenerationContext): JsStatement {
|
||||
// TODO introduce JsCompositeBlock?
|
||||
return JsBlock(expression.statements.map { it.accept(this, data) })
|
||||
return JsBlock(expression.statements.map { it.accept(this, context) })
|
||||
}
|
||||
|
||||
override fun visitExpression(expression: IrExpression, data: Nothing?): JsStatement {
|
||||
return JsExpressionStatement(expression.accept(IrElementToJsExpressionTransformer(), data))
|
||||
override fun visitExpression(expression: IrExpression, context: JsGenerationContext): JsStatement {
|
||||
return JsExpressionStatement(expression.accept(IrElementToJsExpressionTransformer(), context))
|
||||
}
|
||||
|
||||
override fun visitBreak(jump: IrBreak, data: Nothing?): JsStatement {
|
||||
override fun visitBreak(jump: IrBreak, context: JsGenerationContext): JsStatement {
|
||||
return JsBreak(jump.label?.let(::JsNameRef))
|
||||
}
|
||||
|
||||
override fun visitContinue(jump: IrContinue, data: Nothing?): JsStatement {
|
||||
override fun visitContinue(jump: IrContinue, context: JsGenerationContext): JsStatement {
|
||||
return JsContinue(jump.label?.let(::JsNameRef))
|
||||
}
|
||||
|
||||
override fun visitReturn(expression: IrReturn, data: Nothing?): JsStatement {
|
||||
return JsReturn(expression.value.accept(IrElementToJsExpressionTransformer(), data))
|
||||
override fun visitReturn(expression: IrReturn, context: JsGenerationContext): JsStatement {
|
||||
return JsReturn(expression.value.accept(IrElementToJsExpressionTransformer(), context))
|
||||
}
|
||||
|
||||
override fun visitThrow(expression: IrThrow, data: Nothing?): JsStatement {
|
||||
return JsThrow(expression.value.accept(IrElementToJsExpressionTransformer(), data))
|
||||
override fun visitThrow(expression: IrThrow, context: JsGenerationContext): JsStatement {
|
||||
return JsThrow(expression.value.accept(IrElementToJsExpressionTransformer(), context))
|
||||
}
|
||||
|
||||
override fun visitVariable(declaration: IrVariable, data: Nothing?): JsStatement {
|
||||
return jsVar(declaration.name, declaration.initializer)
|
||||
override fun visitVariable(declaration: IrVariable, context: JsGenerationContext): JsStatement {
|
||||
return jsVar(declaration.name, declaration.initializer, context)
|
||||
}
|
||||
|
||||
override fun visitWhen(expression: IrWhen, data: Nothing?): JsStatement {
|
||||
return expression.toJsNode(this, data, ::JsIf) ?: JsEmpty
|
||||
override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall, context: JsGenerationContext): JsStatement {
|
||||
if (KotlinBuiltIns.isAny(expression.symbol.constructedClass)) {
|
||||
return JsEmpty
|
||||
}
|
||||
return expression.accept(IrElementToJsExpressionTransformer(), context).makeStmt()
|
||||
}
|
||||
|
||||
override fun visitWhileLoop(loop: IrWhileLoop, data: Nothing?): JsStatement {
|
||||
override fun visitInstanceInitializerCall(expression: IrInstanceInitializerCall, context: JsGenerationContext): JsStatement {
|
||||
|
||||
// TODO: implement
|
||||
return JsEmpty
|
||||
}
|
||||
|
||||
override fun visitWhen(expression: IrWhen, context: JsGenerationContext): JsStatement {
|
||||
return expression.toJsNode(this, context, ::JsIf) ?: JsEmpty
|
||||
}
|
||||
|
||||
override fun visitWhileLoop(loop: IrWhileLoop, context: JsGenerationContext): JsStatement {
|
||||
//TODO what if body null?
|
||||
return JsWhile(loop.condition.accept(IrElementToJsExpressionTransformer(), data), loop.body?.accept(this, data))
|
||||
return JsWhile(loop.condition.accept(IrElementToJsExpressionTransformer(), context), loop.body?.accept(this, context))
|
||||
}
|
||||
|
||||
override fun visitDoWhileLoop(loop: IrDoWhileLoop, data: Nothing?): JsStatement {
|
||||
override fun visitDoWhileLoop(loop: IrDoWhileLoop, context: JsGenerationContext): JsStatement {
|
||||
//TODO what if body null?
|
||||
return JsDoWhile(loop.condition.accept(IrElementToJsExpressionTransformer(), data), loop.body?.accept(this, data))
|
||||
return JsDoWhile(loop.condition.accept(IrElementToJsExpressionTransformer(), context), loop.body?.accept(this, context))
|
||||
}
|
||||
}
|
||||
|
||||
+8
-5
@@ -5,16 +5,19 @@
|
||||
|
||||
package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs
|
||||
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.JsGenerationContext
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFile
|
||||
import org.jetbrains.kotlin.js.backend.ast.JsBlock
|
||||
import org.jetbrains.kotlin.ir.declarations.name
|
||||
import org.jetbrains.kotlin.js.backend.ast.JsDeclarationScope
|
||||
import org.jetbrains.kotlin.js.backend.ast.JsStatement
|
||||
|
||||
class IrFileToJsTransformer : BaseIrElementToJsNodeTransformer<JsStatement, Nothing?> {
|
||||
override fun visitFile(declaration: IrFile, data: Nothing?): JsStatement {
|
||||
val block = JsBlock()
|
||||
class IrFileToJsTransformer : BaseIrElementToJsNodeTransformer<JsStatement, JsGenerationContext> {
|
||||
override fun visitFile(declaration: IrFile, context: JsGenerationContext): JsStatement {
|
||||
val fileContext = context.newDeclaration(JsDeclarationScope(context.currentScope, "scope for file ${declaration.name}"))
|
||||
val block = fileContext.currentBlock
|
||||
|
||||
declaration.declarations.forEach {
|
||||
block.statements.add(it.accept(IrDeclarationToJsTransformer(), null))
|
||||
block.statements.add(it.accept(IrDeclarationToJsTransformer(), fileContext))
|
||||
}
|
||||
|
||||
return block
|
||||
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 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.backend.js.utils.JsGenerationContext
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.constructedClassName
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.isPrimary
|
||||
import org.jetbrains.kotlin.ir.declarations.IrConstructor
|
||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
||||
import org.jetbrains.kotlin.js.backend.ast.JsFunction
|
||||
import org.jetbrains.kotlin.js.backend.ast.JsStatement
|
||||
|
||||
open class IrFunctionToJsTransformer : BaseIrElementToJsNodeTransformer<JsFunction, JsGenerationContext> {
|
||||
override fun visitSimpleFunction(declaration: IrSimpleFunction, context: JsGenerationContext): JsFunction {
|
||||
return translateFunction(declaration, declaration.name, context)
|
||||
}
|
||||
|
||||
override fun visitConstructor(declaration: IrConstructor, context: JsGenerationContext): JsFunction {
|
||||
assert(declaration.symbol.isPrimary)
|
||||
return translateFunction(declaration, declaration.symbol.constructedClassName, context)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+4
-1
@@ -5,16 +5,19 @@
|
||||
|
||||
package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs
|
||||
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.JsGenerationContext
|
||||
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
||||
import org.jetbrains.kotlin.js.backend.ast.JsNode
|
||||
import org.jetbrains.kotlin.js.backend.ast.JsProgram
|
||||
import org.jetbrains.kotlin.js.backend.ast.JsRootScope
|
||||
|
||||
class IrModuleToJsTransformer : BaseIrElementToJsNodeTransformer<JsNode, Nothing?> {
|
||||
override fun visitModuleFragment(declaration: IrModuleFragment, data: Nothing?): JsNode {
|
||||
val program = JsProgram()
|
||||
val rootContext = JsGenerationContext(JsRootScope(program))
|
||||
|
||||
declaration.files.forEach {
|
||||
program.globalBlock.statements.add(it.accept(IrFileToJsTransformer(), null))
|
||||
program.globalBlock.statements.add(it.accept(IrFileToJsTransformer(), rootContext))
|
||||
}
|
||||
|
||||
return program
|
||||
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* 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.descriptors.CallableMemberDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.*
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.js.backend.ast.*
|
||||
|
||||
class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationContext) {
|
||||
|
||||
private val classNameRef = irClass.name.toJsName().makeRef()
|
||||
private val classPrototypeRef = prototypeOf(classNameRef)
|
||||
private val classBlock = JsBlock()
|
||||
|
||||
fun generate(): JsStatement {
|
||||
|
||||
maybeGeneratePrimaryConstructor()
|
||||
val transformer = IrFunctionToJsTransformer()
|
||||
|
||||
for (declaration in irClass.declarations) {
|
||||
if (declaration is IrConstructor) {
|
||||
classBlock.statements += declaration.accept(transformer, context).makeStmt()
|
||||
classBlock.statements += generateInheritanceCode()
|
||||
} else if (declaration is IrFunction) {
|
||||
if (declaration.symbol.kind != CallableMemberDescriptor.Kind.FAKE_OVERRIDE &&
|
||||
declaration.symbol.modality != Modality.ABSTRACT
|
||||
) {
|
||||
classBlock.statements += declaration.accept(transformer, context).apply { name = null }.let {
|
||||
val memberName = context.getNameForSymbol(declaration.symbol)
|
||||
val memberRef = JsNameRef(memberName, classPrototypeRef)
|
||||
jsAssignment(memberRef, it).makeStmt()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
classBlock.statements += generateClassMetadata()
|
||||
return classBlock
|
||||
}
|
||||
|
||||
private fun maybeGeneratePrimaryConstructor() {
|
||||
if (!irClass.declarations.any { it is IrConstructor }) {
|
||||
val func = JsFunction(JsFunctionScope(context.currentScope, "Ctor for ${irClass.name}"), JsBlock(), "Ctor for ${irClass.name}")
|
||||
func.name = irClass.name.toJsName()
|
||||
classBlock.statements += func.makeStmt()
|
||||
classBlock.statements += generateInheritanceCode()
|
||||
}
|
||||
}
|
||||
|
||||
private fun generateInheritanceCode(): List<JsStatement> {
|
||||
val baseClass = irClass.superClasses.first { it.kind != ClassKind.INTERFACE }
|
||||
if (baseClass.isAny) {
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
val createCall = jsAssignment(
|
||||
classPrototypeRef, JsInvocation(Namer.JS_OBJECT_CREATE_FUNCTION, prototypeOf(baseClass.owner.name.toJsName().makeRef()))
|
||||
).makeStmt()
|
||||
|
||||
val ctorAssign = jsAssignment(JsNameRef(Namer.CONSTRUCTOR_NAME, classPrototypeRef), classNameRef).makeStmt()
|
||||
|
||||
return listOf(createCall, ctorAssign)
|
||||
}
|
||||
|
||||
private fun generateClassMetadata(): JsStatement {
|
||||
val metadataLiteral = JsObjectLiteral(true)
|
||||
val simpleName = irClass.symbol.name
|
||||
|
||||
if (!simpleName.isSpecial) {
|
||||
val simpleNameProp = JsPropertyInitializer(JsNameRef(Namer.METADATA_SIMPLE_NAME), JsStringLiteral(simpleName.identifier))
|
||||
metadataLiteral.propertyInitializers += simpleNameProp
|
||||
}
|
||||
|
||||
metadataLiteral.propertyInitializers += generateSuperClasses()
|
||||
return jsAssignment(JsNameRef(Namer.METADATA, classNameRef), metadataLiteral).makeStmt()
|
||||
}
|
||||
|
||||
private fun generateSuperClasses(): JsPropertyInitializer = JsPropertyInitializer(
|
||||
JsNameRef(Namer.METADATA_INTERFACES),
|
||||
JsArrayLiteral(irClass.superClasses.filter { it.kind == ClassKind.INTERFACE }.map { JsNameRef(it.owner.name.toJsName()) })
|
||||
)
|
||||
|
||||
}
|
||||
+51
-12
@@ -5,14 +5,11 @@
|
||||
|
||||
package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs
|
||||
|
||||
import org.jetbrains.kotlin.ir.expressions.IrBranch
|
||||
import org.jetbrains.kotlin.ir.expressions.IrElseBranch
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrWhen
|
||||
import org.jetbrains.kotlin.js.backend.ast.JsDynamicScope
|
||||
import org.jetbrains.kotlin.js.backend.ast.JsExpression
|
||||
import org.jetbrains.kotlin.js.backend.ast.JsNode
|
||||
import org.jetbrains.kotlin.js.backend.ast.JsVars
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.JsGenerationContext
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.Namer
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.js.backend.ast.*
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
|
||||
// TODO don't use JsDynamicScope
|
||||
@@ -22,12 +19,12 @@ fun Name.toJsName() =
|
||||
// TODO sanitize
|
||||
dummyScope.declareName(asString())
|
||||
|
||||
fun jsVar(name: Name, initializer: IrExpression?): JsVars {
|
||||
val jsInitializer = initializer?.accept(IrElementToJsExpressionTransformer(), null)
|
||||
fun jsVar(name: Name, initializer: IrExpression?, context: JsGenerationContext): JsVars {
|
||||
val jsInitializer = initializer?.accept(IrElementToJsExpressionTransformer(), context)
|
||||
return JsVars(JsVars.JsVar(name.toJsName(), jsInitializer))
|
||||
}
|
||||
|
||||
fun <T : JsNode, D : Nothing?> IrWhen.toJsNode(tr: BaseIrElementToJsNodeTransformer<T, D>, data: D, node: (JsExpression, T, T?) -> T): T? =
|
||||
fun <T : JsNode, D : JsGenerationContext> IrWhen.toJsNode(tr: BaseIrElementToJsNodeTransformer<T, D>, data: D, node: (JsExpression, T, T?) -> T): T? =
|
||||
branches.foldRight<IrBranch, T?>(null) { br, n ->
|
||||
val body = br.result.accept(tr, data)
|
||||
if (br is IrElseBranch) body
|
||||
@@ -35,4 +32,46 @@ fun <T : JsNode, D : Nothing?> IrWhen.toJsNode(tr: BaseIrElementToJsNodeTransfor
|
||||
val condition = br.condition.accept(IrElementToJsExpressionTransformer(), data)
|
||||
node(condition, body, n)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun jsAssignment(left: JsExpression, right: JsExpression) = JsBinaryOperation(JsBinaryOperator.ASG, left, right)
|
||||
|
||||
fun prototypeOf(classNameRef: JsExpression) = JsNameRef(Namer.PROTOTYPE_NAME, classNameRef)
|
||||
|
||||
fun translateFunction(declaration: IrFunction, name: Name?, context: JsGenerationContext): JsFunction {
|
||||
val functionScope = JsFunctionScope(context.currentScope, "scope for ${name ?: "annon"}")
|
||||
val functionContext = context.newDeclaration(functionScope)
|
||||
val body = declaration.body?.accept(IrElementToJsStatementTransformer(), functionContext) as? JsBlock ?: JsBlock()
|
||||
val function = JsFunction(functionScope, body, "member function ${name ?: "annon"}")
|
||||
|
||||
function.name = name?.toJsName()
|
||||
|
||||
fun JsFunction.addParameter(parameterName: String) {
|
||||
val parameter = function.scope.declareName(parameterName)
|
||||
parameters.add(JsParameter(parameter))
|
||||
}
|
||||
|
||||
declaration.extensionReceiverParameter?.let { function.addParameter("\$receiver") }
|
||||
declaration.valueParameters.forEach {
|
||||
if (it.name.isSpecial) {
|
||||
function.addParameter(context.staticContext.getSpecialNameString(it.name.asString()))
|
||||
} else {
|
||||
function.addParameter(it.name.asString())
|
||||
}
|
||||
}
|
||||
|
||||
return function
|
||||
}
|
||||
|
||||
fun translateCallArguments(
|
||||
expression: IrMemberAccessExpression,
|
||||
parameterCount: Int,
|
||||
context: JsGenerationContext
|
||||
): List<JsExpression> {
|
||||
val transformer = IrElementToJsExpressionTransformer()
|
||||
// TODO: map to?
|
||||
return (0 until parameterCount).map {
|
||||
val argument = expression.getValueArgument(it)
|
||||
argument?.accept(transformer, context) ?: JsPrefixOperation(JsUnaryOperator.VOID, JsIntLiteral(1))
|
||||
}
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.IrElementToJsExpressionTransformer
|
||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.IrElementToJsStatementTransformer
|
||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.toJsName
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||
import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression
|
||||
import org.jetbrains.kotlin.ir.symbols.IrSymbol
|
||||
import org.jetbrains.kotlin.js.backend.ast.*
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
|
||||
class JsGenerationContext {
|
||||
fun newDeclaration(scope: JsScope): JsGenerationContext {
|
||||
return JsGenerationContext(this, JsBlock(), scope)
|
||||
}
|
||||
|
||||
val currentBlock: JsBlock
|
||||
val currentScope: JsScope
|
||||
val parent: JsGenerationContext?
|
||||
val staticContext: JsStaticContext
|
||||
private val program: JsProgram
|
||||
|
||||
constructor(rootScope: JsRootScope) {
|
||||
|
||||
this.parent = null
|
||||
this.program = rootScope.program
|
||||
this.staticContext = JsStaticContext(rootScope, program.globalBlock, SimpleNameGenerator())
|
||||
this.currentScope = rootScope
|
||||
this.currentBlock = program.globalBlock
|
||||
}
|
||||
|
||||
constructor(parent: JsGenerationContext, block: JsBlock, scope: JsScope) {
|
||||
this.parent = parent
|
||||
this.program = parent.program
|
||||
this.staticContext = parent.staticContext
|
||||
this.currentBlock = block
|
||||
this.currentScope = scope
|
||||
}
|
||||
|
||||
fun getNameForSymbol(symbol: IrSymbol): JsName = staticContext.getNameForSymbol(symbol)
|
||||
fun getSpecialRefForName(name: Name): JsExpression = staticContext.getSpecialRefForName(name)
|
||||
}
|
||||
+23
@@ -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.utils
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.ir.symbols.IrSymbol
|
||||
import org.jetbrains.kotlin.js.backend.ast.*
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
|
||||
|
||||
class JsStaticContext(
|
||||
private val rootScope: JsRootScope,
|
||||
private val globalBlock: JsGlobalBlock,
|
||||
private val nameGenerator: NameGenerator
|
||||
) {
|
||||
|
||||
fun getNameForSymbol(irSymbol: IrSymbol) = nameGenerator.getNameForSymbol(irSymbol, rootScope)
|
||||
fun getSpecialRefForName(name: Name): JsExpression = nameGenerator.getSpecialRefForName(name)
|
||||
fun getSpecialNameString(specNameString: String): String = nameGenerator.getSpecialNameString(specNameString)
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* 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.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.ir.symbols.IrSymbol
|
||||
import org.jetbrains.kotlin.js.backend.ast.JsExpression
|
||||
import org.jetbrains.kotlin.js.backend.ast.JsName
|
||||
import org.jetbrains.kotlin.js.backend.ast.JsScope
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
|
||||
interface NameGenerator {
|
||||
fun getNameForSymbol(symbol: IrSymbol, scope: JsScope): JsName
|
||||
|
||||
fun getSpecialRefForName(name: Name): JsExpression
|
||||
fun getSpecialNameString(specNameString: String): String
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* 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.idea.KotlinLanguage
|
||||
import org.jetbrains.kotlin.js.backend.ast.JsNameRef
|
||||
|
||||
object Namer {
|
||||
val KOTLIN_NAME = KotlinLanguage.NAME
|
||||
val KOTLIN_LOWER_NAME = KOTLIN_NAME.toLowerCase()
|
||||
|
||||
// val EQUALS_METHOD_NAME = getStableMangledNameForDescriptor(JsPlatform.INSTANCE.getBuiltIns().getAny(), "equals")
|
||||
// val COMPARE_TO_METHOD_NAME = getStableMangledNameForDescriptor(JsPlatform.INSTANCE.getBuiltIns().getComparable(), "compareTo")
|
||||
val LONG_FROM_NUMBER = "fromNumber"
|
||||
val LONG_TO_NUMBER = "toNumber"
|
||||
val LONG_FROM_INT = "fromInt"
|
||||
val LONG_ZERO = "ZERO"
|
||||
val LONG_ONE = "ONE"
|
||||
val LONG_NEG_ONE = "NEG_ONE"
|
||||
val LONG_MAX_VALUE = "MAX_VALUE"
|
||||
val LONG_MIN_VALUE = "MIN_VALUE"
|
||||
val PRIMITIVE_COMPARE_TO = "primitiveCompareTo"
|
||||
val IS_CHAR = "isChar"
|
||||
val IS_NUMBER = "isNumber"
|
||||
val IS_CHAR_SEQUENCE = "isCharSequence"
|
||||
val GET_KCLASS = "getKClass"
|
||||
val GET_KCLASS_FROM_EXPRESSION = "getKClassFromExpression"
|
||||
|
||||
val CALLEE_NAME = "\$fun"
|
||||
|
||||
val CALL_FUNCTION = "call"
|
||||
val APPLY_FUNCTION = "apply"
|
||||
|
||||
val OUTER_FIELD_NAME = "\$outer"
|
||||
|
||||
val DELEGATE = "\$delegate"
|
||||
|
||||
val ROOT_PACKAGE = "_"
|
||||
|
||||
val RECEIVER_PARAMETER_NAME = "\$receiver"
|
||||
val ANOTHER_THIS_PARAMETER_NAME = "$this"
|
||||
|
||||
val THROW_CLASS_CAST_EXCEPTION_FUN_NAME = "throwCCE"
|
||||
val THROW_ILLEGAL_STATE_EXCEPTION_FUN_NAME = "throwISE"
|
||||
val THROW_UNINITIALIZED_PROPERTY_ACCESS_EXCEPTION = "throwUPAE"
|
||||
val NULL_CHECK_INTRINSIC_NAME = "ensureNotNull"
|
||||
val PROTOTYPE_NAME = "prototype"
|
||||
val CONSTRUCTOR_NAME = "constructor"
|
||||
val CAPTURED_VAR_FIELD = "v"
|
||||
|
||||
val IS_ARRAY_FUN_REF = JsNameRef("isArray", "Array")
|
||||
val DEFINE_INLINE_FUNCTION = "defineInlineFunction"
|
||||
val DEFAULT_PARAMETER_IMPLEMENTOR_SUFFIX = "\$default"
|
||||
|
||||
val JS_OBJECT = JsNameRef("Object")
|
||||
val JS_OBJECT_CREATE_FUNCTION = JsNameRef("create", JS_OBJECT)
|
||||
|
||||
val LOCAL_MODULE_PREFIX = "\$module\$"
|
||||
val METADATA = "\$metadata\$"
|
||||
val METADATA_INTERFACES = "interfaces"
|
||||
val METADATA_SIMPLE_NAME = "simpleName"
|
||||
val METADATA_CLASS_KIND = "kind"
|
||||
val CLASS_KIND_ENUM = "Kind"
|
||||
val CLASS_KIND_CLASS = "CLASS"
|
||||
val CLASS_KIND_INTERFACE = "INTERFACE"
|
||||
val CLASS_KIND_OBJECT = "OBJECT"
|
||||
|
||||
val OBJECT_INSTANCE_VAR_SUFFIX = "_instance"
|
||||
val OBJECT_INSTANCE_FUNCTION_SUFFIX = "_getInstance"
|
||||
|
||||
val ENUM_NAME_FIELD = "name\$"
|
||||
val ENUM_ORDINAL_FIELD = "ordinal\$"
|
||||
|
||||
val IMPORTS_FOR_INLINE_PROPERTY = "\$\$importsForInline\$\$"
|
||||
|
||||
val GETTER_PREFIX = "get_"
|
||||
val SETTER_PREFIX = "set_"
|
||||
|
||||
val SETTER_ARGUMENT = "\$setValue"
|
||||
|
||||
val THIS_SPECIAL_NAME = "<this>"
|
||||
val SET_SPECIAL_NAME = "<set-?>"
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* 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.descriptors.*
|
||||
import org.jetbrains.kotlin.ir.symbols.IrSymbol
|
||||
import org.jetbrains.kotlin.js.backend.ast.*
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
|
||||
class SimpleNameGenerator : NameGenerator {
|
||||
|
||||
private val nameCache = mutableMapOf<DeclarationDescriptor, JsName>()
|
||||
|
||||
override fun getNameForSymbol(symbol: IrSymbol, scope: JsScope): JsName = getNameForDescriptor(symbol.descriptor, scope)
|
||||
|
||||
override fun getSpecialRefForName(name: Name): JsExpression {
|
||||
assert(name.isSpecial)
|
||||
|
||||
val nameString = name.asString()
|
||||
return when (nameString) {
|
||||
Namer.THIS_SPECIAL_NAME -> JsThisRef()
|
||||
else -> JsNameRef(getSpecialNameString(nameString))
|
||||
}
|
||||
}
|
||||
|
||||
override fun getSpecialNameString(specNameString: String): String = when (specNameString) {
|
||||
Namer.SET_SPECIAL_NAME -> Namer.SETTER_ARGUMENT
|
||||
else -> TODO("for Name ${specNameString}")
|
||||
}
|
||||
|
||||
private fun getNameForDescriptor(descriptor: DeclarationDescriptor, scope: JsScope): JsName = nameCache.getOrPut(descriptor, {
|
||||
val nameBuilder = StringBuilder()
|
||||
when (descriptor) {
|
||||
is PropertyAccessorDescriptor -> {
|
||||
when (descriptor) {
|
||||
is PropertyGetterDescriptor -> nameBuilder.append(Namer.GETTER_PREFIX)
|
||||
is PropertySetterDescriptor -> nameBuilder.append(Namer.SETTER_PREFIX)
|
||||
}
|
||||
nameBuilder.append(descriptor.correspondingProperty.name.asString())
|
||||
}
|
||||
is CallableDescriptor -> {
|
||||
nameBuilder.append(descriptor.name.asString())
|
||||
descriptor.typeParameters.forEach { nameBuilder.append("_${it.name.asString()}") }
|
||||
descriptor.valueParameters.forEach { nameBuilder.append("_${it.type}") }
|
||||
}
|
||||
|
||||
}
|
||||
return@getOrPut scope.declareName(nameBuilder.toString())
|
||||
})
|
||||
}
|
||||
+19
-4
@@ -5,12 +5,27 @@
|
||||
|
||||
package org.jetbrains.kotlin.ir.backend.js.utils
|
||||
|
||||
import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrValueSymbol
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.ir.symbols.*
|
||||
|
||||
val IrFunctionSymbol.parameterCount get() = descriptor.valueParameters.size
|
||||
|
||||
val IrFunctionSymbol.kind get() = descriptor.kind
|
||||
|
||||
val IrFunctionSymbol.modality get() = descriptor.modality
|
||||
|
||||
val IrConstructorSymbol.isPrimary get() = descriptor.isPrimary
|
||||
|
||||
val IrValueSymbol.name get() = descriptor.name
|
||||
val IrConstructorSymbol.constructedClassName get() = descriptor.constructedClass.name
|
||||
|
||||
val IrConstructorSymbol.constructedClass get() = descriptor.constructedClass
|
||||
|
||||
val IrValueSymbol.isSpecial get() = descriptor.name.isSpecial
|
||||
|
||||
val IrSymbol.name get() = descriptor.name
|
||||
|
||||
val IrClassSymbol.kind get() = descriptor.kind
|
||||
|
||||
val IrClassSymbol.modality get() = descriptor.modality
|
||||
|
||||
val IrClassSymbol.isAny get() = KotlinBuiltIns.isAny(descriptor)
|
||||
Reference in New Issue
Block a user