A Truffle language implementation contains all the services a language should provide to make it
composable with other languages. Implementation classes must be annotated with
Registration in order to be discoverable by the
org.graalvm.polyglot.
TruffleLanguage subclasses must provide a public default constructor.
Lifecycle
A language implementation becomes available for use by an engine when metadata is added using the
Registration annotation and the implementation's JAR file placed on the host Java Virtual
Machine's class path.
A newly created engine locates all available language implementations and creates a
org.graalvm.polyglot.Language for each. The descriptor holds the
language's registered metadata, but its execution environment is not initialized until the
language is needed for code execution. That execution environment remains initialized for the
lifetime of the engine and is isolated from the environment in any other engine instance.
Language global state can be shared between multiple context instances by saving them in a custom
field of the
TruffleLanguage subclass. Languages may control sharing between multiple
contexts using its
Registration#contextPolicy(). By default the context
policy is
ContextPolicy#EXCLUSIVE: each context has its own separate
TruffleLanguage instance.
If the context policy is more permissive then the implementation needs to manually ensure data
isolation between the contexts. This means that state associated with a context must not be
stored in a TruffleLanguage subclass. ASTs and assumptions can be shared across multiple contexts
if modifying them does not affect language semantics. Languages are strongly discouraged from
using static mutable state in their languages. Instead
TruffleLanguage instances should
be used instead to store global state and their sharing should be configured using
Registration#contextPolicy().
Whenever an engine is disposed then each initialized language context will be
#disposeContext(Object).
Context Policy
The number of
TruffleLanguage instances per polyglot
org.graalvm.polyglot.Context is configured by the
Registration#contextPolicy(). By default an
ContextPolicy#EXCLUSIVE
TruffleLanguage instance is created
for every
org.graalvm.polyglot.Context or
TruffleLanguage.Env#newContextBuilder(). With policy
ContextPolicy#REUSE, language instances will be reused after a language context was
TruffleLanguage#disposeContext(Object). With policy
ContextPolicy#SHARED, a language will also be reused if active contexts are not yet disposed. Language
instances will only be shared or reused if they are
TruffleLanguage#areOptionsCompatible(OptionValues,OptionValues). Language
implementations are encouraged to support the most permissive context policy possible. Please see
the individual
ContextPolicy for details on the implications on the language
implementation.
The following illustration shows the cardinalities of the individual components:
N: unbounded
P: N for exclusive, 1 for shared context policy
L: number of installed languages
I: number of installed instruments
- 1 : Host VM Processs
- N :
org.graalvm.polyglot.Engine- N :
org.graalvm.polyglot.Context- L : Language Context
- P * L :
TruffleLanguage- I :
org.graalvm.polyglot.Instrument- 1 :
com.oracle.truffle.api.instrumentation.TruffleInstrument
Parse Caching
The result of the
#parse(ParsingRequest) is cached per language instance,
ParsingRequest#getSource(),
ParsingRequest#getArgumentNames() and environment
Env#getOptions(). The scope of the caching is influenced
by the
Registration#contextPolicy(). Caching may be
Source#isCached() for certain sources. It is enabled for new sources by default.
Language Configuration
On
#createContext(Env) each language context is provided with
information about the environment
Env. Language can optionally declare
org.graalvm.polyglot.Context.Builder#option(String,String) options in
#getOptionDescriptors().
Polyglot Bindings
Language implementations communicate with one another (and with instrumentation-based tools such
as debuggers) by reading/writing named values into the
Env#getPolyglotBindings(). This bindings object is used to implement guest language export/import statements used
for
language interoperation.
A language implementation can also
Env#importSymbol(String) or
Env#exportSymbol(String,Object) a global symbol by name. The scope may be
accessed from multiple threads at the same time. Existing keys are overwritten.
Configuration vs. Initialization
To ensure that a Truffle language can be used in a language-agnostic way, the implementation
should be designed to decouple its configuration and initialization from language specifics as
much as possible. One aspect of this is the initialization and start of execution via the
org.graalvm.polyglot.Context, which should be designed in a generic way.
Language-specific entry points, for instance to emulate the command-line interface of an existing
implementation, should be handled externally.
Multi-threading
There are two kinds of threads that access contexts of Truffle guest languages:
- Internal threads are
Env#createThread(Runnable) and managed by a language for
a context. All internally created threads need to be stopped when the context is
#disposeContext(Object).
- External threads are created and managed by the host application / language launcher. The
host application is allowed to use language contexts from changing threads, sequentially or at
the same time if the language
#isThreadAccessAllowed(Thread,boolean) it.
By default every
#createContext(Env) only allows access from one thread at the
same time. Therefore if the context is tried to be accessed from multiple threads at the same
time the access will fail. Languages that want to allow multi-threaded access to a context may
override
#isThreadAccessAllowed(Thread,boolean) and return true
also for
multi-threaded accesses. Initialization actions for multi-threaded access can be performed by
overriding
#initializeMultiThreading(Object). Threads are
#initializeThread(Object,Thread) and
#disposeContext(Object) before and after use with a context. Languages may
Env#createThread(Runnable) new threads if the environment
Env#isCreateThreadAllowed() it.