Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix Meta.enso_project #10192

Merged
merged 11 commits into from
Jun 11, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ type Project_Description
Arguments:
- prim_root_file: The primitive root file of the project.
- prim_config: The primitive config of the project.
Value prim_root_file prim_config
private Value prim_root_file prim_config

## GROUP Metadata
ICON folder
Expand Down
20 changes: 11 additions & 9 deletions distribution/lib/Standard/Examples/0.0.0-dev/src/Main.enso
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,15 @@ type Example_Error_Type
- message: The message contained in the error type.
Error message

## The standard library data directory.
## The data directory of the Examples project.
data_dir : File
data_dir = enso_project.data
data_dir =
this_proj = Project_Description.new Standard.Examples.Main
this_proj.data

## An example CSV file for experimenting with Table and its APIs.
csv : File
csv = enso_project.data / "food_shop_inventory.csv"
csv = data_dir / "food_shop_inventory.csv"

## The path to the CSV.
csv_path : Text
Expand All @@ -39,7 +41,7 @@ csv_path = csv.path
xls : File
xls =
url = "https://enso-data-samples.s3.us-west-1.amazonaws.com/spreadsheet.xls"
file = enso_project.data / 'spreadsheet.xls'
file = data_dir / 'spreadsheet.xls'
if file.exists.not then
Context.Output.with_enabled <| HTTP.fetch url . body . write file
file
Expand All @@ -54,15 +56,15 @@ xls =
xlsx : File
xlsx =
url = "https://enso-data-samples.s3.us-west-1.amazonaws.com/spreadsheet.xlsx"
file = enso_project.data / 'spreadsheet.xlsx'
file = data_dir / 'spreadsheet.xlsx'
if file.exists.not then
Context.Output.with_enabled <| HTTP.fetch url . body . write file
file

## A file that is used for writing temporary data as part of tests.
scratch_file : File
scratch_file =
file = enso_project.data / "scratch_file"
file = data_dir / "scratch_file"
if file.exists.not then Nothing else
Context.Output.with_enabled <| file.delete
file
Expand Down Expand Up @@ -169,7 +171,7 @@ uri = URI.parse "http://user:[email protected]/foo/bar?key=val"
image_file : File
image_file =
url = "https://upload.wikimedia.org/wikipedia/commons/thumb/e/e9/Hue_alpha_falloff.png/320px-Hue_alpha_falloff.png"
file = enso_project.data / "image.png"
file = data_dir / "image.png"
if file.exists.not then
Context.Output.with_enabled <| HTTP.fetch url . body . write file
file
Expand Down Expand Up @@ -258,12 +260,12 @@ inventory_table = csv.read
## A simple table that contains basic item popularity data for the food shop.
popularity_table : Table
popularity_table =
(enso_project.data / "food_shop_popularity.csv") . read
(data_dir / "food_shop_popularity.csv") . read

## A simple tablethat contains basic transaction data for the food shop.
transactions_table : Table
transactions_table =
(enso_project.data / "food_shop_transactions.csv") . read
(data_dir / "food_shop_transactions.csv") . read

## An example regex match.
match : Match
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
package org.enso.interpreter.test.meta;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;

import java.io.IOException;
import java.util.List;
import java.util.Set;
import org.enso.common.LanguageInfo;
import org.enso.interpreter.util.ScalaConversions;
import org.enso.pkg.QualifiedName;
import org.enso.polyglot.PolyglotContext;
import org.enso.polyglot.RuntimeOptions;
import org.enso.test.utils.ContextUtils;
import org.enso.test.utils.ProjectUtils;
import org.enso.test.utils.SourceModule;
import org.graalvm.polyglot.Source;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;

public class EnsoProjectTest {
@Rule public TemporaryFolder temporaryFolder = new TemporaryFolder();

@Test
public void noProjectWhenEvaluatingSingleFile() {
try (var ctx = ContextUtils.createDefaultContext()) {
var res =
ContextUtils.evalModule(
ctx,
"""
from Standard.Base import all
from Standard.Base.Errors.Common import Module_Not_In_Package_Error

main =
enso_project.is_error
""");
assertThat(res, notNullValue());
assertThat(res.asBoolean(), is(true));
}
}

@Test
public void ensoProjectWorksInOneProject() throws IOException {
var mainMod =
new SourceModule(
QualifiedName.fromString("Main"),
"""
from Standard.Base import all
main =
enso_project.name
""");
var projDir = temporaryFolder.newFolder().toPath();
ProjectUtils.createProject("Proj", Set.of(mainMod), projDir);
ProjectUtils.testProjectRun(
projDir,
(res) -> {
assertThat(res.asString(), is("Proj"));
});
}

@Test
public void ensoProjectWorksInTwoProjects() throws IOException {
var mainMod1 =
new SourceModule(
QualifiedName.fromString("Main"),
"""
from Standard.Base import all

get_enso_project_name =
enso_project.name
""");
var mainMod2 =
new SourceModule(
QualifiedName.fromString("Main"),
"""
from local.Proj1 import get_enso_project_name
main =
get_enso_project_name
""");
var projDir1 = temporaryFolder.newFolder().toPath();
var projDir2 = temporaryFolder.newFolder().toPath();
ProjectUtils.createProject("Proj1", Set.of(mainMod1), projDir1);
ProjectUtils.createProject("Proj2", Set.of(mainMod2), projDir2);
ProjectUtils.testProjectRun(
projDir2,
(res) -> {
assertThat(res.asString(), is("Proj2"));
});
}

@Test
public void ensoProjectCanBeCalledFromJava() throws IOException {
var mainMod =
new SourceModule(
QualifiedName.fromString("Main"),
"""
from Standard.Base import all
main =
42
""");
var projDir = temporaryFolder.newFolder().toPath();
ProjectUtils.createProject("Proj", Set.of(mainMod), projDir);
var mainModFile = projDir.resolve("src").resolve("Main.enso");
assertThat(mainModFile.toFile().exists(), is(true));
try (var ctx =
ContextUtils.defaultContextBuilder()
.option(RuntimeOptions.PROJECT_ROOT, projDir.toAbsolutePath().toString())
.build()) {
var mainSrc = Source.newBuilder(LanguageInfo.ID, mainModFile.toFile()).build();
// First eval the source so that everything is compiled.
ctx.eval(mainSrc);
var polyCtx = new PolyglotContext(ctx);
var mod = polyCtx.getTopScope().getModule("Standard.Base.Meta.Enso_Project");
var assocType = mod.getAssociatedType();
var ensoProjMethod = mod.getMethod(assocType, "enso_project").get();
var projDescr = ensoProjMethod.execute(ScalaConversions.seq(List.of(assocType)));
assertThat(projDescr.hasMembers(), is(true));
assertThat(projDescr.getMetaObject().getMetaSimpleName(), is("Project_Description"));
assertThat(projDescr.hasMember("name"), is(true));
assertThat(projDescr.invokeMember("name").asString(), is("Proj"));
}
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,12 @@
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.Truffle;
import com.oracle.truffle.api.TruffleFile;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.library.CachedLibrary;
import com.oracle.truffle.api.nodes.Node;
import java.lang.ref.WeakReference;
import java.util.Objects;
import java.util.Optional;
import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.node.EnsoRootNode;
import org.enso.interpreter.runtime.EnsoContext;
import org.enso.interpreter.runtime.data.EnsoFile;
import org.enso.interpreter.runtime.data.Type;
Expand All @@ -25,7 +21,8 @@
@BuiltinMethod(
type = "Project_Description",
name = "enso_project_builtin",
description = "Returns the project description of the project given as the argument")
description =
"Returns the project description of the project given as the argument, or the main project")
public abstract class EnsoProjectNode extends Node {

public static EnsoProjectNode build() {
Expand All @@ -35,6 +32,10 @@ public static EnsoProjectNode build() {
/** A weak reference to the context in which this node was last executed. */
@CompilationFinal private WeakReference<EnsoContext> previousCtxRef = new WeakReference<>(null);

/**
* The project descriptor is cached, as it is safe to assume that it will not change during the
* runtime.
*/
private Object cachedProjectDescr;

/**
Expand All @@ -43,9 +44,8 @@ public static EnsoProjectNode build() {
public abstract Object execute(Object module);

/**
* Fetches the second stack frame from Truffle runtime (note that the first stack frame is the
* call of {@code enso_project}) - the caller of {@code enso_project}, and finds in which package
* the caller is located.
* Returns the project description of the main project, i.e., the project specified as the root to
* the engine.
*
* @param nothing Nothing, or interop null.
*/
Expand All @@ -56,50 +56,14 @@ public Object getCurrentProjectDescr(Object nothing) {
if (previousCtx == null || cachedProjectDescr == null || previousCtx != ctx) {
CompilerDirectives.transferToInterpreter();
previousCtxRef = new WeakReference<>(ctx);
// Find the caller of `enso_project`, i.e., of this node, and find in which package
// it is located. The first frame is skipped, because it is always
// `Enso_Project.enso_project`,
// i.e., the first frame is always call of this specialization.
Optional<Package<TruffleFile>> pkgOpt =
Truffle.getRuntime()
.iterateFrames(
frame -> {
var callNode = frame.getCallNode();
assert callNode != null
: "Should skip the first frame, therefore, callNode should not be null";
var callRootNode = callNode.getRootNode();
assert callRootNode != null
: "Should be called only from Enso code, and thus, should always have a"
+ " root node";
if (callRootNode instanceof EnsoRootNode ensoRootNode) {
var pkg = ensoRootNode.getModuleScope().getModule().getPackage();
// Don't return null, as that would signal to Truffle that we want to
// continue the iteration.
if (pkg != null) {
return Optional.of(pkg);
} else {
return Optional.empty();
}
} else {
CompilerDirectives.transferToInterpreter();
throw EnsoContext.get(this)
.raiseAssertionPanic(
this,
"Should not reach here: callRootNode = "
+ callRootNode
+ ". Probably not called from Enso?",
null);
}
},
// The first frame is always Enso_Project.enso_project
1);
if (pkgOpt.isPresent()) {
cachedProjectDescr = createProjectDescriptionAtom(ctx, pkgOpt.get());
var mainPkg = getMainProjectFromCtx(ctx);
if (mainPkg != null) {
cachedProjectDescr = createProjectDescriptionAtom(ctx, mainPkg);
} else {
cachedProjectDescr = notInModuleError(ctx);
}
}
Objects.requireNonNull(cachedProjectDescr);
assert cachedProjectDescr != null;
return cachedProjectDescr;
}

Expand All @@ -124,6 +88,14 @@ public Object getOtherProjectDescr(
}
}

private static Package<TruffleFile> getMainProjectFromCtx(EnsoContext ctx) {
var mainPkgOpt = ctx.getPackageRepository().getMainProjectPackage();
if (mainPkgOpt.isDefined()) {
return mainPkgOpt.get();
}
return null;
}

private static Atom createProjectDescriptionAtom(EnsoContext ctx, Package<TruffleFile> pkg) {
var rootPath = new EnsoFile(pkg.root().normalize());
var cfg = ctx.asGuestValue(pkg.getConfig());
Expand Down
Loading
Loading