refactor(debug): 从DebugToolFragment中分离debug逻辑
This commit is contained in:
@@ -0,0 +1,64 @@
|
|||||||
|
package org.autojs.autojs.pluginclient;
|
||||||
|
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.PipedInputStream;
|
||||||
|
import java.net.Socket;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
public class JsonSocket {
|
||||||
|
|
||||||
|
private Socket mSocket;
|
||||||
|
|
||||||
|
public JsonSocket(Socket socket) {
|
||||||
|
mSocket = socket;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static class SocketReader implements Runnable {
|
||||||
|
|
||||||
|
private final Socket mSocket;
|
||||||
|
private final InputStream mInputStream;
|
||||||
|
private ByteBuffer mByteBuffer;
|
||||||
|
private int mJsonDataLength = -1;
|
||||||
|
private int mReceivedDataLength = 0;
|
||||||
|
private byte[] mBuffer;
|
||||||
|
|
||||||
|
private SocketReader(Socket socket) throws IOException {
|
||||||
|
mSocket = socket;
|
||||||
|
mInputStream = mSocket.getInputStream();
|
||||||
|
mByteBuffer = ByteBuffer.allocateDirect(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
|
||||||
|
try {
|
||||||
|
readLoop();
|
||||||
|
|
||||||
|
} catch (IOException e) {
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void readLoop() throws IOException {
|
||||||
|
byte[] buffer = new byte[4096];
|
||||||
|
int n;
|
||||||
|
while ((n = mInputStream.read(buffer)) > 0) {
|
||||||
|
onChunk(buffer, 0, n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onChunk(byte[] data, int offset, int length) {
|
||||||
|
if(mJsonDataLength != 0){
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ import android.support.design.widget.Snackbar;
|
|||||||
import android.text.InputType;
|
import android.text.InputType;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
import com.stardust.pio.PFiles;
|
import com.stardust.pio.PFiles;
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import com.afollestad.materialdialogs.MaterialDialog;
|
|||||||
import com.stardust.autojs.engine.JavaScriptEngine;
|
import com.stardust.autojs.engine.JavaScriptEngine;
|
||||||
import com.stardust.autojs.engine.ScriptEngine;
|
import com.stardust.autojs.engine.ScriptEngine;
|
||||||
import com.stardust.autojs.execution.ScriptExecution;
|
import com.stardust.autojs.execution.ScriptExecution;
|
||||||
|
import com.stardust.autojs.rhino.debug.Debugger;
|
||||||
import com.stardust.pio.PFiles;
|
import com.stardust.pio.PFiles;
|
||||||
import com.stardust.util.BackPressedHandler;
|
import com.stardust.util.BackPressedHandler;
|
||||||
import com.stardust.util.Callback;
|
import com.stardust.util.Callback;
|
||||||
@@ -386,12 +387,14 @@ public class EditorView extends FrameLayout implements CodeCompletionBar.OnHintC
|
|||||||
.subscribe(s -> run(true));
|
.subscribe(s -> run(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void run(boolean showMessage) {
|
public ScriptExecution run(boolean showMessage) {
|
||||||
if(showMessage){
|
if(showMessage){
|
||||||
Snackbar.make(this, R.string.text_start_running, Snackbar.LENGTH_SHORT).show();
|
Snackbar.make(this, R.string.text_start_running, Snackbar.LENGTH_SHORT).show();
|
||||||
}
|
}
|
||||||
mScriptExecutionId = Scripts.runWithBroadcastSender(mFile).getId();
|
ScriptExecution execution = Scripts.runWithBroadcastSender(mFile);
|
||||||
|
mScriptExecutionId = execution.getId();
|
||||||
setMenuItemStatus(R.id.run, false);
|
setMenuItemStatus(R.id.run, false);
|
||||||
|
return execution;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package org.autojs.autojs.ui.edit.debug;
|
||||||
|
|
||||||
|
import com.stardust.autojs.rhino.debug.Debugger;
|
||||||
|
|
||||||
|
import org.autojs.autojs.autojs.AutoJs;
|
||||||
|
import org.mozilla.javascript.ContextFactory;
|
||||||
|
|
||||||
|
public class DebuggerSingleton {
|
||||||
|
|
||||||
|
private static Debugger sDebugger = new Debugger(AutoJs.getInstance().getScriptEngineService(), ContextFactory.getGlobal());
|
||||||
|
|
||||||
|
public static Debugger get(){
|
||||||
|
return sDebugger;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -50,7 +50,7 @@ public class CodeEditor extends HVScrollView {
|
|||||||
|
|
||||||
|
|
||||||
private CodeEditText mCodeEditText;
|
private CodeEditText mCodeEditText;
|
||||||
private TextViewRedoUndo mTextViewRedoUndo;
|
private TextViewUndoRedo mTextViewRedoUndo;
|
||||||
private JavaScriptHighlighter mJavaScriptHighlighter;
|
private JavaScriptHighlighter mJavaScriptHighlighter;
|
||||||
private Theme mTheme;
|
private Theme mTheme;
|
||||||
private JsBeautifier mJsBeautifier;
|
private JsBeautifier mJsBeautifier;
|
||||||
@@ -77,7 +77,7 @@ public class CodeEditor extends HVScrollView {
|
|||||||
inflate(getContext(), R.layout.code_editor, this);
|
inflate(getContext(), R.layout.code_editor, this);
|
||||||
mCodeEditText = findViewById(R.id.code_edit_text);
|
mCodeEditText = findViewById(R.id.code_edit_text);
|
||||||
mCodeEditText.addTextChangedListener(new AutoIndent(mCodeEditText));
|
mCodeEditText.addTextChangedListener(new AutoIndent(mCodeEditText));
|
||||||
mTextViewRedoUndo = new TextViewRedoUndo(mCodeEditText);
|
mTextViewRedoUndo = new TextViewUndoRedo(mCodeEditText);
|
||||||
mJavaScriptHighlighter = new JavaScriptHighlighter(mTheme, mCodeEditText);
|
mJavaScriptHighlighter = new JavaScriptHighlighter(mTheme, mCodeEditText);
|
||||||
setTheme(Theme.getDefault(getContext()));
|
setTheme(Theme.getDefault(getContext()));
|
||||||
mJsBeautifier = new JsBeautifier(this, "js/beautify.js");
|
mJsBeautifier = new JsBeautifier(this, "js/beautify.js");
|
||||||
@@ -144,7 +144,7 @@ public class CodeEditor extends HVScrollView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean canUndo() {
|
public boolean canUndo() {
|
||||||
return mTextViewRedoUndo.canUndo();
|
return mTextViewRedoUndo.canRedo();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean canRedo() {
|
public boolean canRedo() {
|
||||||
|
|||||||
@@ -21,14 +21,13 @@ import android.text.Editable;
|
|||||||
import android.text.TextWatcher;
|
import android.text.TextWatcher;
|
||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.Stack;
|
import java.util.Stack;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 撤销和恢复撤销
|
* 撤销和恢复撤销
|
||||||
* Created by 沈钦赐 on 16/6/23.
|
* Created by 沈钦赐 on 16/6/23.
|
||||||
*/
|
*/
|
||||||
public class TextViewRedoUndo {
|
public class TextViewRedoUndoer {
|
||||||
//操作序号(一次编辑可能对应多个操作,如替换文字,就是删除+插入)
|
//操作序号(一次编辑可能对应多个操作,如替换文字,就是删除+插入)
|
||||||
int index;
|
int index;
|
||||||
//撤销栈
|
//撤销栈
|
||||||
@@ -44,7 +43,7 @@ public class TextViewRedoUndo {
|
|||||||
private int mInitialHistoryStackSize;
|
private int mInitialHistoryStackSize;
|
||||||
private boolean mEnabled = true;
|
private boolean mEnabled = true;
|
||||||
|
|
||||||
public TextViewRedoUndo(@NonNull EditText editText) {
|
public TextViewRedoUndoer(@NonNull EditText editText) {
|
||||||
this.editable = editText.getText();
|
this.editable = editText.getText();
|
||||||
this.editText = editText;
|
this.editText = editText;
|
||||||
editText.addTextChangedListener(new Watcher());
|
editText.addTextChangedListener(new Watcher());
|
||||||
@@ -246,7 +245,7 @@ public class TextViewRedoUndo {
|
|||||||
editable = s;
|
editable = s;
|
||||||
onEditableChanged(s);
|
onEditableChanged(s);
|
||||||
}
|
}
|
||||||
TextViewRedoUndo.this.onTextChanged(s);
|
TextViewRedoUndoer.this.onTextChanged(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,427 @@
|
|||||||
|
package org.autojs.autojs.ui.edit.editor;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* THIS CLASS IS PROVIDED TO THE PUBLIC DOMAIN FOR FREE WITHOUT ANY
|
||||||
|
* RESTRICTIONS OR ANY WARRANTY.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.util.LinkedList;
|
||||||
|
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.content.SharedPreferences.Editor;
|
||||||
|
import android.text.Editable;
|
||||||
|
import android.text.Selection;
|
||||||
|
import android.text.TextWatcher;
|
||||||
|
import android.text.style.UnderlineSpan;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A generic undo/redo implementation for TextViews.
|
||||||
|
*/
|
||||||
|
public class TextViewUndoRedo {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is undo/redo being performed? This member signals if an undo/redo
|
||||||
|
* operation is currently being performed. Changes in the text during
|
||||||
|
* undo/redo are not recorded because it would mess up the undo history.
|
||||||
|
*/
|
||||||
|
private boolean mIsUndoOrRedo = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The edit history.
|
||||||
|
*/
|
||||||
|
private EditHistory mEditHistory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The change listener.
|
||||||
|
*/
|
||||||
|
private EditTextChangeListener mChangeListener;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The edit text.
|
||||||
|
*/
|
||||||
|
private TextView mTextView;
|
||||||
|
|
||||||
|
private boolean mEnabled = true;
|
||||||
|
|
||||||
|
private int mInitialHistoryStackSize;
|
||||||
|
|
||||||
|
// =================================================================== //
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new TextViewUndoRedo and attach it to the specified TextView.
|
||||||
|
*
|
||||||
|
* @param textView The text view for which the undo/redo is implemented.
|
||||||
|
*/
|
||||||
|
public TextViewUndoRedo(TextView textView) {
|
||||||
|
mTextView = textView;
|
||||||
|
mEditHistory = new EditHistory();
|
||||||
|
mChangeListener = new EditTextChangeListener();
|
||||||
|
mTextView.addTextChangedListener(mChangeListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEnabled() {
|
||||||
|
return mEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEnabled(boolean enabled) {
|
||||||
|
mEnabled = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final void setDefaultText(CharSequence text) {
|
||||||
|
clearHistory();
|
||||||
|
mIsUndoOrRedo = true;
|
||||||
|
((Editable) mTextView.getText()).replace(0, text.length(), text);
|
||||||
|
mIsUndoOrRedo = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isTextChanged(){
|
||||||
|
return mInitialHistoryStackSize != mEditHistory.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void markTextAsUnchanged() {
|
||||||
|
mInitialHistoryStackSize = mEditHistory.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
// =================================================================== //
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disconnect this undo/redo from the text view.
|
||||||
|
*/
|
||||||
|
public void disconnect() {
|
||||||
|
mTextView.removeTextChangedListener(mChangeListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the maximum history size. If size is negative, then history size is
|
||||||
|
* only limited by the device memory.
|
||||||
|
*/
|
||||||
|
public void setMaxHistorySize(int maxHistorySize) {
|
||||||
|
mEditHistory.setMaxHistorySize(maxHistorySize);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear history.
|
||||||
|
*/
|
||||||
|
public void clearHistory() {
|
||||||
|
mEditHistory.clear();
|
||||||
|
mInitialHistoryStackSize = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Can undo be performed?
|
||||||
|
*/
|
||||||
|
public boolean canUndo() {
|
||||||
|
return (mEditHistory.mmPosition > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform undo.
|
||||||
|
*/
|
||||||
|
public void undo() {
|
||||||
|
EditItem edit = mEditHistory.getPrevious();
|
||||||
|
if (edit == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Editable text = mTextView.getEditableText();
|
||||||
|
int start = edit.mmStart;
|
||||||
|
int end = start + (edit.mmAfter != null ? edit.mmAfter.length() : 0);
|
||||||
|
|
||||||
|
mIsUndoOrRedo = true;
|
||||||
|
text.replace(start, end, edit.mmBefore);
|
||||||
|
mIsUndoOrRedo = false;
|
||||||
|
|
||||||
|
// This will get rid of underlines inserted when editor tries to come
|
||||||
|
// up with a suggestion.
|
||||||
|
for (Object o : text.getSpans(0, text.length(), UnderlineSpan.class)) {
|
||||||
|
text.removeSpan(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
Selection.setSelection(text, edit.mmBefore == null ? start
|
||||||
|
: (start + edit.mmBefore.length()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Can redo be performed?
|
||||||
|
*/
|
||||||
|
public boolean canRedo() {
|
||||||
|
return (mEditHistory.mmPosition < mEditHistory.mmHistory.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform redo.
|
||||||
|
*/
|
||||||
|
public void redo() {
|
||||||
|
EditItem edit = mEditHistory.getNext();
|
||||||
|
if (edit == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Editable text = mTextView.getEditableText();
|
||||||
|
int start = edit.mmStart;
|
||||||
|
int end = start + (edit.mmBefore != null ? edit.mmBefore.length() : 0);
|
||||||
|
|
||||||
|
mIsUndoOrRedo = true;
|
||||||
|
text.replace(start, end, edit.mmAfter);
|
||||||
|
mIsUndoOrRedo = false;
|
||||||
|
|
||||||
|
// This will get rid of underlines inserted when editor tries to come
|
||||||
|
// up with a suggestion.
|
||||||
|
for (Object o : text.getSpans(0, text.length(), UnderlineSpan.class)) {
|
||||||
|
text.removeSpan(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
Selection.setSelection(text, edit.mmAfter == null ? start
|
||||||
|
: (start + edit.mmAfter.length()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store preferences.
|
||||||
|
*/
|
||||||
|
public void storePersistentState(Editor editor, String prefix) {
|
||||||
|
// Store hash code of text in the editor so that we can check if the
|
||||||
|
// editor contents has changed.
|
||||||
|
editor.putString(prefix + ".hash",
|
||||||
|
String.valueOf(mTextView.getText().toString().hashCode()));
|
||||||
|
editor.putInt(prefix + ".maxSize", mEditHistory.mmMaxHistorySize);
|
||||||
|
editor.putInt(prefix + ".position", mEditHistory.mmPosition);
|
||||||
|
editor.putInt(prefix + ".size", mEditHistory.mmHistory.size());
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
for (EditItem ei : mEditHistory.mmHistory) {
|
||||||
|
String pre = prefix + "." + i;
|
||||||
|
|
||||||
|
editor.putInt(pre + ".start", ei.mmStart);
|
||||||
|
editor.putString(pre + ".before", ei.mmBefore.toString());
|
||||||
|
editor.putString(pre + ".after", ei.mmAfter.toString());
|
||||||
|
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restore preferences.
|
||||||
|
*
|
||||||
|
* @param prefix The preference key prefix used when state was stored.
|
||||||
|
* @return did restore succeed? If this is false, the undo history will be
|
||||||
|
* empty.
|
||||||
|
*/
|
||||||
|
public boolean restorePersistentState(SharedPreferences sp, String prefix)
|
||||||
|
throws IllegalStateException {
|
||||||
|
|
||||||
|
boolean ok = doRestorePersistentState(sp, prefix);
|
||||||
|
if (!ok) {
|
||||||
|
mEditHistory.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean doRestorePersistentState(SharedPreferences sp, String prefix) {
|
||||||
|
|
||||||
|
String hash = sp.getString(prefix + ".hash", null);
|
||||||
|
if (hash == null) {
|
||||||
|
// No state to be restored.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Integer.valueOf(hash) != mTextView.getText().toString().hashCode()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
mEditHistory.clear();
|
||||||
|
mEditHistory.mmMaxHistorySize = sp.getInt(prefix + ".maxSize", -1);
|
||||||
|
|
||||||
|
int count = sp.getInt(prefix + ".size", -1);
|
||||||
|
if (count == -1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
String pre = prefix + "." + i;
|
||||||
|
|
||||||
|
int start = sp.getInt(pre + ".start", -1);
|
||||||
|
String before = sp.getString(pre + ".before", null);
|
||||||
|
String after = sp.getString(pre + ".after", null);
|
||||||
|
|
||||||
|
if (start == -1 || before == null || after == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
mEditHistory.add(new EditItem(start, before, after));
|
||||||
|
}
|
||||||
|
|
||||||
|
mEditHistory.mmPosition = sp.getInt(prefix + ".position", -1);
|
||||||
|
if (mEditHistory.mmPosition == -1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// =================================================================== //
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Keeps track of all the edit history of a text.
|
||||||
|
*/
|
||||||
|
private final class EditHistory {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The position from which an EditItem will be retrieved when getNext()
|
||||||
|
* is called. If getPrevious() has not been called, this has the same
|
||||||
|
* value as mmHistory.size().
|
||||||
|
*/
|
||||||
|
private int mmPosition = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum undo history size.
|
||||||
|
*/
|
||||||
|
private int mmMaxHistorySize = -1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The list of edits in chronological order.
|
||||||
|
*/
|
||||||
|
private final LinkedList<EditItem> mmHistory = new LinkedList<EditItem>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear history.
|
||||||
|
*/
|
||||||
|
private void clear() {
|
||||||
|
mmPosition = 0;
|
||||||
|
mmHistory.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a new edit operation to the history at the current position. If
|
||||||
|
* executed after a call to getPrevious() removes all the future history
|
||||||
|
* (elements with positions >= current history position).
|
||||||
|
*/
|
||||||
|
private void add(EditItem item) {
|
||||||
|
while (mmHistory.size() > mmPosition) {
|
||||||
|
mmHistory.removeLast();
|
||||||
|
}
|
||||||
|
mmHistory.add(item);
|
||||||
|
mmPosition++;
|
||||||
|
|
||||||
|
if (mmMaxHistorySize >= 0) {
|
||||||
|
trimHistory();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int size(){
|
||||||
|
return mmHistory.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the maximum history size. If size is negative, then history size
|
||||||
|
* is only limited by the device memory.
|
||||||
|
*/
|
||||||
|
private void setMaxHistorySize(int maxHistorySize) {
|
||||||
|
mmMaxHistorySize = maxHistorySize;
|
||||||
|
if (mmMaxHistorySize >= 0) {
|
||||||
|
trimHistory();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trim history when it exceeds max history size.
|
||||||
|
*/
|
||||||
|
private void trimHistory() {
|
||||||
|
while (mmHistory.size() > mmMaxHistorySize) {
|
||||||
|
mmHistory.removeFirst();
|
||||||
|
mmPosition--;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mmPosition < 0) {
|
||||||
|
mmPosition = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Traverses the history backward by one position, returns and item at
|
||||||
|
* that position.
|
||||||
|
*/
|
||||||
|
private EditItem getPrevious() {
|
||||||
|
if (mmPosition == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
mmPosition--;
|
||||||
|
return mmHistory.get(mmPosition);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Traverses the history forward by one position, returns and item at
|
||||||
|
* that position.
|
||||||
|
*/
|
||||||
|
private EditItem getNext() {
|
||||||
|
if (mmPosition >= mmHistory.size()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
EditItem item = mmHistory.get(mmPosition);
|
||||||
|
mmPosition++;
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the changes performed by a single edit operation.
|
||||||
|
*/
|
||||||
|
private final class EditItem {
|
||||||
|
private final int mmStart;
|
||||||
|
private final CharSequence mmBefore;
|
||||||
|
private final CharSequence mmAfter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs EditItem of a modification that was applied at position
|
||||||
|
* start and replaced CharSequence before with CharSequence after.
|
||||||
|
*/
|
||||||
|
public EditItem(int start, CharSequence before, CharSequence after) {
|
||||||
|
mmStart = start;
|
||||||
|
mmBefore = before;
|
||||||
|
mmAfter = after;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class that listens to changes in the text.
|
||||||
|
*/
|
||||||
|
private final class EditTextChangeListener implements TextWatcher {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The text that will be removed by the change event.
|
||||||
|
*/
|
||||||
|
private CharSequence mBeforeChange;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The text that was inserted by the change event.
|
||||||
|
*/
|
||||||
|
private CharSequence mAfterChange;
|
||||||
|
|
||||||
|
public void beforeTextChanged(CharSequence s, int start, int count,
|
||||||
|
int after) {
|
||||||
|
if (mIsUndoOrRedo || !mEnabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mBeforeChange = s.subSequence(start, start + count);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onTextChanged(CharSequence s, int start, int before,
|
||||||
|
int count) {
|
||||||
|
if (mIsUndoOrRedo || !mEnabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mAfterChange = s.subSequence(start, start + count);
|
||||||
|
mEditHistory.add(new EditItem(start, mBeforeChange, mAfterChange));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void afterTextChanged(Editable s) {
|
||||||
|
if (mEditHistory.size() < mInitialHistoryStackSize) {
|
||||||
|
mInitialHistoryStackSize = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,7 +2,6 @@ package org.autojs.autojs.ui.edit.toolbar;
|
|||||||
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Looper;
|
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.support.v7.widget.RecyclerView;
|
import android.support.v7.widget.RecyclerView;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
@@ -10,25 +9,23 @@ import android.util.Log;
|
|||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
|
||||||
import com.stardust.autojs.engine.RhinoJavaScriptEngine;
|
|
||||||
import com.stardust.autojs.execution.ScriptExecution;
|
|
||||||
import com.stardust.autojs.rhino.debug.Dim;
|
|
||||||
import com.stardust.autojs.rhino.debug.DebugCallback;
|
import com.stardust.autojs.rhino.debug.DebugCallback;
|
||||||
|
import com.stardust.autojs.rhino.debug.Debugger;
|
||||||
|
import com.stardust.autojs.rhino.debug.Dim;
|
||||||
import com.stardust.autojs.runtime.exception.ScriptInterruptedException;
|
import com.stardust.autojs.runtime.exception.ScriptInterruptedException;
|
||||||
import com.stardust.pio.PFiles;
|
import com.stardust.pio.PFiles;
|
||||||
|
|
||||||
import org.androidannotations.annotations.Click;
|
import org.androidannotations.annotations.Click;
|
||||||
import org.androidannotations.annotations.EFragment;
|
import org.androidannotations.annotations.EFragment;
|
||||||
import org.autojs.autojs.R;
|
import org.autojs.autojs.R;
|
||||||
import org.autojs.autojs.autojs.AutoJs;
|
|
||||||
import org.autojs.autojs.ui.edit.EditorView;
|
import org.autojs.autojs.ui.edit.EditorView;
|
||||||
import org.autojs.autojs.ui.edit.debug.CodeEvaluator;
|
import org.autojs.autojs.ui.edit.debug.CodeEvaluator;
|
||||||
import org.autojs.autojs.ui.edit.debug.DebugBar;
|
import org.autojs.autojs.ui.edit.debug.DebugBar;
|
||||||
|
import org.autojs.autojs.ui.edit.debug.DebuggerSingleton;
|
||||||
import org.autojs.autojs.ui.edit.debug.WatchingVariable;
|
import org.autojs.autojs.ui.edit.debug.WatchingVariable;
|
||||||
import org.autojs.autojs.ui.edit.editor.CodeEditor;
|
import org.autojs.autojs.ui.edit.editor.CodeEditor;
|
||||||
import org.mozilla.javascript.ContextFactory;
|
|
||||||
|
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -36,15 +33,14 @@ import java.util.List;
|
|||||||
public class DebugToolbarFragment extends ToolbarFragment implements DebugCallback, CodeEditor.CursorChangeCallback, CodeEvaluator {
|
public class DebugToolbarFragment extends ToolbarFragment implements DebugCallback, CodeEditor.CursorChangeCallback, CodeEvaluator {
|
||||||
|
|
||||||
private static final String LOG_TAG = "DebugToolbarFragment";
|
private static final String LOG_TAG = "DebugToolbarFragment";
|
||||||
private Dim mDim;
|
|
||||||
private EditorView mEditorView;
|
private EditorView mEditorView;
|
||||||
|
private boolean mCursorChangeFromUser = true;
|
||||||
|
private Debugger mDebugger;
|
||||||
private Handler mHandler;
|
private Handler mHandler;
|
||||||
private boolean mSkipOtherFileBreakpoint = false;
|
|
||||||
private String mCurrentEditorSourceUrl;
|
private String mCurrentEditorSourceUrl;
|
||||||
private String mInitialEditorSourceUrl;
|
private String mInitialEditorSourceUrl;
|
||||||
private String mInitialEditorSource;
|
private String mInitialEditorSource;
|
||||||
private boolean mCursorChangeFromUser = true;
|
|
||||||
private Dim.SourceInfo mSourceInfo;
|
|
||||||
private final RecyclerView.AdapterDataObserver mVariableChangeObserver = new RecyclerView.AdapterDataObserver() {
|
private final RecyclerView.AdapterDataObserver mVariableChangeObserver = new RecyclerView.AdapterDataObserver() {
|
||||||
@Override
|
@Override
|
||||||
public void onItemRangeInserted(int positionStart, int itemCount) {
|
public void onItemRangeInserted(int positionStart, int itemCount) {
|
||||||
@@ -54,21 +50,17 @@ public class DebugToolbarFragment extends ToolbarFragment implements DebugCallba
|
|||||||
private CodeEditor.BreakpointChangeListener mBreakpointChangeListener = new CodeEditor.BreakpointChangeListener() {
|
private CodeEditor.BreakpointChangeListener mBreakpointChangeListener = new CodeEditor.BreakpointChangeListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onBreakpointChange(int line, boolean enabled) {
|
public void onBreakpointChange(int line, boolean enabled) {
|
||||||
if (mSourceInfo != null) {
|
if (mDebugger != null) {
|
||||||
mSourceInfo.breakpoint(line + 1, enabled);
|
mDebugger.breakpoint(line + 1, enabled);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAllBreakpointRemoved(int count) {
|
public void onAllBreakpointRemoved(int count) {
|
||||||
mDim.clearAllBreakpoints();
|
mDebugger.clearAllBreakpoints();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
public DebugToolbarFragment() {
|
|
||||||
Log.d(LOG_TAG, "DebugToolbarFragment()");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
@@ -79,13 +71,13 @@ public class DebugToolbarFragment extends ToolbarFragment implements DebugCallba
|
|||||||
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
|
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
|
||||||
super.onViewCreated(view, savedInstanceState);
|
super.onViewCreated(view, savedInstanceState);
|
||||||
mEditorView = findEditorView(view);
|
mEditorView = findEditorView(view);
|
||||||
mDim = createDim();
|
mDebugger = DebuggerSingleton.get();
|
||||||
|
mDebugger.setWeakDebugCallback(new WeakReference<>(this));
|
||||||
setInterrupted(false);
|
setInterrupted(false);
|
||||||
mSkipOtherFileBreakpoint = true;
|
|
||||||
mCurrentEditorSourceUrl = mInitialEditorSourceUrl = mEditorView.getFile().toString();
|
mCurrentEditorSourceUrl = mInitialEditorSourceUrl = mEditorView.getFile().toString();
|
||||||
mInitialEditorSource = mEditorView.getEditor().getText();
|
mInitialEditorSource = mEditorView.getEditor().getText();
|
||||||
setupEditor();
|
setupEditor();
|
||||||
mEditorView.run(false);
|
mDebugger.attach(mEditorView.run(false));
|
||||||
Log.d(LOG_TAG, "onViewCreated");
|
Log.d(LOG_TAG, "onViewCreated");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,16 +89,6 @@ public class DebugToolbarFragment extends ToolbarFragment implements DebugCallba
|
|||||||
DebugBar debugBar = mEditorView.getDebugBar();
|
DebugBar debugBar = mEditorView.getDebugBar();
|
||||||
debugBar.registerVariableChangeObserver(mVariableChangeObserver);
|
debugBar.registerVariableChangeObserver(mVariableChangeObserver);
|
||||||
debugBar.setCodeEvaluator(this);
|
debugBar.setCodeEvaluator(this);
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private Dim createDim() {
|
|
||||||
Dim dim = new Dim();
|
|
||||||
dim.setBreak();
|
|
||||||
dim.setBreakOnExceptions(true);
|
|
||||||
dim.attachTo(AutoJs.getInstance().getScriptEngineService(), ContextFactory.getGlobal());
|
|
||||||
dim.setGuiCallback(this);
|
|
||||||
return dim;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setInterrupted(boolean interrupted) {
|
private void setInterrupted(boolean interrupted) {
|
||||||
@@ -120,19 +102,15 @@ public class DebugToolbarFragment extends ToolbarFragment implements DebugCallba
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void detachDebugger() {
|
public void detachDebugger() {
|
||||||
if (!mDim.isAttached()) {
|
if (!mDebugger.isAttached()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Log.d(LOG_TAG, "detachDebugger");
|
Log.d(LOG_TAG, "detachDebugger");
|
||||||
mDim.detach();
|
mDebugger.detach();
|
||||||
mDim.setGuiCallback(null);
|
|
||||||
if (mEditorView == null) {
|
if (mEditorView == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
CodeEditor editor = mEditorView.getEditor();
|
CodeEditor editor = mEditorView.getEditor();
|
||||||
editor.removeCursorChangeCallback(this);
|
|
||||||
editor.setBreakpointChangeListener(null);
|
|
||||||
mSourceInfo = null;
|
|
||||||
editor.setRedoUndoEnabled(true);
|
editor.setRedoUndoEnabled(true);
|
||||||
if (!TextUtils.equals(mInitialEditorSourceUrl, mCurrentEditorSourceUrl)) {
|
if (!TextUtils.equals(mInitialEditorSourceUrl, mCurrentEditorSourceUrl)) {
|
||||||
editor.setText(mInitialEditorSource);
|
editor.setText(mInitialEditorSource);
|
||||||
@@ -140,25 +118,24 @@ public class DebugToolbarFragment extends ToolbarFragment implements DebugCallba
|
|||||||
DebugBar debugBar = mEditorView.getDebugBar();
|
DebugBar debugBar = mEditorView.getDebugBar();
|
||||||
debugBar.setTitle(null);
|
debugBar.setTitle(null);
|
||||||
debugBar.setCodeEvaluator(null);
|
debugBar.setCodeEvaluator(null);
|
||||||
debugBar.unregisterVariableChangeObserver(mVariableChangeObserver);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Click(R.id.step_over)
|
@Click(R.id.step_over)
|
||||||
void stepOver() {
|
void stepOver() {
|
||||||
setInterrupted(false);
|
setInterrupted(false);
|
||||||
mDim.setReturnValue(Dim.STEP_OVER);
|
mDebugger.stepOver();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Click(R.id.step_into)
|
@Click(R.id.step_into)
|
||||||
void stepInto() {
|
void stepInto() {
|
||||||
setInterrupted(false);
|
setInterrupted(false);
|
||||||
mDim.setReturnValue(Dim.STEP_INTO);
|
mDebugger.stepInto();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Click(R.id.step_out)
|
@Click(R.id.step_out)
|
||||||
void stepOut() {
|
void stepOut() {
|
||||||
setInterrupted(false);
|
setInterrupted(false);
|
||||||
mDim.setReturnValue(Dim.STEP_OUT);
|
mDebugger.stepOut();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Click(R.id.stop_script)
|
@Click(R.id.stop_script)
|
||||||
@@ -169,15 +146,12 @@ public class DebugToolbarFragment extends ToolbarFragment implements DebugCallba
|
|||||||
@Click(R.id.resume_script)
|
@Click(R.id.resume_script)
|
||||||
void resumeScript() {
|
void resumeScript() {
|
||||||
setInterrupted(false);
|
setInterrupted(false);
|
||||||
mDim.setReturnValue(Dim.GO);
|
mDebugger.resume();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateSourceText(Dim.SourceInfo sourceInfo) {
|
public void updateSourceText(Dim.SourceInfo sourceInfo) {
|
||||||
Log.d(LOG_TAG, "updateSourceText: url = " + sourceInfo.url());
|
Log.d(LOG_TAG, "updateSourceText: url = " + sourceInfo.url());
|
||||||
if (!sourceInfo.url().equals(mEditorView.getFile().toString())) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
sourceInfo.removeAllBreakpoints();
|
sourceInfo.removeAllBreakpoints();
|
||||||
for (CodeEditor.Breakpoint breakpoint : mEditorView.getEditor().getBreakpoints().values()) {
|
for (CodeEditor.Breakpoint breakpoint : mEditorView.getEditor().getBreakpoints().values()) {
|
||||||
int line = breakpoint.line + 1;
|
int line = breakpoint.line + 1;
|
||||||
@@ -186,18 +160,10 @@ public class DebugToolbarFragment extends ToolbarFragment implements DebugCallba
|
|||||||
Log.d(LOG_TAG, "not breakable: " + line);
|
Log.d(LOG_TAG, "not breakable: " + line);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mSourceInfo = sourceInfo;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void enterInterrupt(Dim.StackFrame stackFrame, String threadName, String message) {
|
public void enterInterrupt(Dim.StackFrame stackFrame, String threadName, String message) {
|
||||||
Log.d(LOG_TAG, "enterInterrupt: threadName = " + threadName + ", url = " + stackFrame.getUrl() + ", line = " + stackFrame.getLineNumber());
|
|
||||||
//刚启动调试时会在init脚本的第一行自动停下,此时应该让脚本继续运行
|
|
||||||
if (mSkipOtherFileBreakpoint && !stackFrame.getUrl().equals(mInitialEditorSourceUrl) && message == null) {
|
|
||||||
mHandler.post(this::resumeScript);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
mSkipOtherFileBreakpoint = false;
|
|
||||||
showDebuggingLineOnEditor(stackFrame, message);
|
showDebuggingLineOnEditor(stackFrame, message);
|
||||||
mHandler.post(this::updateWatchingVariables);
|
mHandler.post(this::updateWatchingVariables);
|
||||||
}
|
}
|
||||||
@@ -207,7 +173,7 @@ public class DebugToolbarFragment extends ToolbarFragment implements DebugCallba
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void updateWatchingVariables(int start, int end) {
|
private void updateWatchingVariables(int start, int end) {
|
||||||
if (!mDim.isAttached()) {
|
if (!mDebugger.isAttached()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
DebugBar debugBar = mEditorView.getDebugBar();
|
DebugBar debugBar = mEditorView.getDebugBar();
|
||||||
@@ -221,11 +187,7 @@ public class DebugToolbarFragment extends ToolbarFragment implements DebugCallba
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String eval(String expr) {
|
public String eval(String expr) {
|
||||||
if (expr == null || !mDim.isAttached() || !mDim.stringIsCompilableUnit(expr)) {
|
return mDebugger.eval(expr);
|
||||||
return null;
|
|
||||||
}
|
|
||||||
mDim.contextSwitch(0);
|
|
||||||
return mDim.eval(expr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showDebuggingLineOnEditor(Dim.StackFrame stackFrame, String message) {
|
private void showDebuggingLineOnEditor(Dim.StackFrame stackFrame, String message) {
|
||||||
@@ -258,21 +220,6 @@ public class DebugToolbarFragment extends ToolbarFragment implements DebugCallba
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isGuiEventThread() {
|
|
||||||
return Looper.getMainLooper() == Looper.myLooper();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void dispatchNextGuiEvent() {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean shouldAttachDebugger(RhinoJavaScriptEngine engine) {
|
|
||||||
ScriptExecution execution = mEditorView.getScriptExecution();
|
|
||||||
return execution != null && execution.getId() == engine.getId();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCursorChange(String line, int ch) {
|
public void onCursorChange(String line, int ch) {
|
||||||
@@ -281,7 +228,7 @@ public class DebugToolbarFragment extends ToolbarFragment implements DebugCallba
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
mCursorChangeFromUser = true;
|
mCursorChangeFromUser = true;
|
||||||
if (!mDim.isAttached()) {
|
if (!mDebugger.isAttached()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
String variable = findVariableOnCursor(line, ch);
|
String variable = findVariableOnCursor(line, ch);
|
||||||
@@ -322,6 +269,13 @@ public class DebugToolbarFragment extends ToolbarFragment implements DebugCallba
|
|||||||
@Override
|
@Override
|
||||||
public void onDestroy() {
|
public void onDestroy() {
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
detachDebugger();
|
if (mEditorView == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
CodeEditor editor = mEditorView.getEditor();
|
||||||
|
editor.removeCursorChangeCallback(this);
|
||||||
|
editor.setBreakpointChangeListener(null);
|
||||||
|
DebugBar debugBar = mEditorView.getDebugBar();
|
||||||
|
debugBar.unregisterVariableChangeObserver(mVariableChangeObserver);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,20 +1,5 @@
|
|||||||
package com.stardust.autojs.rhino.debug;
|
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 {
|
public interface DebugCallback {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -29,22 +14,4 @@ public interface DebugCallback {
|
|||||||
String threadTitle,
|
String threadTitle,
|
||||||
String alertMessage);
|
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.*;
|
||||||
import org.mozilla.javascript.debug.*;
|
import org.mozilla.javascript.debug.*;
|
||||||
import org.mozilla.javascript.tools.debugger.*;
|
import org.mozilla.javascript.tools.debugger.*;
|
||||||
|
import org.mozilla.javascript.debug.Debugger;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
@@ -47,7 +48,7 @@ public class Dim {
|
|||||||
/**
|
/**
|
||||||
* Interface to the debugger GUI.
|
* Interface to the debugger GUI.
|
||||||
*/
|
*/
|
||||||
private DebugCallback callback;
|
private DebugCallbackInternal callback;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether the debugger should break.
|
* Whether the debugger should break.
|
||||||
@@ -162,7 +163,7 @@ public class Dim {
|
|||||||
/**
|
/**
|
||||||
* Sets the GuiCallback object to use.
|
* Sets the GuiCallback object to use.
|
||||||
*/
|
*/
|
||||||
public void setGuiCallback(DebugCallback callback) {
|
public void setGuiCallback(DebugCallbackInternal callback) {
|
||||||
this.callback = callback;
|
this.callback = callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user