Skip to content

Commit

Permalink
[GR-14821] Create API for languages to force their own initialization.
Browse files Browse the repository at this point in the history
  • Loading branch information
tzezula committed Mar 17, 2020
1 parent 86cbec1 commit b97ecb6
Show file tree
Hide file tree
Showing 5 changed files with 180 additions and 0 deletions.
1 change: 1 addition & 0 deletions truffle/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ This changelog summarizes major changes between Truffle versions relevant to lan
* Added [DebugValue#toDisplayString](https://www.graalvm.org/truffle/javadoc/com/oracle/truffle/api/debug/DebugValue.html#toDisplayString--) to convert the value to a language-specific string representation.
* Deprecated `DebugValue#as`, other conversion methods should be used instead.
* Clarify [InteropLibrary](https://www.graalvm.org/truffle/javadoc/com/oracle/truffle/api/interop/InteropLibrary.html) javadoc documentation of message exceptions. [UnsupportedMessageException](https://www.graalvm.org/truffle/javadoc/com/oracle/truffle/api/interop/UnsupportedMessageException.html) is thrown when the operation is never supported for the given receiver type. In other cases [UnknownIdentifierException](https://www.graalvm.org/truffle/javadoc/com/oracle/truffle/api/interop/UnknownIdentifierException.html) or [InvalidArrayIndexException](https://www.graalvm.org/truffle/javadoc/com/oracle/truffle/api/interop/InvalidArrayIndexException.html) are thrown.
* Added [TruffleLanguage.Env.initializeLanguage](https://www.graalvm.org/truffle/javadoc/com/oracle/truffle/api/TruffleLanguage.Env.html#initializeLanguage-com.oracle.truffle.api.nodes.LanguageInfo-) method to force language initialization.


## Version 20.0.0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;

import org.graalvm.options.OptionCategory;
import org.graalvm.options.OptionDescriptor;
Expand Down Expand Up @@ -123,6 +124,8 @@ public class LanguageSPITest {
public void cleanup() {
langContext = null;
ProxyLanguage.setDelegate(new ProxyLanguage());
InitializeTestLanguage.initialized = false;
InitializeTestInternalLanguage.initialized = false;
}

@Test
Expand Down Expand Up @@ -1986,6 +1989,79 @@ protected void initializeContext(LanguageContext c) throws Exception {
context.close();
}

@Test
public void testInitializeInternalLanguage() {
try (Context context = Context.create()) {
testInitializeInternalImpl(context, (env) -> env.getInternalLanguages().get(InitializeTestInternalLanguage.ID), () -> InitializeTestInternalLanguage.initialized);
}
}

@Test
public void testInitializeNonInternalLanguage() {
try (Context context = Context.newBuilder().allowPolyglotAccess(PolyglotAccess.newBuilder().allowEval(ProxyLanguage.ID, InitializeTestLanguage.ID).build()).build()) {
testInitializeInternalImpl(context, (env) -> env.getPublicLanguages().get(InitializeTestLanguage.ID), () -> InitializeTestLanguage.initialized);
}
}

private static void testInitializeInternalImpl(Context context, Function<TruffleLanguage.Env, LanguageInfo> languageResolver, Supplier<Boolean> verifier) {
ProxyLanguage.setDelegate(new ProxyLanguage() {
@Override
protected CallTarget parse(TruffleLanguage.ParsingRequest request) throws Exception {
return Truffle.getRuntime().createCallTarget(new RootNode(languageInstance) {
@Override
public Object execute(VirtualFrame frame) {
Env env = lookupContextReference(ProxyLanguage.class).get().getEnv();
LanguageInfo languageToInitialize = languageResolver.apply(env);
assertNotNull(languageToInitialize);
env.initializeLanguage(languageToInitialize);
return true;
}
});
}
});
assertTrue(context.eval(ProxyLanguage.ID, "").asBoolean());
assertTrue(verifier.get());
}

@Test
public void testInitializeFromServiceInternalLanguage() {
try (Context context = Context.create()) {
testInitializeFromServiceImpl(context, (env) -> env.getInternalLanguages().get(InitializeTestInternalLanguage.ID), () -> InitializeTestInternalLanguage.initialized);
}
}

@Test
public void testInitializeFromServiceNonInternalLanguage() {
try (Context context = Context.newBuilder().allowPolyglotAccess(PolyglotAccess.newBuilder().allowEval(ProxyLanguage.ID, InitializeTestLanguage.ID).build()).build()) {
testInitializeFromServiceImpl(context, (env) -> env.getPublicLanguages().get(InitializeTestLanguage.ID), () -> InitializeTestLanguage.initialized);
}
}

private static void testInitializeFromServiceImpl(Context context, Function<Env, LanguageInfo> languageResolver, Supplier<Boolean> verifier) {
ProxyLanguage.setDelegate(new ProxyLanguage() {
@Override
protected CallTarget parse(TruffleLanguage.ParsingRequest request) throws Exception {
return Truffle.getRuntime().createCallTarget(new RootNode(languageInstance) {
@Override
public Object execute(VirtualFrame frame) {
Env env = lookupContextReference(ProxyLanguage.class).get().getEnv();
LanguageInfo languageProvidingService = languageResolver.apply(env);
assertNotNull(languageProvidingService);
InitializeTestBaseLanguage.Service service = env.lookup(languageProvidingService, InitializeTestBaseLanguage.Service.class);
assertNotNull(service);
service.doNotInitialize();
assertFalse(verifier.get());
service.doesInitialize();
assertTrue(verifier.get());
return true;
}
});
}
});
assertTrue(context.eval(ProxyLanguage.ID, "").asBoolean());
assertTrue(verifier.get());
}

@TruffleLanguage.Registration(id = SERVICE_LANGUAGE, name = SERVICE_LANGUAGE, version = "1.0", contextPolicy = ContextPolicy.SHARED, services = {
LanguageSPITestLanguageService1.class, LanguageSPITestLanguageService2.class})
public static class ServiceTestLanguage extends TruffleLanguage<Env> {
Expand Down Expand Up @@ -2022,4 +2098,77 @@ interface LanguageSPITestLanguageService3 {

}

public abstract static class InitializeTestBaseLanguage extends TruffleLanguage<TruffleLanguage.Env> {
@Override
protected Env createContext(Env env) {
env.registerService(new Service() {

@Override
public void doNotInitialize() {
}

@Override
public void doesInitialize() {
LanguageInfo language = getLanguageInfo(env);
assertNotNull(language);
env.initializeLanguage(language);
}
});
return env;
}

@Override
protected void initializeContext(Env context) throws Exception {
setInitialized(true);
}

@Override
protected CallTarget parse(ParsingRequest request) throws Exception {
return Truffle.getRuntime().createCallTarget(RootNode.createConstantNode(true));
}

abstract LanguageInfo getLanguageInfo(Env env);

abstract void setInitialized(boolean value);

interface Service {
void doNotInitialize();

void doesInitialize();
}
}

@TruffleLanguage.Registration(id = InitializeTestLanguage.ID, name = InitializeTestLanguage.ID, version = "1.0", contextPolicy = ContextPolicy.EXCLUSIVE, services = InitializeTestBaseLanguage.Service.class)
public static final class InitializeTestLanguage extends InitializeTestBaseLanguage {
static final String ID = "LanguageSPITestInitializeTestLanguage";

static boolean initialized;

@Override
LanguageInfo getLanguageInfo(Env env) {
return env.getPublicLanguages().get(ID);
}

@Override
void setInitialized(boolean value) {
initialized = value;
}
}

@TruffleLanguage.Registration(id = InitializeTestInternalLanguage.ID, name = InitializeTestInternalLanguage.ID, version = "1.0", contextPolicy = ContextPolicy.EXCLUSIVE, internal = true, services = InitializeTestBaseLanguage.Service.class)
public static final class InitializeTestInternalLanguage extends InitializeTestBaseLanguage {
static final String ID = "LanguageSPITestInitializeTestInternalLanguage";

static boolean initialized;

@Override
LanguageInfo getLanguageInfo(Env env) {
return env.getInternalLanguages().get(ID);
}

@Override
void setInitialized(boolean value) {
initialized = value;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2213,6 +2213,21 @@ public <S> S lookup(LanguageInfo language, Class<S> type) {
return LanguageAccessor.engineAccess().lookupService(polyglotLanguageContext, language, this.getSpi().languageInfo, type);
}

/**
* Ensures that the target language is initialized. This method firstly verifies that the
* target language is accessible from this language. If not the {@link SecurityException} is
* thrown. Then the target language initialization is performed if not already done.
*
* @param targetLanguage the language to initialize
* @throws SecurityException if an access to {@code targetLanguage} is not permitted
* @since 20.1.0
*/
@TruffleBoundary
public void initializeLanguage(LanguageInfo targetLanguage) {
Objects.requireNonNull(targetLanguage, "TargetLanguage must be non null.");
LanguageAccessor.engineAccess().initializeLanguage(polyglotLanguageContext, targetLanguage);
}

/**
* @since 0.26
* @deprecated
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,8 @@ public abstract Process createSubProcess(Object polyglotLanguageContext, List<St
public abstract Object getLanguageView(LanguageInfo viewLanguage, Object value);

public abstract Object getScopedView(LanguageInfo viewLanguage, Node location, Frame frame, Object value);

public abstract void initializeLanguage(Object polyglotLanguageContext, LanguageInfo targetLanguage);
}

public abstract static class LanguageSupport {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1090,6 +1090,19 @@ public URI getReinitializedURI(TruffleFile truffleFile) {
Path path = EngineAccessor.LANGUAGE.getPath(truffleFile);
return ((FileSystems.PreInitializeContextFileSystem) fs).absolutePathtoURI(path);
}

@Override
public void initializeLanguage(Object polyglotLanguageContext, LanguageInfo targetLanguage) {
PolyglotLanguage targetPolyglotLanguage = (PolyglotLanguage) NODES.getPolyglotLanguage(targetLanguage);
PolyglotLanguageContext targetLanguageContext = ((PolyglotLanguageContext) polyglotLanguageContext).context.getContext(targetPolyglotLanguage);
PolyglotLanguage accessingPolyglotLanguage = ((PolyglotLanguageContext) polyglotLanguageContext).language;
try {
targetLanguageContext.checkAccess(accessingPolyglotLanguage);
} catch (PolyglotIllegalArgumentException notAccessible) {
throw new SecurityException(notAccessible.getMessage());
}
targetLanguageContext.ensureInitialized(accessingPolyglotLanguage);
}
}

abstract static class AbstractClassLoaderSupplier implements Supplier<ClassLoader> {
Expand Down

0 comments on commit b97ecb6

Please sign in to comment.