JS: support bridges and copying of non-abstract methods of interfaces in new pipeline

This commit is contained in:
Alexey Andreev
2017-02-10 19:45:01 +03:00
parent 83140bc5f7
commit 714cb60375
10 changed files with 312 additions and 81 deletions
@@ -16,4 +16,6 @@
package org.jetbrains.kotlin.js.backend.ast
class JsClassModel(val name: JsName, val superName: JsName?)
class JsClassModel(val name: JsName, val superName: JsName?) {
val postDeclarationBlock = JsGlobalBlock()
}
@@ -752,13 +752,4 @@ public final class StaticContext {
String tag = Namer.getFunctionTag(descriptor);
fragment.getInlineModuleMap().put(tag, getModuleExpressionFor(descriptor));
}
/*public void postProcess() {
addInterfaceDefaultMethods();
rootFunction.getBody().getStatements().addAll(importStatements);
addClassPrototypes();
rootFunction.getBody().getStatements().addAll(declarationStatements);
rootFunction.getBody().getStatements().addAll(exporter.getStatements());
rootFunction.getBody().getStatements().addAll(topLevelStatements);
}*/
}
@@ -17,6 +17,8 @@
package org.jetbrains.kotlin.js.translate.context;
import com.intellij.psi.PsiElement;
import kotlin.Unit;
import kotlin.jvm.functions.Function1;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.descriptors.*;
@@ -633,6 +635,19 @@ public class TranslationContext {
staticContext.getDeclarationStatements().add(statement);
}
private final Function1<JsStatement, Unit> declarationStatementConsumer = new Function1<JsStatement, Unit>() {
@Override
public Unit invoke(JsStatement statement) {
addDeclarationStatement(statement);
return Unit.INSTANCE;
}
};
@NotNull
public Function1<JsStatement, Unit> getDeclarationStatementConsumer() {
return declarationStatementConsumer;
}
public void addTopLevelStatement(@NotNull JsStatement statement) {
staticContext.getTopLevelStatements().add(statement);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,15 +16,255 @@
package org.jetbrains.kotlin.js.translate.declaration
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.js.backend.ast.JsClassModel
import org.jetbrains.kotlin.backend.common.CodegenUtil
import org.jetbrains.kotlin.backend.common.bridges.Bridge
import org.jetbrains.kotlin.backend.common.bridges.generateBridgesForFunctionDescriptor
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.js.backend.ast.*
import org.jetbrains.kotlin.js.translate.context.Namer
import org.jetbrains.kotlin.js.translate.context.StaticContext
import org.jetbrains.kotlin.js.translate.context.TranslationContext
import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils.prototypeOf
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils.pureFqn
import org.jetbrains.kotlin.js.translate.utils.generateDelegateCall
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny
import org.jetbrains.kotlin.resolve.descriptorUtil.hasOrInheritsParametersWithDefaultValue
import org.jetbrains.kotlin.resolve.descriptorUtil.hasOwnParametersWithDefaultValue
import org.jetbrains.kotlin.resolve.descriptorUtil.module
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
import org.jetbrains.kotlin.utils.identity
class ClassModelGenerator(val context: StaticContext) {
fun generateClassModel(descriptor: ClassDescriptor): JsClassModel {
val superName = descriptor.getSuperClassNotAny()?.let { context.getInnerNameForDescriptor(it) }
val model = JsClassModel(context.getInnerNameForDescriptor(descriptor), superName)
if (descriptor.kind != ClassKind.ANNOTATION_CLASS && !AnnotationsUtils.isNativeObject(descriptor)) {
copyDefaultMembers(descriptor, model)
generateBridgeMethods(descriptor, model)
}
return model
}
}
private fun copyDefaultMembers(descriptor: ClassDescriptor, model: JsClassModel) {
val members = descriptor.unsubstitutedMemberScope
.getContributedDescriptors(DescriptorKindFilter.FUNCTIONS)
.mapNotNull { it as? CallableMemberDescriptor }
// Traverse fake non-abstract member. Current class does not provide their implementation,
// it can be inherited from interface.
for (member in members.filter { !it.kind.isReal && it.modality != Modality.ABSTRACT }) {
if (member is FunctionDescriptor) {
tryCopyWhenImplementingInterfaceWithDefaultArgs(member, model)
}
copySimpleMember(descriptor, member, model)
// Copy *implementation* functions (i.e. those ones which end with `$default` suffix)
// of Kotlin functions with optional parameters.
if (member is FunctionDescriptor && !hasImplementationInPrototype(member)) {
copyMemberWithOptionalArgs(descriptor, member, model, Namer.DEFAULT_PARAMETER_IMPLEMENTOR_SUFFIX)
}
}
// Traverse non-fake non-abstract members. Current class provides their implementation, but the implementation
// may override function with optional parameters. In this case we already copied *implementation* function
// (with `$default` suffix) but we also need *dispatcher* function (without suffix).
// Case of fake member is covered by previous loop.
for (function in members.filterIsInstance<FunctionDescriptor>().filter { it.modality != Modality.ABSTRACT && it.kind.isReal }) {
copyMemberWithOptionalArgs(descriptor, function, model, "")
}
}
private fun hasImplementationInPrototype(member: CallableMemberDescriptor): Boolean {
return member.overriddenDescriptors.any {
it.modality != Modality.ABSTRACT && !DescriptorUtils.isInterface(it.containingDeclaration)
}
}
// Cover very special case. Consider
//
// open class B { fun foo(x: Int): Unit }
// interface I { fun foo(x: Int = ...): Unit }
// class D : B(), I
//
// Interface I provides dispatcher function, but no implementation function. It's expected that D
// inherits dispatcher function from I (by copying it) and implementation function from B.
// However, D inherits `foo` without suffix (i.e. it corresponds to I's dispatcher function).
// We must copy B.foo to D.foo$default and then I.foo to D.foo
private fun tryCopyWhenImplementingInterfaceWithDefaultArgs(member: FunctionDescriptor, model: JsClassModel) {
val fromInterface = member.overriddenDescriptors.firstOrNull { it.hasOwnParametersWithDefaultValue() } ?: return
if (!DescriptorUtils.isInterface(fromInterface.containingDeclaration)) return
val fromClass = member.overriddenDescriptors.firstOrNull { !DescriptorUtils.isInterface(it.containingDeclaration) } ?: return
val targetClass = member.containingDeclaration as ClassDescriptor
val fromInterfaceName = context.getNameForDescriptor(fromInterface).ident
copyMethod(context.getNameForDescriptor(fromClass).ident, fromInterfaceName + Namer.DEFAULT_PARAMETER_IMPLEMENTOR_SUFFIX,
fromClass.containingDeclaration as ClassDescriptor, targetClass, model.postDeclarationBlock)
copyMethod(fromInterfaceName, context.getNameForDescriptor(member).ident,
fromInterface.containingDeclaration as ClassDescriptor, targetClass,
model.postDeclarationBlock)
}
private fun copySimpleMember(descriptor: ClassDescriptor, member: CallableMemberDescriptor, model: JsClassModel) {
// Special case: fake descriptor denotes (possible multiple) private members from different super interfaces
if (member.visibility == Visibilities.INVISIBLE_FAKE) return copyInvisibleFakeMember(descriptor, member, model)
val memberToCopy = findMemberToCopy(member) ?: return
val classToCopyFrom = memberToCopy.containingDeclaration as ClassDescriptor
if (classToCopyFrom.kind != ClassKind.INTERFACE || AnnotationsUtils.isNativeObject(classToCopyFrom)) return
val name = context.getNameForDescriptor(member).ident
when (member) {
is FunctionDescriptor -> {
copyMethod(name, name, classToCopyFrom, descriptor, model.postDeclarationBlock)
}
is PropertyDescriptor -> copyProperty(name, classToCopyFrom, descriptor, model.postDeclarationBlock)
}
}
private fun copyInvisibleFakeMember(descriptor: ClassDescriptor, member: CallableMemberDescriptor, model: JsClassModel) {
for (overriddenMember in member.overriddenDescriptors) {
val memberToCopy = if (overriddenMember.kind.isReal) overriddenMember else findMemberToCopy(overriddenMember) ?: continue
val classToCopyFrom = memberToCopy.containingDeclaration as ClassDescriptor
if (classToCopyFrom.kind != ClassKind.INTERFACE) continue
val name = context.getNameForDescriptor(memberToCopy).ident
when (member) {
is FunctionDescriptor -> {
copyMethod(name, name, classToCopyFrom, descriptor, model.postDeclarationBlock)
}
is PropertyDescriptor -> copyProperty(name, classToCopyFrom, descriptor, model.postDeclarationBlock)
}
}
}
private fun copyMemberWithOptionalArgs(descriptor: ClassDescriptor, member: FunctionDescriptor, model: JsClassModel, suffix: String) {
val memberToCopy = findOptionalArgsMemberToCopy(member) ?: return
val classToCopyFrom = memberToCopy.containingDeclaration as ClassDescriptor
if (classToCopyFrom.kind != ClassKind.INTERFACE || AnnotationsUtils.isNativeObject(classToCopyFrom)) return
val name = context.getNameForDescriptor(member).ident + suffix
copyMethod(name, name, classToCopyFrom, descriptor, model.postDeclarationBlock)
}
private fun findMemberToCopy(member: CallableMemberDescriptor): CallableMemberDescriptor? {
// If one of overridden members is non-abstract, copy it.
// When none found, we have nothing to copy, ignore.
// When multiple found, our current class should provide implementation, ignore.
val memberToCopy = member.overriddenDescriptors
.filter { it.modality != Modality.ABSTRACT }
.singleOrNull() ?: return null
// If found member is not from interface, we don't need to copy it, it's already in prototype
if ((memberToCopy.containingDeclaration as ClassDescriptor).kind != ClassKind.INTERFACE) return null
// If found member is fake itself, repeat search for it, until we find actual implementation
return if (!memberToCopy.kind.isReal) findMemberToCopy(memberToCopy) else memberToCopy
}
private fun findOptionalArgsMemberToCopy(member: FunctionDescriptor): FunctionDescriptor? {
// If one of overridden members has parameters with default value, copy it.
// When non found, we have nothing to copy, ignore.
// When multiple found, our current class should provide implementation, ignore.
val memberToCopy = member.overriddenDescriptors
.filter { it.hasOrInheritsParametersWithDefaultValue() }
.singleOrNull() ?: return null
// If found member is not from interface, we don't need to copy it, it's already in prototype
if ((memberToCopy.containingDeclaration as ClassDescriptor).kind != ClassKind.INTERFACE) return null
// If found member is fake itself, repeat search for it, until we find actual implementation
return if (!memberToCopy.kind.isReal) findOptionalArgsMemberToCopy(memberToCopy) else memberToCopy
}
private fun generateBridgeMethods(descriptor: ClassDescriptor, model: JsClassModel) {
generateBridgesToTraitImpl(descriptor, model)
generateOtherBridges(descriptor, model)
}
private fun generateBridgesToTraitImpl(descriptor: ClassDescriptor, model: JsClassModel) {
val translationContext = TranslationContext.rootContext(context)
for ((key, value) in CodegenUtil.getNonPrivateTraitMethods(descriptor)) {
val sourceName = context.getNameForDescriptor(key).ident
val targetName = context.getNameForDescriptor(value).ident
if (sourceName != targetName) {
generateDelegateCall(descriptor, key, value, JsLiteral.THIS, translationContext) {
model.postDeclarationBlock.statements += it
}
}
}
}
private fun generateOtherBridges(descriptor: ClassDescriptor, model: JsClassModel) {
for (memberDescriptor in descriptor.defaultType.memberScope.getContributedDescriptors()) {
if (memberDescriptor is FunctionDescriptor) {
val bridgesToGenerate = generateBridgesForFunctionDescriptor(memberDescriptor, identity()) {
//There is no DefaultImpls in js backend so if method non-abstract it should be recognized as non-abstract on bridges calculation
false
}
for (bridge in bridgesToGenerate) {
generateBridge(descriptor, model, bridge)
}
}
}
}
private fun generateBridge(descriptor: ClassDescriptor, model: JsClassModel, bridge: Bridge<FunctionDescriptor>) {
val fromDescriptor = bridge.from
val toDescriptor = bridge.to
if (toDescriptor.visibility == Visibilities.INVISIBLE_FAKE) return
val sourceName = context.getNameForDescriptor(fromDescriptor).ident
val targetName = context.getNameForDescriptor(toDescriptor).ident
if (sourceName == targetName) return
if ((fromDescriptor.containingDeclaration as ClassDescriptor).kind != ClassKind.INTERFACE) {
if (fromDescriptor.kind.isReal && fromDescriptor.modality != Modality.ABSTRACT && !toDescriptor.kind.isReal) return
}
val translationContext = TranslationContext.rootContext(context)
generateDelegateCall(descriptor, fromDescriptor, toDescriptor, JsLiteral.THIS, translationContext) {
model.postDeclarationBlock.statements += it
}
}
private fun copyMethod(
sourceName: String,
targetName: String,
sourceDescriptor: ClassDescriptor,
targetDescriptor: ClassDescriptor,
block: JsBlock
) {
if (targetDescriptor.module != context.currentModule) return
val targetPrototype = prototypeOf(pureFqn(context.getInnerNameForDescriptor(targetDescriptor), null))
val sourcePrototype = prototypeOf(pureFqn(context.getInnerNameForDescriptor(sourceDescriptor), null))
val targetFunction = JsNameRef(targetName, targetPrototype)
val sourceFunction = JsNameRef(sourceName, sourcePrototype)
block.statements += JsAstUtils.assignment(targetFunction, sourceFunction).makeStmt()
}
private fun copyProperty(
name: String,
sourceDescriptor: ClassDescriptor,
targetDescriptor: ClassDescriptor,
block: JsBlock
) {
if (targetDescriptor.module != context.currentModule) return
val targetPrototype = prototypeOf(pureFqn(context.getInnerNameForDescriptor(targetDescriptor), null))
val sourcePrototype = prototypeOf(pureFqn(context.getInnerNameForDescriptor(sourceDescriptor), null))
val nameLiteral = context.program.getStringLiteral(name)
val getPropertyDescriptor = JsInvocation(JsNameRef("getOwnPropertyDescriptor", "Object"), sourcePrototype, nameLiteral)
val defineProperty = JsAstUtils.defineProperty(targetPrototype, name, getPropertyDescriptor, context.program)
block.statements += defineProperty.makeStmt()
}
}
@@ -16,9 +16,6 @@
package org.jetbrains.kotlin.js.translate.declaration
import org.jetbrains.kotlin.backend.common.CodegenUtil
import org.jetbrains.kotlin.backend.common.bridges.Bridge
import org.jetbrains.kotlin.backend.common.bridges.generateBridgesForFunctionDescriptor
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.builtins.functions.FunctionClassDescriptor
import org.jetbrains.kotlin.descriptors.*
@@ -50,7 +47,6 @@ import org.jetbrains.kotlin.types.CommonSupertypes.topologicallySortSuperclasses
import org.jetbrains.kotlin.types.SimpleType
import org.jetbrains.kotlin.types.TypeConstructor
import org.jetbrains.kotlin.utils.DFS
import org.jetbrains.kotlin.utils.identity
/**
* Generates a definition of a single class.
@@ -70,8 +66,6 @@ class ClassTranslator private constructor(
private fun isTrait(): Boolean = descriptor.kind == ClassKind.INTERFACE
private fun isAnnotation(): Boolean = descriptor.kind == ClassKind.ANNOTATION_CLASS
private fun translate() {
val scope = context().getScopeForDescriptor(descriptor)
val context = context().newDeclaration(descriptor)
@@ -105,8 +99,6 @@ class ClassTranslator private constructor(
addSuperclassReferences()
classDeclaration.secondaryConstructors.forEach { generateSecondaryConstructor(context, it) }
generatedBridgeMethods()
if (descriptor.isData) {
JsDataClassGenerator(classDeclaration, context).generate()
}
@@ -491,54 +483,6 @@ class ClassTranslator private constructor(
}
}
private fun generatedBridgeMethods() {
if (isAnnotation()) return
generateBridgesToTraitImpl()
generateOtherBridges()
}
private fun generateBridgesToTraitImpl() {
for ((key, value) in CodegenUtil.getNonPrivateTraitMethods(descriptor)) {
if (!areNamesEqual(key, value)) {
generateDelegateCall(descriptor, value, key, JsLiteral.THIS, context())
}
}
}
private fun generateOtherBridges() {
for (memberDescriptor in descriptor.defaultType.memberScope.getContributedDescriptors()) {
if (memberDescriptor is FunctionDescriptor) {
val bridgesToGenerate = generateBridgesForFunctionDescriptor(memberDescriptor, identity()) {
//There is no DefaultImpls in js backend so if method non-abstract it should be recognized as non-abstract on bridges calculation
false
}
for (bridge in bridgesToGenerate) {
generateBridge(bridge)
}
}
}
}
private fun generateBridge(bridge: Bridge<FunctionDescriptor>) {
val fromDescriptor = bridge.from
val toDescriptor = bridge.to
if (areNamesEqual(fromDescriptor, toDescriptor)) return
if (fromDescriptor.kind.isReal && fromDescriptor.modality != Modality.ABSTRACT && !toDescriptor.kind.isReal)
return
generateDelegateCall(descriptor, fromDescriptor, toDescriptor, JsLiteral.THIS, context())
}
private fun areNamesEqual(first: FunctionDescriptor, second: FunctionDescriptor): Boolean {
val firstName = context().getNameForDescriptor(first)
val secondName = context().getNameForDescriptor(second)
return firstName.ident == secondName.ident
}
companion object {
@JvmStatic fun translate(classDeclaration: KtClassOrObject, context: TranslationContext, enumInitializerName: JsName?) {
return ClassTranslator(classDeclaration, context, enumInitializerName, null).translate()
@@ -99,7 +99,7 @@ class DeclarationBodyVisitor(
override fun addFunction(descriptor: FunctionDescriptor, expression: JsExpression?) {
if (!descriptor.hasOrInheritsParametersWithDefaultValue() || !descriptor.isOverridableOrOverrides) {
if (expression != null) {
context.addFunctionToPrototype(containingClass, descriptor, expression)
context.addFunctionToPrototype(containingClass, descriptor, expression, context.declarationStatementConsumer)
}
}
else {
@@ -139,7 +139,7 @@ class DeclarationBodyVisitor(
}
caller.body.statements += statement
context.addFunctionToPrototype(containingClass, descriptor, caller)
context.addFunctionToPrototype(containingClass, descriptor, caller, context.declarationStatementConsumer)
}
}
}
@@ -89,7 +89,7 @@ class DelegationTranslator(
}
private fun getSuperClass(specifier: KtSuperTypeListEntry): ClassDescriptor =
CodegenUtil.getSuperClassBySuperTypeListEntry(specifier, bindingContext())
CodegenUtil.getSuperClassBySuperTypeListEntry(specifier, bindingContext())
private fun generateDelegates(toClass: ClassDescriptor, field: Field) {
for ((descriptor, overriddenDescriptor) in DelegationResolver.getDelegates(classDescriptor, toClass)) {
@@ -174,10 +174,12 @@ class DelegationTranslator(
// TODO: same logic as in AbstractDeclarationVisitor
if (descriptor.isExtensionProperty || TranslationUtils.shouldAccessViaFunctions(descriptor)) {
val getter = descriptor.getter!!
context().addFunctionToPrototype(classDescriptor, getter, generateDelegateGetterFunction(getter))
context().addFunctionToPrototype(classDescriptor, getter, generateDelegateGetterFunction(getter),
context().declarationStatementConsumer)
if (descriptor.isVar) {
val setter = descriptor.setter!!
context().addFunctionToPrototype(classDescriptor, setter, generateDelegateSetterFunction(setter))
context().addFunctionToPrototype(classDescriptor, setter, generateDelegateSetterFunction(setter),
context().declarationStatementConsumer)
}
}
else {
@@ -194,6 +196,7 @@ class DelegationTranslator(
delegateName: JsName
) {
val delegateRef = JsNameRef(delegateName, JsLiteral.THIS)
generateDelegateCall(classDescriptor, descriptor, overriddenDescriptor, delegateRef, context())
generateDelegateCall(classDescriptor, descriptor, overriddenDescriptor, delegateRef, context(),
context().declarationStatementConsumer)
}
}
@@ -192,7 +192,8 @@ class JsDataClassGenerator extends DataClassMethodGenerator {
private JsFunction generateJsMethod(@NotNull FunctionDescriptor functionDescriptor) {
JsFunction functionObject = context.createRootScopedFunction(functionDescriptor);
ClassDescriptor containingClass = (ClassDescriptor) functionDescriptor.getContainingDeclaration();
UtilsKt.addFunctionToPrototype(context, containingClass, functionDescriptor, functionObject);
UtilsKt.addFunctionToPrototype(context, containingClass, functionDescriptor, functionObject,
context.getDeclarationStatementConsumer());
return functionObject;
}
}
@@ -49,6 +49,13 @@ class Merger(private val rootFunction: JsFunction, val internalModuleName: JsNam
declarationBlock.statements += fragment.declarationBlock
initializerBlock.statements += fragment.initializerBlock
exportBlock.statements += fragment.exportBlock
classes += fragment.classes.values.map { cls ->
val name = nameMap.rename(cls.name)
name to JsClassModel(name, cls.superName?.let { nameMap.rename(it) }).also { copy ->
copy.postDeclarationBlock.statements += cls.postDeclarationBlock.statements.map { nameMap.rename(it) }
}
}
}
val importedModules: List<JsImportedModule>
@@ -116,6 +123,7 @@ class Merger(private val rootFunction: JsFunction, val internalModuleName: JsNam
addClassPrototypes(this)
this += declarationBlock.statements
this += exportBlock.statements
addClassPostDeclarations(this)
this += initializerBlock.statements
}
}
@@ -149,4 +157,25 @@ class Merger(private val rootFunction: JsFunction, val internalModuleName: JsNam
val constructorRef = JsNameRef("constructor", prototype.deepCopy())
statements += JsAstUtils.assignment(constructorRef, classRef.deepCopy()).makeStmt()
}
private fun addClassPostDeclarations(statements: MutableList<JsStatement>) {
val visited = mutableSetOf<JsName>()
for (cls in classes.keys) {
addClassPostDeclarations(cls, visited, statements)
}
}
private fun addClassPostDeclarations(
name: JsName,
visited: MutableSet<JsName>,
statements: MutableList<JsStatement>
) {
if (!visited.add(name)) return
val cls = classes[name] ?: return
val superName = cls.superName
if (superName != null) {
addClassPostDeclarations(superName, visited, statements)
}
statements += cls.postDeclarationBlock.statements
}
}
@@ -42,7 +42,8 @@ fun generateDelegateCall(
fromDescriptor: FunctionDescriptor,
toDescriptor: FunctionDescriptor,
thisObject: JsExpression,
context: TranslationContext
context: TranslationContext,
consumer: (JsStatement) -> Unit
) {
val overriddenMemberFunctionName = context.getNameForDescriptor(toDescriptor)
val overriddenMemberFunctionRef = JsNameRef(overriddenMemberFunctionName, thisObject)
@@ -75,7 +76,7 @@ fun generateDelegateCall(
val functionObject = simpleReturnFunction(context.getScopeForDescriptor(fromDescriptor), invocation)
functionObject.parameters.addAll(parameters)
context.addFunctionToPrototype(classDescriptor, fromDescriptor, functionObject)
context.addFunctionToPrototype(classDescriptor, fromDescriptor, functionObject, consumer)
}
fun <T, S> List<T>.splitToRanges(classifier: (T) -> S): List<Pair<List<T>, S>> {
@@ -120,10 +121,15 @@ fun getReferenceToJsClass(type: KotlinType, context: TranslationContext): JsExpr
return referenceToJsClass
}
fun TranslationContext.addFunctionToPrototype(classDescriptor: ClassDescriptor, descriptor: FunctionDescriptor, function: JsExpression) {
fun TranslationContext.addFunctionToPrototype(
classDescriptor: ClassDescriptor,
descriptor: FunctionDescriptor,
function: JsExpression,
consumer: (JsStatement) -> Unit
) {
val prototypeRef = JsAstUtils.prototypeOf(getInnerReference(classDescriptor))
val functionRef = JsNameRef(getNameForDescriptor(descriptor), prototypeRef)
addDeclarationStatement(JsAstUtils.assignment(functionRef, function).makeStmt())
consumer(JsAstUtils.assignment(functionRef, function).makeStmt())
}
fun TranslationContext.addAccessorsToPrototype(