refactor(debug): 从DebugToolFragment中分离debug逻辑
This commit is contained in:
@@ -1,20 +1,5 @@
|
||||
package com.stardust.autojs.rhino.debug;
|
||||
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
|
||||
import com.stardust.autojs.engine.RhinoJavaScriptEngine;
|
||||
import com.stardust.autojs.engine.ScriptEngine;
|
||||
|
||||
import org.mozilla.javascript.Context;
|
||||
|
||||
/**
|
||||
* Interface for communication between the debugger and its GUI. This
|
||||
* should be implemented by the GUI.
|
||||
*/
|
||||
public interface DebugCallback {
|
||||
|
||||
/**
|
||||
@@ -29,22 +14,4 @@ public interface DebugCallback {
|
||||
String threadTitle,
|
||||
String alertMessage);
|
||||
|
||||
/**
|
||||
* Returns whether the current thread is the GUI's event thread.
|
||||
* This information is required to avoid blocking the event thread
|
||||
* from the debugger.
|
||||
*/
|
||||
boolean isGuiEventThread();
|
||||
|
||||
/**
|
||||
* Processes the next GUI event. This manual pumping of GUI events
|
||||
* is necessary when the GUI event thread itself has been stopped.
|
||||
*/
|
||||
void dispatchNextGuiEvent() throws InterruptedException;
|
||||
|
||||
/**
|
||||
*
|
||||
* Returns whether the debugger should attach to this engine or not.
|
||||
*/
|
||||
boolean shouldAttachDebugger(RhinoJavaScriptEngine engine);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
package com.stardust.autojs.rhino.debug;
|
||||
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
|
||||
import com.stardust.autojs.engine.RhinoJavaScriptEngine;
|
||||
import com.stardust.autojs.engine.ScriptEngine;
|
||||
|
||||
import org.mozilla.javascript.Context;
|
||||
|
||||
/**
|
||||
* Interface for communication between the debugger and its GUI. This
|
||||
* should be implemented by the GUI.
|
||||
*/
|
||||
interface DebugCallbackInternal {
|
||||
|
||||
/**
|
||||
* Called when the source text of some script has been changed.
|
||||
*/
|
||||
void updateSourceText(Dim.SourceInfo sourceInfo);
|
||||
|
||||
/**
|
||||
* Called when the interrupt loop has been entered.
|
||||
*/
|
||||
void enterInterrupt(Dim.StackFrame lastFrame,
|
||||
String threadTitle,
|
||||
String alertMessage);
|
||||
|
||||
/**
|
||||
* Returns whether the current thread is the GUI's event thread.
|
||||
* This information is required to avoid blocking the event thread
|
||||
* from the debugger.
|
||||
*/
|
||||
boolean isGuiEventThread();
|
||||
|
||||
/**
|
||||
* Processes the next GUI event. This manual pumping of GUI events
|
||||
* is necessary when the GUI event thread itself has been stopped.
|
||||
*/
|
||||
void dispatchNextGuiEvent() throws InterruptedException;
|
||||
|
||||
/**
|
||||
* Returns whether the debugger should attach to this engine or not.
|
||||
*/
|
||||
boolean shouldAttachDebugger(RhinoJavaScriptEngine engine);
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
package com.stardust.autojs.rhino.debug;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.util.Log;
|
||||
|
||||
import com.stardust.autojs.ScriptEngineService;
|
||||
import com.stardust.autojs.engine.RhinoJavaScriptEngine;
|
||||
import com.stardust.autojs.execution.ScriptExecution;
|
||||
|
||||
import org.mozilla.javascript.ContextFactory;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
public class Debugger implements DebugCallbackInternal {
|
||||
|
||||
|
||||
private static final String LOG_TAG = "Debugger";
|
||||
|
||||
private final ScriptEngineService mScriptEngineService;
|
||||
private final ContextFactory mContextFactory;
|
||||
|
||||
private final Handler mHandler = new Handler(Looper.getMainLooper());
|
||||
private final Dim mDim = createDim();
|
||||
|
||||
private DebugCallback mDebugCallback;
|
||||
|
||||
private boolean mSkipOtherFileBreakpoint = false;
|
||||
@Nullable
|
||||
private String mSourceUrl;
|
||||
@Nullable
|
||||
private ScriptExecution mScriptExecution;
|
||||
@Nullable
|
||||
private Dim.SourceInfo mSourceInfo;
|
||||
private WeakReference<DebugCallback> mWeakDebugCallback;
|
||||
|
||||
|
||||
public Debugger(ScriptEngineService scriptEngineService, ContextFactory contextFactory) {
|
||||
mScriptEngineService = scriptEngineService;
|
||||
mContextFactory = contextFactory;
|
||||
}
|
||||
|
||||
public void attach(ScriptExecution execution) {
|
||||
if(isAttached()){
|
||||
detach();
|
||||
}
|
||||
mScriptExecution = execution;
|
||||
mSkipOtherFileBreakpoint = true;
|
||||
mSourceUrl = execution.getSource().toString();
|
||||
mDim.attachTo(mScriptEngineService, mContextFactory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateSourceText(Dim.SourceInfo sourceInfo) {
|
||||
if (!sourceInfo.url().equals(mSourceUrl)) {
|
||||
return;
|
||||
}
|
||||
mSourceInfo = sourceInfo;
|
||||
if(mDebugCallback != null){
|
||||
mDebugCallback.updateSourceText(sourceInfo);
|
||||
}
|
||||
DebugCallback callback = mWeakDebugCallback == null ? null : mWeakDebugCallback.get();
|
||||
if(callback != null){
|
||||
callback.updateSourceText(sourceInfo);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enterInterrupt(Dim.StackFrame lastFrame, String threadTitle, String alertMessage) {
|
||||
Log.d(LOG_TAG, "enterInterrupt: threadName = " + threadTitle + ", url = " + lastFrame.getUrl() + ", line = " + lastFrame.getLineNumber());
|
||||
//刚启动调试时会在init脚本的第一行自动停下,此时应该让脚本继续运行
|
||||
if (mSkipOtherFileBreakpoint && !lastFrame.getUrl().equals(mSourceUrl) && alertMessage == null) {
|
||||
mHandler.post(this::resume);
|
||||
return;
|
||||
}
|
||||
mSkipOtherFileBreakpoint = false;
|
||||
if(mDebugCallback != null){
|
||||
mDebugCallback.enterInterrupt(lastFrame, threadTitle, alertMessage);
|
||||
}
|
||||
DebugCallback callback = mWeakDebugCallback == null ? null : mWeakDebugCallback.get();
|
||||
if(callback != null){
|
||||
callback.enterInterrupt(lastFrame, threadTitle, alertMessage);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isGuiEventThread() {
|
||||
return Looper.getMainLooper() == Looper.myLooper();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispatchNextGuiEvent() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldAttachDebugger(RhinoJavaScriptEngine engine) {
|
||||
return mScriptExecution != null && mScriptExecution.getId() == engine.getId();
|
||||
|
||||
}
|
||||
|
||||
public void breakpoint(int line, boolean enabled) {
|
||||
if (mSourceInfo != null) {
|
||||
mSourceInfo.breakpoint(line, enabled);
|
||||
}
|
||||
}
|
||||
|
||||
private Dim createDim() {
|
||||
Dim dim = new Dim();
|
||||
dim.setBreak();
|
||||
dim.setBreakOnExceptions(true);
|
||||
dim.setGuiCallback(this);
|
||||
return dim;
|
||||
}
|
||||
|
||||
public void resume() {
|
||||
mDim.setReturnValue(Dim.GO);
|
||||
}
|
||||
|
||||
public void stepOut() {
|
||||
mDim.setReturnValue(Dim.STEP_OUT);
|
||||
}
|
||||
|
||||
public void stepInto() {
|
||||
mDim.setReturnValue(Dim.STEP_INTO);
|
||||
}
|
||||
|
||||
public void stepOver() {
|
||||
mDim.setReturnValue(Dim.STEP_OVER);
|
||||
}
|
||||
|
||||
public boolean isAttached() {
|
||||
return mDim.isAttached();
|
||||
}
|
||||
|
||||
public String eval(String expr) {
|
||||
if (expr == null || !mDim.isAttached() || !mDim.stringIsCompilableUnit(expr)) {
|
||||
return null;
|
||||
}
|
||||
mDim.contextSwitch(0);
|
||||
return mDim.eval(expr);
|
||||
}
|
||||
|
||||
public void clearAllBreakpoints() {
|
||||
mDim.clearAllBreakpoints();
|
||||
}
|
||||
|
||||
public void detach() {
|
||||
mDim.detach();
|
||||
mScriptExecution = null;
|
||||
mSourceUrl = null;
|
||||
mSourceInfo = null;
|
||||
}
|
||||
|
||||
public void setDebugCallback(DebugCallback debugCallback) {
|
||||
mDebugCallback = debugCallback;
|
||||
}
|
||||
|
||||
public void setWeakDebugCallback(WeakReference<DebugCallback> debugCallback) {
|
||||
mWeakDebugCallback = debugCallback;
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,7 @@ import com.stardust.autojs.engine.ScriptEngineManager;
|
||||
import org.mozilla.javascript.*;
|
||||
import org.mozilla.javascript.debug.*;
|
||||
import org.mozilla.javascript.tools.debugger.*;
|
||||
import org.mozilla.javascript.debug.Debugger;
|
||||
|
||||
import java.util.*;
|
||||
import java.io.*;
|
||||
@@ -47,7 +48,7 @@ public class Dim {
|
||||
/**
|
||||
* Interface to the debugger GUI.
|
||||
*/
|
||||
private DebugCallback callback;
|
||||
private DebugCallbackInternal callback;
|
||||
|
||||
/**
|
||||
* Whether the debugger should break.
|
||||
@@ -162,7 +163,7 @@ public class Dim {
|
||||
/**
|
||||
* Sets the GuiCallback object to use.
|
||||
*/
|
||||
public void setGuiCallback(DebugCallback callback) {
|
||||
public void setGuiCallback(DebugCallbackInternal callback) {
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user