frida-java-bridge
Version:
Java runtime interop from Frida
950 lines (809 loc) • 33.9 kB
JavaScript
export default function Env (handle, vm) {
this.handle = handle;
this.vm = vm;
}
const pointerSize = Process.pointerSize;
const JNI_ABORT = 2;
const CALL_CONSTRUCTOR_METHOD_OFFSET = 28;
const CALL_OBJECT_METHOD_OFFSET = 34;
const CALL_BOOLEAN_METHOD_OFFSET = 37;
const CALL_BYTE_METHOD_OFFSET = 40;
const CALL_CHAR_METHOD_OFFSET = 43;
const CALL_SHORT_METHOD_OFFSET = 46;
const CALL_INT_METHOD_OFFSET = 49;
const CALL_LONG_METHOD_OFFSET = 52;
const CALL_FLOAT_METHOD_OFFSET = 55;
const CALL_DOUBLE_METHOD_OFFSET = 58;
const CALL_VOID_METHOD_OFFSET = 61;
const CALL_NONVIRTUAL_OBJECT_METHOD_OFFSET = 64;
const CALL_NONVIRTUAL_BOOLEAN_METHOD_OFFSET = 67;
const CALL_NONVIRTUAL_BYTE_METHOD_OFFSET = 70;
const CALL_NONVIRTUAL_CHAR_METHOD_OFFSET = 73;
const CALL_NONVIRTUAL_SHORT_METHOD_OFFSET = 76;
const CALL_NONVIRTUAL_INT_METHOD_OFFSET = 79;
const CALL_NONVIRTUAL_LONG_METHOD_OFFSET = 82;
const CALL_NONVIRTUAL_FLOAT_METHOD_OFFSET = 85;
const CALL_NONVIRTUAL_DOUBLE_METHOD_OFFSET = 88;
const CALL_NONVIRTUAL_VOID_METHOD_OFFSET = 91;
const CALL_STATIC_OBJECT_METHOD_OFFSET = 114;
const CALL_STATIC_BOOLEAN_METHOD_OFFSET = 117;
const CALL_STATIC_BYTE_METHOD_OFFSET = 120;
const CALL_STATIC_CHAR_METHOD_OFFSET = 123;
const CALL_STATIC_SHORT_METHOD_OFFSET = 126;
const CALL_STATIC_INT_METHOD_OFFSET = 129;
const CALL_STATIC_LONG_METHOD_OFFSET = 132;
const CALL_STATIC_FLOAT_METHOD_OFFSET = 135;
const CALL_STATIC_DOUBLE_METHOD_OFFSET = 138;
const CALL_STATIC_VOID_METHOD_OFFSET = 141;
const GET_OBJECT_FIELD_OFFSET = 95;
const GET_BOOLEAN_FIELD_OFFSET = 96;
const GET_BYTE_FIELD_OFFSET = 97;
const GET_CHAR_FIELD_OFFSET = 98;
const GET_SHORT_FIELD_OFFSET = 99;
const GET_INT_FIELD_OFFSET = 100;
const GET_LONG_FIELD_OFFSET = 101;
const GET_FLOAT_FIELD_OFFSET = 102;
const GET_DOUBLE_FIELD_OFFSET = 103;
const SET_OBJECT_FIELD_OFFSET = 104;
const SET_BOOLEAN_FIELD_OFFSET = 105;
const SET_BYTE_FIELD_OFFSET = 106;
const SET_CHAR_FIELD_OFFSET = 107;
const SET_SHORT_FIELD_OFFSET = 108;
const SET_INT_FIELD_OFFSET = 109;
const SET_LONG_FIELD_OFFSET = 110;
const SET_FLOAT_FIELD_OFFSET = 111;
const SET_DOUBLE_FIELD_OFFSET = 112;
const GET_STATIC_OBJECT_FIELD_OFFSET = 145;
const GET_STATIC_BOOLEAN_FIELD_OFFSET = 146;
const GET_STATIC_BYTE_FIELD_OFFSET = 147;
const GET_STATIC_CHAR_FIELD_OFFSET = 148;
const GET_STATIC_SHORT_FIELD_OFFSET = 149;
const GET_STATIC_INT_FIELD_OFFSET = 150;
const GET_STATIC_LONG_FIELD_OFFSET = 151;
const GET_STATIC_FLOAT_FIELD_OFFSET = 152;
const GET_STATIC_DOUBLE_FIELD_OFFSET = 153;
const SET_STATIC_OBJECT_FIELD_OFFSET = 154;
const SET_STATIC_BOOLEAN_FIELD_OFFSET = 155;
const SET_STATIC_BYTE_FIELD_OFFSET = 156;
const SET_STATIC_CHAR_FIELD_OFFSET = 157;
const SET_STATIC_SHORT_FIELD_OFFSET = 158;
const SET_STATIC_INT_FIELD_OFFSET = 159;
const SET_STATIC_LONG_FIELD_OFFSET = 160;
const SET_STATIC_FLOAT_FIELD_OFFSET = 161;
const SET_STATIC_DOUBLE_FIELD_OFFSET = 162;
const callMethodOffset = {
pointer: CALL_OBJECT_METHOD_OFFSET,
uint8: CALL_BOOLEAN_METHOD_OFFSET,
int8: CALL_BYTE_METHOD_OFFSET,
uint16: CALL_CHAR_METHOD_OFFSET,
int16: CALL_SHORT_METHOD_OFFSET,
int32: CALL_INT_METHOD_OFFSET,
int64: CALL_LONG_METHOD_OFFSET,
float: CALL_FLOAT_METHOD_OFFSET,
double: CALL_DOUBLE_METHOD_OFFSET,
void: CALL_VOID_METHOD_OFFSET
};
const callNonvirtualMethodOffset = {
pointer: CALL_NONVIRTUAL_OBJECT_METHOD_OFFSET,
uint8: CALL_NONVIRTUAL_BOOLEAN_METHOD_OFFSET,
int8: CALL_NONVIRTUAL_BYTE_METHOD_OFFSET,
uint16: CALL_NONVIRTUAL_CHAR_METHOD_OFFSET,
int16: CALL_NONVIRTUAL_SHORT_METHOD_OFFSET,
int32: CALL_NONVIRTUAL_INT_METHOD_OFFSET,
int64: CALL_NONVIRTUAL_LONG_METHOD_OFFSET,
float: CALL_NONVIRTUAL_FLOAT_METHOD_OFFSET,
double: CALL_NONVIRTUAL_DOUBLE_METHOD_OFFSET,
void: CALL_NONVIRTUAL_VOID_METHOD_OFFSET
};
const callStaticMethodOffset = {
pointer: CALL_STATIC_OBJECT_METHOD_OFFSET,
uint8: CALL_STATIC_BOOLEAN_METHOD_OFFSET,
int8: CALL_STATIC_BYTE_METHOD_OFFSET,
uint16: CALL_STATIC_CHAR_METHOD_OFFSET,
int16: CALL_STATIC_SHORT_METHOD_OFFSET,
int32: CALL_STATIC_INT_METHOD_OFFSET,
int64: CALL_STATIC_LONG_METHOD_OFFSET,
float: CALL_STATIC_FLOAT_METHOD_OFFSET,
double: CALL_STATIC_DOUBLE_METHOD_OFFSET,
void: CALL_STATIC_VOID_METHOD_OFFSET
};
const getFieldOffset = {
pointer: GET_OBJECT_FIELD_OFFSET,
uint8: GET_BOOLEAN_FIELD_OFFSET,
int8: GET_BYTE_FIELD_OFFSET,
uint16: GET_CHAR_FIELD_OFFSET,
int16: GET_SHORT_FIELD_OFFSET,
int32: GET_INT_FIELD_OFFSET,
int64: GET_LONG_FIELD_OFFSET,
float: GET_FLOAT_FIELD_OFFSET,
double: GET_DOUBLE_FIELD_OFFSET
};
const setFieldOffset = {
pointer: SET_OBJECT_FIELD_OFFSET,
uint8: SET_BOOLEAN_FIELD_OFFSET,
int8: SET_BYTE_FIELD_OFFSET,
uint16: SET_CHAR_FIELD_OFFSET,
int16: SET_SHORT_FIELD_OFFSET,
int32: SET_INT_FIELD_OFFSET,
int64: SET_LONG_FIELD_OFFSET,
float: SET_FLOAT_FIELD_OFFSET,
double: SET_DOUBLE_FIELD_OFFSET
};
const getStaticFieldOffset = {
pointer: GET_STATIC_OBJECT_FIELD_OFFSET,
uint8: GET_STATIC_BOOLEAN_FIELD_OFFSET,
int8: GET_STATIC_BYTE_FIELD_OFFSET,
uint16: GET_STATIC_CHAR_FIELD_OFFSET,
int16: GET_STATIC_SHORT_FIELD_OFFSET,
int32: GET_STATIC_INT_FIELD_OFFSET,
int64: GET_STATIC_LONG_FIELD_OFFSET,
float: GET_STATIC_FLOAT_FIELD_OFFSET,
double: GET_STATIC_DOUBLE_FIELD_OFFSET
};
const setStaticFieldOffset = {
pointer: SET_STATIC_OBJECT_FIELD_OFFSET,
uint8: SET_STATIC_BOOLEAN_FIELD_OFFSET,
int8: SET_STATIC_BYTE_FIELD_OFFSET,
uint16: SET_STATIC_CHAR_FIELD_OFFSET,
int16: SET_STATIC_SHORT_FIELD_OFFSET,
int32: SET_STATIC_INT_FIELD_OFFSET,
int64: SET_STATIC_LONG_FIELD_OFFSET,
float: SET_STATIC_FLOAT_FIELD_OFFSET,
double: SET_STATIC_DOUBLE_FIELD_OFFSET
};
const nativeFunctionOptions = {
exceptions: 'propagate'
};
let cachedVtable = null;
let globalRefs = [];
Env.dispose = function (env) {
globalRefs.forEach(env.deleteGlobalRef, env);
globalRefs = [];
};
function register (globalRef) {
globalRefs.push(globalRef);
return globalRef;
}
function vtable (instance) {
if (cachedVtable === null) {
cachedVtable = instance.handle.readPointer();
}
return cachedVtable;
}
function proxy (offset, retType, argTypes, wrapper) {
let impl = null;
return function () {
if (impl === null) {
impl = new NativeFunction(vtable(this).add(offset * pointerSize).readPointer(), retType, argTypes, nativeFunctionOptions);
}
let args = [impl];
args = args.concat.apply(args, arguments);
return wrapper.apply(this, args);
};
}
Env.prototype.getVersion = proxy(4, 'int32', ['pointer'], function (impl) {
return impl(this.handle);
});
Env.prototype.findClass = proxy(6, 'pointer', ['pointer', 'pointer'], function (impl, name) {
const result = impl(this.handle, Memory.allocUtf8String(name));
this.throwIfExceptionPending();
return result;
});
Env.prototype.throwIfExceptionPending = function () {
const throwable = this.exceptionOccurred();
if (throwable.isNull()) {
return;
}
this.exceptionClear();
const handle = this.newGlobalRef(throwable);
this.deleteLocalRef(throwable);
const description = this.vaMethod('pointer', [])(this.handle, handle, this.javaLangObject().toString);
const descriptionStr = this.stringFromJni(description);
this.deleteLocalRef(description);
const error = new Error(descriptionStr);
error.$h = handle;
Script.bindWeak(error, makeErrorHandleDestructor(this.vm, handle));
throw error;
};
function makeErrorHandleDestructor (vm, handle) {
return function () {
vm.perform(env => {
env.deleteGlobalRef(handle);
});
};
}
Env.prototype.fromReflectedMethod = proxy(7, 'pointer', ['pointer', 'pointer'], function (impl, method) {
return impl(this.handle, method);
});
Env.prototype.fromReflectedField = proxy(8, 'pointer', ['pointer', 'pointer'], function (impl, method) {
return impl(this.handle, method);
});
Env.prototype.toReflectedMethod = proxy(9, 'pointer', ['pointer', 'pointer', 'pointer', 'uint8'], function (impl, klass, methodId, isStatic) {
return impl(this.handle, klass, methodId, isStatic);
});
Env.prototype.getSuperclass = proxy(10, 'pointer', ['pointer', 'pointer'], function (impl, klass) {
return impl(this.handle, klass);
});
Env.prototype.isAssignableFrom = proxy(11, 'uint8', ['pointer', 'pointer', 'pointer'], function (impl, klass1, klass2) {
return !!impl(this.handle, klass1, klass2);
});
Env.prototype.toReflectedField = proxy(12, 'pointer', ['pointer', 'pointer', 'pointer', 'uint8'], function (impl, klass, fieldId, isStatic) {
return impl(this.handle, klass, fieldId, isStatic);
});
Env.prototype.throw = proxy(13, 'int32', ['pointer', 'pointer'], function (impl, obj) {
return impl(this.handle, obj);
});
Env.prototype.exceptionOccurred = proxy(15, 'pointer', ['pointer'], function (impl) {
return impl(this.handle);
});
Env.prototype.exceptionDescribe = proxy(16, 'void', ['pointer'], function (impl) {
impl(this.handle);
});
Env.prototype.exceptionClear = proxy(17, 'void', ['pointer'], function (impl) {
impl(this.handle);
});
Env.prototype.pushLocalFrame = proxy(19, 'int32', ['pointer', 'int32'], function (impl, capacity) {
return impl(this.handle, capacity);
});
Env.prototype.popLocalFrame = proxy(20, 'pointer', ['pointer', 'pointer'], function (impl, result) {
return impl(this.handle, result);
});
Env.prototype.newGlobalRef = proxy(21, 'pointer', ['pointer', 'pointer'], function (impl, obj) {
return impl(this.handle, obj);
});
Env.prototype.deleteGlobalRef = proxy(22, 'void', ['pointer', 'pointer'], function (impl, globalRef) {
impl(this.handle, globalRef);
});
Env.prototype.deleteLocalRef = proxy(23, 'void', ['pointer', 'pointer'], function (impl, localRef) {
impl(this.handle, localRef);
});
Env.prototype.isSameObject = proxy(24, 'uint8', ['pointer', 'pointer', 'pointer'], function (impl, ref1, ref2) {
return !!impl(this.handle, ref1, ref2);
});
Env.prototype.newLocalRef = proxy(25, 'pointer', ['pointer', 'pointer'], function (impl, obj) {
return impl(this.handle, obj);
});
Env.prototype.allocObject = proxy(27, 'pointer', ['pointer', 'pointer'], function (impl, clazz) {
return impl(this.handle, clazz);
});
Env.prototype.getObjectClass = proxy(31, 'pointer', ['pointer', 'pointer'], function (impl, obj) {
return impl(this.handle, obj);
});
Env.prototype.isInstanceOf = proxy(32, 'uint8', ['pointer', 'pointer', 'pointer'], function (impl, obj, klass) {
return !!impl(this.handle, obj, klass);
});
Env.prototype.getMethodId = proxy(33, 'pointer', ['pointer', 'pointer', 'pointer', 'pointer'], function (impl, klass, name, sig) {
return impl(this.handle, klass, Memory.allocUtf8String(name), Memory.allocUtf8String(sig));
});
Env.prototype.getFieldId = proxy(94, 'pointer', ['pointer', 'pointer', 'pointer', 'pointer'], function (impl, klass, name, sig) {
return impl(this.handle, klass, Memory.allocUtf8String(name), Memory.allocUtf8String(sig));
});
Env.prototype.getIntField = proxy(100, 'int32', ['pointer', 'pointer', 'pointer'], function (impl, obj, fieldId) {
return impl(this.handle, obj, fieldId);
});
Env.prototype.getStaticMethodId = proxy(113, 'pointer', ['pointer', 'pointer', 'pointer', 'pointer'], function (impl, klass, name, sig) {
return impl(this.handle, klass, Memory.allocUtf8String(name), Memory.allocUtf8String(sig));
});
Env.prototype.getStaticFieldId = proxy(144, 'pointer', ['pointer', 'pointer', 'pointer', 'pointer'], function (impl, klass, name, sig) {
return impl(this.handle, klass, Memory.allocUtf8String(name), Memory.allocUtf8String(sig));
});
Env.prototype.getStaticIntField = proxy(150, 'int32', ['pointer', 'pointer', 'pointer'], function (impl, obj, fieldId) {
return impl(this.handle, obj, fieldId);
});
Env.prototype.getStringLength = proxy(164, 'int32', ['pointer', 'pointer'], function (impl, str) {
return impl(this.handle, str);
});
Env.prototype.getStringChars = proxy(165, 'pointer', ['pointer', 'pointer', 'pointer'], function (impl, str) {
return impl(this.handle, str, NULL);
});
Env.prototype.releaseStringChars = proxy(166, 'void', ['pointer', 'pointer', 'pointer'], function (impl, str, utf) {
impl(this.handle, str, utf);
});
Env.prototype.newStringUtf = proxy(167, 'pointer', ['pointer', 'pointer'], function (impl, str) {
const utf = Memory.allocUtf8String(str);
return impl(this.handle, utf);
});
Env.prototype.getStringUtfChars = proxy(169, 'pointer', ['pointer', 'pointer', 'pointer'], function (impl, str) {
return impl(this.handle, str, NULL);
});
Env.prototype.releaseStringUtfChars = proxy(170, 'void', ['pointer', 'pointer', 'pointer'], function (impl, str, utf) {
impl(this.handle, str, utf);
});
Env.prototype.getArrayLength = proxy(171, 'int32', ['pointer', 'pointer'], function (impl, array) {
return impl(this.handle, array);
});
Env.prototype.newObjectArray = proxy(172, 'pointer', ['pointer', 'int32', 'pointer', 'pointer'], function (impl, length, elementClass, initialElement) {
return impl(this.handle, length, elementClass, initialElement);
});
Env.prototype.getObjectArrayElement = proxy(173, 'pointer', ['pointer', 'pointer', 'int32'], function (impl, array, index) {
return impl(this.handle, array, index);
});
Env.prototype.setObjectArrayElement = proxy(174, 'void', ['pointer', 'pointer', 'int32', 'pointer'], function (impl, array, index, value) {
impl(this.handle, array, index, value);
});
Env.prototype.newBooleanArray = proxy(175, 'pointer', ['pointer', 'int32'], function (impl, length) {
return impl(this.handle, length);
});
Env.prototype.newByteArray = proxy(176, 'pointer', ['pointer', 'int32'], function (impl, length) {
return impl(this.handle, length);
});
Env.prototype.newCharArray = proxy(177, 'pointer', ['pointer', 'int32'], function (impl, length) {
return impl(this.handle, length);
});
Env.prototype.newShortArray = proxy(178, 'pointer', ['pointer', 'int32'], function (impl, length) {
return impl(this.handle, length);
});
Env.prototype.newIntArray = proxy(179, 'pointer', ['pointer', 'int32'], function (impl, length) {
return impl(this.handle, length);
});
Env.prototype.newLongArray = proxy(180, 'pointer', ['pointer', 'int32'], function (impl, length) {
return impl(this.handle, length);
});
Env.prototype.newFloatArray = proxy(181, 'pointer', ['pointer', 'int32'], function (impl, length) {
return impl(this.handle, length);
});
Env.prototype.newDoubleArray = proxy(182, 'pointer', ['pointer', 'int32'], function (impl, length) {
return impl(this.handle, length);
});
Env.prototype.getBooleanArrayElements = proxy(183, 'pointer', ['pointer', 'pointer', 'pointer'], function (impl, array) {
return impl(this.handle, array, NULL);
});
Env.prototype.getByteArrayElements = proxy(184, 'pointer', ['pointer', 'pointer', 'pointer'], function (impl, array) {
return impl(this.handle, array, NULL);
});
Env.prototype.getCharArrayElements = proxy(185, 'pointer', ['pointer', 'pointer', 'pointer'], function (impl, array) {
return impl(this.handle, array, NULL);
});
Env.prototype.getShortArrayElements = proxy(186, 'pointer', ['pointer', 'pointer', 'pointer'], function (impl, array) {
return impl(this.handle, array, NULL);
});
Env.prototype.getIntArrayElements = proxy(187, 'pointer', ['pointer', 'pointer', 'pointer'], function (impl, array) {
return impl(this.handle, array, NULL);
});
Env.prototype.getLongArrayElements = proxy(188, 'pointer', ['pointer', 'pointer', 'pointer'], function (impl, array) {
return impl(this.handle, array, NULL);
});
Env.prototype.getFloatArrayElements = proxy(189, 'pointer', ['pointer', 'pointer', 'pointer'], function (impl, array) {
return impl(this.handle, array, NULL);
});
Env.prototype.getDoubleArrayElements = proxy(190, 'pointer', ['pointer', 'pointer', 'pointer'], function (impl, array) {
return impl(this.handle, array, NULL);
});
Env.prototype.releaseBooleanArrayElements = proxy(191, 'pointer', ['pointer', 'pointer', 'pointer', 'int32'], function (impl, array, cArray) {
impl(this.handle, array, cArray, JNI_ABORT);
});
Env.prototype.releaseByteArrayElements = proxy(192, 'pointer', ['pointer', 'pointer', 'pointer', 'int32'], function (impl, array, cArray) {
impl(this.handle, array, cArray, JNI_ABORT);
});
Env.prototype.releaseCharArrayElements = proxy(193, 'pointer', ['pointer', 'pointer', 'pointer', 'int32'], function (impl, array, cArray) {
impl(this.handle, array, cArray, JNI_ABORT);
});
Env.prototype.releaseShortArrayElements = proxy(194, 'pointer', ['pointer', 'pointer', 'pointer', 'int32'], function (impl, array, cArray) {
impl(this.handle, array, cArray, JNI_ABORT);
});
Env.prototype.releaseIntArrayElements = proxy(195, 'pointer', ['pointer', 'pointer', 'pointer', 'int32'], function (impl, array, cArray) {
impl(this.handle, array, cArray, JNI_ABORT);
});
Env.prototype.releaseLongArrayElements = proxy(196, 'pointer', ['pointer', 'pointer', 'pointer', 'int32'], function (impl, array, cArray) {
impl(this.handle, array, cArray, JNI_ABORT);
});
Env.prototype.releaseFloatArrayElements = proxy(197, 'pointer', ['pointer', 'pointer', 'pointer', 'int32'], function (impl, array, cArray) {
impl(this.handle, array, cArray, JNI_ABORT);
});
Env.prototype.releaseDoubleArrayElements = proxy(198, 'pointer', ['pointer', 'pointer', 'pointer', 'int32'], function (impl, array, cArray) {
impl(this.handle, array, cArray, JNI_ABORT);
});
Env.prototype.getByteArrayRegion = proxy(200, 'void', ['pointer', 'pointer', 'int', 'int', 'pointer'], function (impl, array, start, length, cArray) {
impl(this.handle, array, start, length, cArray);
});
Env.prototype.setBooleanArrayRegion = proxy(207, 'void', ['pointer', 'pointer', 'int32', 'int32', 'pointer'], function (impl, array, start, length, cArray) {
impl(this.handle, array, start, length, cArray);
});
Env.prototype.setByteArrayRegion = proxy(208, 'void', ['pointer', 'pointer', 'int32', 'int32', 'pointer'], function (impl, array, start, length, cArray) {
impl(this.handle, array, start, length, cArray);
});
Env.prototype.setCharArrayRegion = proxy(209, 'void', ['pointer', 'pointer', 'int32', 'int32', 'pointer'], function (impl, array, start, length, cArray) {
impl(this.handle, array, start, length, cArray);
});
Env.prototype.setShortArrayRegion = proxy(210, 'void', ['pointer', 'pointer', 'int32', 'int32', 'pointer'], function (impl, array, start, length, cArray) {
impl(this.handle, array, start, length, cArray);
});
Env.prototype.setIntArrayRegion = proxy(211, 'void', ['pointer', 'pointer', 'int32', 'int32', 'pointer'], function (impl, array, start, length, cArray) {
impl(this.handle, array, start, length, cArray);
});
Env.prototype.setLongArrayRegion = proxy(212, 'void', ['pointer', 'pointer', 'int32', 'int32', 'pointer'], function (impl, array, start, length, cArray) {
impl(this.handle, array, start, length, cArray);
});
Env.prototype.setFloatArrayRegion = proxy(213, 'void', ['pointer', 'pointer', 'int32', 'int32', 'pointer'], function (impl, array, start, length, cArray) {
impl(this.handle, array, start, length, cArray);
});
Env.prototype.setDoubleArrayRegion = proxy(214, 'void', ['pointer', 'pointer', 'int32', 'int32', 'pointer'], function (impl, array, start, length, cArray) {
impl(this.handle, array, start, length, cArray);
});
Env.prototype.registerNatives = proxy(215, 'int32', ['pointer', 'pointer', 'pointer', 'int32'], function (impl, klass, methods, numMethods) {
return impl(this.handle, klass, methods, numMethods);
});
Env.prototype.monitorEnter = proxy(217, 'int32', ['pointer', 'pointer'], function (impl, obj) {
return impl(this.handle, obj);
});
Env.prototype.monitorExit = proxy(218, 'int32', ['pointer', 'pointer'], function (impl, obj) {
return impl(this.handle, obj);
});
Env.prototype.getDirectBufferAddress = proxy(230, 'pointer', ['pointer', 'pointer'], function (impl, obj) {
return impl(this.handle, obj);
});
Env.prototype.getObjectRefType = proxy(232, 'int32', ['pointer', 'pointer'], function (impl, ref) {
return impl(this.handle, ref);
});
const cachedMethods = new Map();
function plainMethod (offset, retType, argTypes, options) {
return getOrMakeMethod(this, 'p', makePlainMethod, offset, retType, argTypes, options);
}
function vaMethod (offset, retType, argTypes, options) {
return getOrMakeMethod(this, 'v', makeVaMethod, offset, retType, argTypes, options);
}
function nonvirtualVaMethod (offset, retType, argTypes, options) {
return getOrMakeMethod(this, 'n', makeNonvirtualVaMethod, offset, retType, argTypes, options);
}
function getOrMakeMethod (env, flavor, construct, offset, retType, argTypes, options) {
if (options !== undefined) {
return construct(env, offset, retType, argTypes, options);
}
const key = [offset, flavor, retType].concat(argTypes).join('|');
let m = cachedMethods.get(key);
if (m === undefined) {
m = construct(env, offset, retType, argTypes, nativeFunctionOptions);
cachedMethods.set(key, m);
}
return m;
}
function makePlainMethod (env, offset, retType, argTypes, options) {
return new NativeFunction(
vtable(env).add(offset * pointerSize).readPointer(),
retType,
['pointer', 'pointer', 'pointer'].concat(argTypes),
options);
}
function makeVaMethod (env, offset, retType, argTypes, options) {
return new NativeFunction(
vtable(env).add(offset * pointerSize).readPointer(),
retType,
['pointer', 'pointer', 'pointer', '...'].concat(argTypes),
options);
}
function makeNonvirtualVaMethod (env, offset, retType, argTypes, options) {
return new NativeFunction(
vtable(env).add(offset * pointerSize).readPointer(),
retType,
['pointer', 'pointer', 'pointer', 'pointer', '...'].concat(argTypes),
options);
}
Env.prototype.constructor = function (argTypes, options) {
return vaMethod.call(this, CALL_CONSTRUCTOR_METHOD_OFFSET, 'pointer', argTypes, options);
};
Env.prototype.vaMethod = function (retType, argTypes, options) {
const offset = callMethodOffset[retType];
if (offset === undefined) {
throw new Error('Unsupported type: ' + retType);
}
return vaMethod.call(this, offset, retType, argTypes, options);
};
Env.prototype.nonvirtualVaMethod = function (retType, argTypes, options) {
const offset = callNonvirtualMethodOffset[retType];
if (offset === undefined) {
throw new Error('Unsupported type: ' + retType);
}
return nonvirtualVaMethod.call(this, offset, retType, argTypes, options);
};
Env.prototype.staticVaMethod = function (retType, argTypes, options) {
const offset = callStaticMethodOffset[retType];
if (offset === undefined) {
throw new Error('Unsupported type: ' + retType);
}
return vaMethod.call(this, offset, retType, argTypes, options);
};
Env.prototype.getField = function (fieldType) {
const offset = getFieldOffset[fieldType];
if (offset === undefined) {
throw new Error('Unsupported type: ' + fieldType);
}
return plainMethod.call(this, offset, fieldType, []);
};
Env.prototype.getStaticField = function (fieldType) {
const offset = getStaticFieldOffset[fieldType];
if (offset === undefined) {
throw new Error('Unsupported type: ' + fieldType);
}
return plainMethod.call(this, offset, fieldType, []);
};
Env.prototype.setField = function (fieldType) {
const offset = setFieldOffset[fieldType];
if (offset === undefined) {
throw new Error('Unsupported type: ' + fieldType);
}
return plainMethod.call(this, offset, 'void', [fieldType]);
};
Env.prototype.setStaticField = function (fieldType) {
const offset = setStaticFieldOffset[fieldType];
if (offset === undefined) {
throw new Error('Unsupported type: ' + fieldType);
}
return plainMethod.call(this, offset, 'void', [fieldType]);
};
let javaLangClass = null;
Env.prototype.javaLangClass = function () {
if (javaLangClass === null) {
const handle = this.findClass('java/lang/Class');
try {
const get = this.getMethodId.bind(this, handle);
javaLangClass = {
handle: register(this.newGlobalRef(handle)),
getName: get('getName', '()Ljava/lang/String;'),
getSimpleName: get('getSimpleName', '()Ljava/lang/String;'),
getGenericSuperclass: get('getGenericSuperclass', '()Ljava/lang/reflect/Type;'),
getDeclaredConstructors: get('getDeclaredConstructors', '()[Ljava/lang/reflect/Constructor;'),
getDeclaredMethods: get('getDeclaredMethods', '()[Ljava/lang/reflect/Method;'),
getDeclaredFields: get('getDeclaredFields', '()[Ljava/lang/reflect/Field;'),
isArray: get('isArray', '()Z'),
isPrimitive: get('isPrimitive', '()Z'),
isInterface: get('isInterface', '()Z'),
getComponentType: get('getComponentType', '()Ljava/lang/Class;')
};
} finally {
this.deleteLocalRef(handle);
}
}
return javaLangClass;
};
let javaLangObject = null;
Env.prototype.javaLangObject = function () {
if (javaLangObject === null) {
const handle = this.findClass('java/lang/Object');
try {
const get = this.getMethodId.bind(this, handle);
javaLangObject = {
handle: register(this.newGlobalRef(handle)),
toString: get('toString', '()Ljava/lang/String;'),
getClass: get('getClass', '()Ljava/lang/Class;')
};
} finally {
this.deleteLocalRef(handle);
}
}
return javaLangObject;
};
let javaLangReflectConstructor = null;
Env.prototype.javaLangReflectConstructor = function () {
if (javaLangReflectConstructor === null) {
const handle = this.findClass('java/lang/reflect/Constructor');
try {
javaLangReflectConstructor = {
getGenericParameterTypes: this.getMethodId(handle, 'getGenericParameterTypes', '()[Ljava/lang/reflect/Type;')
};
} finally {
this.deleteLocalRef(handle);
}
}
return javaLangReflectConstructor;
};
let javaLangReflectMethod = null;
Env.prototype.javaLangReflectMethod = function () {
if (javaLangReflectMethod === null) {
const handle = this.findClass('java/lang/reflect/Method');
try {
const get = this.getMethodId.bind(this, handle);
javaLangReflectMethod = {
getName: get('getName', '()Ljava/lang/String;'),
getGenericParameterTypes: get('getGenericParameterTypes', '()[Ljava/lang/reflect/Type;'),
getParameterTypes: get('getParameterTypes', '()[Ljava/lang/Class;'),
getGenericReturnType: get('getGenericReturnType', '()Ljava/lang/reflect/Type;'),
getGenericExceptionTypes: get('getGenericExceptionTypes', '()[Ljava/lang/reflect/Type;'),
getModifiers: get('getModifiers', '()I'),
isVarArgs: get('isVarArgs', '()Z')
};
} finally {
this.deleteLocalRef(handle);
}
}
return javaLangReflectMethod;
};
let javaLangReflectField = null;
Env.prototype.javaLangReflectField = function () {
if (javaLangReflectField === null) {
const handle = this.findClass('java/lang/reflect/Field');
try {
const get = this.getMethodId.bind(this, handle);
javaLangReflectField = {
getName: get('getName', '()Ljava/lang/String;'),
getType: get('getType', '()Ljava/lang/Class;'),
getGenericType: get('getGenericType', '()Ljava/lang/reflect/Type;'),
getModifiers: get('getModifiers', '()I'),
toString: get('toString', '()Ljava/lang/String;')
};
} finally {
this.deleteLocalRef(handle);
}
}
return javaLangReflectField;
};
let javaLangReflectTypeVariable = null;
Env.prototype.javaLangReflectTypeVariable = function () {
if (javaLangReflectTypeVariable === null) {
const handle = this.findClass('java/lang/reflect/TypeVariable');
try {
const get = this.getMethodId.bind(this, handle);
javaLangReflectTypeVariable = {
handle: register(this.newGlobalRef(handle)),
getName: get('getName', '()Ljava/lang/String;'),
getBounds: get('getBounds', '()[Ljava/lang/reflect/Type;'),
getGenericDeclaration: get('getGenericDeclaration', '()Ljava/lang/reflect/GenericDeclaration;')
};
} finally {
this.deleteLocalRef(handle);
}
}
return javaLangReflectTypeVariable;
};
let javaLangReflectWildcardType = null;
Env.prototype.javaLangReflectWildcardType = function () {
if (javaLangReflectWildcardType === null) {
const handle = this.findClass('java/lang/reflect/WildcardType');
try {
const get = this.getMethodId.bind(this, handle);
javaLangReflectWildcardType = {
handle: register(this.newGlobalRef(handle)),
getLowerBounds: get('getLowerBounds', '()[Ljava/lang/reflect/Type;'),
getUpperBounds: get('getUpperBounds', '()[Ljava/lang/reflect/Type;')
};
} finally {
this.deleteLocalRef(handle);
}
}
return javaLangReflectWildcardType;
};
let javaLangReflectGenericArrayType = null;
Env.prototype.javaLangReflectGenericArrayType = function () {
if (javaLangReflectGenericArrayType === null) {
const handle = this.findClass('java/lang/reflect/GenericArrayType');
try {
javaLangReflectGenericArrayType = {
handle: register(this.newGlobalRef(handle)),
getGenericComponentType: this.getMethodId(handle, 'getGenericComponentType', '()Ljava/lang/reflect/Type;')
};
} finally {
this.deleteLocalRef(handle);
}
}
return javaLangReflectGenericArrayType;
};
let javaLangReflectParameterizedType = null;
Env.prototype.javaLangReflectParameterizedType = function () {
if (javaLangReflectParameterizedType === null) {
const handle = this.findClass('java/lang/reflect/ParameterizedType');
try {
const get = this.getMethodId.bind(this, handle);
javaLangReflectParameterizedType = {
handle: register(this.newGlobalRef(handle)),
getActualTypeArguments: get('getActualTypeArguments', '()[Ljava/lang/reflect/Type;'),
getRawType: get('getRawType', '()Ljava/lang/reflect/Type;'),
getOwnerType: get('getOwnerType', '()Ljava/lang/reflect/Type;')
};
} finally {
this.deleteLocalRef(handle);
}
}
return javaLangReflectParameterizedType;
};
let javaLangString = null;
Env.prototype.javaLangString = function () {
if (javaLangString === null) {
const handle = this.findClass('java/lang/String');
try {
javaLangString = {
handle: register(this.newGlobalRef(handle))
};
} finally {
this.deleteLocalRef(handle);
}
}
return javaLangString;
};
Env.prototype.getClassName = function (classHandle) {
const name = this.vaMethod('pointer', [])(this.handle, classHandle, this.javaLangClass().getName);
try {
return this.stringFromJni(name);
} finally {
this.deleteLocalRef(name);
}
};
Env.prototype.getObjectClassName = function (objHandle) {
const jklass = this.getObjectClass(objHandle);
try {
return this.getClassName(jklass);
} finally {
this.deleteLocalRef(jklass);
}
};
Env.prototype.getActualTypeArgument = function (type) {
const actualTypeArguments = this.vaMethod('pointer', [])(this.handle, type, this.javaLangReflectParameterizedType().getActualTypeArguments);
this.throwIfExceptionPending();
if (!actualTypeArguments.isNull()) {
try {
return this.getTypeNameFromFirstTypeElement(actualTypeArguments);
} finally {
this.deleteLocalRef(actualTypeArguments);
}
}
};
Env.prototype.getTypeNameFromFirstTypeElement = function (typeArray) {
const length = this.getArrayLength(typeArray);
if (length > 0) {
const typeArgument0 = this.getObjectArrayElement(typeArray, 0);
try {
return this.getTypeName(typeArgument0);
} finally {
this.deleteLocalRef(typeArgument0);
}
} else {
// TODO
return 'java.lang.Object';
}
};
Env.prototype.getTypeName = function (type, getGenericsInformation) {
const invokeObjectMethodNoArgs = this.vaMethod('pointer', []);
if (this.isInstanceOf(type, this.javaLangClass().handle)) {
return this.getClassName(type);
} else if (this.isInstanceOf(type, this.javaLangReflectGenericArrayType().handle)) {
return this.getArrayTypeName(type);
} else if (this.isInstanceOf(type, this.javaLangReflectParameterizedType().handle)) {
const rawType = invokeObjectMethodNoArgs(this.handle, type, this.javaLangReflectParameterizedType().getRawType);
this.throwIfExceptionPending();
let result;
try {
result = this.getTypeName(rawType);
} finally {
this.deleteLocalRef(rawType);
}
if (getGenericsInformation) {
result += '<' + this.getActualTypeArgument(type) + '>';
}
return result;
} else if (this.isInstanceOf(type, this.javaLangReflectTypeVariable().handle)) {
// TODO
return 'java.lang.Object';
} else if (this.isInstanceOf(type, this.javaLangReflectWildcardType().handle)) {
// TODO
return 'java.lang.Object';
} else {
return 'java.lang.Object';
}
};
Env.prototype.getArrayTypeName = function (type) {
const invokeObjectMethodNoArgs = this.vaMethod('pointer', []);
if (this.isInstanceOf(type, this.javaLangClass().handle)) {
return this.getClassName(type);
} else if (this.isInstanceOf(type, this.javaLangReflectGenericArrayType().handle)) {
const componentType = invokeObjectMethodNoArgs(this.handle, type, this.javaLangReflectGenericArrayType().getGenericComponentType);
// check for TypeNotPresentException and MalformedParameterizedTypeException
this.throwIfExceptionPending();
try {
return '[L' + this.getTypeName(componentType) + ';';
} finally {
this.deleteLocalRef(componentType);
}
} else {
return '[Ljava.lang.Object;';
}
};
Env.prototype.stringFromJni = function (str) {
const utf = this.getStringChars(str);
if (utf.isNull()) {
throw new Error('Unable to access string');
}
try {
const length = this.getStringLength(str);
return utf.readUtf16String(length);
} finally {
this.releaseStringChars(str, utf);
}
};