From 400d23d2fc1708fa3cd07768fa1d3d3950ce94d6 Mon Sep 17 00:00:00 2001 From: Eric Pabst Date: Thu, 27 Sep 2018 18:28:35 -0600 Subject: [PATCH 1/3] Support abstract classes and abstract functions --- src/main/kotlin/ts2kt/TsClassToKt.kt | 3 ++- src/main/kotlin/ts2kt/TsClassifierToKt.kt | 4 ++-- src/main/kotlin/ts2kt/TsInterfaceToKt.kt | 2 ++ src/main/kotlin/ts2kt/TsInterfaceToKtExtensions.kt | 4 ++-- src/main/kotlin/ts2kt/TypeScriptToKotlin.kt | 8 +++++--- src/main/kotlin/ts2kt/TypeScriptToKotlinBase.kt | 11 +++++++++-- src/main/kotlin/ts2kt/converterUtils.kt | 4 ++-- src/main/kotlin/ts2kt/kotlin/ast/Stringify.kt | 13 +++++++++++-- src/main/kotlin/ts2kt/kotlin/ast/ast.kt | 6 ++++-- src/main/kotlin/ts2kt/mergeDeclarations.kt | 11 ++++++----- testData/class/abstract/abstract.d.kt | 6 ++++++ testData/class/abstract/abstract.d.ts | 4 ++++ 12 files changed, 55 insertions(+), 21 deletions(-) create mode 100644 testData/class/abstract/abstract.d.kt create mode 100644 testData/class/abstract/abstract.d.ts diff --git a/src/main/kotlin/ts2kt/TsClassToKt.kt b/src/main/kotlin/ts2kt/TsClassToKt.kt index 7ca0abe..0cba26c 100644 --- a/src/main/kotlin/ts2kt/TsClassToKt.kt +++ b/src/main/kotlin/ts2kt/TsClassToKt.kt @@ -15,7 +15,8 @@ class TsClassToKt( isOverride: (MethodDeclaration) -> Boolean, isOverrideProperty: (PropertyDeclaration) -> Boolean, override val hasMembersOpenModifier: Boolean = true, - override val needsNoImpl: Boolean = true + override val needsNoImpl: Boolean = true, + override val isAbstract: Boolean = false ) : TsClassifierToKt(typeMapper, isOverride, isOverrideProperty) { override val isInterface = false diff --git a/src/main/kotlin/ts2kt/TsClassifierToKt.kt b/src/main/kotlin/ts2kt/TsClassifierToKt.kt index 79bca81..6ae93ff 100644 --- a/src/main/kotlin/ts2kt/TsClassifierToKt.kt +++ b/src/main/kotlin/ts2kt/TsClassifierToKt.kt @@ -81,7 +81,7 @@ abstract class TsClassifierToKt( open fun isNullable(node: PropertyDeclaration): Boolean = false open fun isLambda(node: PropertyDeclaration): Boolean = false - open fun needsNoImpl(node: MethodDeclaration): Boolean = true + open fun needsNoImpl(node: MethodDeclaration): Boolean = !isAbstract(node) override fun visitPropertyDeclaration(node: PropertyDeclaration) { val declarationName = node.propertyName!! @@ -104,7 +104,7 @@ abstract class TsClassifierToKt( open fun TsClassifierToKt.addFunction(name: String, isOverride: Boolean, needsNoImpl: Boolean, node: MethodDeclaration) { val symbol = typeMapper.typeChecker.getSymbolResolvingAliases(node) node.toKotlinCallSignatureOverloads(typeMapper).forEach { callSignature -> - addFunction(symbol, name, callSignature, isOverride = isOverride, needsNoImpl = needsNoImpl(node)) + addFunction(symbol, name, callSignature, isOverride = isOverride, needsNoImpl = needsNoImpl(node), isAbstract = isAbstract(node)) } assert(node.body == null, "An function in declarations file should not have body, function '${this.name}.$name'") diff --git a/src/main/kotlin/ts2kt/TsInterfaceToKt.kt b/src/main/kotlin/ts2kt/TsInterfaceToKt.kt index b7cff98..6a56625 100644 --- a/src/main/kotlin/ts2kt/TsInterfaceToKt.kt +++ b/src/main/kotlin/ts2kt/TsInterfaceToKt.kt @@ -14,6 +14,8 @@ open class TsInterfaceToKt( override val hasMembersOpenModifier = false + override val isAbstract = false + override val needsNoImpl = false override val isInterface: Boolean = true diff --git a/src/main/kotlin/ts2kt/TsInterfaceToKtExtensions.kt b/src/main/kotlin/ts2kt/TsInterfaceToKtExtensions.kt index 5c43295..2d7f621 100644 --- a/src/main/kotlin/ts2kt/TsInterfaceToKtExtensions.kt +++ b/src/main/kotlin/ts2kt/TsInterfaceToKtExtensions.kt @@ -66,12 +66,12 @@ class TsInterfaceToKtExtensions( needsNoImpl = true, additionalAnnotations = annotations, isOverride = isOverride) } - override fun addFunction(symbol: Symbol?, name: String, callSignature: KtCallSignature, extendsType: KtType?, needsNoImpl: Boolean, additionalAnnotations: List, isOverride: Boolean, isOperator: Boolean) { + override fun addFunction(symbol: Symbol?, name: String, callSignature: KtCallSignature, extendsType: KtType?, needsNoImpl: Boolean, additionalAnnotations: List, isOverride: Boolean, isOperator: Boolean, isAbstract: Boolean) { val typeParamsWithoutClashes = this.typeParams.fixIfClashWith(callSignature.typeParams) val actualExtendsType = if (typeParamsWithoutClashes === this.typeParams) cachedExtendsType else getExtendsType(typeParamsWithoutClashes) val annotations = additionalAnnotations.withNativeAnnotation() - super.addFunction(symbol, name, KtCallSignature(callSignature.params, typeParamsWithoutClashes merge callSignature.typeParams, callSignature.returnType), actualExtendsType, true, annotations, isOverride, isOperator) + super.addFunction(symbol, name, KtCallSignature(callSignature.params, typeParamsWithoutClashes merge callSignature.typeParams, callSignature.returnType), actualExtendsType, true, annotations, isOverride, isOperator, isAbstract) } } diff --git a/src/main/kotlin/ts2kt/TypeScriptToKotlin.kt b/src/main/kotlin/ts2kt/TypeScriptToKotlin.kt index 7579d40..9e7a46c 100644 --- a/src/main/kotlin/ts2kt/TypeScriptToKotlin.kt +++ b/src/main/kotlin/ts2kt/TypeScriptToKotlin.kt @@ -55,6 +55,8 @@ class TypeScriptToKotlin( override val hasMembersOpenModifier = false + override val isAbstract = false + fun getAdditionalAnnotations(node: Node): List { val isShouldSkip = requiredModifier === SyntaxKind.DeclareKeyword && !(node.modifiers?.arr?.any { it.kind === requiredModifier || it.kind === SyntaxKind.ExportKeyword } ?: false ) if (isShouldSkip) return DEFAULT_FAKE_ANNOTATION @@ -125,7 +127,7 @@ class TypeScriptToKotlin( val name = node.propertyName?.asString()!! val symbol = node.name?.let { typeChecker.getSymbolResolvingAliases(it) } node.toKotlinCallSignatureOverloads(typeMapper).forEach { callSignature -> - addFunction(symbol, name, callSignature, additionalAnnotations = additionalAnnotations) + addFunction(symbol, name, callSignature, additionalAnnotations = additionalAnnotations, isAbstract = isAbstract(node)) } processDefaultExport(node, symbol) } @@ -151,7 +153,7 @@ class TypeScriptToKotlin( override fun visitClassDeclaration(node: ClassDeclaration) { val additionalAnnotations = getAdditionalAnnotations(node) - val translator = TsClassToKt(typeMapper, annotations = defaultAnnotations + additionalAnnotations, isOverride = isOverride, isOverrideProperty = isOverrideProperty) + val translator = TsClassToKt(typeMapper, annotations = defaultAnnotations + additionalAnnotations, isOverride = isOverride, isOverrideProperty = isOverrideProperty, isAbstract = isAbstract(node)) translator.visitClassDeclaration(node) val result = translator.createClassifier() @@ -181,7 +183,7 @@ class TypeScriptToKotlin( val enumClass = KtClassifier(KtClassKind.ENUM, KtName(node.identifierName.unescapedText), listOf(), listOf(), listOf(), - entries, listOf(), hasOpenModifier = false) + entries, listOf(), hasOpenModifier = false, isAbstract = isAbstract(node)) val symbol = node.name?.let { typeChecker.getSymbolResolvingAliases(it) } addDeclaration(symbol, enumClass) diff --git a/src/main/kotlin/ts2kt/TypeScriptToKotlinBase.kt b/src/main/kotlin/ts2kt/TypeScriptToKotlinBase.kt index 164a2d4..82d12ed 100644 --- a/src/main/kotlin/ts2kt/TypeScriptToKotlinBase.kt +++ b/src/main/kotlin/ts2kt/TypeScriptToKotlinBase.kt @@ -2,7 +2,9 @@ package ts2kt import ts2kt.kotlin.ast.* import typescriptServices.ts.ImportEqualsDeclaration +import typescriptServices.ts.Node import typescriptServices.ts.Symbol +import typescriptServices.ts.SyntaxKind abstract class TypeScriptToKotlinBase( val declarations: MutableList, @@ -10,6 +12,7 @@ abstract class TypeScriptToKotlinBase( ) : Visitor { abstract val hasMembersOpenModifier: Boolean abstract val isInterface: Boolean + abstract val isAbstract: Boolean open val defaultAnnotations: List = listOf() @@ -18,9 +21,9 @@ abstract class TypeScriptToKotlinBase( addDeclaration(symbol, KtVariable(KtName(name), KtTypeAnnotation(type), extendsType?.let { KtHeritageType(it) }, annotations, typeParams, isVar = isVar, needsNoImpl = needsNoImpl, isInInterface = isInterface, isOverride = isOverride, hasOpenModifier = hasMembersOpenModifier)) } - open fun addFunction(symbol: Symbol?, name: String, callSignature: KtCallSignature, extendsType: KtType? = null, needsNoImpl: Boolean = true, additionalAnnotations: List = listOf(), isOverride: Boolean = false, isOperator: Boolean = false) { + open fun addFunction(symbol: Symbol?, name: String, callSignature: KtCallSignature, extendsType: KtType? = null, needsNoImpl: Boolean = true, additionalAnnotations: List = listOf(), isOverride: Boolean = false, isOperator: Boolean = false, isAbstract: Boolean = false) { val annotations = defaultAnnotations + additionalAnnotations - addDeclaration(symbol, KtFunction(KtName(name), callSignature, extendsType?.let { KtHeritageType(it) }, annotations, needsNoImpl = needsNoImpl, isOverride = isOverride, hasOpenModifier = hasMembersOpenModifier, isOperator = isOperator)) + addDeclaration(symbol, KtFunction(KtName(name), callSignature, extendsType?.let { KtHeritageType(it) }, annotations, needsNoImpl = needsNoImpl, isOverride = isOverride, hasOpenModifier = hasMembersOpenModifier && !isAbstract, isOperator = isOperator, isAbstract = isAbstract)) } protected fun addDeclaration(symbol: Symbol?, declaration: KtMember) { @@ -31,6 +34,10 @@ abstract class TypeScriptToKotlinBase( } } + fun isAbstract(node: Node): Boolean { + return node.modifiers?.arr?.any { it.kind == SyntaxKind.AbstractKeyword } == true + } + // TODO open fun visitList(node: typescriptServices.ts.Node) { forEachChild(this, node) diff --git a/src/main/kotlin/ts2kt/converterUtils.kt b/src/main/kotlin/ts2kt/converterUtils.kt index 275c335..51c5fd4 100644 --- a/src/main/kotlin/ts2kt/converterUtils.kt +++ b/src/main/kotlin/ts2kt/converterUtils.kt @@ -6,7 +6,7 @@ import ts2kt.utils.reportUnsupportedNode import typescriptServices.ts.* fun TsInterfaceToKt.createClassifier() = - KtClassifier(KtClassKind.INTERFACE, KtName(name!!), listOf(), typeParams, parents, declarations, annotations, hasOpenModifier = false) + KtClassifier(KtClassKind.INTERFACE, KtName(name!!), listOf(), typeParams, parents, declarations, annotations, hasOpenModifier = false, isAbstract = false) fun TsClassToKt.createClassifier(): KtClassifier? { if (name == null) return null @@ -19,7 +19,7 @@ fun TsClassToKt.createClassifier(): KtClassifier? { } } ?: declarations - return KtClassifier(kind, KtName(name!!), paramsOfConstructors, typeParams, parents, members, annotations, hasOpenModifier = kind === ts2kt.kotlin.ast.KtClassKind.CLASS) + return KtClassifier(kind, KtName(name!!), paramsOfConstructors, typeParams, parents, members, annotations, hasOpenModifier = kind === ts2kt.kotlin.ast.KtClassKind.CLASS && !isAbstract, isAbstract = isAbstract) } val JS_MODULE = KtName("JsModule") diff --git a/src/main/kotlin/ts2kt/kotlin/ast/Stringify.kt b/src/main/kotlin/ts2kt/kotlin/ast/Stringify.kt index fdece56..05cd36b 100644 --- a/src/main/kotlin/ts2kt/kotlin/ast/Stringify.kt +++ b/src/main/kotlin/ts2kt/kotlin/ast/Stringify.kt @@ -13,6 +13,7 @@ private val EQ_NO_IMPL = " = $NO_IMPL" private val NO_IMPL_PROPERTY_GETTER = " get()" + EQ_NO_IMPL private val NO_IMPL_PROPERTY_SETTER = " set(value)" + EQ_NO_IMPL private val EXTERNAL = "external" +private val ABSTRACT = "abstract" private val INLINE = "inline" private val OPEN = "open" private val OVERRIDE = "override" @@ -81,10 +82,14 @@ class Stringify( out.printIndent() + if (isAbstract) { + out.print("$ABSTRACT ") + } + printExternalIfNeed() if (hasOpenModifier) { - out.print(OPEN + " ") + out.print("$OPEN ") } out.print(kind.keyword) @@ -137,6 +142,10 @@ class Stringify( out.printIndent() + if (isAbstract) { + out.print("$ABSTRACT ") + } + if (function.extendsType == null) { // TODO remove hack printExternalIfNeed() @@ -168,7 +177,7 @@ class Stringify( out.print(name.asString()) - callSignature.printToOut(withTypeParams = false, printUnitReturnType = needsNoImpl, printDefaultValues = !isOverride, noImpl = extendsType == null) + callSignature.printToOut(withTypeParams = false, printUnitReturnType = needsNoImpl || isAbstract, printDefaultValues = !isOverride, noImpl = extendsType == null) if (function.extendsType != null) { out.print(" { " ) diff --git a/src/main/kotlin/ts2kt/kotlin/ast/ast.kt b/src/main/kotlin/ts2kt/kotlin/ast/ast.kt index b51d1f1..00079e8 100644 --- a/src/main/kotlin/ts2kt/kotlin/ast/ast.kt +++ b/src/main/kotlin/ts2kt/kotlin/ast/ast.kt @@ -123,7 +123,8 @@ data class KtClassifier( val parents: List, override var members: List, override var annotations: List, - val hasOpenModifier: Boolean + val hasOpenModifier: Boolean, + val isAbstract: Boolean ) : KtMember, AbstractKtNode(), KtWithMembers { override fun accept(visitor: KtVisitor) { visitor.visitClassifier(this) @@ -159,7 +160,8 @@ data class KtFunction( val needsNoImpl: Boolean = true, val isOverride: Boolean = false, val hasOpenModifier: Boolean = false, - val isOperator: Boolean = false + val isOperator: Boolean = false, + val isAbstract: Boolean = false ) : KtMember, KtExtensionAware, AbstractKtNode() { override fun accept(visitor: KtVisitor) { visitor.visitFunction(this) diff --git a/src/main/kotlin/ts2kt/mergeDeclarations.kt b/src/main/kotlin/ts2kt/mergeDeclarations.kt index 35defbf..5fc8c90 100644 --- a/src/main/kotlin/ts2kt/mergeDeclarations.kt +++ b/src/main/kotlin/ts2kt/mergeDeclarations.kt @@ -95,7 +95,8 @@ private fun mergeClassAndInterface(klass: KtClassifier, iface: KtClassifier): Kt (klass.parents + iface.parents).distinct(), mutableListOf(), mutableListOf() /*annotations will be merged later*/, - klass.hasOpenModifier /* TODO: should it be open? */ + klass.hasOpenModifier /* TODO: should it be open? */, + klass.isAbstract ) return mergeClassifierMembers(result, klass, iface) @@ -106,12 +107,12 @@ private fun mergeClassifierAndVariable(a: KtClassifier, b: KtVariable): KtMember assert(a.getClassObject() == null, "Unxpected `class object` when merge Classifier(kind=${a.kind}) and Variable(${b.stringify()})") if (a.kind === KtClassKind.INTERFACE || a.isModule()) { - val newTrait = KtClassifier(KtClassKind.INTERFACE, a.name, a.paramsOfConstructors, a.typeParams, a.parents, a.members, a.annotations, hasOpenModifier = false) + val newTrait = KtClassifier(KtClassKind.INTERFACE, a.name, a.paramsOfConstructors, a.typeParams, a.parents, a.members, a.annotations, hasOpenModifier = false, isAbstract = false) val delegation = listOf(KtHeritageType(b.type.type, byExpression = NO_IMPL)) // TODO drop hacks - val classObject = KtClassifier(KtClassKind.COMPANION_OBJECT, KtName.NO_NAME, listOf(), listOf(), delegation, listOf(), listOf(), hasOpenModifier = false) + val classObject = KtClassifier(KtClassKind.COMPANION_OBJECT, KtName.NO_NAME, listOf(), listOf(), delegation, listOf(), listOf(), hasOpenModifier = false, isAbstract = false) newTrait.addMember(classObject) @@ -147,7 +148,7 @@ private fun mergeClassAndObject(a: KtClassifier, b: KtClassifier): KtClassifier if (classObject == null) { // TODO drop hack - a.addMember(KtClassifier(KtClassKind.COMPANION_OBJECT, KtName.NO_NAME, listOf(), listOf(), listOf(), b.members, NO_ANNOTATIONS, hasOpenModifier = false)) + a.addMember(KtClassifier(KtClassKind.COMPANION_OBJECT, KtName.NO_NAME, listOf(), listOf(), listOf(), b.members, NO_ANNOTATIONS, hasOpenModifier = false, isAbstract = false)) } else { // TODO drop hack @@ -191,7 +192,7 @@ fun KtPackagePartBuilder.mergeClassesAndPackages() { if (companion == null) { companion = KtClassifier( KtClassKind.COMPANION_OBJECT, KtName.NO_NAME, emptyList(), null, emptyList(), - emptyList(), emptyList(), false) + emptyList(), emptyList(), false, isAbstract = false) } val (classifiers, nonClassifiers) = nestedPackage.members.partition { it is KtClassifier } diff --git a/testData/class/abstract/abstract.d.kt b/testData/class/abstract/abstract.d.kt new file mode 100644 index 0000000..2f5f44d --- /dev/null +++ b/testData/class/abstract/abstract.d.kt @@ -0,0 +1,6 @@ +package abstract + +abstract external class AbstractFoo { + open fun open(): Unit = definedExternally + abstract fun close(): Unit +} diff --git a/testData/class/abstract/abstract.d.ts b/testData/class/abstract/abstract.d.ts new file mode 100644 index 0000000..f33c037 --- /dev/null +++ b/testData/class/abstract/abstract.d.ts @@ -0,0 +1,4 @@ +declare abstract class AbstractFoo { + open(): void + abstract close(): void +} \ No newline at end of file From 7294654533df874e5b30a243812753439009205c Mon Sep 17 00:00:00 2001 From: Eric Pabst Date: Thu, 27 Sep 2018 20:57:18 -0600 Subject: [PATCH 2/3] Support abstract readonly properties --- src/main/kotlin/ts2kt/TsClassifierToKt.kt | 4 +++- src/main/kotlin/ts2kt/TsInterfaceToKtExtensions.kt | 4 ++-- src/main/kotlin/ts2kt/TypeScriptToKotlinBase.kt | 8 ++++++-- src/main/kotlin/ts2kt/kotlin/ast/Stringify.kt | 4 ++++ src/main/kotlin/ts2kt/kotlin/ast/ast.kt | 3 ++- testData/class/abstract/abstract.d.kt | 1 + testData/class/abstract/abstract.d.ts | 1 + 7 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/main/kotlin/ts2kt/TsClassifierToKt.kt b/src/main/kotlin/ts2kt/TsClassifierToKt.kt index 6ae93ff..6e101fa 100644 --- a/src/main/kotlin/ts2kt/TsClassifierToKt.kt +++ b/src/main/kotlin/ts2kt/TsClassifierToKt.kt @@ -77,7 +77,7 @@ abstract class TsClassifierToKt( return this } - open fun needsNoImpl(node: PropertyDeclaration): Boolean = true + open fun needsNoImpl(node: PropertyDeclaration): Boolean = !isAbstract(node) open fun isNullable(node: PropertyDeclaration): Boolean = false open fun isLambda(node: PropertyDeclaration): Boolean = false @@ -97,6 +97,8 @@ abstract class TsClassifierToKt( name, type = varType.copy(isNullable = varType.isNullable || isNullable(node)), isOverride = isOverride, + isVar = !isReadonly(node), + isAbstract = isAbstract(node), needsNoImpl = needsNoImpl(node) ) } diff --git a/src/main/kotlin/ts2kt/TsInterfaceToKtExtensions.kt b/src/main/kotlin/ts2kt/TsInterfaceToKtExtensions.kt index 2d7f621..b050caf 100644 --- a/src/main/kotlin/ts2kt/TsInterfaceToKtExtensions.kt +++ b/src/main/kotlin/ts2kt/TsInterfaceToKtExtensions.kt @@ -57,13 +57,13 @@ class TsInterfaceToKtExtensions( super.translateGetterAndSetter(node, extendsType = cachedExtendsType) } - override fun addVariable(symbol: Symbol?, name: String, type: KtType, extendsType: KtType?, typeParams: List?, isVar: Boolean, needsNoImpl: Boolean, additionalAnnotations: List, isOverride: Boolean) { + override fun addVariable(symbol: Symbol?, name: String, type: KtType, extendsType: KtType?, typeParams: List?, isVar: Boolean, isAbstract: Boolean, needsNoImpl: Boolean, additionalAnnotations: List, isOverride: Boolean) { val typeParamsWithoutClashes = this.typeParams.fixIfClashWith(typeParams) val actualExtendsType = if (typeParamsWithoutClashes === this.typeParams) cachedExtendsType else getExtendsType(typeParamsWithoutClashes) val annotations = additionalAnnotations.withNativeAnnotation() super.addVariable(symbol, name, type, actualExtendsType, typeParamsWithoutClashes merge typeParams, isVar, - needsNoImpl = true, additionalAnnotations = annotations, isOverride = isOverride) + needsNoImpl = !isAbstract, additionalAnnotations = annotations, isOverride = isOverride, isAbstract = isAbstract) } override fun addFunction(symbol: Symbol?, name: String, callSignature: KtCallSignature, extendsType: KtType?, needsNoImpl: Boolean, additionalAnnotations: List, isOverride: Boolean, isOperator: Boolean, isAbstract: Boolean) { diff --git a/src/main/kotlin/ts2kt/TypeScriptToKotlinBase.kt b/src/main/kotlin/ts2kt/TypeScriptToKotlinBase.kt index 82d12ed..3ff4004 100644 --- a/src/main/kotlin/ts2kt/TypeScriptToKotlinBase.kt +++ b/src/main/kotlin/ts2kt/TypeScriptToKotlinBase.kt @@ -16,9 +16,9 @@ abstract class TypeScriptToKotlinBase( open val defaultAnnotations: List = listOf() - open fun addVariable(symbol: Symbol?, name: String, type: KtType, extendsType: KtType? = null, typeParams: List? = null, isVar: Boolean = true, needsNoImpl: Boolean = true, additionalAnnotations: List = listOf(), isOverride: Boolean = false) { + open fun addVariable(symbol: Symbol?, name: String, type: KtType, extendsType: KtType? = null, typeParams: List? = null, isVar: Boolean = true, isAbstract: Boolean = false, needsNoImpl: Boolean = !isAbstract, additionalAnnotations: List = listOf(), isOverride: Boolean = false) { val annotations = defaultAnnotations + additionalAnnotations - addDeclaration(symbol, KtVariable(KtName(name), KtTypeAnnotation(type), extendsType?.let { KtHeritageType(it) }, annotations, typeParams, isVar = isVar, needsNoImpl = needsNoImpl, isInInterface = isInterface, isOverride = isOverride, hasOpenModifier = hasMembersOpenModifier)) + addDeclaration(symbol, KtVariable(KtName(name), KtTypeAnnotation(type), extendsType?.let { KtHeritageType(it) }, annotations, typeParams, isVar = isVar, needsNoImpl = needsNoImpl, isInInterface = isInterface, isOverride = isOverride, hasOpenModifier = hasMembersOpenModifier && !isAbstract, isAbstract = isAbstract)) } open fun addFunction(symbol: Symbol?, name: String, callSignature: KtCallSignature, extendsType: KtType? = null, needsNoImpl: Boolean = true, additionalAnnotations: List = listOf(), isOverride: Boolean = false, isOperator: Boolean = false, isAbstract: Boolean = false) { @@ -38,6 +38,10 @@ abstract class TypeScriptToKotlinBase( return node.modifiers?.arr?.any { it.kind == SyntaxKind.AbstractKeyword } == true } + fun isReadonly(node: Node): Boolean { + return node.modifiers?.arr?.any { it.kind == SyntaxKind.ReadonlyKeyword } == true + } + // TODO open fun visitList(node: typescriptServices.ts.Node) { forEachChild(this, node) diff --git a/src/main/kotlin/ts2kt/kotlin/ast/Stringify.kt b/src/main/kotlin/ts2kt/kotlin/ast/Stringify.kt index 05cd36b..5deb3c5 100644 --- a/src/main/kotlin/ts2kt/kotlin/ast/Stringify.kt +++ b/src/main/kotlin/ts2kt/kotlin/ast/Stringify.kt @@ -202,6 +202,10 @@ class Stringify( out.printIndent() + if (isAbstract) { + out.print("$ABSTRACT ") + } + if (variable.extendsType == null) { // TODO remove hack printExternalIfNeed() diff --git a/src/main/kotlin/ts2kt/kotlin/ast/ast.kt b/src/main/kotlin/ts2kt/kotlin/ast/ast.kt index 00079e8..bcd9607 100644 --- a/src/main/kotlin/ts2kt/kotlin/ast/ast.kt +++ b/src/main/kotlin/ts2kt/kotlin/ast/ast.kt @@ -175,7 +175,8 @@ data class KtVariable( override var annotations: List, val typeParams: List?, var isVar: Boolean, - val needsNoImpl: Boolean = true, + val isAbstract: Boolean = false, + val needsNoImpl: Boolean = !isAbstract, val isInInterface: Boolean, val isOverride: Boolean = false, val hasOpenModifier: Boolean diff --git a/testData/class/abstract/abstract.d.kt b/testData/class/abstract/abstract.d.kt index 2f5f44d..d8521a5 100644 --- a/testData/class/abstract/abstract.d.kt +++ b/testData/class/abstract/abstract.d.kt @@ -1,6 +1,7 @@ package abstract abstract external class AbstractFoo { + abstract val x: Number open fun open(): Unit = definedExternally abstract fun close(): Unit } diff --git a/testData/class/abstract/abstract.d.ts b/testData/class/abstract/abstract.d.ts index f33c037..05d5bbe 100644 --- a/testData/class/abstract/abstract.d.ts +++ b/testData/class/abstract/abstract.d.ts @@ -1,4 +1,5 @@ declare abstract class AbstractFoo { + abstract readonly x: number; open(): void abstract close(): void } \ No newline at end of file From 1f38100a55751e116786124be8d70c7922899049 Mon Sep 17 00:00:00 2001 From: Eric Pabst Date: Thu, 27 Sep 2018 21:06:35 -0600 Subject: [PATCH 3/3] Support protected methods and properties --- src/main/kotlin/ts2kt/TsClassifierToKt.kt | 7 ++--- src/main/kotlin/ts2kt/TsInterfaceToKt.kt | 5 ++-- .../kotlin/ts2kt/TsInterfaceToKtExtensions.kt | 9 ++++--- src/main/kotlin/ts2kt/TypeScriptToKotlin.kt | 2 +- .../kotlin/ts2kt/TypeScriptToKotlinBase.kt | 26 +++++++++++++------ src/main/kotlin/ts2kt/kotlin/ast/Stringify.kt | 9 +++++++ src/main/kotlin/ts2kt/kotlin/ast/ast.kt | 19 +++++++++++--- .../class/methods/accessModifierMethods.d.kt | 6 +++++ .../class/methods/accessModifierMethods.d.ts | 5 ++++ .../variables/accessModifierVariables.d.kt | 6 +++++ .../variables/accessModifierVariables.d.ts | 5 ++++ 11 files changed, 78 insertions(+), 21 deletions(-) create mode 100644 testData/class/methods/accessModifierMethods.d.kt create mode 100644 testData/class/methods/accessModifierMethods.d.ts create mode 100644 testData/class/variables/accessModifierVariables.d.kt create mode 100644 testData/class/variables/accessModifierVariables.d.ts diff --git a/src/main/kotlin/ts2kt/TsClassifierToKt.kt b/src/main/kotlin/ts2kt/TsClassifierToKt.kt index 6e101fa..f396755 100644 --- a/src/main/kotlin/ts2kt/TsClassifierToKt.kt +++ b/src/main/kotlin/ts2kt/TsClassifierToKt.kt @@ -99,14 +99,15 @@ abstract class TsClassifierToKt( isOverride = isOverride, isVar = !isReadonly(node), isAbstract = isAbstract(node), - needsNoImpl = needsNoImpl(node) + needsNoImpl = needsNoImpl(node), + accessModifier = getAccessModifier(node) ) } open fun TsClassifierToKt.addFunction(name: String, isOverride: Boolean, needsNoImpl: Boolean, node: MethodDeclaration) { val symbol = typeMapper.typeChecker.getSymbolResolvingAliases(node) node.toKotlinCallSignatureOverloads(typeMapper).forEach { callSignature -> - addFunction(symbol, name, callSignature, isOverride = isOverride, needsNoImpl = needsNoImpl(node), isAbstract = isAbstract(node)) + addFunction(symbol, name, callSignature, isOverride = isOverride, needsNoImpl = needsNoImpl(node), isAbstract = isAbstract(node), accessModifier = getAccessModifier(node)) } assert(node.body == null, "An function in declarations file should not have body, function '${this.name}.$name'") @@ -123,7 +124,7 @@ abstract class TsClassifierToKt( override fun visitSignatureDeclaration(node: SignatureDeclaration) { node.toKotlinCallSignatureOverloads(typeMapper).forEach { callSignature -> - addFunction(null, INVOKE, callSignature, needsNoImpl = false, additionalAnnotations = listOf(NATIVE_INVOKE_ANNOTATION), isOperator = true) + addFunction(null, INVOKE, callSignature, needsNoImpl = false, additionalAnnotations = listOf(NATIVE_INVOKE_ANNOTATION), isOperator = true, accessModifier = getAccessModifier(node)) } } diff --git a/src/main/kotlin/ts2kt/TsInterfaceToKt.kt b/src/main/kotlin/ts2kt/TsInterfaceToKt.kt index 6a56625..252bb90 100644 --- a/src/main/kotlin/ts2kt/TsInterfaceToKt.kt +++ b/src/main/kotlin/ts2kt/TsInterfaceToKt.kt @@ -30,12 +30,13 @@ open class TsInterfaceToKt( override fun TsClassifierToKt.addFunction(name: String, isOverride: Boolean, needsNoImpl: Boolean, node: MethodDeclaration) { val isOptional = node.questionToken != null val symbol = typeMapper.typeChecker.getSymbolResolvingAliases(node) + val accessModifier = getAccessModifier(node) if (isOptional) { val call = node.toKotlinCallSignature(typeMapper) - addVariable(symbol, name, type = createFunctionType(call.params, call.returnType.type).copy(isNullable = true), typeParams = call.typeParams, isVar = false, needsNoImpl = true, isOverride = isOverride) + addVariable(symbol, name, type = createFunctionType(call.params, call.returnType.type).copy(isNullable = true), typeParams = call.typeParams, isVar = false, needsNoImpl = true, isOverride = isOverride, accessModifier = accessModifier) } else { node.toKotlinCallSignatureOverloads(typeMapper).forEach { call -> - addFunction(symbol, name, call, needsNoImpl = false, isOverride = isOverride) + addFunction(symbol, name, call, needsNoImpl = false, isOverride = isOverride, accessModifier = accessModifier) } } diff --git a/src/main/kotlin/ts2kt/TsInterfaceToKtExtensions.kt b/src/main/kotlin/ts2kt/TsInterfaceToKtExtensions.kt index b050caf..6ecc51d 100644 --- a/src/main/kotlin/ts2kt/TsInterfaceToKtExtensions.kt +++ b/src/main/kotlin/ts2kt/TsInterfaceToKtExtensions.kt @@ -57,21 +57,22 @@ class TsInterfaceToKtExtensions( super.translateGetterAndSetter(node, extendsType = cachedExtendsType) } - override fun addVariable(symbol: Symbol?, name: String, type: KtType, extendsType: KtType?, typeParams: List?, isVar: Boolean, isAbstract: Boolean, needsNoImpl: Boolean, additionalAnnotations: List, isOverride: Boolean) { + override fun addVariable(symbol: Symbol?, name: String, type: KtType, extendsType: KtType?, typeParams: List?, isVar: Boolean, isAbstract: Boolean, needsNoImpl: Boolean, additionalAnnotations: List, isOverride: Boolean, accessModifier: AccessModifier) { val typeParamsWithoutClashes = this.typeParams.fixIfClashWith(typeParams) val actualExtendsType = if (typeParamsWithoutClashes === this.typeParams) cachedExtendsType else getExtendsType(typeParamsWithoutClashes) val annotations = additionalAnnotations.withNativeAnnotation() super.addVariable(symbol, name, type, actualExtendsType, typeParamsWithoutClashes merge typeParams, isVar, - needsNoImpl = !isAbstract, additionalAnnotations = annotations, isOverride = isOverride, isAbstract = isAbstract) + needsNoImpl = !isAbstract, additionalAnnotations = annotations, isOverride = isOverride, isAbstract = isAbstract, + accessModifier = accessModifier) } - override fun addFunction(symbol: Symbol?, name: String, callSignature: KtCallSignature, extendsType: KtType?, needsNoImpl: Boolean, additionalAnnotations: List, isOverride: Boolean, isOperator: Boolean, isAbstract: Boolean) { + override fun addFunction(symbol: Symbol?, name: String, callSignature: KtCallSignature, extendsType: KtType?, needsNoImpl: Boolean, additionalAnnotations: List, isOverride: Boolean, isOperator: Boolean, isAbstract: Boolean, accessModifier: AccessModifier) { val typeParamsWithoutClashes = this.typeParams.fixIfClashWith(callSignature.typeParams) val actualExtendsType = if (typeParamsWithoutClashes === this.typeParams) cachedExtendsType else getExtendsType(typeParamsWithoutClashes) val annotations = additionalAnnotations.withNativeAnnotation() - super.addFunction(symbol, name, KtCallSignature(callSignature.params, typeParamsWithoutClashes merge callSignature.typeParams, callSignature.returnType), actualExtendsType, true, annotations, isOverride, isOperator, isAbstract) + super.addFunction(symbol, name, KtCallSignature(callSignature.params, typeParamsWithoutClashes merge callSignature.typeParams, callSignature.returnType), actualExtendsType, true, annotations, isOverride, isOperator, isAbstract, accessModifier) } } diff --git a/src/main/kotlin/ts2kt/TypeScriptToKotlin.kt b/src/main/kotlin/ts2kt/TypeScriptToKotlin.kt index 9e7a46c..50c0eaa 100644 --- a/src/main/kotlin/ts2kt/TypeScriptToKotlin.kt +++ b/src/main/kotlin/ts2kt/TypeScriptToKotlin.kt @@ -96,7 +96,7 @@ class TypeScriptToKotlin( } } val varType = d.type?.let { typeMapper.mapType(it) } ?: KtType(ANY) - addVariable(symbol, name, varType, additionalAnnotations = additionalAnnotations) + addVariable(symbol, name, varType, additionalAnnotations = additionalAnnotations, accessModifier = getAccessModifier(node)) } } diff --git a/src/main/kotlin/ts2kt/TypeScriptToKotlinBase.kt b/src/main/kotlin/ts2kt/TypeScriptToKotlinBase.kt index 3ff4004..efb717e 100644 --- a/src/main/kotlin/ts2kt/TypeScriptToKotlinBase.kt +++ b/src/main/kotlin/ts2kt/TypeScriptToKotlinBase.kt @@ -16,21 +16,23 @@ abstract class TypeScriptToKotlinBase( open val defaultAnnotations: List = listOf() - open fun addVariable(symbol: Symbol?, name: String, type: KtType, extendsType: KtType? = null, typeParams: List? = null, isVar: Boolean = true, isAbstract: Boolean = false, needsNoImpl: Boolean = !isAbstract, additionalAnnotations: List = listOf(), isOverride: Boolean = false) { + open fun addVariable(symbol: Symbol?, name: String, type: KtType, extendsType: KtType? = null, typeParams: List? = null, isVar: Boolean = true, isAbstract: Boolean = false, needsNoImpl: Boolean = !isAbstract, additionalAnnotations: List = listOf(), isOverride: Boolean = false, accessModifier: AccessModifier = AccessModifier.PUBLIC) { val annotations = defaultAnnotations + additionalAnnotations - addDeclaration(symbol, KtVariable(KtName(name), KtTypeAnnotation(type), extendsType?.let { KtHeritageType(it) }, annotations, typeParams, isVar = isVar, needsNoImpl = needsNoImpl, isInInterface = isInterface, isOverride = isOverride, hasOpenModifier = hasMembersOpenModifier && !isAbstract, isAbstract = isAbstract)) + addDeclaration(symbol, KtVariable(KtName(name), KtTypeAnnotation(type), extendsType?.let { KtHeritageType(it) }, annotations, typeParams, isVar = isVar, needsNoImpl = needsNoImpl, isInInterface = isInterface, isOverride = isOverride, hasOpenModifier = hasMembersOpenModifier && !isAbstract, isAbstract = isAbstract, accessModifier = accessModifier)) } - open fun addFunction(symbol: Symbol?, name: String, callSignature: KtCallSignature, extendsType: KtType? = null, needsNoImpl: Boolean = true, additionalAnnotations: List = listOf(), isOverride: Boolean = false, isOperator: Boolean = false, isAbstract: Boolean = false) { + open fun addFunction(symbol: Symbol?, name: String, callSignature: KtCallSignature, extendsType: KtType? = null, needsNoImpl: Boolean = true, additionalAnnotations: List = listOf(), isOverride: Boolean = false, isOperator: Boolean = false, isAbstract: Boolean = false, accessModifier: AccessModifier = AccessModifier.PUBLIC) { val annotations = defaultAnnotations + additionalAnnotations - addDeclaration(symbol, KtFunction(KtName(name), callSignature, extendsType?.let { KtHeritageType(it) }, annotations, needsNoImpl = needsNoImpl, isOverride = isOverride, hasOpenModifier = hasMembersOpenModifier && !isAbstract, isOperator = isOperator, isAbstract = isAbstract)) + addDeclaration(symbol, KtFunction(KtName(name), callSignature, extendsType?.let { KtHeritageType(it) }, annotations, needsNoImpl = needsNoImpl, isOverride = isOverride, hasOpenModifier = hasMembersOpenModifier && !isAbstract, isOperator = isOperator, isAbstract = isAbstract, accessModifier = accessModifier)) } protected fun addDeclaration(symbol: Symbol?, declaration: KtMember) { - declarations += declaration - if (symbol != null) { - val values = declarationsBySymbol.getOrPut(symbol) { mutableListOf() } - values += declaration + if (declaration.accessModifier.usable) { + declarations += declaration + if (symbol != null) { + val values = declarationsBySymbol.getOrPut(symbol) { mutableListOf() } + values += declaration + } } } @@ -42,6 +44,14 @@ abstract class TypeScriptToKotlinBase( return node.modifiers?.arr?.any { it.kind == SyntaxKind.ReadonlyKeyword } == true } + fun getAccessModifier(node: Node): AccessModifier { + return when { + node.modifiers?.arr?.any { it.kind == SyntaxKind.ProtectedKeyword } == true -> AccessModifier.PROTECTED + node.modifiers?.arr?.any { it.kind == SyntaxKind.PrivateKeyword } == true -> AccessModifier.PRIVATE + else -> AccessModifier.PUBLIC + } + } + // TODO open fun visitList(node: typescriptServices.ts.Node) { forEachChild(this, node) diff --git a/src/main/kotlin/ts2kt/kotlin/ast/Stringify.kt b/src/main/kotlin/ts2kt/kotlin/ast/Stringify.kt index 5deb3c5..6774818 100644 --- a/src/main/kotlin/ts2kt/kotlin/ast/Stringify.kt +++ b/src/main/kotlin/ts2kt/kotlin/ast/Stringify.kt @@ -13,6 +13,7 @@ private val EQ_NO_IMPL = " = $NO_IMPL" private val NO_IMPL_PROPERTY_GETTER = " get()" + EQ_NO_IMPL private val NO_IMPL_PROPERTY_SETTER = " set(value)" + EQ_NO_IMPL private val EXTERNAL = "external" +private val PROTECTED = "protected" private val ABSTRACT = "abstract" private val INLINE = "inline" private val OPEN = "open" @@ -142,6 +143,10 @@ class Stringify( out.printIndent() + if (accessModifier == AccessModifier.PROTECTED) { + out.print("$PROTECTED ") + } + if (isAbstract) { out.print("$ABSTRACT ") } @@ -202,6 +207,10 @@ class Stringify( out.printIndent() + if (accessModifier == AccessModifier.PROTECTED) { + out.print("$PROTECTED ") + } + if (isAbstract) { out.print("$ABSTRACT ") } diff --git a/src/main/kotlin/ts2kt/kotlin/ast/ast.kt b/src/main/kotlin/ts2kt/kotlin/ast/ast.kt index bcd9607..d712ef3 100644 --- a/src/main/kotlin/ts2kt/kotlin/ast/ast.kt +++ b/src/main/kotlin/ts2kt/kotlin/ast/ast.kt @@ -88,7 +88,9 @@ interface KtAnnotated { var annotations: List } -interface KtMember : KtNode, KtNamed, KtAnnotated +interface KtMember : KtNode, KtNamed, KtAnnotated { + val accessModifier: AccessModifier +} interface KtExtensionAware : KtMember { val extendsType: KtHeritageType? @@ -126,6 +128,9 @@ data class KtClassifier( val hasOpenModifier: Boolean, val isAbstract: Boolean ) : KtMember, AbstractKtNode(), KtWithMembers { + + override val accessModifier: AccessModifier = AccessModifier.PUBLIC + override fun accept(visitor: KtVisitor) { visitor.visitClassifier(this) } @@ -161,7 +166,8 @@ data class KtFunction( val isOverride: Boolean = false, val hasOpenModifier: Boolean = false, val isOperator: Boolean = false, - val isAbstract: Boolean = false + val isAbstract: Boolean = false, + override val accessModifier: AccessModifier = AccessModifier.PUBLIC ) : KtMember, KtExtensionAware, AbstractKtNode() { override fun accept(visitor: KtVisitor) { visitor.visitFunction(this) @@ -179,7 +185,8 @@ data class KtVariable( val needsNoImpl: Boolean = !isAbstract, val isInInterface: Boolean, val isOverride: Boolean = false, - val hasOpenModifier: Boolean + val hasOpenModifier: Boolean, + override val accessModifier: AccessModifier = AccessModifier.PUBLIC ) : KtMember, KtExtensionAware, AbstractKtNode() { override fun accept(visitor: KtVisitor) { visitor.visitVariable(this) @@ -187,6 +194,8 @@ data class KtVariable( } data class KtEnumEntry(override var name: KtName, val value: String? = null) : KtMember, AbstractKtNode() { + override val accessModifier: AccessModifier = AccessModifier.PUBLIC + override var annotations = listOf() override fun accept(visitor: KtVisitor) { @@ -251,3 +260,7 @@ data class KtTypeAnnotation(var type: KtType, val isVararg: Boolean = false) : A visitor.visitTypeAnnotation(this) } } + +enum class AccessModifier(val usable: Boolean) { + PUBLIC(true), PROTECTED(true), INTERNAL(false), PRIVATE(false) +} \ No newline at end of file diff --git a/testData/class/methods/accessModifierMethods.d.kt b/testData/class/methods/accessModifierMethods.d.kt new file mode 100644 index 0000000..da9fdc1 --- /dev/null +++ b/testData/class/methods/accessModifierMethods.d.kt @@ -0,0 +1,6 @@ +package accessModifierMethods + +external open class FooWithAccessModifierMethods { + open fun publicMethod(s: String): String = definedExternally + protected open fun protectedMethod(s: String): String = definedExternally +} diff --git a/testData/class/methods/accessModifierMethods.d.ts b/testData/class/methods/accessModifierMethods.d.ts new file mode 100644 index 0000000..5c6c13e --- /dev/null +++ b/testData/class/methods/accessModifierMethods.d.ts @@ -0,0 +1,5 @@ +declare class FooWithAccessModifierMethods { + public publicMethod(s: string): string; + protected protectedMethod(s: string): string; + private privateMethod(s: string): string; +} diff --git a/testData/class/variables/accessModifierVariables.d.kt b/testData/class/variables/accessModifierVariables.d.kt new file mode 100644 index 0000000..2c2a489 --- /dev/null +++ b/testData/class/variables/accessModifierVariables.d.kt @@ -0,0 +1,6 @@ +package accessModifierVariables + +external open class FooWithAccessModifierVariables { + open var publicVar: Number = definedExternally + protected open var protectedVar: Number = definedExternally +} diff --git a/testData/class/variables/accessModifierVariables.d.ts b/testData/class/variables/accessModifierVariables.d.ts new file mode 100644 index 0000000..257a0ee --- /dev/null +++ b/testData/class/variables/accessModifierVariables.d.ts @@ -0,0 +1,5 @@ +declare class FooWithAccessModifierVariables { + public publicVar: number; + protected protectedVar: number; + private privateVar: number; +}