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

org.netbeans.modules.javascript.cdtdebug.CDTDebugger Maven / Gradle / Ivy

The newest version!
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.netbeans.modules.javascript.cdtdebug;

import java.io.IOException;
import java.net.URI;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.netbeans.api.annotations.common.NullAllowed;
import org.netbeans.api.debugger.DebuggerEngine;
import org.netbeans.api.debugger.DebuggerInfo;
import org.netbeans.api.debugger.DebuggerManager;
import org.netbeans.lib.chrome_devtools_protocol.ChromeDevToolsClient;
import org.netbeans.lib.chrome_devtools_protocol.Unregisterer;
import org.netbeans.lib.chrome_devtools_protocol.debugger.CallFrame;
import org.netbeans.lib.chrome_devtools_protocol.debugger.PauseRequest;
import org.netbeans.lib.chrome_devtools_protocol.debugger.ResumeRequest;
import org.netbeans.lib.chrome_devtools_protocol.json.Endpoint;
import org.netbeans.modules.javascript.cdtdebug.api.Connector;
import org.netbeans.modules.javascript.cdtdebug.breakpoints.BreakpointsHandler;
import org.netbeans.modules.javascript.cdtdebug.sources.ChangeLiveSupport;
import org.openide.util.Exceptions;

public class CDTDebugger {

    private static final Logger LOG = Logger.getLogger(CDTDebugger.class.getName());

    private final Set listeners = new CopyOnWriteArraySet<>();
    private final Endpoint endpoint;

    private final ChromeDevToolsClient connection;
    private final ScriptsHandler scriptsHandler;
    private final BreakpointsHandler breakpointsHandler;
     private final ChangeLiveSupport changeLiveSupport;
    private final Runnable finishCallback;
    private boolean finished;
    private boolean suspended;
    private DebuggerEngine engine;
    private DebuggerEngine.Destructor engineDestructor;
    private CallFrame currentFrame;
    private List currentCallStack;

    public static DebuggerEngine startSession(
            Connector.Properties properties,
            @NullAllowed Runnable finishCallback
    ) throws IOException {

        String host = properties.getHostName();
        if(host == null || host.isBlank()) {
            host = "127.0.0.1";
        }

        int port = properties.getPort();

        Endpoint endpoint = null;
        String targetIdentifier = properties.getTargetIdentifier();

        for(Endpoint ep: ChromeDevToolsClient.listEndpoints(host, port)) {
            if(targetIdentifier == null || targetIdentifier.isBlank() || ep.getId().equals(targetIdentifier)) {
                endpoint = ep;
                break;
            }
        }

        CDTDebugger dbg = new CDTDebugger(endpoint, properties, finishCallback);
        DebuggerInfo dInfo = DebuggerInfo.create(CDTDebuggerSessionProvider.DEBUG_INFO, new Object[] {dbg});
        DebuggerEngine[] engines = DebuggerManager.getDebuggerManager().startDebugging(dInfo);
        if (engines.length > 0) {
            dbg.setEngine(engines[0]);
            return engines[0];
        } else {
            if (finishCallback != null) {
                finishCallback.run();
            }
            return null;
        }
    }



    private CDTDebugger(Endpoint endpoint, Connector.Properties properties, Runnable finishCallback) {
        this.finishCallback = finishCallback;
        this.endpoint = endpoint;
        this.connection = new ChromeDevToolsClient(endpoint.getWebSocketDebuggerUrl());
        this.scriptsHandler = new ScriptsHandler(
                properties.getLocalPaths(),
                properties.getServerPaths(),
                properties.getLocalPathExclusionFilters(),
                this
        );
        this.breakpointsHandler = new BreakpointsHandler(this);
        this.changeLiveSupport = new ChangeLiveSupport(this);
        connection.getDebugger().onScriptFailedToParse((dto) -> scriptsHandler.add(new CDTScript(dto)));
        connection.getDebugger().onScriptParsed((dto) -> scriptsHandler.add(new CDTScript(dto)));
        connection.getDebugger().onPaused((paused) -> {
            this.suspended = true;
            currentFrame = paused.getCallFrames().get(0);
            currentCallStack = paused.getCallFrames();
            listeners.forEach(c -> {
                c.notifyCurrentFrame(paused.getCallFrames().get(0));
                c.notifySuspended(true);
            });
        });
        connection.getDebugger().onResumed((resumed) -> {
            currentFrame = null;
            currentCallStack = null;
            listeners.forEach(c -> {
                c.notifyCurrentFrame(null);
                c.notifySuspended(false);
            });
            this.suspended = false;
        });
        try {
            connection.connect();
            connection.getDebugger().enable(null).toCompletableFuture().get();
        } catch (InterruptedException ex) {
            Exceptions.printStackTrace(ex);
        } catch (ExecutionException ex) {
            Exceptions.printStackTrace(ex);
        }
    }

    public URI getWebSocketDebuggerUrl() {
        return endpoint.getWebSocketDebuggerUrl();
    }

    public void start() {
        connection.getRuntime().runIfWaitingForDebugger();
    }

    public void suspend() {
        ChromeDevToolsClient cdtc = connection;
        if(cdtc != null) {
            cdtc.getDebugger().pause(new PauseRequest());
        }
    }

    public void resume() {
        ChromeDevToolsClient cdtc = connection;
        if(cdtc != null) {
            cdtc.getDebugger().resume(new ResumeRequest());
        }
    }

    public void finish() {
        if (finished) {
            return ;
        }
        finished = true;
        try {
            connection.close();
        } catch (IOException ioex) {}
        listeners.forEach(cl -> cl.notifyFinished());
        engineDestructor.killEngine();
        if (finishCallback != null) {
            finishCallback.run();
        }
    }

    private static void unregister(Unregisterer registerer) {
        if (registerer != null) {
            try {
                registerer.close();
            } catch (RuntimeException ex) {
                LOG.log(Level.WARNING, "Failed to unregister", ex);
            }
        }
    }

    public ChromeDevToolsClient getConnection() {
        return connection;
    }

    public ScriptsHandler getScriptsHandler() {
        return scriptsHandler;
    }

    public BreakpointsHandler getBreakpointsHandler() {
        return breakpointsHandler;
    }

    public ChangeLiveSupport getChangeLiveSupport() {
        return changeLiveSupport;
    }

    public void addListener(Listener listener) {
        listeners.add(listener);
    }

    public void removeListener(Listener listener) {
        listeners.remove(listener);
    }

    public boolean isSuspended() {
        return suspended;
    }

    public boolean isFinished() {
        return finished;
    }

    public CallFrame getCurrentFrame() {
        return currentFrame;
    }

    public void setCurrentFrame(CallFrame currentFrame) {
        this.currentFrame = currentFrame;
        listeners.forEach(cl -> cl.notifyCurrentFrame(currentFrame));
    }

    public List getCurrentCallStack() {
        return currentCallStack;
    }

    DebuggerEngine getEngine() {
        return engine;
    }

    void setEngine(DebuggerEngine engine) {
        this.engine = engine;
    }

    void setEngineDestructor(DebuggerEngine.Destructor destructor) {
        this.engineDestructor = destructor;
    }

    public interface Listener {

        void notifySuspended(boolean suspended);

        void notifyCurrentFrame(@NullAllowed CallFrame cf);

        void notifyFinished();

    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy