1/*
2 * Copyright (c) 2023 Shenzhen Kaihong Digital Industry Development Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16const { XTools } = require('../engine/XTools');
17const { X2DFast } = require('../engine/graphics/X2DFast');
18
19const INTYPE = {
20  state: 0,
21  depend: 1,
22  value: 2,
23  framestate: 3,
24  root: 4,
25  other: 5,
26};
27
28class NodeTypeMask {
29  static NONE = 0;
30  static CONTROL = 1 << INTYPE.state;
31  static DEPEND = 1 << INTYPE.depend;
32  static VALUE = 1 << INTYPE.value;
33  static FRAMESTATE = 1 << INTYPE.framestate;
34  static ROOT = 1 << INTYPE.root;
35  static OTHER = 1 << INTYPE.other;
36}
37
38class IrToPicture {
39  static INVALID_DEEP = -99999;
40  static NODEH = 20;
41  static LINE_TYPE = ['state', 'depend', 'value', 'framestate', 'root'];
42  static nodeType(ir) {
43    if (XTools.CONFIG.OpTypeControl.indexOf(ir.op) >= 0) {
44      return 'control';
45    }
46    if (ir.in[INTYPE.state].length > 0 && XTools.CONFIG.OpNotControl.indexOf(ir.op) === -1) {
47      return 'control';
48    }
49    if (XTools.CONFIG.OpTypeDepend.indexOf(ir.op) >= 0 || ir.in[INTYPE.depend].length > 0) {
50      return 'depend';
51    }
52    if (XTools.CONFIG.OpTypeValue.indexOf(ir.op) >= 0 || ir.in[INTYPE.value].length > 0) {
53      return 'value';
54    }
55    return 'other';
56  }
57  static nodeTypeMask(ir) {
58    let mask = NodeTypeMask.NONE;
59    if (XTools.CONFIG.OpTypeControl.indexOf(ir.op) >= 0) {
60      mask |= NodeTypeMask.CONTROL;
61    }
62    if (ir.in[INTYPE.state].length > 0 && XTools.CONFIG.OpNotControl.indexOf(ir.op) === -1) {
63      mask |= NodeTypeMask.CONTROL;
64    }
65    if (XTools.CONFIG.OpTypeDepend.indexOf(ir.op) >= 0 || ir.in[INTYPE.depend].length > 0) {
66      mask |= NodeTypeMask.DEPEND;
67    }
68    if (XTools.CONFIG.OpTypeValue.indexOf(ir.op) >= 0 || ir.in[INTYPE.value].length > 0) {
69      mask |= NodeTypeMask.VALUE;
70    }
71    if (XTools.CONFIG.OpTypeFrameState.indexOf(ir.op) >= 0 || ir.in[INTYPE.framestate].length > 0) {
72      mask |= NodeTypeMask.FRAMESTATE;
73    }
74    if (XTools.CONFIG.OpTypeCircuitRoot.indexOf(ir.op) >= 0 || ir.in[INTYPE.root].length > 0) {
75      mask |= NodeTypeMask.ROOT;
76    }
77    if (mask === NodeTypeMask.NONE) {
78      mask = NodeTypeMask.OTHER;
79    }
80    return mask;
81  }
82  static isLoopBack(l, nodes) {
83    if (XTools.CONFIG.OpTypeLoopBegin.indexOf(nodes[l.toId].ir.op) >= 0 && l.fromId === nodes[l.toId].ir.in[0][1]) {
84      return true;
85    }
86    if (XTools.CONFIG.OpTypeDependSelector.indexOf(nodes[l.toId].ir.op) >= 0 && l.fromId === nodes[l.toId].ir.in[1][1]) {
87      return true;
88    }
89    if (XTools.CONFIG.OpTypeValueSelector.indexOf(nodes[l.toId].ir.op) >= 0 && l.fromId === nodes[l.toId].ir.in[2][1]) {
90      return true;
91    }
92    return false;
93  }
94  static toPicture(irList, type, isBlock) {
95    let nodes = {};
96    let entry = -1;
97    for (let ir of irList) { //用于生成图的所有节点
98      if (type === 0) { //仅控制流
99        if (this.nodeType(ir) !== 'control') {
100          continue;
101        }
102      }
103      let name;
104      if (XTools.CONFIG.OpTypeJsBytecode.indexOf(ir.op) >= 0) {
105        name = ir.id + ',' + ir.bytecode;
106      }
107      else if (ir.typedop) {
108        name = ir.id + ',' + ir.typedop;
109      }
110      else {
111        name = ir.id + ',' + ir.op;
112      }
113      nodes[ir.id] = {
114        type: this.nodeType(ir),
115        mask: this.nodeTypeMask(ir),
116        hide: false,
117        inCount: 0,
118        in: [],
119        inh: {},
120        outCount: 0,
121        out: [],
122        outh: [],
123        pos: { x: -1, y: -1 },
124        deep: this.INVALID_DEEP,
125        name: name,
126        nameWidth: X2DFast.gi().getTextWidth(name, 14),
127        ir: ir,
128      };
129      if (entry === -1) {
130        entry = ir.id;
131      }
132    }
133
134    let lines = [];
135    let lid = 0;
136    this.generateLine(nodes, lines, lid);
137    this.resetPicture(nodes, isBlock);
138
139    return {
140      nodes: nodes,
141      lines: lines,
142    };
143  }
144  static generateLine(nodes, lines, lid) {
145    for (let i in nodes) { //生成连接线
146      let inId = parseInt(i);
147      for (let inP1 = 0; inP1 < nodes[inId].ir.in.length; inP1++) {
148        for (let inP2 = 0; inP2 < nodes[inId].ir.in[inP1].length; inP2++) {
149          let outId = nodes[inId].ir.in[inP1][inP2];
150          if (outId in nodes) {
151            let line = {
152              lid: lid++,
153              lineType: this.LINE_TYPE[inP1],
154              inNum: nodes[inId].inCount,
155              outNum: nodes[outId].outCount,
156              fromId: outId,
157              toId: inId,
158              inP1: inP1,
159              inP2: inP2,
160              outP: nodes[outId].ir.out.indexOf(inId),
161              used: false,
162            };
163            nodes[inId].inCount++;
164            nodes[inId].in.push(line);
165            nodes[outId].outCount++;
166            nodes[outId].out.push(line);
167            lines.push(line);
168          }
169        }
170      }
171    }
172  }
173
174  static deepTest(n, nodes, isBlock, stack, dist) {
175    try {
176      stack.push(n.ir.id);
177    }
178    catch (e) {
179      console.log(1);
180    }
181    if (stack.length > Object.keys(nodes).length * 2) {
182      return true;
183    }
184    if (stack.length > 1 && n.ir.id === dist) {
185      return true;
186    }
187    for (let i = 0; i < n.out.length; i++) {
188      let nout = nodes[n.out[i].toId];
189      if (n.deep !== this.INVALID_DEEP) {
190        if (nout.deep === this.INVALID_DEEP) {
191          nout.deep = n.deep + 1;
192          if (this.deepTest(nout, nodes, isBlock, stack, dist)) {
193            return true;
194          }
195        }
196        if (nout.deep <= n.deep) {
197          if (!this.isLoopBack(n.out[i], nodes) && !isBlock) {
198            nout.deep = n.deep + 1;
199            if (this.deepTest(nout, nodes, isBlock, stack, dist)) {
200              return true;
201            }
202          }
203        }
204      }
205    }
206    stack.pop();
207    return false;
208  }
209  static checkoutLoop(ls) {
210    console.log(JSON.stringify(ls));
211    let dicts = {};
212    for (let l of ls) {
213      if (!(l in dicts)) {
214        dicts[l] = 1;
215      }
216      else {
217        dicts[l]++;
218      }
219    }
220    console.log(JSON.stringify(dicts, null, 4));
221  }
222  static TEST_LOOP = true;
223  static resetPicture(nodes, isBlock) {
224    if (this.TEST_LOOP && Object.keys(nodes).length > 0) {
225      for (let k in nodes) {
226        if (k === 0) {
227          nodes[k].deep = 0;
228        }
229        else {
230          nodes[k].deep = this.INVALID_DEEP;
231        }
232      }
233      let testResult = [];
234      this.deepTest(nodes[0], nodes, isBlock, testResult, 0);
235      if (testResult.length > 0) {
236        this.checkoutLoop(testResult);
237      }
238    }
239
240    let entry = true;
241    let enums = [];
242    for (let k in nodes) {
243      let n = nodes[k];
244      if (n.hide) {
245        continue;
246      }
247      if (entry) {
248        n.pos.x = 0;
249        n.pos.y = 0;
250        n.deep = 0;
251        entry = false;
252        enums.push(k);
253      }
254      else {
255        n.deep = this.INVALID_DEEP;
256      }
257    }
258    let collectDebug = [];
259    while (enums.length > 0) { //12,18,27,28,31,34
260      let nextenums = [];
261      for (let e = 0; e < enums.length; e++) {
262        let k = enums[e];
263        let n = nodes[k];
264        if (n.hide) {
265          continue;
266        }
267        for (let i = 0; i < n.out.length; i++) {
268          let nout = nodes[n.out[i].toId];
269          if (n.deep !== this.INVALID_DEEP) {
270            if (nout.deep === this.INVALID_DEEP) {
271              nout.deep = n.deep + 1;
272              nextenums.push(nout.ir.id);
273            }
274            if (nout.deep <= n.deep) {
275              if (!this.isLoopBack(n.out[i], nodes) && !isBlock) {
276                nout.deep = n.deep + 1;
277                nextenums.push(nout.ir.id);
278              }
279            }
280          }
281        }
282        for (let i = 0; i < n.in.length; i++) {
283          let nin = nodes[n.in[i].fromId];
284          if (n.deep !== this.INVALID_DEEP) {
285            if (nin.deep === this.INVALID_DEEP) {
286              nin.deep = n.deep - 1;
287              nextenums.push(nin.ir.id);
288            }
289            if (nin.deep >= n.deep) {
290              if (!this.isLoopBack(n.in[i], nodes) && !isBlock) {
291                n.deep = nin.deep + 1;
292                nextenums.push(n.ir.id);
293              }
294            }
295          }
296        }
297      }
298      collectDebug.push(enums);
299
300      enums = nextenums;
301    }
302
303    let levels = {};
304    for (let k in nodes) { //节点分层
305      let n = nodes[k];
306      if (n.hide) {
307        continue;
308      }
309      if (!(n.deep in levels)) {
310        levels[n.deep] = [];
311      }
312      levels[n.deep].push(n);
313    }
314    let ty = 50;
315    for (let k in nodes) {
316      let n = nodes[k];
317      let ltypes = [];
318      for (let l of n.out) {
319        if (ltypes.indexOf(l.lineType) < 0) {
320          ltypes.push(l.lineType);
321        }
322      }
323      n.ltypes = ltypes;
324      if (n.hide) {
325        continue;
326      }
327      if (n.deep === this.INVALID_DEEP) {
328        n.pos.x = this.INVALID_DEEP; //Scr.logicw - 100;
329        n.pos.y = ty;
330        ty += 50;
331      }
332    }
333    let posy = 0;
334    let ks = Object.keys(levels).sort((a, b) => { return parseInt(a) - parseInt(b) });
335    for (let k of ks) {
336      k = parseInt(k);
337      if (k === this.INVALID_DEEP) {
338        continue;
339      }
340      let inCount = 0;
341      let outCount = 0;
342      let inP = 0;
343      for (let i = 0; i < levels[k].length; i++) {
344        let n = levels[k];
345        if (n.hide) {
346          continue;
347        }
348        for (let j = 0; j < n[i].in.length; j++) {
349          let l = n[i].in[j];
350          if (!n[i].inh[l.fromId + l.lineType]) {
351            n[i].inh[l.fromId + l.lineType] = (inP + 1) * 5;
352            inP += 1;
353          }
354        }
355        inCount += Object.keys(n[i].inh).length;
356
357        outCount += n[i].ltypes.length;
358      }
359      posy += (inCount + 1) * 5;
360
361      let outP = 0;
362      for (let i = 0; i < levels[k].length; i++) {
363        let n = levels[k];
364        if (n.hide) {
365          continue;
366        }
367        for (let j = 0; j < n[i].out.length; j++) {
368          n[i].outh[j] = (outP + 1 + n[i].ltypes.indexOf(n[i].out[j].lineType)) * 5;
369        }
370        n[i].pos.y = posy;
371        outP += n[i].ltypes.length;
372      }
373
374      posy += (outCount + 1) * 5 + this.NODEH;
375
376      let w = 0;
377      for (let i = 0; i < levels[k].length; i++) { //当前行总宽度
378        w += levels[k][i].nameWidth + 20;
379      }
380      let x = -w / 2;
381      for (let i = 0; i < levels[k].length; i++) { //每个节点x偏移
382        levels[k][i].pos.x = x + 10;
383        x += levels[k][i].nameWidth + 20;
384      }
385    }
386  }
387}
388
389module.exports = {
390  IrToPicture
391};