From 956db744f9c52019379a3117e1fbd3ef45fbdaa0 Mon Sep 17 00:00:00 2001
From: Michele Alberti <michele.alberti@cea.fr>
Date: Wed, 13 May 2020 19:46:01 +0200
Subject: [PATCH] [Ivette] Correct use of async/await with error handling.

---
 ivette/src/frama-c/server.ts    |  17 ++--
 ivette/src/frama-c/states.ts    | 153 ++++++++++++++++++++------------
 ivette/src/renderer/ASTview.tsx |  41 ++++++---
 3 files changed, 131 insertions(+), 80 deletions(-)

diff --git a/ivette/src/frama-c/server.ts b/ivette/src/frama-c/server.ts
index 4744cd01e28..163b7a6b572 100644
--- a/ivette/src/frama-c/server.ts
+++ b/ivette/src/frama-c/server.ts
@@ -862,24 +862,25 @@ function _poll() {
   }
 }
 
-function _send() {
+async function _send() {
   // when busy, will be eventually re-triggered
   if (!zmqIsBusy) {
     const cmds = queueCmd;
     if (!cmds.length && _waiting()) cmds.push('POLL');
     if (cmds.length) {
+      zmqIsBusy = true;
       const ids = queueId;
       queueCmd = [];
       queueId = [];
-      if (zmqSocket) {
-        zmqIsBusy = true;
-        zmqSocket.send(cmds)
-          .then(() => zmqSocket?.receive().then((resp: any) => _receive(resp)))
-          .catch(() => _cancel(ids))
-          .finally(() => { zmqIsBusy = false; Dome.emit(STATUS); });
-      } else {
+      try {
+        await zmqSocket?.send(cmds);
+        const resp = await zmqSocket?.receive();
+        _receive(resp);
+      } catch (error) {
+        PP.error(`Error in send/receive on ZMQ socket. ${error.toString()}`);
         _cancel(ids);
       }
+      zmqIsBusy = false;
     } else {
       // No pending command nor pending response
       rqCount = 0;
diff --git a/ivette/src/frama-c/states.ts b/ivette/src/frama-c/states.ts
index 13298285713..1cf0620f980 100644
--- a/ivette/src/frama-c/states.ts
+++ b/ivette/src/frama-c/states.ts
@@ -33,23 +33,35 @@ export const PROJECT = 'frama-c.project';
  */
 export const STATE = 'frama-c.state.';
 
+// --------------------------------------------------------------------------
+// --- Pretty Printing (Browser Console)
+// --------------------------------------------------------------------------
+
+class PP {
+  static warning(t: string) { console.warn(`[Frama-C States] ${t}.`); }
+  static error(t: string) { console.error(`[Frama-C States] ${t}.`); }
+}
+
 // --------------------------------------------------------------------------
 // --- Synchronized Current Project
 // --------------------------------------------------------------------------
 
-let currentProject = '<None>';
+let currentProject: string | null = null;
 let states: any = {};
 const stateDefaults: any = {};
 
 Server.onReady(async () => {
-  const sr: Server.Request = {
-    endpoint: 'kernel.project.getCurrent',
-    params: {},
-  };
-  const current: { id: string } = await Server.GET(sr);
-  currentProject = current.id;
-  Dome.emit(PROJECT);
-  return;
+  try {
+    const sr: Server.Request = {
+      endpoint: 'kernel.project.getCurrent',
+      params: {},
+    };
+    const current: { id: string } = await Server.GET(sr);
+    currentProject = current.id;
+    Dome.emit(PROJECT);
+  } catch (error) {
+    PP.error(`Fail to retrieve the current project. ${error.toString()}`);
+  }
 });
 
 Server.onShutdown(() => {
@@ -80,14 +92,14 @@ export function useProject() {
  */
 export async function setProject(project: string) {
   if (Server.isRunning()) {
-    const sr: Server.Request = {
-      endpoint: 'kernel.project.setCurrent',
-      params: project,
-    };
-    await Server.SET(sr);
-    currentProject = project;
-    Dome.emit(PROJECT);
-    return;
+    try {
+      const sr = { endpoint: 'kernel.project.setCurrent', params: project };
+      await Server.SET(sr);
+      currentProject = project;
+      Dome.emit(PROJECT);
+    } catch (error) {
+      PP.error(`Fail to set the current project. ${error.toString()}`);
+    }
   }
 }
 
@@ -95,12 +107,12 @@ export async function setProject(project: string) {
 // --- Projectified State
 // --------------------------------------------------------------------------
 
-function getValue(id: string, project: string) {
+function getValue(id: string, project: string | null) {
   if (!project) return undefined;
   return _.get(states, [project, id], stateDefaults[id]);
 }
 
-function setValue(id: string, project: string, value: any) {
+function setValue(id: string, project: string | null, value: any) {
   if (!project) return;
   _.set(states, [project, id], value);
   Dome.emit(STATE + id, value);
@@ -171,9 +183,8 @@ export function useRequest(rq: string, params: any, options: any = {}) {
       try {
         const r = await Server.GET({ endpoint: rq, params });
         setResponse(r);
-      } catch (errmsg) {
-        if (Dome.DEVEL)
-          console.warn(`[Server] use request '${rq}':`, errmsg);
+      } catch (error) {
+        PP.error(`Fail in useRequest '${rq}'. ${error.toString()}`);
         const err = options.error;
         if (err !== undefined) setResponse(err);
       }
@@ -283,19 +294,29 @@ class SyncState {
   }
 
   async setValue(v: any) {
-    this.insync = true;
-    this.value = v;
-    const sr: Server.Request = { endpoint: this.setRq, params: v };
-    await Server.SET(sr);
-    Dome.emit(this.UPDATE);
+    try {
+      this.insync = true;
+      this.value = v;
+      const sr: Server.Request = { endpoint: this.setRq, params: v };
+      await Server.SET(sr);
+      Dome.emit(this.UPDATE);
+    } catch (error) {
+      PP.error(
+        `Fail to set value of syncState '${this.id}'. ${error.toString()}`,
+      );
+    }
   }
 
   async update() {
-    this.insync = true;
-    const sr: Server.Request = { endpoint: this.getRq, params: {} };
-    const v = await Server.GET(sr);
-    this.value = v;
-    Dome.emit(this.UPDATE);
+    try {
+      this.insync = true;
+      const sr: Server.Request = { endpoint: this.getRq, params: {} };
+      const v = await Server.GET(sr);
+      this.value = v;
+      Dome.emit(this.UPDATE);
+    } catch (error) {
+      PP.error(`Fail to update syncState '${this.id}'. ${error.toString()}`);
+    }
   }
 }
 
@@ -359,6 +380,7 @@ export function useSyncValue(id: string) {
 
 // one per project
 class SyncArray {
+  id: string;
   UPDATE: string;
   signal: string;
   fetchRq: string;
@@ -367,6 +389,7 @@ class SyncArray {
   insync: boolean;
 
   constructor(id: string) {
+    this.id = id;
     this.UPDATE = STATE + id;
     this.signal = `${id}.sig`;
     this.fetchRq = `${id}.fetch`;
@@ -387,37 +410,49 @@ class SyncArray {
   }
 
   async fetch() {
-    this.insync = true;
-    const sr: Server.Request = { endpoint: this.fetchRq, params: 50 };
-    const data = await Server.GET(sr);
-    const { reload = false, removed = [], updated = [], pending = 0 } = data;
-    let reloaded = false;
-    if (reload) {
-      reloaded = this.isEmpty();
-      this.index = {};
-    }
-    removed.forEach((key: any) => {
-      delete this.index[key];
-    });
-    updated.forEach((item: any) => {
-      this.index[item.key] = item;
-    });
-    if (reloaded || removed.length || updated.length) {
-      this.index = { ...this.index };
-      Dome.emit(this.UPDATE);
-    }
-    if (pending > 0) {
-      this.fetch();
+    try {
+      this.insync = true;
+      const sr: Server.Request = { endpoint: this.fetchRq, params: 50 };
+      const data = await Server.GET(sr);
+      const { reload = false, removed = [], updated = [], pending = 0 } = data;
+      let reloaded = false;
+      if (reload) {
+        reloaded = this.isEmpty();
+        this.index = {};
+      }
+      removed.forEach((key: any) => {
+        delete this.index[key];
+      });
+      updated.forEach((item: any) => {
+        this.index[item.key] = item;
+      });
+      if (reloaded || removed.length || updated.length) {
+        this.index = { ...this.index };
+        Dome.emit(this.UPDATE);
+      }
+      if (pending > 0) {
+        this.fetch();
+      }
+    } catch (error) {
+      PP.error(
+        `Fail to retrieve the value of syncArray '${this.id}. ` +
+        `${error.toString()}`,
+      );
     }
-    return;
   }
 
   async reload() {
-    const sr: Server.Request = { endpoint: this.reloadRq, params: {} };
-    await Server.SET(sr);
-    this.index = {};
-    this.insync = false;
-    Dome.emit(this.UPDATE);
+    try {
+      const sr: Server.Request = { endpoint: this.reloadRq, params: {} };
+      await Server.SET(sr);
+      this.index = {};
+      this.insync = false;
+      Dome.emit(this.UPDATE);
+    } catch (error) {
+      PP.error(
+        `Fail to set reload of syncArray '${this.id}. ${error.toString()}`,
+      );
+    }
   }
 }
 
diff --git a/ivette/src/renderer/ASTview.tsx b/ivette/src/renderer/ASTview.tsx
index cc6bfd19604..2642ffcf8a7 100644
--- a/ivette/src/renderer/ASTview.tsx
+++ b/ivette/src/renderer/ASTview.tsx
@@ -23,6 +23,15 @@ const THEMES = [
   { id: 'solarized dark', label: 'Solarized Dark' },
 ];
 
+// --------------------------------------------------------------------------
+// --- Pretty Printing (Browser Console)
+// --------------------------------------------------------------------------
+
+class PP {
+  static warning(t: string) { console.warn(`[AST View] ${t}.`); }
+  static error(t: string) { console.error(`[AST View] ${t}.`); }
+}
+
 // --------------------------------------------------------------------------
 // --- Rich Text Printer
 // --------------------------------------------------------------------------
@@ -47,19 +56,25 @@ async function loadAST(buffer: any, theFunction?: string, theMarker?: string) {
   if (theFunction) {
     buffer.log('// Loading', theFunction, '…');
     (async () => {
-      const data = await Server.GET({
-        endpoint: 'kernel.ast.printFunction',
-        params: theFunction,
-      });
-      buffer.operation(() => {
-        buffer.clear();
-        if (!data)
-          buffer.log('// No code for function ', theFunction);
-        printAST(buffer, data);
-        if (theMarker)
-          buffer.scroll(theMarker, undefined);
-      });
-      return;
+      try {
+        const data = await Server.GET({
+          endpoint: 'kernel.ast.printFunction',
+          params: theFunction,
+        });
+        buffer.operation(() => {
+          buffer.clear();
+          if (!data)
+            buffer.log('// No code for function ', theFunction);
+          printAST(buffer, data);
+          if (theMarker)
+            buffer.scroll(theMarker, undefined);
+        });
+      } catch (error) {
+        PP.error(
+          `Fail to retrieve the AST of function ${theFunction}, ` +
+          `on marker ${theMarker}`,
+        );
+      }
     })();
   }
 }
-- 
GitLab