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 NODE_TYPE_MASK { 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 = NODE_TYPE_MASK.NONE; 59 if (XTools.CONFIG.OpTypeControl.indexOf(ir.op) >= 0) { 60 mask |= NODE_TYPE_MASK.CONTROL; 61 } 62 if (ir.in[INTYPE.state].length > 0 && XTools.CONFIG.OpNotControl.indexOf(ir.op) === -1) { 63 mask |= NODE_TYPE_MASK.CONTROL; 64 } 65 if (XTools.CONFIG.OpTypeDepend.indexOf(ir.op) >= 0 || ir.in[INTYPE.depend].length > 0) { 66 mask |= NODE_TYPE_MASK.DEPEND; 67 } 68 if (XTools.CONFIG.OpTypeValue.indexOf(ir.op) >= 0 || ir.in[INTYPE.value].length > 0) { 69 mask |= NODE_TYPE_MASK.VALUE; 70 } 71 if (XTools.CONFIG.OpTypeFrameState.indexOf(ir.op) >= 0 || ir.in[INTYPE.framestate].length > 0) { 72 mask |= NODE_TYPE_MASK.FRAMESTATE; 73 } 74 if (XTools.CONFIG.OpTypeCircuitRoot.indexOf(ir.op) >= 0 || ir.in[INTYPE.root].length > 0) { 75 mask |= NODE_TYPE_MASK.ROOT; 76 } 77 if (mask === NODE_TYPE_MASK.NONE) { 78 mask = NODE_TYPE_MASK.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") continue; 100 } 101 let name = ir.id + "," + ir.op; 102 if (XTools.CONFIG.OpTypeJsBytecode.indexOf(ir.op) >= 0) { 103 name = ir.id + "," + ir.bytecode; 104 } 105 nodes[ir.id] = { 106 type: this.nodeType(ir), 107 mask: this.nodeTypeMask(ir), 108 hide: false, 109 inCount: 0, 110 in: [], 111 inh: {}, 112 outCount: 0, 113 out: [], 114 outh: [], 115 pos: { x: -1, y: -1 }, 116 deep: this.INVALID_DEEP, 117 name: name, 118 nameWidth: X2DFast.gi().getTextWidth(name, 14), 119 ir: ir, 120 } 121 if (entry === -1) { 122 entry = ir.id; 123 } 124 } 125 126 let lines = []; 127 let lid = 0; 128 for (let i in nodes) {//生成连接线 129 let inId = parseInt(i); 130 for (let inP1 = 0; inP1 < nodes[inId].ir.in.length; inP1++) { 131 for (let inP2 = 0; inP2 < nodes[inId].ir.in[inP1].length; inP2++) { 132 let outId = nodes[inId].ir.in[inP1][inP2]; 133 if (outId in nodes) { 134 let line = { 135 lid: lid++, 136 lineType: this.LINE_TYPE[inP1], 137 inNum: nodes[inId].inCount, 138 outNum: nodes[outId].outCount, 139 fromId: outId, 140 toId: inId, 141 inP1: inP1, 142 inP2: inP2, 143 outP: nodes[outId].ir.out.indexOf(inId), 144 used: false, 145 }; 146 nodes[inId].inCount++; 147 nodes[inId].in.push(line); 148 nodes[outId].outCount++; 149 nodes[outId].out.push(line); 150 lines.push(line); 151 } 152 } 153 } 154 } 155 156 this.resetPicture(nodes, isBlock); 157 158 return { 159 nodes: nodes, 160 lines: lines, 161 }; 162 } 163 static deepTest(n, nodes, isBlock, stack, dist) { 164 try { 165 stack.push(n.ir.id); 166 } 167 catch (e) { 168 console.log(1); 169 } 170 if (stack.length > Object.keys(nodes).length * 2) { 171 return true; 172 } 173 if (stack.length > 1 && n.ir.id === dist) { 174 return true; 175 } 176 for (let i = 0; i < n.out.length; i++) { 177 let nout = nodes[n.out[i].toId]; 178 if (n.deep !== this.INVALID_DEEP) { 179 if (nout.deep === this.INVALID_DEEP) { 180 nout.deep = n.deep + 1; 181 if (this.deepTest(nout, nodes, isBlock, stack, dist)) { 182 return true; 183 } 184 } 185 if (nout.deep <= n.deep) { 186 if (!this.isLoopBack(n.out[i], nodes) && !isBlock) { 187 nout.deep = n.deep + 1; 188 if (this.deepTest(nout, nodes, isBlock, stack, dist)) { 189 return true; 190 } 191 } 192 } 193 } 194 } 195 stack.pop(); 196 return false; 197 } 198 static checkoutLoop(ls) { 199 console.log(JSON.stringify(ls)); 200 let dicts = {}; 201 for (let l of ls) { 202 if (!(l in dicts)) { 203 dicts[l] = 1; 204 } 205 else { 206 dicts[l]++; 207 } 208 } 209 console.log(JSON.stringify(dicts, null, 4)); 210 } 211 static TEST_LOOP = true; 212 static resetPicture(nodes, isBlock) { 213 if (this.TEST_LOOP && Object.keys(nodes).length > 0) { 214 for (let k in nodes) { 215 if (k === 0) { 216 nodes[k].deep = 0; 217 } 218 else { 219 nodes[k].deep = this.INVALID_DEEP; 220 } 221 } 222 let testResult = []; 223 this.deepTest(nodes[0], nodes, isBlock, testResult, 0); 224 if (testResult.length > 0) { 225 this.checkoutLoop(testResult); 226 } 227 } 228 229 let entry = true; 230 let enums = []; 231 for (let k in nodes) { 232 let n = nodes[k]; 233 if (n.hide) { 234 continue; 235 } 236 if (entry) { 237 n.pos.x = 0; 238 n.pos.y = 0; 239 n.deep = 0; 240 entry = false; 241 enums.push(k); 242 } 243 else { 244 n.deep = this.INVALID_DEEP; 245 } 246 } 247 let collectDebug = []; 248 while (enums.length > 0) {//12,18,27,28,31,34 249 let nextenums = []; 250 for (let e = 0; e < enums.length; e++) { 251 let k = enums[e]; 252 let n = nodes[k]; 253 if (n.hide) { 254 continue; 255 } 256 for (let i = 0; i < n.out.length; i++) { 257 let nout = nodes[n.out[i].toId]; 258 if (n.deep !== this.INVALID_DEEP) { 259 if (nout.deep === this.INVALID_DEEP) { 260 nout.deep = n.deep + 1; 261 nextenums.push(nout.ir.id); 262 } 263 if (nout.deep <= n.deep) { 264 if (!this.isLoopBack(n.out[i], nodes) && !isBlock) { 265 nout.deep = n.deep + 1; 266 nextenums.push(nout.ir.id); 267 } 268 } 269 } 270 } 271 for (let i = 0; i < n.in.length; i++) { 272 let nin = nodes[n.in[i].fromId]; 273 if (n.deep !== this.INVALID_DEEP) { 274 if (nin.deep === this.INVALID_DEEP) { 275 nin.deep = n.deep - 1; 276 nextenums.push(nin.ir.id); 277 } 278 if (nin.deep >= n.deep) { 279 if (!this.isLoopBack(n.in[i], nodes) && !isBlock) { 280 n.deep = nin.deep + 1; 281 nextenums.push(n.ir.id); 282 } 283 } 284 } 285 } 286 } 287 collectDebug.push(enums); 288 289 enums = nextenums; 290 } 291 292 let levels = {}; 293 for (let k in nodes) {//节点分层 294 let n = nodes[k]; 295 if (n.hide) { 296 continue; 297 } 298 if (!(n.deep in levels)) { 299 levels[n.deep] = []; 300 } 301 levels[n.deep].push(n); 302 } 303 let ty = 50; 304 for (let k in nodes) { 305 let n = nodes[k]; 306 let ltypes = []; 307 for (let l of n.out) { 308 if (ltypes.indexOf(l.lineType) < 0) { 309 ltypes.push(l.lineType); 310 } 311 } 312 n.ltypes = ltypes; 313 if (n.hide) { 314 continue; 315 } 316 if (n.deep === this.INVALID_DEEP) { 317 n.pos.x = this.INVALID_DEEP;//Scr.logicw - 100; 318 n.pos.y = ty; 319 ty += 50; 320 } 321 } 322 let posy = 0; 323 let ks = Object.keys(levels).sort((a, b) => { return parseInt(a) - parseInt(b) }); 324 for (let k of ks) { 325 k = parseInt(k); 326 if (k === this.INVALID_DEEP) { 327 continue; 328 } 329 let inCount = 0; 330 let outCount = 0; 331 let inP = 0; 332 for (let i = 0; i < levels[k].length; i++) { 333 let n = levels[k]; 334 if (n.hide) { 335 continue; 336 } 337 for (let j = 0; j < n[i].in.length; j++) { 338 let l = n[i].in[j]; 339 if (!n[i].inh[l.fromId + l.lineType]) { 340 n[i].inh[l.fromId + l.lineType] = (inP + 1) * 5; 341 inP += 1; 342 } 343 } 344 inCount += Object.keys(n[i].inh).length; 345 346 outCount += n[i].ltypes.length; 347 } 348 posy += (inCount + 1) * 5; 349 350 let outP = 0; 351 for (let i = 0; i < levels[k].length; i++) { 352 let n = levels[k]; 353 if (n.hide) { 354 continue; 355 } 356 for (let j = 0; j < n[i].out.length; j++) { 357 n[i].outh[j] = (outP + 1 + n[i].ltypes.indexOf(n[i].out[j].lineType)) * 5; 358 } 359 n[i].pos.y = posy; 360 outP += n[i].ltypes.length; 361 } 362 363 posy += (outCount + 1) * 5 + this.NODEH; 364 365 let w = 0; 366 for (let i = 0; i < levels[k].length; i++) {//当前行总宽度 367 w += levels[k][i].nameWidth + 20; 368 } 369 let x = -w / 2; 370 for (let i = 0; i < levels[k].length; i++) {//每个节点x偏移 371 levels[k][i].pos.x = x + 10; 372 x += levels[k][i].nameWidth + 20; 373 } 374 } 375 } 376} 377 378module.exports = { 379 IrToPicture 380}