Files
android-dev-ext/src/threads.js
Dave Holoway cb6c78070c version 1.4.0 (#144)
* upgrade package-lock.jsons

* upgrade debugadapter package

* upgrade debugprotocol package

* upgrade long package

* upgrade unzipper package

* upgrade uuid package

* upgrade ws package

* upgrade xpath package

* update dev dependencies

* fix eslint config to allow newer language features (async shorthand functions and optional catch parameters)

* fix import type declarations

* update eslint

* remove unsupported stopOnEntry properties

* code tidy - fix warnings, separate type imports from value imports, remove unused code

* report stack on adb connection error and default host name to 127.0.0.1

* fix imported types in jdwp

* lang server tidyups

* add a new helper for creating android API library cache file

* update the android API cache file to 34

* bump to version 1.4.0
2024-01-02 15:52:50 +00:00

189 lines
5.7 KiB
JavaScript

const { DebuggerStackFrame } = require('./stack-frame');
const { VariableManager } = require('./variable-manager');
/**
* @typedef {import('./debugger').Debugger} Debugger
* @typedef {import('./debugger-types').DebuggerException} DebuggerException
* @typedef {import('./debugger-types').DebuggerFrameInfo} DebuggerFrameInfo
* @typedef {import('./debugger-types').SourceLocation} SourceLocation
*/
// vscode doesn't like thread id reuse (the Android runtime is OK with it)
let nextVSCodeThreadId = 0;
/**
* Scales used to build VSCVariableReferences.
* Each reference contains a thread id, frame id and variable index.
* eg. VariableReference 1005000000 has thread:1 and frame:5
*
* The variable index is the bottom 1M values.
* - A 0 value is used for locals scope
* - A 1 value is used for exception scope
* - Values above 10 are used for variables
*/
const var_ref_thread_scale = 1e9;
const var_ref_frame_scale = 1e6;
const var_ref_global_frame = 999e6;
class ThreadPauseInfo {
/**
* @param {string} reason
* @param {SourceLocation} location
* @param {DebuggerException} last_exception
*/
constructor(reason, location, last_exception) {
this.when = Date.now(); // when
this.reasons = [reason]; // why
this.location = location; // where
this.last_exception = last_exception;
/**
* @type {Map<VSCVariableReference,DebuggerStackFrame>}
*/
this.stack_frames = new Map();
/**
* instance used to manage variables created for expressions evaluated in the global context
* @type {VariableManager}
*/
this.global_vars = null;
this.stoppedEvent = null; // event we (eventually) send to vscode
}
/**
* @param {number} frameId
*/
getLocals(frameId) {
return this.stack_frames.get(frameId).locals;
}
}
/*
Class used to manage a single thread reported by JDWP
*/
class AndroidThread {
/**
*
* @param {Debugger} dbgr
* @param {string} name
* @param {JavaThreadID} threadid
*/
constructor(dbgr, name, threadid) {
// the Android debugger instance
this.dbgr = dbgr;
// the java thread id (hex string)
this.threadid = threadid;
// the vscode thread id (number)
this.vscode_threadid = (nextVSCodeThreadId += 1);
// the (Java) name of the thread
this.name = name;
// the thread break info
this.paused = null;
// the timeout during a step which, if it expires, we allow other threads to break
this.stepTimeout = null;
}
threadNotSuspendedError() {
return new Error(`Thread ${this.vscode_threadid} not suspended`);
}
/**
* @param {DebuggerFrameInfo} frame
* @param {number} call_stack_level
*/
createStackFrameVariable(frame, call_stack_level) {
if (!this.paused) {
throw this.threadNotSuspendedError();
}
const frameId = AndroidThread.makeFrameVariableReference(this.vscode_threadid, call_stack_level) ;
const stack_frame = new DebuggerStackFrame(this.dbgr, frame, frameId);
this.paused.stack_frames.set(frameId, stack_frame);
return stack_frame;
}
/**
* Retrieve the variable manager used to maintain variableReferences for
* expressions evaluated in the global context for this thread.
*/
getGlobalVariableManager() {
if (!this.paused) {
throw this.threadNotSuspendedError();
}
if (!this.paused.global_vars) {
const globalFrameId = AndroidThread.makeGlobalVariableReference(this.vscode_threadid) ;
this.paused.global_vars = new VariableManager(globalFrameId);
}
return this.paused.global_vars;
}
/**
* set a new VSCode thread ID for this thread
*/
allocateNewThreadID() {
this.vscode_threadid = (nextVSCodeThreadId += 1);
}
clearStepTimeout() {
if (this.stepTimeout) {
clearTimeout(this.stepTimeout);
this.stepTimeout = null;
}
}
/**
* @param {VSCVariableReference} variablesReference
*/
findStackFrame(variablesReference) {
if (!this.paused) {
return null;
}
const stack_frame_ref = AndroidThread.variableRefToFrameId(variablesReference);
return this.paused.stack_frames.get(stack_frame_ref);
}
/**
* @param {string} reason
* @param {SourceLocation} location
* @param {DebuggerException} last_exception
*/
setPaused(reason, location, last_exception) {
this.paused = new ThreadPauseInfo(reason, location, last_exception);
this.clearStepTimeout();
}
/**
* @param {VSCThreadID} vscode_threadid
* @param {number} call_stack_level
* @returns {VSCVariableReference}
*/
static makeFrameVariableReference(vscode_threadid, call_stack_level) {
return (vscode_threadid * var_ref_thread_scale) + (call_stack_level * var_ref_frame_scale)
}
static makeGlobalVariableReference(vscode_threadid) {
return (vscode_threadid * var_ref_thread_scale) + var_ref_global_frame;
}
/**
* Convert a variable reference ID to a VSCode thread ID
* @param {VSCVariableReference} variablesReference
*/
static variableRefToThreadId(variablesReference) {
return Math.trunc(variablesReference / var_ref_thread_scale);
}
/**
* Convert a variable reference ID to a frame ID
* @param {VSCVariableReference} variablesReference
*/
static variableRefToFrameId(variablesReference) {
return Math.trunc(variablesReference / var_ref_frame_scale) * var_ref_frame_scale;
}
}
module.exports = {
AndroidThread,
}