diff --git a/compiler/cli/src/org/jetbrains/jet/cli/jvm/repl/ReplInterpreter.java b/compiler/cli/src/org/jetbrains/jet/cli/jvm/repl/ReplInterpreter.java new file mode 100644 index 00000000000..63521577bdc --- /dev/null +++ b/compiler/cli/src/org/jetbrains/jet/cli/jvm/repl/ReplInterpreter.java @@ -0,0 +1,124 @@ +/* + * 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.repl; + +import com.google.common.base.Predicates; +import com.intellij.openapi.Disposable; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Disposer; +import com.intellij.openapi.vfs.CharsetToolkit; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiFileFactory; +import com.intellij.psi.impl.PsiFileFactoryImpl; +import com.intellij.testFramework.LightVirtualFile; +import com.intellij.util.ExceptionUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.analyzer.AnalyzeExhaust; +import org.jetbrains.jet.cli.common.messages.CompilerMessageLocation; +import org.jetbrains.jet.cli.common.messages.CompilerMessageSeverity; +import org.jetbrains.jet.cli.jvm.compiler.JetCoreEnvironment; +import org.jetbrains.jet.codegen.ClassBuilderFactories; +import org.jetbrains.jet.codegen.CompilationErrorHandler; +import org.jetbrains.jet.codegen.GeneratedClassLoader; +import org.jetbrains.jet.codegen.GenerationState; +import org.jetbrains.jet.di.InjectorForTopDownAnalyzerForJvm; +import org.jetbrains.jet.lang.descriptors.ModuleDescriptor; +import org.jetbrains.jet.lang.psi.JetFile; +import org.jetbrains.jet.lang.resolve.AnalyzerScriptParameter; +import org.jetbrains.jet.lang.resolve.AnalyzingUtils; +import org.jetbrains.jet.lang.resolve.BindingTrace; +import org.jetbrains.jet.lang.resolve.BindingTraceContext; +import org.jetbrains.jet.lang.resolve.TopDownAnalysisParameters; +import org.jetbrains.jet.lang.resolve.TopDownAnalyzer; +import org.jetbrains.jet.lang.resolve.java.CompilerDependencies; +import org.jetbrains.jet.lang.resolve.name.Name; +import org.jetbrains.jet.lang.types.lang.JetStandardLibrary; +import org.jetbrains.jet.plugin.JetLanguage; +import org.jetbrains.jet.utils.ExceptionUtils; +import org.jetbrains.jet.utils.Progress; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.util.Collections; + +/** + * @author Stepan Koltsov + */ +public class ReplInterpreter { + + private int lineNumber = 0; + + @NotNull + private final InjectorForTopDownAnalyzerForJvm injector; + @NotNull + private final JetCoreEnvironment jetCoreEnvironment; + @NotNull + private final BindingTraceContext trace; + + public ReplInterpreter(@NotNull Disposable disposable, @NotNull CompilerDependencies compilerDependencies) { + jetCoreEnvironment = new JetCoreEnvironment(disposable, compilerDependencies); + Project project = jetCoreEnvironment.getProject(); + trace = new BindingTraceContext(); + ModuleDescriptor module = new ModuleDescriptor(Name.special("")); + TopDownAnalysisParameters topDownAnalysisParameters = new TopDownAnalysisParameters( + Predicates.alwaysTrue(), + false, + true, + Collections.emptyList()); + injector = new InjectorForTopDownAnalyzerForJvm(project, topDownAnalysisParameters, trace, module, compilerDependencies); + } + + public Object eval(@NotNull String line) { + ++lineNumber; + + LightVirtualFile virtualFile = new LightVirtualFile("line" + lineNumber + ".ktscript", JetLanguage.INSTANCE, line); + virtualFile.setCharset(CharsetToolkit.UTF8_CHARSET); + JetFile psiFile = (JetFile) ((PsiFileFactoryImpl) PsiFileFactory.getInstance(jetCoreEnvironment.getProject())).trySetupPsiForFile(virtualFile, JetLanguage.INSTANCE, true, false); + + AnalyzingUtils.checkForSyntacticErrors(psiFile); + + injector.getTopDownAnalyzer().prepareForTheNextReplLine(); + + injector.getTopDownAnalyzer().analyzeFiles(Collections.singletonList(psiFile), Collections.emptyList()); + + AnalyzingUtils.throwExceptionOnErrors(trace.getBindingContext()); + + Progress backendProgress = new Progress() { + @Override + public void log(String message) { + } + }; + + GenerationState generationState = new GenerationState(jetCoreEnvironment.getProject(), ClassBuilderFactories.binaries(false), backendProgress, + AnalyzeExhaust.success(trace.getBindingContext(), JetStandardLibrary.getInstance()), Collections.singletonList(psiFile), + jetCoreEnvironment.getCompilerDependencies().getCompilerSpecialMode()); + generationState.compileCorrectFiles(CompilationErrorHandler.THROW_EXCEPTION); + + try { + Class scriptClass = new GeneratedClassLoader(generationState.getFactory()).loadClass("Script"); + Constructor scriptInstanceConstructor = scriptClass.getConstructor(new Class[0]); + Object scriptInstance = scriptInstanceConstructor.newInstance(new Object[0]); + Field rvField = scriptClass.getDeclaredField("rv"); + rvField.setAccessible(true); + Object rv = rvField.get(scriptInstance); + return rv; + } catch (Exception e) { + throw ExceptionUtils.rethrow(e); + } + } + +} diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/TopDownAnalyzer.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/TopDownAnalyzer.java index c6ef9fac2e2..f5918f0731c 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/TopDownAnalyzer.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/TopDownAnalyzer.java @@ -298,6 +298,12 @@ public class TopDownAnalyzer { } + public void prepareForTheNextReplLine() { + context.getScriptScopes().clear(); + context.getScripts().clear(); + } + + } diff --git a/compiler/testData/repl/constants.repl b/compiler/testData/repl/constants.repl new file mode 100644 index 00000000000..12a55758c7f --- /dev/null +++ b/compiler/testData/repl/constants.repl @@ -0,0 +1,4 @@ +>>> 1 +1 +>>> "aa" +aa diff --git a/compiler/testData/repl/simple.repl b/compiler/testData/repl/simple.repl new file mode 100644 index 00000000000..4a053fa90f8 --- /dev/null +++ b/compiler/testData/repl/simple.repl @@ -0,0 +1,4 @@ +>>> 12 + 13 +25 +>>> "foo ${1 + 2}" +foo 3 diff --git a/compiler/tests/org/jetbrains/jet/repl/ReplInterpreterTest.java b/compiler/tests/org/jetbrains/jet/repl/ReplInterpreterTest.java new file mode 100644 index 00000000000..ce668d0e4d8 --- /dev/null +++ b/compiler/tests/org/jetbrains/jet/repl/ReplInterpreterTest.java @@ -0,0 +1,79 @@ +/* + * 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.repl; + +import com.intellij.openapi.Disposable; +import com.intellij.openapi.util.Disposer; +import com.intellij.openapi.util.Pair; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.CompileCompilerDependenciesTest; +import org.jetbrains.jet.cli.jvm.repl.ReplInterpreter; +import org.jetbrains.jet.lang.resolve.java.CompilerDependencies; +import org.jetbrains.jet.lang.resolve.java.CompilerSpecialMode; +import org.junit.After; +import org.junit.Assert; +import org.junit.Test; + +import java.io.File; + +/** + * @author Stepan Koltsov + */ +public class ReplInterpreterTest { + + static { + System.setProperty("java.awt.headless", "true"); + } + + private final Disposable disposable = new Disposable() { + @Override + public void dispose() { + } + }; + + @After + public void tearDown() { + Disposer.dispose(disposable); + } + + private void testFile(@NotNull String relativePath) { + CompilerDependencies compilerDependencies = CompileCompilerDependenciesTest.compilerDependenciesForTests(CompilerSpecialMode.JDK_HEADERS, false); + ReplInterpreter repl = new ReplInterpreter(disposable, compilerDependencies); + + ReplSessionTestFile file = ReplSessionTestFile.load(new File("compiler/testData/repl/" + relativePath)); + for (Pair t : file.getLines()) { + String code = t.first; + String expected = t.second; + + Object actual = repl.eval(code); + String actualString = actual != null ? actual.toString() : "null"; + + Assert.assertEquals("after evaluation of: " + code, actualString, expected); + } + } + + @Test + public void constants() { + testFile("constants.repl"); + } + + @Test + public void simple() { + testFile("simple.repl"); + } + +} diff --git a/compiler/tests/org/jetbrains/jet/repl/ReplSessionTestFile.java b/compiler/tests/org/jetbrains/jet/repl/ReplSessionTestFile.java new file mode 100644 index 00000000000..3336e518a94 --- /dev/null +++ b/compiler/tests/org/jetbrains/jet/repl/ReplSessionTestFile.java @@ -0,0 +1,83 @@ +/* + * 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.repl; + +import com.google.common.collect.Lists; +import com.intellij.openapi.util.Pair; +import org.jetbrains.annotations.NotNull; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.List; + +/** + * @author Stepan Koltsov + */ +public class ReplSessionTestFile { + + @NotNull + private final List> lines; + + public ReplSessionTestFile(@NotNull List> lines) { + this.lines = lines; + } + + @NotNull + public List> getLines() { + return lines; + } + + public static ReplSessionTestFile load(@NotNull File file) { + try { + FileInputStream inputStream = new FileInputStream(file); + try { + BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, "utf-8")); + return load(reader); + } finally { + inputStream.close(); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private static ReplSessionTestFile load(@NotNull BufferedReader reader) throws IOException { + List> list = Lists.newArrayList(); + while (true) { + String odd = reader.readLine(); + if (odd == null) { + return new ReplSessionTestFile(list); + } + + if (!odd.startsWith(">>> ")) { + throw new IllegalStateException("odd lines must start with >>>"); + } + String code = odd.substring(4); + + String even = reader.readLine(); + if (even == null) { + throw new IllegalStateException("expecting even"); + } + + list.add(Pair.create(code, even)); + } + } + +}