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};