From 9f92b0b5d7c0b034050464410868cd8705128704 Mon Sep 17 00:00:00 2001 From: pTalanov Date: Wed, 25 Apr 2012 20:08:55 +0400 Subject: [PATCH] Refactor cli: all the jvm stuff goes under org.jetbrains.jet.cli.jvm, common stuff under org.jetbrains.jet.cli.common --- .../jet/cli/jvm/compiler/TipsManager.java | 211 +++++++ .../jet/cli/common/CompilerPlugin.java | 25 + .../jet/cli/common/CompilerPluginContext.java | 51 ++ .../jetbrains/jet/cli/common/ExitCode.java | 36 ++ .../messages/AnalyzerWithCompilerReport.java | 151 +++++ .../messages/CompilerMessageLocation.java | 57 ++ .../messages/CompilerMessageSeverity.java | 32 + .../cli/common/messages/MessageCollector.java | 31 + .../cli/common/messages/MessageRenderer.java | 99 +++ .../jet/cli/common/messages/MessageUtil.java | 32 + .../messages/PrintingMessageCollector.java | 73 +++ .../jetbrains/jet/cli/js/K2JSCompiler.java | 27 + .../jet/cli/js/K2JSCompilerArguments.java | 24 + .../jetbrains/jet/cli/jvm/K2JVMCompiler.java | 255 ++++++++ .../jet/cli/jvm/K2JVMCompilerArguments.java | 165 +++++ .../jet/cli/jvm/K2JVMCompilerVersion.java | 26 + .../cli/jvm/compiler/CliJetFilesProvider.java | 60 ++ .../CompileEnvironmentConfiguration.java | 66 ++ .../compiler/CompileEnvironmentException.java | 34 ++ .../jvm/compiler/CompileEnvironmentUtil.java | 340 +++++++++++ .../cli/jvm/compiler/JetCoreEnvironment.java | 155 +++++ .../compiler/KotlinToJVMBytecodeCompiler.java | 282 +++++++++ .../compiler/ModuleExecutionException.java | 34 ++ .../jvm/compiler/CompileEnvironmentTest.java | 104 ++++ .../CompileJavaAgainstKotlinTest.java | 113 ++++ .../CompileKotlinAgainstKotlinTest.java | 133 ++++ .../compiler/JavaDescriptorResolverTest.java | 72 +++ .../cli/jvm/compiler/NamespaceComparator.java | 568 ++++++++++++++++++ .../jvm/compiler/ReadJavaBinaryClassTest.java | 132 ++++ .../compiler/ReadKotlinBinaryClassTest.java | 110 ++++ .../cli/jvm/compiler/TestCaseWithTmpdir.java | 37 ++ .../cli/jvm/compiler/WriteSignatureTest.java | 316 ++++++++++ .../jvm/compiler/longTest/LibFromMaven.java | 57 ++ ...solveDescriptorsFromExternalLibraries.java | 199 ++++++ .../jet/plugin/compiler/K2JSCompiler.java | 64 ++ 35 files changed, 4171 insertions(+) create mode 100644 compiler/backend/src/org/jetbrains/jet/cli/jvm/compiler/TipsManager.java create mode 100644 compiler/cli/src/org/jetbrains/jet/cli/common/CompilerPlugin.java create mode 100644 compiler/cli/src/org/jetbrains/jet/cli/common/CompilerPluginContext.java create mode 100644 compiler/cli/src/org/jetbrains/jet/cli/common/ExitCode.java create mode 100644 compiler/cli/src/org/jetbrains/jet/cli/common/messages/AnalyzerWithCompilerReport.java create mode 100644 compiler/cli/src/org/jetbrains/jet/cli/common/messages/CompilerMessageLocation.java create mode 100644 compiler/cli/src/org/jetbrains/jet/cli/common/messages/CompilerMessageSeverity.java create mode 100644 compiler/cli/src/org/jetbrains/jet/cli/common/messages/MessageCollector.java create mode 100644 compiler/cli/src/org/jetbrains/jet/cli/common/messages/MessageRenderer.java create mode 100644 compiler/cli/src/org/jetbrains/jet/cli/common/messages/MessageUtil.java create mode 100644 compiler/cli/src/org/jetbrains/jet/cli/common/messages/PrintingMessageCollector.java create mode 100644 compiler/cli/src/org/jetbrains/jet/cli/js/K2JSCompiler.java create mode 100644 compiler/cli/src/org/jetbrains/jet/cli/js/K2JSCompilerArguments.java create mode 100644 compiler/cli/src/org/jetbrains/jet/cli/jvm/K2JVMCompiler.java create mode 100644 compiler/cli/src/org/jetbrains/jet/cli/jvm/K2JVMCompilerArguments.java create mode 100644 compiler/cli/src/org/jetbrains/jet/cli/jvm/K2JVMCompilerVersion.java create mode 100644 compiler/cli/src/org/jetbrains/jet/cli/jvm/compiler/CliJetFilesProvider.java create mode 100644 compiler/cli/src/org/jetbrains/jet/cli/jvm/compiler/CompileEnvironmentConfiguration.java create mode 100644 compiler/cli/src/org/jetbrains/jet/cli/jvm/compiler/CompileEnvironmentException.java create mode 100644 compiler/cli/src/org/jetbrains/jet/cli/jvm/compiler/CompileEnvironmentUtil.java create mode 100644 compiler/cli/src/org/jetbrains/jet/cli/jvm/compiler/JetCoreEnvironment.java create mode 100644 compiler/cli/src/org/jetbrains/jet/cli/jvm/compiler/KotlinToJVMBytecodeCompiler.java create mode 100644 compiler/cli/src/org/jetbrains/jet/cli/jvm/compiler/ModuleExecutionException.java create mode 100644 compiler/tests/org/jetbrains/jet/cli/jvm/compiler/CompileEnvironmentTest.java create mode 100644 compiler/tests/org/jetbrains/jet/cli/jvm/compiler/CompileJavaAgainstKotlinTest.java create mode 100644 compiler/tests/org/jetbrains/jet/cli/jvm/compiler/CompileKotlinAgainstKotlinTest.java create mode 100644 compiler/tests/org/jetbrains/jet/cli/jvm/compiler/JavaDescriptorResolverTest.java create mode 100644 compiler/tests/org/jetbrains/jet/cli/jvm/compiler/NamespaceComparator.java create mode 100644 compiler/tests/org/jetbrains/jet/cli/jvm/compiler/ReadJavaBinaryClassTest.java create mode 100644 compiler/tests/org/jetbrains/jet/cli/jvm/compiler/ReadKotlinBinaryClassTest.java create mode 100644 compiler/tests/org/jetbrains/jet/cli/jvm/compiler/TestCaseWithTmpdir.java create mode 100644 compiler/tests/org/jetbrains/jet/cli/jvm/compiler/WriteSignatureTest.java create mode 100644 compiler/tests/org/jetbrains/jet/cli/jvm/compiler/longTest/LibFromMaven.java create mode 100644 compiler/tests/org/jetbrains/jet/cli/jvm/compiler/longTest/ResolveDescriptorsFromExternalLibraries.java create mode 100644 idea/src/org/jetbrains/jet/plugin/compiler/K2JSCompiler.java diff --git a/compiler/backend/src/org/jetbrains/jet/cli/jvm/compiler/TipsManager.java b/compiler/backend/src/org/jetbrains/jet/cli/jvm/compiler/TipsManager.java new file mode 100644 index 00000000000..3e7972e5e28 --- /dev/null +++ b/compiler/backend/src/org/jetbrains/jet/cli/jvm/compiler/TipsManager.java @@ -0,0 +1,211 @@ +/* + * Copyright 2010-2012 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. + * 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 org.jetbrains.jet.cli.jvm.compiler; + +import com.google.common.base.Predicate; +import com.google.common.collect.Collections2; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.lang.descriptors.CallableDescriptor; +import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; +import org.jetbrains.jet.lang.descriptors.NamespaceDescriptor; +import org.jetbrains.jet.lang.psi.JetExpression; +import org.jetbrains.jet.lang.psi.JetImportDirective; +import org.jetbrains.jet.lang.psi.JetNamespaceHeader; +import org.jetbrains.jet.lang.psi.JetSimpleNameExpression; +import org.jetbrains.jet.lang.resolve.BindingContext; +import org.jetbrains.jet.lang.resolve.calls.autocasts.AutoCastServiceImpl; +import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo; +import org.jetbrains.jet.lang.resolve.scopes.JetScope; +import org.jetbrains.jet.lang.resolve.scopes.JetScopeUtils; +import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver; +import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor; +import org.jetbrains.jet.lang.types.JetType; +import org.jetbrains.jet.lang.types.NamespaceType; +import org.jetbrains.jet.lang.types.expressions.ExpressionTypingUtils; + +import java.util.*; + +/** + * @author Nikolay Krasko, Alefas + */ +public final class TipsManager { + + private TipsManager() { + } + + @NotNull + public static Collection getReferenceVariants(JetSimpleNameExpression expression, BindingContext context) { + JetExpression receiverExpression = expression.getReceiverExpression(); + if (receiverExpression != null) { + // Process as call expression + final JetScope resolutionScope = context.get(BindingContext.RESOLUTION_SCOPE, expression); + final JetType expressionType = context.get(BindingContext.EXPRESSION_TYPE, receiverExpression); + + if (expressionType != null && resolutionScope != null) { + if (!(expressionType instanceof NamespaceType)) { + ExpressionReceiver receiverDescriptor = new ExpressionReceiver(receiverExpression, expressionType); + Set descriptors = new HashSet(); + + DataFlowInfo info = context.get(BindingContext.NON_DEFAULT_EXPRESSION_DATA_FLOW, expression); + if (info == null) { + info = DataFlowInfo.EMPTY; + } + + AutoCastServiceImpl autoCastService = new AutoCastServiceImpl(info, context); + List variantsForExplicitReceiver = autoCastService.getVariantsForReceiver(receiverDescriptor); + + for (ReceiverDescriptor descriptor : variantsForExplicitReceiver) { + descriptors.addAll(includeExternalCallableExtensions( + excludePrivateDescriptors(descriptor.getType().getMemberScope().getAllDescriptors()), + resolutionScope, descriptor)); + } + + return descriptors; + } + + return includeExternalCallableExtensions( + excludePrivateDescriptors(expressionType.getMemberScope().getAllDescriptors()), + resolutionScope, new ExpressionReceiver(receiverExpression, expressionType)); + } + return Collections.emptyList(); + } + else { + return getVariantsNoReceiver(expression, context); + } + } + + public static Collection getVariantsNoReceiver(JetExpression expression, BindingContext context) { + JetScope resolutionScope = context.get(BindingContext.RESOLUTION_SCOPE, expression); + if (resolutionScope != null) { + if (expression.getParent() instanceof JetImportDirective || expression.getParent() instanceof JetNamespaceHeader) { + return excludeNonPackageDescriptors(resolutionScope.getAllDescriptors()); + } + else { + HashSet descriptorsSet = Sets.newHashSet(); + + ArrayList result = new ArrayList(); + resolutionScope.getImplicitReceiversHierarchy(result); + + for (ReceiverDescriptor receiverDescriptor : result) { + JetType receiverType = receiverDescriptor.getType(); + descriptorsSet.addAll(receiverType.getMemberScope().getAllDescriptors()); + } + + descriptorsSet.addAll(resolutionScope.getAllDescriptors()); + return excludeNotCallableExtensions(excludePrivateDescriptors(descriptorsSet), resolutionScope); + } + } + return Collections.emptyList(); + } + + @NotNull + public static Collection getReferenceVariants(JetNamespaceHeader expression, BindingContext context) { + JetScope resolutionScope = context.get(BindingContext.RESOLUTION_SCOPE, expression); + if (resolutionScope != null) { + return excludeNonPackageDescriptors(resolutionScope.getAllDescriptors()); + } + + return Collections.emptyList(); + } + + public static Collection excludePrivateDescriptors( + @NotNull Collection descriptors) { + + return Collections2.filter(descriptors, new Predicate() { + @Override + public boolean apply(@Nullable DeclarationDescriptor descriptor) { + if (descriptor == null) { + return false; + } + + if (descriptor instanceof NamespaceDescriptor) { + NamespaceDescriptor namespaceDescriptor = (NamespaceDescriptor) descriptor; + if (namespaceDescriptor.getName().isEmpty()) { + return false; + } + } + + return true; + } + }); + } + + public static Collection excludeNotCallableExtensions( + @NotNull Collection descriptors, @NotNull final JetScope scope + ) { + final Set descriptorsSet = Sets.newHashSet(descriptors); + + final ArrayList result = new ArrayList(); + scope.getImplicitReceiversHierarchy(result); + + descriptorsSet.removeAll( + Collections2.filter(JetScopeUtils.getAllExtensions(scope), new Predicate() { + @Override + public boolean apply(CallableDescriptor callableDescriptor) { + if (!callableDescriptor.getReceiverParameter().exists()) { + return false; + } + for (ReceiverDescriptor receiverDescriptor : result) { + if (ExpressionTypingUtils.checkIsExtensionCallable(receiverDescriptor, callableDescriptor)) { + return false; + } + } + return true; + } + })); + + return Lists.newArrayList(descriptorsSet); + } + + private static Collection excludeNonPackageDescriptors( + @NotNull Collection descriptors) { + return Collections2.filter(descriptors, new Predicate() { + @Override + public boolean apply(DeclarationDescriptor declarationDescriptor) { + return declarationDescriptor instanceof NamespaceDescriptor; + } + }); + } + + private static Set includeExternalCallableExtensions( + @NotNull Collection descriptors, + @NotNull final JetScope externalScope, + @NotNull final ReceiverDescriptor receiverDescriptor + ) { + // It's impossible to add extension function for namespace + JetType receiverType = receiverDescriptor.getType(); + if (receiverType instanceof NamespaceType) { + return new HashSet(descriptors); + } + + Set descriptorsSet = Sets.newHashSet(descriptors); + + descriptorsSet.addAll( + Collections2.filter(JetScopeUtils.getAllExtensions(externalScope), + new Predicate() { + @Override + public boolean apply(CallableDescriptor callableDescriptor) { + return ExpressionTypingUtils.checkIsExtensionCallable(receiverDescriptor, callableDescriptor); + } + })); + + return descriptorsSet; + } +} diff --git a/compiler/cli/src/org/jetbrains/jet/cli/common/CompilerPlugin.java b/compiler/cli/src/org/jetbrains/jet/cli/common/CompilerPlugin.java new file mode 100644 index 00000000000..8587929d80c --- /dev/null +++ b/compiler/cli/src/org/jetbrains/jet/cli/common/CompilerPlugin.java @@ -0,0 +1,25 @@ +/* + * Copyright 2010-2012 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. + * 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 org.jetbrains.jet.cli.common; + +/** + * A simple interface for compiler plugins to run after the compiler has finished such as for things like + * generating documentation or code generation etc + */ +public interface CompilerPlugin { + void processFiles(CompilerPluginContext context); +} diff --git a/compiler/cli/src/org/jetbrains/jet/cli/common/CompilerPluginContext.java b/compiler/cli/src/org/jetbrains/jet/cli/common/CompilerPluginContext.java new file mode 100644 index 00000000000..7716e3130ed --- /dev/null +++ b/compiler/cli/src/org/jetbrains/jet/cli/common/CompilerPluginContext.java @@ -0,0 +1,51 @@ +/* + * Copyright 2010-2012 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. + * 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 org.jetbrains.jet.cli.common; + +import com.intellij.openapi.project.Project; +import org.jetbrains.jet.lang.psi.JetFile; +import org.jetbrains.jet.lang.resolve.BindingContext; + +import java.util.List; + +/** + * Represents the context of available state in which a {@link CompilerPlugin} runs such as + * the {@link Project}, the {@link BindingContext} and the underlying {@link JetFile} files. + */ +public class CompilerPluginContext { + private final Project project; + private final BindingContext context; + private final List files; + + public CompilerPluginContext(Project project, BindingContext context, List files) { + this.project = project; + this.context = context; + this.files = files; + } + + public BindingContext getContext() { + return context; + } + + public List getFiles() { + return files; + } + + public Project getProject() { + return project; + } +} diff --git a/compiler/cli/src/org/jetbrains/jet/cli/common/ExitCode.java b/compiler/cli/src/org/jetbrains/jet/cli/common/ExitCode.java new file mode 100644 index 00000000000..db3c954d0db --- /dev/null +++ b/compiler/cli/src/org/jetbrains/jet/cli/common/ExitCode.java @@ -0,0 +1,36 @@ +/* + * Copyright 2010-2012 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. + * 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 org.jetbrains.jet.cli.common; + +/** +* @author Pavel Talanov +*/ +public enum ExitCode { + OK(0), + COMPILATION_ERROR(1), + INTERNAL_ERROR(2); + + private final int code; + + ExitCode(int code) { + this.code = code; + } + + public int getCode() { + return code; + } +} diff --git a/compiler/cli/src/org/jetbrains/jet/cli/common/messages/AnalyzerWithCompilerReport.java b/compiler/cli/src/org/jetbrains/jet/cli/common/messages/AnalyzerWithCompilerReport.java new file mode 100644 index 00000000000..24c2f1b1175 --- /dev/null +++ b/compiler/cli/src/org/jetbrains/jet/cli/common/messages/AnalyzerWithCompilerReport.java @@ -0,0 +1,151 @@ +/* + * Copyright 2010-2012 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. + * 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 org.jetbrains.jet.cli.common.messages; + +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.PsiErrorElement; +import com.intellij.psi.PsiRecursiveElementWalkingVisitor; +import jet.Function0; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.analyzer.AnalyzeExhaust; +import org.jetbrains.jet.lang.descriptors.ClassDescriptor; +import org.jetbrains.jet.lang.diagnostics.*; +import org.jetbrains.jet.lang.diagnostics.rendering.DefaultErrorMessages; +import org.jetbrains.jet.lang.psi.JetFile; +import org.jetbrains.jet.lang.resolve.BindingContext; +import org.jetbrains.jet.lang.resolve.DescriptorUtils; + +import java.util.Collection; + +/** + * @author Pavel Talanov + */ +public final class AnalyzerWithCompilerReport { + + @NotNull + private static CompilerMessageSeverity convertSeverity(@NotNull Severity severity) { + switch (severity) { + case INFO: + return CompilerMessageSeverity.INFO; + case ERROR: + return CompilerMessageSeverity.ERROR; + case WARNING: + return CompilerMessageSeverity.WARNING; + } + throw new IllegalStateException("Unknown severity: " + severity); + } + + @NotNull + private static final SimpleDiagnosticFactory SYNTAX_ERROR_FACTORY = SimpleDiagnosticFactory.create(Severity.ERROR); + + private boolean hasErrors = false; + @NotNull + private final MessageCollector messageCollectorWrapper; + @Nullable + private AnalyzeExhaust analyzeExhaust = null; + + public AnalyzerWithCompilerReport(@NotNull final MessageCollector collector) { + messageCollectorWrapper = new MessageCollector() { + @Override + public void report(@NotNull CompilerMessageSeverity severity, + @NotNull String message, + @NotNull CompilerMessageLocation location) { + if (CompilerMessageSeverity.ERRORS.contains(severity)) { + hasErrors = true; + } + collector.report(severity, message, location); + } + }; + } + + private void reportDiagnostic(@NotNull Diagnostic diagnostic) { + DiagnosticUtils.LineAndColumn lineAndColumn = DiagnosticUtils.getLineAndColumn(diagnostic); + VirtualFile virtualFile = diagnostic.getPsiFile().getVirtualFile(); + String path = virtualFile == null ? null : virtualFile.getPath(); + String render; + if (diagnostic.getFactory() == SYNTAX_ERROR_FACTORY) { + render = ((SyntaxErrorDiagnostic)diagnostic).message; + } + else { + render = DefaultErrorMessages.RENDERER.render(diagnostic); + } + messageCollectorWrapper.report(convertSeverity(diagnostic.getSeverity()), render, + CompilerMessageLocation.create(path, lineAndColumn.getLine(), lineAndColumn.getColumn())); + } + + private void reportIncompleteHierarchies() { + assert analyzeExhaust != null; + Collection incompletes = analyzeExhaust.getBindingContext().getKeys(BindingContext.INCOMPLETE_HIERARCHY); + if (!incompletes.isEmpty()) { + StringBuilder message = new StringBuilder("The following classes have incomplete hierarchies:\n"); + for (ClassDescriptor incomplete : incompletes) { + String fqName = DescriptorUtils.getFQName(incomplete).getFqName(); + message.append(" ").append(fqName).append("\n"); + } + messageCollectorWrapper.report(CompilerMessageSeverity.ERROR, message.toString(), CompilerMessageLocation.NO_LOCATION); + } + } + + private void reportDiagnostics() { + assert analyzeExhaust != null; + for (Diagnostic diagnostic : analyzeExhaust.getBindingContext().getDiagnostics()) { + reportDiagnostic(diagnostic); + } + } + + private void reportSyntaxErrors(@NotNull Collection files) { + for (JetFile file : files) { + file.accept(new PsiRecursiveElementWalkingVisitor() { + @Override + public void visitErrorElement(PsiErrorElement element) { + String description = element.getErrorDescription(); + String message = StringUtil.isEmpty(description) ? "Syntax error" : description; + Diagnostic diagnostic = new SyntaxErrorDiagnostic(element, Severity.ERROR, message); + reportDiagnostic(diagnostic); + } + }); + } + } + + @Nullable + public AnalyzeExhaust getAnalyzeExhaust() { + return analyzeExhaust; + } + + public boolean hasErrors() { + return hasErrors; + } + + public void analyzeAndReport(@NotNull Function0 analyzer, @NotNull Collection files) { + reportSyntaxErrors(files); + analyzeExhaust = analyzer.invoke(); + reportDiagnostics(); + reportIncompleteHierarchies(); + } + + + public static class SyntaxErrorDiagnostic extends SimpleDiagnostic { + private String message; + + public SyntaxErrorDiagnostic(@NotNull PsiErrorElement psiElement, @NotNull Severity severity, String message) { + super(psiElement, SYNTAX_ERROR_FACTORY, severity); + this.message = message; + } + } +} diff --git a/compiler/cli/src/org/jetbrains/jet/cli/common/messages/CompilerMessageLocation.java b/compiler/cli/src/org/jetbrains/jet/cli/common/messages/CompilerMessageLocation.java new file mode 100644 index 00000000000..247656bc88b --- /dev/null +++ b/compiler/cli/src/org/jetbrains/jet/cli/common/messages/CompilerMessageLocation.java @@ -0,0 +1,57 @@ +/* + * Copyright 2010-2012 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. + * 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 org.jetbrains.jet.cli.common.messages; + +import org.jetbrains.annotations.Nullable; + +/** + * @author abreslav + */ +public class CompilerMessageLocation { + + public static final CompilerMessageLocation NO_LOCATION = new CompilerMessageLocation(null, -1, -1); + + public static CompilerMessageLocation create(@Nullable String path, int line, int column) { + if (path == null) { + return NO_LOCATION; + } + return new CompilerMessageLocation(path, line, column); + } + + private final String path; + private final int line; + private final int column; + + private CompilerMessageLocation(@Nullable String path, int line, int column) { + this.path = path; + this.line = line; + this.column = column; + } + + @Nullable + public String getPath() { + return path; + } + + public int getLine() { + return line; + } + + public int getColumn() { + return column; + } +} diff --git a/compiler/cli/src/org/jetbrains/jet/cli/common/messages/CompilerMessageSeverity.java b/compiler/cli/src/org/jetbrains/jet/cli/common/messages/CompilerMessageSeverity.java new file mode 100644 index 00000000000..2608639a9f6 --- /dev/null +++ b/compiler/cli/src/org/jetbrains/jet/cli/common/messages/CompilerMessageSeverity.java @@ -0,0 +1,32 @@ +/* + * Copyright 2010-2012 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. + * 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 org.jetbrains.jet.cli.common.messages; + +import java.util.EnumSet; + +/** + * @author abreslav + */ +public enum CompilerMessageSeverity { + INFO, + ERROR, + WARNING, + EXCEPTION, + LOGGING; + + public static final EnumSet ERRORS = EnumSet.of(ERROR, EXCEPTION); +} diff --git a/compiler/cli/src/org/jetbrains/jet/cli/common/messages/MessageCollector.java b/compiler/cli/src/org/jetbrains/jet/cli/common/messages/MessageCollector.java new file mode 100644 index 00000000000..44b2697262b --- /dev/null +++ b/compiler/cli/src/org/jetbrains/jet/cli/common/messages/MessageCollector.java @@ -0,0 +1,31 @@ +/* + * Copyright 2010-2012 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. + * 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 org.jetbrains.jet.cli.common.messages; + +import org.jetbrains.annotations.NotNull; + +public interface MessageCollector { + MessageCollector PLAIN_TEXT_TO_SYSTEM_ERR = new MessageCollector() { + @Override + public void report(@NotNull CompilerMessageSeverity severity, @NotNull String message, @NotNull CompilerMessageLocation location) { + System.err.println(MessageRenderer.PLAIN.render(severity, message, location)); + } + }; + + void report(@NotNull CompilerMessageSeverity severity, @NotNull String message, @NotNull CompilerMessageLocation location); +} + diff --git a/compiler/cli/src/org/jetbrains/jet/cli/common/messages/MessageRenderer.java b/compiler/cli/src/org/jetbrains/jet/cli/common/messages/MessageRenderer.java new file mode 100644 index 00000000000..15eb3824455 --- /dev/null +++ b/compiler/cli/src/org/jetbrains/jet/cli/common/messages/MessageRenderer.java @@ -0,0 +1,99 @@ +/* + * Copyright 2010-2012 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. + * 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 org.jetbrains.jet.cli.common.messages; + +import com.intellij.openapi.util.text.StringUtil; +import org.jetbrains.annotations.NotNull; + +import java.io.PrintWriter; +import java.io.StringWriter; + +/** + * @author abreslav + */ +public interface MessageRenderer { + + MessageRenderer TAGS = new MessageRenderer() { + @Override + public String renderPreamble() { + return ""; + } + + @Override + public String render(@NotNull CompilerMessageSeverity severity, @NotNull String message, @NotNull CompilerMessageLocation location) { + StringBuilder out = new StringBuilder(); + out.append("<").append(severity.toString()); + if (location.getPath() != null) { + out.append(" path=\"").append(e(location.getPath())).append("\""); + out.append(" line=\"").append(location.getLine()).append("\""); + out.append(" column=\"").append(location.getColumn()).append("\""); + } + out.append(">\n"); + + out.append(e(message)); + + out.append("\n"); + return out.toString(); + } + + private String e(String str) { + return StringUtil.escapeXml(str); + } + + @Override + public String renderException(@NotNull Throwable e) { + return render(CompilerMessageSeverity.EXCEPTION, PLAIN.renderException(e), CompilerMessageLocation.NO_LOCATION); + } + + @Override + public String renderConclusion() { + return ""; + } + }; + + MessageRenderer PLAIN = new MessageRenderer() { + @Override + public String renderPreamble() { + return ""; + } + + @Override + public String render(@NotNull CompilerMessageSeverity severity, @NotNull String message, @NotNull CompilerMessageLocation location) { + String path = location.getPath(); + String position = path == null ? "" : path + ": (" + (location.getLine() + ", " + location.getColumn()) + ") "; + return severity + ": " + position + message; + } + + @Override + public String renderException(@NotNull Throwable e) { + StringWriter out = new StringWriter(); + //noinspection IOResourceOpenedButNotSafelyClosed + e.printStackTrace(new PrintWriter(out)); + return out.toString(); + } + + @Override + public String renderConclusion() { + return ""; + } + }; + + String renderPreamble(); + String render(@NotNull CompilerMessageSeverity severity, @NotNull String message, @NotNull CompilerMessageLocation location); + String renderException(@NotNull Throwable e); + String renderConclusion(); +} diff --git a/compiler/cli/src/org/jetbrains/jet/cli/common/messages/MessageUtil.java b/compiler/cli/src/org/jetbrains/jet/cli/common/messages/MessageUtil.java new file mode 100644 index 00000000000..9e3775e3ef4 --- /dev/null +++ b/compiler/cli/src/org/jetbrains/jet/cli/common/messages/MessageUtil.java @@ -0,0 +1,32 @@ +/* + * Copyright 2010-2012 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. + * 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 org.jetbrains.jet.cli.common.messages; + +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import org.jetbrains.jet.lang.diagnostics.DiagnosticUtils; + +/** + * @author abreslav + */ +public class MessageUtil { + public static CompilerMessageLocation psiElementToMessageLocation(PsiElement element) { + PsiFile file = element.getContainingFile(); + DiagnosticUtils.LineAndColumn lineAndColumn = DiagnosticUtils.getLineAndColumnInPsiFile(file, element.getTextRange()); + return CompilerMessageLocation.create(file.getVirtualFile().getPath(), lineAndColumn.getLine(), lineAndColumn.getColumn()); + } +} diff --git a/compiler/cli/src/org/jetbrains/jet/cli/common/messages/PrintingMessageCollector.java b/compiler/cli/src/org/jetbrains/jet/cli/common/messages/PrintingMessageCollector.java new file mode 100644 index 00000000000..9f9e8b0120d --- /dev/null +++ b/compiler/cli/src/org/jetbrains/jet/cli/common/messages/PrintingMessageCollector.java @@ -0,0 +1,73 @@ +/* + * Copyright 2010-2012 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. + * 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 org.jetbrains.jet.cli.common.messages; + +import com.google.common.collect.LinkedHashMultimap; +import com.google.common.collect.Multimap; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.cli.common.messages.CompilerMessageLocation; +import org.jetbrains.jet.cli.common.messages.CompilerMessageSeverity; +import org.jetbrains.jet.cli.common.messages.MessageCollector; +import org.jetbrains.jet.cli.common.messages.MessageRenderer; + +import java.io.PrintStream; +import java.util.Collection; + +/** + * @author Pavel Talanov + */ +public class PrintingMessageCollector implements MessageCollector { + private final boolean verbose; + private final PrintStream errStream; + private final MessageRenderer messageRenderer; + + // File path (nullable) -> error message + private final Multimap groupedMessages = LinkedHashMultimap.create(); + + public PrintingMessageCollector(PrintStream errStream, + MessageRenderer messageRenderer, + boolean verbose) { + this.verbose = verbose; + this.errStream = errStream; + this.messageRenderer = messageRenderer; + } + + @Override + public void report(@NotNull CompilerMessageSeverity severity, + @NotNull String message, + @NotNull CompilerMessageLocation location) { + String text = messageRenderer.render(severity, message, location); + if (severity == CompilerMessageSeverity.LOGGING) { + if (!verbose) { + return; + } + errStream.println(text); + } + groupedMessages.put(location.getPath(), text); + } + + public void printToErrStream() { + if (!groupedMessages.isEmpty()) { + for (String path : groupedMessages.keySet()) { + Collection messageTexts = groupedMessages.get(path); + for (String text : messageTexts) { + errStream.println(text); + } + } + } + } +} diff --git a/compiler/cli/src/org/jetbrains/jet/cli/js/K2JSCompiler.java b/compiler/cli/src/org/jetbrains/jet/cli/js/K2JSCompiler.java new file mode 100644 index 00000000000..63d8e8fb778 --- /dev/null +++ b/compiler/cli/src/org/jetbrains/jet/cli/js/K2JSCompiler.java @@ -0,0 +1,27 @@ +/* + * Copyright 2010-2012 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. + * 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 org.jetbrains.jet.cli.js; + +/** + * @author Pavel Talanov + */ +public final class K2JSCompiler { + + public static void main(String... args) { + //TODO + } +} diff --git a/compiler/cli/src/org/jetbrains/jet/cli/js/K2JSCompilerArguments.java b/compiler/cli/src/org/jetbrains/jet/cli/js/K2JSCompilerArguments.java new file mode 100644 index 00000000000..46522696f05 --- /dev/null +++ b/compiler/cli/src/org/jetbrains/jet/cli/js/K2JSCompilerArguments.java @@ -0,0 +1,24 @@ +/* + * Copyright 2010-2012 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. + * 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 org.jetbrains.jet.cli.js; + +/** + * @author Pavel Talanov + */ +//TODO +public class K2JSCompilerArguments { +} diff --git a/compiler/cli/src/org/jetbrains/jet/cli/jvm/K2JVMCompiler.java b/compiler/cli/src/org/jetbrains/jet/cli/jvm/K2JVMCompiler.java new file mode 100644 index 00000000000..007253e25e5 --- /dev/null +++ b/compiler/cli/src/org/jetbrains/jet/cli/jvm/K2JVMCompiler.java @@ -0,0 +1,255 @@ +/* + * Copyright 2010-2012 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. + * 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 org.jetbrains.jet.cli.jvm; + +import com.google.common.base.Splitter; +import com.google.common.collect.Iterables; +import com.intellij.openapi.Disposable; +import com.intellij.openapi.util.Disposer; +import com.sampullara.cli.Args; +import jet.modules.Module; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.cli.common.CompilerPlugin; +import org.jetbrains.jet.cli.common.ExitCode; +import org.jetbrains.jet.cli.common.messages.PrintingMessageCollector; +import org.jetbrains.jet.codegen.CompilationException; +import org.jetbrains.jet.cli.jvm.compiler.*; +import org.jetbrains.jet.cli.common.messages.*; +import org.jetbrains.jet.lang.resolve.java.CompilerDependencies; +import org.jetbrains.jet.lang.resolve.java.CompilerSpecialMode; +import org.jetbrains.jet.utils.PathUtil; + +import java.io.File; +import java.io.PrintStream; +import java.util.List; + +import static org.jetbrains.jet.cli.common.ExitCode.*; + +/** + * @author yole + * @author alex.tkachman + */ +@SuppressWarnings("UseOfSystemOutOrSystemErr") +public class K2JVMCompiler { + + public static void main(String... args) { + doMain(new K2JVMCompiler(), args); + } + + /** + * Useful main for derived command line tools + */ + public static void doMain(K2JVMCompiler compiler, String[] args) { + try { + ExitCode rc = compiler.exec(System.out, args); + if (rc != OK) { + System.err.println("exec() finished with " + rc + " return code"); + System.exit(rc.getCode()); + } + } + catch (CompileEnvironmentException e) { + System.err.println(e.getMessage()); + System.exit(INTERNAL_ERROR.getCode()); + } + } + + public ExitCode exec(PrintStream errStream, String... args) { + K2JVMCompilerArguments arguments = createArguments(); + if (!parseArguments(errStream, arguments, args)) { + return INTERNAL_ERROR; + } + return exec(errStream, arguments); + } + + /** + * Executes the compiler on the parsed arguments + */ + public ExitCode exec(final PrintStream errStream, K2JVMCompilerArguments arguments) { + if (arguments.help) { + usage(errStream); + return OK; + } + System.setProperty("java.awt.headless", "true"); + + final MessageRenderer messageRenderer = arguments.tags ? MessageRenderer.TAGS : MessageRenderer.PLAIN; + + errStream.print(messageRenderer.renderPreamble()); + + try { + if (arguments.version) { + errStream.println(messageRenderer.render(CompilerMessageSeverity.INFO, "Kotlin Compiler version " + K2JVMCompilerVersion.VERSION, CompilerMessageLocation.NO_LOCATION)); + } + + CompilerSpecialMode mode = parseCompilerSpecialMode(arguments); + + File jdkHeadersJar; + if (mode.includeJdkHeaders()) { + if (arguments.jdkHeaders != null) { + jdkHeadersJar = new File(arguments.jdkHeaders); + } + else { + jdkHeadersJar = PathUtil.getAltHeadersPath(); + } + } + else { + jdkHeadersJar = null; + } + File runtimeJar; + + if (mode.includeKotlinRuntime()) { + if (arguments.stdlib != null) { + runtimeJar = new File(arguments.stdlib); + } + else { + runtimeJar = PathUtil.getDefaultRuntimePath(); + } + } + else { + runtimeJar = null; + } + + CompilerDependencies dependencies = new CompilerDependencies(mode, CompilerDependencies.findRtJar(), jdkHeadersJar, runtimeJar); + PrintingMessageCollector messageCollector = new PrintingMessageCollector(errStream, messageRenderer, arguments.verbose); + Disposable rootDisposable = CompileEnvironmentUtil.createMockDisposable(); + + JetCoreEnvironment environment = new JetCoreEnvironment(rootDisposable, dependencies); + CompileEnvironmentConfiguration configuration = new CompileEnvironmentConfiguration(environment, dependencies, messageCollector); + + messageCollector.report(CompilerMessageSeverity.LOGGING, "Configuring the compilation environment", + CompilerMessageLocation.NO_LOCATION); + try { + configureEnvironment(configuration, arguments); + + boolean noErrors; + if (arguments.module != null) { + List modules = CompileEnvironmentUtil.loadModuleScript(arguments.module, new PrintingMessageCollector(errStream, messageRenderer, false)); + File directory = new File(arguments.module).getParentFile(); + noErrors = KotlinToJVMBytecodeCompiler.compileModules(configuration, modules, + directory, arguments.jar, arguments.outputDir, + arguments.includeRuntime); + } + else { + // TODO ideally we'd unify to just having a single field that supports multiple files/dirs + if (arguments.getSourceDirs() != null) { + noErrors = KotlinToJVMBytecodeCompiler.compileBunchOfSourceDirectories(configuration, + arguments.getSourceDirs(), arguments.jar, arguments.outputDir, arguments.includeRuntime); + } + else { + noErrors = KotlinToJVMBytecodeCompiler.compileBunchOfSources(configuration, + arguments.src, arguments.jar, arguments.outputDir, arguments.includeRuntime); + } + } + return noErrors ? OK : COMPILATION_ERROR; + } + catch (CompilationException e) { + messageCollector.report(CompilerMessageSeverity.EXCEPTION, MessageRenderer.PLAIN.renderException(e), + MessageUtil.psiElementToMessageLocation(e.getElement())); + return INTERNAL_ERROR; + } + catch (Throwable t) { + messageCollector.report(CompilerMessageSeverity.EXCEPTION, MessageRenderer.PLAIN.renderException(t), CompilerMessageLocation.NO_LOCATION); + return INTERNAL_ERROR; + } + finally { + Disposer.dispose(rootDisposable); + messageCollector.printToErrStream(); + } + } + finally { + errStream.print(messageRenderer.renderConclusion()); + } + } + + @NotNull + private CompilerSpecialMode parseCompilerSpecialMode(@NotNull K2JVMCompilerArguments arguments) { + if (arguments.mode == null) { + return CompilerSpecialMode.REGULAR; + } + else { + for (CompilerSpecialMode variant : CompilerSpecialMode.values()) { + if (arguments.mode.equalsIgnoreCase(variant.name().replaceAll("_", ""))) { + return variant; + } + } + } + // TODO: report properly + throw new IllegalArgumentException("unknown compiler mode: " + arguments.mode); + } + + /** + * Returns true if the arguments can be parsed correctly + */ + protected boolean parseArguments(PrintStream errStream, K2JVMCompilerArguments arguments, String[] args) { + try { + Args.parse(arguments, args); + return true; + } + catch (IllegalArgumentException e) { + usage(errStream); + } + catch (Throwable t) { + // Always use tags + errStream.println(MessageRenderer.TAGS.renderException(t)); + } + return false; + } + + protected void usage(PrintStream target) { + // We should say something like + // Args.usage(target, K2JVMCompilerArguments.class); + // but currently cli-parser we are using does not support that + // a corresponding patch has been sent to the authors + // For now, we are using this: + + PrintStream oldErr = System.err; + System.setErr(target); + try { + // TODO: use proper argv0 + Args.usage(new K2JVMCompilerArguments()); + } finally { + System.setErr(oldErr); + } + } + + /** + * Allow derived classes to add additional command line arguments + */ + protected K2JVMCompilerArguments createArguments() { + return new K2JVMCompilerArguments(); + } + + /** + * Strategy method to configure the environment, allowing compiler + * based tools to customise their own plugins + */ + protected void configureEnvironment(CompileEnvironmentConfiguration configuration, K2JVMCompilerArguments arguments) { + // install any compiler plugins + List plugins = arguments.getCompilerPlugins(); + if (plugins != null) { + configuration.getCompilerPlugins().addAll(plugins); + } + + if (configuration.getCompilerDependencies().getRuntimeJar() != null) { + CompileEnvironmentUtil.addToClasspath(configuration.getEnvironment(), configuration.getCompilerDependencies().getRuntimeJar()); + } + + if (arguments.classpath != null) { + final Iterable classpath = Splitter.on(File.pathSeparatorChar).split(arguments.classpath); + CompileEnvironmentUtil.addToClasspath(configuration.getEnvironment(), Iterables.toArray(classpath, String.class)); + } + } +} diff --git a/compiler/cli/src/org/jetbrains/jet/cli/jvm/K2JVMCompilerArguments.java b/compiler/cli/src/org/jetbrains/jet/cli/jvm/K2JVMCompilerArguments.java new file mode 100644 index 00000000000..bed558b5f08 --- /dev/null +++ b/compiler/cli/src/org/jetbrains/jet/cli/jvm/K2JVMCompilerArguments.java @@ -0,0 +1,165 @@ +/** + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.jetbrains.jet.cli.jvm; + +import com.sampullara.cli.Argument; +import org.jetbrains.jet.cli.common.CompilerPlugin; + +import java.util.ArrayList; +import java.util.List; + +/** + * Command line arguments for the {@link K2JVMCompiler} + */ +public class K2JVMCompilerArguments { + private List compilerPlugins = new ArrayList(); + + // TODO ideally we'd unify this with 'src' to just having a single field that supports multiple files/dirs + private List sourceDirs; + + public List getSourceDirs() { + return sourceDirs; + } + + public void setSourceDirs(List sourceDirs) { + this.sourceDirs = sourceDirs; + } + + @Argument(value = "output", description = "output directory") + public String outputDir; + + @Argument(value = "jar", description = "jar file name") + public String jar; + + @Argument(value = "src", description = "source file or directory") + public String src; + + @Argument(value = "module", description = "module to compile") + public String module; + + @Argument(value = "classpath", description = "classpath to use when compiling") + public String classpath; + + @Argument(value = "includeRuntime", description = "include Kotlin runtime in to resulting jar") + public boolean includeRuntime; + + @Argument(value = "stdlib", description = "Path to the stdlib.jar") + public String stdlib; + + @Argument(value = "jdkHeaders", description = "Path to the kotlin-jdk-headers.jar") + public String jdkHeaders; + + @Argument(value = "help", alias = "h", description = "show help") + public boolean help; + + @Argument(value = "mode", description = "Special compiler modes: stubs or jdkHeaders") + public String mode; + + @Argument(value = "tags", description = "Demarcate each compilation message (error, warning, etc) with an open and close tag") + public boolean tags; + + @Argument(value = "verbose", description = "Enable verbose logging output") + public boolean verbose; + + @Argument(value = "version", description = "Display compiler version") + public boolean version; + + + public String getClasspath() { + return classpath; + } + + public void setClasspath(String classpath) { + this.classpath = classpath; + } + + public boolean isHelp() { + return help; + } + + public void setHelp(boolean help) { + this.help = help; + } + + public boolean isIncludeRuntime() { + return includeRuntime; + } + + public void setIncludeRuntime(boolean includeRuntime) { + this.includeRuntime = includeRuntime; + } + + public String getJar() { + return jar; + } + + public void setJar(String jar) { + this.jar = jar; + } + + public String getModule() { + return module; + } + + public void setModule(String module) { + this.module = module; + } + + public String getOutputDir() { + return outputDir; + } + + public void setOutputDir(String outputDir) { + this.outputDir = outputDir; + } + + public String getSrc() { + return src; + } + + public void setSrc(String src) { + this.src = src; + } + + public String getStdlib() { + return stdlib; + } + + public void setStdlib(String stdlib) { + this.stdlib = stdlib; + } + + public boolean isTags() { + return tags; + } + + public void setTags(boolean tags) { + this.tags = tags; + } + + public List getCompilerPlugins() { + return compilerPlugins; + } + + /** + * Sets the compiler plugins to be used when working with the {@link K2JVMCompiler} + */ + public void setCompilerPlugins(List compilerPlugins) { + this.compilerPlugins = compilerPlugins; + } +} diff --git a/compiler/cli/src/org/jetbrains/jet/cli/jvm/K2JVMCompilerVersion.java b/compiler/cli/src/org/jetbrains/jet/cli/jvm/K2JVMCompilerVersion.java new file mode 100644 index 00000000000..460d5c89493 --- /dev/null +++ b/compiler/cli/src/org/jetbrains/jet/cli/jvm/K2JVMCompilerVersion.java @@ -0,0 +1,26 @@ +/* + * Copyright 2010-2012 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. + * 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 org.jetbrains.jet.cli.jvm; + +/** + * @author abreslav + */ +public class K2JVMCompilerVersion { + // The value of this constant is generated by the build script + // DON'T MODIFY IT + public static final String VERSION = "@snapshot@"; +} diff --git a/compiler/cli/src/org/jetbrains/jet/cli/jvm/compiler/CliJetFilesProvider.java b/compiler/cli/src/org/jetbrains/jet/cli/jvm/compiler/CliJetFilesProvider.java new file mode 100644 index 00000000000..fa66c13bcaa --- /dev/null +++ b/compiler/cli/src/org/jetbrains/jet/cli/jvm/compiler/CliJetFilesProvider.java @@ -0,0 +1,60 @@ +/* + * Copyright 2010-2012 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. + * 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. + */ + +/* + * @author max + */ +package org.jetbrains.jet.cli.jvm.compiler; + +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.util.Function; +import org.jetbrains.jet.lang.psi.JetFile; +import org.jetbrains.jet.lang.resolve.java.JetFilesProvider; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +public class CliJetFilesProvider extends JetFilesProvider { + private final JetCoreEnvironment environment; + private Function> all_files = new Function>() { + @Override + public Collection fun(JetFile file) { + return environment.getSourceFiles(); + } + + }; + + public CliJetFilesProvider(JetCoreEnvironment environment) { + this.environment = environment; + } + + @Override + public Function> sampleToAllFilesInModule() { + return all_files; + } + + @Override + public List allInScope(GlobalSearchScope scope) { + List answer = new ArrayList(); + for (JetFile file : environment.getSourceFiles()) { + if (scope.contains(file.getVirtualFile())) { + answer.add(file); + } + } + return answer; + } +} diff --git a/compiler/cli/src/org/jetbrains/jet/cli/jvm/compiler/CompileEnvironmentConfiguration.java b/compiler/cli/src/org/jetbrains/jet/cli/jvm/compiler/CompileEnvironmentConfiguration.java new file mode 100644 index 00000000000..e95f3ba7186 --- /dev/null +++ b/compiler/cli/src/org/jetbrains/jet/cli/jvm/compiler/CompileEnvironmentConfiguration.java @@ -0,0 +1,66 @@ +/* + * Copyright 2010-2012 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. + * 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 org.jetbrains.jet.cli.jvm.compiler; + +import com.google.common.collect.Lists; +import com.intellij.openapi.util.Disposer; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.cli.common.CompilerPlugin; +import org.jetbrains.jet.cli.common.messages.MessageCollector; +import org.jetbrains.jet.lang.resolve.java.CompilerDependencies; + +import java.util.List; + +/** + * @author abreslav + */ +public class CompileEnvironmentConfiguration { + private final JetCoreEnvironment environment; + private final CompilerDependencies compilerDependencies; + private final MessageCollector messageCollector; + + private List compilerPlugins = Lists.newArrayList(); + + /** + * NOTE: It's very important to call dispose for every object of this class or there will be memory leaks. + * @see Disposer + */ + public CompileEnvironmentConfiguration(@NotNull JetCoreEnvironment environment, + @NotNull CompilerDependencies compilerDependencies, @NotNull MessageCollector messageCollector) { + this.messageCollector = messageCollector; + this.compilerDependencies = compilerDependencies; + this.environment = environment; + } + + public JetCoreEnvironment getEnvironment() { + return environment; + } + + @NotNull + public CompilerDependencies getCompilerDependencies() { + return compilerDependencies; + } + + @NotNull + public MessageCollector getMessageCollector() { + return messageCollector; + } + + public List getCompilerPlugins() { + return compilerPlugins; + } +} diff --git a/compiler/cli/src/org/jetbrains/jet/cli/jvm/compiler/CompileEnvironmentException.java b/compiler/cli/src/org/jetbrains/jet/cli/jvm/compiler/CompileEnvironmentException.java new file mode 100644 index 00000000000..92ef9b972d7 --- /dev/null +++ b/compiler/cli/src/org/jetbrains/jet/cli/jvm/compiler/CompileEnvironmentException.java @@ -0,0 +1,34 @@ +/* + * Copyright 2010-2012 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. + * 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 org.jetbrains.jet.cli.jvm.compiler; + +/** + * @author yole + */ +public class CompileEnvironmentException extends RuntimeException { + public CompileEnvironmentException(String message) { + super(message); + } + + public CompileEnvironmentException(Throwable cause) { + super(cause); + } + + public CompileEnvironmentException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/compiler/cli/src/org/jetbrains/jet/cli/jvm/compiler/CompileEnvironmentUtil.java b/compiler/cli/src/org/jetbrains/jet/cli/jvm/compiler/CompileEnvironmentUtil.java new file mode 100644 index 00000000000..344fa99cd59 --- /dev/null +++ b/compiler/cli/src/org/jetbrains/jet/cli/jvm/compiler/CompileEnvironmentUtil.java @@ -0,0 +1,340 @@ +/* + * Copyright 2010-2012 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. + * 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 org.jetbrains.jet.cli.jvm.compiler; + +import com.intellij.openapi.Disposable; +import com.intellij.openapi.util.Disposer; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.psi.JavaPsiFacade; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.util.Processor; +import jet.modules.AllModules; +import jet.modules.Module; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.cli.common.messages.MessageCollector; +import org.jetbrains.jet.codegen.ClassFileFactory; +import org.jetbrains.jet.codegen.GeneratedClassLoader; +import org.jetbrains.jet.codegen.GenerationState; +import org.jetbrains.jet.lang.resolve.FqName; +import org.jetbrains.jet.lang.resolve.java.CompilerDependencies; +import org.jetbrains.jet.lang.resolve.java.CompilerSpecialMode; +import org.jetbrains.jet.lang.resolve.java.JvmAbi; +import org.jetbrains.jet.utils.PathUtil; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.lang.reflect.Method; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.List; +import java.util.jar.*; + +/** + * @author abreslav + */ +public class CompileEnvironmentUtil { + public static Disposable createMockDisposable() { + return new Disposable() { + @Override + public void dispose() { + } + }; + } + + @Nullable + public static File getUnpackedRuntimePath() { + URL url = CompileEnvironmentConfiguration.class.getClassLoader().getResource("jet/JetObject.class"); + if (url != null && url.getProtocol().equals("file")) { + return new File(url.getPath()).getParentFile().getParentFile(); + } + return null; + } + + @Nullable + public static File getRuntimeJarPath() { + URL url = CompileEnvironmentConfiguration.class.getClassLoader().getResource("jet/JetObject.class"); + if (url != null && url.getProtocol().equals("jar")) { + String path = url.getPath(); + return new File(path.substring(path.indexOf(":") + 1, path.indexOf("!/"))); + } + return null; + } + + public static void ensureKotlinRuntime(JetCoreEnvironment env) { + if (JavaPsiFacade.getInstance(env.getProject()).findClass("jet.JetObject", GlobalSearchScope.allScope(env.getProject())) == null) { + // TODO: prepend + File kotlin = PathUtil.getDefaultRuntimePath(); + if (kotlin == null || !kotlin.exists()) { + kotlin = getUnpackedRuntimePath(); + if (kotlin == null) kotlin = getRuntimeJarPath(); + } + if (kotlin == null) { + throw new IllegalStateException("kotlin runtime not found"); + } + env.addToClasspath(kotlin); + } + } + + + public static void ensureJdkRuntime(JetCoreEnvironment env) { + if (JavaPsiFacade.getInstance(env.getProject()).findClass("java.lang.Object", GlobalSearchScope.allScope(env.getProject())) == + null) { + // TODO: prepend + env.addToClasspath(findRtJar()); + } + } + + public static File findRtJar() { + String javaHome = System.getProperty("java.home"); + if ("jre".equals(new File(javaHome).getName())) { + javaHome = new File(javaHome).getParent(); + } + + File rtJar = findRtJar(javaHome); + + if (rtJar == null || !rtJar.exists()) { + throw new CompileEnvironmentException("No JDK rt.jar found under " + javaHome); + } + + return rtJar; + } + + private static File findRtJar(String javaHome) { + File rtJar = new File(javaHome, "jre/lib/rt.jar"); + if (rtJar.exists()) { + return rtJar; + } + + File classesJar = new File(new File(javaHome).getParentFile().getAbsolutePath(), "Classes/classes.jar"); + if (classesJar.exists()) { + return classesJar; + } + return null; + } + + public static void ensureRuntime(JetCoreEnvironment environment, CompilerDependencies compilerDependencies) { + if (compilerDependencies.getCompilerSpecialMode() == CompilerSpecialMode.REGULAR) { + ensureJdkRuntime(environment); + ensureKotlinRuntime(environment); + } + else if (compilerDependencies.getCompilerSpecialMode() == CompilerSpecialMode.JDK_HEADERS) { + ensureJdkRuntime(environment); + } + else if (compilerDependencies.getCompilerSpecialMode() == CompilerSpecialMode.STDLIB) { + ensureJdkRuntime(environment); + } + else if (compilerDependencies.getCompilerSpecialMode() == CompilerSpecialMode.BUILTINS) { + // nop + } + else { + throw new IllegalStateException("unknown mode: " + compilerDependencies.getCompilerSpecialMode()); + } + } + + public static List loadModuleScript(String moduleScriptFile, MessageCollector messageCollector) { + Disposable disposable = new Disposable() { + @Override + public void dispose() { + + } + }; + CompilerDependencies dependencies = CompilerDependencies.compilerDependenciesForProduction(CompilerSpecialMode.REGULAR); + JetCoreEnvironment scriptEnvironment = new JetCoreEnvironment(disposable, dependencies); + ensureRuntime(scriptEnvironment, dependencies); + scriptEnvironment.addSources(moduleScriptFile); + + GenerationState generationState = KotlinToJVMBytecodeCompiler + .analyzeAndGenerate(new CompileEnvironmentConfiguration(scriptEnvironment, dependencies, messageCollector), false); + if (generationState == null) { + return null; + } + + List modules = runDefineModules(dependencies, moduleScriptFile, generationState.getFactory()); + + Disposer.dispose(disposable); + + if (modules == null) { + throw new CompileEnvironmentException("Module script " + moduleScriptFile + " compilation failed"); + } + + if (modules.isEmpty()) { + throw new CompileEnvironmentException("No modules where defined by " + moduleScriptFile); + } + return modules; + } + + private static List runDefineModules(CompilerDependencies compilerDependencies, String moduleFile, ClassFileFactory factory) { + File stdlibJar = compilerDependencies.getRuntimeJar(); + GeneratedClassLoader loader; + if (stdlibJar != null) { + try { + loader = new GeneratedClassLoader(factory, new URLClassLoader(new URL[]{stdlibJar.toURI().toURL()}, + AllModules.class.getClassLoader())); + } + catch (MalformedURLException e) { + throw new RuntimeException(e); + } + } + else { + loader = new GeneratedClassLoader(factory, CompileEnvironmentConfiguration.class.getClassLoader()); + } + try { + Class namespaceClass = loader.loadClass(JvmAbi.PACKAGE_CLASS); + final Method method = namespaceClass.getDeclaredMethod("project"); + if (method == null) { + throw new CompileEnvironmentException("Module script " + moduleFile + " must define project() function"); + } + + method.setAccessible(true); + method.invoke(null); + + ArrayList answer = new ArrayList(AllModules.modules.get()); + AllModules.modules.get().clear(); + return answer; + } + catch (Exception e) { + throw new ModuleExecutionException(e); + } + finally { + loader.dispose(); + } + } + + public static void writeToJar(ClassFileFactory factory, final OutputStream fos, @Nullable FqName mainClass, boolean includeRuntime) { + try { + Manifest manifest = new Manifest(); + final Attributes mainAttributes = manifest.getMainAttributes(); + mainAttributes.putValue("Manifest-Version", "1.0"); + mainAttributes.putValue("Created-By", "JetBrains Kotlin"); + if (mainClass != null) { + mainAttributes.putValue("Main-Class", mainClass.getFqName()); + } + JarOutputStream stream = new JarOutputStream(fos, manifest); + try { + for (String file : factory.files()) { + stream.putNextEntry(new JarEntry(file)); + stream.write(factory.asBytes(file)); + } + if (includeRuntime) { + writeRuntimeToJar(stream); + } + } + finally { + stream.close(); + fos.close(); + } + } + catch (IOException e) { + throw new CompileEnvironmentException("Failed to generate jar file", e); + } + } + + private static void writeRuntimeToJar(final JarOutputStream stream) throws IOException { + final File unpackedRuntimePath = getUnpackedRuntimePath(); + if (unpackedRuntimePath != null) { + FileUtil.processFilesRecursively(unpackedRuntimePath, new Processor() { + @Override + public boolean process(File file) { + if (file.isDirectory()) return true; + final String relativePath = FileUtil.getRelativePath(unpackedRuntimePath, file); + try { + stream.putNextEntry(new JarEntry(FileUtil.toSystemIndependentName(relativePath))); + FileInputStream fis = new FileInputStream(file); + try { + FileUtil.copy(fis, stream); + } + finally { + fis.close(); + } + } + catch (IOException e) { + throw new RuntimeException(e); + } + return true; + } + }); + } + else { + File runtimeJarPath = getRuntimeJarPath(); + if (runtimeJarPath != null) { + JarInputStream jis = new JarInputStream(new FileInputStream(runtimeJarPath)); + try { + while (true) { + JarEntry e = jis.getNextJarEntry(); + if (e == null) { + break; + } + if (FileUtil.getExtension(e.getName()).equals("class")) { + stream.putNextEntry(e); + FileUtil.copy(jis, stream); + } + } + } + finally { + jis.close(); + } + } + else { + throw new CompileEnvironmentException("Couldn't find runtime library"); + } + } + } + + public static void writeToOutputDirectory(ClassFileFactory factory, final String outputDir) { + List files = factory.files(); + for (String file : files) { + File target = new File(outputDir, file); + try { + FileUtil.writeToFile(target, factory.asBytes(file)); + } + catch (IOException e) { + throw new CompileEnvironmentException(e); + } + } + } + + /** + * Add path specified to the compilation environment. + * + * @param environment compilation environment to add to + * @param paths paths to add + */ + public static void addToClasspath(JetCoreEnvironment environment, File... paths) { + for (File path : paths) { + if (!path.exists()) { + throw new CompileEnvironmentException("'" + path + "' does not exist"); + } + environment.addToClasspath(path); + } + } + + /** + * Add path specified to the compilation environment. + * + * @param environment compilation environment to add to + * @param paths paths to add + */ + public static void addToClasspath(JetCoreEnvironment environment, String... paths) { + for (String path : paths) { + addToClasspath(environment, new File(path)); + } + } +} diff --git a/compiler/cli/src/org/jetbrains/jet/cli/jvm/compiler/JetCoreEnvironment.java b/compiler/cli/src/org/jetbrains/jet/cli/jvm/compiler/JetCoreEnvironment.java new file mode 100644 index 00000000000..14e5111f06b --- /dev/null +++ b/compiler/cli/src/org/jetbrains/jet/cli/jvm/compiler/JetCoreEnvironment.java @@ -0,0 +1,155 @@ +/* + * Copyright 2010-2012 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. + * 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 org.jetbrains.jet.cli.jvm.compiler; + +import com.intellij.core.JavaCoreEnvironment; +import com.intellij.lang.java.JavaParserDefinition; +import com.intellij.mock.MockApplication; +import com.intellij.openapi.Disposable; +import com.intellij.openapi.extensions.Extensions; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.PsiElementFinder; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiManager; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.asJava.JavaElementFinder; +import org.jetbrains.jet.lang.parsing.JetParserDefinition; +import org.jetbrains.jet.lang.psi.JetFile; +import org.jetbrains.jet.lang.resolve.java.CompilerDependencies; +import org.jetbrains.jet.lang.resolve.java.CompilerSpecialMode; +import org.jetbrains.jet.lang.resolve.java.JetFilesProvider; +import org.jetbrains.jet.lang.types.lang.JetStandardLibrary; +import org.jetbrains.jet.plugin.JetFileType; + +import java.io.File; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.List; + +/** + * @author yole + */ +public class JetCoreEnvironment extends JavaCoreEnvironment { + private final List sourceFiles = new ArrayList(); + + public JetCoreEnvironment(Disposable parentDisposable, @NotNull CompilerDependencies compilerDependencies) { + super(parentDisposable); + registerFileType(JetFileType.INSTANCE, "kt"); + registerFileType(JetFileType.INSTANCE, "kts"); + registerFileType(JetFileType.INSTANCE, "ktm"); + registerFileType(JetFileType.INSTANCE, "jet"); + registerParserDefinition(new JavaParserDefinition()); + registerParserDefinition(new JetParserDefinition()); + + + myProject.registerService(JetFilesProvider.class, new CliJetFilesProvider(this)); + Extensions.getArea(myProject) + .getExtensionPoint(PsiElementFinder.EP_NAME) + .registerExtension(new JavaElementFinder(myProject)); + + CompilerSpecialMode compilerSpecialMode = compilerDependencies.getCompilerSpecialMode(); + + addToClasspath(compilerDependencies.getJdkJar()); + + if (compilerSpecialMode.includeJdkHeaders()) { + for (VirtualFile root : compilerDependencies.getJdkHeaderRoots()) { + addLibraryRoot(root); + } + } + if (compilerSpecialMode.includeKotlinRuntime()) { + for (VirtualFile root : compilerDependencies.getRuntimeRoots()) { + addLibraryRoot(root); + } + } + + JetStandardLibrary.initialize(getProject()); + } + + public MockApplication getApplication() { + return myApplication; + } + + private void addSources(File file) { + if(file.isDirectory()) { + File[] files = file.listFiles(); + if (files != null) { + for (File child : files) { + addSources(child); + } + } + } + else { + VirtualFile fileByPath = getLocalFileSystem().findFileByPath(file.getAbsolutePath()); + if (fileByPath != null) { + PsiFile psiFile = PsiManager.getInstance(getProject()).findFile(fileByPath); + if(psiFile instanceof JetFile) { + sourceFiles.add((JetFile)psiFile); + } + } + } + } + + public void addSources(VirtualFile vFile) { + if (vFile.isDirectory()) { + for (VirtualFile virtualFile : vFile.getChildren()) { + addSources(virtualFile); + } + } + else { + if (vFile.getFileType() == JetFileType.INSTANCE) { + PsiFile psiFile = PsiManager.getInstance(getProject()).findFile(vFile); + if (psiFile instanceof JetFile) { + sourceFiles.add((JetFile)psiFile); + } + } + } + } + + public void addSources(String path) { + if(path == null) + return; + + VirtualFile vFile = getLocalFileSystem().findFileByPath(path); + if (vFile == null) { + throw new CompileEnvironmentException("File/directory not found: " + path); + } + if (!vFile.isDirectory() && vFile.getFileType() != JetFileType.INSTANCE) { + throw new CompileEnvironmentException("Not a Kotlin file: " + path); + } + + addSources(new File(path)); + } + + public List getSourceFiles() { + return sourceFiles; + } + + public void addToClasspathFromClassLoader(ClassLoader loader) { + ClassLoader parent = loader.getParent(); + if(parent != null) + addToClasspathFromClassLoader(parent); + + if(loader instanceof URLClassLoader) { + for (URL url : ((URLClassLoader) loader).getURLs()) { + File file = new File(url.getPath()); + if(file.exists() && (!file.isFile() || file.getPath().endsWith(".jar"))) + addToClasspath(file); + } + } + } +} diff --git a/compiler/cli/src/org/jetbrains/jet/cli/jvm/compiler/KotlinToJVMBytecodeCompiler.java b/compiler/cli/src/org/jetbrains/jet/cli/jvm/compiler/KotlinToJVMBytecodeCompiler.java new file mode 100644 index 00000000000..af98cf42dc6 --- /dev/null +++ b/compiler/cli/src/org/jetbrains/jet/cli/jvm/compiler/KotlinToJVMBytecodeCompiler.java @@ -0,0 +1,282 @@ +/* + * Copyright 2010-2012 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. + * 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 org.jetbrains.jet.cli.jvm.compiler; + +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiFile; +import com.intellij.testFramework.LightVirtualFile; +import com.intellij.util.LocalTimeCounter; +import jet.Function0; +import jet.modules.Module; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.analyzer.AnalyzeExhaust; +import org.jetbrains.jet.cli.common.CompilerPlugin; +import org.jetbrains.jet.cli.common.CompilerPluginContext; +import org.jetbrains.jet.codegen.*; +import org.jetbrains.jet.cli.common.messages.AnalyzerWithCompilerReport; +import org.jetbrains.jet.cli.common.messages.CompilerMessageLocation; +import org.jetbrains.jet.cli.common.messages.CompilerMessageSeverity; +import org.jetbrains.jet.lang.cfg.pseudocode.JetControlFlowDataTraceFactory; +import org.jetbrains.jet.lang.psi.JetFile; +import org.jetbrains.jet.lang.psi.JetPsiUtil; +import org.jetbrains.jet.lang.resolve.FqName; +import org.jetbrains.jet.lang.resolve.java.AnalyzerFacadeForJVM; +import org.jetbrains.jet.lang.resolve.java.JvmAbi; +import org.jetbrains.jet.plugin.JetLanguage; +import org.jetbrains.jet.plugin.JetMainDetector; +import org.jetbrains.jet.utils.Progress; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.util.List; + +/** + * @author yole + * @author abreslav + */ +public class KotlinToJVMBytecodeCompiler { + + private KotlinToJVMBytecodeCompiler() { + } + + @Nullable + public static ClassFileFactory compileModule( + CompileEnvironmentConfiguration configuration, + Module moduleBuilder, + File directory + ) { + if (moduleBuilder.getSourceFiles().isEmpty()) { + throw new CompileEnvironmentException("No source files where defined"); + } + + for (String sourceFile : moduleBuilder.getSourceFiles()) { + File source = new File(sourceFile); + if (!source.isAbsolute()) { + source = new File(directory, sourceFile); + } + + if (!source.exists()) { + throw new CompileEnvironmentException("'" + source + "' does not exist"); + } + + configuration.getEnvironment().addSources(source.getPath()); + } + for (String classpathRoot : moduleBuilder.getClasspathRoots()) { + configuration.getEnvironment().addToClasspath(new File(classpathRoot)); + } + + CompileEnvironmentUtil.ensureRuntime(configuration.getEnvironment(), configuration.getCompilerDependencies()); + + GenerationState generationState = analyzeAndGenerate(configuration); + if (generationState == null) { + return null; + } + return generationState.getFactory(); + } + + public static boolean compileModules( + CompileEnvironmentConfiguration configuration, + + @NotNull List modules, + + @NotNull File directory, + @Nullable String jarPath, + @Nullable String outputDir, + boolean jarRuntime) { + + for (Module moduleBuilder : modules) { + // TODO: this should be done only once for the environment + if (configuration.getCompilerDependencies().getRuntimeJar() != null) { + CompileEnvironmentUtil + .addToClasspath(configuration.getEnvironment(), configuration.getCompilerDependencies().getRuntimeJar()); + } + ClassFileFactory moduleFactory = compileModule(configuration, moduleBuilder, directory); + if (moduleFactory == null) { + return false; + } + if (outputDir != null) { + CompileEnvironmentUtil.writeToOutputDirectory(moduleFactory, outputDir); + } + else { + String path = jarPath != null ? jarPath : new File(directory, moduleBuilder.getModuleName() + ".jar").getPath(); + try { + CompileEnvironmentUtil.writeToJar(moduleFactory, new FileOutputStream(path), null, jarRuntime); + } + catch (FileNotFoundException e) { + throw new CompileEnvironmentException("Invalid jar path " + path, e); + } + } + } + return true; + } + + private static boolean compileBunchOfSources( + CompileEnvironmentConfiguration configuration, + String jar, + String outputDir, + boolean includeRuntime + ) { + FqName mainClass = null; + for (JetFile file : configuration.getEnvironment().getSourceFiles()) { + if (JetMainDetector.hasMain(file.getDeclarations())) { + FqName fqName = JetPsiUtil.getFQName(file); + mainClass = fqName.child(JvmAbi.PACKAGE_CLASS); + break; + } + } + + CompileEnvironmentUtil.ensureRuntime(configuration.getEnvironment(), configuration.getCompilerDependencies()); + + GenerationState generationState = analyzeAndGenerate(configuration); + if (generationState == null) { + return false; + } + + try { + ClassFileFactory factory = generationState.getFactory(); + if (jar != null) { + try { + CompileEnvironmentUtil.writeToJar(factory, new FileOutputStream(jar), mainClass, includeRuntime); + } + catch (FileNotFoundException e) { + throw new CompileEnvironmentException("Invalid jar path " + jar, e); + } + } + else if (outputDir != null) { + CompileEnvironmentUtil.writeToOutputDirectory(factory, outputDir); + } + else { + throw new CompileEnvironmentException("Output directory or jar file is not specified - no files will be saved to the disk"); + } + return true; + } + finally { + generationState.destroy(); + } + } + + public static boolean compileBunchOfSources( + CompileEnvironmentConfiguration configuration, + + String sourceFileOrDir, String jar, String outputDir, boolean includeRuntime) { + configuration.getEnvironment().addSources(sourceFileOrDir); + + return compileBunchOfSources(configuration, jar, outputDir, includeRuntime); + } + + public static boolean compileBunchOfSourceDirectories( + CompileEnvironmentConfiguration configuration, + + List sources, String jar, String outputDir, boolean includeRuntime) { + for (String source : sources) { + configuration.getEnvironment().addSources(source); + } + + return compileBunchOfSources(configuration, jar, outputDir, includeRuntime); + } + + @Nullable + public static ClassLoader compileText( + CompileEnvironmentConfiguration configuration, + String code) { + configuration.getEnvironment() + .addSources(new LightVirtualFile("script" + LocalTimeCounter.currentTime() + ".kt", JetLanguage.INSTANCE, code)); + + GenerationState generationState = analyzeAndGenerate(configuration); + if (generationState == null) { + return null; + } + return new GeneratedClassLoader(generationState.getFactory()); + } + + @Nullable + public static GenerationState analyzeAndGenerate(CompileEnvironmentConfiguration configuration) { + return analyzeAndGenerate(configuration, configuration.getCompilerDependencies().getCompilerSpecialMode().isStubs()); + } + + @Nullable + public static GenerationState analyzeAndGenerate( + CompileEnvironmentConfiguration configuration, + boolean stubs + ) { + AnalyzeExhaust exhaust = analyze(configuration, stubs); + + if (exhaust == null) { + return null; + } + + exhaust.throwIfError(); + + return generate(configuration, exhaust, stubs); + } + + @Nullable + private static AnalyzeExhaust analyze( + final CompileEnvironmentConfiguration configuration, + boolean stubs) { + final JetCoreEnvironment environment = configuration.getEnvironment(); + AnalyzerWithCompilerReport analyzerWithCompilerReport = new AnalyzerWithCompilerReport(configuration.getMessageCollector()); + final Predicate filesToAnalyzeCompletely = + stubs ? Predicates.alwaysFalse() : Predicates.alwaysTrue(); + analyzerWithCompilerReport.analyzeAndReport( + new Function0() { + @NotNull + @Override + public AnalyzeExhaust invoke() { + return AnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration( + environment.getProject(), environment.getSourceFiles(), filesToAnalyzeCompletely, + JetControlFlowDataTraceFactory.EMPTY, + configuration.getCompilerDependencies()); + } + }, environment.getSourceFiles() + ); + + return analyzerWithCompilerReport.hasErrors() ? null : analyzerWithCompilerReport.getAnalyzeExhaust(); + } + + @NotNull + private static GenerationState generate( + final CompileEnvironmentConfiguration configuration, + AnalyzeExhaust exhaust, + boolean stubs) { + JetCoreEnvironment environment = configuration.getEnvironment(); + Project project = environment.getProject(); + Progress backendProgress = new Progress() { + @Override + public void log(String message) { + configuration.getMessageCollector().report(CompilerMessageSeverity.LOGGING, message, CompilerMessageLocation.NO_LOCATION); + } + }; + GenerationState generationState = new GenerationState(project, ClassBuilderFactories.binaries(stubs), backendProgress, + exhaust, environment.getSourceFiles(), + configuration.getCompilerDependencies().getCompilerSpecialMode()); + generationState.compileCorrectFiles(CompilationErrorHandler.THROW_EXCEPTION); + + List plugins = configuration.getCompilerPlugins(); + if (plugins != null) { + CompilerPluginContext context = new CompilerPluginContext(project, exhaust.getBindingContext(), environment.getSourceFiles()); + for (CompilerPlugin plugin : plugins) { + plugin.processFiles(context); + } + } + return generationState; + } +} diff --git a/compiler/cli/src/org/jetbrains/jet/cli/jvm/compiler/ModuleExecutionException.java b/compiler/cli/src/org/jetbrains/jet/cli/jvm/compiler/ModuleExecutionException.java new file mode 100644 index 00000000000..e20d73888fd --- /dev/null +++ b/compiler/cli/src/org/jetbrains/jet/cli/jvm/compiler/ModuleExecutionException.java @@ -0,0 +1,34 @@ +/* + * Copyright 2010-2012 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. + * 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 org.jetbrains.jet.cli.jvm.compiler; + +/** + * @author yole + */ +public class ModuleExecutionException extends RuntimeException { + public ModuleExecutionException(String message) { + super(message); + } + + public ModuleExecutionException(Throwable cause) { + super(cause); + } + + public ModuleExecutionException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/compiler/tests/org/jetbrains/jet/cli/jvm/compiler/CompileEnvironmentTest.java b/compiler/tests/org/jetbrains/jet/cli/jvm/compiler/CompileEnvironmentTest.java new file mode 100644 index 00000000000..57ddba262e0 --- /dev/null +++ b/compiler/tests/org/jetbrains/jet/cli/jvm/compiler/CompileEnvironmentTest.java @@ -0,0 +1,104 @@ +/* + * Copyright 2010-2012 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. + * 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 org.jetbrains.jet.cli.jvm.compiler; + +import com.intellij.openapi.util.io.FileUtil; +import junit.framework.TestCase; +import org.jetbrains.jet.cli.common.ExitCode; +import org.jetbrains.jet.cli.jvm.K2JVMCompiler; +import org.jetbrains.jet.codegen.forTestCompile.ForTestCompileJdkHeaders; +import org.jetbrains.jet.codegen.forTestCompile.ForTestCompileRuntime; +import org.jetbrains.jet.parsing.JetParsingTest; +import org.junit.Assert; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.jar.JarEntry; +import java.util.jar.JarInputStream; + +/** + * @author yole + * @author alex.tkachman + */ +public class CompileEnvironmentTest extends TestCase { + + public void testSmokeWithCompilerJar() throws IOException { + File tempDir = FileUtil.createTempDirectory("compilerTest", "compilerTest"); + + try { + File stdlib = ForTestCompileRuntime.runtimeJarForTests(); + File jdkHeaders = ForTestCompileJdkHeaders.jdkHeadersForTests(); + File resultJar = new File(tempDir, "result.jar"); + ExitCode rv = new K2JVMCompiler().exec(System.out, + "-module", JetParsingTest.getTestDataDir() + "/compiler/smoke/Smoke.kts", + "-jar", resultJar.getAbsolutePath(), + "-stdlib", stdlib.getAbsolutePath(), + "-jdkHeaders", jdkHeaders.getAbsolutePath()); + Assert.assertEquals("compilation completed with non-zero code", ExitCode.OK, rv); + FileInputStream fileInputStream = new FileInputStream(resultJar); + try { + JarInputStream is = new JarInputStream(fileInputStream); + try { + final List entries = listEntries(is); + assertTrue(entries.contains("Smoke/namespace.class")); + assertEquals(1, entries.size()); + } + finally { + is.close(); + } + } + finally { + fileInputStream.close(); + } + } + finally { + FileUtil.delete(tempDir); + } + } + + public void testSmokeWithCompilerOutput() throws IOException { + File tempDir = FileUtil.createTempDirectory("compilerTest", "compilerTest"); + try { + File out = new File(tempDir, "out"); + File stdlib = ForTestCompileRuntime.runtimeJarForTests(); + File jdkHeaders = ForTestCompileJdkHeaders.jdkHeadersForTests(); + ExitCode exitCode = new K2JVMCompiler() + .exec(System.out, "-src", JetParsingTest.getTestDataDir() + "/compiler/smoke/Smoke.kt", "-output", + out.getAbsolutePath(), "-stdlib", stdlib.getAbsolutePath(), "-jdkHeaders", jdkHeaders.getAbsolutePath()); + Assert.assertEquals(ExitCode.OK, exitCode); + assertEquals(1, out.listFiles().length); + assertEquals(1, out.listFiles()[0].listFiles().length); + } finally { + FileUtil.delete(tempDir); + } + } + + private static List listEntries(JarInputStream is) throws IOException { + List entries = new ArrayList(); + while (true) { + final JarEntry jarEntry = is.getNextJarEntry(); + if (jarEntry == null) { + break; + } + entries.add(jarEntry.getName()); + } + return entries; + } +} diff --git a/compiler/tests/org/jetbrains/jet/cli/jvm/compiler/CompileJavaAgainstKotlinTest.java b/compiler/tests/org/jetbrains/jet/cli/jvm/compiler/CompileJavaAgainstKotlinTest.java new file mode 100644 index 00000000000..6f995fb3ff5 --- /dev/null +++ b/compiler/tests/org/jetbrains/jet/cli/jvm/compiler/CompileJavaAgainstKotlinTest.java @@ -0,0 +1,113 @@ +/* + * Copyright 2010-2012 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. + * 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 org.jetbrains.jet.cli.jvm.compiler; + +import com.intellij.openapi.util.Disposer; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.vfs.CharsetToolkit; +import com.intellij.psi.PsiFileFactory; +import com.intellij.psi.impl.PsiFileFactoryImpl; +import com.intellij.testFramework.LightVirtualFile; +import junit.framework.Test; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.JetTestCaseBuilder; +import org.jetbrains.jet.JetTestUtils; +import org.jetbrains.jet.codegen.*; +import org.jetbrains.jet.lang.psi.JetFile; +import org.jetbrains.jet.lang.resolve.java.CompilerSpecialMode; +import org.jetbrains.jet.plugin.JetLanguage; +import org.junit.Assert; + +import javax.tools.JavaCompiler; +import javax.tools.JavaFileObject; +import javax.tools.StandardJavaFileManager; +import javax.tools.ToolProvider; +import java.io.File; +import java.nio.charset.Charset; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Locale; + +/** + * @author Stepan Koltsov + * + * @see WriteSignatureTest + */ +public class CompileJavaAgainstKotlinTest extends TestCaseWithTmpdir { + + private final File ktFile; + private final File javaFile; + private JetCoreEnvironment jetCoreEnvironment; + + public CompileJavaAgainstKotlinTest(File ktFile) { + this.ktFile = ktFile; + Assert.assertTrue(ktFile.getName().endsWith(".kt")); + this.javaFile = new File(ktFile.getPath().replaceFirst("\\.kt", ".java")); + } + + @Override + public String getName() { + return ktFile.getName(); + } + + @Override + protected void runTest() throws Throwable { + jetCoreEnvironment = JetTestUtils.createEnvironmentWithMockJdk(myTestRootDisposable); + + + String text = FileUtil.loadFile(ktFile); + + LightVirtualFile virtualFile = new LightVirtualFile(ktFile.getName(), JetLanguage.INSTANCE, text); + virtualFile.setCharset(CharsetToolkit.UTF8_CHARSET); + JetFile psiFile = (JetFile) ((PsiFileFactoryImpl) PsiFileFactory.getInstance(jetCoreEnvironment.getProject())).trySetupPsiForFile(virtualFile, JetLanguage.INSTANCE, true, false); + + ClassFileFactory classFileFactory = GenerationUtils.compileFileGetClassFileFactoryForTest(psiFile, CompilerSpecialMode.REGULAR); + + CompileEnvironmentUtil.writeToOutputDirectory(classFileFactory, tmpdir.getPath()); + + Disposer.dispose(myTestRootDisposable); + + JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler(); + + StandardJavaFileManager fileManager = javaCompiler.getStandardFileManager(null, Locale.ENGLISH, Charset.forName("utf-8")); + try { + Iterable javaFileObjectsFromFiles = fileManager.getJavaFileObjectsFromFiles(Collections.singleton(javaFile)); + List options = Arrays.asList( + "-classpath", tmpdir.getPath() + System.getProperty("path.separator") + "out/production/stdlib", + "-d", tmpdir.getPath() + ); + JavaCompiler.CompilationTask task = javaCompiler.getTask(null, fileManager, null, options, null, javaFileObjectsFromFiles); + + Assert.assertTrue(task.call()); + } finally { + fileManager.close(); + } + } + + public static Test suite() { + return JetTestCaseBuilder.suiteForDirectory(JetTestCaseBuilder.getTestDataPathBase(), "/compileJavaAgainstKotlin", true, new JetTestCaseBuilder.NamedTestFactory() { + @NotNull + @Override + public Test createTest(@NotNull String dataPath, @NotNull String name, @NotNull File file) { + return new CompileJavaAgainstKotlinTest(file); + } + }); + + } + +} diff --git a/compiler/tests/org/jetbrains/jet/cli/jvm/compiler/CompileKotlinAgainstKotlinTest.java b/compiler/tests/org/jetbrains/jet/cli/jvm/compiler/CompileKotlinAgainstKotlinTest.java new file mode 100644 index 00000000000..d4fca06a78f --- /dev/null +++ b/compiler/tests/org/jetbrains/jet/cli/jvm/compiler/CompileKotlinAgainstKotlinTest.java @@ -0,0 +1,133 @@ +/* + * Copyright 2010-2012 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. + * 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 org.jetbrains.jet.cli.jvm.compiler; + +import com.intellij.openapi.util.Disposer; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.vfs.CharsetToolkit; +import com.intellij.psi.PsiFileFactory; +import com.intellij.psi.impl.PsiFileFactoryImpl; +import com.intellij.testFramework.LightVirtualFile; +import junit.framework.Assert; +import junit.framework.Test; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.JetTestCaseBuilder; +import org.jetbrains.jet.JetTestUtils; +import org.jetbrains.jet.codegen.*; +import org.jetbrains.jet.lang.psi.JetFile; +import org.jetbrains.jet.lang.resolve.java.CompilerSpecialMode; +import org.jetbrains.jet.plugin.JetLanguage; + +import java.io.File; +import java.io.FilenameFilter; +import java.io.IOException; +import java.lang.reflect.Method; +import java.net.URL; +import java.net.URLClassLoader; + +/** + * @author Stepan Koltsov + */ +public class CompileKotlinAgainstKotlinTest extends TestCaseWithTmpdir { + + private final File ktAFile; + private final File ktBFile; + + public CompileKotlinAgainstKotlinTest(File ktAFile) { + Assert.assertTrue(ktAFile.getName().endsWith("A.kt")); + this.ktAFile = ktAFile; + this.ktBFile = new File(ktAFile.getPath().replaceFirst("A\\.kt$", "B.kt")); + } + + @Override + public String getName() { + return ktAFile.getName(); + } + + private File aDir; + private File bDir; + + @Override + protected void setUp() throws Exception { + super.setUp(); + aDir = new File(tmpdir, "a"); + bDir = new File(tmpdir, "b"); + JetTestUtils.mkdirs(aDir); + JetTestUtils.mkdirs(bDir); + } + + @Override + protected void runTest() throws Throwable { + compileA(); + compileB(); + + URLClassLoader classLoader = new URLClassLoader(new URL[]{ aDir.toURI().toURL(), bDir.toURI().toURL() }); + Class clazz = classLoader.loadClass("bbb.namespace"); + Method main = clazz.getMethod("main", new Class[] { String[].class }); + main.invoke(null, new Object[] { new String[0] }); + } + + private void compileA() throws IOException { + JetCoreEnvironment jetCoreEnvironment = JetTestUtils.createEnvironmentWithMockJdk(myTestRootDisposable); + + String text = FileUtil.loadFile(ktAFile); + + LightVirtualFile virtualFile = new LightVirtualFile(ktAFile.getName(), JetLanguage.INSTANCE, text); + virtualFile.setCharset(CharsetToolkit.UTF8_CHARSET); + JetFile psiFile = (JetFile) ((PsiFileFactoryImpl) PsiFileFactory.getInstance(jetCoreEnvironment.getProject())).trySetupPsiForFile(virtualFile, JetLanguage.INSTANCE, true, false); + + ClassFileFactory classFileFactory = GenerationUtils.compileFileGetClassFileFactoryForTest(psiFile, CompilerSpecialMode.REGULAR); + + CompileEnvironmentUtil.writeToOutputDirectory(classFileFactory, aDir.getPath()); + + Disposer.dispose(myTestRootDisposable); + } + + private void compileB() throws IOException { + JetCoreEnvironment jetCoreEnvironment = JetTestUtils.createEnvironmentWithMockJdk(myTestRootDisposable); + + jetCoreEnvironment.addToClasspath(aDir); + + String text = FileUtil.loadFile(ktBFile); + + LightVirtualFile virtualFile = new LightVirtualFile(ktAFile.getName(), JetLanguage.INSTANCE, text); + virtualFile.setCharset(CharsetToolkit.UTF8_CHARSET); + JetFile psiFile = (JetFile) ((PsiFileFactoryImpl) PsiFileFactory.getInstance(jetCoreEnvironment.getProject())).trySetupPsiForFile(virtualFile, JetLanguage.INSTANCE, true, false); + + ClassFileFactory classFileFactory = GenerationUtils.compileFileGetClassFileFactoryForTest(psiFile, CompilerSpecialMode.REGULAR); + + CompileEnvironmentUtil.writeToOutputDirectory(classFileFactory, bDir.getPath()); + + Disposer.dispose(myTestRootDisposable); + } + + public static Test suite() { + class Filter implements FilenameFilter { + @Override + public boolean accept(File dir, String name) { + return name.endsWith("A.kt"); + } + } + return JetTestCaseBuilder.suiteForDirectory(JetTestCaseBuilder.getTestDataPathBase(), "/compileKotlinAgainstKotlin", true, new Filter(), new JetTestCaseBuilder.NamedTestFactory() { + @NotNull + @Override + public Test createTest(@NotNull String dataPath, @NotNull String name, @NotNull File file) { + return new CompileKotlinAgainstKotlinTest(file); + } + }); + } +} diff --git a/compiler/tests/org/jetbrains/jet/cli/jvm/compiler/JavaDescriptorResolverTest.java b/compiler/tests/org/jetbrains/jet/cli/jvm/compiler/JavaDescriptorResolverTest.java new file mode 100644 index 00000000000..d2f9e3ffc2d --- /dev/null +++ b/compiler/tests/org/jetbrains/jet/cli/jvm/compiler/JavaDescriptorResolverTest.java @@ -0,0 +1,72 @@ +/* + * Copyright 2010-2012 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. + * 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 org.jetbrains.jet.cli.jvm.compiler; + +import org.jetbrains.jet.CompileCompilerDependenciesTest; +import org.jetbrains.jet.JetTestUtils; +import org.jetbrains.jet.di.InjectorForJavaSemanticServices; +import org.jetbrains.jet.lang.descriptors.ClassDescriptor; +import org.jetbrains.jet.lang.resolve.FqName; +import org.jetbrains.jet.lang.resolve.java.CompilerSpecialMode; +import org.jetbrains.jet.lang.resolve.java.DescriptorSearchRule; +import org.jetbrains.jet.lang.resolve.java.JavaDescriptorResolver; +import org.junit.Assert; + +import javax.tools.*; +import java.io.File; +import java.nio.charset.Charset; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Locale; + +/** + * @author Stepan Koltsov + * @see ReadJavaBinaryClassTest + */ +public class JavaDescriptorResolverTest extends TestCaseWithTmpdir { + + // This test can be implemented in ReadJavaBinaryClass test, but it is simpler here + public void testInner() throws Exception { + JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler(); + + StandardJavaFileManager fileManager = javaCompiler.getStandardFileManager(null, Locale.ENGLISH, Charset.forName("utf-8")); + try { + File file = new File("compiler/testData/javaDescriptorResolver/inner.java"); + Iterable javaFileObjectsFromFiles = fileManager.getJavaFileObjectsFromFiles(Collections.singleton(file)); + List options = Arrays.asList( + "-d", tmpdir.getPath() + ); + JavaCompiler.CompilationTask task = javaCompiler.getTask(null, fileManager, null, options, null, javaFileObjectsFromFiles); + + Assert.assertTrue(task.call()); + } finally { + fileManager.close(); + } + + JetCoreEnvironment jetCoreEnvironment = JetTestUtils.createEnvironmentWithMockJdk(myTestRootDisposable, CompilerSpecialMode.JDK_HEADERS); + + jetCoreEnvironment.addToClasspath(tmpdir); + + InjectorForJavaSemanticServices injector = new InjectorForJavaSemanticServices( + CompileCompilerDependenciesTest.compilerDependenciesForTests(CompilerSpecialMode.JDK_HEADERS, true), jetCoreEnvironment.getProject()); + JavaDescriptorResolver javaDescriptorResolver = injector.getJavaDescriptorResolver(); + ClassDescriptor classDescriptor = javaDescriptorResolver.resolveClass(new FqName("A"), DescriptorSearchRule.ERROR_IF_FOUND_IN_KOTLIN); + Assert.assertNotNull(classDescriptor); + } + +} diff --git a/compiler/tests/org/jetbrains/jet/cli/jvm/compiler/NamespaceComparator.java b/compiler/tests/org/jetbrains/jet/cli/jvm/compiler/NamespaceComparator.java new file mode 100644 index 00000000000..e7be1728e58 --- /dev/null +++ b/compiler/tests/org/jetbrains/jet/cli/jvm/compiler/NamespaceComparator.java @@ -0,0 +1,568 @@ +/* + * Copyright 2010-2012 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. + * 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 org.jetbrains.jet.cli.jvm.compiler; + +import com.google.common.io.Files; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.codegen.PropertyCodegen; +import org.jetbrains.jet.lang.descriptors.*; +import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor; +import org.jetbrains.jet.lang.resolve.DescriptorUtils; +import org.jetbrains.jet.lang.resolve.scopes.JetScope; +import org.jetbrains.jet.lang.resolve.scopes.receivers.ExtensionReceiver; +import org.jetbrains.jet.lang.types.JetType; +import org.jetbrains.jet.lang.types.TypeProjection; +import org.jetbrains.jet.lang.types.Variance; +import org.junit.Assert; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.StringReader; +import java.lang.reflect.Method; +import java.nio.charset.Charset; +import java.util.*; + +/** + * @author Stepan Koltsov + */ +class NamespaceComparator { + + private final boolean includeObject; + + private NamespaceComparator(boolean includeObject) { + this.includeObject = includeObject; + } + + public static void compareNamespaces(@NotNull NamespaceDescriptor nsa, @NotNull NamespaceDescriptor nsb, boolean includeObject, + @NotNull File txtFile) { + String serialized = new NamespaceComparator(includeObject).doCompareNamespaces(nsa, nsb); + try { + for (;;) { + String expected = Files.toString(txtFile, Charset.forName("utf-8")).replace("\r\n", "\n"); + + if (expected.contains("kick me")) { + // for developer + System.err.println("generating " + txtFile); + Files.write(serialized, txtFile, Charset.forName("utf-8")); + continue; + } + + // compare with hardcopy: make sure nothing is lost in output + Assert.assertEquals(expected, serialized); + break; + } + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private String doCompareNamespaces(@NotNull NamespaceDescriptor nsa, @NotNull NamespaceDescriptor nsb) { + StringBuilder sb = new StringBuilder(); + Assert.assertEquals(nsa.getName(), nsb.getName()); + + sb.append("namespace " + nsa.getName() + "\n\n"); + + Assert.assertTrue(!nsa.getMemberScope().getAllDescriptors().isEmpty()); + + Set classifierNames = new HashSet(); + Set propertyNames = new HashSet(); + Set functionNames = new HashSet(); + + for (DeclarationDescriptor ad : nsa.getMemberScope().getAllDescriptors()) { + if (ad instanceof ClassifierDescriptor) { + classifierNames.add(ad.getName()); + } + else if (ad instanceof PropertyDescriptor) { + propertyNames.add(ad.getName()); + } + else if (ad instanceof FunctionDescriptor) { + functionNames.add(ad.getName()); + } + else { + throw new AssertionError("unknown member: " + ad); + } + } + + for (String name : classifierNames) { + ClassifierDescriptor ca = nsa.getMemberScope().getClassifier(name); + ClassifierDescriptor cb = nsb.getMemberScope().getClassifier(name); + Assert.assertTrue(ca != null); + Assert.assertTrue(cb != null); + compareClassifiers(ca, cb, sb); + } + + for (String name : propertyNames) { + Set pa = nsa.getMemberScope().getProperties(name); + Set pb = nsb.getMemberScope().getProperties(name); + compareDeclarationSets(pa, pb, sb); + + Assert.assertTrue(nsb.getMemberScope().getFunctions(PropertyCodegen.getterName(name)).isEmpty()); + Assert.assertTrue(nsb.getMemberScope().getFunctions(PropertyCodegen.setterName(name)).isEmpty()); + } + + for (String name : functionNames) { + Set fa = nsa.getMemberScope().getFunctions(name); + Set fb = nsb.getMemberScope().getFunctions(name); + compareDeclarationSets(fa, fb, sb); + } + + return sb.toString(); + } + + private static void compareDeclarationSets(Set a, Set b, + @NotNull StringBuilder sb) { + String at = serializedDeclarationSets(a); + String bt = serializedDeclarationSets(b); + Assert.assertEquals(at, bt); + sb.append(at); + } + + private static String serializedDeclarationSets(Collection ds) { + List strings = new ArrayList(); + for (DeclarationDescriptor d : ds) { + StringBuilder sb = new StringBuilder(); + new Serializer(sb).serialize(d); + strings.add(sb.toString()); + } + + Collections.sort(strings, new MemberComparator()); + + StringBuilder r = new StringBuilder(); + for (String string : strings) { + r.append(string); + r.append("\n"); + } + return r.toString(); + } + + /** + * This comparator only affects test output, you can drop it if you don't want to understand it. + */ + private static class MemberComparator implements Comparator { + + @NotNull + private String normalize(String s) { + return s.replaceFirst( + "^ *(private|final|abstract|open|override|fun|val|var|/\\*.*?\\*/|((?!)<.*?>)| )*", + "") + + s; + } + + @Override + public int compare(@NotNull String a, @NotNull String b) { + return normalize(a).compareTo(normalize(b)); + } + } + + private void compareClassifiers(@NotNull ClassifierDescriptor a, @NotNull ClassifierDescriptor b, @NotNull StringBuilder sb) { + StringBuilder sba = new StringBuilder(); + StringBuilder sbb = new StringBuilder(); + + new FullContentSerialier(sba).serialize((ClassDescriptor) a); + new FullContentSerialier(sbb).serialize((ClassDescriptor) b); + + String as = sba.toString(); + String bs = sbb.toString(); + + Assert.assertEquals(as, bs); + sb.append(as); + } + + + + private static class Serializer { + + protected final StringBuilder sb; + + public Serializer(StringBuilder sb) { + this.sb = sb; + } + + public void serialize(ClassKind kind) { + switch (kind) { + case CLASS: + sb.append("class"); + break; + case TRAIT: + sb.append("trait"); + break; + case OBJECT: + sb.append("object"); + break; + case ANNOTATION_CLASS: + sb.append("annotation class"); + break; + default: + throw new IllegalStateException("unknown class kind: " + kind); + } + } + + + private static Object invoke(Method method, Object thiz, Object... args) { + try { + return method.invoke(thiz, args); + } catch (Exception e) { + throw new RuntimeException("failed to invoke " + method + ": " + e, e); + } + } + + + public void serialize(FunctionDescriptor fun) { + serialize(fun.getModality()); + sb.append(" "); + + if (!fun.getAnnotations().isEmpty()) { + new Serializer(sb).serializeSeparated(fun.getAnnotations(), " "); + sb.append(" "); + } + + if (!fun.getOverriddenDescriptors().isEmpty()) { + sb.append("override /*" + fun.getOverriddenDescriptors().size() + "*/ "); + } + + if (fun instanceof ConstructorDescriptor) { + sb.append("/*constructor*/ "); + } + sb.append("fun "); + if (!fun.getTypeParameters().isEmpty()) { + sb.append("<"); + new Serializer(sb).serializeCommaSeparated(fun.getTypeParameters()); + sb.append(">"); + } + + if (fun.getReceiverParameter().exists()) { + new TypeSerializer(sb).serialize(fun.getReceiverParameter()); + sb.append("."); + } + + sb.append(fun.getName()); + sb.append("("); + new TypeSerializer(sb).serializeCommaSeparated(fun.getValueParameters()); + sb.append("): "); + new TypeSerializer(sb).serialize(fun.getReturnType()); + } + + public void serialize(ExtensionReceiver extensionReceiver) { + serialize(extensionReceiver.getType()); + } + + public void serialize(PropertyDescriptor prop) { + serialize(prop.getModality()); + sb.append(" "); + + if (!prop.getAnnotations().isEmpty()) { + new Serializer(sb).serializeSeparated(prop.getAnnotations(), " "); + sb.append(" "); + } + + if (prop.isVar()) { + sb.append("var "); + } + else { + sb.append("val "); + } + if (!prop.getTypeParameters().isEmpty()) { + sb.append(" <"); + new Serializer(sb).serializeCommaSeparated(prop.getTypeParameters()); + sb.append("> "); + } + if (prop.getReceiverParameter().exists()) { + new TypeSerializer(sb).serialize(prop.getReceiverParameter().getType()); + sb.append("."); + } + sb.append(prop.getName()); + sb.append(": "); + new TypeSerializer(sb).serialize(prop.getType()); + } + + public void serialize(ValueParameterDescriptor valueParameter) { + sb.append("/*"); + sb.append(valueParameter.getIndex()); + sb.append("*/ "); + if (valueParameter.getVarargElementType() != null) { + sb.append("vararg "); + } + sb.append(valueParameter.getName()); + sb.append(": "); + if (valueParameter.getVarargElementType() != null) { + new TypeSerializer(sb).serialize(valueParameter.getVarargElementType()); + } + else { + new TypeSerializer(sb).serialize(valueParameter.getType()); + } + if (valueParameter.hasDefaultValue()) { + sb.append(" = ?"); + } + } + + public void serialize(Variance variance) { + if (variance == Variance.INVARIANT) { + + } + else { + sb.append(variance); + sb.append(' '); + } + } + + public void serialize(Modality modality) { + sb.append(modality.name().toLowerCase()); + } + + public void serialize(AnnotationDescriptor annotation) { + new TypeSerializer(sb).serialize(annotation.getType()); + sb.append("("); + serializeCommaSeparated(annotation.getValueArguments()); + sb.append(")"); + } + + public void serializeCommaSeparated(List list) { + serializeSeparated(list, ", "); + } + + public void serializeSeparated(List list, String sep) { + boolean first = true; + for (Object o : list) { + if (!first) { + sb.append(sep); + } + serialize(o); + first = false; + } + } + + private Method getMethodToSerialize(Object o) { + if (o == null) { + throw new IllegalStateException("won't serialize null"); + } + + // TODO: cache + for (Method method : this.getClass().getMethods()) { + if (!method.getName().equals("serialize")) { + continue; + } + if (method.getParameterTypes().length != 1) { + continue; + } + if (method.getParameterTypes()[0].equals(Object.class)) { + continue; + } + if (method.getParameterTypes()[0].isInstance(o)) { + method.setAccessible(true); + return method; + } + } + throw new IllegalStateException("don't know how to serialize " + o + " (of " + o.getClass() + ")"); + } + + public void serialize(Object o) { + Method method = getMethodToSerialize(o); + invoke(method, this, o); + } + + public void serialize(String s) { + sb.append(s); + } + + public void serialize(ClassDescriptor clazz) { + sb.append(DescriptorUtils.getFQName(clazz)); + } + + public void serialize(NamespaceDescriptor ns) { + sb.append(DescriptorUtils.getFQName(ns)); + } + + public void serialize(TypeParameterDescriptor param) { + sb.append("/*"); + sb.append(param.getIndex()); + if (param.isReified()) { + sb.append(",r"); + } + sb.append("*/ "); + serialize(param.getVariance()); + sb.append(param.getName()); + if (!param.getUpperBounds().isEmpty()) { + sb.append(" : "); + List list = new ArrayList(); + for (JetType upper : param.getUpperBounds()) { + StringBuilder sb = new StringBuilder(); + new TypeSerializer(sb).serialize(upper); + list.add(sb.toString()); + } + Collections.sort(list); + serializeSeparated(list, " & "); // TODO: use where + } + // TODO: lower bounds + } + + } + + private static class TypeSerializer extends Serializer { + + public TypeSerializer(StringBuilder sb) { + super(sb); + } + + @Override + public void serialize(TypeParameterDescriptor param) { + sb.append(param.getName()); + } + + public void serialize(@NotNull JetType type) { + serialize(type.getConstructor().getDeclarationDescriptor()); + if (!type.getArguments().isEmpty()) { + sb.append("<"); + boolean first = true; + for (TypeProjection proj : type.getArguments()) { + if (!first) { + sb.append(", "); + } + serialize(proj.getProjectionKind()); + serialize(proj.getType()); + first = false; + } + sb.append(">"); + } + if (type.isNullable()) { + sb.append("?"); + } + } + + } + + private static boolean isRootNs(DeclarationDescriptor ns) { + return ns instanceof NamespaceDescriptor && ns.getContainingDeclaration() instanceof ModuleDescriptor; + } + + private static class NamespacePrefixSerializer extends Serializer { + + public NamespacePrefixSerializer(StringBuilder sb) { + super(sb); + } + + @Override + public void serialize(NamespaceDescriptor ns) { + super.serialize(ns); + if (isRootNs(ns)) { + return; + } + sb.append("."); + } + + @Override + public void serialize(ClassDescriptor clazz) { + super.serialize(clazz); + sb.append("."); + } + } + + private class FullContentSerialier extends Serializer { + private FullContentSerialier(StringBuilder sb) { + super(sb); + } + + public void serialize(ClassDescriptor klass) { + + if (!klass.getAnnotations().isEmpty()) { + new Serializer(sb).serializeSeparated(klass.getAnnotations(), " "); + sb.append(" "); + } + serialize(klass.getModality()); + sb.append(" "); + + serialize(klass.getKind()); + sb.append(" "); + + new Serializer(sb).serialize(klass); + + if (!klass.getTypeConstructor().getParameters().isEmpty()) { + sb.append("<"); + serializeCommaSeparated(klass.getTypeConstructor().getParameters()); + sb.append(">"); + } + + if (!klass.getTypeConstructor().getSupertypes().isEmpty()) { + sb.append(" : "); + new TypeSerializer(sb).serializeCommaSeparated(new ArrayList(klass.getTypeConstructor().getSupertypes())); + } + + sb.append(" {\n"); + + List typeArguments = new ArrayList(); + for (TypeParameterDescriptor param : klass.getTypeConstructor().getParameters()) { + typeArguments.add(new TypeProjection(Variance.INVARIANT, param.getDefaultType())); + } + + List memberStrings = new ArrayList(); + + for (ConstructorDescriptor constructor : klass.getConstructors()) { + StringBuilder constructorSb = new StringBuilder(); + new Serializer(constructorSb).serialize(constructor); + memberStrings.add(constructorSb.toString()); + } + + JetScope memberScope = klass.getMemberScope(typeArguments); + for (DeclarationDescriptor member : memberScope.getAllDescriptors()) { + if (!includeObject) { + if (member.getName().matches("equals|hashCode|finalize|wait|notify(All)?|toString|clone|getClass")) { + continue; + } + } + StringBuilder memberSb = new StringBuilder(); + new FullContentSerialier(memberSb).serialize(member); + memberStrings.add(memberSb.toString()); + } + + Collections.sort(memberStrings, new MemberComparator()); + + for (String memberString : memberStrings) { + sb.append(indent(memberString)); + } + + if (klass.getClassObjectDescriptor() != null) { + StringBuilder sbForClassObject = new StringBuilder(); + new FullContentSerialier(sbForClassObject).serialize(klass.getClassObjectDescriptor()); + sb.append(indent(sbForClassObject.toString())); + } + + sb.append("}\n"); + } + } + + + private static String indent(String string) { + try { + StringBuilder r = new StringBuilder(); + BufferedReader reader = new BufferedReader(new StringReader(string)); + for (;;) { + String line = reader.readLine(); + if (line == null) { + break; + } + r.append(" "); + r.append(line); + r.append("\n"); + } + return r.toString(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + +} diff --git a/compiler/tests/org/jetbrains/jet/cli/jvm/compiler/ReadJavaBinaryClassTest.java b/compiler/tests/org/jetbrains/jet/cli/jvm/compiler/ReadJavaBinaryClassTest.java new file mode 100644 index 00000000000..7e76f3be6dd --- /dev/null +++ b/compiler/tests/org/jetbrains/jet/cli/jvm/compiler/ReadJavaBinaryClassTest.java @@ -0,0 +1,132 @@ +/* + * Copyright 2010-2012 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. + * 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 org.jetbrains.jet.cli.jvm.compiler; + +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.vfs.CharsetToolkit; +import com.intellij.psi.PsiFileFactory; +import com.intellij.psi.impl.PsiFileFactoryImpl; +import com.intellij.testFramework.LightVirtualFile; +import junit.framework.Test; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.CompileCompilerDependenciesTest; +import org.jetbrains.jet.JetTestCaseBuilder; +import org.jetbrains.jet.JetTestUtils; +import org.jetbrains.jet.di.InjectorForJavaSemanticServices; +import org.jetbrains.jet.lang.cfg.pseudocode.JetControlFlowDataTraceFactory; +import org.jetbrains.jet.lang.descriptors.NamespaceDescriptor; +import org.jetbrains.jet.lang.psi.JetFile; +import org.jetbrains.jet.lang.resolve.BindingContext; +import org.jetbrains.jet.lang.resolve.FqName; +import org.jetbrains.jet.lang.resolve.java.AnalyzerFacadeForJVM; +import org.jetbrains.jet.lang.resolve.java.CompilerSpecialMode; +import org.jetbrains.jet.lang.resolve.java.DescriptorSearchRule; +import org.jetbrains.jet.lang.resolve.java.JavaDescriptorResolver; +import org.jetbrains.jet.plugin.JetLanguage; +import org.junit.Assert; + +import javax.tools.JavaCompiler; +import javax.tools.JavaFileObject; +import javax.tools.StandardJavaFileManager; +import javax.tools.ToolProvider; +import java.io.File; +import java.nio.charset.Charset; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Locale; + +/** + * @author Stepan Koltsov + */ +public class ReadJavaBinaryClassTest extends TestCaseWithTmpdir { + + private final File ktFile; + private final File javaFile; + private final File txtFile; + + public ReadJavaBinaryClassTest(File ktFile) { + this.ktFile = ktFile; + Assert.assertTrue(ktFile.getName().endsWith(".kt")); + this.javaFile = new File(ktFile.getPath().replaceFirst("\\.kt$", ".java")); + this.txtFile = new File(ktFile.getPath().replaceFirst("\\.kt$", ".txt")); + setName(javaFile.getName()); + } + + + @Override + public void runTest() throws Exception { + NamespaceDescriptor nsa = compileKotlin(); + NamespaceDescriptor nsb = compileJava(); + NamespaceComparator.compareNamespaces(nsa, nsb, false, txtFile); + } + + private NamespaceDescriptor compileKotlin() throws Exception { + JetCoreEnvironment jetCoreEnvironment = JetTestUtils.createEnvironmentWithMockJdk(myTestRootDisposable, CompilerSpecialMode.JDK_HEADERS); + + String text = FileUtil.loadFile(ktFile); + + LightVirtualFile virtualFile = new LightVirtualFile(ktFile.getName(), JetLanguage.INSTANCE, text); + virtualFile.setCharset(CharsetToolkit.UTF8_CHARSET); + JetFile psiFile = (JetFile) ((PsiFileFactoryImpl) PsiFileFactory.getInstance(jetCoreEnvironment.getProject())).trySetupPsiForFile(virtualFile, JetLanguage.INSTANCE, true, false); + + BindingContext bindingContext = AnalyzerFacadeForJVM.analyzeOneFileWithJavaIntegrationAndCheckForErrors( + psiFile, JetControlFlowDataTraceFactory.EMPTY, + CompileCompilerDependenciesTest.compilerDependenciesForTests(CompilerSpecialMode.JDK_HEADERS, true)) + .getBindingContext(); + return bindingContext.get(BindingContext.FQNAME_TO_NAMESPACE_DESCRIPTOR, FqName.topLevel("test")); + } + + private NamespaceDescriptor compileJava() throws Exception { + JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler(); + + StandardJavaFileManager fileManager = javaCompiler.getStandardFileManager(null, Locale.ENGLISH, Charset.forName("utf-8")); + try { + Iterable javaFileObjectsFromFiles = fileManager.getJavaFileObjectsFromFiles(Collections.singleton(javaFile)); + List options = Arrays.asList( + "-classpath", "out/production/runtime" + File.pathSeparator + JetTestUtils.getAnnotationsJar().getPath(), + "-d", tmpdir.getPath() + ); + JavaCompiler.CompilationTask task = javaCompiler.getTask(null, fileManager, null, options, null, javaFileObjectsFromFiles); + + Assert.assertTrue(task.call()); + } finally { + fileManager.close(); + } + + JetCoreEnvironment jetCoreEnvironment = JetTestUtils.createEnvironmentWithMockJdk(myTestRootDisposable, CompilerSpecialMode.JDK_HEADERS); + + jetCoreEnvironment.addToClasspath(tmpdir); + jetCoreEnvironment.addToClasspath(new File("out/production/runtime")); + + InjectorForJavaSemanticServices injector = new InjectorForJavaSemanticServices( + CompileCompilerDependenciesTest.compilerDependenciesForTests(CompilerSpecialMode.JDK_HEADERS, true), jetCoreEnvironment.getProject()); + JavaDescriptorResolver javaDescriptorResolver = injector.getJavaDescriptorResolver(); + return javaDescriptorResolver.resolveNamespace(FqName.topLevel("test"), DescriptorSearchRule.ERROR_IF_FOUND_IN_KOTLIN); + } + + public static Test suite() { + return JetTestCaseBuilder.suiteForDirectory(JetTestCaseBuilder.getTestDataPathBase(), "/readJavaBinaryClass", true, new JetTestCaseBuilder.NamedTestFactory() { + @NotNull + @Override + public Test createTest(@NotNull String dataPath, @NotNull String name, @NotNull File file) { + return new ReadJavaBinaryClassTest(file); + } + }); + } + +} diff --git a/compiler/tests/org/jetbrains/jet/cli/jvm/compiler/ReadKotlinBinaryClassTest.java b/compiler/tests/org/jetbrains/jet/cli/jvm/compiler/ReadKotlinBinaryClassTest.java new file mode 100644 index 00000000000..a15d70efffc --- /dev/null +++ b/compiler/tests/org/jetbrains/jet/cli/jvm/compiler/ReadKotlinBinaryClassTest.java @@ -0,0 +1,110 @@ +/* + * Copyright 2010-2012 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. + * 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 org.jetbrains.jet.cli.jvm.compiler; + +import com.intellij.openapi.util.Disposer; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.vfs.CharsetToolkit; +import com.intellij.psi.PsiFileFactory; +import com.intellij.psi.impl.PsiFileFactoryImpl; +import com.intellij.testFramework.LightVirtualFile; +import junit.framework.Assert; +import junit.framework.Test; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.CompileCompilerDependenciesTest; +import org.jetbrains.jet.JetTestCaseBuilder; +import org.jetbrains.jet.JetTestUtils; +import org.jetbrains.jet.codegen.ClassFileFactory; +import org.jetbrains.jet.codegen.GenerationState; +import org.jetbrains.jet.codegen.GenerationUtils; +import org.jetbrains.jet.di.InjectorForJavaSemanticServices; +import org.jetbrains.jet.lang.descriptors.NamespaceDescriptor; +import org.jetbrains.jet.lang.psi.JetFile; +import org.jetbrains.jet.lang.resolve.BindingContext; +import org.jetbrains.jet.lang.resolve.FqName; +import org.jetbrains.jet.lang.resolve.java.CompilerSpecialMode; +import org.jetbrains.jet.lang.resolve.java.DescriptorSearchRule; +import org.jetbrains.jet.lang.resolve.java.JavaDescriptorResolver; +import org.jetbrains.jet.plugin.JetLanguage; + +import java.io.File; + +/** + * Compile Kotlin and then parse model from .class files. + * + * @author Stepan Koltsov + */ +public class ReadKotlinBinaryClassTest extends TestCaseWithTmpdir { + + private JetCoreEnvironment jetCoreEnvironment; + + private final File testFile; + private final File txtFile; + + public ReadKotlinBinaryClassTest(@NotNull File testFile) { + this.testFile = testFile; + this.txtFile = new File(testFile.getPath().replaceFirst("\\.kt$", ".txt")); + setName(testFile.getName()); + } + + @Override + public void runTest() throws Exception { + jetCoreEnvironment = JetTestUtils.createEnvironmentWithMockJdk(myTestRootDisposable, CompilerSpecialMode.JDK_HEADERS); + + String text = FileUtil.loadFile(testFile); + + LightVirtualFile virtualFile = new LightVirtualFile(testFile.getName(), JetLanguage.INSTANCE, text); + virtualFile.setCharset(CharsetToolkit.UTF8_CHARSET); + JetFile psiFile = (JetFile) ((PsiFileFactoryImpl) PsiFileFactory.getInstance(jetCoreEnvironment.getProject())).trySetupPsiForFile(virtualFile, JetLanguage.INSTANCE, true, false); + + GenerationState state = GenerationUtils.compileFileGetGenerationStateForTest(psiFile, CompilerSpecialMode.JDK_HEADERS); + + ClassFileFactory classFileFactory = state.getFactory(); + + CompileEnvironmentUtil.writeToOutputDirectory(classFileFactory, tmpdir.getPath()); + + NamespaceDescriptor namespaceFromSource = state.getBindingContext().get(BindingContext.FILE_TO_NAMESPACE, psiFile); + + Assert.assertEquals("test", namespaceFromSource.getName()); + + Disposer.dispose(myTestRootDisposable); + + + jetCoreEnvironment = JetTestUtils.createEnvironmentWithMockJdk(myTestRootDisposable, CompilerSpecialMode.JDK_HEADERS); + + jetCoreEnvironment.addToClasspath(tmpdir); + jetCoreEnvironment.addToClasspath(new File("out/production/runtime")); + + InjectorForJavaSemanticServices injector = new InjectorForJavaSemanticServices( + CompileCompilerDependenciesTest.compilerDependenciesForTests(CompilerSpecialMode.JDK_HEADERS, true), jetCoreEnvironment.getProject()); + JavaDescriptorResolver javaDescriptorResolver = injector.getJavaDescriptorResolver(); + NamespaceDescriptor namespaceFromClass = javaDescriptorResolver.resolveNamespace(FqName.topLevel("test"), DescriptorSearchRule.ERROR_IF_FOUND_IN_KOTLIN); + + NamespaceComparator.compareNamespaces(namespaceFromSource, namespaceFromClass, false, txtFile); + } + + public static Test suite() { + return JetTestCaseBuilder.suiteForDirectory(JetTestCaseBuilder.getTestDataPathBase(), "/readKotlinBinaryClass", true, new JetTestCaseBuilder.NamedTestFactory() { + @NotNull + @Override + public Test createTest(@NotNull String dataPath, @NotNull String name, @NotNull File file) { + return new ReadKotlinBinaryClassTest(file); + } + }); + } + +} diff --git a/compiler/tests/org/jetbrains/jet/cli/jvm/compiler/TestCaseWithTmpdir.java b/compiler/tests/org/jetbrains/jet/cli/jvm/compiler/TestCaseWithTmpdir.java new file mode 100644 index 00000000000..c3b7635b3a1 --- /dev/null +++ b/compiler/tests/org/jetbrains/jet/cli/jvm/compiler/TestCaseWithTmpdir.java @@ -0,0 +1,37 @@ +/* + * Copyright 2010-2012 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. + * 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 org.jetbrains.jet.cli.jvm.compiler; + +import com.intellij.testFramework.UsefulTestCase; +import org.jetbrains.jet.JetTestUtils; + +import java.io.File; + +/** + * @author Stepan Koltsov + */ +public abstract class TestCaseWithTmpdir extends UsefulTestCase { + + protected File tmpdir; + + @Override + protected void setUp() throws Exception { + super.setUp(); + tmpdir = JetTestUtils.tmpDirForTest(this); + } + +} diff --git a/compiler/tests/org/jetbrains/jet/cli/jvm/compiler/WriteSignatureTest.java b/compiler/tests/org/jetbrains/jet/cli/jvm/compiler/WriteSignatureTest.java new file mode 100644 index 00000000000..9ffdf0eb2c1 --- /dev/null +++ b/compiler/tests/org/jetbrains/jet/cli/jvm/compiler/WriteSignatureTest.java @@ -0,0 +1,316 @@ +/* + * Copyright 2010-2012 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. + * 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 org.jetbrains.jet.cli.jvm.compiler; + +import com.google.common.io.Closeables; +import com.google.common.io.Files; +import com.intellij.openapi.util.Disposer; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.vfs.CharsetToolkit; +import com.intellij.psi.PsiFileFactory; +import com.intellij.psi.impl.PsiFileFactoryImpl; +import com.intellij.testFramework.LightVirtualFile; +import junit.framework.Test; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.JetTestCaseBuilder; +import org.jetbrains.jet.JetTestUtils; +import org.jetbrains.jet.codegen.*; +import org.jetbrains.jet.lang.psi.JetFile; +import org.jetbrains.jet.lang.resolve.java.CompilerSpecialMode; +import org.jetbrains.jet.lang.resolve.java.JvmStdlibNames; +import org.jetbrains.jet.plugin.JetLanguage; +import org.junit.Assert; +import org.objectweb.asm.AnnotationVisitor; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.commons.EmptyVisitor; +import org.objectweb.asm.commons.Method; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.nio.charset.Charset; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Test correctness of written JVM signature + * + * @author Stepan Koltsov + * + * @see CompileJavaAgainstKotlinTest + */ +public class WriteSignatureTest extends TestCaseWithTmpdir { + + private final File ktFile; + private JetCoreEnvironment jetCoreEnvironment; + + public WriteSignatureTest(File ktFile) { + this.ktFile = ktFile; + } + + @Override + public String getName() { + return ktFile.getName(); + } + + @Override + protected void runTest() throws Throwable { + jetCoreEnvironment = JetTestUtils.createEnvironmentWithMockJdk(myTestRootDisposable); + + + String text = FileUtil.loadFile(ktFile); + + LightVirtualFile virtualFile = new LightVirtualFile(ktFile.getName(), JetLanguage.INSTANCE, text); + virtualFile.setCharset(CharsetToolkit.UTF8_CHARSET); + JetFile psiFile = (JetFile) ((PsiFileFactoryImpl) PsiFileFactory.getInstance(jetCoreEnvironment.getProject())).trySetupPsiForFile(virtualFile, JetLanguage.INSTANCE, true, false); + + ClassFileFactory classFileFactory = GenerationUtils.compileFileGetClassFileFactoryForTest(psiFile, CompilerSpecialMode.REGULAR); + + CompileEnvironmentUtil.writeToOutputDirectory(classFileFactory, tmpdir.getPath()); + + Disposer.dispose(myTestRootDisposable); + + final Expectation expectation = parseExpectations(); + + ActualSignature actualSignature = readSignature(expectation.className, expectation.methodName); + + String template = + "jvm signature: %s\n" + + "generic signature: %s\n" + + "kotlin signature: %s\n" + + ""; + + String expected = String.format(template, expectation.jvmSignature, expectation.genericSignature, expectation.kotlinSignature); + String actual = String.format(template, actualSignature.jvmSignature, actualSignature.genericSignature, actualSignature.kotlinSignature); + + Assert.assertEquals(expected, actual); + } + + private static class ActualSignature { + private final String jvmSignature; + private final String genericSignature; + private final String kotlinSignature; + + private ActualSignature(@NotNull String jvmSignature, @Nullable String genericSignature, @Nullable String kotlinSignature) { + this.jvmSignature = jvmSignature; + this.genericSignature = genericSignature; + this.kotlinSignature = kotlinSignature; + } + } + + @NotNull + private ActualSignature readSignature(String className, final String methodName) throws Exception { + // ugly unreadable code begin + FileInputStream classInputStream = new FileInputStream(tmpdir + "/" + className.replace('.', '/') + ".class"); + try { + class Visitor extends EmptyVisitor { + ActualSignature readSignature; + + @Override + public MethodVisitor visitMethod(int access, String name, final String desc, final String signature, String[] exceptions) { + if (name.equals(methodName)) { + + final int parameterCount = new Method(name, desc).getArgumentTypes().length; + + return new EmptyVisitor() { + String typeParameters = ""; + String returnType; + String[] parameterTypes = new String[parameterCount]; + + @Override + public AnnotationVisitor visitAnnotation(String desc, boolean visible) { + if (desc.equals(JvmStdlibNames.JET_METHOD.getDescriptor())) { + return new EmptyVisitor() { + @Override + public void visit(String name, Object value) { + if (name.equals(JvmStdlibNames.JET_METHOD_TYPE_PARAMETERS_FIELD)) { + typeParameters = (String) value; + } + else if (name.equals(JvmStdlibNames.JET_METHOD_RETURN_TYPE_FIELD)) { + returnType = (String) value; + } + } + + @Override + public AnnotationVisitor visitAnnotation(String desc, boolean visible) { + return new EmptyVisitor(); + } + + @Override + public AnnotationVisitor visitArray(String name) { + return new EmptyVisitor(); + } + }; + } + else { + return new EmptyVisitor(); + } + } + + @Override + public AnnotationVisitor visitParameterAnnotation(final int parameter, String desc, boolean visible) { + if (desc.equals(JvmStdlibNames.JET_VALUE_PARAMETER.getDescriptor())) { + return new EmptyVisitor() { + @Override + public void visit(String name, Object value) { + if (name.equals(JvmStdlibNames.JET_VALUE_PARAMETER_TYPE_FIELD)) { + parameterTypes[parameter] = (String) value; + } + } + + @Override + public AnnotationVisitor visitAnnotation(String desc, boolean visible) { + return new EmptyVisitor(); + } + + @Override + public AnnotationVisitor visitArray(String name) { + return new EmptyVisitor(); + } + }; + } + else { + return new EmptyVisitor(); + } + } + + @Override + public AnnotationVisitor visitAnnotationDefault() { + return new EmptyVisitor(); + } + + @Nullable + private String makeKotlinSignature() { + boolean allNulls = true; + + StringBuilder sb = new StringBuilder(); + sb.append(typeParameters); + if (typeParameters != null && typeParameters.length() > 0) { + allNulls = false; + } + sb.append("("); + for (String parameterType : parameterTypes) { + sb.append(parameterType); + if (parameterType != null) { + allNulls = false; + } + } + sb.append(")"); + sb.append(returnType); + if (returnType != null) { + allNulls = false; + } + if (allNulls) { + return null; + } + else { + return sb.toString(); + } + } + + @Override + public void visitEnd() { + Assert.assertNull(readSignature); + readSignature = new ActualSignature(desc, signature, makeKotlinSignature()); + } + }; + } + return super.visitMethod(access, name, desc, signature, exceptions); + } + } + + Visitor visitor = new Visitor(); + + new ClassReader(classInputStream).accept(visitor, + ClassReader.SKIP_CODE|ClassReader.SKIP_DEBUG|ClassReader.SKIP_FRAMES); + + Assert.assertNotNull("method not found: " + className + "::" + methodName, visitor.readSignature); + + return visitor.readSignature; + } finally { + Closeables.closeQuietly(classInputStream); + } + // ugly unreadable code end + } + + private static class Expectation { + private final String className; + private final String methodName; + private final String jvmSignature; + private final String genericSignature; + private final String kotlinSignature; + + private Expectation(@NotNull String className, @NotNull String methodName, + @NotNull String jvmSignature, @Nullable String genericSignature, @Nullable String kotlinSignature) { + this.className = className; + this.methodName = methodName; + this.jvmSignature = jvmSignature; + this.genericSignature = genericSignature; + this.kotlinSignature = kotlinSignature; + } + } + + @NotNull + private static final Pattern methodPattern = Pattern.compile("^// method: *(.*)::(.*?) *(//.*)?"); + private static final Pattern jvmSignaturePattern = Pattern.compile("^// jvm signature: *(.+?) *(//.*)?"); + private static final Pattern genericSignaturePattern = Pattern.compile("^// generic signature: *(.+?) *(//.*)?"); + private static final Pattern kotlinSignaturePattern = Pattern.compile("^// kotlin signature: *(.+?) *(//.*)?"); + private Expectation parseExpectations() throws IOException { + List lines = Files.readLines(ktFile, Charset.forName("utf-8")); + for (int i = 0; i < lines.size() - 3; ++i) { + Matcher methodMatcher = methodPattern.matcher(lines.get(i)); + if (methodMatcher.matches()) { + Matcher jvmSignatureMatcher = jvmSignaturePattern.matcher(lines.get(i + 1)); + Matcher genericSignatureMatcher = genericSignaturePattern.matcher(lines.get(i + 2)); + Matcher kotlinSignatureMatcher = kotlinSignaturePattern.matcher(lines.get(i + 3)); + if (!jvmSignatureMatcher.matches() || !genericSignatureMatcher.matches() || !kotlinSignatureMatcher.matches()) { + throw new AssertionError("'method:' must be followed ... bla bla ... use the source luke"); + } + + String className = methodMatcher.group(1); + String methodName = methodMatcher.group(2); + + String jvmSignature = jvmSignatureMatcher.group(1); + String genericSignature = genericSignatureMatcher.group(1); + String kotlinSignature = kotlinSignatureMatcher.group(1); + if (genericSignature.equals("null")) { + genericSignature = null; + } + if (kotlinSignature.equals("null")) { + kotlinSignature = null; + } + return new Expectation(className, methodName, jvmSignature, genericSignature, kotlinSignature); + } + } + throw new AssertionError("test instructions not found in " + ktFile); + } + + public static Test suite() { + return JetTestCaseBuilder.suiteForDirectory(JetTestCaseBuilder.getTestDataPathBase(), "/writeSignature", true, new JetTestCaseBuilder.NamedTestFactory() { + @NotNull + @Override + public Test createTest(@NotNull String dataPath, @NotNull String name, @NotNull File file) { + return new WriteSignatureTest(file); + } + }); + + } + +} diff --git a/compiler/tests/org/jetbrains/jet/cli/jvm/compiler/longTest/LibFromMaven.java b/compiler/tests/org/jetbrains/jet/cli/jvm/compiler/longTest/LibFromMaven.java new file mode 100644 index 00000000000..d39b3ca3f80 --- /dev/null +++ b/compiler/tests/org/jetbrains/jet/cli/jvm/compiler/longTest/LibFromMaven.java @@ -0,0 +1,57 @@ +/* + * Copyright 2010-2012 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. + * 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 org.jetbrains.jet.cli.jvm.compiler.longTest; + +import org.jetbrains.annotations.NotNull; + +/** + * @author Stepan Koltsov + */ +public class LibFromMaven { + @NotNull + private final String org; + @NotNull + private final String module; + @NotNull + private final String rev; + + public LibFromMaven(@NotNull String org, @NotNull String module, @NotNull String rev) { + this.org = org; + this.module = module; + this.rev = rev; + } + + @NotNull + public String getOrg() { + return org; + } + + @NotNull + public String getModule() { + return module; + } + + @NotNull + public String getRev() { + return rev; + } + + @Override + public String toString() { + return org + "/" + module + "/" + rev; + } +} diff --git a/compiler/tests/org/jetbrains/jet/cli/jvm/compiler/longTest/ResolveDescriptorsFromExternalLibraries.java b/compiler/tests/org/jetbrains/jet/cli/jvm/compiler/longTest/ResolveDescriptorsFromExternalLibraries.java new file mode 100644 index 00000000000..2351d449223 --- /dev/null +++ b/compiler/tests/org/jetbrains/jet/cli/jvm/compiler/longTest/ResolveDescriptorsFromExternalLibraries.java @@ -0,0 +1,199 @@ +/* + * Copyright 2010-2012 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. + * 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 org.jetbrains.jet.cli.jvm.compiler.longTest; + +import com.google.common.io.ByteStreams; +import com.intellij.openapi.Disposable; +import com.intellij.openapi.util.Disposer; +import org.apache.commons.httpclient.HttpClient; +import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager; +import org.apache.commons.httpclient.methods.GetMethod; +import org.apache.commons.httpclient.params.HttpMethodParams; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.CompileCompilerDependenciesTest; +import org.jetbrains.jet.JetTestUtils; +import org.jetbrains.jet.TimeUtils; +import org.jetbrains.jet.cli.jvm.compiler.JetCoreEnvironment; +import org.jetbrains.jet.di.InjectorForJavaSemanticServices; +import org.jetbrains.jet.lang.descriptors.ClassDescriptor; +import org.jetbrains.jet.lang.resolve.FqName; +import org.jetbrains.jet.lang.resolve.java.CompilerDependencies; +import org.jetbrains.jet.lang.resolve.java.CompilerSpecialMode; +import org.jetbrains.jet.lang.resolve.java.DescriptorSearchRule; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +/** + * @author Stepan Koltsov + */ +public class ResolveDescriptorsFromExternalLibraries { + + @NotNull + private final CompilerDependencies compilerDependencies; + + + public static void main(String[] args) throws Exception { + new ResolveDescriptorsFromExternalLibraries().run(); + System.out.println("$"); + } + + + public ResolveDescriptorsFromExternalLibraries() { + System.out.println("Getting compiler dependencies"); + compilerDependencies = CompileCompilerDependenciesTest.compilerDependenciesForTests(CompilerSpecialMode.REGULAR, true); + } + + private void run() throws Exception { + testLibrary("com.google.guava", "guava", "12.0-rc2"); + testLibrary("org.springframework", "spring-core", "3.1.1.RELEASE"); + } + + private void testLibrary(@NotNull String org, @NotNull String module, @NotNull String rev) throws Exception { + LibFromMaven lib = new LibFromMaven(org, module, rev); + File jar = getLibrary(lib); + System.out.println("Testing library " + lib + "..."); + System.out.println("Using file " + jar); + + long start = System.currentTimeMillis(); + + Disposable junk = new Disposable() { + @Override + public void dispose() { } + }; + + JetCoreEnvironment jetCoreEnvironment = JetTestUtils.createEnvironmentWithMockJdk(junk); + jetCoreEnvironment.addToClasspath(jar); + + + InjectorForJavaSemanticServices injector = new InjectorForJavaSemanticServices(compilerDependencies, jetCoreEnvironment.getProject()); + + FileInputStream is = new FileInputStream(jar); + try { + ZipInputStream zip = new ZipInputStream(is); + for (;;) { + ZipEntry entry = zip.getNextEntry(); + if (entry == null) { + break; + } + String entryName = entry.getName(); + if (!entryName.endsWith(".class")) { + continue; + } + if (entryName.matches("(.*/|)package-info\\.class")) { + continue; + } + if (entryName.contains("$")) { + continue; + } + String className = entryName.substring(0, entryName.length() - ".class".length()).replace("/", "."); + ClassDescriptor clazz = injector.getJavaDescriptorResolver().resolveClass(new FqName(className), DescriptorSearchRule.ERROR_IF_FOUND_IN_KOTLIN); + if (clazz == null) { + throw new IllegalStateException("class not found by name " + className + " in " + lib); + } + clazz.getDefaultType().getMemberScope().getAllDescriptors(); + } + + } finally { + try { + is.close(); + } + catch (Throwable e) {} + + Disposer.dispose(junk); + } + + System.out.println("Testing library " + lib + " done in " + TimeUtils.millisecondsToSecondsString(System.currentTimeMillis() - start) + "s"); + } + + @NotNull + private File getLibrary(@NotNull LibFromMaven lib) throws Exception { + File userHome = new File(System.getProperty("user.home")); + String fileName = lib.getModule() + "-" + lib.getRev() + ".jar"; + + File dir = new File(userHome, ".kotlin-project/resolve-libraries/" + lib.getOrg() + "/" + lib.getModule()); + + File file = new File(dir, fileName); + if (file.exists()) { + return file; + } + + JetTestUtils.mkdirs(dir); + + File tmp = new File(dir, fileName + "~"); + + String uri = url(lib); + GetMethod method = new GetMethod(uri); + + FileOutputStream os = null; + + System.out.println("Downloading library " + lib + " to " + file); + + MultiThreadedHttpConnectionManager connectionManager = null; + try { + connectionManager = new MultiThreadedHttpConnectionManager(); + HttpClient httpClient = new HttpClient(connectionManager); + os = new FileOutputStream(tmp); + String userAgent = ResolveDescriptorsFromExternalLibraries.class.getName() + "/commons-httpclient"; + method.getParams().setParameter(HttpMethodParams.USER_AGENT, userAgent); + int code = httpClient.executeMethod(method); + if (code != 200) { + throw new RuntimeException("failed to execute GET " + uri + ", code is " + code); + } + InputStream responseBodyAsStream = method.getResponseBodyAsStream(); + if (responseBodyAsStream == null) { + throw new RuntimeException("method is executed fine, but response is null"); + } + ByteStreams.copy(responseBodyAsStream, os); + os.close(); + if (!tmp.renameTo(file)) { + throw new RuntimeException("failed to rename file: " + tmp + " to " + file); + } + return file; + } + finally { + if (method != null) { + try { + method.releaseConnection(); + } + catch (Throwable e) {} + } + if (connectionManager != null) { + try { + connectionManager.shutdown(); + } + catch (Throwable e) {} + } + if (os != null) { + try { + os.close(); + } + catch (Throwable e) {} + } + tmp.delete(); + } + } + + private static String url(LibFromMaven lib) { + return "http://repo1.maven.org/maven2/" + lib.getOrg().replace(".", "/") + "/" + lib.getModule() + "/" + lib.getRev() + + "/" + lib.getModule() + "-" + lib.getRev() + ".jar"; + } +} diff --git a/idea/src/org/jetbrains/jet/plugin/compiler/K2JSCompiler.java b/idea/src/org/jetbrains/jet/plugin/compiler/K2JSCompiler.java new file mode 100644 index 00000000000..ca8f5e0bb58 --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/compiler/K2JSCompiler.java @@ -0,0 +1,64 @@ +/* + * Copyright 2010-2012 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. + * 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 org.jetbrains.jet.plugin.compiler; + +import com.intellij.openapi.compiler.CompileContext; +import com.intellij.openapi.compiler.CompileScope; +import com.intellij.openapi.compiler.CompilerMessageCategory; +import com.intellij.openapi.compiler.TranslatingCompiler; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.Chunk; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.plugin.JetFileType; +import org.jetbrains.jet.plugin.project.JsModuleDetector; + +/** + * @author Pavel Talanov + */ +public final class K2JSCompiler implements TranslatingCompiler { + + @Override + public boolean isCompilableFile(VirtualFile file, CompileContext context) { + if (!(file.getFileType() instanceof JetFileType)) { + return false; + } + Project project = context.getProject(); + if (project == null) { + return false; + } + return JsModuleDetector.isJsProject(project); + } + + @Override + public void compile(final CompileContext context, Chunk moduleChunk, final VirtualFile[] files, OutputSink sink) { + context.addMessage(CompilerMessageCategory.INFORMATION, "Some useful code will be there", null, -1, -1); + //TODO: + } + + @NotNull + @Override + public String getDescription() { + return "Kotlin to JavaScript compiler"; + } + + @Override + public boolean validateConfiguration(CompileScope scope) { + return true; + } +}