diff --git a/app/src/main/java/org/autojs/autojs/ui/edit/EditorMenu.java b/app/src/main/java/org/autojs/autojs/ui/edit/EditorMenu.java index 9c1e39e9..a2a7dbbc 100644 --- a/app/src/main/java/org/autojs/autojs/ui/edit/EditorMenu.java +++ b/app/src/main/java/org/autojs/autojs/ui/edit/EditorMenu.java @@ -10,6 +10,7 @@ import com.stardust.pio.PFiles; import org.autojs.autojs.R; import org.autojs.autojs.ui.build.BuildActivity; import org.autojs.autojs.ui.build.BuildActivity_; +import org.autojs.autojs.ui.common.NotAskAgainDialog; import org.autojs.autojs.ui.edit.editor.CodeEditor; import org.autojs.autojs.ui.log.LogActivity_; import org.autojs.autojs.theme.dialog.ThemeColorMaterialDialogBuilder; @@ -68,7 +69,15 @@ public class EditorMenu { mEditor.addOrRemoveBreakpointAtCurrentLine(); return true; case R.id.action_launch_debugger: - mEditorView.launchDebugger(); + new NotAskAgainDialog.Builder(mEditorView.getContext(), "editor.debug.long_click_hint") + .title(R.string.text_alert) + .content(R.string.hint_long_click_run_to_debug) + .positiveText(R.string.ok) + .show(); + mEditorView.debug(); + return true; + case R.id.action_remove_all_breakpoints: + mEditor.removeAllBreakpoints(); return true; } return false; diff --git a/app/src/main/java/org/autojs/autojs/ui/edit/EditorView.java b/app/src/main/java/org/autojs/autojs/ui/edit/EditorView.java index 99199119..47c15af2 100644 --- a/app/src/main/java/org/autojs/autojs/ui/edit/EditorView.java +++ b/app/src/main/java/org/autojs/autojs/ui/edit/EditorView.java @@ -48,6 +48,7 @@ import org.autojs.autojs.model.indices.Property; import org.autojs.autojs.model.script.Scripts; import org.autojs.autojs.ui.doc.ManualDialog; import org.autojs.autojs.ui.edit.completion.CodeCompletionBar; +import org.autojs.autojs.ui.edit.debug.DebugBar; import org.autojs.autojs.ui.edit.editor.CodeEditor; import org.autojs.autojs.ui.edit.keyboard.FunctionsKeyboardHelper; import org.autojs.autojs.ui.edit.keyboard.FunctionsKeyboardView; @@ -104,6 +105,9 @@ public class EditorView extends FrameLayout implements CodeCompletionBar.OnHintC @ViewById(R.id.functions_keyboard) FunctionsKeyboardView mFunctionsKeyboard; + @ViewById(R.id.debug_bar) + DebugBar mDebugBar; + @ViewById(R.id.docs) EWebView mDocsWebView; @@ -273,8 +277,15 @@ public class EditorView extends FrameLayout implements CodeCompletionBar.OnHintC private void initNormalToolbar() { mNormalToolbar.setOnMenuItemClickListener(this); + mNormalToolbar.setOnMenuItemLongClickListener(id -> { + if(id == R.id.run){ + debug(); + return true; + } + return false; + }); Fragment fragment = getActivity().getSupportFragmentManager().findFragmentById(R.id.toolbar_menu); - if(fragment == null){ + if (fragment == null) { showNormalToolbar(); } } @@ -304,7 +315,7 @@ public class EditorView extends FrameLayout implements CodeCompletionBar.OnHintC setMenuItemStatus(R.id.undo, mEditor.canUndo()); setMenuItemStatus(R.id.redo, mEditor.canRedo()); })); - mEditor.setCursorChangeCallback(this::autoComplete); + mEditor.addCursorChangeCallback(this::autoComplete); mEditor.getCodeEditText().setTextSize(Pref.getEditorTextSize((int) ViewUtils.pxToSp(getContext(), mEditor.getCodeEditText().getTextSize()))); } @@ -312,6 +323,10 @@ public class EditorView extends FrameLayout implements CodeCompletionBar.OnHintC mAutoCompletion.onCursorChange(line, cursor); } + public DebugBar getDebugBar() { + return mDebugBar; + } + public void setTheme(Theme theme) { mEditorTheme = theme; mEditor.setTheme(theme); @@ -368,11 +383,13 @@ public class EditorView extends FrameLayout implements CodeCompletionBar.OnHintC @SuppressLint("CheckResult") public void runAndSaveFileIfNeeded() { save().observeOn(AndroidSchedulers.mainThread()) - .subscribe(s -> run()); + .subscribe(s -> run(true)); } - public void run() { - Snackbar.make(this, R.string.text_start_running, Snackbar.LENGTH_SHORT).show(); + public void run(boolean showMessage) { + if(showMessage){ + Snackbar.make(this, R.string.text_start_running, Snackbar.LENGTH_SHORT).show(); + } mScriptExecutionId = Scripts.runWithBroadcastSender(mFile).getId(); setMenuItemStatus(R.id.run, false); } @@ -534,12 +551,14 @@ public class EditorView extends FrameLayout implements CodeCompletionBar.OnHintC } - public void launchDebugger() { + public void debug() { DebugToolbarFragment debugToolbarFragment = DebugToolbarFragment_.builder() .build(); getActivity().getSupportFragmentManager().beginTransaction() .replace(R.id.toolbar_menu, debugToolbarFragment) .commit(); + mDebugBar.setVisibility(VISIBLE); + mInputMethodEnhanceBar.setVisibility(GONE); mDebugging = true; } @@ -551,6 +570,8 @@ public class EditorView extends FrameLayout implements CodeCompletionBar.OnHintC } showNormalToolbar(); mEditor.setDebuggingLine(-1); + mDebugBar.setVisibility(GONE); + mInputMethodEnhanceBar.setVisibility(VISIBLE); mDebugging = false; } @@ -622,7 +643,7 @@ public class EditorView extends FrameLayout implements CodeCompletionBar.OnHintC } @Nullable - public ScriptExecution getScriptExecution(){ + public ScriptExecution getScriptExecution() { return AutoJs.getInstance().getScriptEngineService().getScriptExecution(mScriptExecutionId); } diff --git a/app/src/main/java/org/autojs/autojs/ui/edit/debug/CodeEvaluateDialogBuilder.java b/app/src/main/java/org/autojs/autojs/ui/edit/debug/CodeEvaluateDialogBuilder.java new file mode 100644 index 00000000..d3fcecd2 --- /dev/null +++ b/app/src/main/java/org/autojs/autojs/ui/edit/debug/CodeEvaluateDialogBuilder.java @@ -0,0 +1,56 @@ +package org.autojs.autojs.ui.edit.debug; + +import android.content.Context; +import android.content.SharedPreferences; +import android.preference.PreferenceManager; +import android.support.annotation.NonNull; +import android.text.Editable; +import android.text.TextUtils; +import android.view.View; +import android.widget.EditText; +import android.widget.TextView; + +import org.autojs.autojs.R; +import org.autojs.autojs.theme.dialog.ThemeColorMaterialDialogBuilder; + +public class CodeEvaluateDialogBuilder extends ThemeColorMaterialDialogBuilder { + + private static final String KEY_CODE = CodeEvaluateDialogBuilder.class.getName() + ".code"; + private CodeEvaluator mCodeEvaluator; + private TextView mResult; + private EditText mCode; + private SharedPreferences mSharedPreferences; + + public CodeEvaluateDialogBuilder(@NonNull Context context) { + super(context); + setupViews(); + mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(context); + mCode.setText(mSharedPreferences.getString(KEY_CODE, "")); + } + + private void setupViews() { + View view = View.inflate(context, R.layout.dialog_code_evaluate, null); + customView(view, true); + mResult = view.findViewById(R.id.result); + mCode = view.findViewById(R.id.code); + positiveText(R.string.text_run); + negativeText(R.string.text_close); + autoDismiss(false); + onNegative((dialog, which) -> dialog.dismiss()); + onPositive(((dialog, which) -> { + Editable code = mCode.getText(); + if (!TextUtils.isEmpty(code)) { + String codeStr = code.toString(); + mSharedPreferences.edit().putString(KEY_CODE, codeStr).apply(); + mResult.setText(mCodeEvaluator.eval(codeStr)); + } + })); + } + + public CodeEvaluateDialogBuilder codeEvaluator(CodeEvaluator evaluator) { + mCodeEvaluator = evaluator; + return this; + } + + +} diff --git a/app/src/main/java/org/autojs/autojs/ui/edit/debug/CodeEvaluator.java b/app/src/main/java/org/autojs/autojs/ui/edit/debug/CodeEvaluator.java new file mode 100644 index 00000000..c45ab2a9 --- /dev/null +++ b/app/src/main/java/org/autojs/autojs/ui/edit/debug/CodeEvaluator.java @@ -0,0 +1,5 @@ +package org.autojs.autojs.ui.edit.debug; + +public interface CodeEvaluator { + String eval(String code); +} diff --git a/app/src/main/java/org/autojs/autojs/ui/edit/debug/DebugBar.java b/app/src/main/java/org/autojs/autojs/ui/edit/debug/DebugBar.java new file mode 100644 index 00000000..30b667bb --- /dev/null +++ b/app/src/main/java/org/autojs/autojs/ui/edit/debug/DebugBar.java @@ -0,0 +1,165 @@ +package org.autojs.autojs.ui.edit.debug; + +import android.content.Context; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.text.TextUtils; +import android.util.AttributeSet; +import android.view.View; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.TextView; + +import com.stardust.util.ClipboardUtil; + +import org.autojs.autojs.R; +import org.autojs.autojs.theme.dialog.ThemeColorMaterialDialogBuilder; +import org.autojs.autojs.ui.widget.AutoAdapter; +import org.autojs.autojs.ui.widget.BindableViewHolder; + +import java.util.List; + + +public class DebugBar extends FrameLayout { + + private AutoAdapter mVariablesAdapter; + private final WatchingVariable mCurrentVariable = new WatchingVariable(null, null, true); + private TextView mTitle; + private CodeEvaluator mCodeEvaluator; + + public DebugBar(@NonNull Context context) { + super(context); + init(); + } + + public DebugBar(@NonNull Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + init(); + } + + public DebugBar(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(); + } + + public void setCodeEvaluator(CodeEvaluator codeEvaluator) { + mCodeEvaluator = codeEvaluator; + } + + public List getWatchingVariables() { + return mVariablesAdapter.getData(); + } + + private void init() { + inflate(getContext(), R.layout.debug_bar, this); + mVariablesAdapter = new AutoAdapter<>(VariableViewHolder::new, R.layout.item_debug_variable_recycler_view); + RecyclerView variablesView = findViewById(R.id.variables); + variablesView.setAdapter(mVariablesAdapter); + variablesView.setLayoutManager(new LinearLayoutManager(getContext())); + mVariablesAdapter.add(mCurrentVariable); + findViewById(R.id.add).setOnClickListener(view -> showNewWatchingVariableDialog()); + findViewById(R.id.execute).setOnClickListener(view -> showExecuteCodeDialog()); + mTitle = findViewById(R.id.title); + } + + private void showExecuteCodeDialog() { + if (mCodeEvaluator == null) { + return; + } + new CodeEvaluateDialogBuilder(getContext()) + .codeEvaluator(mCodeEvaluator) + .title(R.string.text_execute_code) + .show(); + } + + + public void setTitle(String title) { + if (title == null) { + mTitle.setText(R.string.text_debug); + } else { + mTitle.setText(getResources().getString(R.string.format_debug_bar_title, title)); + } + } + + private void showNewWatchingVariableDialog() { + new ThemeColorMaterialDialogBuilder(getContext()) + .title(R.string.text_new_watching_variable) + .input(getResources().getString(R.string.text_variable_or_expr), "", (dialog, input) -> { + if (TextUtils.isEmpty(input)) { + return; + } + mVariablesAdapter.add(new WatchingVariable(input.toString())); + }) + .show(); + } + + public void updateCurrentVariable(String name, String value) { + mCurrentVariable.setDisplayName(name); + mCurrentVariable.setName(name); + mCurrentVariable.setValue(value); + mVariablesAdapter.notifyItemChanged(0); + } + + public void refresh(int start, int count) { + mVariablesAdapter.notifyItemRangeChanged(start, count); + } + + public void registerVariableChangeObserver(RecyclerView.AdapterDataObserver observer) { + mVariablesAdapter.registerAdapterDataObserver(observer); + } + + public void unregisterVariableChangeObserver(RecyclerView.AdapterDataObserver observer) { + mVariablesAdapter.unregisterAdapterDataObserver(observer); + } + + + private void showVariable(WatchingVariable variable) { + new ThemeColorMaterialDialogBuilder(getContext()) + .title(variable.getDisplayName()) + .content(variable.getValue()) + .positiveText(R.string.ok) + .negativeText(R.string.text_copy_value) + .autoDismiss(true) + .onNegative((dialog, which) -> ClipboardUtil.setClip(getContext(), variable.getValue())) + .show(); + } + + + class VariableViewHolder extends BindableViewHolder { + + private final TextView mVariable; + private final ImageView mIcon; + + VariableViewHolder(View itemView) { + super(itemView); + mVariable = itemView.findViewById(R.id.variable); + mIcon = itemView.findViewById(R.id.icon); + mIcon.setOnClickListener(view -> { + int pos = getAdapterPosition(); + WatchingVariable variable = mVariablesAdapter.get(pos); + if (!variable.isPinned()) { + mVariablesAdapter.remove(pos); + } + }); + itemView.setOnClickListener(view -> { + int pos = getAdapterPosition(); + WatchingVariable variable = mVariablesAdapter.get(pos); + showVariable(variable); + }); + } + + @Override + public void bind(WatchingVariable data, int position) { + if (TextUtils.isEmpty(data.getDisplayName())) { + mVariable.setText(""); + } else { + mVariable.setText(String.format("%s = %s", data.getDisplayName(), data.getSingleLineValue())); + } + mIcon.setVisibility(data.isPinned() ? View.INVISIBLE : View.VISIBLE); + } + } + + +} diff --git a/app/src/main/java/org/autojs/autojs/ui/edit/debug/WatchingVariable.java b/app/src/main/java/org/autojs/autojs/ui/edit/debug/WatchingVariable.java new file mode 100644 index 00000000..c11d947b --- /dev/null +++ b/app/src/main/java/org/autojs/autojs/ui/edit/debug/WatchingVariable.java @@ -0,0 +1,53 @@ +package org.autojs.autojs.ui.edit.debug; + +public class WatchingVariable { + + private String mDisplayName; + private String mName; + private final boolean mPinned; + private String mValue; + private String mSingleLineValue; + + public WatchingVariable(String displayName, String name, boolean pinned) { + mDisplayName = displayName; + mName = name; + mPinned = pinned; + } + + public WatchingVariable(String name) { + this(name, name, false); + } + + public boolean isPinned() { + return mPinned; + } + + public String getValue() { + return mValue; + } + + public void setValue(String value) { + mValue = value; + mSingleLineValue = value == null ? null : value.replaceAll("\n", " "); + } + + public String getSingleLineValue() { + return mSingleLineValue; + } + + public void setDisplayName(String displayName) { + mDisplayName = displayName; + } + + public void setName(String name) { + mName = name; + } + + public String getDisplayName() { + return mDisplayName; + } + + public String getName() { + return mName; + } +} diff --git a/app/src/main/java/org/autojs/autojs/ui/edit/editor/CodeEditText.java b/app/src/main/java/org/autojs/autojs/ui/edit/editor/CodeEditText.java index c8e8489a..fab5e057 100644 --- a/app/src/main/java/org/autojs/autojs/ui/edit/editor/CodeEditText.java +++ b/app/src/main/java/org/autojs/autojs/ui/edit/editor/CodeEditText.java @@ -32,18 +32,16 @@ import android.util.Log; import android.util.TimingLogger; import android.view.Gravity; -import org.autojs.autojs.R; import org.autojs.autojs.ui.edit.theme.Theme; import org.autojs.autojs.ui.edit.theme.TokenMapping; -import com.stardust.autojs.execution.ScriptExecution; import com.stardust.util.ClipboardUtil; import com.stardust.util.TextUtils; import org.mozilla.javascript.Token; -import java.util.ArrayList; import java.util.LinkedHashMap; +import java.util.concurrent.CopyOnWriteArrayList; import static org.autojs.autojs.ui.edit.editor.BracketMatching.UNMATCHED_BRACKET; @@ -60,7 +58,7 @@ public class CodeEditText extends AppCompatEditText { // 文字范围 protected HVScrollView mParentScrollView; - private CodeEditor.CursorChangeCallback mCallback; + private final CopyOnWriteArrayList mCursorChangeCallbacks = new CopyOnWriteArrayList<>(); private volatile JavaScriptHighlighter.HighlightTokens mHighlightTokens; private Theme mTheme; private TimingLogger mLogger = new TimingLogger(LOG_TAG, "draw"); @@ -110,6 +108,11 @@ public class CodeEditText extends AppCompatEditText { if (mParentScrollView == null) { mParentScrollView = (HVScrollView) getParent(); } + if (getLayout() == null) { + super.onDraw(canvas); + invalidate(); + return; + } updatePaddingForGutter(); updateLineRangeForDraw(canvas); @@ -266,8 +269,12 @@ public class CodeEditText extends AppCompatEditText { if (line < mFirstLineForDraw || line > mLastLineForDraw || mFirstLineForDraw < 0 || line < 0) { return; } - int lineTop = getLayout().getLineTop(line); - int lineBottom = getLayout().getLineTop(line + 1); + Layout layout = getLayout(); + if(layout == null){ + return; + } + int lineTop = layout.getLineTop(line); + int lineBottom = layout.getLineTop(line + 1); canvas.drawRect(0, lineTop, canvas.getWidth(), lineBottom, paint); } @@ -322,7 +329,8 @@ public class CodeEditText extends AppCompatEditText { protected void onSelectionChanged(int selStart, int selEnd) { //调用父类的onSelectionChanged时会发送一个AccessibilityEvent,当文本过大时造成异常 //super.onSelectionChanged(selStart, selEnd); - if (mCallback == null || selStart != selEnd) { + //父类构造函数会调用onSelectionChanged, 此时mCursorChangeCallbacks还没有初始化 + if (mCursorChangeCallbacks == null || mCursorChangeCallbacks.isEmpty() || selStart != selEnd) { return; } callCursorChangeCallback(getText(), selStart); @@ -365,7 +373,7 @@ public class CodeEditText extends AppCompatEditText { if (text.length() == 0) { return; } - if (mCallback == null) + if (mCursorChangeCallbacks.isEmpty()) return; int lineStart = TextUtils.lastIndexOf(text, '\n', sel - 1) + 1; if (lineStart < 0) { @@ -382,13 +390,21 @@ public class CodeEditText extends AppCompatEditText { return; String line = text.subSequence(lineStart, lineEnd).toString(); int cursor = sel - lineStart; - mCallback.onCursorChange(line, cursor); + for(CodeEditor.CursorChangeCallback callback : mCursorChangeCallbacks){ + callback.onCursorChange(line, cursor); + } } - public void setCursorChangeCallback(CodeEditor.CursorChangeCallback callback) { - mCallback = callback; + public void addCursorChangeCallback(CodeEditor.CursorChangeCallback callback) { + mCursorChangeCallbacks.add(callback); } + public boolean removeCursorChangeCallback(CodeEditor.CursorChangeCallback callback) { + return mCursorChangeCallbacks.remove(callback); + } + + + public void updateHighlightTokens(JavaScriptHighlighter.HighlightTokens highlightTokens) { mHighlightTokens = highlightTokens; postInvalidate(); @@ -427,7 +443,7 @@ public class CodeEditText extends AppCompatEditText { Parcelable superData = bundle.getParcelable("super_data"); mDebuggingLine = bundle.getInt("debugging_line", -1); int[] breakpoints = bundle.getIntArray("breakpoints"); - if(breakpoints != null){ + if (breakpoints != null) { for (int breakpoint : breakpoints) { mBreakpoints.put(breakpoint, new CodeEditor.Breakpoint(breakpoint)); } diff --git a/app/src/main/java/org/autojs/autojs/ui/edit/editor/CodeEditor.java b/app/src/main/java/org/autojs/autojs/ui/edit/editor/CodeEditor.java index 5e7e9520..382fd363 100644 --- a/app/src/main/java/org/autojs/autojs/ui/edit/editor/CodeEditor.java +++ b/app/src/main/java/org/autojs/autojs/ui/edit/editor/CodeEditor.java @@ -176,9 +176,14 @@ public class CodeEditor extends HVScrollView { } public void setReadOnly(boolean readOnly) { - setEnabled(!readOnly); + mCodeEditText.setEnabled(!readOnly); } + public void setRedoUndoEnabled(boolean enabled) { + mTextViewRedoUndo.setEnabled(enabled); + } + + public void setProgress(boolean progress) { if (progress) { if (mProcessDialog != null) { @@ -202,8 +207,12 @@ public class CodeEditor extends HVScrollView { mCodeEditText.setText(text); } - public void setCursorChangeCallback(CursorChangeCallback callback) { - mCodeEditText.setCursorChangeCallback(callback); + public void addCursorChangeCallback(CursorChangeCallback callback) { + mCodeEditText.addCursorChangeCallback(callback); + } + + public boolean removeCursorChangeCallback(CursorChangeCallback callback) { + return mCodeEditText.removeCursorChangeCallback(callback); } @@ -337,7 +346,6 @@ public class CodeEditor extends HVScrollView { } public void setDebuggingLine(int line) { - jumpTo(line, 0); mCodeEditText.setDebuggingLine(line); } @@ -356,6 +364,11 @@ public class CodeEditor extends HVScrollView { addOrRemoveBreakpoint(line); } + public void removeAllBreakpoints(){ + mCodeEditText.getBreakpoints().clear(); + mCodeEditText.invalidate(); + } + @Override protected void onDraw(Canvas canvas) { diff --git a/app/src/main/java/org/autojs/autojs/ui/edit/editor/TextViewRedoUndo.java b/app/src/main/java/org/autojs/autojs/ui/edit/editor/TextViewRedoUndo.java index 8d24fd4e..aa20f7b1 100644 --- a/app/src/main/java/org/autojs/autojs/ui/edit/editor/TextViewRedoUndo.java +++ b/app/src/main/java/org/autojs/autojs/ui/edit/editor/TextViewRedoUndo.java @@ -42,6 +42,7 @@ public class TextViewRedoUndo { private boolean flag = false; private int mInitialHistoryStackSize; + private boolean mEnabled = true; public TextViewRedoUndo(@NonNull EditText editText) { this.editable = editText.getText(); @@ -156,6 +157,14 @@ public class TextViewRedoUndo { mInitialHistoryStackSize = history.size(); } + public void setEnabled(boolean enabled) { + mEnabled = enabled; + } + + public boolean isEnabled() { + return mEnabled; + } + private class Watcher implements TextWatcher { /** @@ -168,6 +177,9 @@ public class TextViewRedoUndo { */ @Override public final void beforeTextChanged(CharSequence s, int start, int count, int after) { + if (!editText.isEnabled() || !mEnabled) { + return; + } if (flag) return; int end = start + count; if (end > start && end <= s.length()) { @@ -201,6 +213,9 @@ public class TextViewRedoUndo { */ @Override public final void onTextChanged(CharSequence s, int start, int before, int count) { + if (!editText.isEnabled() || !mEnabled) { + return; + } if (flag) return; int end = start + count; if (end > start) { @@ -223,6 +238,9 @@ public class TextViewRedoUndo { @Override public final void afterTextChanged(Editable s) { + if (!editText.isEnabled() || !mEnabled) { + return; + } if (flag) return; if (s != editable) { editable = s; diff --git a/app/src/main/java/org/autojs/autojs/ui/edit/toolbar/DebugToolbarFragment.java b/app/src/main/java/org/autojs/autojs/ui/edit/toolbar/DebugToolbarFragment.java index 21feb219..1b5e0324 100644 --- a/app/src/main/java/org/autojs/autojs/ui/edit/toolbar/DebugToolbarFragment.java +++ b/app/src/main/java/org/autojs/autojs/ui/edit/toolbar/DebugToolbarFragment.java @@ -4,20 +4,28 @@ import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.support.annotation.Nullable; +import android.support.v7.widget.RecyclerView; +import android.text.TextUtils; import android.util.Log; import android.view.View; +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.runtime.exception.ScriptInterruptedException; +import com.stardust.pio.PFiles; import org.androidannotations.annotations.Click; import org.androidannotations.annotations.EFragment; import org.autojs.autojs.R; import org.autojs.autojs.autojs.AutoJs; import org.autojs.autojs.ui.edit.EditorView; +import org.autojs.autojs.ui.edit.debug.CodeEvaluator; +import org.autojs.autojs.ui.edit.debug.DebugBar; +import org.autojs.autojs.ui.edit.debug.WatchingVariable; import org.autojs.autojs.ui.edit.editor.CodeEditor; import org.mozilla.javascript.ContextFactory; @@ -25,12 +33,23 @@ import java.util.Arrays; import java.util.List; @EFragment(R.layout.fragment_debug_toolbar) -public class DebugToolbarFragment extends ToolbarFragment implements DebugCallback { +public class DebugToolbarFragment extends ToolbarFragment implements DebugCallback, CodeEditor.CursorChangeCallback, CodeEvaluator { private static final String LOG_TAG = "DebugToolbarFragment"; private Dim mDim; private EditorView mEditorView; private Handler mHandler; + private boolean mSkipOtherFileBreakpoint = false; + private String mCurrentEditorSourceUrl; + private String mInitialEditorSourceUrl; + private String mInitialEditorSource; + private boolean mCursorChangeFromUser = true; + private final RecyclerView.AdapterDataObserver mVariableChangeObserver = new RecyclerView.AdapterDataObserver() { + @Override + public void onItemRangeInserted(int positionStart, int itemCount) { + updateWatchingVariables(positionStart, positionStart + itemCount); + } + }; public DebugToolbarFragment() { Log.d(LOG_TAG, "DebugToolbarFragment()"); @@ -46,37 +65,64 @@ public class DebugToolbarFragment extends ToolbarFragment implements DebugCallba public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); mEditorView = findEditorView(view); - ScriptExecution scriptExecution = mEditorView.getScriptExecution(); - if (scriptExecution != null) { - mDim = (Dim) scriptExecution.getEngine().getTag(Dim.TAG); - } - if (mDim == null) { - mDim = new Dim(); - mDim.setBreak(); - mDim.setBreakOnExceptions(true); - mDim.attachTo(AutoJs.getInstance().getScriptEngineService(), ContextFactory.getGlobal()); - mDim.setGuiCallback(this); - setInterrupted(false); - mEditorView.run(); - } else { - mDim.setGuiCallback(this); - } + mDim = createDim(); + setInterrupted(false); + mSkipOtherFileBreakpoint = true; + mCurrentEditorSourceUrl = mInitialEditorSourceUrl = mEditorView.getFile().toString(); + mInitialEditorSource = mEditorView.getEditor().getText(); + setupEditor(); + mEditorView.run(false); Log.d(LOG_TAG, "onViewCreated"); } + private void setupEditor() { + CodeEditor editor = mEditorView.getEditor(); + editor.setRedoUndoEnabled(false); + editor.addCursorChangeCallback(this); + DebugBar debugBar = mEditorView.getDebugBar(); + debugBar.registerVariableChangeObserver(mVariableChangeObserver); + 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) { setMenuItemStatus(R.id.step_into, interrupted); setMenuItemStatus(R.id.step_over, interrupted); setMenuItemStatus(R.id.step_out, interrupted); setMenuItemStatus(R.id.resume_script, interrupted); - if (!interrupted) { + if (!interrupted && mEditorView != null) { mEditorView.getEditor().setDebuggingLine(-1); } } public void detachDebugger() { + if (!mDim.isAttached()) { + return; + } mDim.detach(); mDim.setGuiCallback(null); + if (mEditorView == null) { + return; + } + CodeEditor editor = mEditorView.getEditor(); + editor.removeCursorChangeCallback(this); + editor.setRedoUndoEnabled(true); + if (!TextUtils.equals(mInitialEditorSourceUrl, mCurrentEditorSourceUrl)) { + editor.setText(mInitialEditorSource); + } + DebugBar debugBar = mEditorView.getDebugBar(); + debugBar.setTitle(null); + debugBar.setCodeEvaluator(null); + debugBar.unregisterVariableChangeObserver(mVariableChangeObserver); } @Click(R.id.step_over) @@ -125,18 +171,72 @@ public class DebugToolbarFragment extends ToolbarFragment implements DebugCallba } @Override - public void enterInterrupt(Dim.StackFrame stackFrame, String threadName, String s1) { + public void enterInterrupt(Dim.StackFrame stackFrame, String threadName, String message) { Log.d(LOG_TAG, "enterInterrupt: threadName = " + threadName + ", url = " + stackFrame.getUrl() + ", line = " + stackFrame.getLineNumber()); - if (stackFrame.getUrl().equals(mEditorView.getFile().toString())) { - final int line = stackFrame.getLineNumber() - 1; - mHandler.post(() -> { - mEditorView.getEditor().setDebuggingLine(line); - setInterrupted(true); - }); - - } else { + //刚启动调试时会在init脚本的第一行自动停下,此时应该让脚本继续运行 + if (mSkipOtherFileBreakpoint && !stackFrame.getUrl().equals(mInitialEditorSourceUrl) && message == null) { mHandler.post(this::resumeScript); + return; } + mSkipOtherFileBreakpoint = false; + showDebuggingLineOnEditor(stackFrame, message); + mHandler.post(this::updateWatchingVariables); + } + + private void updateWatchingVariables() { + updateWatchingVariables(0, mEditorView.getDebugBar().getWatchingVariables().size()); + } + + private void updateWatchingVariables(int start, int end) { + if (!mDim.isAttached()) { + return; + } + DebugBar debugBar = mEditorView.getDebugBar(); + List variables = debugBar.getWatchingVariables(); + for (int i = start; i < end; i++) { + WatchingVariable variable = variables.get(i); + String value = eval(variable.getName()); + variable.setValue(value); + } + debugBar.refresh(start, end - start); + } + + public String eval(String expr) { + if (expr == null || !mDim.isAttached() || !mDim.stringIsCompilableUnit(expr)) { + return null; + } + mDim.contextSwitch(0); + return mDim.eval(expr); + } + + private void showDebuggingLineOnEditor(Dim.StackFrame stackFrame, String message) { + //如果调试进入到其他脚本(例如模块脚本),则改变当前编辑器的文本为自动调试的脚本的代码 + String source; + //标记是否需要更改编辑器文本 + boolean shouldChangeText = !stackFrame.getUrl().equals(mCurrentEditorSourceUrl); + if (shouldChangeText) { + source = stackFrame.sourceInfo().source(); + } else { + source = null; + } + mCurrentEditorSourceUrl = stackFrame.getUrl(); + final int line = stackFrame.getLineNumber() - 1; + mHandler.post(() -> { + if (mEditorView == null) { + return; + } + if (shouldChangeText) { + mEditorView.getEditor().setText(source); + } + mCursorChangeFromUser = false; + mEditorView.getEditor().setDebuggingLine(line); + mEditorView.getEditor().jumpTo(line, 0); + mEditorView.getDebugBar().setTitle(PFiles.getName(mCurrentEditorSourceUrl)); + setInterrupted(true); + if (message != null && !message.equals(ScriptInterruptedException.class.getName())) { + Toast.makeText(mEditorView.getContext(), message, Toast.LENGTH_LONG).show(); + } + }); } @Override @@ -155,9 +255,54 @@ public class DebugToolbarFragment extends ToolbarFragment implements DebugCallba } + @Override + public void onCursorChange(String line, int ch) { + if (ch == 0 && !mCursorChangeFromUser) { + mCursorChangeFromUser = true; + return; + } + mCursorChangeFromUser = true; + if (!mDim.isAttached()) { + return; + } + String variable = findVariableOnCursor(line, ch); + Log.d(LOG_TAG, "onCursorChange: variable = " + variable + ", ch = " + ch + ", line = " + line); + String value = eval(variable); + mEditorView.getDebugBar().updateCurrentVariable(variable, value); + } + + private String findVariableOnCursor(String line, int ch) { + int end; + for (end = ch; end < line.length(); end++) { + if (!isIdentifierChar(line.charAt(end))) { + break; + } + } + int start; + for (start = Math.min(ch - 1, line.length() - 1); start >= 0; start--) { + if (!isIdentifierChar(line.charAt(start))) { + break; + } + } + start++; + if (start < end && start < line.length() && start >= 0) { + return line.substring(start, end); + } + return null; + } + + private boolean isIdentifierChar(char c) { + return Character.isDigit(c) || Character.isLetter(c) || c == '.' || c == '_'; + } + @Override public List getMenuItemIds() { return Arrays.asList(R.id.step_over, R.id.step_into, R.id.step_out, R.id.resume_script, R.id.stop_script); } + @Override + public void onDestroy() { + super.onDestroy(); + detachDebugger(); + } } diff --git a/app/src/main/java/org/autojs/autojs/ui/edit/toolbar/NormalToolbarFragment.java b/app/src/main/java/org/autojs/autojs/ui/edit/toolbar/NormalToolbarFragment.java index 1f718d2f..64f2ecc8 100644 --- a/app/src/main/java/org/autojs/autojs/ui/edit/toolbar/NormalToolbarFragment.java +++ b/app/src/main/java/org/autojs/autojs/ui/edit/toolbar/NormalToolbarFragment.java @@ -1,8 +1,5 @@ package org.autojs.autojs.ui.edit.toolbar; -import android.support.v4.app.Fragment; - -import org.androidannotations.annotations.Click; import org.androidannotations.annotations.EFragment; import org.autojs.autojs.R; @@ -12,7 +9,6 @@ import java.util.List; @EFragment(R.layout.fragment_normal_toolbar) public class NormalToolbarFragment extends ToolbarFragment { - @Override public List getMenuItemIds() { return Arrays.asList(R.id.run, R.id.undo, R.id.redo, R.id.save); diff --git a/app/src/main/java/org/autojs/autojs/ui/edit/toolbar/ToolbarFragment.java b/app/src/main/java/org/autojs/autojs/ui/edit/toolbar/ToolbarFragment.java index aed9a309..c531971f 100644 --- a/app/src/main/java/org/autojs/autojs/ui/edit/toolbar/ToolbarFragment.java +++ b/app/src/main/java/org/autojs/autojs/ui/edit/toolbar/ToolbarFragment.java @@ -11,19 +11,29 @@ import org.autojs.autojs.ui.edit.EditorView; import java.util.List; -public abstract class ToolbarFragment extends Fragment implements View.OnClickListener { +public abstract class ToolbarFragment extends Fragment implements View.OnClickListener, View.OnLongClickListener { + public interface OnMenuItemClickListener { void onToolbarMenuItemClick(int id); } + public interface OnMenuItemLongClickListener { + boolean onToolbarMenuItemLongClick(int id); + } + private OnMenuItemClickListener mOnMenuItemClickListener; + private OnMenuItemLongClickListener mOnMenuItemLongClickListener; private List mMenuItemIds; public void setOnMenuItemClickListener(OnMenuItemClickListener listener) { mOnMenuItemClickListener = listener; } + public void setOnMenuItemLongClickListener(OnMenuItemLongClickListener onMenuItemLongClickListener) { + mOnMenuItemLongClickListener = onMenuItemLongClickListener; + } + public abstract List getMenuItemIds(); @Override @@ -54,6 +64,7 @@ public abstract class ToolbarFragment extends Fragment implements View.OnClickLi for (int id : mMenuItemIds) { View view = rootView.findViewById(id); view.setOnClickListener(this); + view.setOnLongClickListener(this); view.setEnabled(editorView.getMenuItemStatus(id, view.isEnabled())); } } @@ -66,6 +77,13 @@ public abstract class ToolbarFragment extends Fragment implements View.OnClickLi } } + + @Override + public boolean onLongClick(View v) { + return mOnMenuItemLongClickListener != null && + mOnMenuItemLongClickListener.onToolbarMenuItemLongClick(v.getId()); + } + public void setMenuItemStatus(int id, boolean enabled) { if (mMenuItemIds == null) { mMenuItemIds = getMenuItemIds(); diff --git a/app/src/main/java/org/autojs/autojs/ui/main/drawer/DrawerFragment.java b/app/src/main/java/org/autojs/autojs/ui/main/drawer/DrawerFragment.java index 595de0a2..d6c92d15 100644 --- a/app/src/main/java/org/autojs/autojs/ui/main/drawer/DrawerFragment.java +++ b/app/src/main/java/org/autojs/autojs/ui/main/drawer/DrawerFragment.java @@ -89,7 +89,7 @@ public class DrawerFragment extends android.support.v4.app.Fragment { RecyclerView mDrawerMenu; - private DrawerMenuItem mConnectionItem = new DrawerMenuItem(R.drawable.ic_debug, R.string.debug, 0, this::connectOrDisconnectToRemote); + private DrawerMenuItem mConnectionItem = new DrawerMenuItem(R.drawable.ic_connect_to_pc, R.string.debug, 0, this::connectOrDisconnectToRemote); private DrawerMenuItem mAccessibilityServiceItem = new DrawerMenuItem(R.drawable.ic_service_green, R.string.text_accessibility_service, 0, this::enableOrDisableAccessibilityService); private DrawerMenuItem mStableModeItem = new DrawerMenuItem(R.drawable.ic_stable, R.string.text_stable_mode, R.string.key_stable_mode, null) { @Override diff --git a/app/src/main/java/org/autojs/autojs/ui/widget/AutoAdapter.java b/app/src/main/java/org/autojs/autojs/ui/widget/AutoAdapter.java index 1cef1024..400e225b 100644 --- a/app/src/main/java/org/autojs/autojs/ui/widget/AutoAdapter.java +++ b/app/src/main/java/org/autojs/autojs/ui/widget/AutoAdapter.java @@ -14,12 +14,16 @@ import java.util.List; public class AutoAdapter
extends RecyclerView.Adapter> { private ViewHolderSupplier> mViewHolderSupplier; - private List
mList = new ArrayList<>(); + private final List
mData = new ArrayList<>(); public AutoAdapter(ViewHolderSupplier> viewHolderSupplier) { mViewHolderSupplier = viewHolderSupplier; } + public AutoAdapter(ViewHolderSupplier.ViewHolderCreator> viewHolderCreator, int layoutRes) { + mViewHolderSupplier = ViewHolderSupplier.of(viewHolderCreator, layoutRes); + } + @Override public BindableViewHolder
onCreateViewHolder(ViewGroup parent, int viewType) { @@ -28,36 +32,48 @@ public class AutoAdapter
extends RecyclerView.Adapter @Override public void onBindViewHolder(BindableViewHolder
holder, int position) { - holder.bind(mList.get(position), position); + holder.bind(mData.get(position), position); } @Override public int getItemCount() { - return mList.size(); + return mData.size(); } public void remove(DT data) { - int pos = mList.indexOf(data); + int pos = mData.indexOf(data); if (pos < 0) return; - mList.remove(pos); + mData.remove(pos); notifyItemRemoved(pos); } public void remove(int index) { - mList.remove(index); + mData.remove(index); notifyItemRemoved(index); } public void addAll(Collection c) { - mList.addAll(c); - notifyItemRangeInserted(mList.size() - c.size() - 1, mList.size() - 1); + mData.addAll(c); + notifyItemRangeInserted(mData.size() - c.size() - 1, mData.size() - 1); + } + + public DT get(int index){ + return mData.get(index); } public void setData(Collection c) { - mList.clear(); - mList.addAll(c); + mData.clear(); + mData.addAll(c); notifyDataSetChanged(); } + public void add(DT item) { + mData.add(item); + notifyItemInserted(mData.size() - 1); + } + + public List
getData() { + return mData; + } } diff --git a/app/src/main/java/org/autojs/autojs/ui/widget/ViewHolderSupplier.java b/app/src/main/java/org/autojs/autojs/ui/widget/ViewHolderSupplier.java index df55553d..6178ac27 100644 --- a/app/src/main/java/org/autojs/autojs/ui/widget/ViewHolderSupplier.java +++ b/app/src/main/java/org/autojs/autojs/ui/widget/ViewHolderSupplier.java @@ -11,22 +11,30 @@ import java.lang.reflect.Constructor; * Created by Stardust on 2017/4/8. */ -public abstract class ViewHolderSupplier { +public interface ViewHolderSupplier { - public abstract VH createViewHolder(ViewGroup parent, int viewType); + VH createViewHolder(ViewGroup parent, int viewType); - public static ViewHolderSupplier of(final Class c, final int layoutRes) { - return new ViewHolderSupplier() { - @Override - public VH createViewHolder(ViewGroup parent, int viewType) { - try { - Constructor constructor = c.getConstructor(View.class); - return constructor.newInstance(LayoutInflater.from(parent.getContext()).inflate(layoutRes, parent, false)); - } catch (Exception e) { - throw new RuntimeException(e); - } + interface ViewHolderCreator { + VH createViewHolder(View itemView); + } + + static ViewHolderSupplier of(final Class c, final int layoutRes) { + return (parent, viewType) -> { + try { + Constructor constructor = c.getConstructor(View.class); + return constructor.newInstance(LayoutInflater.from(parent.getContext()).inflate(layoutRes, parent, false)); + } catch (Exception e) { + throw new RuntimeException(e); } }; } + static ViewHolderSupplier of(ViewHolderCreator creator, final int layoutRes) { + return (parent, viewType) -> + creator.createViewHolder(LayoutInflater.from(parent.getContext()).inflate(layoutRes, parent, false) + ); + } + + } diff --git a/app/src/main/res/drawable-xhdpi/ic_connect_to_pc.png b/app/src/main/res/drawable-xhdpi/ic_connect_to_pc.png new file mode 100644 index 00000000..c766543b Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_connect_to_pc.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_debug.png b/app/src/main/res/drawable-xhdpi/ic_debug.png index c766543b..1ae9a64d 100644 Binary files a/app/src/main/res/drawable-xhdpi/ic_debug.png and b/app/src/main/res/drawable-xhdpi/ic_debug.png differ diff --git a/app/src/main/res/drawable/ic_debug.png b/app/src/main/res/drawable/ic_connect_to_pc.png similarity index 100% rename from app/src/main/res/drawable/ic_debug.png rename to app/src/main/res/drawable/ic_connect_to_pc.png diff --git a/app/src/main/res/drawable/ic_script.png b/app/src/main/res/drawable/ic_script.png new file mode 100644 index 00000000..47fdf3c2 Binary files /dev/null and b/app/src/main/res/drawable/ic_script.png differ diff --git a/app/src/main/res/layout/debug_bar.xml b/app/src/main/res/layout/debug_bar.xml new file mode 100644 index 00000000..66b9f8f1 --- /dev/null +++ b/app/src/main/res/layout/debug_bar.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_code_evaluate.xml b/app/src/main/res/layout/dialog_code_evaluate.xml new file mode 100644 index 00000000..4421dc07 --- /dev/null +++ b/app/src/main/res/layout/dialog_code_evaluate.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/editor_view.xml b/app/src/main/res/layout/editor_view.xml index 29112e7c..09081cb3 100644 --- a/app/src/main/res/layout/editor_view.xml +++ b/app/src/main/res/layout/editor_view.xml @@ -88,6 +88,13 @@ android:layout_height="match_parent" android:visibility="gone"/> + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/menu_editor.xml b/app/src/main/res/menu/menu_editor.xml index bc7ae83f..744e95bc 100644 --- a/app/src/main/res/menu/menu_editor.xml +++ b/app/src/main/res/menu/menu_editor.xml @@ -94,6 +94,11 @@ android:id="@+id/action_launch_debugger" android:title="@string/text_launch_debugger" app:showAsAction="never"/> + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index fff2e2fc..5ab6524d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -367,4 +367,15 @@ 单步 继续 停止 + 增加监视的变量或表达式 + 变量或表达式 + 变量 + 删除所有断点 + 长按\"运行\"图标也可以启动调试 + 调试 [%s] + 复制值 + 执行代码 + 代码 + 结果 + 关闭 diff --git a/autojs/src/main/assets/modules/object-observe-lite.min.js b/autojs/src/main/assets/modules/object-observe-lite.min.js index 61a22aee..1e1a42d9 100644 --- a/autojs/src/main/assets/modules/object-observe-lite.min.js +++ b/autojs/src/main/assets/modules/object-observe-lite.min.js @@ -1,3 +1,3 @@ module.exports = function(){ - Object.observe||function(e,t,n,r){"use strict";var o,i,c=["add","update","delete","reconfigure","setPrototype","preventExtensions"],a=t.isArray||function(e){return function(t){return"[object Array]"===e.call(t)}}(e.prototype.toString),f=t.prototype.indexOf?t.indexOf||function(e,n,r){return t.prototype.indexOf.call(e,n,r)}:function(e,t,n){for(var r=n||0;r-1},get:function(n){return t[f(e,n)]},set:function(n,r){var o=f(e,n);-1===o?(e.push(n),t.push(r),this.size++):t[o]=r},"delete":function(n){var r=f(e,n);r>-1&&(e.splice(r,1),t.splice(r,1),this.size--)},forEach:function(n){for(var r=0;r-1&&n.splice(o,1);return n})}return t}():function(t){var n,r,o=[];if("hasOwnProperty"in t)for(n in t)t.hasOwnProperty(n)&&o.push(n);else{r=e.hasOwnProperty;for(n in t)r.call(t,n)&&o.push(n)}return a(t)&&o.push("length"),o},p=n.requestAnimationFrame||n.webkitRequestAnimationFrame||function(){var e=+new Date,t=e;return function(n){return setTimeout(function(){n((t=+new Date)-e)},17)}}(),l=function(e,t,n){var r=o.get(e);r?(v(r,e),g(e,r,t,n)):(r=h(e),g(e,r,t,n),1===o.size&&p(d))},h=function(e,t){for(var n=u(e),r=[],i=0,t={handlers:s(),properties:n,values:r,notifier:b(e,t)};i-1&&t.changeRecords.push(n)})};o=s(),i=s(),e.observe=function(t,n,o){if(!t||"object"!=typeof t&&"function"!=typeof t)throw new TypeError("Object.observe cannot observe non-object");if("function"!=typeof n)throw new TypeError("Object.observe cannot deliver to non-function");if(e.isFrozen&&e.isFrozen(n))throw new TypeError("Object.observe cannot deliver to a frozen function object");if(o===r)o=c;else if(!o||"object"!=typeof o)throw new TypeError("Third argument to Object.observe must be an array of strings.");return l(t,n,o),t},e.unobserve=function(e,t){if(null===e||"object"!=typeof e&&"function"!=typeof e)throw new TypeError("Object.unobserve cannot unobserve non-object");if("function"!=typeof t)throw new TypeError("Object.unobserve cannot deliver to non-function");var n,r=i.get(t);return r&&(n=r.observed.get(e))&&(r.observed.forEach(function(e,t){v(e.data,t)}),p(function(){y(r,t)}),1===r.observed.size&&r.observed.has(e)?i["delete"](t):r.observed["delete"](e),1===n.data.handlers.size?o["delete"](e):n.data.handlers["delete"](t)),e},e.getNotifier=function(t){if(null===t||"object"!=typeof t&&"function"!=typeof t)throw new TypeError("Object.getNotifier cannot getNotifier non-object");return e.isFrozen&&e.isFrozen(t)?null:b(t)},e.deliverChangeRecords=function(e){if("function"!=typeof e)throw new TypeError("Object.deliverChangeRecords cannot deliver to non-function");var t=i.get(e);t&&(t.observed.forEach(function(e,t){v(e.data,t)}),y(t,e))}}(Object,Array,this); + Object.observe||function(e,t,n,r){var o,i,c=["add","update","delete","reconfigure","setPrototype","preventExtensions"],a=t.isArray||function(e){return function(t){return"[object Array]"===e.call(t)}}(e.prototype.toString),f=t.prototype.indexOf?t.indexOf||function(e,n,r){return t.prototype.indexOf.call(e,n,r)}:function(e,t,n){for(var r=n||0;r-1},get:function(n){return t[f(e,n)]},set:function(n,r){var o=f(e,n);-1===o?(e.push(n),t.push(r),this.size++):t[o]=r},"delete":function(n){var r=f(e,n);r>-1&&(e.splice(r,1),t.splice(r,1),this.size--)},forEach:function(n){for(var r=0;r-1&&n.splice(o,1);return n})}return t}():function(t){var n,r,o=[];if("hasOwnProperty"in t)for(n in t)t.hasOwnProperty(n)&&o.push(n);else{r=e.hasOwnProperty;for(n in t)r.call(t,n)&&o.push(n)}return a(t)&&o.push("length"),o},p=n.requestAnimationFrame||n.webkitRequestAnimationFrame||function(){var e=+new Date,t=e;return function(n){return setTimeout(function(){n((t=+new Date)-e)},17)}}(),l=function(e,t,n){var r=o.get(e);r?(v(r,e),g(e,r,t,n)):(r=h(e),g(e,r,t,n),1===o.size&&p(d))},h=function(e,t){for(var n=u(e),r=[],i=0,t={handlers:s(),properties:n,values:r,notifier:b(e,t)};i-1&&t.changeRecords.push(n)})};o=s(),i=s(),e.observe=function(t,n,o){if(!t||"object"!=typeof t&&"function"!=typeof t)throw new TypeError("Object.observe cannot observe non-object");if("function"!=typeof n)throw new TypeError("Object.observe cannot deliver to non-function");if(e.isFrozen&&e.isFrozen(n))throw new TypeError("Object.observe cannot deliver to a frozen function object");if(o===r)o=c;else if(!o||"object"!=typeof o)throw new TypeError("Third argument to Object.observe must be an array of strings.");return l(t,n,o),t},e.unobserve=function(e,t){if(null===e||"object"!=typeof e&&"function"!=typeof e)throw new TypeError("Object.unobserve cannot unobserve non-object");if("function"!=typeof t)throw new TypeError("Object.unobserve cannot deliver to non-function");var n,r=i.get(t);return r&&(n=r.observed.get(e))&&(r.observed.forEach(function(e,t){v(e.data,t)}),p(function(){y(r,t)}),1===r.observed.size&&r.observed.has(e)?i["delete"](t):r.observed["delete"](e),1===n.data.handlers.size?o["delete"](e):n.data.handlers["delete"](t)),e},e.getNotifier=function(t){if(null===t||"object"!=typeof t&&"function"!=typeof t)throw new TypeError("Object.getNotifier cannot getNotifier non-object");return e.isFrozen&&e.isFrozen(t)?null:b(t)},e.deliverChangeRecords=function(e){if("function"!=typeof e)throw new TypeError("Object.deliverChangeRecords cannot deliver to non-function");var t=i.get(e);t&&(t.observed.forEach(function(e,t){v(e.data,t)}),y(t,e))}}(Object,Array,this); } \ No newline at end of file diff --git a/autojs/src/main/java/com/stardust/autojs/rhino/debug/Dim.java b/autojs/src/main/java/com/stardust/autojs/rhino/debug/Dim.java index ef5bed5d..95722e05 100644 --- a/autojs/src/main/java/com/stardust/autojs/rhino/debug/Dim.java +++ b/autojs/src/main/java/com/stardust/autojs/rhino/debug/Dim.java @@ -78,7 +78,7 @@ public class Dim { /** * The ContextFactory to listen to for debugging information. */ - private ContextFactory contextFactory; + private volatile ContextFactory contextFactory; private ScriptEngineService scriptEngineService; @@ -222,7 +222,7 @@ public class Dim { detach(); this.contextFactory = factory; this.scriptEngineService = scriptEngineService; - this.listener = new DimIProxy(this, IPROXY_LISTEN); + this.listener = new DimIProxy(IPROXY_LISTEN); scriptEngineService.registerEngineLifecycleCallback(this.listener); } @@ -517,6 +517,10 @@ public class Dim { } } + public boolean isAttached() { + return contextFactory != null && scriptEngineService != null; + } + /** * Returns the current ContextData object. */ @@ -585,7 +589,7 @@ public class Dim { * Compiles the given script. */ public void compileScript(String url, String text) { - DimIProxy action = new DimIProxy(this, IPROXY_COMPILE_SCRIPT); + DimIProxy action = new DimIProxy(IPROXY_COMPILE_SCRIPT); action.url = url; action.text = text; action.withContext(); @@ -595,7 +599,7 @@ public class Dim { * Evaluates the given script. */ public void evalScript(final String url, final String text) { - DimIProxy action = new DimIProxy(this, IPROXY_EVAL_SCRIPT); + DimIProxy action = new DimIProxy(IPROXY_EVAL_SCRIPT); action.url = url; action.text = text; action.withContext(); @@ -605,7 +609,7 @@ public class Dim { * Converts the given script object to a string. */ public String objectToString(Object object) { - DimIProxy action = new DimIProxy(this, IPROXY_OBJECT_TO_STRING); + DimIProxy action = new DimIProxy(IPROXY_OBJECT_TO_STRING); action.object = object; action.withContext(); return action.stringResult; @@ -615,7 +619,7 @@ public class Dim { * Returns whether the given string is syntactically valid script. */ public boolean stringIsCompilableUnit(String str) { - DimIProxy action = new DimIProxy(this, IPROXY_STRING_IS_COMPILABLE); + DimIProxy action = new DimIProxy(IPROXY_STRING_IS_COMPILABLE); action.text = str; action.withContext(); return action.booleanResult; @@ -625,7 +629,7 @@ public class Dim { * Returns the value of a property on the given script object. */ public Object getObjectProperty(Object object, Object id) { - DimIProxy action = new DimIProxy(this, IPROXY_OBJECT_PROPERTY); + DimIProxy action = new DimIProxy(IPROXY_OBJECT_PROPERTY); action.object = object; action.id = id; action.withContext(); @@ -636,7 +640,7 @@ public class Dim { * Returns an array of the property names on the given script object. */ public Object[] getObjectIds(Object object) { - DimIProxy action = new DimIProxy(this, IPROXY_OBJECT_IDS); + DimIProxy action = new DimIProxy(IPROXY_OBJECT_IDS); action.object = object; action.withContext(); return action.objectArrayResult; @@ -894,11 +898,6 @@ public class Dim { private class DimIProxy implements ContextAction, ScriptEngineManager.EngineLifecycleCallback, Debugger { - /** - * The debugger. - */ - private Dim dim; - /** * The interface implementation type. One of the IPROXY_* constants * defined in {@link Dim}. @@ -948,8 +947,7 @@ public class Dim { /** * Creates a new DimIProxy. */ - private DimIProxy(Dim dim, int type) { - this.dim = dim; + private DimIProxy(int type) { this.type = type; } @@ -966,8 +964,8 @@ public class Dim { case IPROXY_EVAL_SCRIPT: { Scriptable scope = null; - if (dim.scopeProvider != null) { - scope = dim.scopeProvider.getScope(); + if (scopeProvider != null) { + scope = scopeProvider.getScope(); } if (scope == null) { scope = new ImporterTopLevel(cx); @@ -993,11 +991,11 @@ public class Dim { break; case IPROXY_OBJECT_PROPERTY: - objectResult = dim.getObjectPropertyImpl(cx, object, id); + objectResult = getObjectPropertyImpl(cx, object, id); break; case IPROXY_OBJECT_IDS: - objectArrayResult = dim.getObjectIdsImpl(cx, object); + objectArrayResult = getObjectIdsImpl(cx, object); break; default: @@ -1011,7 +1009,7 @@ public class Dim { * {@link ContextFactory}. */ private void withContext() { - dim.contextFactory.call(this); + contextFactory.call(this); } @Override @@ -1024,7 +1022,7 @@ public class Dim { Context cx = ((RhinoJavaScriptEngine) engine).getContext(); ContextData contextData = new ContextData(); - Debugger debugger = new DimIProxy(dim, IPROXY_DEBUG); + Debugger debugger = new DimIProxy(IPROXY_DEBUG); cx.setDebugger(debugger, contextData); cx.setGeneratingDebug(true); cx.setOptimizationLevel(-1); @@ -1044,12 +1042,12 @@ public class Dim { public DebugFrame getFrame(Context cx, DebuggableScript fnOrScript) { if (type != IPROXY_DEBUG) Kit.codeBug(); - FunctionSource item = dim.getFunctionSource(fnOrScript); + FunctionSource item = getFunctionSource(fnOrScript); if (item == null) { // Can not debug if source is not available return null; } - return new StackFrame(cx, dim, item); + return new StackFrame(cx, Dim.this, item); } /** @@ -1063,7 +1061,7 @@ public class Dim { if (!fnOrScript.isTopLevel()) { return; } - dim.registerTopScript(fnOrScript, source); + registerTopScript(fnOrScript, source); } }