arnaucube
arnaucube/circomPublic Self-hosted repository
circom/ports/wasm/builder.js
ports/wasm/builder.jsRaw
   1const assert = require("assert");
   2const ModuleBuilder = require("wasmbuilder").ModuleBuilder;
   3const ModuleBuilderWat = require("wasmbuilder").ModuleBuilderWat;
   4const buildRuntime = require("./build_runtime");
   5const Scalar = require("ffjavascript").Scalar;
   6const F1Field = require("ffjavascript").F1Field;
   7
   8
   9const errs = require("./errs");
  10
  11function hexToBytesR(hex) {
  12    for (var bytes = [], c = hex.length-2; c >=0;  c -= 2)
  13        bytes.push(parseInt(hex.substr(c, 2), 16));
  14    return bytes;
  15}
  16
  17
  18function intToBytes32(v) {
  19    return [ v &0xFF, (v>>8) & 0xFF,  (v>>16) & 0xFF, (v>>24) & 0xFF ];
  20}
  21
  22
  23class CodeBuilderWasm {
  24    constructor(fnBuilder) {
  25        this.fnBuilder = fnBuilder;
  26        this.ops = [];
  27    }
  28
  29    addComment(comment) {
  30        this.ops.push({op: "COMMENT", comment});
  31    }
  32
  33    addBlock(block) {
  34        this.ops.push({op: "BLOCK", block});
  35    }
  36
  37    calcOffset(dLabel, offsets) {
  38        this.ops.push({op: "CALCOFFSETS", dLabel, offsets});
  39    }
  40
  41    assign(dLabel, src, sOffset) {
  42        this.ops.push({op: "ASSIGN", dLabel, src, sOffset});
  43    }
  44
  45    getSubComponentOffset(dLabel, component, hash, hashLabel) {
  46        this.ops.push({op: "GETSUBCOMPONENTOFFSET", dLabel, component, hash, hashLabel});
  47    }
  48
  49    getSubComponentSizes(dLabel, component, hash, hashLabel) {
  50        this.ops.push({op: "GETSUBCOMPONENTSIZES", dLabel, component, hash, hashLabel});
  51    }
  52
  53    getSignalOffset(dLabel, component, hash, hashLabel) {
  54        this.ops.push({op: "GETSIGNALOFFSET", dLabel, component, hash, hashLabel});
  55    }
  56
  57    getSignalSizes(dLabel, component, hash, hashLabel) {
  58        this.ops.push({op: "GETSIGNALSIZES", dLabel, component, hash, hashLabel});
  59    }
  60
  61    setSignal(component, signal, value) {
  62        this.ops.push({op: "SETSIGNAL", component, signal, value});
  63    }
  64
  65    getSignal(dLabel, component, signal) {
  66        this.ops.push({op: "GETSIGNAL", dLabel, component, signal});
  67    }
  68
  69    copyN(dLabel, offset, src, n) {
  70        this.ops.push({op: "COPYN", dLabel, offset, src, n});
  71    }
  72
  73    copyNRet(src, n) {
  74        this.ops.push({op: "COPYNRET", src, n});
  75    }
  76
  77    fieldOp(dLabel, fOp, params) {
  78        this.ops.push({op: "FOP", dLabel, fOp, params});
  79    }
  80
  81    ret() {
  82        this.ops.push({op: "RET"});
  83    }
  84
  85    addLoop(condLabel, body) {
  86        this.ops.push({op: "LOOP", condLabel, body});
  87    }
  88
  89    addIf(condLabel, thenCode, elseCode) {
  90        this.ops.push({op: "IF", condLabel, thenCode, elseCode});
  91    }
  92
  93    fnCall(fnName, retLabel, params) {
  94        this.ops.push({op: "FNCALL", fnName, retLabel, params});
  95    }
  96
  97    checkConstraint(a, b, strErr) {
  98        this.ops.push({op: "CHECKCONSTRAINT", a, b, strErr});
  99    }
 100
 101
 102    concat(cb) {
 103        this.ops.push(...cb.ops);
 104    }
 105
 106    log(val) {
 107        this.ops.push({op: "LOG", val});
 108    }
 109
 110    hasCode() {
 111        for (let i=0; i<this.ops.length; i++) {
 112            if (this.ops[i].op != "COMMENT") return true;
 113        }
 114        return false;
 115    }
 116
 117
 118    _buildOffset(c, offsets) {
 119        let rN=0;
 120        let S = null;
 121        offsets.forEach((o) => {
 122            if ((o[0][0] == "V") && (o[1][0]== "V")) {
 123                rN += o[0][1]*o[1][1];
 124                return;
 125            }
 126            let f=[];
 127            if (o[0][0] == "V") {
 128                f = c.i32_const(o[0][1]);
 129            } else if (o[0][0] == "RI") {
 130                f = c.i32_load(this.fnBuilder._getPtr(c, o[0][1]));
 131            } else if (o[0][0] == "R") {
 132                f = c.call("Fr_toInt", this.fnBuilder._getPtr(c, o[0][1]));
 133            } else {
 134                assert(false);
 135            }
 136            if (o[1][0] == "V") {
 137                if (o[1][1]==0) return;
 138                if (o[1][1]>1) {
 139                    f = c.i32_mul(f, c.i32_const(o[1][1]));
 140                }
 141            } else if (o[1][0] == "RS") {
 142                f = c.i32_mul(
 143                    f,
 144                    c.i32_load(
 145                        c.i32_add(
 146                            c.i32_load(this.fnBuilder._getPtr(c, o[1][1])),
 147                            c.i32_const(o[1][2]*4)
 148                        )
 149                    )
 150                );
 151            } else {
 152                assert(false);
 153            }
 154            if (S!=null) {
 155                S = c.i32_add( S, f);
 156            } else {
 157                S = f;
 158            }
 159        });
 160        if (rN>0) {
 161            if (S!=null) {
 162                S = c.i32_add( S, c.i32_const(rN));
 163            } else {
 164                S = c.i32_const(rN);
 165            }
 166        }
 167        return S;
 168    }
 169
 170    build(c) {
 171        const code = [];
 172        this.ops.forEach( (o) => {
 173            if (o.op == "COMMENT") {
 174                code.push(
 175                    c.comment(o.comment)
 176                );
 177                // DO nothing
 178            } else if (o.op == "BLOCK") {
 179                code.push(
 180                    o.block.build(c)
 181                );
 182            } else if (o.op == "CALCOFFSETS") {
 183                code.push(
 184                    c.i32_store(
 185                        this.fnBuilder._getPtr(c, o.dLabel),
 186                        this._buildOffset(c, o.offsets)
 187                    )
 188                );
 189            } else if (o.op == "ASSIGN") {
 190                code.push(
 191                    c.i32_store(
 192                        this.fnBuilder._getPtr(c, o.dLabel),
 193                        c.i32_add(
 194                            this.fnBuilder._deRefFr(c, o.src),
 195                            c.i32_mul(
 196                                this.fnBuilder._deRefInt(c, o.sOffset),
 197                                c.i32_const(this.fnBuilder.builder.sizeFr)
 198                            )
 199                        )
 200                    )
 201                );
 202            } else if (o.op == "GETSUBCOMPONENTOFFSET") {
 203                code.push(
 204                    c.call(
 205                        "getSubComponentOffset",
 206                        this.fnBuilder._getPtr(c, o.dLabel),
 207                        this.fnBuilder._deRefInt(c, o.component),
 208                        c.i64_const("0x" + o.hash)
 209                    )
 210                );
 211            } else if (o.op == "GETSUBCOMPONENTSIZES") {
 212                code.push(
 213                    c.call(
 214                        "getSubComponentSizes",
 215                        this.fnBuilder._getPtr(c, o.dLabel),
 216                        this.fnBuilder._deRefInt(c, o.component),
 217                        c.i64_const("0x" + o.hash)
 218                    )
 219                );
 220            } else if (o.op == "GETSIGNALOFFSET") {
 221                code.push(
 222                    c.call(
 223                        "getSignalOffset",
 224                        this.fnBuilder._getPtr(c, o.dLabel),
 225                        this.fnBuilder._deRefInt(c, o.component),
 226                        c.i64_const("0x" + o.hash)
 227                    )
 228                );
 229            } else if (o.op == "GETSIGNALSIZES") {
 230                code.push(
 231                    c.call(
 232                        "getSignalSizes",
 233                        this.fnBuilder._getPtr(c, o.dLabel),
 234                        this.fnBuilder._deRefInt(c, o.component),
 235                        c.i64_const("0x" + o.hash)
 236                    )
 237                );
 238            } else if (o.op == "SETSIGNAL") {
 239                code.push(
 240                    c.call(
 241                        "setSignal",
 242                        c.getLocal("cIdx"),
 243                        this.fnBuilder._deRefInt(c, o.component),
 244                        this.fnBuilder._deRefInt(c, o.signal),
 245                        this.fnBuilder._deRefFr(c, o.value)
 246                    )
 247                );
 248            } else if (o.op == "GETSIGNAL") {
 249                code.push(
 250                    c.call(
 251                        "getSignal",
 252                        c.getLocal("cIdx"),
 253                        this.fnBuilder._getPtr(c, o.dLabel),
 254                        this.fnBuilder._deRefInt(c, o.component),
 255                        this.fnBuilder._deRefInt(c, o.signal)
 256                    )
 257                );
 258            } else if (o.op == "COPYN") {
 259                code.push(
 260                    c.call(
 261                        "Fr_copyn",
 262                        c.i32_add(
 263                            this.fnBuilder._getPtr(c, o.dLabel),
 264                            c.i32_mul(
 265                                this.fnBuilder._deRefInt(c, o.offset),
 266                                c.i32_const(this.fnBuilder.builder.sizeFr)
 267                            )
 268                        ),
 269                        this.fnBuilder._deRefFr(c, o.src),
 270                        c.i32_const(o.n)
 271                    )
 272                );
 273            } else if (o.op == "COPYNRET") {
 274                code.push(
 275                    c.call(
 276                        "Fr_copyn",
 277                        c.getLocal("pRet"),
 278                        this.fnBuilder._deRefFr(c, o.src),
 279                        c.i32_const(o.n)
 280                    )
 281                );
 282            } else if (o.op == "RET") {
 283                code.push(this.fnBuilder._freeStack(c));
 284                code.push(c.ret([]));
 285            } else if (o.op == "FOP") {
 286                let params = [];
 287                for (let i=0; i<o.params.length; i++) {
 288                    params.push(  this.fnBuilder._deRefFr(c, o.params[i]));
 289                }
 290                code.push(
 291                    c.call(
 292                        "Fr_" + o.fOp,
 293                        this.fnBuilder._getPtr(c, o.dLabel),
 294                        ...params
 295                    )
 296                );
 297            } else if (o.op == "LOOP") {
 298                code.push(
 299                    c.block(c.loop(
 300                        c.br_if(1, c.i32_eqz(c.call("Fr_isTrue", this.fnBuilder._deRefFr(c, ["R", o.condLabel])))),
 301                        o.body.build(c),
 302                        c.br(0)
 303                    ))
 304                );
 305            } else if (o.op == "IF") {
 306                code.push(
 307                    c.if(
 308                        c.call("Fr_isTrue", this.fnBuilder._deRefFr(c, ["R", o.condLabel])),
 309                        o.thenCode.build(c),
 310                        o.elseCode ? o.elseCode.build(c) : undefined
 311                    )
 312                );
 313            } else if (o.op == "FNCALL") {
 314                const params = [];
 315                for (let i=0; i<o.params.length; i++) {
 316                    params.push(this.fnBuilder._deRefFr(c, ["R", o.params[i]]));
 317                }
 318                code.push(
 319                    c.call(
 320                        o.fnName,
 321                        this.fnBuilder._getPtr(c, o.retLabel),
 322                        ...params
 323                    )
 324                );
 325            } else if (o.op == "CHECKCONSTRAINT") {
 326                code.push(
 327                    c.call(
 328                        "checkConstraint",
 329                        c.getLocal("cIdx"),
 330                        this.fnBuilder._deRefFr(c, o.a),
 331                        this.fnBuilder._deRefFr(c, o.b),
 332                        c.i32_const(this.fnBuilder.builder.module.allocString(o.strErr))
 333                    )
 334                );
 335            } else if (o.op == "LOG") {
 336                code.push(
 337                    c.call(
 338                        "log",
 339                        this.fnBuilder._deRefFr(c, o.val)
 340                    )
 341                );
 342            }
 343        });
 344        return code;
 345    }
 346}
 347
 348class FunctionBuilderWasm {
 349
 350    constructor(builder, name, instanceDef, type, params) {
 351        this.builder = builder;
 352        this.name = name;
 353        this.instanceDef = instanceDef;
 354        this.type = type; // "COMPONENT" or "FUNCTION"
 355        this.definedFrElements = [];
 356        this.definedIntElements = [];
 357        this.definedSizeElements = [];
 358        this.definedPFrElements = [];
 359        this.initializedElements = [];
 360        this.initializedSignalOffset = [];
 361        this.initializedSignalSizes = [];
 362
 363        this.refs = {};
 364
 365        this.nFr = 0;
 366        this.nInt = 0;
 367        this.nSizes = 0;
 368        this.nPFr = 0;
 369
 370    }
 371
 372    defineFrElements(dLabel, size) {
 373        this.refs[dLabel] = {
 374            t: "Fr",
 375            n: size,
 376            idx: this.nFr
 377        };
 378        this.nFr += size;
 379    }
 380
 381    defineIntElement(dLabel) {
 382        this.refs[dLabel] = {
 383            t: "I",
 384            idx: this.nInt
 385        };
 386        this.nInt++;
 387    }
 388
 389    defineSizesElement(dLabel) {
 390        this.refs[dLabel] = {
 391            t: "S",
 392            idx: this.nSizes
 393        };
 394        this.nSizes++;
 395    }
 396
 397    definePFrElement(dLabel) {
 398        this.refs[dLabel] = {
 399            t: "PFr",
 400            idx: this.nPFr
 401        };
 402        this.nPFr++;
 403    }
 404
 405    initializeFrElement(dLabel, offset, idConstant) {
 406        this.initializedElements.push({dLabel, offset, idConstant});
 407    }
 408
 409    initializeSignalOffset(dLabel, component, hash, hashLabel) {
 410        this.initializedSignalOffset.push({dLabel, component, hash, hashLabel});
 411    }
 412
 413    initializeSignalSizes(dLabel, component, hash, hashLabel) {
 414        this.initializedSignalSizes.push({dLabel, component, hash, hashLabel});
 415    }
 416
 417    setParams(params) {
 418        assert(typeof this.params === "undefined");
 419        assert(this.type === "FUNCTION");
 420        this.params=params;
 421        for (let i=0; i<this.params.length; i++) {
 422            assert(typeof this.refs[this.params[i]] === "undefined");
 423            this.refs[this.params[i]] = {
 424                t: "P",
 425                l: this.params[i]
 426            };
 427        }
 428    }
 429
 430    _getPtr(c, label, offset) {
 431        offset = offset || 0;
 432        assert (typeof this.refs[label] !== "undefined");
 433        if (this.refs[label].t == "Fr") {
 434            return c.i32_add(
 435                c.getLocal("sp"),
 436                c.i32_const((this.refs[label].idx + offset) * this.builder.sizeFr)
 437            );
 438        } else if (this.refs[label].t == "I") {
 439            assert(offset == 0);
 440            return c.i32_add(
 441                c.getLocal("sp"),
 442                c.i32_const(
 443                    this.nFr * this.builder.sizeFr +
 444                    this.refs[label].idx * 4)
 445            );
 446        } else if (this.refs[label].t == "S") {
 447            assert(offset == 0);
 448            return c.i32_add(
 449                c.getLocal("sp"),
 450                c.i32_const(
 451                    this.nFr * this.builder.sizeFr +
 452                    this.nInt * 4 +
 453                    this.refs[label].idx * 4)
 454            );
 455        } else if (this.refs[label].t == "PFr") {
 456            assert(offset == 0);
 457            return c.i32_add(
 458                c.getLocal("sp"),
 459                c.i32_const(
 460                    this.nFr * this.builder.sizeFr +
 461                    this.nInt * 4 +
 462                    this.nSizes * 4 +
 463                    this.refs[label].idx * 4)
 464            );
 465        } else if (this.refs[label].t == "P") {
 466            return c.i32_add(
 467                c.getLocal(this.refs[label].l),
 468                c.i32_const( offset * this.builder.sizeFr )
 469            );
 470        }
 471    }
 472
 473    _getPtrConstant(c, idConstant) {
 474        return c.i32_const( this.builder.pConstants + idConstant * this.builder.sizeFr );
 475    }
 476
 477    _deRefInt(c, ref) {
 478        if (ref[0] == "R") {
 479            return c.call(
 480                "Fr_toInt",
 481                this._getPtr(c, ref[1])
 482            );
 483        } else if (ref[0] == "RI") {
 484            return c.i32_load(this._getPtr(c, ref[1]));
 485        } else if (ref[0] == "V") {
 486            return c.i32_const(ref[1]);
 487        } else if (ref[0] == "C") {
 488            return c.call(
 489                "Fr_toInt",
 490                this._getPtrConstant(c, ref[1])
 491            );
 492        } else if (ref[0] == "CC") {
 493            return c.getLocal("cIdx");
 494        } else {
 495            assert(false);
 496        }
 497    }
 498
 499    _deRefFr(c, ref) {
 500        if (ref[0] == "R") {
 501            if (this.refs[ref[1]].t == "Fr") {
 502                return this._getPtr(c, ref[1]);
 503            } else if (this.refs[ref[1]].t == "PFr") {
 504                return c.i32_load(this._getPtr(c, ref[1]));
 505            } else if (this.refs[ref[1]].t == "P") {
 506                return c.getLocal(ref[1]);
 507            } else {
 508                assert(false);
 509            }
 510        } else if (ref[0] == "C") {
 511            return this._getPtrConstant(c, ref[1]);
 512        } else {
 513            assert(false);
 514        }
 515    }
 516
 517    _reserveStack(c) {
 518        const code = [];
 519        code.push(
 520            // Load SP
 521            c.setLocal("sp", c.i32_load(c.i32_const(4))),
 522
 523            // Check we have enough memory
 524            c.if(
 525                c.i32_gt_u(
 526                    c.i32_const(this.nStackSize),
 527                    c.getLocal("sp")
 528                ),
 529                c.call(
 530                    "error",
 531                    c.i32_const(errs.STACK_OUT_OF_MEM.code),
 532                    c.i32_const(errs.STACK_OUT_OF_MEM.pointer),
 533                    c.i32_const(0),
 534                    c.i32_const(0),
 535                    c.i32_const(0),
 536                    c.i32_const(0)
 537                )
 538            ),
 539
 540            // Reserve space in sp
 541            c.setLocal(
 542                "sp",
 543                c.i32_sub(
 544                    c.getLocal("sp"),
 545                    c.i32_const(this.nStackSize)
 546                )
 547            ),
 548
 549            // Check if we have enought free memory
 550            c.if(
 551                c.i32_gt_u(
 552                    c.i32_load(c.i32_const(0)),
 553                    c.getLocal("sp")
 554                ),
 555                c.call(
 556                    "error",
 557                    c.i32_const(errs.STACK_TOO_SMALL.code),
 558                    c.i32_const(errs.STACK_TOO_SMALL.pointer),
 559                    c.i32_const(0),
 560                    c.i32_const(0),
 561                    c.i32_const(0),
 562                    c.i32_const(0)
 563                )
 564            ),
 565
 566            // Save sp
 567            c.i32_store(c.i32_const(4), c.getLocal("sp"))
 568        );
 569        return code;
 570    }
 571
 572    _freeStack(c) {
 573        const code = [];
 574        code.push(
 575            c.i32_store(
 576                c.i32_const(4),
 577                c.i32_add(
 578                    c.getLocal("sp"),
 579                    c.i32_const(this.nStackSize)
 580                )
 581            )
 582        );
 583        return code;
 584    }
 585
 586    _buildHeader(c) {
 587        const code = [];
 588        this.nStackSize =
 589            this.nFr * this.builder.sizeFr +
 590            this.nInt * 4 +
 591            this.nSizes * 4 +
 592            this.nPFr * 4;
 593
 594
 595        code.push( this._reserveStack(c));
 596
 597        this.initializedElements.forEach( (o) => {
 598            code.push(
 599                c.call(
 600                    "Fr_copy",
 601                    this._getPtr(c, o.dLabel, o.offset),
 602                    this._getPtrConstant(c, o.idConstant)
 603                )
 604            );
 605        });
 606        this.initializedSignalOffset.forEach( (o) => {
 607            code.push(
 608                c.call(
 609                    "getSignalOffset",
 610                    this._getPtr(c, o.dLabel, o.offset),
 611                    this._deRefInt(c, o.component),
 612                    c.i64_const("0x" + o.hash)
 613                )
 614            );
 615        });
 616        this.initializedSignalSizes.forEach( (o) => {
 617            code.push(
 618                c.call(
 619                    "getSignalSizes",
 620                    this._getPtr(c, o.dLabel, o.offset),
 621                    this._deRefInt(c, o.component),
 622                    c.i64_const("0x" + o.hash)
 623                )
 624            );
 625        });
 626        return code;
 627    }
 628
 629    _buildFooter(c) {
 630        return this._freeStack(c);
 631    }
 632
 633    newCodeBuilder() {
 634        return new CodeBuilderWasm(this);
 635    }
 636
 637    setBody(body) {
 638        this.body = body;
 639    }
 640
 641    build(module) {
 642
 643
 644        const f = module.addFunction(this.name, this.instanceDef);
 645
 646        if (this.type=="COMPONENT") {
 647            f.addParam("cIdx", "i32");
 648        } else if (this.type=="FUNCTION") {
 649            f.addParam("pRet", "i32");
 650            for (let i=0;i<this.params.length;i++ ) {
 651                f.addParam(this.params[i], "i32");
 652            }
 653        } else {
 654            assert(false);
 655        }
 656        f.addLocal("sp", "i32");
 657
 658        const c = f.getCodeBuilder();
 659
 660
 661        const code = [];
 662        if (this.type=="COMPONENT") {
 663            code.push(c.call("componentStarted", c.getLocal("cIdx")));
 664        }
 665
 666        code.push(this._buildHeader(c));
 667        code.push(this.body.build(c));
 668        if (this.type=="COMPONENT") {
 669            code.push(c.call("componentFinished", c.getLocal("cIdx")));
 670        }
 671        code.push(this._buildFooter(c));
 672
 673        f.addCode(flatArray(code));
 674
 675        function flatArray(c) {
 676            const res=[];
 677            for (let i=0; i<c.length; i++) {
 678                if (Array.isArray(c[i])) {
 679                    res.push(...flatArray(c[i]));
 680                } else {
 681                    res.push(c[i]);
 682                }
 683            }
 684            return res;
 685        }
 686    }
 687
 688}
 689
 690class BuilderWasm {
 691    constructor(p) {
 692        this.F = new F1Field(p);
 693        this.hashMaps={};
 694        this.componentEntriesTables={};
 695        this.sizes ={};
 696        this.constants = [];
 697        this.usedConstants = {};
 698        this.functions = [];
 699        this.components = [];
 700
 701        this.TYPE_SIGNAL = 1;
 702        this.TYPE_COMPONENT = 2;
 703
 704        this.addConstant(Scalar.fromString("0"));  // constants[0] = 0;
 705        this.addConstant(Scalar.fromString("1"));  // constants[1] = 1;
 706
 707        this.offsetComponentNInputSignals = 12;
 708        this.sizeofComponent = 20;
 709    }
 710
 711    setHeader(header) {
 712        this.header=header;
 713
 714        this.n64 = Math.floor((Scalar.bitLength(this.header.P) - 1) / 64)+1;
 715        this.sizeFr = this.n64*8 + 8;
 716    }
 717
 718    // ht is an array of 256 element that can be undefined or [Hash, Idx, KeyName] elements.
 719    addHashMap(name, table) {
 720        this.hashMaps[name] = {table};
 721    }
 722
 723    addComponentEntriesTable(name, cet) {
 724        this.componentEntriesTables[name] = cet;
 725    }
 726
 727    addSizes(name, table) {
 728        this.sizes[name] = {table};
 729    }
 730
 731    addConstant(c) {
 732        c = this.F.e(c);
 733        const cS = c.toString();
 734        if (typeof this.usedConstants[cS] != "undefined") return this.usedConstants[cS];
 735        this.constants.push(c);
 736        this.usedConstants[cS] = this.constants.length - 1;
 737        return this.constants.length - 1;
 738    }
 739
 740    addFunction(fnBuilder) {
 741        this.functions.push(fnBuilder);
 742    }
 743
 744    addComponent(component) {
 745        this.components.push(component);
 746    }
 747
 748    setMapIsInput(map) {
 749        this.mapIsInput = map;
 750    }
 751
 752    setWit2Sig(wit2sig) {
 753        this.wit2sig = wit2sig;
 754    }
 755
 756
 757    newComponentFunctionBuilder(name, instanceDef) {
 758        return new FunctionBuilderWasm(this, name, instanceDef, "COMPONENT");
 759    }
 760
 761    newFunctionBuilder(name, instanceDef, params) {
 762        return new FunctionBuilderWasm(this, name, instanceDef, "FUNCTION", params);
 763    }
 764
 765
 766    // Body functions
 767
 768    _buildHeader(module) {
 769
 770        this.pCircuit = module.alloc(48);
 771
 772        this.pNSignals = this.pCircuit;
 773        this.pNComponents = this.pCircuit + 4;
 774        this.pNInputs = this.pCircuit + 8;
 775        this.pNOutputs = this.pCircuit + 12;
 776        this.pNVars = this.pCircuit + 16;
 777        this.ppWit2sig = this.pCircuit + 20;
 778        this.ppComponents = this.pCircuit + 24;
 779        this.ppMapIsInput = this.pCircuit + 28;
 780        this.ppConstants = this.pCircuit + 32;
 781        this.ppSignals = this.pCircuit + 36;
 782        this.ppInputSignalsToTrigger = this.pCircuit + 40;
 783        this.ppSignalsAssigned = this.pCircuit + 44;
 784    }
 785
 786    _buildSizes(module) {
 787        for (let sName in this.sizes) {
 788            const accSizes = this.sizes[sName];
 789
 790            const bytes = [];
 791
 792            for (let i=0; i<accSizes.table.length; i++) {
 793                bytes.push(intToBytes32(accSizes.table[i]));
 794            }
 795
 796            const fBytes = [].concat(...bytes);
 797            accSizes.pointer = module.alloc(fBytes);
 798        }
 799    }
 800
 801
 802    _buildHashMaps(module) {
 803
 804        for (let hmName in this.hashMaps ) {
 805            const hm = this.hashMaps[hmName];
 806
 807            const bytes = [];
 808
 809            for (let i=0; i<256; i++) {
 810                if (hm.table[i]) {
 811                    bytes.push(hexToBytesR(hm.table[i][0]));
 812                    bytes.push(intToBytes32(hm.table[i][1]));
 813                } else {
 814                    bytes.push([0,0,0,0,0,0,0,0,0,0,0,0]);
 815                }
 816            }
 817
 818            const fBytes = [].concat(...bytes);
 819            hm.pointer = module.alloc(fBytes);
 820        }
 821
 822    }
 823
 824    _buildComponentEntriesTables(module) {
 825
 826        for (let cetName in this.componentEntriesTables) {
 827            const cet = this.componentEntriesTables[cetName];
 828
 829            const bytes = [];
 830
 831            for (let j=0; j<cet.length; j++) {
 832                const ty = cet[j].type == "S" ? this.TYPE_SIGNAL : this.TYPE_COMPONENT;
 833                bytes.push(intToBytes32(cet[j].offset));
 834                bytes.push(intToBytes32(this.sizes[cet[j].sizeName].pointer));
 835                bytes.push(intToBytes32(ty));
 836            }
 837
 838            const fBytes = [].concat(...bytes);
 839            this.componentEntriesTables[cetName].pointer = module.alloc(fBytes);
 840        }
 841    }
 842
 843
 844    _buildConstants(module) {
 845        const self = this;
 846
 847        const bytes = [];
 848        for (let i=0; i<self.constants.length; i++) {
 849            bytes.push(Fr2Bytes(self.constants[i]));
 850        }
 851
 852        const fBytes = [].concat(...bytes);
 853        this.pConstants = module.alloc(fBytes);
 854
 855
 856        function Fr2Bytes(n) {
 857            const minShort = self.F.neg(self.F.e("80000000"));
 858            const maxShort = self.F.e("7FFFFFFF", 16);
 859
 860            if (  (self.F.geq(n, minShort))
 861                &&(self.F.leq(n, maxShort)))
 862            {
 863                if (self.F.geq(n, self.F.zero)) {
 864                    return shortMontgomeryPositive(n);
 865                } else {
 866                    return shortMontgomeryNegative(n);
 867                }
 868            }
 869
 870            return longMontgomery(n);
 871
 872
 873            function shortMontgomeryPositive(a) {
 874                return [
 875                    ...intToBytes32(Scalar.toNumber(a)),
 876                    ...intToBytes32(0x40000000),
 877                    ...long(toMontgomery(a))
 878                ];
 879            }
 880
 881
 882            function shortMontgomeryNegative(a) {
 883                const b = -Scalar.toNumber(self.F.neg(a));
 884                return [
 885                    ...intToBytes32(b),
 886                    ...intToBytes32(0x40000000),
 887                    ...long(toMontgomery(a))
 888                ];
 889            }
 890
 891            function longMontgomery(a) {
 892                return [
 893                    ...intToBytes32(0),
 894                    ...intToBytes32(0xC0000000),
 895                    ...long(toMontgomery(a))
 896                ];
 897            }
 898
 899            function long(a) {
 900
 901                const bytes = [];
 902                const arr = Scalar.toArray(a, 0x100000000);
 903                for (let i=0; i<self.F.n64*2; i++) {
 904                    const idx = arr.length-1-i;
 905
 906                    if ( idx >=0) {
 907                        bytes.push(...intToBytes32(arr[idx]));
 908                    } else {
 909                        bytes.push(...intToBytes32(0));
 910                    }
 911                }
 912
 913                return bytes;
 914            }
 915
 916            function toMontgomery(a) {
 917                return self.F.mul(a, self.F.R);
 918            }
 919
 920        }
 921    }
 922
 923    _buildFunctions(module) {
 924        for (let i=0; i<this.functions.length; i++) {
 925            const cfb = this.functions[i];
 926            cfb.build(module);
 927        }
 928    }
 929
 930    _buildComponents(module) {
 931        const bytes = new Array(this.components.length*5*4);
 932        bytes.length=0;
 933        for (let i=0; i<this.components.length; i++) {
 934            const c = this.components[i];
 935
 936            bytes.push(...intToBytes32(this.hashMaps[c.hashMapName].pointer));
 937            bytes.push(...intToBytes32(this.componentEntriesTables[c.entryTableName].pointer));
 938            bytes.push(...intToBytes32(i));
 939            bytes.push(...intToBytes32(c.nInSignals));
 940            bytes.push(...intToBytes32(c.newThread ? 1 : 0));
 941
 942            module.addFunctionToTable(c.functionName);
 943        }
 944
 945        this.pComponents = module.alloc(bytes);
 946    }
 947
 948    _buildMapIsInput(module) {
 949        const bytes = new Array(this.mapIsInput.length*4);
 950        bytes.length=0;
 951        for (let i=0; i<this.mapIsInput.length; i++) {
 952            bytes.push(...intToBytes32(this.mapIsInput[i]));
 953        }
 954
 955        this.pMapIsInput = module.alloc(bytes);
 956    }
 957
 958    _buildWit2Sig(module) {
 959        const bytes = new Array(this.wit2sig.length*4);
 960        bytes.length =0;
 961        for (let i=0; i<this.wit2sig.length; i++) {
 962            bytes.push(...intToBytes32(this.wit2sig[i]));
 963        }
 964
 965        this.pWit2sig = module.alloc(bytes);
 966    }
 967
 968    _buildCircuitVar(module) {
 969        module.addData(this.pNSignals, intToBytes32(this.header.NSignals));
 970        module.addData(this.pNComponents, intToBytes32(this.header.NComponents));
 971        module.addData(this.pNInputs, intToBytes32(this.header.NInputs));
 972        module.addData(this.pNOutputs, intToBytes32(this.header.NOutputs));
 973        module.addData(this.pNVars, intToBytes32(this.header.NVars));
 974        module.addData(this.ppWit2sig, intToBytes32(this.pWit2sig));
 975        module.addData(this.ppComponents, intToBytes32(this.pComponents));
 976        module.addData(this.ppMapIsInput, intToBytes32(this.pMapIsInput));
 977        module.addData(this.ppConstants, intToBytes32(this.pConstants));
 978        module.addData(this.ppSignals, intToBytes32(this.pSignals));
 979        module.addData(this.ppInputSignalsToTrigger, intToBytes32(this.pInputSignalsToTrigger));
 980        module.addData(this.ppSignalsAssigned, intToBytes32(this.pSignalsAssigned));
 981
 982    }
 983
 984    _buildErrors(module) {
 985        for (let e in errs) {
 986            errs[e].pointer = module.allocString(errs[e].str);
 987        }
 988    }
 989
 990
 991    async build(fd, outType) {
 992        const encoder = new TextEncoder("utf-8");
 993        let module;
 994        if (outType == "wasm") {
 995            module=new ModuleBuilder();
 996        } else if (outType == "wat") {
 997            module=new ModuleBuilderWat();
 998        } else {
 999            assert(false);
1000        }
1001        this.module = module;
1002
1003        // First of all reseve the space for the header so this has a fixed position starting at 8.
1004        this._buildHeader(module);
1005        this._buildErrors(module);
1006        buildRuntime(module, this);
1007        this._buildSizes(module);
1008        this._buildConstants(module);
1009        this._buildHashMaps(module);
1010        this._buildComponentEntriesTables(module);
1011        this._buildFunctions(module);
1012        this._buildComponents(module);
1013        this._buildMapIsInput(module);
1014        this._buildWit2Sig(module);
1015        this._buildCircuitVar(module);
1016
1017        module.setMemory(2000);
1018        if (outType == "wasm") {
1019            const bytes = module.build();
1020            const bytesArr = new Uint8Array(bytes);
1021            await fd.write(bytesArr);
1022        } else if (outType == "wat") {
1023            const code = module.build();
1024            await writeCode(code);
1025        } else {
1026            assert(false);
1027        }
1028
1029        async function writeCode(c) {
1030            if (c.push) {
1031                for (let i=0; i<c.length; i++) {
1032                    await writeCode(c[i]);
1033                }
1034            } else if (typeof c === "string") {
1035                await fd.write(encoder.encode(c + "\n"));
1036            }
1037        }
1038    }
1039}
1040
1041
1042module.exports = BuilderWasm;