mirror of
https://github.com/enso-org/enso.git
synced 2024-11-26 08:52:58 +03:00
Import all available libraries in --repl mode (#10746)
Continuation of REPL work (#10709): Import all available libraries when running `--repl`.
This commit is contained in:
parent
0c55ee50cf
commit
71285e6ff8
@ -21,12 +21,14 @@
|
||||
- [Space-precedence does not apply to value-level operators][10597]
|
||||
- [Must specify `--repl` to enable debug server][10709]
|
||||
- [Improved parser error reporting and performance][10734]
|
||||
- [Import all available libraries in `--repl` mode][10746]
|
||||
|
||||
[10468]: https://github.com/enso-org/enso/pull/10468
|
||||
[10535]: https://github.com/enso-org/enso/pull/10535
|
||||
[10597]: https://github.com/enso-org/enso/pull/10597
|
||||
[10709]: https://github.com/enso-org/enso/pull/10709
|
||||
[10734]: https://github.com/enso-org/enso/pull/10734
|
||||
[10746]: https://github.com/enso-org/enso/pull/10746
|
||||
|
||||
#### Enso IDE
|
||||
|
||||
|
@ -9,6 +9,7 @@ public class MethodNames {
|
||||
public static final String REGISTER_MODULE = "register_module";
|
||||
public static final String UNREGISTER_MODULE = "unregister_module";
|
||||
public static final String COMPILE = "compile";
|
||||
public static final String LOCAL_LIBRARIES = "local_libraries";
|
||||
}
|
||||
|
||||
public static class Module {
|
||||
|
@ -0,0 +1,57 @@
|
||||
package org.enso.polyglot;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import org.enso.common.LanguageInfo;
|
||||
import org.graalvm.polyglot.Context;
|
||||
import org.graalvm.polyglot.Source;
|
||||
|
||||
public record PolyglotContext(Context context) {
|
||||
/**
|
||||
* Evaluates provided code string as a new module.
|
||||
*
|
||||
* @param code the code to evaluate.
|
||||
* @param moduleName the name for the newly parsed module.
|
||||
* @return the module representing evaluated code.
|
||||
*/
|
||||
public Module evalModule(String code, String moduleName) {
|
||||
var source = Source.newBuilder(LanguageInfo.ID, code, moduleName).buildLiteral();
|
||||
return new Module(context.eval(source));
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluates provided code file as a new module.
|
||||
*
|
||||
* @param codeFile the code to evaluate.
|
||||
* @return the module representing evaluated code.
|
||||
*/
|
||||
public Module evalModule(File codeFile) throws IOException {
|
||||
var source = Source.newBuilder(LanguageInfo.ID, codeFile).build();
|
||||
return new Module(context.eval(source));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates and evaluates default repl script for this context.
|
||||
*
|
||||
* @param mainMethodName name of the main method
|
||||
* @return module representing evaluated code
|
||||
*/
|
||||
public Module evalReplModule(String mainMethodName) {
|
||||
var replModuleName = "Internal_Repl_Module___";
|
||||
var sb = new StringBuilder();
|
||||
sb.append("import Standard.Base.Runtime.Debug\n");
|
||||
for (var libName : getTopScope().getLibraries()) {
|
||||
sb.append("from ").append(libName).append(" import all\n");
|
||||
}
|
||||
sb.append("\n");
|
||||
sb.append(mainMethodName).append(" = Debug.breakpoint");
|
||||
return evalModule(sb.toString(), replModuleName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the top scope of Enso execution context
|
||||
*/
|
||||
public TopScope getTopScope() {
|
||||
return new TopScope(context.getBindings(LanguageInfo.ID));
|
||||
}
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
package org.enso.polyglot
|
||||
import java.io.File
|
||||
import org.enso.common.LanguageInfo
|
||||
|
||||
import org.graalvm.polyglot.{Context, Source}
|
||||
|
||||
/** Exposes language specific aliases for generic polyglot context operations.
|
||||
* @param context the Graal polyglot context to use.
|
||||
*/
|
||||
class PolyglotContext(val context: Context) {
|
||||
|
||||
/** Evaluates provided code string as a new module.
|
||||
*
|
||||
* @param code the code to evaluate.
|
||||
* @param moduleName the name for the newly parsed module.
|
||||
* @return the module representing evaluated code.
|
||||
*/
|
||||
def evalModule(code: String, moduleName: String): Module = {
|
||||
val source = Source
|
||||
.newBuilder(LanguageInfo.ID, code, moduleName)
|
||||
.build()
|
||||
new Module(context.eval(source))
|
||||
}
|
||||
|
||||
/** Evaluates provided code file as a new module.
|
||||
*
|
||||
* @param codeFile the code to evaluate.
|
||||
* @return the module representing evaluated code.
|
||||
*/
|
||||
def evalModule(codeFile: File): Module = {
|
||||
val source = Source.newBuilder(LanguageInfo.ID, codeFile).build
|
||||
new Module(context.eval(source))
|
||||
}
|
||||
|
||||
/** @return the top scope of Enso execution context
|
||||
*/
|
||||
def getTopScope: TopScope = {
|
||||
new TopScope(context.getBindings(LanguageInfo.ID))
|
||||
}
|
||||
}
|
@ -37,4 +37,7 @@ class TopScope(private val value: Value) {
|
||||
def compile(shouldCompileDependencies: Boolean): Unit = {
|
||||
value.invokeMember(COMPILE, shouldCompileDependencies)
|
||||
}
|
||||
|
||||
def getLibraries(): Array[String] =
|
||||
value.invokeMember(LOCAL_LIBRARIES).as(classOf[Array[String]])
|
||||
}
|
||||
|
@ -818,7 +818,8 @@ public class Main {
|
||||
}
|
||||
|
||||
private void runSingleFile(
|
||||
PolyglotContext context, File file, java.util.List<String> additionalArgs) {
|
||||
PolyglotContext context, File file, java.util.List<String> additionalArgs)
|
||||
throws IOException {
|
||||
var mainModule = context.evalModule(file);
|
||||
runMain(mainModule, file, additionalArgs, "main");
|
||||
}
|
||||
@ -893,15 +894,6 @@ public class Main {
|
||||
boolean enableIrCaches,
|
||||
boolean enableStaticAnalysis) {
|
||||
var mainMethodName = "internal_repl_entry_point___";
|
||||
var dummySourceToTriggerRepl =
|
||||
"""
|
||||
from Standard.Base import all
|
||||
import Standard.Base.Runtime.Debug
|
||||
|
||||
$mainMethodName = Debug.breakpoint
|
||||
"""
|
||||
.replace("$mainMethodName", mainMethodName);
|
||||
var replModuleName = "Internal_Repl_Module___";
|
||||
var projectRoot = projectPath != null ? projectPath : "";
|
||||
var options = Collections.singletonMap(DebugServerInfo.ENABLE_OPTION, "true");
|
||||
|
||||
@ -917,7 +909,8 @@ public class Main {
|
||||
.disableLinting(true)
|
||||
.enableStaticAnalysis(enableStaticAnalysis)
|
||||
.build());
|
||||
var mainModule = context.evalModule(dummySourceToTriggerRepl, replModuleName);
|
||||
|
||||
var mainModule = context.evalReplModule(mainMethodName);
|
||||
runMain(mainModule, null, Collections.emptyList(), mainMethodName);
|
||||
throw exitSuccess();
|
||||
}
|
||||
|
@ -29,6 +29,10 @@ trait PackageRepository {
|
||||
libraryName: LibraryName
|
||||
): Either[PackageRepository.Error, Unit]
|
||||
|
||||
/** Iterates over all installed libraries.
|
||||
*/
|
||||
def findAvailableLocalLibraries(): Seq[LibraryName]
|
||||
|
||||
/** Checks if the library has already been loaded */
|
||||
def isPackageLoaded(libraryName: LibraryName): Boolean
|
||||
|
||||
|
@ -0,0 +1,52 @@
|
||||
package org.enso.interpreter.runtime;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.List;
|
||||
import org.enso.common.LanguageInfo;
|
||||
import org.enso.common.MethodNames;
|
||||
import org.enso.polyglot.PolyglotContext;
|
||||
import org.enso.test.utils.ContextUtils;
|
||||
import org.graalvm.polyglot.Context;
|
||||
import org.graalvm.polyglot.TypeLiteral;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
public class ListLibrariesTest {
|
||||
private static Context ctx;
|
||||
|
||||
@BeforeClass
|
||||
public static void initCtx() {
|
||||
ctx = ContextUtils.createDefaultContext();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void closeCtx() {
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void listLibraries() {
|
||||
var b = ctx.getBindings(LanguageInfo.ID);
|
||||
var libs = b.invokeMember(MethodNames.TopScope.LOCAL_LIBRARIES);
|
||||
assertTrue("Array of lib names: " + libs, libs.hasArrayElements());
|
||||
var list = libs.as(new TypeLiteral<List<String>>() {});
|
||||
assertTrue("At least five libs: " + list, list.size() >= 5);
|
||||
|
||||
assertTrue("Base found " + list, list.contains("Standard.Base"));
|
||||
assertTrue("Table found " + list, list.contains("Standard.Table"));
|
||||
assertTrue("DB found " + list, list.contains("Standard.Database"));
|
||||
assertTrue("AWS found " + list, list.contains("Standard.AWS"));
|
||||
assertTrue("Geo found " + list, list.contains("Standard.Geo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void evaluateDefaultReplScript() {
|
||||
var pc = new PolyglotContext(ctx);
|
||||
final var fnName = "main_fn_name__";
|
||||
var module = pc.evalReplModule(fnName);
|
||||
var result = module.evalExpression(fnName);
|
||||
assertTrue("Returns Nothing", result.isNull());
|
||||
}
|
||||
}
|
@ -21,14 +21,16 @@ class ReplTest
|
||||
): Unit = {
|
||||
|
||||
"initialize properly" in {
|
||||
val code =
|
||||
"""
|
||||
|import Standard.Base.Runtime.Debug
|
||||
|
|
||||
|main = Debug.breakpoint
|
||||
|""".stripMargin
|
||||
setSessionManager(executor => executor.exit())
|
||||
eval(code)
|
||||
var counter = 0;
|
||||
setSessionManager(executor => {
|
||||
counter = counter + 1
|
||||
executor.exit()
|
||||
})
|
||||
val mainFn = "my_main_fn__"
|
||||
val replModule =
|
||||
interpreterContext.executionContext.evalReplModule(mainFn)
|
||||
replModule.evalExpression(mainFn)
|
||||
counter shouldEqual 1
|
||||
}
|
||||
|
||||
"be able to execute arbitrary code in the caller scope" in {
|
||||
@ -293,7 +295,7 @@ class ReplTest
|
||||
}
|
||||
eval(code)
|
||||
val errorMsg =
|
||||
"Compile_Error.Error"
|
||||
"Compile error: The name `undefined` could not be found."
|
||||
evalResult.left.value.getMessage shouldEqual errorMsg
|
||||
}
|
||||
|
||||
|
@ -117,6 +117,7 @@ public final class TopLevelScope implements EnsoObject {
|
||||
MethodNames.TopScope.CREATE_MODULE,
|
||||
MethodNames.TopScope.REGISTER_MODULE,
|
||||
MethodNames.TopScope.UNREGISTER_MODULE,
|
||||
MethodNames.TopScope.LOCAL_LIBRARIES,
|
||||
MethodNames.TopScope.COMPILE);
|
||||
}
|
||||
|
||||
@ -165,6 +166,18 @@ public final class TopLevelScope implements EnsoObject {
|
||||
return context.getNothing();
|
||||
}
|
||||
|
||||
@CompilerDirectives.TruffleBoundary
|
||||
private static EnsoObject localLibraries(EnsoContext context) {
|
||||
var seq = context.getTopScope().packageRepository.findAvailableLocalLibraries();
|
||||
String[] names = new String[seq.size()];
|
||||
var it = seq.iterator();
|
||||
var i = 0;
|
||||
while (it.hasNext()) {
|
||||
names[i++] = it.next().toString();
|
||||
}
|
||||
return ArrayLikeHelpers.wrapStrings(names);
|
||||
}
|
||||
|
||||
private static Object leakContext(EnsoContext context) {
|
||||
return context.asGuestValue(context);
|
||||
}
|
||||
@ -186,22 +199,19 @@ public final class TopLevelScope implements EnsoObject {
|
||||
@Specialization
|
||||
static Object doInvoke(TopLevelScope scope, String member, Object[] arguments)
|
||||
throws UnknownIdentifierException, ArityException, UnsupportedTypeException {
|
||||
switch (member) {
|
||||
case MethodNames.TopScope.GET_MODULE:
|
||||
return getModule(scope, arguments);
|
||||
case MethodNames.TopScope.CREATE_MODULE:
|
||||
return createModule(scope, arguments, EnsoContext.get(null));
|
||||
case MethodNames.TopScope.REGISTER_MODULE:
|
||||
return registerModule(scope, arguments, EnsoContext.get(null));
|
||||
case MethodNames.TopScope.UNREGISTER_MODULE:
|
||||
return unregisterModule(scope, arguments, EnsoContext.get(null));
|
||||
case MethodNames.TopScope.LEAK_CONTEXT:
|
||||
return leakContext(EnsoContext.get(null));
|
||||
case MethodNames.TopScope.COMPILE:
|
||||
return compile(arguments, EnsoContext.get(null));
|
||||
default:
|
||||
throw UnknownIdentifierException.create(member);
|
||||
}
|
||||
return switch (member) {
|
||||
case MethodNames.TopScope.GET_MODULE -> getModule(scope, arguments);
|
||||
case MethodNames.TopScope.CREATE_MODULE -> createModule(
|
||||
scope, arguments, EnsoContext.get(null));
|
||||
case MethodNames.TopScope.REGISTER_MODULE -> registerModule(
|
||||
scope, arguments, EnsoContext.get(null));
|
||||
case MethodNames.TopScope.UNREGISTER_MODULE -> unregisterModule(
|
||||
scope, arguments, EnsoContext.get(null));
|
||||
case MethodNames.TopScope.LEAK_CONTEXT -> leakContext(EnsoContext.get(null));
|
||||
case MethodNames.TopScope.LOCAL_LIBRARIES -> localLibraries(EnsoContext.get(null));
|
||||
case MethodNames.TopScope.COMPILE -> compile(arguments, EnsoContext.get(null));
|
||||
default -> throw UnknownIdentifierException.create(member);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -218,6 +228,7 @@ public final class TopLevelScope implements EnsoObject {
|
||||
|| member.equals(MethodNames.TopScope.REGISTER_MODULE)
|
||||
|| member.equals(MethodNames.TopScope.UNREGISTER_MODULE)
|
||||
|| member.equals(MethodNames.TopScope.LEAK_CONTEXT)
|
||||
|| member.equals(MethodNames.TopScope.LOCAL_LIBRARIES)
|
||||
|| member.equals(MethodNames.TopScope.COMPILE);
|
||||
}
|
||||
|
||||
|
@ -138,6 +138,10 @@ private class DefaultPackageRepository(
|
||||
go(component.name.split(LibraryName.separator))
|
||||
}
|
||||
|
||||
override def findAvailableLocalLibraries(): Seq[LibraryName] = {
|
||||
libraryProvider.findAvailableLocalLibraries()
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
override def getModuleMap: PackageRepository.ModuleMap = loadedModules
|
||||
|
||||
|
@ -84,7 +84,7 @@ prefer-local-libraries: true
|
||||
* main} method
|
||||
*/
|
||||
public static void testProjectRun(
|
||||
Context.Builder ctxBuilder, Path projDir, Consumer<Value> resultConsumer) {
|
||||
Context.Builder ctxBuilder, Path projDir, Consumer<Value> resultConsumer) throws IOException {
|
||||
if (!(projDir.toFile().exists() && projDir.toFile().isDirectory())) {
|
||||
throw new IllegalArgumentException(
|
||||
"Project directory " + projDir + " must already be created");
|
||||
@ -115,7 +115,8 @@ prefer-local-libraries: true
|
||||
* @param resultConsumer Any action that is to be evaluated on the result of running the {@code
|
||||
* main} method
|
||||
*/
|
||||
public static void testProjectRun(Path projDir, Consumer<Value> resultConsumer) {
|
||||
public static void testProjectRun(Path projDir, Consumer<Value> resultConsumer)
|
||||
throws IOException {
|
||||
testProjectRun(ContextUtils.defaultContextBuilder(), projDir, resultConsumer);
|
||||
}
|
||||
|
||||
|
@ -40,6 +40,11 @@ class DefaultLibraryProvider(
|
||||
private val logger = Logger[DefaultLibraryProvider]
|
||||
private val resolver = LibraryResolver(localLibraryProvider)
|
||||
|
||||
override def findAvailableLocalLibraries(): Seq[LibraryName] = {
|
||||
val libs = edition.getAllDefinedLibraries
|
||||
libs.keys.toSeq
|
||||
}
|
||||
|
||||
/** Resolves the library version that should be used based on the
|
||||
* configuration and returns its location on the filesystem.
|
||||
*
|
||||
|
@ -5,6 +5,9 @@ import org.enso.editions.{LibraryName, LibraryVersion}
|
||||
/** A helper class for resolving libraries. */
|
||||
trait ResolvingLibraryProvider {
|
||||
|
||||
/** Finds available library names */
|
||||
def findAvailableLocalLibraries(): Seq[LibraryName]
|
||||
|
||||
/** Resolves which library version should be used and finds its path within
|
||||
* local libraries or the cache.
|
||||
*
|
||||
|
Loading…
Reference in New Issue
Block a user