Version 1 (#83)

* replace jq-promises with native Promises

* updates to use native promises and async await

* Fix variable errors, remove extra parameters and correct export declaratons

* refactor launch request to use async/await

* fix running debugger on custom ADB port

* remove unused files

* move socket_ended check to ensure we don't loop reading 0 bytes

* refactor logcat code and ensure disconnect status is passed on to webview

* Fix warnings

* Clean up util and remove unused functions

* convert Debugger into a class

* update jsconfig target to es2018 and enable checkJS

* more updates to use async/await and more readable refactoring.

- added type definitions and debugger classes
- improved expression evaluation
- refactored expressions into parsing, evaluation and variable assignment
- fixed invoking methods with parameters
- added support for static method invokes
- improved exception display reliability
- refactored launch into smaller functions
- refactored utils into smaller modules
- removed redundant code
- converted JDWP functions to classes

* set version 1.0.0 and update dependencies

* add changelog notes
This commit is contained in:
Dave Holoway
2020-04-20 12:53:08 +01:00
committed by GitHub
parent f92f247ef6
commit 0672e54401
45 changed files with 8050 additions and 9829 deletions

53
src/utils/char-decode.js Normal file
View File

@@ -0,0 +1,53 @@
const BACKSLASH_ESCAPE_MAP = {
b: '\b',
f: '\f',
r: '\r',
n: '\n',
t: '\t',
v: '\v',
'0': '\0',
'\\': '\\',
};
/**
* De-escape backslash escaped characters
* @param {string} c
*/
function decode_char(c) {
switch(true) {
case /^\\u[0-9a-fA-F]{4}$/.test(c):
// unicode escape
return String.fromCharCode(parseInt(c.slice(2),16));
case /^\\.$/.test(c):
// backslash escape
const char = BACKSLASH_ESCAPE_MAP[c[1]];
return char || c[1];
case c.length === 1:
return c;
}
throw new Error('Invalid character value');
}
/**
* Convert a Java string literal to a raw string
* @param {string} s
*/
function decodeJavaStringLiteral(s) {
return s.slice(1, -1).replace(/\\u[0-9a-fA-F]{4}|\\./g, decode_char);
}
/**
* Convert a Java char literal to a raw character
* @param {string} s
*/
function decodeJavaCharLiteral(s) {
return decode_char(s.slice(1, -1));
}
module.exports = {
decode_char,
decodeJavaCharLiteral,
decodeJavaStringLiteral,
}

103
src/utils/nbc.js Normal file
View File

@@ -0,0 +1,103 @@
// arbitrary precision helper class for 64 bit numbers
const NumberBaseConverter = {
// Adds two arrays for the given base (10 or 16), returning the result.
add(x, y, base) {
const z = [], n = Math.max(x.length, y.length);
let carry = 0;
for (let i = 0; i < n || carry; i++) {
const xi = i < x.length ? x[i] : 0;
const yi = i < y.length ? y[i] : 0;
const zi = carry + xi + yi;
z.push(zi % base);
carry = Math.floor(zi / base);
}
return z;
},
// Returns a*x, where x is an array of decimal digits and a is an ordinary
// JavaScript number. base is the number base of the array x.
multiplyByNumber(num, x, base) {
if (num < 0) {
return null;
}
if (num == 0) {
return [];
}
let result = [], power = x;
for(;;) {
if (num & 1) {
result = this.add(result, power, base);
}
num = num >> 1;
if (num === 0) {
return result;
}
power = this.add(power, power, base);
}
},
twosComplement(str, base) {
const invdigits = str.split('').map(c => base - 1 - parseInt(c,base)).reverse();
const negdigits = this.add(invdigits, [1], base).slice(0,str.length);
return negdigits.reverse().map(d => d.toString(base)).join('');
},
convertBase(str, fromBase, toBase) {
if (fromBase === 10 && /[eE]/.test(str)) {
// convert exponents to a string of zeros
const s = str.split(/[eE]/);
str = s[0] + '0'.repeat(parseInt(s[1],10)); // works for 0/+ve exponent,-ve throws
}
const digits = str.split('').map(d => parseInt(d,fromBase)).reverse();
let outArray = [], power = [1];
for (let i = 0; i < digits.length; i++) {
if (digits[i]) {
outArray = this.add(outArray, this.multiplyByNumber(digits[i], power, toBase), toBase);
}
power = this.multiplyByNumber(fromBase, power, toBase);
}
return outArray.reverse().map(d => d.toString(toBase)).join('');
},
decToHex(decstr, minlen) {
let res;
const isneg = decstr[0] === '-';
if (isneg) {
decstr = decstr.slice(1);
}
decstr = decstr.match(/^0*(.+)$/)[1]; // strip leading zeros
if (decstr.length < 16 && !/[eE]/.test(decstr)) { // 16 = Math.pow(2,52).toString(10).length
// less than 52 bits - just use parseInt
res = parseInt(decstr, 10).toString(16);
} else {
res = NumberBaseConverter.convertBase(decstr, 10, 16);
}
if (isneg) {
res = NumberBaseConverter.twosComplement(res, 16);
if (/^[0-7]/.test(res)) res = 'f'+res; //msb must be set for -ve numbers
} else if (/^[^0-7]/.test(res)) {
res = '0' + res; // msb must not be set for +ve numbers
}
if (minlen && res.length < minlen) {
res = (isneg?'f':'0').repeat(minlen - res.length) + res;
}
return res;
},
hexToDec(hexstr, signed) {
const isneg = /^[^0-7]/.test(hexstr);
if (hexstr.match(/^0*(.+)$/)[1].length*4 < 52) {
// less than 52 bits - just use parseInt
let res = parseInt(hexstr, 16);
if (signed && isneg) {
res = -res;
}
return res.toString(10);
}
if (isneg) {
hexstr = NumberBaseConverter.twosComplement(hexstr, 16);
}
const res = (isneg ? '-' : '') + NumberBaseConverter.convertBase(hexstr, 16, 10);
return res;
},
};
module.exports = {
NumberBaseConverter,
}

51
src/utils/print.js Normal file
View File

@@ -0,0 +1,51 @@
/**
* Set of callbacks to be called when any message is output to the console
* @type {Set<Function>}
* */
const messagePrintCallbacks = new Set();
function callMessagePrintCallbacks(args) {
messagePrintCallbacks.forEach(cb => cb(...args));
}
/**
* print a debug message to the console
* @param {...any} args
*/
function D(...args) {
console.log(...args);
callMessagePrintCallbacks(args);
}
/**
* print an error message to the console
* @param {...any} args
*/
function E(...args) {
console.error(...args);
callMessagePrintCallbacks(args);
}
/**
* print a warning message to the console
* @param {...any} args
*/
function W(...args) {
console.warn(...args);
callMessagePrintCallbacks(args);
}
/**
* Adds a callback to be called when any message is output
* @param {Function} cb
*/
function onMessagePrint(cb) {
messagePrintCallbacks.add(cb);
}
module.exports = {
D,
E,
W,
onMessagePrint,
}

21
src/utils/source-file.js Normal file
View File

@@ -0,0 +1,21 @@
/**
* Returns true if the string has an extension we recognise as a source file
* @param {string} s
*/
function hasValidSourceFileExtension(s) {
return /\.(java|kt)$/i.test(s);
}
function splitSourcePath(filepath) {
const m = filepath.match(/^\/([^/]+(?:\/[^/]+)*)?\/([^./]+)\.(java|kt)$/);
return {
pkg: m[1].replace(/\/+/g, '.'),
type: m[2],
qtype: `${m[1]}/${m[2]}`,
}
}
module.exports = {
hasValidSourceFileExtension,
splitSourcePath,
}

11
src/utils/thread.js Normal file
View File

@@ -0,0 +1,11 @@
/**
* Returns a Promise which resolves after the specified period.
* @param {number} ms wait time in milliseconds
*/
function sleep(ms) {
return new Promise(r => setTimeout(r, ms));
}
module.exports = {
sleep,
}