
org.jetbrains.jet.cli.jvm.compiler.KotlinToJVMBytecodeCompiler Maven / Gradle / Ivy
/*
* Copyright 2010-2014 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.Predicates;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.util.Disposer;
import com.intellij.psi.PsiFile;
import kotlin.Function0;
import kotlin.modules.AllModules;
import kotlin.modules.Module;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.analyzer.AnalyzeExhaust;
import org.jetbrains.jet.cli.common.CLIConfigurationKeys;
import org.jetbrains.jet.cli.common.CompilerPlugin;
import org.jetbrains.jet.cli.common.CompilerPluginContext;
import org.jetbrains.jet.cli.common.messages.AnalyzerWithCompilerReport;
import org.jetbrains.jet.cli.common.messages.MessageCollector;
import org.jetbrains.jet.cli.common.output.OutputDirector;
import org.jetbrains.jet.cli.common.output.SingleDirectoryDirector;
import org.jetbrains.jet.cli.jvm.JVMConfigurationKeys;
import org.jetbrains.jet.codegen.*;
import org.jetbrains.jet.codegen.inline.InlineCodegenUtil;
import org.jetbrains.jet.codegen.state.GenerationState;
import org.jetbrains.jet.codegen.state.Progress;
import org.jetbrains.jet.config.CommonConfigurationKeys;
import org.jetbrains.jet.config.CompilerConfiguration;
import org.jetbrains.jet.lang.descriptors.ModuleDescriptorImpl;
import org.jetbrains.jet.lang.parsing.JetScriptDefinition;
import org.jetbrains.jet.lang.parsing.JetScriptDefinitionProvider;
import org.jetbrains.jet.lang.psi.JetFile;
import org.jetbrains.jet.lang.resolve.AnalyzerScriptParameter;
import org.jetbrains.jet.lang.resolve.BindingTrace;
import org.jetbrains.jet.lang.resolve.ScriptNameUtil;
import org.jetbrains.jet.lang.resolve.java.AnalyzerFacadeForJVM;
import org.jetbrains.jet.lang.resolve.java.PackageClassUtils;
import org.jetbrains.jet.lang.resolve.name.FqName;
import org.jetbrains.jet.plugin.MainFunctionDetector;
import org.jetbrains.jet.utils.KotlinPaths;
import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
public class KotlinToJVMBytecodeCompiler {
private static final boolean COMPILE_CHUNK_AS_ONE_MODULE = true;
private KotlinToJVMBytecodeCompiler() {
}
@Nullable
public static ClassFileFactory compileModule(CompilerConfiguration configuration, Module module, File directory) {
List sourceFiles = module.getSourceFiles();
if (sourceFiles.isEmpty()) {
throw new CompileEnvironmentException("No source files where defined in module " + module.getModuleName());
}
CompilerConfiguration compilerConfiguration = configuration.copy();
for (String sourceFile : sourceFiles) {
File source = new File(sourceFile);
if (!source.isAbsolute()) {
source = new File(directory, sourceFile);
}
if (!source.exists()) {
throw new CompileEnvironmentException("'" + source + "' does not exist in module " + module.getModuleName());
}
compilerConfiguration.add(CommonConfigurationKeys.SOURCE_ROOTS_KEY, source.getPath());
}
for (String classpathRoot : module.getClasspathRoots()) {
compilerConfiguration.add(JVMConfigurationKeys.CLASSPATH_KEY, new File(classpathRoot));
}
for (String annotationsRoot : module.getAnnotationsRoots()) {
compilerConfiguration.add(JVMConfigurationKeys.ANNOTATIONS_PATH_KEY, new File(annotationsRoot));
}
Disposable parentDisposable = Disposer.newDisposable();
JetCoreEnvironment moduleEnvironment = null;
try {
moduleEnvironment = JetCoreEnvironment.createForProduction(parentDisposable, compilerConfiguration);
GenerationState generationState = analyzeAndGenerate(moduleEnvironment);
if (generationState == null) {
return null;
}
return generationState.getFactory();
} finally {
if (moduleEnvironment != null) {
Disposer.dispose(parentDisposable);
}
}
}
private static void writeOutput(
CompilerConfiguration configuration,
ClassFileFactory outputFiles,
OutputDirector outputDir,
File jarPath,
boolean jarRuntime,
FqName mainClass
) {
MessageCollector messageCollector = configuration.get(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, MessageCollector.NONE);
CompileEnvironmentUtil.writeOutputToDirOrJar(jarPath, outputDir, jarRuntime, mainClass, outputFiles, messageCollector);
}
public static boolean compileModules(
CompilerConfiguration configuration,
@NotNull final ModuleChunk chunk,
@NotNull File directory,
@Nullable File jarPath,
boolean jarRuntime
) {
List modules = chunk.getModules();
if (COMPILE_CHUNK_AS_ONE_MODULE && modules.size() > 1) {
modules = Collections.singletonList(new ChunkAsOneModule(chunk));
}
for (Module module : modules) {
ClassFileFactory outputFiles = compileModule(configuration, module, directory);
if (outputFiles == null) {
return false;
}
OutputDirector outputDir = new OutputDirector() {
@NotNull
@Override
public File getOutputDirectory(@NotNull Collection extends File> sourceFiles) {
for (File sourceFile : sourceFiles) {
// Note that here we track original modules:
Module module = chunk.findModuleBySourceFile(sourceFile);
if (module != null) {
return new File(module.getOutputDirectory());
}
}
throw new IllegalStateException("No module found for source files: " + sourceFiles);
}
};
writeOutput(configuration, outputFiles, outputDir, jarPath, jarRuntime, null);
}
return true;
}
@Nullable
private static FqName findMainClass(@NotNull GenerationState generationState, @NotNull List files) {
MainFunctionDetector mainFunctionDetector = new MainFunctionDetector(generationState.getBindingContext());
FqName mainClass = null;
for (JetFile file : files) {
if (mainFunctionDetector.hasMain(file.getDeclarations())) {
if (mainClass != null) {
// more than one main
return null;
}
FqName fqName = file.getPackageFqName();
mainClass = PackageClassUtils.getPackageClassFqName(fqName);
}
}
return mainClass;
}
public static boolean compileBunchOfSources(
JetCoreEnvironment environment,
@Nullable File jar,
@Nullable File outputDir,
boolean includeRuntime
) {
GenerationState generationState = analyzeAndGenerate(environment);
if (generationState == null) {
return false;
}
FqName mainClass = findMainClass(generationState, environment.getSourceFiles());
try {
OutputDirector outputDirector = outputDir != null ? new SingleDirectoryDirector(outputDir) : null;
writeOutput(environment.getConfiguration(), generationState.getFactory(), outputDirector, jar, includeRuntime, mainClass);
return true;
}
finally {
generationState.destroy();
}
}
public static void compileAndExecuteScript(
@NotNull KotlinPaths paths,
@NotNull JetCoreEnvironment environment,
@NotNull List scriptArgs
) {
Class> scriptClass = compileScript(paths, environment);
if (scriptClass == null) return;
try {
scriptClass.getConstructor(String[].class).newInstance(new Object[]{scriptArgs.toArray(new String[scriptArgs.size()])});
}
catch (RuntimeException e) {
throw e;
}
catch (Exception e) {
throw new RuntimeException("Failed to evaluate script: " + e, e);
}
}
@Nullable
public static Class> compileScript(@NotNull KotlinPaths paths, @NotNull JetCoreEnvironment environment) {
List scriptParameters = environment.getConfiguration().getList(JVMConfigurationKeys.SCRIPT_PARAMETERS);
if (!scriptParameters.isEmpty()) {
JetScriptDefinitionProvider.getInstance(environment.getProject()).addScriptDefinition(
new JetScriptDefinition(".kts", scriptParameters)
);
}
GenerationState state = analyzeAndGenerate(environment);
if (state == null) {
return null;
}
GeneratedClassLoader classLoader;
try {
classLoader = new GeneratedClassLoader(state.getFactory(),
new URLClassLoader(new URL[] {
// TODO: add all classpath
paths.getRuntimePath().toURI().toURL()
}, AllModules.class.getClassLoader())
);
FqName nameForScript = ScriptNameUtil.classNameForScript(environment.getSourceFiles().get(0).getScript());
return classLoader.loadClass(nameForScript.asString());
}
catch (Exception e) {
throw new RuntimeException("Failed to evaluate script: " + e, e);
}
}
@Nullable
public static GenerationState analyzeAndGenerate(@NotNull JetCoreEnvironment environment) {
AnalyzeExhaust exhaust = analyze(environment);
if (exhaust == null) {
return null;
}
exhaust.throwIfError();
return generate(environment, exhaust);
}
@Nullable
private static AnalyzeExhaust analyze(@NotNull final JetCoreEnvironment environment) {
AnalyzerWithCompilerReport analyzerWithCompilerReport = new AnalyzerWithCompilerReport(
environment.getConfiguration().get(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY));
analyzerWithCompilerReport.analyzeAndReport(
new Function0() {
@NotNull
@Override
public AnalyzeExhaust invoke() {
CliLightClassGenerationSupport support = CliLightClassGenerationSupport.getInstanceForCli(environment.getProject());
BindingTrace sharedTrace = support.getTrace();
ModuleDescriptorImpl sharedModule = support.getModule();
return AnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration(
environment.getProject(),
environment.getSourceFiles(),
sharedTrace,
Predicates.alwaysTrue(),
sharedModule,
new CliSourcesMemberFilter(environment));
}
}, environment.getSourceFiles()
);
AnalyzeExhaust exhaust = analyzerWithCompilerReport.getAnalyzeExhaust();
assert exhaust != null : "AnalyzeExhaust should be non-null, compiling: " + environment.getSourceFiles();
CompilerPluginContext context = new CompilerPluginContext(environment.getProject(), exhaust.getBindingContext(),
environment.getSourceFiles());
for (CompilerPlugin plugin : environment.getConfiguration().getList(CLIConfigurationKeys.COMPILER_PLUGINS)) {
plugin.processFiles(context);
}
return analyzerWithCompilerReport.hasErrors() ? null : exhaust;
}
@NotNull
private static GenerationState generate(@NotNull JetCoreEnvironment environment, @NotNull AnalyzeExhaust exhaust) {
CompilerConfiguration configuration = environment.getConfiguration();
GenerationState generationState = new GenerationState(
environment.getProject(), ClassBuilderFactories.BINARIES, Progress.DEAF,
exhaust.getModuleDescriptor(), exhaust.getBindingContext(), environment.getSourceFiles(),
configuration.get(JVMConfigurationKeys.GENERATE_NOT_NULL_ASSERTIONS, false),
configuration.get(JVMConfigurationKeys.GENERATE_NOT_NULL_PARAMETER_ASSERTIONS, false),
GenerationState.GenerateClassFilter.GENERATE_ALL,
configuration.get(JVMConfigurationKeys.ENABLE_INLINE, InlineCodegenUtil.DEFAULT_INLINE_FLAG)
);
KotlinCodegenFacade.compileCorrectFiles(generationState, CompilationErrorHandler.THROW_EXCEPTION);
return generationState;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy