Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Translate classes extending constructor variable (in stdlib cases) #405

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions compiler/test/data/typescript/stdlib/arrayWithoutParam.d.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// [test] arrayWithoutParam.kt
@file:Suppress("INTERFACE_WITH_SUPERCLASS", "OVERRIDING_FINAL_MEMBER", "RETURN_TYPE_MISMATCH_ON_OVERRIDE", "CONFLICTING_OVERLOADS")

import kotlin.js.*
import org.khronos.webgl.*
import org.w3c.dom.*
import org.w3c.dom.events.*
import org.w3c.dom.parsing.*
import org.w3c.dom.svg.*
import org.w3c.dom.url.*
import org.w3c.fetch.*
import org.w3c.files.*
import org.w3c.notifications.*
import org.w3c.performance.*
import org.w3c.workers.*
import org.w3c.xhr.*

typealias Table = Array<Any>
5 changes: 5 additions & 0 deletions compiler/test/data/typescript/stdlib/arrayWithoutParam.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
declare class Table extends Array {
constructor();
toString(): string;
static version: string;
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,11 @@ import org.jetbrains.dukat.model.commonLowerings.ModelLowering
import org.jetbrains.dukat.model.commonLowerings.ModelWithOwnerTypeLowering
import org.jetbrains.dukat.ownerContext.NodeOwner
import org.jetbrains.dukat.stdlib.TSLIBROOT
import org.jetbrains.dukat.stdlib.isKotlinStdlibPrefixed
import org.jetbrains.dukat.stdlib.isTsStdlibPrefixed

private fun TypeValueModel.isLibReference(): Boolean {
return fqName?.isTsStdlibPrefixed() == true
return fqName?.isTsStdlibPrefixed() == true || fqName?.isKotlinStdlibPrefixed() == true
}

private fun HeritageModel.isLibReference(): Boolean {
Expand Down
19 changes: 14 additions & 5 deletions typescript/ts-converter/src/AstConverter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -468,7 +468,7 @@ export class AstConverter {

let symbol = this.typeChecker.getSymbolAtLocation(type.typeName);
let typeReference: ReferenceEntity | null = null;
let declaration = this.getFirstDeclaration(symbol);
let declaration = this.getFirstDeclaration(symbol, params.length);

if (declaration) {
if (ts.isTypeParameterDeclaration(declaration)) {
Expand Down Expand Up @@ -617,6 +617,12 @@ export class AstConverter {
member.type ? this.convertType(member.type) : this.createTypeDeclaration("Unit"),
this.convertTypeParams(member.typeParameters)
)
} else if (ts.isConstructSignatureDeclaration(member)) {
return this.astFactory.createConstructSignatureDeclaration(
this.convertParameterDeclarations(member.parameters),
member.type ? this.convertType(member.type) : this.createTypeDeclaration("Unit"),
this.convertTypeParams(member.typeParameters)
)
}

return null;
Expand Down Expand Up @@ -740,15 +746,18 @@ export class AstConverter {
return this.astFactory.createQualifiedNameEntity(convertedExpression, name);
}

private getFirstDeclaration(symbol: ts.Symbol | null): ts.Declaration | null {
private getFirstDeclaration(symbol: ts.Symbol | null, typeParamsNum: number): ts.Declaration | null {
if (symbol == null) {
return null;
}

if (Array.isArray(symbol.declarations)) {
return symbol.declarations.find(decl => !ts.isModuleDeclaration(decl) &&
!ts.isVariableDeclaration(decl) && !ts.isPropertyDeclaration(decl) && !ts.isPropertySignature(decl) &&
!ts.isFunctionLike(decl))
!ts.isPropertyDeclaration(decl) && !ts.isPropertySignature(decl) &&
!ts.isFunctionLike(decl) && !(ts.isInterfaceDeclaration(decl) && decl.typeParameters && decl.typeParameters.filter(
param => !param.default
).length > typeParamsNum)
)
}

return null;
Expand Down Expand Up @@ -781,7 +790,7 @@ export class AstConverter {
}

let symbol = this.typeChecker.getSymbolAtLocation(type.expression);
let declaration = this.getFirstDeclaration(symbol);
let declaration = this.getFirstDeclaration(symbol, typeArguments.length);

// class can implement itself, but in overwhelming majority of cases this was not the intention of the declaration author - see https://stackoverflow.com/questions/62418219/class-implementing-itself-instead-of-inheriting-an-eponymous-interface-in-outer
if (declaration != parent) {
Expand Down
4 changes: 2 additions & 2 deletions typescript/ts-converter/src/Dependency.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export class TranslateSubsetOfSymbolsDependency implements Dependency {
symbols.forEach(node => {
let parent = node.parent;
while (parent) {
if (ts.isModuleDeclaration(parent)) {
if (ts.isModuleDeclaration(parent) || ts.isVariableStatement(parent)) {
parentUids.add(parent);
}
parent = parent.parent;
Expand Down Expand Up @@ -78,7 +78,7 @@ export class TranslateSubsetOfSymbolsDependency implements Dependency {
return true;
}

if (ts.isModuleDeclaration(node) && this.parentUids.has(node)) {
if ((ts.isModuleDeclaration(node) || ts.isVariableStatement(node)) && this.parentUids.has(node)) {
return true;
}

Expand Down
4 changes: 2 additions & 2 deletions typescript/ts-converter/src/DependencyBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ export class DependencyBuilder {

for (let declaration of declarations) {
if (this.checkedReferences.has(declaration)) {
return;
continue;
}
this.checkedReferences.add(declaration);
let sourceFile = declaration.getSourceFile();
Expand Down Expand Up @@ -103,7 +103,7 @@ export class DependencyBuilder {
this.checkReferences(node.type)
} else if (ts.isHeritageClause(node)) {
for (let type of node.types) {
this.checkReferences(type);
this.checkReferences(type.expression);
}
} else if (ts.isExportDeclaration(node)) {
if (node.exportClause) {
Expand Down
2 changes: 1 addition & 1 deletion typescript/ts-converter/src/ExportContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ function resolveName(node: ts.Node): string | null {
return null;
}

export function resolveDeclarations(node: ts.Identifier, typeChecker: ts.TypeChecker): Array<ts.Node> {
export function resolveDeclarations(node: ts.Node, typeChecker: ts.TypeChecker): Array<ts.Node> {
let symbolAtLocation = typeChecker.getSymbolAtLocation(node);
if (symbolAtLocation) {

Expand Down
12 changes: 12 additions & 0 deletions typescript/ts-converter/src/ast/AstFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import {
CaseDeclarationProto,
ClassDeclarationProto,
ConstructorDeclarationProto,
ConstructSignatureDeclarationProto,
ContinueStatementDeclarationProto,
DefinitionInfoDeclarationProto,
EnumDeclarationProto,
Expand Down Expand Up @@ -143,6 +144,17 @@ export class AstFactory {
return memberProto;
}

createConstructSignatureDeclaration(parameters: Array<ParameterDeclaration>, type: TypeDeclaration, typeParams: Array<TypeParameter>): MemberDeclaration {
let callSignature = new ConstructSignatureDeclarationProto();
callSignature.setParametersList(parameters);
callSignature.setType(type);
callSignature.setTypeparametersList(typeParams);

let memberProto = new MemberDeclarationProto();
memberProto.setConstructsignature(callSignature);
return memberProto;
}

createClassDeclaration(name: NameEntity, members: Array<MemberDeclaration>, typeParams: Array<TypeParameter>, parentEntities: Array<HeritageClauseDeclaration>, modifiers: Array<ModifierDeclaration>, definitions: Array<DefinitionInfoDeclaration>, uid: string): ClassDeclaration {
let classDeclaration = new ClassDeclarationProto();
classDeclaration.setName(name);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import org.jetbrains.dukat.ownerContext.wrap
import org.jetbrains.dukat.tsmodel.CallSignatureDeclaration
import org.jetbrains.dukat.tsmodel.ClassDeclaration
import org.jetbrains.dukat.tsmodel.ClassLikeDeclaration
import org.jetbrains.dukat.tsmodel.ConstructSignatureDeclaration
import org.jetbrains.dukat.tsmodel.ConstructorDeclaration
import org.jetbrains.dukat.tsmodel.Declaration
import org.jetbrains.dukat.tsmodel.FunctionDeclaration
Expand Down Expand Up @@ -68,6 +69,16 @@ interface DeclarationLowering : TopLevelDeclarationLowering, DeclarationStatemen
)
}

fun lowerConstructSignatureDeclaration(declaration: ConstructSignatureDeclaration, owner: NodeOwner<MemberDeclaration>?): ConstructSignatureDeclaration {
return declaration.copy(
type = lowerParameterValue(declaration.type, owner.wrap(declaration)),
parameters = declaration.parameters.map { parameter -> lowerParameterDeclaration(parameter, owner.wrap(declaration)) },
typeParameters = declaration.typeParameters.map { typeParameter ->
typeParameter.copy(constraints = typeParameter.constraints.map { constraint -> lowerParameterValue(constraint, owner.wrap(declaration)) })
}
)
}

fun lowerIndexSignatureDeclaration(declaration: IndexSignatureDeclaration, owner: NodeOwner<MemberDeclaration>?): IndexSignatureDeclaration {
return declaration.copy(
parameters = declaration.parameters.map { indexType -> lowerParameterDeclaration(indexType, owner.wrap(declaration)) },
Expand All @@ -84,6 +95,7 @@ interface DeclarationLowering : TopLevelDeclarationLowering, DeclarationStatemen
is MethodSignatureDeclaration -> lowerMethodSignatureDeclaration(declaration, newOwner)
is MethodDeclaration -> lowerMethodDeclaration(declaration, newOwner)
is CallSignatureDeclaration -> lowerCallSignatureDeclaration(declaration, newOwner)
is ConstructSignatureDeclaration -> lowerConstructSignatureDeclaration(declaration, newOwner)
is IndexSignatureDeclaration -> lowerIndexSignatureDeclaration(declaration, newOwner)
else -> {
logger.debug("[${this}] skipping ${declaration}")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package org.jetbrains.dukat.tsLowerings

import org.jetbrains.dukat.astCommon.IdentifierEntity
import org.jetbrains.dukat.ownerContext.NodeOwner
import org.jetbrains.dukat.tsmodel.ClassLikeDeclaration
import org.jetbrains.dukat.tsmodel.ConstructSignatureDeclaration
import org.jetbrains.dukat.tsmodel.HeritageClauseDeclaration
import org.jetbrains.dukat.tsmodel.InterfaceDeclaration
import org.jetbrains.dukat.tsmodel.ModuleDeclaration
import org.jetbrains.dukat.tsmodel.ParameterOwnerDeclaration
import org.jetbrains.dukat.tsmodel.SourceSetDeclaration
import org.jetbrains.dukat.tsmodel.VariableDeclaration
import org.jetbrains.dukat.tsmodel.types.ParameterValueDeclaration
import org.jetbrains.dukat.tsmodel.types.TypeDeclaration

private class ReplaceTypesLowering(private val typesToReplace: Map<String, ParameterValueDeclaration>, private val depth: Int) : DeclarationLowering {
private val newTypesToReplace = mutableMapOf<String, ParameterValueDeclaration>()

private fun findNewType(type: ParameterValueDeclaration): ParameterValueDeclaration? {
if (type is TypeDeclaration && type.typeReference != null) {
val uid = type.typeReference!!.uid
if (typesToReplace.containsKey(uid)) {
return typesToReplace.getValue(uid)
}
}
return null
}

override fun lowerParameterValue(
declaration: ParameterValueDeclaration,
owner: NodeOwner<ParameterOwnerDeclaration>?
): ParameterValueDeclaration {
return findNewType(declaration) ?: declaration
}

override fun lowerHeritageClause(
heritageClause: HeritageClauseDeclaration,
owner: NodeOwner<ClassLikeDeclaration>?
): HeritageClauseDeclaration {
val reference = heritageClause.typeReference
if (reference != null && typesToReplace.containsKey(reference.uid)) {
val newType = typesToReplace.getValue(reference.uid)
if (newType is TypeDeclaration) {
return heritageClause.copy(name = newType.value, typeArguments = newType.params, typeReference = newType.typeReference)
}
}
return heritageClause
}

override fun lowerVariableDeclaration(declaration: VariableDeclaration, owner: NodeOwner<ModuleDeclaration>?): VariableDeclaration {
val newType = findNewType(declaration.type)
if (newType != null) {
newTypesToReplace[declaration.uid] = newType
return declaration.copy(type = newType)
}
return declaration
}

override fun lower(source: SourceSetDeclaration): SourceSetDeclaration {
val newSource = super.lower(source)
return if (depth == 0) {
ReplaceTypesLowering(newTypesToReplace, depth + 1).lower(newSource)
} else {
newSource
}
}
}

private class ProcessConstructorInterfacesLowering : DeclarationLowering {

private val typesToReplace = mutableMapOf<String, ParameterValueDeclaration>()

private fun determineCommonReturnType(constructors: List<ConstructSignatureDeclaration>): ParameterValueDeclaration? {
return constructors[0].type
}

override fun lowerInterfaceDeclaration(
declaration: InterfaceDeclaration,
owner: NodeOwner<ModuleDeclaration>?
): InterfaceDeclaration {
val constructSignatures = declaration.members.filterIsInstance<ConstructSignatureDeclaration>()
if (constructSignatures.isEmpty()) {
return declaration
}
val commonReturnType = determineCommonReturnType(constructSignatures)
if (commonReturnType is TypeDeclaration && commonReturnType.value == IdentifierEntity("Array")) {
typesToReplace[declaration.uid] = commonReturnType
}
return declaration
}

override fun lower(source: SourceSetDeclaration): SourceSetDeclaration {
val newSource = super.lower(source)
return ReplaceTypesLowering(typesToReplace, 0).lower(newSource)
}
}

class ProcessConstructorInterfaces : TsLowering {
override fun lower(source: SourceSetDeclaration): SourceSetDeclaration {
return ProcessConstructorInterfacesLowering().lower(source)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import org.jetbrains.dukat.tsmodel.GeneratedInterfaceReferenceDeclaration
import org.jetbrains.dukat.tsmodel.ImportEqualsDeclaration
import org.jetbrains.dukat.tsmodel.InterfaceDeclaration
import org.jetbrains.dukat.tsmodel.CallableMemberDeclaration
import org.jetbrains.dukat.tsmodel.ConstructSignatureDeclaration
import org.jetbrains.dukat.tsmodel.MethodDeclaration
import org.jetbrains.dukat.tsmodel.MethodSignatureDeclaration
import org.jetbrains.dukat.tsmodel.ModuleDeclaration
Expand Down Expand Up @@ -66,6 +67,7 @@ private fun CallableMemberDeclaration.copyWithParams(params: List<ParameterDecla
is CallSignatureDeclaration -> copy(parameters = params)
is IndexSignatureDeclaration -> copy(parameters = params)
is MethodDeclaration -> copy(parameters = params)
is ConstructSignatureDeclaration -> copy(parameters = params)
else -> raiseConcern("can not update params for ${this}") { this }
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -245,14 +245,14 @@ internal class DocumentConverter(
typeResolved.value == aliasName
}

private fun ReferenceEntity.getFqName(): NameEntity? {
return uidToNameMapper[uid]?.fqName
private fun ReferenceEntity?.getFqName(ownerName: NameEntity): NameEntity? {
return this?.let { uidToNameMapper[uid]?.fqName } ?: if (KotlinStdlibEntities.contains(ownerName)) {
KLIBROOT.appendLeft(ownerName)
} else null
}

private fun TypeDeclaration.getFqName(): NameEntity? {
return typeReference?.getFqName() ?: if (KotlinStdlibEntities.contains(value)) {
KLIBROOT.appendLeft(value)
} else null
return typeReference.getFqName(value)
}

private fun ClassLikeDeclaration.processMembers(): Members {
Expand All @@ -272,15 +272,15 @@ internal class DocumentConverter(
private fun HeritageClauseDeclaration.convertToModel(): HeritageModel {
val isNamedImport = typeReference?.origin == ReferenceOriginDeclaration.NAMED_IMPORT
if (isNamedImport) {
typeReference?.getFqName()?.let { resolvedName ->
typeReference.getFqName(name)?.let { resolvedName ->
imports.add(ImportModel(resolvedName, name.rightMost()))
}
}

val fqName = if (isNamedImport) {
name
} else {
typeReference?.getFqName()
typeReference.getFqName(name)
}
return HeritageModel(
value = TypeValueModel(name, emptyList(), null, fqName),
Expand Down Expand Up @@ -407,7 +407,7 @@ internal class DocumentConverter(
name,
typeParams,
meta?.processMeta(),
typeReference?.getFqName(),
typeReference.getFqName(name),
nullable
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import org.jetbrains.dukat.astCommon.TopLevelEntity
import org.jetbrains.dukat.panic.raiseConcern
import org.jetbrains.dukat.tsmodel.CallSignatureDeclaration
import org.jetbrains.dukat.tsmodel.ClassDeclaration
import org.jetbrains.dukat.tsmodel.ConstructSignatureDeclaration
import org.jetbrains.dukat.tsmodel.ConstructorDeclaration
import org.jetbrains.dukat.tsmodel.EnumDeclaration
import org.jetbrains.dukat.tsmodel.FunctionDeclaration
Expand Down Expand Up @@ -34,6 +35,7 @@ fun convertMemberDeclaration(declaration: MemberEntity, inDeclaredDeclaration: B
is PropertyDeclaration -> convertPropertyDeclaration(declaration, inDeclaredDeclaration)
is IndexSignatureDeclaration -> declaration
is ConstructorDeclaration -> declaration
is ConstructSignatureDeclaration -> null
else -> raiseConcern("unknown member declaration ${declaration::class.java}") { null }
}
}
Expand Down
Loading