All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.oracle.truffle.api.instrument.Instrumenter Maven / Gradle / Ivy

Go to download

Truffle is a multi-language framework for executing dynamic languages that achieves high performance when combined with Graal.

There is a newer version: 1.0.0-rc7
Show newest version
/*
 * Copyright (c) 2015, 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.instrument;

import java.io.IOException;
import java.io.PrintStream;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.oracle.truffle.api.CallTarget;
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
import com.oracle.truffle.api.TruffleLanguage;
import com.oracle.truffle.api.impl.Accessor;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.nodes.RootNode;
import com.oracle.truffle.api.source.Source;
import com.oracle.truffle.api.source.SourceSection;

/**
 * Client access to instrumentation services in a Truffle execution environment.
 * 

* Services include: *

    *
  • A collection of {@linkplain Probe Probes}, each of which is {@linkplain #probe(Node) created} * by clients in permanent association with a particular {@linkplain SourceSection source code * location} in an AST executing in this environment. The Probe keeps tracks of all clones * of the AST and ensures that any instrumentation attached to the Probe is put into effect * in each AST clone at the {@link Node} that corresponds to the Probe's source code location.
  • *

    *

  • A collection of {@linkplain ProbeListener listeners} that have registered to be notified * whenever a new {@link Probe} is created in this environment and whenever a new {@link SyntaxTag} * (or simply "tag") is newly added to an existing {@link Probe} in this environment.
  • *

    *

  • The ability to {@linkplain #findProbesTaggedAs(SyntaxTag) enumerate} all existing * {@linkplain Probe Probes} in this environment, optionally filtered to include only those to which * a specific {@linkplain SyntaxTag tag} has been added.
  • *

    *

  • The ability to attach a client-provided event listener to an existing * {@link Probe} in this environment. The listener subsequently receives notification of execution * events at the {@linkplain Node Nodes} corresponding to the Probe's {@linkplain SourceSection * source code location}. The attachment also produces a {@link ProbeInstrument} that * represents the binding, and which can be used to {@linkplain Instrument#dispose() detach} the * listener from the Probe and stop event notification. A listener can be attached to any number of * Probes, each time producing a new {@linkplain ProbeInstrument} that represents the binding.
  • *

    *

  • The ability to attach a client-provided event listener to a specific * {@linkplain SyntaxTag tag} for all {@linkplain Probe Probes} in the environment. A maximum of * one listener may be attached to receive notification of "before" execution * events (i.e. the flow of execution is just about to enter a {@link Node}), and a maximum of * one listener may be attached to receive notification of "after" execution * events. The attachment also produces a {@link TagInstrument} that represents the * binding, and which can be used to {@linkplain Instrument#dispose() detach} the listener from the * Probes and stop event notification. This mechanism is designed for much lower runtime overhead * than other ways to accomplish the same thing, e.g. by attaching one listener individually to * every Probe with the desired tag.
  • *

    *

  • A collection of {@linkplain Tool Tools}, possibly client-provided, that can be * {@linkplain #install(Tool) installed} for data collection, possibly providing their own services * with the resulting information.
  • *
* * @since 0.8 or earlier */ @SuppressWarnings("deprecation") @Deprecated public final class Instrumenter { private static final boolean TRACE = false; private static final String TRACE_PREFIX = "Instrumenter: "; private static final PrintStream OUT = System.out; private static void trace(String format, Object... args) { if (TRACE) { OUT.println(TRACE_PREFIX + String.format(format, args)); } } private enum ToolState { /** Not yet installed, inert. */ UNINSTALLED, /** Installed, collecting data. */ ENABLED, /** Installed, not collecting data. */ DISABLED, /** Was installed, but now removed, inactive, and no longer usable. */ DISPOSED; } /** * {@linkplain Instrumenter Instrumentation}-based collectors of data during Guest Language * program execution. *

* Tools share a common life cycle: *

    *
  • A newly created tool is "UNINSTALLED"; it does nothing until * {@linkplain Instrumenter#install(Tool) installed} .
  • *
  • An installed tool becomes "ENABLED" and immediately begins attaching * {@linkplain ProbeInstrument instrumentation} to ASTs and collecting execution data.
  • *
  • A tool may only be installed once.
  • *
  • It is possible to install multiple instances of a tool, possibly (but not necessarily) * configured differently with respect to what data is being collected.
  • *
  • Once installed, a tool can be {@linkplain #setEnabled(boolean) "ENABLED" and "DISABLED"} * arbitrarily.
  • *
  • A disabled tool: *
      *
    • Collects no data;
    • *
    • Retains existing AST instrumentation;
    • *
    • Continues to instrument newly created ASTs; and
    • *
    • Retains previously collected data.
    • *
    *
  • *
  • An installed tool may be {@linkplain #reset() reset} at any time, which leaves the tool * installed but with all previously collected data removed.
  • *
  • A {@linkplain #dispose() disposed} tool removes all instrumentation (but not * {@linkplain Probe probes}) and becomes permanently disabled; previously collected data * persists.
  • *
*

* Tool-specific methods that access data collected by the tool should: *

    *
  • Return modification-safe representations of the data; and
  • *
  • Not change the state of the data.
  • *
* * @since 0.8 or earlier */ public abstract static class Tool { private ToolState toolState = ToolState.UNINSTALLED; private Instrumenter instrumenter; /** @since 0.8 or earlier */ protected Tool() { } final void install(Instrumenter inst) { checkUninstalled(); this.instrumenter = inst; if (internalInstall()) { toolState = ToolState.ENABLED; } instrumenter.tools.add(this); } /** * @return whether the tool is currently collecting data. * @since 0.8 or earlier */ public final boolean isEnabled() { return toolState == ToolState.ENABLED; } /** * Switches tool state between enabled (collecting data) and disabled (not * collecting data, but keeping data already collected). * * @throws IllegalStateException if not yet installed or disposed. * @since 0.8 or earlier */ public final void setEnabled(boolean isEnabled) { checkInstalled(); internalSetEnabled(isEnabled); toolState = isEnabled ? ToolState.ENABLED : ToolState.DISABLED; } /** * Clears any data already collected, but otherwise does not change the state of the tool. * * @throws IllegalStateException if not yet installed or disposed. * @since 0.8 or earlier */ public final void reset() { checkInstalled(); internalReset(); } /** * Makes the tool permanently disabled, removes instrumentation, but keeps data * already collected. * * @throws IllegalStateException if not yet installed or disposed. * @since 0.8 or earlier */ public final void dispose() { checkInstalled(); internalDispose(); toolState = ToolState.DISPOSED; instrumenter.tools.remove(this); } /** * @return whether the installation succeeded. * @since 0.8 or earlier */ protected abstract boolean internalInstall(); /** * No subclass action required. * * @param isEnabled * @since 0.8 or earlier */ protected void internalSetEnabled(boolean isEnabled) { } /** @since 0.8 or earlier */ protected abstract void internalReset(); /** @since 0.8 or earlier */ protected abstract void internalDispose(); /** @since 0.8 or earlier */ protected final Instrumenter getInstrumenter() { return instrumenter; } /** * Ensure that the tool is currently installed. * * @throws IllegalStateException */ private void checkInstalled() throws IllegalStateException { if (toolState == ToolState.UNINSTALLED) { throw new IllegalStateException("Tool " + getClass().getSimpleName() + " not yet installed"); } if (toolState == ToolState.DISPOSED) { throw new IllegalStateException("Tool " + getClass().getSimpleName() + " has been disposed"); } } /** * Ensure that the tool has not yet been installed. * * @throws IllegalStateException */ private void checkUninstalled() { if (toolState != ToolState.UNINSTALLED) { throw new IllegalStateException("Tool " + getClass().getSimpleName() + " has already been installed"); } } } private final Object vm; /** Tools that have been created, but not yet disposed. */ Set tools = new HashSet<>(); private final Set astProbers = Collections.synchronizedSet(new LinkedHashSet()); private final List probeListeners = new ArrayList<>(); /** * All Probes that have been created. */ private final List> probes = new ArrayList<>(); /** * A global instrument that triggers notification just before executing any Node that is Probed * with a matching tag. */ @CompilationFinal private com.oracle.truffle.api.instrument.TagInstrument.BeforeTagInstrument beforeTagInstrument = null; /** * A global instrument that triggers notification just after executing any Node that is Probed * with a matching tag. */ @CompilationFinal private com.oracle.truffle.api.instrument.TagInstrument.AfterTagInstrument afterTagInstrument = null; Instrumenter(Object vm) { this.vm = vm; } /** * Prepares an AST node for {@linkplain ProbeInstrument instrumentation}, where the node is * presumed to be part of a well-formed Truffle AST that has not yet been executed. *

* Probing a node is idempotent: *

    *
  • If the node has not been Probed, modifies the AST by first inserting a * {@linkplain #createWrapperNode(Node) wrapper node} between the node and its parent and then * returning the newly created Probe associated with the wrapper.
  • *
  • If the node has been Probed, returns the Probe associated with its existing wrapper.
  • *
  • No more than one {@link Probe} may be associated with a node, so a wrapper may not wrap * another wrapper.
  • *
* It is a runtime error to attempt Probing an AST node with no parent. * * @return a (possibly newly created) {@link Probe} associated with this node. * @throws ProbeException (unchecked) when a Probe cannot be created, leaving the AST unchanged * @since 0.8 or earlier */ @SuppressWarnings("rawtypes") public Probe probe(Node node) { final Node parent = node.getParent(); if (node instanceof WrapperNode) { throw new ProbeException(ProbeFailure.Reason.WRAPPER_NODE, null, node, null); } if (parent == null) { throw new ProbeException(ProbeFailure.Reason.NO_PARENT, null, node, null); } if (parent instanceof WrapperNode) { final WrapperNode wrapper = (WrapperNode) parent; if (TRACE) { final Probe probe = wrapper.getProbe(); final SourceSection sourceSection = wrapper.getChild().getSourceSection(); final String location = sourceSection == null ? "" : sourceSection.getShortDescription(); trace("PROBE FOUND %s %s %s", "Probe@", location, probe.getTagsDescription()); } return wrapper.getProbe(); } if (!ACCESSOR.isInstrumentable(vm, node)) { throw new ProbeException(ProbeFailure.Reason.NOT_INSTRUMENTABLE, parent, node, null); } // Create a new wrapper/Probe with this node as its child. final WrapperNode wrapper = createWrapperNode(node); if (wrapper == null || !(wrapper instanceof Node)) { throw new ProbeException(ProbeFailure.Reason.NO_WRAPPER, parent, node, wrapper); } final Node wrapperNode = (Node) wrapper; if (!node.isSafelyReplaceableBy(wrapperNode)) { throw new ProbeException(ProbeFailure.Reason.WRAPPER_TYPE, parent, node, wrapper); } final SourceSection sourceSection = wrapper.getChild().getSourceSection(); final ProbeNode probeNode = new ProbeNode(); Class l = AccessorInstrument.nodesAccess().findLanguage(wrapper.getChild().getRootNode()); final Probe probe = new Probe(this, l, probeNode, sourceSection); probes.add(new WeakReference<>(probe)); probeNode.probe = probe; // package private access wrapper.insertEventHandlerNode(probeNode); node.replace(wrapperNode); if (TRACE) { final String location = sourceSection == null ? "" : sourceSection.getShortDescription(); trace("PROBED %s %s %s", "Probe@", location, probe.getTagsDescription()); } for (ProbeListener listener : probeListeners) { listener.newProbeInserted(probe); } return probe; } /** * Adds a {@link ProbeListener} to receive events. * * @since 0.8 or earlier */ public void addProbeListener(ProbeListener listener) { assert listener != null; probeListeners.add(listener); } /** * Removes a {@link ProbeListener}. Ignored if listener not found. * * @since 0.8 or earlier */ public void removeProbeListener(ProbeListener listener) { probeListeners.remove(listener); } /** * Returns all {@link Probe}s holding a particular {@link SyntaxTag}, or the whole collection of * probes if the specified tag is {@code null}. * * @return A collection of probes containing the given tag. * @since 0.8 or earlier */ public Collection findProbesTaggedAs(SyntaxTag tag) { final List taggedProbes = new ArrayList<>(); for (WeakReference ref : probes) { Probe probe = ref.get(); if (probe != null) { if (tag == null || probe.isTaggedAs(tag)) { taggedProbes.add(ref.get()); } } } return taggedProbes; } /** * Enables instrumentation at selected nodes in all subsequently constructed ASTs. Ignored if * the argument is already registered, runtime error if argument is {@code null}. * * @since 0.8 or earlier */ public void registerASTProber(ASTProber prober) { if (prober == null) { throw new IllegalArgumentException("Register non-null ASTProbers"); } astProbers.add(prober); } /** @since 0.8 or earlier */ public void unregisterASTProber(ASTProber prober) { astProbers.remove(prober); } /** * Attaches a {@link SimpleInstrumentListener listener} to a {@link Probe}, creating a * binding called an {@link ProbeInstrument}. Until the Instrument is * {@linkplain ProbeInstrument#dispose() disposed}, it routes synchronous notification of * {@linkplain EventHandlerNode execution events} taking place at the Probe's AST location to * the listener. * * @param probe source of AST execution events, non-null * @param listener receiver of execution events * @param instrumentInfo optional documentation about the Instrument * @return a handle for access to the binding * @since 0.8 or earlier */ public ProbeInstrument attach(Probe probe, SimpleInstrumentListener listener, String instrumentInfo) { assert probe.getInstrumenter() == this; final ProbeInstrument instrument = new ProbeInstrument.SimpleInstrument(listener, instrumentInfo); probe.attach(instrument); return instrument; } /** * Attaches a {@link StandardInstrumentListener listener} to a {@link Probe}, creating * a binding called an {@link ProbeInstrument}. Until the Instrument is * {@linkplain ProbeInstrument#dispose() disposed}, it routes synchronous notification of * {@linkplain EventHandlerNode execution events} taking place at the Probe's AST location to * the listener. * * @param probe source of AST execution events, non-null * @param listener receiver of execution events * @param instrumentInfo optional documentation about the Instrument * @return a handle for access to the binding * @since 0.8 or earlier */ public ProbeInstrument attach(Probe probe, StandardInstrumentListener listener, String instrumentInfo) { assert probe.getInstrumenter() == this; final ProbeInstrument instrument = new ProbeInstrument.StandardInstrument(listener, instrumentInfo); probe.attach(instrument); return instrument; } /** * Attaches a fragment of source text that is to be evaluated just before execution * enters the location of a {@link Probe}, creating a binding called an * {@link ProbeInstrument}. The outcome of the evaluation is reported to an optional * {@link EvalInstrumentListener listener}, but the outcome does not affect the flow of guest * language execution, even if the evaluation produces an exception. *

* The source text is assumed to be expressed in the language identified by its associated * {@linkplain Source#getMimeType() MIME type}, if specified, otherwise by the language * associated with the AST location associated with the {@link Probe}. *

* The source text is parsed in the lexical context of the AST location associated with the * {@link Probe}. *

* The source text executes subject to full Truffle optimization. * * @param probe source of AST execution events, non-null * @param languageClass the language in which the source text is to be executed * @param source the source code to be evaluated, non-null and non-empty * @param listener optional client callback for results/failure notification * @param instrumentInfo instrumentInfo optional documentation about the Instrument * @return a handle for access to the binding * @deprecated * @since 0.8 or earlier */ @Deprecated public ProbeInstrument attach(Probe probe, Class> languageClass, Source source, EvalInstrumentListener listener, String instrumentInfo) { return attach(probe, languageClass, source, listener, instrumentInfo, new String[0], new Object[0]); } /** * Attaches a fragment of source text that is to be evaluated just before execution * enters the location of a {@link Probe}, creating a binding called an * {@link ProbeInstrument}. The outcome of the evaluation is reported to an optional * {@link EvalInstrumentListener listener}, but the outcome does not affect the flow of guest * language execution, even if the evaluation produces an exception. *

* The source text is assumed to be expressed in the language identified by its associated * {@linkplain Source#getMimeType() MIME type}, if specified, otherwise by the language * associated with the AST location associated with the {@link Probe}. *

* The source text is parsed in the lexical context of the AST location associated with the * {@link Probe}. *

* The source text executes subject to full Truffle optimization. * * @param probe source of AST execution events, non-null * @param source the source code to be evaluated, non-null and non-empty, preferably with * {@link Source#withMimeType(java.lang.String) specified mime type} that determines * the {@link TruffleLanguage} to use when processing the source * @param listener optional client callback for results/failure notification * @param instrumentInfo instrumentInfo optional documentation about the Instrument * @param parameters keys are the parameter names to pass to * {@link TruffleLanguage#parse(com.oracle.truffle.api.source.Source, com.oracle.truffle.api.nodes.Node, java.lang.String...) * parse} method; values will be passed to * {@link CallTarget#call(java.lang.Object...)} returned from the parse * method; the value can be null * @return a handle for access to the binding * @since 0.8 or earlier */ public ProbeInstrument attach(Probe probe, Source source, EvalInstrumentListener listener, String instrumentInfo, Map parameters) { final int size = parameters == null ? 0 : parameters.size(); String[] names = new String[size]; Object[] params = new Object[size]; if (parameters != null) { int index = 0; for (Map.Entry entry : parameters.entrySet()) { names[index] = entry.getKey(); params[index] = entry.getValue(); index++; } } return attach(probe, null, source, listener, instrumentInfo, names, params); } private ProbeInstrument attach(Probe probe, Class> languageClass, Source source, EvalInstrumentListener listener, String instrumentInfo, String[] argumentNames, Object[] parameters) { assert probe.getInstrumenter() == this; Class> foundLanguageClass = null; if (languageClass == null) { if (source.getMimeType() == null) { foundLanguageClass = Util.toClass(probe.getLanguage()); } } else { foundLanguageClass = languageClass; } final com.oracle.truffle.api.instrument.ProbeInstrument.EvalInstrument instrument = new com.oracle.truffle.api.instrument.ProbeInstrument.EvalInstrument(foundLanguageClass, source, listener, instrumentInfo, argumentNames, parameters); probe.attach(instrument); return instrument; } /** * Sets the current "before" TagInstrument; there can be no more than one in effect. *

    *
  • The Instrument triggers a callback just before execution * reaches any {@link Probe} (either existing or subsequently created) * with the specified {@link SyntaxTag}.
  • *
  • Calling {@link TagInstrument#dispose()} removes the instrument.
  • *
* * @param tag identifies the nodes to be instrumented * @param listener receiver of before execution events * @param instrumentInfo optional, mainly for debugging. * @return a newly created, active Instrument * @throws IllegalStateException if called when a before Instrument is active. * @since 0.8 or earlier */ public TagInstrument attach(SyntaxTag tag, StandardBeforeInstrumentListener listener, String instrumentInfo) { if (beforeTagInstrument != null) { throw new IllegalStateException("Only one 'before' TagInstrument at a time"); } this.beforeTagInstrument = new TagInstrument.BeforeTagInstrument(this, tag, listener, instrumentInfo); notifyTagInstrumentChange(); return beforeTagInstrument; } /** * Sets the current "after" TagInstrument; there can be no more than one in effect. *
    *
  • The Instrument triggers a callback just after execution reaches * any {@link Probe} (either existing or subsequently created) with * the specified {@link SyntaxTag}.
  • *
  • Calling {@link TagInstrument#dispose()} removes the instrument.
  • *
* * @param tag identifies the nodes to be instrumented * @param listener receiver of after execution events * @param instrumentInfo optional, mainly for debugging. * @return a newly created, active Instrument * @throws IllegalStateException if called when a after Instrument is active. * @since 0.8 or earlier */ public TagInstrument attach(SyntaxTag tag, StandardAfterInstrumentListener listener, String instrumentInfo) { if (afterTagInstrument != null) { throw new IllegalStateException("Only one 'afater' TagInstrument at a time"); } this.afterTagInstrument = new TagInstrument.AfterTagInstrument(this, tag, listener, instrumentInfo); notifyTagInstrumentChange(); return afterTagInstrument; } /** * Connects the tool to some part of the Truffle runtime, and enable data collection to start. * * @return the tool * @throws IllegalStateException if the tool has previously been installed or has been disposed. * @since 0.8 or earlier */ public Tool install(Tool tool) { tool.install(this); return tool; } @SuppressWarnings("unused") void executionStarted(Source s) { } void executionEnded() { } WrapperNode createWrapperNode(Node node) { return ACCESSOR.createWrapperNode(vm, node); } void tagAdded(Probe probe, SyntaxTag tag, Object tagValue) { for (ProbeListener listener : probeListeners) { listener.probeTaggedAs(probe, tag, tagValue); } } com.oracle.truffle.api.instrument.TagInstrument.BeforeTagInstrument getBeforeTagInstrument() { return beforeTagInstrument; } com.oracle.truffle.api.instrument.TagInstrument.AfterTagInstrument getAfterTagInstrument() { return afterTagInstrument; } void disposeBeforeTagInstrument() { beforeTagInstrument = null; notifyTagInstrumentChange(); } void disposeAfterTagInstrument() { afterTagInstrument = null; notifyTagInstrumentChange(); } private void notifyTagInstrumentChange() { for (WeakReference ref : probes) { final Probe probe = ref.get(); if (probe != null) { probe.notifyTagInstrumentsChanged(); } } } /** * Enables instrumentation in a newly created AST by applying all registered instances of * {@link ASTProber}. */ private void probeAST(RootNode rootNode) { if (!astProbers.isEmpty()) { String name = ""; final SourceSection sourceSection = rootNode.getSourceSection(); if (sourceSection != null) { final Source source = sourceSection.getSource(); if (source != null) { name = source.getShortName(); } else { name = sourceSection.getShortDescription(); } } trace("START %s", name); for (ProbeListener listener : probeListeners) { listener.startASTProbing(rootNode); } for (ASTProber prober : astProbers) { prober.probeAST(this, rootNode); } for (ProbeListener listener : probeListeners) { listener.endASTProbing(rootNode); } trace("FINISHED %s", name); } } static final class AccessorInstrument extends Accessor { @Deprecated @SuppressWarnings({"rawtypes"}) boolean isInstrumentable(Object vm, Node node) { final RootNode rootNode = node.getRootNode(); Class languageClazz = nodesAccess().findLanguage(rootNode); TruffleLanguage language = engineSupport().findLanguageImpl(vm, languageClazz, null); return languageSupport().isInstrumentable(node, language); } /** * Provided by each {@linkplain TruffleLanguage language implementation}. */ @Deprecated @SuppressWarnings({"rawtypes"}) com.oracle.truffle.api.instrument.WrapperNode createWrapperNode(Object vm, Node node) { final RootNode rootNode = node.getRootNode(); Class languageClazz = nodesAccess().findLanguage(rootNode); TruffleLanguage language = engineSupport().findLanguageImpl(vm, languageClazz, null); return (com.oracle.truffle.api.instrument.WrapperNode) languageSupport().createWrapperNode(node, language); } static Accessor.Nodes nodesAccess() { return ACCESSOR.nodes(); } @SuppressWarnings("rawtypes") protected CallTarget parse(Class languageClass, Source code, Node context, String... argumentNames) throws IOException { final TruffleLanguage truffleLanguage = engineSupport().findLanguageImpl(null, languageClass, code.getMimeType()); return languageSupport().parse(truffleLanguage, code, context, argumentNames); } @Override protected OldInstrumentSupport oldInstrumentSupport() { return new OldInstrumentSupport() { @Override public void probeAST(RootNode rootNode) { final EngineSupport engineSupport = engineSupport(); // Normally null vm argument; can be reflectively set for testing Instrumenter instrumenter = (Instrumenter) (engineSupport != null ? engineSupport.getInstrumenter(testVM) : null); if (instrumenter != null) { instrumenter.probeAST(rootNode); } } }; } } static final AccessorInstrument ACCESSOR = new AccessorInstrument(); // Normally null; set for testing where the Accessor hasn't been fully initialized private static Object testVM = null; }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy