diff --git a/ivette/src/dome/renderer/data/json.ts b/ivette/src/dome/renderer/data/json.ts index 63e3ed2354b8bec681e80fce604c60339e548bd0..11f7a039384b5a76bcf6a106b9b096adc4c44825 100644 --- a/ivette/src/dome/renderer/data/json.ts +++ b/ivette/src/dome/renderer/data/json.ts @@ -47,13 +47,54 @@ export type jobject = { [key: string]: json }; Stores a string telling what was expected and a json of what have been given */ -class JsonError extends Error { - expected = ''; - constructor(expected: string, given: json) { - super(`expected ${expected} but given ${given}`); +export class JsonError extends Error { + given: json; + + constructor(given: json) { + super("wrong json data type"); this.name = this.constructor.name; + this.given = given; + } +} + +class JsonTypeError extends JsonError { + expected: string; + + constructor(expected: string, given: json) { + super(given); this.expected = expected; } + + toString(): string { + return `expected ${this.expected} but given ${JSON.stringify(this.given)}`; + } +} + +class JsonUnionError extends JsonError { + #errors: JsonError[]; + + constructor(errors: JsonError[], given: json) { + super(given); + this.#errors = errors; + } + + get errors(): JsonTypeError[] { + let errorsDeep: JsonTypeError[] = []; + this.#errors.forEach(e => { + if (e instanceof JsonTypeError) { + errorsDeep.push(e); + } + else if (e instanceof JsonUnionError) { + errorsDeep = errorsDeep.concat(e.errors); + } + }); + return errorsDeep; + } + + toString(): string { + return 'none of the union options are valid.\n' + + this.errors.map((e) => ` - ${e}`).join('\n'); + } } /** @@ -124,10 +165,10 @@ export function identity<A>(v: A): A { return v; } export const jNull: Decoder<null> = (js: json) => { if (js === null) { return null; - } - else { - throw new JsonError("null", js); - } + } + else { + throw new JsonTypeError("null", js); + } }; /** Identity. */ @@ -139,7 +180,7 @@ export const jObj: Decoder<jobject> = (js: json) => { return js; } else { - throw new JsonError("object", js); + throw new JsonTypeError("object", js); } }; @@ -149,7 +190,7 @@ export const jNumber: Decoder<number> = (js: json) => { return js; } else { - throw new JsonError("object", js); + throw new JsonTypeError("number", js); } }; @@ -159,7 +200,7 @@ export const jInt: Decoder<number> = (js: json) => { return js; } else { - throw new JsonError("integer", js); + throw new JsonTypeError("integer", js); } }; @@ -174,7 +215,7 @@ export const jBoolean: Decoder<boolean> = (js: json) => { return js; } else { - throw new JsonError("boolean", js); + throw new JsonTypeError("boolean", js); } }; @@ -190,14 +231,14 @@ export const jFalse: Decoder<boolean> = (js: json) => ( /** Primitive JSON string or throws JsonError. */ export const jString: Decoder<string> = (js: json) => { - if (typeof js === 'string') { - return js; - } - else { - throw new JsonError("string", js); - } -}; - + if (typeof js === 'string') { + return js; + } + else { + throw new JsonTypeError("string", js); + } + }; + /** JSON constant. Capture the tag or throw JsonError. @@ -210,7 +251,7 @@ export function jTag<A>(tg: A): Decoder<A> { return tg; } else { - throw new JsonError(`"${tg}"`, js); + throw new JsonTypeError(`"${tg}"`, js); } }; } @@ -226,7 +267,7 @@ export function jEnum<A>(d: { [tag: string]: A }): Decoder<A> { } else { const tags = Object.keys(d).map((tg) => `"${tg}"`); - throw new JsonError(tags.join(' | '), js); + throw new JsonTypeError(tags.join(' | '), js); } }; } @@ -245,7 +286,7 @@ export function jTags<A extends string | number>(...values: A[]): Decoder<A> { } else { const tags = values.map((tg) => typeof tg === 'string' ? `"${tg}"` : tg); - throw new JsonError(tags.join(' | '), js); + throw new JsonTypeError(tags.join(' | '), js); } }; } @@ -293,7 +334,7 @@ export function jMap<A>(fn: Decoder<A>): Decoder<Map<string, A>> { return m; } else { - throw new JsonError('object', js); + throw new JsonTypeError('object', js); } }; } @@ -324,7 +365,7 @@ export function jArray<A>(fn: Decoder<A>): Decoder<A[]> { return js.map(fn); } else { - throw new JsonError('array', js); + throw new JsonTypeError('array', js); } }; } @@ -354,7 +395,7 @@ export function jList<A>(fn: Decoder<A>): Decoder<A[]> { return buffer; } else { - throw new JsonError('array', js); + throw new JsonTypeError('array', js); } }; } @@ -385,7 +426,7 @@ export function jPair<A, B>( return [fa(js[0]) as A, fb(js[1]) as B]; } else { - throw new JsonError('[A, B]', js); + throw new JsonTypeError('[A, B]', js); } }; } @@ -401,7 +442,7 @@ export function jTriple<A, B, C>( return [fa(js[0]), fb(js[1]), fc(js[2])]; } else { - throw new JsonError('[A, B, C]', js); + throw new JsonTypeError('[A, B, C]', js); } }; } @@ -418,7 +459,7 @@ export function jTuple4<A, B, C, D>( return [fa(js[0]), fb(js[1]), fc(js[2]), fd(js[3])]; } else { - throw new JsonError('[A, B, C, D]', js); + throw new JsonTypeError('[A, B, C, D]', js); } }; } @@ -436,7 +477,7 @@ export function jTuple5<A, B, C, D, E>( return [fa(js[0]), fb(js[1]), fc(js[2]), fd(js[3]), fe(js[4])]; } else { - throw new JsonError('[A, B, C, D, E]', js); + throw new JsonTypeError('[A, B, C, D, E]', js); } }; } @@ -463,7 +504,7 @@ export function jObject<A extends object>(decoders: Props<A>): Decoder<A> { return buffer as A; // All fields should be present } else { - throw new JsonError('object', js); + throw new JsonTypeError('object', js); } }; } @@ -473,14 +514,14 @@ export function jObject<A extends object>(decoders: Props<A>): Decoder<A> { */ export function jUnion<A>(...cases: Decoder<A>[]): Decoder<A> { return (js: json) => { - const errors = []; + const errors: JsonError[] = []; for (const fv of cases) { try { return fv(js); } catch (err) { if (err instanceof JsonError) { - errors.push(err.expected); + errors.push(err); continue; } else { @@ -488,7 +529,7 @@ export function jUnion<A>(...cases: Decoder<A>[]): Decoder<A> { } } } - throw new JsonError(errors.join(' or '), js); + throw new JsonUnionError(errors, js); }; } @@ -548,7 +589,7 @@ export function jKey<K>(kd: K): Decoder<key<K>> { return forge(kd, js); } else { - throw new JsonError(`key<${kd}>`, js); + throw new JsonTypeError(`key<${kd}>`, js); } }; } @@ -560,7 +601,7 @@ export function jIndex<K>(kd: K): Decoder<index<K>> { return forge(kd, js); } else { - throw new JsonError(`index<${kd}>`, js); + throw new JsonTypeError(`index<${kd}>`, js); } }; } diff --git a/ivette/src/frama-c/server.ts b/ivette/src/frama-c/server.ts index d4f5c94ba9e6456f2dc163598f58a34b0b62610f..2d404a244c3fa91dfacbc3135df8d727def34985 100644 --- a/ivette/src/frama-c/server.ts +++ b/ivette/src/frama-c/server.ts @@ -764,7 +764,13 @@ export function send<In, Out>( try { resolve(request.output(js)); } catch (err) { - reject(`Invalid ${request.name} response (${err})`); + const message = `Invalid ${request.name} response: ${err}`; + if (err instanceof Json.JsonError) { + reject(`${message}\nThe whole response was:\n${Json.stringify(js)}`); + } + else { + reject(message); + } } }; pending.set(rid, { resolve: unwrap, reject }); diff --git a/src/plugins/server/kernel_properties.ml b/src/plugins/server/kernel_properties.ml index f07736aa5202b5d706ef413f81c33b1493eef696..9dbdcb8872383edef7505de55b4033f3a396addb 100644 --- a/src/plugins/server/kernel_properties.ml +++ b/src/plugins/server/kernel_properties.ml @@ -321,15 +321,20 @@ let is_relevant ip = not (Ast_info.is_frama_c_builtin (Kernel_function.get_name kf) || Cil_builtins.is_unused_builtin (Kernel_function.get_vi kf)) -let iter f = Property_status.iter (fun ip -> if is_relevant ip then f ip) +let iter f = + Property_status.iter (fun ip -> if is_relevant ip then f ip) -let add_update_hook f = - Property_status.register_property_add_hook - (fun ip -> if is_relevant ip then f ip); +(* Must reload the entire table when status changed: property dependencies + are not taken into account when status are updated. *) +let add_reload_hook (f : unit -> unit) : unit = Property_status.register_status_update_hook - (fun _emitter ip _status -> if is_relevant ip then f ip) + (fun _emitter _ip _status -> f()) + +let add_update_hook (f : Property.t -> unit) : unit = + Property_status.register_property_add_hook + (fun ip -> if is_relevant ip then f ip) -let add_remove_hook f = +let add_remove_hook (f : Property.t -> unit) : unit = Property_status.register_property_remove_hook (fun ip -> if is_relevant ip then f ip) @@ -341,8 +346,9 @@ let array = ~key:(fun ip -> Kernel_ast.Marker.tag (PIP ip)) ~keyType:Kernel_ast.Marker.jtype ~iter - ~add_update_hook + ~add_reload_hook ~add_remove_hook + ~add_update_hook model let reload () = States.reload array