Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
/example/node_modules
/node_modules
npm-debug.log
/dist
161 changes: 109 additions & 52 deletions index.js → index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,64 @@ const IN_OUT = Symbol('in-out');

const pointerSize = Process.pointerSize;

export default function trace (spec) {
type Direction = typeof IN | typeof OUT | typeof IN_OUT;
type Predicate = (value: any) => boolean;

interface Spec {
module?: string;
vtable: NativePointer;
functions: Func[];
callbacks: {
onError: (error: Error) => void;
shouldSkip?: (context: InvocationContext) => boolean,
onEvent: (event: Event) => void,
onEnter?: (event: Event, args: InvocationArguments) => void,
onLeave?: (event: Event, retval: InvocationReturnValue) => void,
};
}

interface Type {
parse(rawValue: NativePointer, parameters: any[]): any;
read(ptr: NativePointer): any;
}

interface Func {
name: string,
ret: Arg,
args: Arg[]
}

interface Arg {
direction: Direction,
name: string,
type: {
parse: boolean
},
condition: Condition,
requires: string[]
}

interface Binding {
property: string
value: string
}

type Parse = (value: any, params?: any) => void;

// action, params
type Action = [(values:NativePointer[], event:Event, params:any) => void, any];

interface Condition {
value: string;
property?: string;
predicate?: Predicate
}

export default function trace (spec: Spec) {
const {module, vtable, functions} = spec;
const {onError} = spec.callbacks;

const listeners = [];
const listeners : (InvocationListener | undefined)[] = [];
const intercept = makeInterceptor(spec);

if (module !== undefined) {
Expand Down Expand Up @@ -85,16 +138,16 @@ trace.types = {
cstring: cstring
};

function makeInterceptor (spec) {
function makeInterceptor (spec: Spec) {
const {shouldSkip, onEvent, onEnter, onLeave, onError} = spec.callbacks;

return function (func, impl) {
return function (func: Func, impl: NativePointerValue) : InvocationListener | undefined {
const name = func.name;

const inputActions = [];
const outputActions = [];
const inputActions: Action[] = [];
const outputActions: Action[] = [];
if (!computeActions(func, inputActions, outputActions)) {
onError(new Error(`Oops. It seems ${module}!${name} has circular dependencies.`));
onError(new Error(`Oops. It seems ${spec.module}!${name} has circular dependencies.`));
return;
}

Expand All @@ -108,7 +161,7 @@ function makeInterceptor (spec) {
this._skip = true;
return;
}
const values = [];
const values: NativePointer[] = [];
for (let i = 0; i !== numArgs; i++) {
values.push(args[i]);
}
Expand All @@ -129,8 +182,8 @@ function makeInterceptor (spec) {
if (this._skip === true) {
return;
}
const values = this.values;
const event = this.event;
const values: NativePointer[] = this.values;
const event: Event = this.event;

values.push(retval);

Expand All @@ -148,13 +201,13 @@ function makeInterceptor (spec) {
};
}

function computeActions (func, inputActions, outputActions) {
function computeActions (func: Func, inputActions: Action[], outputActions: Action[]) {
const args = func.args.slice();
if (func.ret !== null) {
args.push(func.ret);
}

const satisfied = new Set();
const satisfied: Set<string> = new Set();
let previousSatisfiedSize;

do {
Expand Down Expand Up @@ -192,7 +245,7 @@ function computeActions (func, inputActions, outputActions) {
return !args.some(arg => !satisfied.has(arg.name));
}

function computeAction (arg, index) {
function computeAction (arg: Arg, index: number): Action {
const {name, type, condition} = arg;

const hasDependentType = Array.isArray(type);
Expand All @@ -210,59 +263,59 @@ function computeAction (arg, index) {
return [readValue, [index, name, type.parse]];
}

function readValue (values, event, params) {
function readValue (values: any[], event: Event, params: [number, string, Parse]) {
const [index, name, parse] = params;

event.set(name, parse(values[index]));
}

function readValueConditionally (values, event, params) {
function readValueConditionally (values: any[], event: Event, params: [number, string, Parse, Condition]) {
const [index, name, parse, condition] = params;

if (condition.predicate(event.get(condition.value))) {
if (condition.predicate!(event.get(condition.value))) {
event.set(name, parse(values[index]));
}
}

function readValueWithDependentType (values, event, params) {
function readValueWithDependentType (values: any[], event: Event, params: [number, string, Parse, Binding]) {
const [index, name, parse, binding] = params;

const typeParameters = {};
const typeParameters: Record<string, any> = {};
typeParameters[binding.property] = event.get(binding.value);
event.set(name, parse(values[index], typeParameters));
}

function readValueWithDependentTypeConditionally (values, event, params) {
function readValueWithDependentTypeConditionally (values: any[], event: Event, params: [number, string, any, Binding, Condition]) {
const [index, name, parse, binding, condition] = params;

if (condition.predicate(event.get(condition.value))) {
const typeParameters = {};
if (condition.predicate!(event.get(condition.value))) {
const typeParameters: Record<string, any> = {};
typeParameters[binding.property] = event.get(binding.value);
event.set(name, parse(values[index], typeParameters));
}
}

function func (name, ret, args) {
function func (name: string, ret: any, args: Arg[]): Func {
return {
name: name,
ret: ret,
args: args
};
}

function argIn (name, type, condition) {
function argIn (name: string, type: Type, condition: Condition) {
return arg(IN, name, type, condition);
}

function argOut (name, type, condition) {
function argOut (name: string, type: Type, condition: Condition) {
return arg(OUT, name, type, condition);
}

function argInOut (name, type, condition) {
function argInOut (name: string, type: Type, condition: Condition) {
return arg(IN_OUT, name, type, condition);
}

function arg (direction, name, type, condition) {
function arg (direction: Direction, name: string, type: Type, condition: Condition) {
condition = condition || null;

return {
Expand All @@ -274,29 +327,29 @@ function arg (direction, name, type, condition) {
};
}

function retval (type, condition) {
function retval (type: Type, condition: Condition) {
return argOut('result', type, condition);
}

function padding (n) {
function padding (n:any) {
return [n];
}

function bind (property, value) {
function bind (property: string, value: string): Condition {
return {
property: property,
value: value
};
}

function when (value, predicate) {
function when (value: string, predicate: Predicate): Condition {
return {
value: value,
predicate: predicate
};
}

function dependencies (direction, type, condition) {
function dependencies (direction: Direction, type: Type, condition: Condition) {
const result = [];

if (direction === OUT) {
Expand All @@ -314,7 +367,7 @@ function dependencies (direction, type, condition) {
return result;
}

function bool () {
function bool (): Type {
return {
parse (rawValue) {
return !!rawValue.toInt32();
Expand All @@ -325,7 +378,7 @@ function bool () {
};
}

function byte () {
function byte (): Type {
return {
parse (rawValue) {
return rawValue.toInt32() & 0xff;
Expand All @@ -336,7 +389,7 @@ function byte () {
};
}

function short () {
function short (): Type {
return {
parse (rawValue) {
return rawValue.toInt32() & 0xffff;
Expand All @@ -347,20 +400,20 @@ function short () {
};
}

function int () {
function int (): Type {
return {
parse (rawValue) {
parse (rawValue: NativePointer) {
return rawValue.toInt32();
},
read (ptr) {
read (ptr: NativePointer) {
return ptr.readInt();
}
};
}

function pointer (pointee) {
function pointer (pointee?: any): Type {
return {
parse (rawValue, parameters) {
parse (rawValue: NativePointer, parameters: any[]) {
if (pointee) {
if (rawValue.isNull()) {
return null;
Expand All @@ -371,49 +424,50 @@ function pointer (pointee) {
return rawValue;
}
},
read (ptr) {
read (ptr: NativePointer) {
return ptr.readPointer();
}
};
}

function byteArray () {
function byteArray (): Type {
return pointer({
read (ptr, parameters) {
read (ptr: NativePointer, parameters: any[]) {
return ptr.readByteArray(parameters.length);
}
});
}

function utf8 () {
function utf8 (): Type {
return pointer({
read (ptr, parameters) {
read (ptr: NativePointer, parameters: any[]) {
const length = (parameters === undefined) ? -1 : parameters.length;
return ptr.readUtf8String(length);
}
});
}

function utf16 () {
function utf16 (): Type {
return pointer({
read (ptr, parameters) {
read (ptr: NativePointer, parameters: any[]) {
const length = (parameters === undefined) ? -1 : parameters.length;
return ptr.readUtf16String(length);
}
});
}

function cstring () {
function cstring (): Type {
return pointer({
read (ptr, parameters) {
read (ptr: NativePointer, parameters: any[]) {
const length = (parameters === undefined) ? -1 : parameters.length;
return ptr.readCString(length);
}
});
}

class Session {
constructor (listeners) {
_listeners: InvocationListener[];
constructor (listeners: any[]) {
this._listeners = listeners;
}

Expand All @@ -425,16 +479,19 @@ class Session {
}

class Event {
constructor (name) {
name: string;
args: Record<string, any>;
result: any;
constructor (name: string) {
this.name = name;
this.args = {};
}

get (key) {
get (key: string) {
return (key === 'result') ? this.result : this.args[key];
}

set (key, value) {
set (key: string, value: any) {
if (key === 'result') {
this.result = value;
} else {
Expand Down
Loading