1// Copyright 2017 The Wuffs Authors. 2// 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// https://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 15package cgen 16 17import ( 18 "errors" 19 "flag" 20 "fmt" 21 "math/big" 22 "sort" 23 "strings" 24 25 "github.com/google/wuffs/internal/cgen/data" 26 "github.com/google/wuffs/lang/builtin" 27 "github.com/google/wuffs/lang/generate" 28 "github.com/google/wuffs/lib/dumbindent" 29 30 cf "github.com/google/wuffs/cmd/commonflags" 31 32 a "github.com/google/wuffs/lang/ast" 33 t "github.com/google/wuffs/lang/token" 34) 35 36var ( 37 zero = big.NewInt(0) 38 one = big.NewInt(1) 39 eight = big.NewInt(8) 40 sixtyFour = big.NewInt(64) 41 42 mibi = big.NewInt(1 << 20) 43 44 maxInt64 = big.NewInt((1 << 63) - 1) 45 46 typeExprUtility = a.NewTypeExpr(0, t.IDBase, t.IDUtility, nil, nil, nil) 47) 48 49// Prefixes are prepended to names to form a namespace and to avoid e.g. 50// "double" being a valid Wuffs variable name but not a valid C one. 51const ( 52 aPrefix = "a_" // Function argument. 53 fPrefix = "f_" // Struct field. 54 iPrefix = "i_" // Iterate variable. 55 oPrefix = "o_" // Temporary io_bind variable. 56 pPrefix = "p_" // Coroutine suspension point (program counter). 57 sPrefix = "s_" // Coroutine stack (saved local variables). 58 tPrefix = "t_" // Temporary local variable. 59 uPrefix = "u_" // Derived from a local variable. 60 vPrefix = "v_" // Local variable. 61) 62 63// I/O (reader/writer) prefixes. In the generated C code, the variables with 64// these prefixes all have type uint8_t*. The iop_etc variables are the key 65// ones. For an io_reader or io_writer function argument a_src or a_dst, 66// reading or writing the next byte (and advancing the stream) is essentially 67// "etc = *iop_a_src++" or "*io_a_dst++ = etc". 68// 69// The other two prefixes, giving names like io1_etc and io2_etc, are 70// auxilliary pointers: lower and upper inclusive bounds. As an iop_etc pointer 71// advances, it cannot advance past io2_etc. In the rarer case that an iop_etc 72// pointer retreats, undoing a read or write, it cannot retreat past io1_etc. 73// 74// The iop_etc pointer can change over the lifetime of a function. The ioN_etc 75// pointers, for numeric N, cannot. 76// 77// At the start of a function, these pointers are initialized from an 78// io_buffer's fields (ptr, ri, wi, len). For an io_reader: 79// - io0_etc = ptr 80// - io1_etc = ptr + ri 81// - iop_etc = ptr + ri 82// - io2_etc = ptr + wi 83// and for an io_writer: 84// - io0_etc = ptr 85// - io1_etc = ptr + wi 86// - iop_etc = ptr + wi 87// - io2_etc = ptr + len 88const ( 89 io0Prefix = "io0_" // Base. 90 io1Prefix = "io1_" // Lower bound. 91 io2Prefix = "io2_" // Upper bound. 92 iopPrefix = "iop_" // Pointer. 93) 94 95// BaseSubModules is the list of lower-cased XXX's in the base module's 96// WUFFS_CONFIG__MODULE__BASE__XXX sub-modules. 97var BaseSubModules = []string{ 98 "core", 99 "floatconv", 100 "intconv", 101 "interfaces", 102 "pixconv", 103 "utf8", 104} 105 106// Do transpiles a Wuffs program to a C program. 107// 108// The arguments list the source Wuffs files. If no arguments are given, it 109// reads from stdin. 110// 111// The generated program is written to stdout. 112func Do(args []string) error { 113 flags := flag.FlagSet{} 114 genlinenumFlag := flags.Bool("genlinenum", cf.GenlinenumDefault, cf.GenlinenumUsage) 115 116 return generate.Do(&flags, args, func(pkgName string, tm *t.Map, files []*a.File) ([]byte, error) { 117 unformatted := []byte(nil) 118 if pkgName == "base" { 119 if len(files) != 0 { 120 return nil, fmt.Errorf("base package shouldn't have any .wuffs files") 121 } 122 buf := make(buffer, 0, 128*1024) 123 if err := expandBangBangInsert(&buf, data.BaseAllImplC, map[string]func(*buffer) error{ 124 "// !! INSERT InterfaceDeclarations.\n": insertInterfaceDeclarations, 125 "// !! INSERT InterfaceDefinitions.\n": insertInterfaceDefinitions, 126 "// !! INSERT base/all-private.h.\n": insertBaseAllPrivateH, 127 "// !! INSERT base/all-public.h.\n": insertBaseAllPublicH, 128 "// !! INSERT base/copyright\n": insertBaseCopyright, 129 "// !! INSERT base/floatconv-submodule.c.\n": insertBaseFloatConvSubmoduleC, 130 "// !! INSERT base/intconv-submodule.c.\n": insertBaseIntConvSubmoduleC, 131 "// !! INSERT base/pixconv-submodule.c.\n": insertBasePixConvSubmoduleC, 132 "// !! INSERT base/utf8-submodule.c.\n": insertBaseUTF8SubmoduleC, 133 "// !! INSERT vtable names.\n": func(b *buffer) error { 134 for _, n := range builtin.Interfaces { 135 buf.printf("const char wuffs_base__%s__vtable_name[] = "+ 136 "\"{vtable}wuffs_base__%s\";\n", n, n) 137 } 138 return nil 139 }, 140 "// !! INSERT wuffs_base__status strings.\n": func(b *buffer) error { 141 for _, z := range builtin.Statuses { 142 msg, _ := t.Unescape(z) 143 if msg == "" { 144 continue 145 } 146 pre := "note" 147 if msg[0] == '$' { 148 pre = "suspension" 149 } else if msg[0] == '#' { 150 pre = "error" 151 } 152 b.printf("const char wuffs_base__%s__%s[] = \"%sbase: %s\";\n", 153 pre, cName(msg, ""), msg[:1], msg[1:]) 154 } 155 return nil 156 }, 157 }); err != nil { 158 return nil, err 159 } 160 unformatted = []byte(buf) 161 162 } else { 163 g := &gen{ 164 PKGPREFIX: "WUFFS_" + strings.ToUpper(pkgName) + "__", 165 PKGNAME: strings.ToUpper(pkgName), 166 pkgPrefix: "wuffs_" + pkgName + "__", 167 pkgName: pkgName, 168 tm: tm, 169 files: files, 170 genlinenum: *genlinenumFlag, 171 } 172 var err error 173 unformatted, err = g.generate() 174 if err != nil { 175 return nil, err 176 } 177 } 178 179 // The base package is largely hand-written C, not transpiled from 180 // Wuffs, and that part is presumably already formatted. The rest is 181 // generated by this package. We take care here to print well indented 182 // C code, so further C formatting is unnecessary. 183 if pkgName == "base" { 184 return unformatted, nil 185 } 186 187 return dumbindent.FormatBytes(nil, unformatted, nil), nil 188 }) 189} 190 191type visibility uint32 192 193const ( 194 bothPubPri = visibility(iota) 195 pubOnly 196 priOnly 197) 198 199const ( 200 maxIOBinds = 100 201 maxTemp = 10000 202) 203 204type status struct { 205 cName string 206 msg string 207 fromThisPkg bool 208 public bool 209} 210 211func statusMsgIsError(msg string) bool { 212 return (len(msg) != 0) && (msg[0] == '#') 213} 214 215func statusMsgIsNote(msg string) bool { 216 return (len(msg) == 0) || (msg[0] != '$' && msg[0] != '#') 217} 218 219func statusMsgIsSuspension(msg string) bool { 220 return (len(msg) != 0) && (msg[0] == '$') 221} 222 223type buffer []byte 224 225func (b *buffer) Write(p []byte) (int, error) { 226 *b = append(*b, p...) 227 return len(p), nil 228} 229 230func (b *buffer) printf(format string, args ...interface{}) { fmt.Fprintf(b, format, args...) } 231func (b *buffer) writeb(x byte) { *b = append(*b, x) } 232func (b *buffer) writes(s string) { *b = append(*b, s...) } 233func (b *buffer) writex(s []byte) { *b = append(*b, s...) } 234 235func expandBangBangInsert(b *buffer, s string, m map[string]func(*buffer) error) error { 236 for { 237 remaining := "" 238 if i := strings.IndexByte(s, '\n'); i >= 0 { 239 s, remaining = s[:i+1], s[i+1:] 240 } 241 242 const prefix = "// !! INSERT " 243 if !strings.HasPrefix(s, prefix) { 244 b.writes(s) 245 } else if f := m[s]; f == nil { 246 msg := []byte(fmt.Sprintf("unrecognized line %q, want one of:\n", s)) 247 keys := []string(nil) 248 for k := range m { 249 keys = append(keys, k) 250 } 251 sort.Strings(keys) 252 for _, k := range keys { 253 msg = append(msg, '\t') 254 msg = append(msg, k...) 255 } 256 return errors.New(string(msg)) 257 } else if err := f(b); err != nil { 258 return err 259 } 260 261 if remaining == "" { 262 break 263 } 264 s = remaining 265 } 266 return nil 267} 268 269func insertBaseAllPrivateH(buf *buffer) error { 270 buf.writes(data.BaseFundamentalPrivateH) 271 buf.writeb('\n') 272 buf.writes(data.BaseRangePrivateH) 273 buf.writeb('\n') 274 buf.writes(data.BaseIOPrivateH) 275 buf.writeb('\n') 276 buf.writes(data.BaseTokenPrivateH) 277 buf.writeb('\n') 278 buf.writes(data.BaseMemoryPrivateH) 279 buf.writeb('\n') 280 buf.writes(data.BaseImagePrivateH) 281 buf.writeb('\n') 282 buf.writes(data.BaseStrConvPrivateH) 283 return nil 284} 285 286func insertBaseAllPublicH(buf *buffer) error { 287 if err := expandBangBangInsert(buf, data.BaseFundamentalPublicH, map[string]func(*buffer) error{ 288 "// !! INSERT FourCCs.\n": func(b *buffer) error { 289 for i, z := range builtin.FourCCs { 290 if i != 0 { 291 b.writeb('\n') 292 } 293 b.printf("// %s.\n#define WUFFS_BASE__FOURCC__%s 0x%02X%02X%02X%02X\n", 294 z[1], 295 strings.ToUpper(strings.TrimSpace(z[0])), 296 z[0][0], 297 z[0][1], 298 z[0][2], 299 z[0][3], 300 ) 301 } 302 return nil 303 }, 304 "// !! INSERT wuffs_base__status names.\n": func(b *buffer) error { 305 for _, z := range builtin.Statuses { 306 msg, _ := t.Unescape(z) 307 if msg == "" { 308 return fmt.Errorf("bad built-in status %q", z) 309 } 310 pre := "note" 311 if statusMsgIsError(msg) { 312 pre = "error" 313 } else if statusMsgIsSuspension(msg) { 314 pre = "suspension" 315 } 316 b.printf("extern const char wuffs_base__%s__%s[];\n", pre, cName(msg, "")) 317 } 318 return nil 319 }, 320 }); err != nil { 321 return err 322 } 323 buf.writeb('\n') 324 325 buf.writes(data.BaseRangePublicH) 326 buf.writeb('\n') 327 buf.writes(data.BaseIOPublicH) 328 buf.writeb('\n') 329 buf.writes(data.BaseTokenPublicH) 330 buf.writeb('\n') 331 buf.writes(data.BaseMemoryPublicH) 332 buf.writeb('\n') 333 buf.writes(data.BaseImagePublicH) 334 buf.writeb('\n') 335 buf.writes(data.BaseStrConvPublicH) 336 return nil 337} 338 339func insertBaseCopyright(buf *buffer) error { 340 buf.writes(data.BaseCopyright) 341 return nil 342} 343 344func insertBaseFloatConvSubmoduleC(buf *buffer) error { 345 buf.writes(data.BaseFloatConvSubmoduleDataC) 346 buf.writeb('\n') 347 buf.writes(data.BaseFloatConvSubmoduleCodeC) 348 return nil 349} 350 351func insertBaseIntConvSubmoduleC(buf *buffer) error { 352 buf.writes(data.BaseIntConvSubmoduleC) 353 return nil 354} 355 356func insertBasePixConvSubmoduleC(buf *buffer) error { 357 buf.writes(data.BasePixConvSubmoduleC) 358 return nil 359} 360 361func insertBaseUTF8SubmoduleC(buf *buffer) error { 362 buf.writes(data.BaseUTF8SubmoduleC) 363 return nil 364} 365 366func insertInterfaceDeclarations(buf *buffer) error { 367 if err := parseBuiltInInterfaceMethods(); err != nil { 368 return err 369 } 370 371 g := &gen{ 372 pkgPrefix: "wuffs_base__", 373 pkgName: "base", 374 tm: &builtInTokenMap, 375 } 376 377 buf.writes("// ---------------- Interface Declarations.\n\n") 378 379 buf.writes("// For modular builds that divide the base module into sub-modules, using these\n") 380 buf.writes("// functions require the WUFFS_CONFIG__MODULE__BASE__INTERFACES sub-module, not\n") 381 buf.writes("// just WUFFS_CONFIG__MODULE__BASE__CORE.\n") 382 383 for _, n := range builtin.Interfaces { 384 buf.writes("\n// --------\n\n") 385 386 qid := t.QID{t.IDBase, builtInTokenMap.ByName(n)} 387 388 buf.printf("extern const char wuffs_base__%s__vtable_name[];\n\n", n) 389 390 buf.writes("typedef struct {\n") 391 for _, f := range builtInInterfaceMethods[qid] { 392 buf.writes(" ") 393 if err := g.writeFuncSignature(buf, f, wfsCFuncPtrField); err != nil { 394 return err 395 } 396 buf.writes(";\n") 397 } 398 buf.printf("} wuffs_base__%s__func_ptrs;\n\n", n) 399 400 buf.printf("typedef struct wuffs_base__%s__struct wuffs_base__%s;\n\n", n, n) 401 402 for _, f := range builtInInterfaceMethods[qid] { 403 if err := g.writeFuncSignature(buf, f, wfsCDecl); err != nil { 404 return err 405 } 406 buf.writes(";\n\n") 407 } 408 409 buf.writes("#if defined(__cplusplus) || defined(WUFFS_IMPLEMENTATION)\n\n") 410 411 buf.printf("struct wuffs_base__%s__struct {\n", n) 412 buf.writes(" struct {\n") 413 buf.writes(" uint32_t magic;\n") 414 buf.writes(" uint32_t active_coroutine;\n") 415 buf.writes(" wuffs_base__vtable first_vtable;\n") 416 buf.writes(" } private_impl;\n\n") 417 418 buf.writes("#ifdef __cplusplus\n") 419 buf.writes("#if (__cplusplus >= 201103L)\n") 420 buf.printf(" using unique_ptr = std::unique_ptr<wuffs_base__%s, decltype(&free)>;\n", n) 421 buf.writes("#endif\n\n") 422 423 for _, f := range builtInInterfaceMethods[qid] { 424 if err := g.writeFuncSignature(buf, f, wfsCppDecl); err != nil { 425 return err 426 } 427 buf.writes(" {\n return ") 428 buf.writes(g.funcCName(f)) 429 if len(f.In().Fields()) == 0 { 430 buf.writes("(this") 431 } else { 432 buf.writes("(\n this") 433 for _, o := range f.In().Fields() { 434 buf.writes(", ") 435 buf.writes(aPrefix) 436 buf.writes(o.AsField().Name().Str(g.tm)) 437 } 438 } 439 buf.writes(");\n }\n\n") 440 } 441 buf.writes("#endif // __cplusplus\n") 442 buf.printf("}; // struct wuffs_base__%s__struct\n\n", n) 443 444 buf.writes("#endif // defined(__cplusplus) || defined(WUFFS_IMPLEMENTATION)\n") 445 } 446 return nil 447} 448 449func insertInterfaceDefinitions(buf *buffer) error { 450 if err := parseBuiltInInterfaceMethods(); err != nil { 451 return err 452 } 453 454 g := &gen{ 455 pkgPrefix: "wuffs_base__", 456 pkgName: "base", 457 tm: &builtInTokenMap, 458 } 459 460 buf.writes("// ---------------- Interface Definitions.\n") 461 for i, n := range builtin.Interfaces { 462 if i > 0 { 463 buf.writes("// --------\n") 464 } 465 466 qid := t.QID{t.IDBase, builtInTokenMap.ByName(n)} 467 468 for _, f := range builtInInterfaceMethods[qid] { 469 returnsStatus := f.Effect().Coroutine() || 470 ((f.Out() != nil) && f.Out().IsStatus()) 471 472 buf.writeb('\n') 473 if err := g.writeFuncSignature(buf, f, wfsCDecl); err != nil { 474 return err 475 } 476 buf.writes(" {\n") 477 if err := writeFuncImplSelfMagicCheck(buf, g.tm, f); err != nil { 478 return err 479 } 480 481 buf.writes("\n const wuffs_base__vtable* v = &self->private_impl.first_vtable;\n") 482 buf.writes(" int i;\n") 483 buf.printf(" for (i = 0; i < %d; i++) {\n", a.MaxImplements) 484 buf.printf(" if (v->vtable_name == wuffs_base__%s__vtable_name) {\n", n) 485 buf.printf(" const wuffs_base__%s__func_ptrs* func_ptrs =\n"+ 486 " (const wuffs_base__%s__func_ptrs*)(v->function_pointers);\n", n, n) 487 buf.printf(" return (*func_ptrs->%s)(self", f.FuncName().Str(g.tm)) 488 for _, o := range f.In().Fields() { 489 buf.writes(", ") 490 buf.writes(aPrefix) 491 buf.writes(o.AsField().Name().Str(g.tm)) 492 } 493 buf.writes(");\n") 494 buf.writes(" } else if (v->vtable_name == NULL) {\n") 495 buf.writes(" break;\n") 496 buf.writes(" }\n") 497 buf.writes(" v++;\n") 498 buf.writes(" }\n\n") 499 500 buf.writes(" return ") 501 if returnsStatus { 502 buf.writes("wuffs_base__make_status(wuffs_base__error__bad_vtable)") 503 } else if err := writeOutParamZeroValue(buf, g.tm, f.Out()); err != nil { 504 return err 505 } 506 buf.writes(";\n}\n") 507 } 508 if (i + 1) < len(builtin.Interfaces) { 509 buf.writeb('\n') 510 } 511 } 512 513 return nil 514} 515 516var ( 517 builtInTokenMap = t.Map{} 518 builtInInterfaceMethods = map[t.QID][]*a.Func{} 519) 520 521func parseBuiltInInterfaceMethods() error { 522 if len(builtInInterfaceMethods) != 0 { 523 return nil 524 } 525 return builtin.ParseFuncs(&builtInTokenMap, builtin.InterfaceFuncs, func(f *a.Func) error { 526 qid := f.Receiver() 527 builtInInterfaceMethods[qid] = append(builtInInterfaceMethods[qid], f) 528 return nil 529 }) 530} 531 532type gen struct { 533 PKGPREFIX string // e.g. "WUFFS_JPEG__" 534 PKGNAME string // e.g. "JPEG" 535 pkgPrefix string // e.g. "wuffs_jpeg__" 536 pkgName string // e.g. "jpeg" 537 538 tm *t.Map 539 files []*a.File 540 541 // genlinenum is whether to print "// foo.wuffs:123" comments in the 542 // generated C code. This can be useful for debugging, although it is not 543 // enabled by default as it can lead to many spurious changes in the 544 // generated C code (due to line numbers changing) when editing Wuffs code. 545 genlinenum bool 546 547 privateDataFields map[t.QQID]struct{} 548 scalarConstsMap map[t.QID]*a.Const 549 statusList []status 550 statusMap map[t.QID]status 551 structList []*a.Struct 552 structMap map[t.QID]*a.Struct 553 554 currFunk funk 555 funks map[t.QQID]funk 556 557 numPublicCoroutines map[t.QID]uint32 558} 559 560func (g *gen) generate() ([]byte, error) { 561 b := new(buffer) 562 563 g.statusMap = map[t.QID]status{} 564 if err := g.forEachStatus(b, bothPubPri, (*gen).gatherStatuses); err != nil { 565 return nil, err 566 } 567 for _, z := range builtin.Statuses { 568 id, err := g.tm.Insert(z) 569 if err != nil { 570 return nil, err 571 } 572 msg, _ := t.Unescape(z) 573 if msg == "" { 574 return nil, fmt.Errorf("bad built-in status %q", z) 575 } 576 if err := g.addStatus(t.QID{t.IDBase, id}, msg, true); err != nil { 577 return nil, err 578 } 579 } 580 581 g.scalarConstsMap = map[t.QID]*a.Const{} 582 if err := g.forEachConst(b, bothPubPri, (*gen).gatherScalarConsts); err != nil { 583 return nil, err 584 } 585 586 // Make a topologically sorted list of structs. 587 unsortedStructs := []*a.Struct(nil) 588 for _, file := range g.files { 589 for _, tld := range file.TopLevelDecls() { 590 if tld.Kind() == a.KStruct { 591 unsortedStructs = append(unsortedStructs, tld.AsStruct()) 592 } 593 } 594 } 595 var ok bool 596 g.structList, ok = a.TopologicalSortStructs(unsortedStructs) 597 if !ok { 598 return nil, fmt.Errorf("cyclical struct definitions") 599 } 600 g.structMap = map[t.QID]*a.Struct{} 601 g.privateDataFields = map[t.QQID]struct{}{} 602 g.numPublicCoroutines = map[t.QID]uint32{} 603 for _, n := range g.structList { 604 qid := n.QID() 605 g.structMap[qid] = n 606 for _, f := range n.Fields() { 607 f := f.AsField() 608 if f.PrivateData() { 609 g.privateDataFields[t.QQID{qid[0], qid[1], f.Name()}] = struct{}{} 610 } 611 } 612 } 613 614 g.funks = map[t.QQID]funk{} 615 if err := g.forEachFunc(nil, bothPubPri, (*gen).gatherFuncImpl); err != nil { 616 return nil, err 617 } 618 619 includeGuard := "WUFFS_INCLUDE_GUARD__" + g.PKGNAME 620 b.printf("#ifndef %s\n#define %s\n\n", includeGuard, includeGuard) 621 622 if err := g.genIncludes(b); err != nil { 623 return nil, err 624 } 625 626 b.writes("// !! WUFFS MONOLITHIC RELEASE DISCARDS EVERYTHING ABOVE.\n\n") 627 628 if err := g.genHeader(b); err != nil { 629 return nil, err 630 } 631 b.writex(wiStartImpl) 632 if err := g.genImpl(b); err != nil { 633 return nil, err 634 } 635 b.writex(wiEnd) 636 637 b.writes("// !! WUFFS MONOLITHIC RELEASE DISCARDS EVERYTHING BELOW.\n\n") 638 639 b.printf("#endif // %s\n\n", includeGuard) 640 return *b, nil 641} 642 643var ( 644 wiStartImpl = []byte("\n// WUFFS C HEADER ENDS HERE.\n#ifdef WUFFS_IMPLEMENTATION\n\n") 645 wiEnd = []byte("\n#endif // WUFFS_IMPLEMENTATION\n\n") 646) 647 648func (g *gen) genIncludes(b *buffer) error { 649 b.writes("#if defined(WUFFS_IMPLEMENTATION) && !defined(WUFFS_CONFIG__MODULES)\n") 650 b.writes("#define WUFFS_CONFIG__MODULES\n") 651 b.printf("#define WUFFS_CONFIG__MODULE__%s\n", g.PKGNAME) 652 b.writes("#endif\n\n") 653 654 usesList := []string(nil) 655 usesMap := map[string]struct{}{} 656 657 for _, file := range g.files { 658 for _, tld := range file.TopLevelDecls() { 659 if tld.Kind() != a.KUse { 660 continue 661 } 662 useDirname := g.tm.ByID(tld.AsUse().Path()) 663 useDirname, _ = t.Unescape(useDirname) 664 665 // TODO: sanity check useDirname via commonflags.IsValidUsePath? 666 667 if _, ok := usesMap[useDirname]; ok { 668 continue 669 } 670 usesMap[useDirname] = struct{}{} 671 usesList = append(usesList, useDirname) 672 } 673 } 674 sort.Strings(usesList) 675 676 b.writes("#include \"./wuffs-base.c\"\n") 677 for _, use := range usesList { 678 b.printf("#include \"./wuffs-%s.c\"\n", 679 strings.Replace(use, "/", "-", -1)) 680 } 681 682 b.writeb('\n') 683 return nil 684} 685 686func (g *gen) genHeader(b *buffer) error { 687 b.writes("\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n") 688 689 b.writes("// ---------------- Status Codes\n\n") 690 691 wroteStatus := false 692 for _, z := range g.statusList { 693 if !z.fromThisPkg || !z.public { 694 continue 695 } 696 b.printf("extern const char %s[];\n", z.cName) 697 wroteStatus = true 698 } 699 if wroteStatus { 700 b.writes("\n") 701 } 702 703 b.writes("// ---------------- Public Consts\n\n") 704 if err := g.forEachConst(b, pubOnly, (*gen).writeConst); err != nil { 705 return err 706 } 707 708 b.writes("// ---------------- Struct Declarations\n\n") 709 for _, n := range g.structList { 710 structName := n.QID().Str(g.tm) 711 b.printf("typedef struct %s%s__struct %s%s;\n\n", g.pkgPrefix, structName, g.pkgPrefix, structName) 712 } 713 714 b.writes("// ---------------- Public Initializer Prototypes\n\n") 715 b.writes("// For any given \"wuffs_foo__bar* self\", \"wuffs_foo__bar__initialize(self,\n") 716 b.writes("// etc)\" should be called before any other \"wuffs_foo__bar__xxx(self, etc)\".\n") 717 b.writes("//\n") 718 b.writes("// Pass sizeof(*self) and WUFFS_VERSION for sizeof_star_self and wuffs_version.\n") 719 b.writes("// Pass 0 (or some combination of WUFFS_INITIALIZE__XXX) for options.\n\n") 720 for _, n := range g.structList { 721 if n.Public() { 722 if err := g.writeInitializerPrototype(b, n); err != nil { 723 return err 724 } 725 } 726 } 727 728 b.writes("// ---------------- Allocs\n\n") 729 730 b.writes("// These functions allocate and initialize Wuffs structs. They return NULL if\n") 731 b.writes("// memory allocation fails. If they return non-NULL, there is no need to call\n") 732 b.writes("// wuffs_foo__bar__initialize, but the caller is responsible for eventually\n") 733 b.writes("// calling free on the returned pointer. That pointer is effectively a C++\n") 734 b.writes("// std::unique_ptr<T, decltype(&free)>.\n\n") 735 736 for _, n := range g.structList { 737 if !n.Public() { 738 continue 739 } 740 if err := g.writeAllocSignature(b, n); err != nil { 741 return err 742 } 743 b.writes(";\n\n") 744 structName := n.QID().Str(g.tm) 745 for _, impl := range n.Implements() { 746 iQID := impl.AsTypeExpr().QID() 747 iName := fmt.Sprintf("wuffs_%s__%s", iQID[0].Str(g.tm), iQID[1].Str(g.tm)) 748 b.printf("static inline %s*\n", iName) 749 b.printf("%s%s__alloc_as__%s() {\n", g.pkgPrefix, structName, iName) 750 b.printf("return (%s*)(%s%s__alloc());\n", iName, g.pkgPrefix, structName) 751 b.printf("}\n\n") 752 } 753 } 754 755 b.writes("// ---------------- Upcasts\n\n") 756 for _, n := range g.structList { 757 structName := n.QID().Str(g.tm) 758 for _, impl := range n.Implements() { 759 iQID := impl.AsTypeExpr().QID() 760 iName := fmt.Sprintf("wuffs_%s__%s", iQID[0].Str(g.tm), iQID[1].Str(g.tm)) 761 b.printf("static inline %s*\n", iName) 762 b.printf("%s%s__upcast_as__%s(\n %s%s* p) {\n", 763 g.pkgPrefix, structName, iName, g.pkgPrefix, structName) 764 b.printf("return (%s*)p;\n", iName) 765 b.printf("}\n\n") 766 } 767 } 768 769 b.writes("// ---------------- Public Function Prototypes\n\n") 770 if err := g.forEachFunc(b, pubOnly, (*gen).writeFuncPrototype); err != nil { 771 return err 772 } 773 774 b.writes("// ---------------- Struct Definitions\n\n") 775 b.writes("// These structs' fields, and the sizeof them, are private implementation\n") 776 b.writes("// details that aren't guaranteed to be stable across Wuffs versions.\n") 777 b.writes("//\n") 778 b.writes("// See https://en.wikipedia.org/wiki/Opaque_pointer#C\n\n") 779 b.writes("#if defined(__cplusplus) || defined(WUFFS_IMPLEMENTATION)\n\n") 780 781 for _, n := range g.structList { 782 if err := g.writeStruct(b, n); err != nil { 783 return err 784 } 785 } 786 b.writes("#endif // defined(__cplusplus) || defined(WUFFS_IMPLEMENTATION)\n") 787 788 b.writes("\n#ifdef __cplusplus\n} // extern \"C\"\n#endif\n\n") 789 return nil 790} 791 792func (g *gen) genImpl(b *buffer) error { 793 module := "!defined(WUFFS_CONFIG__MODULES) || defined(WUFFS_CONFIG__MODULE__" + g.PKGNAME + ")" 794 b.printf("#if %s\n\n", module) 795 796 b.writes("// ---------------- Status Codes Implementations\n\n") 797 798 wroteStatus := false 799 for _, z := range g.statusList { 800 if !z.fromThisPkg || z.msg == "" { 801 continue 802 } 803 b.printf("const char %s[] = \"%s%s: %s\";\n", z.cName, z.msg[:1], g.pkgName, z.msg[1:]) 804 wroteStatus = true 805 } 806 if wroteStatus { 807 b.writes("\n") 808 } 809 810 b.writes("// ---------------- Private Consts\n\n") 811 if err := g.forEachConst(b, priOnly, (*gen).writeConst); err != nil { 812 return err 813 } 814 815 b.writes("// ---------------- Private Initializer Prototypes\n\n") 816 for _, n := range g.structList { 817 if !n.Public() { 818 if err := g.writeInitializerPrototype(b, n); err != nil { 819 return err 820 } 821 } 822 } 823 824 b.writes("// ---------------- Private Function Prototypes\n\n") 825 if err := g.forEachFunc(b, priOnly, (*gen).writeFuncPrototype); err != nil { 826 return err 827 } 828 829 b.writes("// ---------------- VTables\n\n") 830 for _, n := range g.structList { 831 if err := g.writeVTableImpl(b, n); err != nil { 832 return err 833 } 834 } 835 836 b.writes("// ---------------- Initializer Implementations\n\n") 837 for _, n := range g.structList { 838 if err := g.writeInitializerImpl(b, n); err != nil { 839 return err 840 } 841 } 842 843 b.writes("// ---------------- Function Implementations\n\n") 844 if err := g.forEachFunc(b, bothPubPri, (*gen).writeFuncImpl); err != nil { 845 return err 846 } 847 848 b.printf("#endif // %s\n\n", module) 849 return nil 850} 851 852func (g *gen) forEachConst(b *buffer, v visibility, f func(*gen, *buffer, *a.Const) error) error { 853 for _, file := range g.files { 854 for _, tld := range file.TopLevelDecls() { 855 if tld.Kind() != a.KConst || 856 (v == pubOnly && tld.AsRaw().Flags()&a.FlagsPublic == 0) || 857 (v == priOnly && tld.AsRaw().Flags()&a.FlagsPublic != 0) { 858 continue 859 } 860 if err := f(g, b, tld.AsConst()); err != nil { 861 return err 862 } 863 } 864 } 865 return nil 866} 867 868func (g *gen) forEachFunc(b *buffer, v visibility, f func(*gen, *buffer, *a.Func) error) error { 869 for _, file := range g.files { 870 for _, tld := range file.TopLevelDecls() { 871 if tld.Kind() != a.KFunc || 872 (v == pubOnly && tld.AsRaw().Flags()&a.FlagsPublic == 0) || 873 (v == priOnly && tld.AsRaw().Flags()&a.FlagsPublic != 0) { 874 continue 875 } 876 if err := f(g, b, tld.AsFunc()); err != nil { 877 return err 878 } 879 } 880 } 881 return nil 882} 883 884func (g *gen) forEachStatus(b *buffer, v visibility, f func(*gen, *buffer, *a.Status) error) error { 885 for _, file := range g.files { 886 for _, tld := range file.TopLevelDecls() { 887 if tld.Kind() != a.KStatus || 888 (v == pubOnly && tld.AsRaw().Flags()&a.FlagsPublic == 0) || 889 (v == priOnly && tld.AsRaw().Flags()&a.FlagsPublic != 0) { 890 continue 891 } 892 if err := f(g, b, tld.AsStatus()); err != nil { 893 return err 894 } 895 } 896 } 897 return nil 898} 899 900func (g *gen) cName(name string) string { 901 return cName(name, g.pkgPrefix) 902} 903 904func cName(name string, pkgPrefix string) string { 905 s := []byte(pkgPrefix) 906 underscore := true 907 for _, r := range name { 908 if 'A' <= r && r <= 'Z' { 909 s = append(s, byte(r+'a'-'A')) 910 underscore = false 911 } else if ('a' <= r && r <= 'z') || ('0' <= r && r <= '9') { 912 s = append(s, byte(r)) 913 underscore = false 914 } else if !underscore { 915 s = append(s, '_') 916 underscore = true 917 } 918 } 919 if underscore { 920 s = s[:len(s)-1] 921 } 922 return string(s) 923} 924 925func uintBits(qid t.QID) uint32 { 926 if qid[0] == t.IDBase { 927 switch qid[1] { 928 case t.IDU8: 929 return 8 930 case t.IDU16: 931 return 16 932 case t.IDU32: 933 return 32 934 case t.IDU64: 935 return 64 936 } 937 } 938 return 0 939} 940 941func (g *gen) sizeof(typ *a.TypeExpr) (uint32, error) { 942 if typ.Decorator() == 0 { 943 if n := uintBits(typ.QID()); n != 0 { 944 return n / 8, nil 945 } 946 } 947 return 0, fmt.Errorf("unknown sizeof for %q", typ.Str(g.tm)) 948} 949 950func (g *gen) gatherStatuses(b *buffer, n *a.Status) error { 951 raw := n.QID()[1].Str(g.tm) 952 msg, ok := t.Unescape(raw) 953 if !ok || msg == "" { 954 return fmt.Errorf("bad status message %q", raw) 955 } 956 return g.addStatus(n.QID(), msg, n.Public()) 957} 958 959func (g *gen) addStatus(qid t.QID, msg string, public bool) error { 960 category := "note__" 961 if msg[0] == '$' { 962 category = "suspension__" 963 } else if msg[0] == '#' { 964 category = "error__" 965 } 966 z := status{ 967 cName: g.packagePrefix(qid) + category + cName(msg, ""), 968 msg: msg, 969 fromThisPkg: qid[0] == 0, 970 public: public, 971 } 972 g.statusList = append(g.statusList, z) 973 g.statusMap[qid] = z 974 return nil 975} 976 977func (g *gen) gatherScalarConsts(b *buffer, n *a.Const) error { 978 if cv := n.Value().ConstValue(); cv != nil { 979 g.scalarConstsMap[n.QID()] = n 980 } 981 return nil 982} 983 984func (g *gen) writeConst(b *buffer, n *a.Const) error { 985 if cv := n.Value().ConstValue(); cv != nil { 986 b.printf("#define %s%s %v\n\n", g.PKGPREFIX, n.QID()[1].Str(g.tm), cv) 987 } else { 988 b.writes("static const ") 989 if err := g.writeCTypeName(b, n.XType(), "\n"+g.PKGPREFIX, n.QID()[1].Str(g.tm)); err != nil { 990 return err 991 } 992 b.writes("WUFFS_BASE__POTENTIALLY_UNUSED = ") 993 if err := g.writeConstList(b, n.Value()); err != nil { 994 return err 995 } 996 b.writes(";\n\n") 997 } 998 return nil 999} 1000 1001func (g *gen) writeConstList(b *buffer, n *a.Expr) error { 1002 if n.Operator() == t.IDComma { 1003 b.writeb('{') 1004 for i, o := range n.Args() { 1005 if i&7 == 0 { 1006 b.writeb('\n') 1007 } 1008 if err := g.writeConstList(b, o.AsExpr()); err != nil { 1009 return err 1010 } 1011 b.writes(", ") 1012 } 1013 b.writes("\n}") 1014 } else if cv := n.ConstValue(); cv != nil { 1015 b.writes(cv.String()) 1016 } else { 1017 return fmt.Errorf("invalid const value %q", n.Str(g.tm)) 1018 } 1019 return nil 1020} 1021 1022func (g *gen) writeStructPrivateImpl(b *buffer, n *a.Struct) error { 1023 // TODO: allow max depth > 1 for recursive coroutines. 1024 const maxDepth = 1 1025 1026 b.writes("// Do not access the private_impl's or private_data's fields directly. There\n") 1027 b.writes("// is no API/ABI compatibility or safety guarantee if you do so. Instead, use\n") 1028 b.writes("// the wuffs_foo__bar__baz functions.\n") 1029 b.writes("//\n") 1030 b.writes("// It is a struct, not a struct*, so that the outermost wuffs_foo__bar struct\n") 1031 b.writes("// can be stack allocated when WUFFS_IMPLEMENTATION is defined.\n\n") 1032 1033 b.writes("struct {\n") 1034 if n.Classy() { 1035 b.writes("uint32_t magic;\n") 1036 b.writes("uint32_t active_coroutine;\n") 1037 for _, impl := range n.Implements() { 1038 qid := impl.AsTypeExpr().QID() 1039 b.printf("wuffs_base__vtable vtable_for__wuffs_%s__%s;\n", 1040 qid[0].Str(g.tm), qid[1].Str(g.tm)) 1041 } 1042 b.writes("wuffs_base__vtable null_vtable;\n") 1043 b.writes("\n") 1044 } 1045 1046 for _, o := range n.Fields() { 1047 o := o.AsField() 1048 if o.PrivateData() || o.XType().Eq(typeExprUtility) { 1049 continue 1050 } 1051 if err := g.writeCTypeName(b, o.XType(), fPrefix, o.Name().Str(g.tm)); err != nil { 1052 return err 1053 } 1054 b.writes(";\n") 1055 } 1056 1057 if n.Classy() { 1058 needEmptyLine := true 1059 for _, file := range g.files { 1060 for _, tld := range file.TopLevelDecls() { 1061 if tld.Kind() != a.KFunc { 1062 continue 1063 } 1064 o := tld.AsFunc() 1065 if o.Receiver() != n.QID() || !o.Effect().Coroutine() { 1066 continue 1067 } 1068 k := g.funks[o.QQID()] 1069 if k.coroSuspPoint == 0 { 1070 continue 1071 } 1072 1073 if needEmptyLine { 1074 needEmptyLine = false 1075 b.writeb('\n') 1076 } 1077 b.printf("uint32_t %s%s[%d];\n", pPrefix, o.FuncName().Str(g.tm), maxDepth) 1078 } 1079 } 1080 1081 } 1082 b.writes("} private_impl;\n\n") 1083 1084 { 1085 oldOuterLenB0 := len(*b) 1086 b.writes("struct {\n") 1087 oldOuterLenB1 := len(*b) 1088 1089 for _, o := range n.Fields() { 1090 o := o.AsField() 1091 if !o.PrivateData() || o.XType().Eq(typeExprUtility) { 1092 continue 1093 } 1094 if err := g.writeCTypeName(b, o.XType(), fPrefix, o.Name().Str(g.tm)); err != nil { 1095 return err 1096 } 1097 b.writes(";\n") 1098 } 1099 1100 needEmptyLine := oldOuterLenB1 != len(*b) 1101 for _, file := range g.files { 1102 for _, tld := range file.TopLevelDecls() { 1103 if tld.Kind() != a.KFunc { 1104 continue 1105 } 1106 o := tld.AsFunc() 1107 if o.Receiver() != n.QID() || !o.Effect().Coroutine() { 1108 continue 1109 } 1110 k := g.funks[o.QQID()] 1111 if k.coroSuspPoint == 0 && !k.usesScratch { 1112 continue 1113 } 1114 1115 oldInnerLenB0 := len(*b) 1116 oldNeedEmptyLine := needEmptyLine 1117 if needEmptyLine { 1118 needEmptyLine = false 1119 b.writeb('\n') 1120 } 1121 b.writes("struct {\n") 1122 oldInnerLenB1 := len(*b) 1123 if k.coroSuspPoint != 0 { 1124 if err := g.writeVars(b, &k, true); err != nil { 1125 return err 1126 } 1127 } 1128 if k.usesScratch { 1129 b.writes("uint64_t scratch;\n") 1130 } 1131 if oldInnerLenB1 != len(*b) { 1132 b.printf("} %s%s[%d];\n", sPrefix, o.FuncName().Str(g.tm), maxDepth) 1133 } else { 1134 *b = (*b)[:oldInnerLenB0] 1135 needEmptyLine = oldNeedEmptyLine 1136 } 1137 } 1138 } 1139 1140 if oldOuterLenB1 != len(*b) { 1141 b.writes("} private_data;\n\n") 1142 } else { 1143 *b = (*b)[:oldOuterLenB0] 1144 } 1145 } 1146 1147 return nil 1148} 1149 1150func (g *gen) writeStruct(b *buffer, n *a.Struct) error { 1151 structName := n.QID().Str(g.tm) 1152 fullStructName := g.pkgPrefix + structName + "__struct" 1153 b.printf("struct %s {\n", fullStructName) 1154 1155 if err := g.writeStructPrivateImpl(b, n); err != nil { 1156 return err 1157 } 1158 1159 if n.AsNode().AsRaw().Flags()&a.FlagsPublic != 0 { 1160 if err := g.writeCppMethods(b, n); err != nil { 1161 return err 1162 } 1163 } 1164 1165 b.printf("}; // struct %s\n\n", fullStructName) 1166 return nil 1167} 1168 1169func (g *gen) writeCppMethods(b *buffer, n *a.Struct) error { 1170 structName := n.QID().Str(g.tm) 1171 fullStructName := g.pkgPrefix + structName + "__struct" 1172 b.writes("#ifdef __cplusplus\n") 1173 1174 b.writes("#if (__cplusplus >= 201103L)\n") 1175 b.printf("using unique_ptr = std::unique_ptr<%s%s, decltype(&free)>;\n\n", g.pkgPrefix, structName) 1176 b.writes("// On failure, the alloc_etc functions return nullptr. They don't throw.\n\n") 1177 b.writes("static inline unique_ptr\n") 1178 b.writes("alloc() {\n") 1179 b.printf("return unique_ptr(%s%s__alloc(), &free);\n", g.pkgPrefix, structName) 1180 b.writes("}\n") 1181 for _, impl := range n.Implements() { 1182 iQID := impl.AsTypeExpr().QID() 1183 iName := fmt.Sprintf("wuffs_%s__%s", iQID[0].Str(g.tm), iQID[1].Str(g.tm)) 1184 b.printf("\nstatic inline %s::unique_ptr\n", iName) 1185 b.printf("alloc_as__%s() {\n", iName) 1186 b.printf("return %s::unique_ptr(\n%s%s__alloc_as__%s(), &free);\n", 1187 iName, g.pkgPrefix, structName, iName) 1188 b.printf("}\n") 1189 } 1190 b.writes("#endif // (__cplusplus >= 201103L)\n\n") 1191 1192 b.writes("#if (__cplusplus >= 201103L) && !defined(WUFFS_IMPLEMENTATION)\n") 1193 b.writes("// Disallow constructing or copying an object via standard C++ mechanisms,\n") 1194 b.writes("// e.g. the \"new\" operator, as this struct is intentionally opaque. Its total\n") 1195 b.writes("// size and field layout is not part of the public, stable, memory-safe API.\n") 1196 b.writes("// Use malloc or memcpy and the sizeof__wuffs_foo__bar function instead, and\n") 1197 b.writes("// call wuffs_foo__bar__baz methods (which all take a \"this\"-like pointer as\n") 1198 b.writes("// their first argument) rather than tweaking bar.private_impl.qux fields.\n") 1199 b.writes("//\n") 1200 b.writes("// In C, we can just leave wuffs_foo__bar as an incomplete type (unless\n") 1201 b.writes("// WUFFS_IMPLEMENTATION is #define'd). In C++, we define a complete type in\n") 1202 b.writes("// order to provide convenience methods. These forward on \"this\", so that you\n") 1203 b.writes("// can write \"bar->baz(etc)\" instead of \"wuffs_foo__bar__baz(bar, etc)\".\n") 1204 b.printf("%s() = delete;\n", fullStructName) 1205 b.printf("%s(const %s&) = delete;\n", fullStructName, fullStructName) 1206 b.printf("%s& operator=(\nconst %s&) = delete;\n", fullStructName, fullStructName) 1207 b.writes("\n") 1208 b.writes("// As above, the size of the struct is not part of the public API, and unless\n") 1209 b.writes("// WUFFS_IMPLEMENTATION is #define'd, this struct type T should be heap\n") 1210 b.writes("// allocated, not stack allocated. Its size is not intended to be known at\n") 1211 b.writes("// compile time, but it is unfortunately divulged as a side effect of\n") 1212 b.writes("// defining C++ convenience methods. Use \"sizeof__T()\", calling the function,\n") 1213 b.writes("// instead of \"sizeof T\", invoking the operator. To make the two values\n") 1214 b.writes("// different, so that passing the latter will be rejected by the initialize\n") 1215 b.writes("// function, we add an arbitrary amount of dead weight.\n") 1216 b.writes("uint8_t dead_weight[123000000]; // 123 MB.\n") 1217 b.writes("#endif // (__cplusplus >= 201103L) && !defined(WUFFS_IMPLEMENTATION)\n\n") 1218 1219 b.writes("inline wuffs_base__status WUFFS_BASE__WARN_UNUSED_RESULT\n" + 1220 "initialize(\nsize_t sizeof_star_self,\nuint64_t wuffs_version,\nuint32_t options) {\n") 1221 b.printf("return %s%s__initialize(\nthis, sizeof_star_self, wuffs_version, options);\n}\n\n", 1222 g.pkgPrefix, structName) 1223 1224 for _, impl := range n.Implements() { 1225 iQID := impl.AsTypeExpr().QID() 1226 iName := fmt.Sprintf("wuffs_%s__%s", iQID[0].Str(g.tm), iQID[1].Str(g.tm)) 1227 b.printf("inline %s*\n", iName) 1228 b.printf("upcast_as__%s() {\n", iName) 1229 b.printf("return (%s*)this;\n", iName) 1230 b.printf("}\n\n") 1231 } 1232 1233 structID := n.QID()[1] 1234 for _, file := range g.files { 1235 for _, tld := range file.TopLevelDecls() { 1236 if (tld.Kind() != a.KFunc) || (tld.AsRaw().Flags()&a.FlagsPublic == 0) { 1237 continue 1238 } 1239 f := tld.AsFunc() 1240 qqid := f.QQID() 1241 if qqid[1] != structID { 1242 continue 1243 } 1244 1245 if err := g.writeFuncSignature(b, f, wfsCppDecl); err != nil { 1246 return err 1247 } 1248 b.writes(" {\n return ") 1249 b.writes(g.funcCName(f)) 1250 b.writes("(this") 1251 for _, o := range f.In().Fields() { 1252 b.writes(", ") 1253 b.writes(aPrefix) 1254 b.writes(o.AsField().Name().Str(g.tm)) 1255 } 1256 b.writes(");\n }\n\n") 1257 } 1258 } 1259 1260 b.writes("#endif // __cplusplus\n") 1261 return nil 1262} 1263 1264func (g *gen) writeVTableImpl(b *buffer, n *a.Struct) error { 1265 impls := n.Implements() 1266 if len(impls) == 0 { 1267 return nil 1268 } 1269 1270 if err := parseBuiltInInterfaceMethods(); err != nil { 1271 return err 1272 } 1273 1274 nQID := n.QID() 1275 for _, impl := range impls { 1276 iQID := impl.AsTypeExpr().QID() 1277 b.printf("const wuffs_%s__%s__func_ptrs\n%s%s__func_ptrs_for__wuffs_%s__%s = {\n", 1278 iQID[0].Str(g.tm), iQID[1].Str(g.tm), 1279 g.pkgPrefix, nQID[1].Str(g.tm), 1280 iQID[0].Str(g.tm), iQID[1].Str(g.tm), 1281 ) 1282 1283 // Note the two t.Map values: g.tm and builtInTokenMap. 1284 altQID := t.QID{ 1285 builtInTokenMap.ByName(iQID[0].Str(g.tm)), 1286 builtInTokenMap.ByName(iQID[1].Str(g.tm)), 1287 } 1288 for _, f := range builtInInterfaceMethods[altQID] { 1289 b.writeb('(') 1290 if err := g.writeFuncSignature(b, f, wfsCFuncPtrType); err != nil { 1291 return err 1292 } 1293 b.printf(")(&%s%s__%s),\n", 1294 g.pkgPrefix, nQID[1].Str(g.tm), 1295 f.FuncName().Str(&builtInTokenMap), 1296 ) 1297 } 1298 b.writes("};\n\n") 1299 } 1300 return nil 1301} 1302 1303func (g *gen) writeInitializerSignature(b *buffer, n *a.Struct, public bool) error { 1304 structName := n.QID().Str(g.tm) 1305 b.printf("wuffs_base__status WUFFS_BASE__WARN_UNUSED_RESULT\n"+ 1306 "%s%s__initialize(\n"+ 1307 " %s%s* self,\n"+ 1308 " size_t sizeof_star_self,\n"+ 1309 " uint64_t wuffs_version,\n"+ 1310 " uint32_t options)", 1311 g.pkgPrefix, structName, g.pkgPrefix, structName) 1312 return nil 1313} 1314 1315func (g *gen) writeAllocSignature(b *buffer, n *a.Struct) error { 1316 structName := n.QID().Str(g.tm) 1317 b.printf("%s%s*\n%s%s__alloc()", g.pkgPrefix, structName, g.pkgPrefix, structName) 1318 return nil 1319} 1320 1321func (g *gen) writeSizeofSignature(b *buffer, n *a.Struct) error { 1322 structName := n.QID().Str(g.tm) 1323 b.printf("size_t\nsizeof__%s%s()", g.pkgPrefix, structName) 1324 return nil 1325} 1326 1327func (g *gen) writeInitializerPrototype(b *buffer, n *a.Struct) error { 1328 if !n.Classy() { 1329 return nil 1330 } 1331 if err := g.writeInitializerSignature(b, n, n.Public()); err != nil { 1332 return err 1333 } 1334 b.writes(";\n\n") 1335 1336 if n.Public() { 1337 if err := g.writeSizeofSignature(b, n); err != nil { 1338 return err 1339 } 1340 b.writes(";\n\n") 1341 } 1342 return nil 1343} 1344 1345func (g *gen) writeInitializerImpl(b *buffer, n *a.Struct) error { 1346 if !n.Classy() { 1347 return nil 1348 } 1349 if err := g.writeInitializerSignature(b, n, false); err != nil { 1350 return err 1351 } 1352 b.writes("{\n") 1353 b.writes("if (!self) {\n") 1354 b.writes(" return wuffs_base__make_status(wuffs_base__error__bad_receiver);\n") 1355 b.writes("}\n") 1356 1357 b.writes("if (sizeof(*self) != sizeof_star_self) {\n") 1358 b.writes(" return wuffs_base__make_status(wuffs_base__error__bad_sizeof_receiver);\n") 1359 b.writes("}\n") 1360 b.writes("if (((wuffs_version >> 32) != WUFFS_VERSION_MAJOR) ||\n" + 1361 "(((wuffs_version >> 16) & 0xFFFF) > WUFFS_VERSION_MINOR)) {\n") 1362 b.writes(" return wuffs_base__make_status(wuffs_base__error__bad_wuffs_version);\n") 1363 b.writes("}\n\n") 1364 1365 b.writes("if ((options & WUFFS_INITIALIZE__ALREADY_ZEROED) != 0) {\n") 1366 b.writes(" // The whole point of this if-check is to detect an uninitialized *self.\n") 1367 b.writes(" // We disable the warning on GCC. Clang-5.0 does not have this warning.\n") 1368 b.writes(" #if !defined(__clang__) && defined(__GNUC__)\n") 1369 b.writes(" #pragma GCC diagnostic push\n") 1370 b.writes(" #pragma GCC diagnostic ignored \"-Wmaybe-uninitialized\"\n") 1371 b.writes(" #endif\n") 1372 b.writes(" if (self->private_impl.magic != 0) {\n") 1373 b.writes(" return wuffs_base__make_status(wuffs_base__error__initialize_falsely_claimed_already_zeroed);\n") 1374 b.writes(" }\n") 1375 b.writes(" #if !defined(__clang__) && defined(__GNUC__)\n") 1376 b.writes(" #pragma GCC diagnostic pop\n") 1377 b.writes(" #endif\n") 1378 b.writes("} else {\n") 1379 b.writes(" if ((options & WUFFS_INITIALIZE__LEAVE_INTERNAL_BUFFERS_UNINITIALIZED) == 0) {\n") 1380 b.writes(" memset(self, 0, sizeof(*self));\n") 1381 b.writes(" options |= WUFFS_INITIALIZE__ALREADY_ZEROED;\n") 1382 b.writes(" } else {\n") 1383 b.writes(" memset(&(self->private_impl), 0, sizeof(self->private_impl));\n") 1384 b.writes(" }\n") 1385 b.writes("}\n\n") 1386 1387 // Call any ctors on sub-structs. 1388 for _, f := range n.Fields() { 1389 f := f.AsField() 1390 x := f.XType() 1391 if x != x.Innermost() { 1392 // TODO: arrays of sub-structs. 1393 continue 1394 } 1395 1396 prefix := g.pkgPrefix 1397 qid := x.QID() 1398 if qid[0] == t.IDBase { 1399 // Base types don't need further initialization. 1400 continue 1401 } else if qid[0] != 0 { 1402 // See gen.packagePrefix for a related TODO with otherPkg. 1403 otherPkg := g.tm.ByID(qid[0]) 1404 prefix = "wuffs_" + otherPkg + "__" 1405 } else if g.structMap[qid] == nil { 1406 continue 1407 } 1408 1409 b.printf("{\n") 1410 b.printf("wuffs_base__status z = %s%s__initialize(\n"+ 1411 "&self->private_data.%s%s, sizeof(self->private_data.%s%s), WUFFS_VERSION, options);\n", 1412 prefix, qid[1].Str(g.tm), fPrefix, f.Name().Str(g.tm), fPrefix, f.Name().Str(g.tm)) 1413 b.printf("if (z.repr) {\nreturn z;\n}\n") 1414 b.printf("}\n") 1415 } 1416 1417 b.writes("self->private_impl.magic = WUFFS_BASE__MAGIC;\n") 1418 for _, impl := range n.Implements() { 1419 qid := impl.AsTypeExpr().QID() 1420 iName := fmt.Sprintf("wuffs_%s__%s", qid[0].Str(g.tm), qid[1].Str(g.tm)) 1421 b.printf("self->private_impl.vtable_for__%s.vtable_name =\n"+ 1422 "%s__vtable_name;\n", iName, iName) 1423 b.printf("self->private_impl.vtable_for__%s.function_pointers =\n"+ 1424 "(const void*)(&%s%s__func_ptrs_for__%s);\n", 1425 iName, g.pkgPrefix, n.QID().Str(g.tm), iName) 1426 } 1427 b.writes("return wuffs_base__make_status(NULL);\n") 1428 b.writes("}\n\n") 1429 1430 if n.Public() { 1431 structName := n.QID().Str(g.tm) 1432 if err := g.writeAllocSignature(b, n); err != nil { 1433 return err 1434 } 1435 b.writes(" {\n") 1436 b.printf("%s%s* x =\n(%s%s*)(calloc(sizeof(%s%s), 1));\n", 1437 g.pkgPrefix, structName, g.pkgPrefix, structName, g.pkgPrefix, structName) 1438 b.writes("if (!x) {\nreturn NULL;\n}\n") 1439 b.printf("if (%s%s__initialize(\nx, sizeof(%s%s), "+ 1440 "WUFFS_VERSION, WUFFS_INITIALIZE__ALREADY_ZEROED).repr) {\n", 1441 g.pkgPrefix, structName, g.pkgPrefix, structName) 1442 b.writes("free(x);\nreturn NULL;\n}\n") 1443 b.writes("return x;\n") 1444 b.writes("}\n\n") 1445 1446 if err := g.writeSizeofSignature(b, n); err != nil { 1447 return err 1448 } 1449 b.printf(" {\nreturn sizeof(%s%s);\n}\n\n", g.pkgPrefix, structName) 1450 } 1451 return nil 1452} 1453