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

org.graalvm.tools.lsp.server.request.CoverageRequestHandler Maven / Gradle / Ivy

/*
 * Copyright (c) 2020, 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 org.graalvm.tools.lsp.server.request;

import java.net.URI;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Function;

import org.graalvm.tools.lsp.server.ContextAwareExecutor;
import org.graalvm.tools.lsp.exceptions.DiagnosticsNotification;
import org.graalvm.tools.lsp.server.types.Coverage;
import org.graalvm.tools.lsp.server.types.Diagnostic;
import org.graalvm.tools.lsp.server.types.DiagnosticSeverity;
import org.graalvm.tools.lsp.server.types.Range;
import org.graalvm.tools.lsp.server.utils.CoverageEventNode;
import org.graalvm.tools.lsp.server.utils.SourceSectionReference;
import org.graalvm.tools.lsp.server.utils.SourcePredicateBuilder;
import org.graalvm.tools.lsp.server.utils.SourceUtils;
import org.graalvm.tools.lsp.server.utils.TextDocumentSurrogate;
import org.graalvm.tools.lsp.server.utils.TextDocumentSurrogateMap;
import org.graalvm.tools.lsp.server.utils.ThreadId;

import com.oracle.truffle.api.CallTarget;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.instrumentation.EventBinding;
import com.oracle.truffle.api.instrumentation.EventContext;
import com.oracle.truffle.api.instrumentation.ExecutionEventNode;
import com.oracle.truffle.api.instrumentation.ExecutionEventNodeFactory;
import com.oracle.truffle.api.instrumentation.LoadSourceSectionEvent;
import com.oracle.truffle.api.instrumentation.LoadSourceSectionListener;
import com.oracle.truffle.api.instrumentation.SourceSectionFilter;
import com.oracle.truffle.api.instrumentation.SourceSectionFilter.SourcePredicate;
import com.oracle.truffle.api.instrumentation.StandardTags.StatementTag;
import com.oracle.truffle.api.instrumentation.TruffleInstrument.Env;
import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.interop.UnsupportedMessageException;
import com.oracle.truffle.api.nodes.LanguageInfo;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.source.SourceSection;

public final class CoverageRequestHandler extends AbstractRequestHandler {

    private final SourceCodeEvaluator sourceCodeEvaluator;

    public CoverageRequestHandler(Env envMain, Env env, TextDocumentSurrogateMap surrogateMap, ContextAwareExecutor contextAwareExecutor, SourceCodeEvaluator sourceCodeEvaluator) {
        super(envMain, env, surrogateMap, contextAwareExecutor);
        this.sourceCodeEvaluator = sourceCodeEvaluator;
    }

    public Boolean runCoverageAnalysisWithEnteredContext(final URI uri) throws DiagnosticsNotification {
        final TextDocumentSurrogate surrogateOfOpenedFile = surrogateMap.get(uri);

        if (surrogateOfOpenedFile == null) {
            return Boolean.FALSE;
        }

        TextDocumentSurrogate surrogateOfTestFile = sourceCodeEvaluator.createSurrogateForTestFile(surrogateOfOpenedFile, null);
        final URI runScriptUri = surrogateOfTestFile.getUri();

        clearRelatedCoverageData(runScriptUri);

        try {
            final CallTarget callTarget = sourceCodeEvaluator.parse(surrogateOfTestFile);
            LanguageInfo languageInfo = surrogateOfTestFile.getLanguageInfo();
            SourcePredicate predicate = SourcePredicateBuilder.newBuilder().language(languageInfo).excludeInternal(env.getOptions()).build();
            SourceSectionFilter eventFilter = SourceSectionFilter.newBuilder().sourceIs(predicate).build();
            EventBinding eventFactoryBinding = env.getInstrumenter().attachExecutionEventFactory(
                            eventFilter,
                            new ExecutionEventNodeFactory() {
                                private final long creatorThreadId = ThreadId.getCurrent();

                                @Override
                                public ExecutionEventNode create(final EventContext eventContext) {
                                    final SourceSection section = eventContext.getInstrumentedSourceSection();
                                    if (section != null && section.isAvailable()) {
                                        final Node instrumentedNode = eventContext.getInstrumentedNode();
                                        Function func = (sourceUri) -> {
                                            return surrogateMap.getOrCreateSurrogate(sourceUri, () -> instrumentedNode.getRootNode().getLanguageInfo());
                                        };

                                        return new CoverageEventNode(section, instrumentedNode, runScriptUri, func, creatorThreadId);
                                    } else {
                                        return null;
                                    }
                                }
                            });
            try {
                callTarget.call();
            } finally {
                eventFactoryBinding.dispose();
            }

            surrogateOfOpenedFile.setCoverageAnalysisDone(true);

            return Boolean.TRUE;
        } catch (DiagnosticsNotification e) {
            throw e;
        } catch (Exception e) {
            InteropLibrary interopLib = InteropLibrary.getUncached();
            if (interopLib.isException(e)) {
                SourceSection sourceSection;
                try {
                    sourceSection = interopLib.hasSourceLocation(e) ? interopLib.getSourceLocation(e) : null;
                } catch (UnsupportedMessageException um) {
                    throw CompilerDirectives.shouldNotReachHere(um);
                }
                URI uriOfErronousSource = sourceSection != null ? sourceSection.getSource().getURI() : null;
                if (uriOfErronousSource == null) {
                    uriOfErronousSource = uri;
                }
                throw DiagnosticsNotification.create(uriOfErronousSource,
                                Diagnostic.create(SourceUtils.getRangeFrom(e, interopLib), e.getMessage(), DiagnosticSeverity.Error, null, "Coverage analysis", null));
            }

            throw e;
        }
    }

    /**
     * Clears all coverage data from previous runs, which was collected by running the runScriptUri
     * resource. This avoids clearing coverage data collected by other script runs.
     *
     * Also clears all coverage data for the runScriptUri resource itself.
     *
     * @param runScriptUri URI of the script to kick-off the coverage analysis
     */
    private void clearRelatedCoverageData(final URI runScriptUri) {
        TextDocumentSurrogate surrogateOfRunScript = surrogateMap.get(runScriptUri);
        assert surrogateOfRunScript != null;
        surrogateOfRunScript.clearCoverage();
        surrogateMap.getSurrogates().stream().forEach(surrogate -> surrogate.clearCoverage(runScriptUri));
    }

    public Coverage getCoverageWithEnteredContext(URI uri) {
        final TextDocumentSurrogate surrogate = surrogateMap.get(uri);
        if (surrogate != null && surrogate.getSourceWrapper() != null && surrogate.getSourceWrapper().isParsingSuccessful()) {
            final SourceSectionFilter filter = SourceSectionFilter.newBuilder() //
                            .sourceIs(surrogate.getSourceWrapper().getSource()) //
                            .tagIs(StatementTag.class) //
                            .build();
            final Set duplicateFilter = new HashSet<>();
            final List covered = new ArrayList<>();
            final List uncovered = new ArrayList<>();
            env.getInstrumenter().attachLoadSourceSectionListener(filter, new LoadSourceSectionListener() {

                @Override
                public void onLoad(LoadSourceSectionEvent event) {
                    SourceSection section = event.getSourceSection();
                    if (duplicateFilter.add(section)) {
                        if (surrogate.isLocationCovered(SourceSectionReference.from(section))) {
                            covered.add(SourceUtils.sourceSectionToRange(section));
                        } else {
                            uncovered.add(SourceUtils.sourceSectionToRange(section));
                        }
                    }
                }
            }, true).dispose();
            return Coverage.create(covered, uncovered);
        }
        return null;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy