Skip to content

Commit

Permalink
[GR-8688] Proposal for languageHome getter in TruffleLanguage.
Browse files Browse the repository at this point in the history
PullRequest: graal/1151
  • Loading branch information
tzezula committed Apr 3, 2018
2 parents 5b80332 + e75a139 commit f0fb864
Show file tree
Hide file tree
Showing 8 changed files with 122 additions and 28 deletions.
1 change: 1 addition & 0 deletions truffle/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ This changelog summarizes major changes between Truffle versions relevant to lan
* Added Debugger#find(TruffleInstrument.Env) and Debugger#find(Engine)
* Added [FileSystem](http://www.graalvm.org/truffle/javadoc/org/graalvm/polyglot/io/FileSystem.html) SPI to allow embedder to virtualize TruffleLanguage Input/Output operations.
* Added [EventContext.lookupExecutionEventNodes](http://www.graalvm.org/truffle/javadoc/com/oracle/truffle/api/instrumentation/EventContext.html#lookupExecutionEventNodes-java.util.Collection-) to lookup all execution event nodes created by the bindings at the source location.
* Added `TruffleLanguage#getLanguageHome` to return the language directory in the GraalVM distribution or the location of the language Jar file.

## Version 0.33

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
Expand Down Expand Up @@ -789,6 +790,28 @@ public void testMoreLanguagesExceptionFromContextPatch() throws Exception {
assertEquals(1, secondLangCtx.disposeThreadCount); // Close initializes thread
}

@Test
public void testLanguageHome() throws Exception {
setPatchable(FIRST);
String expectedPath = Paths.get(String.format("/compile-graalvm/languages/%s", FIRST)).toString();
System.setProperty(String.format("%s.home", FIRST), expectedPath);
doContextPreinitialize(FIRST);
List<CountingContext> contexts = new ArrayList<>(emittedContexts);
assertEquals(1, contexts.size());
final CountingContext firstLangCtx = findContext(FIRST, contexts);
Assert.assertEquals(expectedPath, firstLangCtx.languageHome);

expectedPath = Paths.get(String.format("/run-graalvm/languages/%s", FIRST)).toString();
System.setProperty(String.format("%s.home", FIRST), expectedPath);
try (Context ctx = Context.newBuilder().build()) {
Value res = ctx.eval(Source.create(FIRST, "test"));
assertEquals("test", res.asString());
contexts = new ArrayList<>(emittedContexts);
assertEquals(1, contexts.size());
Assert.assertEquals(expectedPath, firstLangCtx.languageHome);
}
}

private static void resetSystemPropertiesOptions() {
System.getProperties().remove("polyglot.engine.PreinitializeContexts");
System.getProperties().remove(SYS_OPTION1_KEY);
Expand Down Expand Up @@ -858,6 +881,7 @@ static class CountingContext {
int disposeThreadOrder = -1;
final Map<OptionKey<Boolean>, Boolean> optionValues;
final List<String> arguments;
String languageHome;

CountingContext(final String id, final TruffleLanguage.Env env) {
this.id = id;
Expand Down Expand Up @@ -894,6 +918,7 @@ public Object execute(VirtualFrame frame) {
final CountingContext ctx = new CountingContext(languageId, env);
ctx.createContextCount++;
ctx.createContextOrder = nextId();
ctx.languageHome = getLanguageHome();
Collections.addAll(ctx.arguments, env.getApplicationArguments());
emittedContexts.add(ctx);
return ctx;
Expand All @@ -911,6 +936,7 @@ protected boolean patchContext(CountingContext context, TruffleLanguage.Env newE
assertNotNull(getContextReference().get());
context.patchContextCount++;
context.patchContextOrder = nextId();
context.languageHome = getLanguageHome();
context.environment(newEnv);
context.arguments.clear();
Collections.addAll(context.arguments, newEnv.getApplicationArguments());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,11 @@

import java.io.IOException;
import java.io.InputStream;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
Expand Down Expand Up @@ -60,6 +64,7 @@ final class LanguageCache implements Comparable<LanguageCache> {
private final boolean interactive;
private final boolean internal;
private final ClassLoader loader;
private String languageHome;
final TruffleLanguage<?> singletonLanguage;
private volatile Class<? extends TruffleLanguage<?>> languageClass;

Expand All @@ -69,19 +74,18 @@ final class LanguageCache implements Comparable<LanguageCache> {
}
}

private LanguageCache(String prefix, Properties info, ClassLoader loader) {
private LanguageCache(String id, String prefix, Properties info, ClassLoader loader, String url) {
this.loader = loader;
this.className = info.getProperty(prefix + "className");
this.name = info.getProperty(prefix + "name");
this.implementationName = info.getProperty(prefix + "implementationName");
this.version = info.getProperty(prefix + "version");
String resolvedId = info.getProperty(prefix + "id");
this.mimeTypes = parseList(info, prefix + "mimeType");
this.dependentLanguages = parseList(info, prefix + "dependentLanguage");
this.id = resolvedId == null ? defaultId() : resolvedId;
this.id = id;
this.interactive = Boolean.valueOf(info.getProperty(prefix + "interactive"));
this.internal = Boolean.valueOf(info.getProperty(prefix + "internal"));

this.languageHome = url;
if (TruffleOptions.AOT) {
this.languageClass = loadLanguageClass();
this.singletonLanguage = readSingleton(languageClass);
Expand Down Expand Up @@ -118,25 +122,7 @@ private static TreeSet<String> parseList(Properties info, String prefix) {
this.loader = instance.getClass().getClassLoader();
this.singletonLanguage = instance;
this.languageClass = (Class<? extends TruffleLanguage<?>>) instance.getClass();
}

private String defaultId() {
String resolvedId;
if (name.isEmpty()) {
int lastIndex = className.lastIndexOf('$');
if (lastIndex == -1) {
lastIndex = className.lastIndexOf('.');
}
resolvedId = className.substring(lastIndex + 1, className.length());
} else {
// TODO remove this hack for single character languages
if (name.length() == 1) {
resolvedId = name;
} else {
resolvedId = name.toLowerCase();
}
}
return resolvedId;
this.languageHome = null;
}

static Map<String, LanguageCache> languages() {
Expand Down Expand Up @@ -183,9 +169,11 @@ private static void collectLanguages(ClassLoader loader, List<LanguageCache> lis
while (en.hasMoreElements()) {
URL u = en.nextElement();
Properties p;
URLConnection connection;
try {
p = new Properties();
try (InputStream is = u.openStream()) {
connection = u.openConnection();
try (InputStream is = connection.getInputStream()) {
p.load(is);
}
} catch (IOException ex) {
Expand All @@ -200,9 +188,39 @@ private static void collectLanguages(ClassLoader loader, List<LanguageCache> lis
if (name == null) {
break;
}
list.add(new LanguageCache(prefix, p, loader));
String id = p.getProperty(prefix + "id");
if (id == null) {
id = defaultId(name, p.getProperty(prefix + "className"));
}
String languageHome = System.getProperty(id + ".home");
if (languageHome == null && connection instanceof JarURLConnection) {
// (tfel): This seems a bit brittle, but is actually the best API
// for this I could find.
Path path = Paths.get(((JarURLConnection) connection).getJarFileURL().getPath());
languageHome = path.getParent().toString();
}
list.add(new LanguageCache(id, prefix, p, loader, languageHome));
}
}
}

private static String defaultId(final String name, final String className) {
String resolvedId;
if (name.isEmpty()) {
int lastIndex = className.lastIndexOf('$');
if (lastIndex == -1) {
lastIndex = className.lastIndexOf('.');
}
resolvedId = className.substring(lastIndex + 1, className.length());
} else {
// TODO remove this hack for single character languages
if (name.length() == 1) {
resolvedId = name;
} else {
resolvedId = name.toLowerCase();
}
}
return resolvedId;
}

public int compareTo(LanguageCache o) {
Expand Down Expand Up @@ -245,6 +263,20 @@ boolean isInteractive() {
return interactive;
}

String getLanguageHome() {
if (languageHome == null) {
String home = System.getProperty(id + ".home");
if (home == null) {
String graalVmHome = System.getProperty("graalvm.home");
if (graalVmHome != null) {
home = Paths.get(graalVmHome).resolve("jre").resolve("languages").resolve(name).toString();
}
}
languageHome = home;
}
return languageHome;
}

LoadedLanguage loadLanguage() {
TruffleLanguage<?> instance;
boolean singleton = true;
Expand Down Expand Up @@ -312,6 +344,12 @@ private static TruffleLanguage<?> readSingleton(Class<?> languageClass) {
}
}

static void resetNativeImageCacheLanguageHomes() {
for (LanguageCache languageCache : languages().values()) {
languageCache.languageHome = null;
}
}

static final class LoadedLanguage {

private final TruffleLanguage<?> language;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1481,5 +1481,10 @@ public Class<? extends TruffleLanguage<?>> getLanguageClass(LanguageInfo languag
public boolean isDefaultFileSystem(FileSystem fs) {
return false;
}

@Override
public String getLanguageHome(Object engineObject) {
return ((PolyglotRuntime.LanguageShared) engineObject).cache.getLanguageHome();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -703,8 +703,14 @@ Collection<Thread> getAllThreads(PolyglotContextImpl context) {
static PolyglotEngineImpl preInitialize(PolyglotImpl impl, DispatchOutputStream out, DispatchOutputStream err, InputStream in, ClassLoader contextClassLoader) {
final PolyglotEngineImpl engine = new PolyglotEngineImpl(impl, out, err, in, new HashMap<>(), 0, null, false, true, contextClassLoader, true, true);
synchronized (engine) {
engine.preInitializedContext = PolyglotContextImpl.preInitialize(engine);
engine.addContext(engine.preInitializedContext);
try {
engine.preInitializedContext = PolyglotContextImpl.preInitialize(engine);
engine.addContext(engine.preInitializedContext);
} finally {
// Reset language homes from native-image compilatio time, will be recomputed in
// image execution time
LanguageCache.resetNativeImageCacheLanguageHomes();
}
}
return engine;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -848,6 +848,10 @@ public <T> T getOrCreateRuntimeData(Object sourceVM, Supplier<T> constructor) {
public boolean isDefaultFileSystem(FileSystem fs) {
return FileSystems.getDefaultFileSystem() == fs;
}
}

@Override
public String getLanguageHome(Object engineObject) {
return ((PolyglotLanguage) engineObject).cache.getLanguageHome();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1115,6 +1115,18 @@ protected static <C, T extends TruffleLanguage<C>> C getCurrentContext(Class<T>
return AccessAPI.engineAccess().getCurrentContext(languageClass);
}

/**
* Returns the home location for this language. This corresponds to the directory in which the
* Jar file is located, if run from a Jar file. For an AOT compiled binary, this corresponds to
* the location of the language files in the default GraalVM distribution layout. executable or
* shared library.
*
* @since 1.0
*/
protected final String getLanguageHome() {
return AccessAPI.engineAccess().getLanguageHome(AccessAPI.nodesAccess().getEngineObject(languageInfo));
}

/**
* Represents execution environment of the {@link TruffleLanguage}. Each active
* {@link TruffleLanguage} receives instance of the environment before any code is executed upon
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,8 @@ public final void detachOutputConsumer(DispatchOutputStream dos, OutputStream ou
public abstract Object findMetaObjectForLanguage(Object vmObject, Object value);

public abstract boolean isDefaultFileSystem(FileSystem fs);

public abstract String getLanguageHome(Object engineObject);
}

public abstract static class LanguageSupport {
Expand Down

0 comments on commit f0fb864

Please sign in to comment.