diff --git a/migrator/build.xml b/migrator/build.xml index a3d1b797c671fa87db7eb15afb019b0056da2c54..a16ddfa0782124b72b3bff52e148dc4e66875fa0 100644 --- a/migrator/build.xml +++ b/migrator/build.xml @@ -202,6 +202,38 @@ ${test.err} + + + + + + + + + + + + + + + + One or several tests have failed! + STDERR: + ${test.err} + + + + + + + + diff --git a/migrator/lib/kotlin-compiler/kotlin-test.jar b/migrator/lib/kotlin-compiler/kotlin-test.jar new file mode 100644 index 0000000000000000000000000000000000000000..de1035f93d1fc1f2a85c9ead01a564d4bf4d5d04 Binary files /dev/null and b/migrator/lib/kotlin-compiler/kotlin-test.jar differ diff --git a/migrator/src/com/ohos/migrator/kotlin/KotlinTransformer.java b/migrator/src/com/ohos/migrator/kotlin/KotlinTransformer.java index 168912365fda6c3c08e9a3b14a7663dd9d45040c..1c05654be427c8de25eec84573fee9449a63fc85 100644 --- a/migrator/src/com/ohos/migrator/kotlin/KotlinTransformer.java +++ b/migrator/src/com/ohos/migrator/kotlin/KotlinTransformer.java @@ -15,35 +15,172 @@ package com.ohos.migrator.kotlin; +import com.intellij.openapi.util.TextRange; +import com.ohos.migrator.Main; +import com.ohos.migrator.ResultCode; import com.ohos.migrator.Transformer; +import com.ohos.migrator.staticTS.NodeBuilder; +import com.ohos.migrator.staticTS.parser.StaticTSParser; import com.ohos.migrator.staticTS.parser.StaticTSParser.*; import org.antlr.v4.runtime.ParserRuleContext; +import org.antlr.v4.runtime.tree.TerminalNode; + +import com.intellij.psi.PsiElement; import org.jetbrains.kotlin.analyzer.AnalysisResult; -import org.jetbrains.kotlin.psi.KtFile; -import org.jetbrains.kotlin.psi.KtVisitor; +import org.jetbrains.kotlin.diagnostics.PsiDiagnosticUtils; +import org.jetbrains.kotlin.lexer.KtTokens; +import org.jetbrains.kotlin.psi.*; +import org.jetbrains.kotlin.psi.psiUtil.KtPsiUtilKt; +import org.jetbrains.kotlin.psi.psiUtil.PsiUtilsKt; import org.jetbrains.kotlin.resolve.BindingContext; +import java.io.File; + /** * Performs transformation of the Kotlin AST into StaticTS AST. */ public class KotlinTransformer extends KtVisitor implements Transformer { + File srcFile; KtFile ktFile; AnalysisResult analysisResult; BindingContext bindingContext; + CompilationUnitContext stsCU; - public KotlinTransformer(KtFile ktFile, AnalysisResult analysisResult) { + public KotlinTransformer(KtFile ktFile, File srcFile, AnalysisResult analysisResult) { + this.srcFile = srcFile; this.ktFile = ktFile; this.analysisResult = analysisResult; this.bindingContext = analysisResult.getBindingContext(); } public CompilationUnitContext transform() { - return (CompilationUnitContext)visitKtFile(ktFile, null); + return visitKtFile(ktFile, null); + } + + private PsiDiagnosticUtils.LineAndColumn getLineAndColumn(PsiElement psiElement) { + TextRange textRange; + + if (psiElement == null) { + return PsiDiagnosticUtils.LineAndColumn.NONE; + } + + if (psiElement instanceof KtDeclaration) { + // If any comment precedes function or class declaration, then it will + // be considered a part of declaration and will affect the calculation + // of node's line number. + textRange = PsiUtilsKt.getTextRangeWithoutComments(psiElement); + } else { + textRange = psiElement.getTextRange(); + } + + return PsiDiagnosticUtils.offsetToLineAndColumn(ktFile.getViewProvider().getDocument(), textRange.getStartOffset()); + } + + private void reportError(String message, PsiElement ktNode) { + String loc = srcFile.getPath() + ":" + getLineAndColumn(ktNode); + Main.addError(ResultCode.TranspileError, message + " at " + loc); + } + + + /** + * Visit element's children and add result of translation of each child to the specified STS context. + */ + private void visitChildren(KtElement ktElement, ParserRuleContext stsContext) { + for (PsiElement child : ktElement.getChildren()) { + if (child instanceof KtElement) { + ParserRuleContext stsResult = ((KtElement) child).accept(this, null); + + if (stsResult != null && stsContext != null) { + stsContext.addChild(stsResult).setParent(stsContext); + } + } + } + } + + // Default visit method for kotlin node. Simply visit all children of the element. + @Override + public ParserRuleContext visitKtElement(KtElement ktElement, Void data) { + visitChildren(ktElement, null); + return null; } @Override - public ParserRuleContext visitKtFile(KtFile node, Void data) { - return new CompilationUnitContext(null, 0); + public CompilationUnitContext visitKtFile(KtFile ktFile, Void data) { + stsCU = new CompilationUnitContext(null, 0); + visitChildren(ktFile, stsCU); + return stsCU; } + + @Override + public ParserRuleContext visitPackageDirective(KtPackageDirective ktPackageDirective, Void data) { + PackageDeclarationContext stsPackage = new PackageDeclarationContext(null, 0); + stsPackage.addChild(NodeBuilder.terminalNode(StaticTSParser.Package)); + stsPackage.addChild(NodeBuilder.qualifiedName(ktPackageDirective.getQualifiedName())).setParent(stsPackage); + return stsPackage; + } + + @Override + public ParserRuleContext visitImportDirective(KtImportDirective ktImportDirective, Void data) { + ImportDeclarationContext stsImport = new ImportDeclarationContext(null, 0); + stsImport.addChild(NodeBuilder.terminalNode(StaticTSParser.Import)); + stsImport.addChild(NodeBuilder.qualifiedName(ktImportDirective.getImportedFqName().asString())).setParent(stsImport); + + String alias = ktImportDirective.getAliasName(); + if (alias != null && !alias.isEmpty()) { + stsImport.addChild(NodeBuilder.terminalNode(StaticTSParser.As)); + stsImport.addChild(NodeBuilder.terminalIdentifier(alias)); + } + else if (ktImportDirective.isAllUnder()) { + stsImport.addChild(NodeBuilder.terminalNode(StaticTSParser.Dot)); + stsImport.addChild(NodeBuilder.terminalNode(StaticTSParser.Multiply)); + } + + stsCU.addChild(stsImport); + + return null; + } + + @Override + public ParserRuleContext visitClass(KtClass ktClass, Void data) { + ParserRuleContext stsResult; + +// if (ktClass.isInterface()) { +// stsResult = new InterfaceDeclarationContext(null, 0); +// stsResult.addChild(NodeBuilder.terminalNode(StaticTSParser.Interface)); +// } else { + stsResult = new ClassDeclarationContext(null, 0); + stsResult.addChild(NodeBuilder.terminalNode(StaticTSParser.Class)); +// } + + if (KtPsiUtilKt.isAbstract(ktClass)) + stsResult.addChild(NodeBuilder.terminalNode(StaticTSParser.Abstract)); + else if (ktClass.hasModifier(KtTokens.OPEN_KEYWORD)) + stsResult.addChild(NodeBuilder.terminalNode(StaticTSParser.Open)); + + + stsResult.addChild(NodeBuilder.terminalIdentifier(ktClass.getName())); + + ClassBodyContext stsClassBody = new ClassBodyContext(stsResult, 0); + stsResult.addChild(stsClassBody).setParent(stsResult); + + visitChildren(ktClass, stsClassBody); + + if (ktClass.isTopLevel()) { + TopDeclarationContext stsTopDecl = new TopDeclarationContext(null, 0); + + if (KtPsiUtilKt.isPublic(ktClass)) { + stsTopDecl.addChild(NodeBuilder.terminalNode(StaticTSParser.Export)); + } + + stsTopDecl.addChild(stsResult).setParent(stsTopDecl); + stsResult = stsTopDecl; + } + // else { + // TODO: inner, nested, local + // } + + return stsResult; + } + } \ No newline at end of file diff --git a/migrator/src/com/ohos/migrator/kotlin/KotlinTranspiler.java b/migrator/src/com/ohos/migrator/kotlin/KotlinTranspiler.java index ec393871f92e8834e8e13e31a0f562646c4831c4..a9087d65d3eb5430f83e899b41029fbc9e5ca1c0 100644 --- a/migrator/src/com/ohos/migrator/kotlin/KotlinTranspiler.java +++ b/migrator/src/com/ohos/migrator/kotlin/KotlinTranspiler.java @@ -16,9 +16,11 @@ package com.ohos.migrator.kotlin; import com.ohos.migrator.AbstractTranspiler; +import com.ohos.migrator.Main; import com.ohos.migrator.ResultCode; import com.ohos.migrator.TranspileException; import com.ohos.migrator.staticTS.parser.StaticTSParser.CompilationUnitContext; + import com.intellij.openapi.Disposable; import com.intellij.openapi.util.Disposer; import org.jetbrains.kotlin.analyzer.AnalysisResult; @@ -36,10 +38,14 @@ import org.jetbrains.kotlin.config.CommonConfigurationKeys; import org.jetbrains.kotlin.config.CompilerConfiguration; import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmProtoBufUtil; import org.jetbrains.kotlin.psi.KtFile; +import org.jetbrains.kotlin.utils.PathUtil; import java.io.File; import java.io.IOException; +import java.net.URISyntaxException; import java.util.*; +import java.util.jar.JarFile; +import java.util.jar.Manifest; /** * Kotlin to StaticTS transpiler class inherited from AbstractTranspiler class. @@ -54,6 +60,8 @@ public class KotlinTranspiler extends AbstractTranspiler { CompilerConfiguration configuration; KotlinCoreEnvironment environment; + String MANIFEST_IMPLEMENTATION_TITLE = "Implementation-Title"; + static { org.jetbrains.kotlin.cli.common.environment.UtilKt.setIdeaIoUseFallback(); } @@ -116,8 +124,50 @@ public class KotlinTranspiler extends AbstractTranspiler { // Add user-defined classpaths. JvmContentRootsKt.addJvmClasspathRoots(configuration, libFiles); - // TODO: Add Kotlin standard library? - // TODO: Add JDK standard library? + // Configure kotlin libraries. + JvmContentRootsKt.addJvmClasspathRoots(configuration, getKotlinJars()); + + // Configure JDK classpaths. + JvmContentRootsKt.configureJdkClasspathRoots(configuration); + } + + private List getKotlinJars() { + // NOTE: The Kotlin libraries are currently placed in the same + // directory as the application jar. + File[] jarFiles = null; + try { + File libDir = new File(Main.class.getProtectionDomain().getCodeSource().getLocation().toURI()).getParentFile(); + jarFiles = libDir.listFiles(file -> isBuiltinKotlinJar(getManifestImplementationTitle(file))); + } catch (URISyntaxException e1) + {} + + /* Just to be safe */ + if (jarFiles == null) { + return new ArrayList<>(); + } + return Arrays.asList(jarFiles); + } + + private boolean isBuiltinKotlinJar(String name) { + return (name != null && + (name.startsWith(PathUtil.KOTLIN_JAVA_STDLIB_NAME) || // kotlin-stdlib + name.equals(PathUtil.KOTLIN_TEST_NAME) || // kotlin-test + name.equals(PathUtil.KOTLIN_JAVA_REFLECT_NAME) || // kotlin-reflect + name.equals(PathUtil.KOTLIN_JAVA_SCRIPT_RUNTIME_NAME))); // kotlin-script-runtime + } + + private String getManifestImplementationTitle(File file){ + try { + JarFile jarFile = new JarFile(file); + Manifest manifest = jarFile.getManifest(); + + if (manifest == null) { + return null; + } + return manifest.getMainAttributes().getValue(MANIFEST_IMPLEMENTATION_TITLE); + } catch (IOException e) { + return null; + } } @Override @@ -129,7 +179,7 @@ public class KotlinTranspiler extends AbstractTranspiler { AnalysisResult analysisResult = analyze(sourceToKotlin.values()); for (Map.Entry entry : sourceToKotlin.entrySet()) { - CompilationUnitContext stsCU = transform(entry.getValue(), analysisResult); + CompilationUnitContext stsCU = transform(entry.getValue(), entry.getKey(), analysisResult); write(stsCU, entry.getKey()); } } catch (Exception e) { @@ -177,8 +227,8 @@ public class KotlinTranspiler extends AbstractTranspiler { return analyzer.getAnalysisResult(); } - private CompilationUnitContext transform(KtFile ktFile, AnalysisResult analysisResult) { - KotlinTransformer transformer = new KotlinTransformer(ktFile, analysisResult); + private CompilationUnitContext transform(KtFile ktFile, File srcFile, AnalysisResult analysisResult) { + KotlinTransformer transformer = new KotlinTransformer(ktFile, srcFile, analysisResult); return transformer.transform(); } diff --git a/migrator/src/com/ohos/migrator/staticTS/writer/StaticTSWriter.java b/migrator/src/com/ohos/migrator/staticTS/writer/StaticTSWriter.java index 3fba2b340c05af650cc40a037126c2d75d643142..44b7c04f3e20d1234288321b69d7b34e9f5b63d4 100644 --- a/migrator/src/com/ohos/migrator/staticTS/writer/StaticTSWriter.java +++ b/migrator/src/com/ohos/migrator/staticTS/writer/StaticTSWriter.java @@ -750,7 +750,7 @@ public class StaticTSWriter extends StaticTSParserBaseVisitor { sb.append(termMult.getText()); } else if (termAs != null) { - sb.append(termAs.getText()).append(' '); + sb.append(' ').append(termAs.getText()).append(' '); TerminalNode termId = stsImportDeclatation.Identifier(); assert(termId != null); sb.append(termId.getText()); diff --git a/migrator/test/kotlin/empty_class_declaration.kt b/migrator/test/kotlin/empty_class_declaration.kt new file mode 100644 index 0000000000000000000000000000000000000000..2509632ccd9876fa8f3a32dc70a9cdb8b26f7ee0 --- /dev/null +++ b/migrator/test/kotlin/empty_class_declaration.kt @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2022-2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.ohos.migrator.test.kotlin; + +class Empty + +class EmptyWithBody { } + +abstract class AbstractClass + +open class OpenClass + +final class FinalClass + +public class PublicClass + +private class PrivateClass + +internal class InternalClass \ No newline at end of file diff --git a/migrator/test/kotlin/empty_class_declaration.kt.sts b/migrator/test/kotlin/empty_class_declaration.kt.sts new file mode 100644 index 0000000000000000000000000000000000000000..b6c4a7ba783014c469544b820a484cb9c78b33d0 --- /dev/null +++ b/migrator/test/kotlin/empty_class_declaration.kt.sts @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2022-2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.ohos.migrator.test.kotlin; + +export class Empty { +} + +export class EmptyWithBody { +} + +export abstract class AbstractClass { +} + +export open class OpenClass { +} + +export class FinalClass { +} + +export class PublicClass { +} + +class PrivateClass { +} + +class InternalClass { +} + + diff --git a/migrator/test/kotlin/imports.kt b/migrator/test/kotlin/imports.kt new file mode 100644 index 0000000000000000000000000000000000000000..b9339a12cb106b908b538e420e7a274d3457665a --- /dev/null +++ b/migrator/test/kotlin/imports.kt @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2022-2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.ohos.migrator.test.kotlin; + +import kotlin.random.Random; // Random is now accessible without qualification +import kotlin.math.*; // everything in 'kotlin.math' becomes accessible +import kotlin.reflect.KClass as Class // 'Class' stands for kotlin.reflect.KClass + +fun main() { + println(PI); + + var rnd : Random = Random(5); + + var kk : Class; +} \ No newline at end of file diff --git a/migrator/test/kotlin/imports.kt.sts b/migrator/test/kotlin/imports.kt.sts new file mode 100644 index 0000000000000000000000000000000000000000..38e0ee32a1892db52ec7bc3675e5294115354dae --- /dev/null +++ b/migrator/test/kotlin/imports.kt.sts @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2022-2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.ohos.migrator.test.kotlin; + +import kotlin.random.Random; +import kotlin.math.*; +import kotlin.reflect.KClass as Class; \ No newline at end of file