Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.oracle.truffle.api.instrumentation.InstrumentationHandler Maven / Gradle / Ivy
Go to download
Truffle is a multi-language framework for executing dynamic languages
that achieves high performance when combined with Graal.
/*
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.oracle.truffle.api.instrumentation;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import com.oracle.truffle.api.CallTarget;
import com.oracle.truffle.api.TruffleLanguage;
import com.oracle.truffle.api.impl.Accessor;
import com.oracle.truffle.api.instrumentation.InstrumentableFactory.WrapperNode;
import com.oracle.truffle.api.instrumentation.ProbeNode.EventChainNode;
import com.oracle.truffle.api.instrumentation.TruffleInstrument.Env;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.nodes.NodeVisitor;
import com.oracle.truffle.api.nodes.RootNode;
import com.oracle.truffle.api.source.Source;
import com.oracle.truffle.api.source.SourceSection;
/**
* Central coordinator class for the Truffle instrumentation framework. Allocated once per
* {@linkplain com.oracle.truffle.api.vm.PolyglotEngine engine}.
*/
final class InstrumentationHandler {
/* Enable trace output to stdout. */
private static final boolean TRACE = Boolean.getBoolean("truffle.instrumentation.trace");
/* All roots that were initialized (executed at least once) */
private final Map roots = Collections.synchronizedMap(new WeakHashMap());
/* All bindings that have been globally created by instrumenter instances. */
private final List> bindings = new ArrayList<>();
/* Cached instance for reuse for newly installed root nodes. */
private final AddBindingsVisitor addAllBindingsVisitor = new AddBindingsVisitor(bindings);
/*
* Fast lookup of instrumenter instances based on a key provided by the accessor.
*/
private final Map instrumenterMap = new HashMap<>();
/* Has the instrumentation framework been initialized? */
private volatile boolean instrumentationInitialized;
private final OutputStream out;
private final OutputStream err;
private final InputStream in;
private final Map, Set>> cachedProvidedTags = new HashMap<>();
private InstrumentationHandler(OutputStream out, OutputStream err, InputStream in) {
this.out = out;
this.err = err;
this.in = in;
}
void installRootNode(RootNode root) {
if (!AccessorInstrumentHandler.nodesAccess().isInstrumentable(root)) {
return;
}
if (!instrumentationInitialized) {
initializeInstrumentation();
}
roots.put(root, null);
visitRoot(root, addAllBindingsVisitor);
}
void addInstrument(Object key, Class> clazz) {
addInstrumenter(key, new InstrumentClientInstrumenter(clazz, out, err, in));
}
void disposeInstrumenter(Object key, boolean cleanupRequired) {
if (TRACE) {
trace("BEGIN: Dispose instrumenter %n", key);
}
AbstractInstrumenter disposedInstrumenter = instrumenterMap.get(key);
List> disposedBindings = new ArrayList<>();
for (Iterator> iterator = bindings.listIterator(); iterator.hasNext();) {
EventBinding> binding = iterator.next();
if (binding.getInstrumenter() == disposedInstrumenter) {
iterator.remove();
disposedBindings.add(binding);
}
}
disposedInstrumenter.dispose();
instrumenterMap.remove(key);
if (cleanupRequired) {
DisposeBindingsVisitor disposeVisitor = new DisposeBindingsVisitor(disposedBindings);
for (RootNode root : roots.keySet()) {
visitRoot(root, disposeVisitor);
}
}
if (TRACE) {
trace("END: Disposed instrumenter %n", key);
}
}
Instrumenter forLanguage(TruffleLanguage.Env context, TruffleLanguage> language) {
return new LanguageClientInstrumenter<>(language, context);
}
void detachLanguage(Object context) {
if (instrumenterMap.containsKey(context)) {
/*
* TODO (chumer): do we need cleanup/invalidate here? With shared CallTargets we
* probably will.
*/
disposeInstrumenter(context, false);
}
}
EventBinding addBinding(EventBinding binding) {
if (TRACE) {
trace("BEGIN: Adding binding %s, %s%n", binding.getFilter(), binding.getElement());
}
this.bindings.add(binding);
if (instrumentationInitialized) {
AddBindingVisitor addBindingsVisitor = new AddBindingVisitor(binding);
for (RootNode root : roots.keySet()) {
visitRoot(root, addBindingsVisitor);
}
}
if (TRACE) {
trace("END: Added binding %s, %s%n", binding.getFilter(), binding.getElement());
}
return binding;
}
void disposeBinding(EventBinding> binding) {
if (TRACE) {
trace("BEGIN: Dispose binding %s, %s%n", binding.getFilter(), binding.getElement());
}
this.bindings.remove(binding);
DisposeBindingVisitor disposeVisitor = new DisposeBindingVisitor(binding);
for (RootNode root : roots.keySet()) {
visitRoot(root, disposeVisitor);
}
if (TRACE) {
trace("END: Disposed binding %s, %s%n", binding.getFilter(), binding.getElement());
}
}
EventChainNode installBindings(ProbeNode probeNodeImpl) {
EventContext context = probeNodeImpl.getContext();
SourceSection sourceSection = context.getInstrumentedSourceSection();
if (TRACE) {
trace("BEGIN: Lazy update for %s%n", sourceSection);
}
RootNode rootNode = probeNodeImpl.getRootNode();
Node instrumentedNode = probeNodeImpl.findWrapper().getDelegateNode();
Set> providedTags = getProvidedTags(rootNode);
EventChainNode root = null;
EventChainNode parent = null;
for (int i = 0; i < bindings.size(); i++) {
EventBinding> binding = bindings.get(i);
if (binding.isInstrumentedFull(providedTags, rootNode, instrumentedNode, sourceSection)) {
if (TRACE) {
trace(" Found binding %s, %s%n", binding.getFilter(), binding.getElement());
}
EventChainNode next = probeNodeImpl.createEventChainCallback(binding);
if (next == null) {
continue;
}
if (root == null) {
root = next;
} else {
assert parent != null;
parent.setNext(next);
}
parent = next;
}
}
if (TRACE) {
trace("END: Lazy updated for %s%n", sourceSection);
}
return root;
}
private void initializeInstrumentation() {
synchronized (this) {
if (!instrumentationInitialized) {
if (TRACE) {
trace("BEGIN: Initialize instrumentation%n");
}
for (AbstractInstrumenter instrumenter : instrumenterMap.values()) {
instrumenter.initialize();
}
if (TRACE) {
trace("END: Initialized instrumentation%n");
}
instrumentationInitialized = true;
}
}
}
private void addInstrumenter(Object key, AbstractInstrumenter instrumenter) throws AssertionError {
if (instrumenterMap.containsKey(key)) {
throw new AssertionError("Instrument already added.");
}
if (instrumentationInitialized) {
instrumenter.initialize();
List> addedBindings = new ArrayList<>();
for (EventBinding> binding : bindings) {
if (binding.getInstrumenter() == instrumenter) {
addedBindings.add(binding);
}
}
AddBindingsVisitor visitor = new AddBindingsVisitor(addedBindings);
for (RootNode root : roots.keySet()) {
visitRoot(root, visitor);
}
}
instrumenterMap.put(key, instrumenter);
}
@SuppressWarnings("unchecked")
private void insertWrapper(Node instrumentableNode, SourceSection sourceSection) {
Node node = instrumentableNode;
Node parent = node.getParent();
if (parent instanceof WrapperNode) {
// already wrapped, need to invalidate the wrapper something changed
invalidateWrapperImpl((WrapperNode) parent, node);
return;
}
ProbeNode probe = new ProbeNode(InstrumentationHandler.this, sourceSection);
WrapperNode wrapper;
try {
Class> factory = null;
Class> currentClass = instrumentableNode.getClass();
while (currentClass != null) {
Instrumentable instrumentable = currentClass.getAnnotation(Instrumentable.class);
if (instrumentable != null) {
factory = instrumentable.factory();
break;
}
currentClass = currentClass.getSuperclass();
}
if (factory == null) {
if (TRACE) {
trace("No wrapper inserted for %s, section %s. Not annotated with @Instrumentable.%n", node, sourceSection);
}
// node or superclass is not annotated with @Instrumentable
return;
}
if (TRACE) {
trace("Insert wrapper for %s, section %s%n", node, sourceSection);
}
wrapper = ((InstrumentableFactory) factory.newInstance()).createWrapper(instrumentableNode, probe);
} catch (Exception e) {
throw new IllegalStateException("Failed to create wrapper node. ", e);
}
if (!(wrapper instanceof Node)) {
throw new IllegalStateException(String.format("Implementation of %s must be a subclass of %s.", WrapperNode.class.getSimpleName(), Node.class.getSimpleName()));
}
Node wrapperNode = (Node) wrapper;
if (wrapperNode.getParent() != null) {
throw new IllegalStateException(String.format("Instance of provided %s is already adopted by another parent.", WrapperNode.class.getSimpleName()));
}
if (parent == null) {
throw new IllegalStateException(String.format("Instance of instrumentable %s is not adopted by a parent.", Node.class.getSimpleName()));
}
if (!node.isSafelyReplaceableBy(wrapperNode)) {
throw new IllegalStateException(String.format("WrapperNode implementation %s cannot be safely replaced in parent node class %s.", wrapperNode.getClass().getName(),
parent.getClass().getName()));
}
node.replace(wrapperNode);
if (node.getParent() != wrapperNode) {
throw new IllegalStateException("InstrumentableNode must have a WrapperNode as parent after createInstrumentationWrappwer is invoked.");
}
}
private EventBinding attachFactory(AbstractInstrumenter instrumenter, SourceSectionFilter filter, T factory) {
return addBinding(new EventBinding<>(instrumenter, filter, factory));
}
private EventBinding attachListener(AbstractInstrumenter instrumenter, SourceSectionFilter filter, T listener) {
return addBinding(new EventBinding<>(instrumenter, filter, listener));
}
Set> getProvidedTags(Class> language) {
Set> tags = cachedProvidedTags.get(language);
if (tags == null) {
ProvidedTags languageTags = language.getAnnotation(ProvidedTags.class);
List> languageTagsList = languageTags != null ? Arrays.asList(languageTags.value()) : Collections.> emptyList();
tags = Collections.unmodifiableSet(new HashSet<>(languageTagsList));
cachedProvidedTags.put(language, tags);
}
return tags;
}
Set> getProvidedTags(RootNode root) {
Class> language = AccessorInstrumentHandler.nodesAccess().findLanguage(root);
if (language != null) {
return getProvidedTags(language);
} else {
return Collections.emptySet();
}
}
private static boolean isInstrumentableNode(Node node, SourceSection sourceSection) {
return !(node instanceof WrapperNode) && !(node instanceof RootNode) && sourceSection != null;
}
private static void trace(String message, Object... args) {
PrintStream out = System.out;
out.printf(message, args);
}
private void visitRoot(final RootNode root, final AbstractNodeVisitor visitor) {
if (TRACE) {
trace("BEGIN: Visit root %s wrappers for %s%n", visitor, root.toString());
}
visitor.root = root;
visitor.providedTags = getProvidedTags(root);
try {
if (visitor.shouldVisit()) {
if (TRACE) {
trace("BEGIN: Traverse root %s wrappers for %s%n", visitor, root.toString());
}
// found a filter that matched
root.atomic(new Runnable() {
public void run() {
root.accept(visitor);
}
});
if (TRACE) {
trace("END: Traverse root %s wrappers for %s%n", visitor, root.toString());
}
}
if (TRACE) {
trace("END: Visited root %s wrappers for %s%n", visitor, root.toString());
}
} finally {
visitor.root = null;
visitor.providedTags = null;
}
}
static void removeWrapper(ProbeNode node) {
if (TRACE) {
trace("Remove wrapper for %s%n", node.getContext().getInstrumentedSourceSection());
}
WrapperNode wrapperNode = node.findWrapper();
((Node) wrapperNode).replace(wrapperNode.getDelegateNode());
}
private static void invalidateWrapper(Node node) {
Node parent = node.getParent();
if (!(parent instanceof WrapperNode)) {
// not yet wrapped
return;
}
invalidateWrapperImpl((WrapperNode) parent, node);
}
private static void invalidateWrapperImpl(WrapperNode parent, Node node) {
ProbeNode probeNode = parent.getProbeNode();
if (TRACE) {
SourceSection section = probeNode.getContext().getInstrumentedSourceSection();
trace("Invalidate wrapper for %s, section %s %n", node, section);
}
if (probeNode != null) {
probeNode.invalidate();
}
}
static boolean hasTagImpl(Set> providedTags, Node node, Class> tag) {
if (providedTags.contains(tag)) {
return AccessorInstrumentHandler.nodesAccess().isTaggedWith(node, tag);
}
return false;
}
static Instrumentable getInstrumentable(Node node) {
Instrumentable instrumentable = node.getClass().getAnnotation(Instrumentable.class);
if (instrumentable != null && !(node instanceof WrapperNode)) {
return instrumentable;
}
return null;
}
private T lookup(Object key, Class type) {
AbstractInstrumenter value = instrumenterMap.get(key);
return value == null ? null : value.lookup(this, type);
}
private abstract static class AbstractNodeVisitor implements NodeVisitor {
RootNode root;
Set> providedTags;
abstract boolean shouldVisit();
}
private abstract class AbstractBindingVisitor extends AbstractNodeVisitor {
protected final EventBinding> binding;
AbstractBindingVisitor(EventBinding> binding) {
this.binding = binding;
}
@Override
boolean shouldVisit() {
return binding.isInstrumentedRoot(providedTags, root, root.getSourceSection());
}
public final boolean visit(Node node) {
SourceSection sourceSection = node.getSourceSection();
if (isInstrumentableNode(node, sourceSection)) {
if (binding.isInstrumentedLeaf(providedTags, node, sourceSection)) {
if (TRACE) {
traceFilterCheck("hit", providedTags, binding, node, sourceSection);
}
visitInstrumented(node, sourceSection);
} else {
if (TRACE) {
traceFilterCheck("miss", providedTags, binding, node, sourceSection);
}
}
}
return true;
}
protected abstract void visitInstrumented(Node node, SourceSection section);
}
private static void traceFilterCheck(String result, Set> providedTags, EventBinding> binding, Node node, SourceSection sourceSection) {
Set> tags = binding.getFilter().getReferencedTags();
Set> containedTags = new HashSet<>();
for (Class> tag : tags) {
if (hasTagImpl(providedTags, node, tag)) {
containedTags.add(tag);
}
}
trace(" Filter %4s %s section:%s tags:%s%n", result, binding.getFilter(), sourceSection, containedTags);
}
private abstract class AbstractBindingsVisitor extends AbstractNodeVisitor {
private final List> bindings;
AbstractBindingsVisitor(List> bindings) {
this.bindings = bindings;
}
@Override
boolean shouldVisit() {
final RootNode localRoot = root;
if (localRoot == null) {
return false;
}
SourceSection sourceSection = localRoot.getSourceSection();
for (int i = 0; i < bindings.size(); i++) {
EventBinding> binding = bindings.get(i);
if (binding.isInstrumentedRoot(providedTags, localRoot, sourceSection)) {
return true;
}
}
return false;
}
public final boolean visit(Node node) {
SourceSection sourceSection = node.getSourceSection();
if (isInstrumentableNode(node, sourceSection)) {
List> b = bindings;
for (int i = 0; i < b.size(); i++) {
EventBinding> binding = b.get(i);
if (binding.isInstrumentedFull(providedTags, root, node, sourceSection)) {
if (TRACE) {
traceFilterCheck("hit", providedTags, binding, node, sourceSection);
}
visitInstrumented(node, sourceSection);
break;
} else {
if (TRACE) {
traceFilterCheck("miss", providedTags, binding, node, sourceSection);
}
}
}
}
return true;
}
protected abstract void visitInstrumented(Node node, SourceSection section);
}
/* Insert wrappers for a single bindings. */
private final class AddBindingVisitor extends AbstractBindingVisitor {
AddBindingVisitor(EventBinding> filter) {
super(filter);
}
@Override
protected void visitInstrumented(Node node, SourceSection section) {
insertWrapper(node, section);
}
}
private final class DisposeBindingVisitor extends AbstractBindingVisitor {
DisposeBindingVisitor(EventBinding> binding) {
super(binding);
}
@Override
protected void visitInstrumented(Node node, SourceSection section) {
invalidateWrapper(node);
}
}
private final class AddBindingsVisitor extends AbstractBindingsVisitor {
AddBindingsVisitor(List> bindings) {
super(bindings);
}
@Override
protected void visitInstrumented(Node node, SourceSection section) {
insertWrapper(node, section);
}
}
private final class DisposeBindingsVisitor extends AbstractBindingsVisitor {
DisposeBindingsVisitor(List> bindings) {
super(bindings);
}
@Override
protected void visitInstrumented(Node node, SourceSection section) {
invalidateWrapper(node);
}
}
/**
* Provider of instrumentation services for {@linkplain TruffleInstrument external clients} of
* instrumentation.
*/
final class InstrumentClientInstrumenter extends AbstractInstrumenter {
private final Class> instrumentClass;
private Object[] services;
private TruffleInstrument instrument;
private final Env env;
InstrumentClientInstrumenter(Class> instrumentClass, OutputStream out, OutputStream err, InputStream in) {
this.instrumentClass = instrumentClass;
this.env = new Env(this, out, err, in);
}
@Override
boolean isInstrumentableRoot(RootNode rootNode) {
return true;
}
@Override
public Set> queryTags(Node node) {
return queryTagsImpl(node, null);
}
@Override
void verifyFilter(SourceSectionFilter filter) {
}
Class> getInstrumentClass() {
return instrumentClass;
}
Env getEnv() {
return env;
}
@Override
void initialize() {
if (TRACE) {
trace("Initialize instrument %s class %s %n", instrument, instrumentClass);
}
assert instrument == null;
try {
this.instrument = (TruffleInstrument) instrumentClass.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
failInstrumentInitialization(String.format("Failed to create new instrumenter class %s", instrumentClass.getName()), e);
return;
}
try {
services = env.onCreate(instrument);
} catch (Throwable e) {
failInstrumentInitialization(String.format("Failed calling onCreate of instrument class %s", instrumentClass.getName()), e);
return;
}
if (TRACE) {
trace("Initialized instrument %s class %s %n", instrument, instrumentClass);
}
}
private void failInstrumentInitialization(String message, Throwable t) {
Exception exception = new Exception(message, t);
PrintStream stream = new PrintStream(env.err());
exception.printStackTrace(stream);
}
boolean isInitialized() {
return instrument != null;
}
TruffleInstrument getInstrument() {
return instrument;
}
@Override
void dispose() {
if (isInitialized()) {
instrument.onDispose(env);
}
}
@Override
T lookup(InstrumentationHandler handler, Class type) {
if (instrument == null) {
handler.initializeInstrumentation();
}
if (services != null) {
for (Object service : services) {
if (type.isInstance(service)) {
return type.cast(service);
}
}
}
return null;
}
}
/**
* Provider of instrumentation services for {@linkplain TruffleLanguage language
* implementations}.
*/
final class LanguageClientInstrumenter extends AbstractInstrumenter {
@SuppressWarnings("unused") private final TruffleLanguage.Env env;
private final TruffleLanguage language;
LanguageClientInstrumenter(TruffleLanguage language, TruffleLanguage.Env env) {
this.language = language;
this.env = env;
}
@Override
boolean isInstrumentableRoot(RootNode node) {
if (AccessorInstrumentHandler.nodesAccess().findLanguage(node.getRootNode()) != language.getClass()) {
return false;
}
// TODO (chumer) check for the context instance
return true;
}
@Override
public Set> queryTags(Node node) {
return queryTagsImpl(node, language.getClass());
}
@Override
void verifyFilter(SourceSectionFilter filter) {
Set> providedTags = getProvidedTags(language.getClass());
// filters must not reference tags not declared in @RequiredTags
Set> referencedTags = filter.getReferencedTags();
if (!providedTags.containsAll(referencedTags)) {
Set> missingTags = new HashSet<>(referencedTags);
missingTags.removeAll(providedTags);
Set> allTags = new LinkedHashSet<>(providedTags);
allTags.addAll(missingTags);
StringBuilder builder = new StringBuilder("{");
String sep = "";
for (Class> tag : allTags) {
builder.append(sep);
builder.append(tag.getSimpleName());
sep = ", ";
}
builder.append("}");
throw new IllegalArgumentException(String.format("The attached filter %s references the following tags %s which are not declared as provided by the language. " +
"To fix this annotate the language class %s with @%s(%s).",
filter, missingTags, language.getClass().getName(), ProvidedTags.class.getSimpleName(), builder));
}
}
@Override
void initialize() {
// nothing to do
}
@Override
void dispose() {
// nothing to do
}
@Override
S lookup(InstrumentationHandler handler, Class type) {
return null;
}
}
/**
* Shared implementation of instrumentation services for clients whose requirements and
* privileges may vary.
*/
abstract class AbstractInstrumenter extends Instrumenter {
abstract void initialize();
abstract void dispose();
abstract T lookup(InstrumentationHandler handler, Class type);
void disposeBinding(EventBinding> binding) {
InstrumentationHandler.this.disposeBinding(binding);
}
abstract boolean isInstrumentableRoot(RootNode rootNode);
final Set> queryTagsImpl(Node node, Class> onlyLanguage) {
SourceSection sourceSection = node.getSourceSection();
if (!InstrumentationHandler.isInstrumentableNode(node, sourceSection)) {
return Collections.emptySet();
}
RootNode root = node.getRootNode();
if (root == null) {
return Collections.emptySet();
}
Class> language = AccessorInstrumentHandler.nodesAccess().findLanguage(root);
if (onlyLanguage != null && language != onlyLanguage) {
throw new IllegalArgumentException("The language instrumenter cannot query tags of nodes of other languages.");
}
Set> providedTags = getProvidedTags(root);
if (providedTags.isEmpty()) {
return Collections.emptySet();
}
Set> tags = new HashSet<>();
for (Class> providedTag : providedTags) {
if (hasTagImpl(providedTags, node, providedTag)) {
tags.add(providedTag);
}
}
return Collections.unmodifiableSet(tags);
}
@Override
public final EventBinding attachFactory(SourceSectionFilter filter, T factory) {
verifyFilter(filter);
return InstrumentationHandler.this.attachFactory(this, filter, factory);
}
@Override
public final EventBinding attachListener(SourceSectionFilter filter, T listener) {
verifyFilter(filter);
return InstrumentationHandler.this.attachListener(this, filter, listener);
}
abstract void verifyFilter(SourceSectionFilter filter);
}
static final AccessorInstrumentHandler ACCESSOR = new AccessorInstrumentHandler();
static final class AccessorInstrumentHandler extends Accessor {
static Accessor.Nodes nodesAccess() {
return ACCESSOR.nodes();
}
static Accessor.LanguageSupport langAccess() {
return ACCESSOR.languageSupport();
}
static Accessor.EngineSupport engineAccess() {
return ACCESSOR.engineSupport();
}
@SuppressWarnings("rawtypes")
protected CallTarget parse(Class extends TruffleLanguage> languageClass, Source code, Node context, String... argumentNames) throws IOException {
final TruffleLanguage> truffleLanguage = engineSupport().findLanguageImpl(null, languageClass, code.getMimeType());
return langAccess().parse(truffleLanguage, code, context, argumentNames);
}
@Override
protected InstrumentSupport instrumentSupport() {
return new InstrumentImpl();
}
static final class InstrumentImpl extends InstrumentSupport {
@Override
public Object createInstrumentationHandler(Object vm, OutputStream out, OutputStream err, InputStream in) {
return new InstrumentationHandler(out, err, in);
}
@Override
public void addInstrument(Object instrumentationHandler, Object key, Class> instrumentClass) {
((InstrumentationHandler) instrumentationHandler).addInstrument(key, instrumentClass);
}
@Override
public void disposeInstrument(Object instrumentationHandler, Object key, boolean cleanupRequired) {
((InstrumentationHandler) instrumentationHandler).disposeInstrumenter(key, cleanupRequired);
}
@Override
public void collectEnvServices(Set collectTo, Object vm, TruffleLanguage> impl, TruffleLanguage.Env env) {
InstrumentationHandler instrumentationHandler = (InstrumentationHandler) engineAccess().getInstrumentationHandler(vm);
Instrumenter instrumenter = instrumentationHandler.forLanguage(env, impl);
collectTo.add(instrumenter);
}
@Override
public T getInstrumentationHandlerService(Object vm, Object key, Class type) {
InstrumentationHandler instrumentationHandler = (InstrumentationHandler) vm;
return instrumentationHandler.lookup(key, type);
}
@Override
public void detachLanguageFromInstrumentation(Object vm, com.oracle.truffle.api.TruffleLanguage.Env env) {
InstrumentationHandler instrumentationHandler = (InstrumentationHandler) engineAccess().getInstrumentationHandler(vm);
instrumentationHandler.detachLanguage(langAccess().findContext(env));
}
@Override
public void onFirstExecution(RootNode rootNode) {
Object instrumentationHandler = engineAccess().getInstrumentationHandler(null);
// we want to still support cases where call targets are executed without an
// enclosing
// engine.
if (instrumentationHandler != null) {
((InstrumentationHandler) instrumentationHandler).installRootNode(rootNode);
}
}
}
}
}