mirror of
https://github.com/adelphes/android-dev-ext.git
synced 2025-12-23 09:59:25 +00:00
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:
109
src/expression/assign.js
Normal file
109
src/expression/assign.js
Normal file
@@ -0,0 +1,109 @@
|
||||
const { Debugger } = require('../debugger');
|
||||
const { DebuggerValue, JavaTaggedValue, JavaType } = require('../debugger-types');
|
||||
const { NumberBaseConverter } = require('../utils/nbc');
|
||||
|
||||
const validmap = {
|
||||
B: 'BC', // char might not fit into a byte - we special-case this
|
||||
S: 'BSC',
|
||||
I: 'BSIC',
|
||||
J: 'BSIJC',
|
||||
F: 'BSIJCF',
|
||||
D: 'BSIJCFD',
|
||||
C: 'BSC',
|
||||
Z: 'Z',
|
||||
isCharInRangeForByte: c => c.charCodeAt(0) < 256,
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if the value will fit into a variable with given type
|
||||
* @param {JavaType} variable_type
|
||||
* @param {DebuggerValue} value
|
||||
*/
|
||||
function checkPrimitiveSize(variable_type, value) {
|
||||
// variable_type_signature must be a primitive
|
||||
if (!Object.prototype.hasOwnProperty.call(validmap, variable_type.signature)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let value_type_signature = value.type.signature;
|
||||
if (value.vtype === 'literal' && /[BSI]/.test(value_type_signature)) {
|
||||
// for integer literals, find the minimum type the value will fit into
|
||||
if (value.value >= -128 && value.value <= 127) value_type_signature = 'B';
|
||||
else if (value.value >= -32768 && value.value <= 32767) value_type_signature = 'S';
|
||||
else if (value.value >= -2147483648 && value.value <= 2147483647) value_type_signature = 'I';
|
||||
}
|
||||
|
||||
let is_in_range = validmap[variable_type.signature].indexOf(value_type_signature) >= 0;
|
||||
|
||||
// special check to see if a char value fits into a single byte
|
||||
if (JavaType.isByte(variable_type) && JavaType.isChar(value.type)) {
|
||||
is_in_range = validmap.isCharInRangeForByte(value.value);
|
||||
}
|
||||
|
||||
return is_in_range;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Debugger} dbgr
|
||||
* @param {DebuggerValue} destvar
|
||||
* @param {string} name
|
||||
* @param {DebuggerValue} result
|
||||
*/
|
||||
async function assignVariable(dbgr, destvar, name, result) {
|
||||
if (!destvar || !/^(field|local|arrelem)$/.test(destvar.vtype)) {
|
||||
throw new Error(`The value is read-only and cannot be updated.`);
|
||||
}
|
||||
|
||||
// non-string reference types can only set to null
|
||||
if (JavaType.isReference(destvar.type) && !JavaType.isString(destvar.type)) {
|
||||
if (!result.hasnullvalue) {
|
||||
throw new Error('Object references can only be set to null');
|
||||
}
|
||||
}
|
||||
|
||||
// as a nicety, if the destination is a string, stringify any primitive value
|
||||
if (JavaType.isPrimitive(result.type) && JavaType.isString(destvar.type)) {
|
||||
result = await dbgr.createJavaStringLiteral(result.value.toString(), { israw:true });
|
||||
}
|
||||
|
||||
if (JavaType.isPrimitive(destvar.type)) {
|
||||
// if the destination is a primitive, we need to range-check it here
|
||||
// Neither our debugger nor the JDWP endpoint validates primitives, so we end up with
|
||||
// weirdness if we allow primitives to be set with out-of-range values
|
||||
const is_in_range = checkPrimitiveSize(destvar.type, result);
|
||||
if (!is_in_range) {
|
||||
throw new Error(`'${result.value}' is not compatible with variable type: ${destvar.type.typename}`);
|
||||
}
|
||||
}
|
||||
|
||||
const data = JavaTaggedValue.from(result, destvar.type.signature);
|
||||
|
||||
if (JavaType.isLong(destvar.type) && typeof data.value === 'number') {
|
||||
// convert ints to hex-string longs
|
||||
data.value = NumberBaseConverter.decToHex(data.value.toString(),16);
|
||||
}
|
||||
|
||||
// convert the debugger value to a JavaTaggedValue
|
||||
let newlocalvar;
|
||||
// setxxxvalue sets the new value and then returns a new local for the variable
|
||||
switch(destvar.vtype) {
|
||||
case 'field':
|
||||
newlocalvar = await dbgr.setFieldValue(destvar.data.objvar, destvar.data.field, data);
|
||||
break;
|
||||
case 'local':
|
||||
newlocalvar = await dbgr.setLocalVariableValue(destvar.data.frame, destvar.data.slotinfo, data);
|
||||
break;
|
||||
case 'arrelem':
|
||||
newlocalvar = await dbgr.setArrayElements(destvar.data.array, parseInt(name, 10), 1, data);
|
||||
newlocalvar = newlocalvar[0];
|
||||
break;
|
||||
default:
|
||||
throw new Error('Unsupported variable type');
|
||||
}
|
||||
|
||||
return newlocalvar;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
assignVariable,
|
||||
}
|
||||
Reference in New Issue
Block a user