feat(debug): supports debugging across files; supports watching variables; supports evaluate code on debug;

This commit is contained in:
hyb1996 2018-09-08 17:30:01 +08:00
parent 5329278e8d
commit a2c46d0787
27 changed files with 786 additions and 103 deletions

View File

@ -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;

View File

@ -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);
}

View File

@ -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;
}
}

View File

@ -0,0 +1,5 @@
package org.autojs.autojs.ui.edit.debug;
public interface CodeEvaluator {
String eval(String code);
}

View File

@ -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<WatchingVariable> 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<WatchingVariable> 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<WatchingVariable> {
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);
}
}
}

View File

@ -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;
}
}

View File

@ -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<CodeEditor.CursorChangeCallback> 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));
}

View File

@ -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) {

View File

@ -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;

View File

@ -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<WatchingVariable> 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<Integer> 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();
}
}

View File

@ -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<Integer> getMenuItemIds() {
return Arrays.asList(R.id.run, R.id.undo, R.id.redo, R.id.save);

View File

@ -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<Integer> mMenuItemIds;
public void setOnMenuItemClickListener(OnMenuItemClickListener listener) {
mOnMenuItemClickListener = listener;
}
public void setOnMenuItemLongClickListener(OnMenuItemLongClickListener onMenuItemLongClickListener) {
mOnMenuItemLongClickListener = onMenuItemLongClickListener;
}
public abstract List<Integer> 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();

View File

@ -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

View File

@ -14,12 +14,16 @@ import java.util.List;
public class AutoAdapter<DT> extends RecyclerView.Adapter<BindableViewHolder<DT>> {
private ViewHolderSupplier<? extends BindableViewHolder<DT>> mViewHolderSupplier;
private List<DT> mList = new ArrayList<>();
private final List<DT> mData = new ArrayList<>();
public AutoAdapter(ViewHolderSupplier<? extends BindableViewHolder<DT>> viewHolderSupplier) {
mViewHolderSupplier = viewHolderSupplier;
}
public AutoAdapter(ViewHolderSupplier.ViewHolderCreator<? extends BindableViewHolder<DT>> viewHolderCreator, int layoutRes) {
mViewHolderSupplier = ViewHolderSupplier.of(viewHolderCreator, layoutRes);
}
@Override
public BindableViewHolder<DT> onCreateViewHolder(ViewGroup parent, int viewType) {
@ -28,36 +32,48 @@ public class AutoAdapter<DT> extends RecyclerView.Adapter<BindableViewHolder<DT>
@Override
public void onBindViewHolder(BindableViewHolder<DT> 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<? extends DT> 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<? extends DT> 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<DT> getData() {
return mData;
}
}

View File

@ -11,22 +11,30 @@ import java.lang.reflect.Constructor;
* Created by Stardust on 2017/4/8.
*/
public abstract class ViewHolderSupplier<VH extends RecyclerView.ViewHolder> {
public interface ViewHolderSupplier<VH extends RecyclerView.ViewHolder> {
public abstract VH createViewHolder(ViewGroup parent, int viewType);
VH createViewHolder(ViewGroup parent, int viewType);
public static <VH extends RecyclerView.ViewHolder> ViewHolderSupplier<VH> of(final Class<VH> c, final int layoutRes) {
return new ViewHolderSupplier<VH>() {
@Override
public VH createViewHolder(ViewGroup parent, int viewType) {
try {
Constructor<VH> 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 extends RecyclerView.ViewHolder> {
VH createViewHolder(View itemView);
}
static <VH extends RecyclerView.ViewHolder> ViewHolderSupplier<VH> of(final Class<VH> c, final int layoutRes) {
return (parent, viewType) -> {
try {
Constructor<VH> constructor = c.getConstructor(View.class);
return constructor.newInstance(LayoutInflater.from(parent.getContext()).inflate(layoutRes, parent, false));
} catch (Exception e) {
throw new RuntimeException(e);
}
};
}
static <VH extends RecyclerView.ViewHolder> ViewHolderSupplier<VH> of(ViewHolderCreator<VH> creator, final int layoutRes) {
return (parent, viewType) ->
creator.createViewHolder(LayoutInflater.from(parent.getContext()).inflate(layoutRes, parent, false)
);
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#aaffa726"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal"
android:padding="8dp">
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginRight="8dp"
android:src="@drawable/ic_debug"/>
<TextView
android:id="@+id/title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/text_debug"
android:textColor="@android:color/white"
android:textSize="15sp"/>
<ImageView
android:id="@+id/execute"
android:layout_width="28dp"
android:layout_height="28dp"
android:layout_marginRight="8dp"
android:background="?selectableItemBackgroundBorderless"
android:src="@drawable/ic_script"
android:tint="@android:color/white"/>
<ImageView
android:id="@+id/add"
android:layout_width="28dp"
android:layout_height="28dp"
android:background="?selectableItemBackgroundBorderless"
android:src="@drawable/ic_add_white_48dp"
android:tint="@android:color/white"/>
</LinearLayout>
<android.support.v7.widget.RecyclerView
android:id="@+id/variables"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>

View File

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.stardust.theme.widget.ThemeColorTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="8dp"
android:text="@string/text_code"
android:textSize="14sp"/>
<EditText
android:id="@+id/code"
android:layout_width="match_parent"
android:paddingBottom="8dp"
android:paddingLeft="8dp"
android:textSize="15sp"
android:layout_height="wrap_content"
android:textColor="@android:color/primary_text_light"/>
<com.stardust.theme.widget.ThemeColorTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingBottom="8dp"
android:paddingLeft="8dp"
android:text="@string/text_result"
android:textSize="14sp"/>
<TextView
android:id="@+id/result"
android:paddingLeft="8dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#727377"
android:textSize="16sp"/>
</LinearLayout>

View File

@ -88,6 +88,13 @@
android:layout_height="match_parent"
android:visibility="gone"/>
<org.autojs.autojs.ui.edit.debug.DebugBar
android:id="@+id/debug_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"/>
</LinearLayout>
<org.autojs.autojs.ui.widget.EWebView

View File

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="28dp"
android:gravity="center_vertical"
android:orientation="horizontal"
android:background="?selectableItemBackground"
android:paddingLeft="12dp">
<TextView
android:id="@+id/variable"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:ellipsize="end"
android:maxLines="1"
android:textColor="@android:color/black"
android:textSize="14sp"
tools:text="variable = value"/>
<ImageView
android:id="@+id/icon"
android:layout_width="28dp"
android:layout_height="28dp"
android:background="?selectableItemBackgroundBorderless"
android:padding="2dp"
android:layout_marginRight="6dp"
android:src="@drawable/ic_delete_black_48dp"
android:tint="@android:color/white"/>
</LinearLayout>

View File

@ -94,6 +94,11 @@
android:id="@+id/action_launch_debugger"
android:title="@string/text_launch_debugger"
app:showAsAction="never"/>
<item
android:id="@+id/action_remove_all_breakpoints"
android:title="@string/text_remove_all_breakpoints"
app:showAsAction="never"/>
</menu>
</item>

View File

@ -367,4 +367,15 @@
<string name="text_debug_step_over">单步</string>
<string name="text_debug_resume_script">继续</string>
<string name="text_stop">停止</string>
<string name="text_new_watching_variable">增加监视的变量或表达式</string>
<string name="text_variable_or_expr">变量或表达式</string>
<string name="text_variable">变量</string>
<string name="text_remove_all_breakpoints">删除所有断点</string>
<string name="hint_long_click_run_to_debug">长按\"运行\"图标也可以启动调试</string>
<string name="format_debug_bar_title">调试 [%s]</string>
<string name="text_copy_value">复制值</string>
<string name="text_execute_code">执行代码</string>
<string name="text_code">代码</string>
<string name="text_result">结果</string>
<string name="text_close">关闭</string>
</resources>

View File

@ -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<e.length;r++)if(e[r]===t)return r;return-1},s=n.Map!==r&&Map.prototype.forEach?function(){return new Map}:function(){var e=[],t=[];return{size:0,has:function(t){return f(e,t)>-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<e.length;r++)n.call(arguments[1],t[r],e[r],this)}}},u=e.getOwnPropertyNames?function(){var t=e.getOwnPropertyNames;try{arguments.callee}catch(n){var r=(t(f).join(" ")+" ").replace(/prototype |length |name /g,"").slice(0,-1).split(" ");r.length&&(t=function(t){var n=e.getOwnPropertyNames(t);if("function"==typeof t)for(var o,i=0;i<r.length;)(o=f(n,r[i++]))>-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<n.length;)r[i]=e[n[i++]];return o.set(e,t),t},v=function(e,t,n){if(e.handlers.size){var r,o,i,c,a,s,p,l=e.values,h=0;for(r=e.properties.slice(),o=r.length,i=u(t);h<i.length;)a=i[h++],c=f(r,a),s=t[a],-1===c?(w(t,e,{name:a,type:"add",object:t},n),e.properties.push(a),l.push(s)):(p=l[c],r[c]=null,o--,(p===s?0===p&&1/p!==1/s:p===p||s===s)&&(w(t,e,{name:a,type:"update",object:t,oldValue:p},n),e.values[c]=s));for(h=r.length;o&&h--;)null!==r[h]&&(w(t,e,{name:r[h],type:"delete",object:t,oldValue:l[h]},n),e.properties.splice(h,1),e.values.splice(h,1),o--)}},d=function(){o.size&&(o.forEach(v),i.forEach(y),p(d))},y=function(e,t){var n=e.changeRecords;n.length&&(e.changeRecords=[],t(n))},b=function(e,t){return arguments.length<2&&(t=o.get(e)),t&&t.notifier||{notify:function(t){t.type;var n=o.get(e);if(n){var r,i={object:e};for(r in t)"object"!==r&&(i[r]=t[r]);w(e,n,i)}},performChange:function(t,n){if("string"!=typeof t)throw new TypeError("Invalid non-string changeType");if("function"!=typeof n)throw new TypeError("Cannot perform non-function");var i,c,a=o.get(e),f=arguments[2],s=f===r?n():n.call(f);if(a&&v(a,e,t),a&&s&&"object"==typeof s){c={object:e,type:t};for(i in s)"object"!==i&&"type"!==i&&(c[i]=s[i]);w(e,a,c)}}}},g=function(e,t,n,r){var o=i.get(n);o||i.set(n,o={observed:s(),changeRecords:[]}),o.observed.set(e,{acceptList:r.slice(),data:t}),t.handlers.set(n,o)},w=function(e,t,n,r){t.handlers.forEach(function(t){var o=t.observed.get(e).acceptList;("string"!=typeof r||-1===f(o,r))&&f(o,n.type)>-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<e.length;r++)if(e[r]===t)return r;return-1},s=n.Map!==r&&Map.prototype.forEach?function(){return new Map}:function(){var e=[],t=[];return{size:0,has:function(t){return f(e,t)>-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<e.length;r++)n.call(arguments[1],t[r],e[r],this)}}},u=e.getOwnPropertyNames?function(){var t=e.getOwnPropertyNames;try{arguments.callee}catch(n){var r=(t(f).join(" ")+" ").replace(/prototype |length |name /g,"").slice(0,-1).split(" ");r.length&&(t=function(t){var n=e.getOwnPropertyNames(t);if("function"==typeof t)for(var o,i=0;i<r.length;)(o=f(n,r[i++]))>-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<n.length;)r[i]=e[n[i++]];return o.set(e,t),t},v=function(e,t,n){if(e.handlers.size){var r,o,i,c,a,s,p,l=e.values,h=0;for(r=e.properties.slice(),o=r.length,i=u(t);h<i.length;)a=i[h++],c=f(r,a),s=t[a],-1===c?(w(t,e,{name:a,type:"add",object:t},n),e.properties.push(a),l.push(s)):(p=l[c],r[c]=null,o--,(p===s?0===p&&1/p!==1/s:p===p||s===s)&&(w(t,e,{name:a,type:"update",object:t,oldValue:p},n),e.values[c]=s));for(h=r.length;o&&h--;)null!==r[h]&&(w(t,e,{name:r[h],type:"delete",object:t,oldValue:l[h]},n),e.properties.splice(h,1),e.values.splice(h,1),o--)}},d=function(){o.size&&(o.forEach(v),i.forEach(y),p(d))},y=function(e,t){var n=e.changeRecords;n.length&&(e.changeRecords=[],t(n))},b=function(e,t){return arguments.length<2&&(t=o.get(e)),t&&t.notifier||{notify:function(t){t.type;var n=o.get(e);if(n){var r,i={object:e};for(r in t)"object"!==r&&(i[r]=t[r]);w(e,n,i)}},performChange:function(t,n){if("string"!=typeof t)throw new TypeError("Invalid non-string changeType");if("function"!=typeof n)throw new TypeError("Cannot perform non-function");var i,c,a=o.get(e),f=arguments[2],s=f===r?n():n.call(f);if(a&&v(a,e,t),a&&s&&"object"==typeof s){c={object:e,type:t};for(i in s)"object"!==i&&"type"!==i&&(c[i]=s[i]);w(e,a,c)}}}},g=function(e,t,n,r){var o=i.get(n);o||i.set(n,o={observed:s(),changeRecords:[]}),o.observed.set(e,{acceptList:r.slice(),data:t}),t.handlers.set(n,o)},w=function(e,t,n,r){t.handlers.forEach(function(t){var o=t.observed.get(e).acceptList;("string"!=typeof r||-1===f(o,r))&&f(o,n.type)>-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);
}

View File

@ -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);
}
}