Skip to content

Commit

Permalink
fix JS analyzer, split binding context to lib and sources
Browse files Browse the repository at this point in the history
  • Loading branch information
develar committed Nov 23, 2012
1 parent 9ec30f3 commit 3ba9ad5
Show file tree
Hide file tree
Showing 14 changed files with 226 additions and 205 deletions.
Expand Up @@ -207,24 +207,20 @@ public void visitErrorElement(PsiErrorElement element) {
return new SyntaxErrorReport(visitor.hasErrors, visitor.onlyErrorAtEof);
}

@Nullable
public AnalyzeExhaust getAnalyzeExhaust() {
return analyzeExhaust;
}

public boolean hasErrors() {
return hasErrors;
}

public void analyzeAndReport(@NotNull Function0<AnalyzeExhaust> analyzer, @NotNull Collection<JetFile> files) {
@Nullable
public AnalyzeExhaust analyzeAndReport(@NotNull Function0<AnalyzeExhaust> analyzer, @NotNull Collection<JetFile> files) {
reportSyntaxErrors(files);
analyzeExhaust = analyzer.invoke();
reportDiagnostics(analyzeExhaust.getBindingContext(), messageCollectorWrapper);
reportIncompleteHierarchies();
reportAlternativeSignatureErrors();
return hasErrors() ? null : analyzeExhaust;
}


private static class MyDiagnostic<E extends PsiElement> extends SimpleDiagnostic<E> {
private String message;

Expand Down
81 changes: 36 additions & 45 deletions compiler/cli/src/org/jetbrains/jet/cli/js/K2JSCompiler.java
Expand Up @@ -18,27 +18,23 @@

import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.base.Predicates;
import com.google.common.collect.Iterables;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiFile;
import jet.Function0;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.analyzer.AnalyzeExhaust;
import org.jetbrains.jet.cli.common.CLICompiler;
import org.jetbrains.jet.cli.common.ExitCode;
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.cli.common.messages.PrintingMessageCollector;
import org.jetbrains.jet.cli.common.messages.*;
import org.jetbrains.jet.cli.jvm.compiler.JetCoreEnvironment;
import org.jetbrains.jet.config.CommonConfigurationKeys;
import org.jetbrains.jet.config.CompilerConfiguration;
import org.jetbrains.jet.lang.psi.JetFile;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.k2js.analyze.AnalyzerFacadeForJS;
import org.jetbrains.k2js.config.*;
import org.jetbrains.k2js.facade.K2JSTranslator;
Expand All @@ -65,14 +61,18 @@ protected K2JSCompilerArguments createArguments() {
return new K2JSCompilerArguments();
}


@NotNull
@Override
protected ExitCode doExecute(K2JSCompilerArguments arguments, PrintingMessageCollector messageCollector, Disposable rootDisposable) {
if (arguments.sourceFiles == null) {
messageCollector.report(CompilerMessageSeverity.ERROR, "Specify sources location via -sourceFiles", NO_LOCATION);
return ExitCode.INTERNAL_ERROR;
}
String outputFile = arguments.outputFile;
if (outputFile == null) {
messageCollector.report(CompilerMessageSeverity.ERROR, "Specify output file via -output", CompilerMessageLocation.NO_LOCATION);
return ExitCode.INTERNAL_ERROR;
}

CompilerConfiguration configuration = new CompilerConfiguration();
configuration.addAll(CommonConfigurationKeys.SOURCE_ROOTS_KEY, Arrays.asList(arguments.sourceFiles));
Expand All @@ -81,26 +81,46 @@ protected ExitCode doExecute(K2JSCompilerArguments arguments, PrintingMessageCol
Project project = environmentForJS.getProject();

ClassPathLibrarySourcesLoader sourceLoader = new ClassPathLibrarySourcesLoader(project);
List<JetFile> sourceFiles = sourceLoader.findSourceFiles();
environmentForJS.getSourceFiles().addAll(sourceFiles);
environmentForJS.getSourceFiles().addAll(sourceLoader.findSourceFiles());

if (arguments.isVerbose()) {
reportCompiledSourcesList(messageCollector, environmentForJS);
}

Config config = getConfig(arguments, project);
if (analyzeAndReportErrors(messageCollector, environmentForJS.getSourceFiles(), config)) {
final Config config = getConfig(arguments, project);
final List<JetFile> sources = environmentForJS.getSourceFiles();
AnalyzeExhaust libraryExhaust = analyze(messageCollector, config, config.getLibFiles(), null, false);
if (libraryExhaust == null) {
return ExitCode.COMPILATION_ERROR;
}
libraryExhaust.throwIfError();

String outputFile = arguments.outputFile;
if (outputFile == null) {
messageCollector.report(CompilerMessageSeverity.ERROR, "Specify output file via -output", CompilerMessageLocation.NO_LOCATION);
return ExitCode.INTERNAL_ERROR;
AnalyzeExhaust exhaust = analyze(messageCollector, config, sources, libraryExhaust.getBindingContext(), true);
if (exhaust == null) {
return ExitCode.COMPILATION_ERROR;
}
exhaust.throwIfError();

MainCallParameters mainCallParameters = arguments.createMainCallParameters();
return translateAndGenerateOutputFile(mainCallParameters, messageCollector, environmentForJS, config, outputFile);
try {
K2JSTranslator.translateWithMainCallParametersAndSaveToFile(mainCallParameters, environmentForJS.getSourceFiles(), outputFile,
config, exhaust);
}
catch (Throwable e) {
messageCollector.report(CompilerMessageSeverity.EXCEPTION, MessageRenderer.PLAIN.renderException(e),
CompilerMessageLocation.NO_LOCATION);
return ExitCode.INTERNAL_ERROR;
}
return ExitCode.OK;
}

private static AnalyzeExhaust analyze(PrintingMessageCollector messageCollector, final Config config, final List<JetFile> sources, final BindingContext parentBindingContext, final boolean analyzeCompletely) {
return new AnalyzerWithCompilerReport(messageCollector).analyzeAndReport(new Function0<AnalyzeExhaust>() {
@Override
public AnalyzeExhaust invoke() {
return AnalyzerFacadeForJS.analyzeFiles(sources, analyzeCompletely, config, parentBindingContext, false);
}
}, sources);
}

private static void reportCompiledSourcesList(@NotNull PrintingMessageCollector messageCollector,
Expand All @@ -121,35 +141,6 @@ public String apply(@Nullable JetFile file) {
CompilerMessageLocation.NO_LOCATION);
}

@NotNull
private static ExitCode translateAndGenerateOutputFile(@NotNull MainCallParameters mainCall,
@NotNull PrintingMessageCollector messageCollector,
@NotNull JetCoreEnvironment environmentForJS, @NotNull Config config, @NotNull String outputFile) {
try {
K2JSTranslator.translateWithMainCallParametersAndSaveToFile(mainCall, environmentForJS.getSourceFiles(), outputFile, config);
}
catch (Exception e) {
messageCollector.report(CompilerMessageSeverity.ERROR, "Exception while translating:\n" + e.getMessage(),
CompilerMessageLocation.NO_LOCATION);
// TODO we should report the exception nicely to the collector so it can report
// for example inside a mvn plugin we need to see the stack trace
return ExitCode.INTERNAL_ERROR;
}
return ExitCode.OK;
}

private static boolean analyzeAndReportErrors(@NotNull PrintingMessageCollector messageCollector,
@NotNull final List<JetFile> sources, @NotNull final Config config) {
AnalyzerWithCompilerReport analyzerWithCompilerReport = new AnalyzerWithCompilerReport(messageCollector);
analyzerWithCompilerReport.analyzeAndReport(new Function0<AnalyzeExhaust>() {
@Override
public AnalyzeExhaust invoke() {
return AnalyzerFacadeForJS.analyzeFiles(sources, Predicates.<PsiFile>alwaysTrue(), config);
}
}, sources);
return analyzerWithCompilerReport.hasErrors();
}

@NotNull
private static Config getConfig(@NotNull K2JSCompilerArguments arguments, @NotNull Project project) {
EcmaVersion ecmaVersion = EcmaVersion.fromString(arguments.target);
Expand Down
Expand Up @@ -311,7 +311,7 @@ private static AnalyzeExhaust analyze(
environment.getConfiguration().get(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY));
final Predicate<PsiFile> filesToAnalyzeCompletely =
stubs ? Predicates.<PsiFile>alwaysFalse() : Predicates.<PsiFile>alwaysTrue();
analyzerWithCompilerReport.analyzeAndReport(
return analyzerWithCompilerReport.analyzeAndReport(
new Function0<AnalyzeExhaust>() {
@NotNull
@Override
Expand All @@ -325,8 +325,6 @@ public AnalyzeExhaust invoke() {
}
}, environment.getSourceFiles()
);

return analyzerWithCompilerReport.hasErrors() ? null : analyzerWithCompilerReport.getAnalyzeExhaust();
}

@NotNull
Expand Down
Expand Up @@ -234,11 +234,6 @@ public static void addSuperTypes(JetType type, Set<JetType> set) {
}
}

public static boolean isTopLevelNamespace(@NotNull NamespaceDescriptor namespaceDescriptor) {
return namespaceDescriptor.getContainingDeclaration() instanceof NamespaceDescriptor
&& namespaceDescriptor.getContainingDeclaration().getContainingDeclaration() instanceof ModuleDescriptor;
}

public static boolean isRootNamespace(@NotNull NamespaceDescriptor namespaceDescriptor) {
return namespaceDescriptor.getContainingDeclaration() instanceof ModuleDescriptor;
}
Expand Down
10 changes: 4 additions & 6 deletions idea/src/org/jetbrains/jet/plugin/compiler/K2JSCompiler.java
Expand Up @@ -173,15 +173,13 @@ private static void collectModuleDependencies(Module dependentModule, Set<Module
}

if (isDirectDependency) {
if (!modules.add(module)) {
continue;
if (modules.add(module)) {
collectModuleDependencies(module, modules, false);
}
}
else if (!moduleEntry.isExported() || !modules.add(module)) {
continue;
else if (modules.add(module)) {
collectModuleDependencies(module, modules, false);
}

collectModuleDependencies(module, modules, false);
}
}
}
Expand Down
Expand Up @@ -76,8 +76,7 @@ public boolean apply(PsiFile file) {
return isFileWithCode((JetFile) file);
}
};
AnalyzeExhaust exhaust = AnalyzerFacadeForJS
.analyzeFiles(allLibFiles, filesWithCode, Config.getEmptyConfig(project));
AnalyzeExhaust exhaust = AnalyzerFacadeForJS.analyzeFiles(allLibFiles, filesWithCode, Config.getEmptyConfig(project));
context = exhaust.getBindingContext();
AnalyzerFacadeForJS.checkForErrors(allLibFiles, context);
cachedLibraryContext = new SoftReference<BindingContext>(context);
Expand Down
Expand Up @@ -50,12 +50,11 @@ private AnalyzerFacadeForJS() {
@NotNull
public static BindingContext analyzeFilesAndCheckErrors(@NotNull List<JetFile> files,
@NotNull Config config) {
BindingContext bindingContext = analyzeFiles(files, Predicates.<PsiFile>alwaysTrue(), config).getBindingContext();
BindingContext bindingContext = analyzeFiles(files, config);
checkForErrors(Config.withJsLibAdded(files, config), bindingContext);
return bindingContext;
}


//NOTE: web demo related method
@SuppressWarnings("UnusedDeclaration")
@NotNull
Expand All @@ -70,32 +69,26 @@ public static AnalyzeExhaust analyzeFiles(
return analyzeFiles(files, filesToAnalyzeCompletely, config, false);
}

//TODO: refactor
@NotNull
public static AnalyzeExhaust analyzeFiles(
@NotNull Collection<JetFile> files,
@NotNull Predicate<PsiFile> filesToAnalyzeCompletely, @NotNull Config config,
boolean storeContextForBodiesResolve) {
@NotNull Predicate<PsiFile> filesToAnalyzeCompletely,
@NotNull Config config,
boolean storeContextForBodiesResolve
) {
Project project = config.getProject();

final ModuleDescriptor owner = new ModuleDescriptor(Name.special("<module>"));

ModuleDescriptor owner = new ModuleDescriptor(Name.special("<module>"));
Predicate<PsiFile> completely = Predicates.and(notLibFiles(config.getLibFiles()), filesToAnalyzeCompletely);

TopDownAnalysisParameters topDownAnalysisParameters = new TopDownAnalysisParameters(
completely, false, false, Collections.<AnalyzerScriptParameter>emptyList());

TopDownAnalysisParameters topDownAnalysisParameters =
new TopDownAnalysisParameters(completely, false, false, Collections.<AnalyzerScriptParameter>emptyList());
BindingContext libraryBindingContext = config.getLibraryBindingContext();
BindingTrace trace = libraryBindingContext == null ?
new ObservableBindingTrace(new BindingTraceContext()) :
new DelegatingBindingTrace(libraryBindingContext, "trace for analyzing library in js");
InjectorForTopDownAnalyzerForJs injector = new InjectorForTopDownAnalyzerForJs(
project, topDownAnalysisParameters, trace, owner,
new JsConfiguration(project, libraryBindingContext));
InjectorForTopDownAnalyzerForJs injector = new InjectorForTopDownAnalyzerForJs(project, topDownAnalysisParameters, trace, owner,
new JsConfiguration(project, libraryBindingContext));
try {
Collection<JetFile> allFiles = libraryBindingContext != null ?
files :
Config.withJsLibAdded(files, config);
Collection<JetFile> allFiles = libraryBindingContext != null ? files : Config.withJsLibAdded(files, config);
injector.getTopDownAnalyzer().analyzeFiles(allFiles, Collections.<AnalyzerScriptParameter>emptyList());
BodiesResolveContext bodiesResolveContext = storeContextForBodiesResolve ?
new CachedBodiesResolveContext(injector.getTopDownAnalysisContext()) :
Expand All @@ -107,6 +100,35 @@ public static AnalyzeExhaust analyzeFiles(
}
}

@NotNull
public static AnalyzeExhaust analyzeFiles(
@NotNull Collection<JetFile> files,
boolean analyzeCompletely,
@NotNull Config config,
BindingContext parentBindingContext,
boolean storeContextForBodiesResolve
) {
Project project = config.getProject();
ModuleDescriptor owner = new ModuleDescriptor(Name.special("<module>"));
TopDownAnalysisParameters topDownAnalysisParameters =
new TopDownAnalysisParameters(analyzeCompletely ? Predicates.<PsiFile>alwaysTrue() : Predicates.<PsiFile>alwaysFalse(), false, false, Collections.<AnalyzerScriptParameter>emptyList());
BindingTrace trace = parentBindingContext == null ?
new ObservableBindingTrace(new BindingTraceContext()) :
new DelegatingBindingTrace(parentBindingContext, "trace for analyzing library in js");
InjectorForTopDownAnalyzerForJs injector = new InjectorForTopDownAnalyzerForJs(project, topDownAnalysisParameters, trace, owner,
new JsModuleConfiguration(project, parentBindingContext));
try {
injector.getTopDownAnalyzer().analyzeFiles(files, Collections.<AnalyzerScriptParameter>emptyList());
BodiesResolveContext bodiesResolveContext = storeContextForBodiesResolve ?
new CachedBodiesResolveContext(injector.getTopDownAnalysisContext()) :
null;
return AnalyzeExhaust.success(trace.getBindingContext(), bodiesResolveContext, injector.getModuleConfiguration());
}
finally {
injector.destroy();
}
}

@NotNull
public static AnalyzeExhaust analyzeBodiesInFiles(
@NotNull Predicate<PsiFile> filesToAnalyzeCompletely,
Expand Down
Expand Up @@ -84,25 +84,18 @@ public void extendNamespaceScope(@NotNull BindingTrace trace, @NotNull Namespace
}

if (hasPreanalyzedContextForTests()) {
extendScopeWithPreAnalyzedContextForTests(namespaceDescriptor, namespaceMemberScope);
if (isNamespaceImportedByDefault(namespaceDescriptor) || isRootNamespace(namespaceDescriptor)) {
FqName descriptorName = DescriptorUtils.getFQName(namespaceDescriptor).toSafe();
NamespaceDescriptor alreadyAnalyzedNamespace = preanalyzedContext.get(BindingContext.FQNAME_TO_NAMESPACE_DESCRIPTOR, descriptorName);
namespaceMemberScope.importScope(alreadyAnalyzedNamespace.getMemberScope());
}
}
}

private boolean hasPreanalyzedContextForTests() {
return preanalyzedContext != null;
}

/*NOTE: this code is wrong. Check it if you have tests failing for frontend reasons*/
@SuppressWarnings("ConstantConditions")
private void extendScopeWithPreAnalyzedContextForTests(@NotNull NamespaceDescriptor namespaceDescriptor,
@NotNull WritableScope namespaceMemberScope) {
if (isNamespaceImportedByDefault(namespaceDescriptor) || isRootNamespace(namespaceDescriptor)) {
FqName descriptorName = DescriptorUtils.getFQName(namespaceDescriptor).toSafe();
NamespaceDescriptor alreadyAnalyzedNamespace = preanalyzedContext.get(BindingContext.FQNAME_TO_NAMESPACE_DESCRIPTOR, descriptorName);
namespaceMemberScope.importScope(alreadyAnalyzedNamespace.getMemberScope());
}
}

private static boolean isNamespaceImportedByDefault(@NotNull NamespaceDescriptor namespaceDescriptor) {
for (ImportPath path : DEFAULT_IMPORT_PATHS) {
if (path.fqnPart().equals(DescriptorUtils.getFQName(namespaceDescriptor).toSafe())) {
Expand Down

0 comments on commit 3ba9ad5

Please sign in to comment.