capnp.c++ (72002B)
1 // Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors 2 // Licensed under the MIT License: 3 // 4 // Permission is hereby granted, free of charge, to any person obtaining a copy 5 // of this software and associated documentation files (the "Software"), to deal 6 // in the Software without restriction, including without limitation the rights 7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 // copies of the Software, and to permit persons to whom the Software is 9 // furnished to do so, subject to the following conditions: 10 // 11 // The above copyright notice and this permission notice shall be included in 12 // all copies or substantial portions of the Software. 13 // 14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 // THE SOFTWARE. 21 22 #ifndef _GNU_SOURCE 23 #define _GNU_SOURCE 24 #endif 25 26 #if _WIN32 27 #include <kj/win32-api-version.h> 28 #endif 29 30 #include "lexer.h" 31 #include "parser.h" 32 #include "compiler.h" 33 #include "module-loader.h" 34 #include "node-translator.h" 35 #include <capnp/pretty-print.h> 36 #include <capnp/schema.capnp.h> 37 #include <kj/vector.h> 38 #include <kj/io.h> 39 #include <kj/miniposix.h> 40 #include <kj/debug.h> 41 #include "../message.h" 42 #include <iostream> 43 #include <kj/main.h> 44 #include <kj/parse/char.h> 45 #include <sys/stat.h> 46 #include <sys/types.h> 47 #include <capnp/serialize.h> 48 #include <capnp/serialize-packed.h> 49 #include <capnp/serialize-text.h> 50 #include <capnp/compat/json.h> 51 #include <errno.h> 52 #include <stdlib.h> 53 #include <kj/map.h> 54 55 #if _WIN32 56 #include <process.h> 57 #include <windows.h> 58 #include <kj/windows-sanity.h> 59 #undef CONST 60 #else 61 #include <sys/wait.h> 62 #endif 63 64 #if HAVE_CONFIG_H 65 #include "config.h" 66 #endif 67 68 #ifndef VERSION 69 #define VERSION "(unknown)" 70 #endif 71 72 namespace capnp { 73 namespace compiler { 74 75 static const char VERSION_STRING[] = "Cap'n Proto version " VERSION; 76 77 class CompilerMain final: public GlobalErrorReporter { 78 public: 79 explicit CompilerMain(kj::ProcessContext& context) 80 : context(context), disk(kj::newDiskFilesystem()), loader(*this) {} 81 82 kj::MainFunc getMain() { 83 if (context.getProgramName().endsWith("capnpc") || context.getProgramName().endsWith("capnpc.exe")) { 84 kj::MainBuilder builder(context, VERSION_STRING, 85 "Compiles Cap'n Proto schema files and generates corresponding source code in one or " 86 "more languages."); 87 addGlobalOptions(builder); 88 addCompileOptions(builder); 89 builder.addOption({'i', "generate-id"}, KJ_BIND_METHOD(*this, generateId), 90 "Generate a new 64-bit unique ID for use in a Cap'n Proto schema."); 91 return builder.build(); 92 } else { 93 kj::MainBuilder builder(context, VERSION_STRING, 94 "Command-line tool for Cap'n Proto development and debugging."); 95 builder.addSubCommand("compile", KJ_BIND_METHOD(*this, getCompileMain), 96 "Generate source code from schema files.") 97 .addSubCommand("id", KJ_BIND_METHOD(*this, getGenIdMain), 98 "Generate a new unique ID.") 99 .addSubCommand("convert", KJ_BIND_METHOD(*this, getConvertMain), 100 "Convert messages between binary, text, JSON, etc.") 101 .addSubCommand("decode", KJ_BIND_METHOD(*this, getDecodeMain), 102 "DEPRECATED (use `convert`)") 103 .addSubCommand("encode", KJ_BIND_METHOD(*this, getEncodeMain), 104 "DEPRECATED (use `convert`)") 105 .addSubCommand("eval", KJ_BIND_METHOD(*this, getEvalMain), 106 "Evaluate a const from a schema file."); 107 addGlobalOptions(builder); 108 return builder.build(); 109 } 110 } 111 112 kj::MainFunc getCompileMain() { 113 kj::MainBuilder builder(context, VERSION_STRING, 114 "Compiles Cap'n Proto schema files and generates corresponding source code in one or " 115 "more languages."); 116 addGlobalOptions(builder); 117 addCompileOptions(builder); 118 return builder.build(); 119 } 120 121 kj::MainFunc getGenIdMain() { 122 return kj::MainBuilder(context, VERSION_STRING, 123 "Generates a new 64-bit unique ID for use in a Cap'n Proto schema.") 124 .callAfterParsing(KJ_BIND_METHOD(*this, generateId)) 125 .build(); 126 } 127 128 kj::MainFunc getConvertMain() { 129 // Only parse the schemas we actually need for decoding. 130 compileEagerness = Compiler::NODE; 131 132 // Drop annotations since we don't need them. This avoids importing files like c++.capnp. 133 annotationFlag = Compiler::DROP_ANNOTATIONS; 134 135 kj::MainBuilder builder(context, VERSION_STRING, 136 "Converts messages between formats. Reads a stream of messages from stdin in format " 137 "<from> and writes them to stdout in format <to>. Valid formats are:\n" 138 " binary standard binary format\n" 139 " packed packed binary format (deflates zeroes)\n" 140 " flat binary single segment, no segment table (rare)\n" 141 " flat-packed flat and packed\n" 142 " canonical canonicalized binary single segment, no segment table\n" 143 " text schema language struct literal format\n" 144 " json JSON format\n" 145 "When using \"text\" or \"json\" format, you must specify <schema-file> and <type> " 146 "(but they are ignored and can be omitted for binary-to-binary conversions). " 147 "<type> names names a struct type defined in <schema-file>, which is the root type " 148 "of the message(s)."); 149 addGlobalOptions(builder); 150 builder.addOption({"short"}, KJ_BIND_METHOD(*this, printShort), 151 "Write text or JSON output in short (non-pretty) format. Each message will " 152 "be printed on one line, without using whitespace to improve readability.") 153 .addOptionWithArg({"segment-size"}, KJ_BIND_METHOD(*this, setSegmentSize), "<n>", 154 "For binary output, sets the preferred segment size on the MallocMessageBuilder to <n> " 155 "words and turns off heuristic growth. This flag is mainly useful " 156 "for testing. Without it, each message will be written as a single " 157 "segment.") 158 .addOption({"quiet"}, KJ_BIND_METHOD(*this, setQuiet), 159 "Do not print warning messages about the input being in the wrong format. " 160 "Use this if you find the warnings are wrong (but also let us know so " 161 "we can improve them).") 162 .expectArg("<from>:<to>", KJ_BIND_METHOD(*this, setConversion)) 163 .expectOptionalArg("<schema-file>", KJ_BIND_METHOD(*this, addSource)) 164 .expectOptionalArg("<type>", KJ_BIND_METHOD(*this, setRootType)) 165 .callAfterParsing(KJ_BIND_METHOD(*this, convert)); 166 return builder.build(); 167 } 168 169 kj::MainFunc getDecodeMain() { 170 // Only parse the schemas we actually need for decoding. 171 compileEagerness = Compiler::NODE; 172 173 // Drop annotations since we don't need them. This avoids importing files like c++.capnp. 174 annotationFlag = Compiler::DROP_ANNOTATIONS; 175 176 kj::MainBuilder builder(context, VERSION_STRING, 177 "Decodes one or more encoded Cap'n Proto messages as text. The messages have root " 178 "type <type> defined in <schema-file>. Messages are read from standard input and " 179 "by default are expected to be in standard Cap'n Proto serialization format."); 180 addGlobalOptions(builder); 181 builder.addOption({"flat"}, KJ_BIND_METHOD(*this, codeFlat), 182 "Interpret the input as one large single-segment message rather than a " 183 "stream in standard serialization format. (Rarely used.)") 184 .addOption({'p', "packed"}, KJ_BIND_METHOD(*this, codePacked), 185 "Expect the input to be packed using standard Cap'n Proto packing, which " 186 "deflates zero-valued bytes. (This reads messages written with " 187 "capnp::writePackedMessage*() from <capnp/serialize-packed.h>. Do not use " 188 "this for messages written with capnp::writeMessage*() from " 189 "<capnp/serialize.h>.)") 190 .addOption({"short"}, KJ_BIND_METHOD(*this, printShort), 191 "Print in short (non-pretty) format. Each message will be printed on one " 192 "line, without using whitespace to improve readability.") 193 .addOption({"quiet"}, KJ_BIND_METHOD(*this, setQuiet), 194 "Do not print warning messages about the input being in the wrong format. " 195 "Use this if you find the warnings are wrong (but also let us know so " 196 "we can improve them).") 197 .expectArg("<schema-file>", KJ_BIND_METHOD(*this, addSource)) 198 .expectArg("<type>", KJ_BIND_METHOD(*this, setRootType)) 199 .callAfterParsing(KJ_BIND_METHOD(*this, decode)); 200 return builder.build(); 201 } 202 203 kj::MainFunc getEncodeMain() { 204 // Only parse the schemas we actually need for decoding. 205 compileEagerness = Compiler::NODE; 206 207 // Drop annotations since we don't need them. This avoids importing files like c++.capnp. 208 annotationFlag = Compiler::DROP_ANNOTATIONS; 209 210 kj::MainBuilder builder(context, VERSION_STRING, 211 "Encodes one or more textual Cap'n Proto messages to binary. The messages have root " 212 "type <type> defined in <schema-file>. Messages are read from standard input. Each " 213 "message is a parenthesized struct literal, like the format used to specify constants " 214 "and default values of struct type in the schema language. For example:\n" 215 " (foo = 123, bar = \"hello\", baz = [true, false, true])\n" 216 "The input may contain any number of such values; each will be encoded as a separate " 217 "message.", 218 219 "Note that the current implementation reads the entire input into memory before " 220 "beginning to encode. A better implementation would read and encode one message at " 221 "a time."); 222 addGlobalOptions(builder); 223 builder.addOption({"flat"}, KJ_BIND_METHOD(*this, codeFlat), 224 "Expect only one input value, serializing it as a single-segment message " 225 "with no framing.") 226 .addOption({'p', "packed"}, KJ_BIND_METHOD(*this, codePacked), 227 "Pack the output message with standard Cap'n Proto packing, which " 228 "deflates zero-valued bytes. (This writes messages using " 229 "capnp::writePackedMessage() from <capnp/serialize-packed.h>. Without " 230 "this, capnp::writeMessage() from <capnp/serialize.h> is used.)") 231 .addOptionWithArg({"segment-size"}, KJ_BIND_METHOD(*this, setSegmentSize), "<n>", 232 "Sets the preferred segment size on the MallocMessageBuilder to <n> " 233 "words and turns off heuristic growth. This flag is mainly useful " 234 "for testing. Without it, each message will be written as a single " 235 "segment.") 236 .expectArg("<schema-file>", KJ_BIND_METHOD(*this, addSource)) 237 .expectArg("<type>", KJ_BIND_METHOD(*this, setRootType)) 238 .callAfterParsing(KJ_BIND_METHOD(*this, encode)); 239 return builder.build(); 240 } 241 242 kj::MainFunc getEvalMain() { 243 // Only parse the schemas we actually need for decoding. 244 compileEagerness = Compiler::NODE; 245 246 // Drop annotations since we don't need them. This avoids importing files like c++.capnp. 247 annotationFlag = Compiler::DROP_ANNOTATIONS; 248 249 // Default convert to text unless -o is given. 250 convertTo = Format::TEXT; 251 252 kj::MainBuilder builder(context, VERSION_STRING, 253 "Prints (or encodes) the value of <name>, which must be defined in <schema-file>. " 254 "<name> must refer to a const declaration, a field of a struct type (prints the default " 255 "value), or a field or list element nested within some other value. Examples:\n" 256 " capnp eval myschema.capnp MyType.someField\n" 257 " capnp eval myschema.capnp someConstant\n" 258 " capnp eval myschema.capnp someConstant.someField\n" 259 " capnp eval myschema.capnp someConstant.someList[4]\n" 260 " capnp eval myschema.capnp someConstant.someList[4].anotherField[1][2][3]\n" 261 "Since consts can have complex struct types, and since you can define a const using " 262 "import and variable substitution, this can be a convenient way to write text-format " 263 "config files which are compiled to binary before deployment.", 264 265 "By default the value is written in text format and can have any type. The -b, -p, " 266 "and --flat flags specify binary output, in which case the const must be of struct " 267 "type."); 268 addGlobalOptions(builder); 269 builder.addOptionWithArg({'o', "output"}, KJ_BIND_METHOD(*this, setEvalOutputFormat), 270 "<format>", "Encode the output in the given format. See `capnp help convert` " 271 "for a list of formats. Defaults to \"text\".") 272 .addOption({'b', "binary"}, KJ_BIND_METHOD(*this, codeBinary), 273 "same as -obinary") 274 .addOption({"flat"}, KJ_BIND_METHOD(*this, codeFlat), 275 "same as -oflat") 276 .addOption({'p', "packed"}, KJ_BIND_METHOD(*this, codePacked), 277 "same as -opacked") 278 .addOption({"short"}, KJ_BIND_METHOD(*this, printShort), 279 "If output format is text or JSON, write in short (non-pretty) format. The " 280 "message will be printed on one line, without using whitespace to improve " 281 "readability.") 282 .expectArg("<schema-file>", KJ_BIND_METHOD(*this, addSource)) 283 .expectArg("<name>", KJ_BIND_METHOD(*this, evalConst)); 284 return builder.build(); 285 } 286 287 void addGlobalOptions(kj::MainBuilder& builder) { 288 builder.addOptionWithArg({'I', "import-path"}, KJ_BIND_METHOD(*this, addImportPath), "<dir>", 289 "Add <dir> to the list of directories searched for non-relative " 290 "imports (ones that start with a '/').") 291 .addOption({"no-standard-import"}, KJ_BIND_METHOD(*this, noStandardImport), 292 "Do not add any default import paths; use only those specified by -I. " 293 "Otherwise, typically /usr/include and /usr/local/include are added by " 294 "default."); 295 } 296 297 void addCompileOptions(kj::MainBuilder& builder) { 298 builder.addOptionWithArg({'o', "output"}, KJ_BIND_METHOD(*this, addOutput), "<lang>[:<dir>]", 299 "Generate source code for language <lang> in directory <dir> " 300 "(default: current directory). <lang> actually specifies a plugin " 301 "to use. If <lang> is a simple word, the compiler searches for a plugin " 302 "called 'capnpc-<lang>' in $PATH. If <lang> is a file path " 303 "containing slashes, it is interpreted as the exact plugin " 304 "executable file name, and $PATH is not searched. If <lang> is '-', " 305 "the compiler dumps the request to standard output.") 306 .addOptionWithArg({"src-prefix"}, KJ_BIND_METHOD(*this, addSourcePrefix), "<prefix>", 307 "If a file specified for compilation starts with <prefix>, remove " 308 "the prefix for the purpose of deciding the names of output files. " 309 "For example, the following command:\n" 310 " capnp compile --src-prefix=foo/bar -oc++:corge foo/bar/baz/qux.capnp\n" 311 "would generate the files corge/baz/qux.capnp.{h,c++}.") 312 .expectOneOrMoreArgs("<source>", KJ_BIND_METHOD(*this, addSource)) 313 .callAfterParsing(KJ_BIND_METHOD(*this, generateOutput)); 314 } 315 316 // ===================================================================================== 317 // shared options 318 319 kj::MainBuilder::Validity addImportPath(kj::StringPtr path) { 320 KJ_IF_MAYBE(dir, getSourceDirectory(path, false)) { 321 loader.addImportPath(*dir); 322 return true; 323 } else { 324 return "no such directory"; 325 } 326 } 327 328 kj::MainBuilder::Validity noStandardImport() { 329 addStandardImportPaths = false; 330 return true; 331 } 332 333 kj::MainBuilder::Validity addSource(kj::StringPtr file) { 334 if (!compilerConstructed) { 335 compiler = compilerSpace.construct(annotationFlag); 336 compilerConstructed = true; 337 } 338 339 if (addStandardImportPaths) { 340 static constexpr kj::StringPtr STANDARD_IMPORT_PATHS[] = { 341 "/usr/local/include"_kj, 342 "/usr/include"_kj, 343 #ifdef CAPNP_INCLUDE_DIR 344 KJ_CONCAT(CAPNP_INCLUDE_DIR, _kj), 345 #endif 346 }; 347 for (auto path: STANDARD_IMPORT_PATHS) { 348 KJ_IF_MAYBE(dir, getSourceDirectory(path, false)) { 349 loader.addImportPath(*dir); 350 } else { 351 // ignore standard path that doesn't exist 352 } 353 } 354 355 addStandardImportPaths = false; 356 } 357 358 auto dirPathPair = interpretSourceFile(file); 359 KJ_IF_MAYBE(module, loader.loadModule(dirPathPair.dir, dirPathPair.path)) { 360 auto compiled = compiler->add(*module); 361 compiler->eagerlyCompile(compiled.getId(), compileEagerness); 362 sourceFiles.add(SourceFile { compiled.getId(), compiled, module->getSourceName(), &*module }); 363 } else { 364 return "no such file"; 365 } 366 367 return true; 368 } 369 370 public: 371 // ===================================================================================== 372 // "id" command 373 374 kj::MainBuilder::Validity generateId() { 375 context.exitInfo(kj::str("@0x", kj::hex(generateRandomId()))); 376 KJ_CLANG_KNOWS_THIS_IS_UNREACHABLE_BUT_GCC_DOESNT; 377 } 378 379 // ===================================================================================== 380 // "compile" command 381 382 kj::MainBuilder::Validity addOutput(kj::StringPtr spec) { 383 KJ_IF_MAYBE(split, spec.findFirst(':')) { 384 kj::StringPtr dir = spec.slice(*split + 1); 385 auto plugin = spec.slice(0, *split); 386 387 if (*split == 1 && (dir.startsWith("/") || dir.startsWith("\\"))) { 388 // The colon is the second character and is immediately followed by a slash or backslash. 389 // So, the user passed something like `-o c:/foo`. Is this a request to run the C plugin 390 // and output to `/foo`? Or are we on Windows, and is this a request to run the plugin 391 // `c:/foo`? 392 KJ_IF_MAYBE(split2, dir.findFirst(':')) { 393 // There are two colons. The first ':' was the second char, and was followed by '/' or 394 // '\', e.g.: 395 // capnp compile -o c:/foo.exe:bar 396 // 397 // In this case we can conclude that the second colon is actually meant to be the 398 // plugin/location separator, and the first colon was simply signifying a drive letter. 399 // 400 // Proof by contradiction: 401 // - Say that none of the colons were meant to be plugin/location separators; i.e. the 402 // whole argument is meant to be a plugin indicator and the location defaults to ".". 403 // -> In this case, the plugin path has two colons, which is not valid. 404 // -> CONTRADICTION 405 // - Say that the first colon was meant to be the plugin/location separator. 406 // -> In this case, the second colon must be the drive letter separator for the 407 // output location. 408 // -> However, the output location begins with '/' or '\', which is not a drive letter. 409 // -> CONTRADICTION 410 // - Say that there are more colons beyond the first two, and one of these is meant to 411 // be the plugin/location separator. 412 // -> In this case, the plugin path has two or more colons, which is not valid. 413 // -> CONTRADICTION 414 // 415 // We therefore conclude that the *second* colon is in fact the plugin/location separator. 416 417 dir = dir.slice(*split2 + 1); 418 plugin = spec.slice(0, *split2 + 2); 419 #if _WIN32 420 } else { 421 // The user wrote something like: 422 // 423 // capnp compile -o c:/foo/bar 424 // 425 // What does this mean? It depends on what system we're on. On a Unix system, the above 426 // clearly is a request to run the `capnpc-c` plugin (perhaps to output C code) and write 427 // to the directory /foo/bar. But on Windows, absolute paths do not start with '/', and 428 // the above is actually a request to run the plugin `c:/foo/bar`, outputting to the 429 // current directory. 430 431 outputs.add(OutputDirective { spec.asArray(), nullptr }); 432 return true; 433 #endif 434 } 435 } 436 437 struct stat stats; 438 if (stat(dir.cStr(), &stats) < 0 || !S_ISDIR(stats.st_mode)) { 439 return "output location is inaccessible or is not a directory"; 440 } 441 outputs.add(OutputDirective { plugin, disk->getCurrentPath().evalNative(dir) }); 442 } else { 443 outputs.add(OutputDirective { spec.asArray(), nullptr }); 444 } 445 446 return true; 447 } 448 449 kj::MainBuilder::Validity addSourcePrefix(kj::StringPtr prefix) { 450 if (getSourceDirectory(prefix, true) == nullptr) { 451 return "no such directory"; 452 } else { 453 return true; 454 } 455 } 456 457 kj::MainBuilder::Validity generateOutput() { 458 if (hadErrors()) { 459 // Skip output if we had any errors. 460 return true; 461 } 462 463 // We require one or more sources and if they failed to compile we quit above, so this should 464 // pass. (This assertion also guarantees that `compiler` has been initialized.) 465 KJ_ASSERT(sourceFiles.size() > 0, "Shouldn't have gotten here without sources."); 466 467 if (outputs.size() == 0) { 468 return "no outputs specified"; 469 } 470 471 MallocMessageBuilder message; 472 auto request = message.initRoot<schema::CodeGeneratorRequest>(); 473 474 auto version = request.getCapnpVersion(); 475 version.setMajor(CAPNP_VERSION_MAJOR); 476 version.setMinor(CAPNP_VERSION_MINOR); 477 version.setMicro(CAPNP_VERSION_MICRO); 478 479 auto schemas = compiler->getLoader().getAllLoaded(); 480 auto nodes = request.initNodes(schemas.size()); 481 for (size_t i = 0; i < schemas.size(); i++) { 482 nodes.setWithCaveats(i, schemas[i].getProto()); 483 } 484 485 request.adoptSourceInfo(compiler->getAllSourceInfo(message.getOrphanage())); 486 487 auto requestedFiles = request.initRequestedFiles(sourceFiles.size()); 488 for (size_t i = 0; i < sourceFiles.size(); i++) { 489 auto requestedFile = requestedFiles[i]; 490 requestedFile.setId(sourceFiles[i].id); 491 requestedFile.setFilename(sourceFiles[i].name); 492 requestedFile.adoptImports(compiler->getFileImportTable( 493 *sourceFiles[i].module, Orphanage::getForMessageContaining(requestedFile))); 494 } 495 496 for (auto& output: outputs) { 497 if (kj::str(output.name) == "-") { 498 writeMessageToFd(STDOUT_FILENO, message); 499 continue; 500 } 501 502 int pipeFds[2]; 503 KJ_SYSCALL(kj::miniposix::pipe(pipeFds)); 504 505 kj::String exeName; 506 bool shouldSearchPath = true; 507 for (char c: output.name) { 508 #if _WIN32 509 if (c == '/' || c == '\\') { 510 #else 511 if (c == '/') { 512 #endif 513 shouldSearchPath = false; 514 break; 515 } 516 } 517 if (shouldSearchPath) { 518 exeName = kj::str("capnpc-", output.name); 519 } else { 520 exeName = kj::heapString(output.name); 521 } 522 523 kj::Array<char> pwd = kj::heapArray<char>(256); 524 while (getcwd(pwd.begin(), pwd.size()) == nullptr) { 525 KJ_REQUIRE(pwd.size() < 8192, "WTF your working directory path is more than 8k?"); 526 pwd = kj::heapArray<char>(pwd.size() * 2); 527 } 528 529 #if _WIN32 530 int oldStdin; 531 KJ_SYSCALL(oldStdin = dup(STDIN_FILENO)); 532 intptr_t child; 533 534 #else // _WIN32 535 pid_t child; 536 KJ_SYSCALL(child = fork()); 537 if (child == 0) { 538 // I am the child! 539 540 KJ_SYSCALL(close(pipeFds[1])); 541 #endif // _WIN32, else 542 543 KJ_SYSCALL(dup2(pipeFds[0], STDIN_FILENO)); 544 KJ_SYSCALL(close(pipeFds[0])); 545 546 KJ_IF_MAYBE(d, output.dir) { 547 #if _WIN32 548 KJ_SYSCALL(SetCurrentDirectoryW(d->forWin32Api(true).begin()), d->toWin32String(true)); 549 #else 550 auto wd = d->toString(true); 551 KJ_SYSCALL(chdir(wd.cStr()), wd); 552 KJ_SYSCALL(setenv("PWD", wd.cStr(), true)); 553 #endif 554 } 555 556 #if _WIN32 557 // MSVCRT's spawn*() don't correctly escape arguments, which is necessary on Windows 558 // since the underlying system call takes a single command line string rather than 559 // an arg list. We do the escaping ourselves by wrapping the name in quotes. We know 560 // that exeName itself can't contain quotes (since filenames aren't allowed to contain 561 // quotes on Windows), so we don't have to account for those. 562 KJ_ASSERT(exeName.findFirst('\"') == nullptr, 563 "Windows filenames can't contain quotes", exeName); 564 auto escapedExeName = kj::str("\"", exeName, "\""); 565 #endif 566 567 if (shouldSearchPath) { 568 #if _WIN32 569 child = _spawnlp(_P_NOWAIT, exeName.cStr(), escapedExeName.cStr(), nullptr); 570 #else 571 execlp(exeName.cStr(), exeName.cStr(), nullptr); 572 #endif 573 } else { 574 #if _WIN32 575 if (!exeName.startsWith("/") && !exeName.startsWith("\\") && 576 !(exeName.size() >= 2 && exeName[1] == ':')) { 577 #else 578 if (!exeName.startsWith("/")) { 579 #endif 580 // The name is relative. Prefix it with our original working directory path. 581 exeName = kj::str(pwd.begin(), "/", exeName); 582 } 583 584 #if _WIN32 585 child = _spawnl(_P_NOWAIT, exeName.cStr(), escapedExeName.cStr(), nullptr); 586 #else 587 execl(exeName.cStr(), exeName.cStr(), nullptr); 588 #endif 589 } 590 591 #if _WIN32 592 if (child == -1) { 593 #endif 594 int error = errno; 595 if (error == ENOENT) { 596 context.exitError(kj::str(output.name, ": no such plugin (executable should be '", 597 exeName, "')")); 598 } else { 599 #if _WIN32 600 KJ_FAIL_SYSCALL("spawn()", error); 601 #else 602 KJ_FAIL_SYSCALL("exec()", error); 603 #endif 604 } 605 #if _WIN32 606 } 607 608 // Restore stdin. 609 KJ_SYSCALL(dup2(oldStdin, STDIN_FILENO)); 610 KJ_SYSCALL(close(oldStdin)); 611 612 // Restore current directory. 613 KJ_SYSCALL(chdir(pwd.begin()), pwd.begin()); 614 #else // _WIN32 615 } 616 617 KJ_SYSCALL(close(pipeFds[0])); 618 #endif // _WIN32, else 619 620 writeMessageToFd(pipeFds[1], message); 621 KJ_SYSCALL(close(pipeFds[1])); 622 623 #if _WIN32 624 int status; 625 if (_cwait(&status, child, 0) == -1) { 626 KJ_FAIL_SYSCALL("_cwait()", errno); 627 } 628 629 if (status != 0) { 630 context.error(kj::str(output.name, ": plugin failed: exit code ", status)); 631 } 632 633 #else // _WIN32 634 int status; 635 KJ_SYSCALL(waitpid(child, &status, 0)); 636 if (WIFSIGNALED(status)) { 637 context.error(kj::str(output.name, ": plugin failed: ", strsignal(WTERMSIG(status)))); 638 } else if (WIFEXITED(status) && WEXITSTATUS(status) != 0) { 639 context.error(kj::str(output.name, ": plugin failed: exit code ", WEXITSTATUS(status))); 640 } 641 #endif // _WIN32, else 642 } 643 644 return true; 645 } 646 647 // ===================================================================================== 648 // "convert" command 649 650 private: 651 enum class Format { 652 BINARY, 653 PACKED, 654 FLAT, 655 FLAT_PACKED, 656 CANONICAL, 657 TEXT, 658 JSON 659 }; 660 661 kj::Maybe<Format> parseFormatName(kj::StringPtr name) { 662 if (name == "binary" ) return Format::BINARY; 663 if (name == "packed" ) return Format::PACKED; 664 if (name == "flat" ) return Format::FLAT; 665 if (name == "flat-packed") return Format::FLAT_PACKED; 666 if (name == "canonical" ) return Format::CANONICAL; 667 if (name == "text" ) return Format::TEXT; 668 if (name == "json" ) return Format::JSON; 669 670 return nullptr; 671 } 672 673 kj::StringPtr toString(Format format) { 674 switch (format) { 675 case Format::BINARY : return "binary"; 676 case Format::PACKED : return "packed"; 677 case Format::FLAT : return "flat"; 678 case Format::FLAT_PACKED: return "flat-packed"; 679 case Format::CANONICAL : return "canonical"; 680 case Format::TEXT : return "text"; 681 case Format::JSON : return "json"; 682 } 683 KJ_UNREACHABLE; 684 } 685 686 Format formatFromDeprecatedFlags(Format defaultFormat) { 687 // For deprecated commands "decode" and "encode". 688 if (flat) { 689 if (packed) { 690 return Format::FLAT_PACKED; 691 } else { 692 return Format::FLAT; 693 } 694 } if (packed) { 695 return Format::PACKED; 696 } else if (binary) { 697 return Format::BINARY; 698 } else { 699 return defaultFormat; 700 } 701 } 702 703 kj::MainBuilder::Validity verifyRequirements(Format format) { 704 if ((format == Format::TEXT || format == Format::JSON) && rootType == StructSchema()) { 705 return kj::str("format requires schema: ", toString(format)); 706 } else { 707 return true; 708 } 709 } 710 711 public: 712 kj::MainBuilder::Validity setConversion(kj::StringPtr conversion) { 713 KJ_IF_MAYBE(colon, conversion.findFirst(':')) { 714 auto from = kj::str(conversion.slice(0, *colon)); 715 auto to = conversion.slice(*colon + 1); 716 717 KJ_IF_MAYBE(f, parseFormatName(from)) { 718 convertFrom = *f; 719 } else { 720 return kj::str("unknown format: ", from); 721 } 722 723 KJ_IF_MAYBE(t, parseFormatName(to)) { 724 convertTo = *t; 725 } else { 726 return kj::str("unknown format: ", to); 727 } 728 729 if (convertFrom == Format::JSON || convertTo == Format::JSON) { 730 // We need annotations to process JSON. 731 // TODO(someday): Find a way that we can process annotations from json.capnp without 732 // requiring other annotation-only imports like c++.capnp 733 annotationFlag = Compiler::COMPILE_ANNOTATIONS; 734 } 735 736 return true; 737 } else { 738 return "invalid conversion, format is: <from>:<to>"; 739 } 740 } 741 742 kj::MainBuilder::Validity convert() { 743 { 744 auto result = verifyRequirements(convertFrom); 745 if (result.getError() != nullptr) return result; 746 } 747 { 748 auto result = verifyRequirements(convertTo); 749 if (result.getError() != nullptr) return result; 750 } 751 752 kj::FdInputStream rawInput(STDIN_FILENO); 753 kj::BufferedInputStreamWrapper input(rawInput); 754 755 kj::FdOutputStream output(STDOUT_FILENO); 756 757 if (!quiet) { 758 auto result = checkPlausibility(convertFrom, input.getReadBuffer()); 759 if (result.getError() != nullptr) { 760 return kj::mv(result); 761 } 762 } 763 764 while (input.tryGetReadBuffer().size() > 0) { 765 readOneAndConvert(input, output); 766 } 767 768 context.exit(); 769 KJ_CLANG_KNOWS_THIS_IS_UNREACHABLE_BUT_GCC_DOESNT; 770 } 771 772 private: 773 kj::Vector<byte> readAll(kj::BufferedInputStreamWrapper& input) { 774 kj::Vector<byte> allBytes; 775 for (;;) { 776 auto buffer = input.tryGetReadBuffer(); 777 if (buffer.size() == 0) break; 778 allBytes.addAll(buffer); 779 input.skip(buffer.size()); 780 } 781 return allBytes; 782 } 783 784 kj::String readOneText(kj::BufferedInputStreamWrapper& input) { 785 // Consume and return one parentheses-delimited message from the input. 786 // 787 // Accounts for nested parentheses, comments, and string literals. 788 789 enum { 790 NORMAL, 791 COMMENT, 792 QUOTE, 793 QUOTE_ESCAPE, 794 DQUOTE, 795 DQUOTE_ESCAPE 796 } state = NORMAL; 797 uint depth = 0; 798 bool sawClose = false; 799 800 kj::Vector<char> chars; 801 802 for (;;) { 803 auto buffer = input.tryGetReadBuffer(); 804 805 if (buffer == nullptr) { 806 // EOF 807 chars.add('\0'); 808 return kj::String(chars.releaseAsArray()); 809 } 810 811 for (auto i: kj::indices(buffer)) { 812 char c = buffer[i]; 813 switch (state) { 814 case NORMAL: 815 switch (c) { 816 case '#': state = COMMENT; break; 817 case '(': 818 if (depth == 0 && sawClose) { 819 // We already got one complete message. This is the start of the next message. 820 // Stop here. 821 chars.addAll(buffer.slice(0, i)); 822 chars.add('\0'); 823 input.skip(i); 824 return kj::String(chars.releaseAsArray()); 825 } 826 ++depth; 827 break; 828 case ')': 829 if (depth > 0) { 830 if (--depth == 0) { 831 sawClose = true; 832 } 833 } 834 break; 835 default: break; 836 } 837 break; 838 case COMMENT: 839 switch (c) { 840 case '\n': state = NORMAL; break; 841 default: break; 842 } 843 break; 844 case QUOTE: 845 switch (c) { 846 case '\'': state = NORMAL; break; 847 case '\\': state = QUOTE_ESCAPE; break; 848 default: break; 849 } 850 break; 851 case QUOTE_ESCAPE: 852 break; 853 case DQUOTE: 854 switch (c) { 855 case '\"': state = NORMAL; break; 856 case '\\': state = DQUOTE_ESCAPE; break; 857 default: break; 858 } 859 break; 860 case DQUOTE_ESCAPE: 861 break; 862 } 863 } 864 865 chars.addAll(buffer); 866 input.skip(buffer.size()); 867 } 868 } 869 870 kj::String readOneJson(kj::BufferedInputStreamWrapper& input) { 871 // Consume and return one brace-delimited message from the input. 872 // 873 // Accounts for nested braces, string literals, and comments starting with # or //. Technically 874 // JSON does not permit comments but this code is lenient in case we change things later. 875 876 enum { 877 NORMAL, 878 SLASH, 879 COMMENT, 880 QUOTE, 881 QUOTE_ESCAPE, 882 DQUOTE, 883 DQUOTE_ESCAPE 884 } state = NORMAL; 885 uint depth = 0; 886 bool sawClose = false; 887 888 kj::Vector<char> chars; 889 890 for (;;) { 891 auto buffer = input.tryGetReadBuffer(); 892 893 if (buffer == nullptr) { 894 // EOF 895 chars.add('\0'); 896 return kj::String(chars.releaseAsArray()); 897 } 898 899 for (auto i: kj::indices(buffer)) { 900 char c = buffer[i]; 901 switch (state) { 902 case SLASH: 903 if (c == '/') { 904 state = COMMENT; 905 break; 906 } 907 KJ_FALLTHROUGH; 908 case NORMAL: 909 switch (c) { 910 case '#': state = COMMENT; break; 911 case '/': state = SLASH; break; 912 case '{': 913 if (depth == 0 && sawClose) { 914 // We already got one complete message. This is the start of the next message. 915 // Stop here. 916 chars.addAll(buffer.slice(0, i)); 917 chars.add('\0'); 918 input.skip(i); 919 return kj::String(chars.releaseAsArray()); 920 } 921 ++depth; 922 break; 923 case '}': 924 if (depth > 0) { 925 if (--depth == 0) { 926 sawClose = true; 927 } 928 } 929 break; 930 default: break; 931 } 932 break; 933 case COMMENT: 934 switch (c) { 935 case '\n': state = NORMAL; break; 936 default: break; 937 } 938 break; 939 case QUOTE: 940 switch (c) { 941 case '\'': state = NORMAL; break; 942 case '\\': state = QUOTE_ESCAPE; break; 943 default: break; 944 } 945 break; 946 case QUOTE_ESCAPE: 947 break; 948 case DQUOTE: 949 switch (c) { 950 case '\"': state = NORMAL; break; 951 case '\\': state = DQUOTE_ESCAPE; break; 952 default: break; 953 } 954 break; 955 case DQUOTE_ESCAPE: 956 break; 957 } 958 } 959 960 chars.addAll(buffer); 961 input.skip(buffer.size()); 962 } 963 } 964 965 class ParseErrorCatcher: public kj::ExceptionCallback { 966 public: 967 ParseErrorCatcher(kj::ProcessContext& context): context(context) {} 968 ~ParseErrorCatcher() noexcept(false) { 969 if (!unwindDetector.isUnwinding()) { 970 KJ_IF_MAYBE(e, exception) { 971 context.error(kj::str( 972 "*** ERROR CONVERTING PREVIOUS MESSAGE ***\n" 973 "The following error occurred while converting the message above.\n" 974 "This probably means the input data is invalid/corrupted.\n", 975 "Exception description: ", e->getDescription(), "\n" 976 "Code location: ", e->getFile(), ":", e->getLine(), "\n" 977 "*** END ERROR ***")); 978 } 979 } 980 } 981 982 void onRecoverableException(kj::Exception&& e) { 983 // Only capture the first exception, on the assumption that later exceptions are probably 984 // just cascading problems. 985 if (exception == nullptr) { 986 exception = kj::mv(e); 987 } 988 } 989 990 private: 991 kj::ProcessContext& context; 992 kj::Maybe<kj::Exception> exception; 993 kj::UnwindDetector unwindDetector; 994 }; 995 996 void readOneAndConvert(kj::BufferedInputStreamWrapper& input, kj::OutputStream& output) { 997 // Since this is a debug tool, lift the usual security limits. Worse case is the process 998 // crashes or has to be killed. 999 ReaderOptions options; 1000 options.nestingLimit = kj::maxValue; 1001 options.traversalLimitInWords = kj::maxValue; 1002 1003 ParseErrorCatcher parseErrorCatcher(context); 1004 1005 switch (convertFrom) { 1006 case Format::BINARY: { 1007 capnp::InputStreamMessageReader message(input, options); 1008 return writeConversion(message.getRoot<AnyStruct>(), output); 1009 } 1010 case Format::PACKED: { 1011 capnp::PackedMessageReader message(input, options); 1012 return writeConversion(message.getRoot<AnyStruct>(), output); 1013 } 1014 case Format::FLAT: 1015 case Format::CANONICAL: { 1016 auto allBytes = readAll(input); 1017 1018 // Technically we don't know if the bytes are aligned so we'd better copy them to a new 1019 // array. Note that if we have a non-whole number of words we chop off the straggler 1020 // bytes. This is fine because if those bytes are actually part of the message we will 1021 // hit an error later and if they are not then who cares? 1022 auto words = kj::heapArray<word>(allBytes.size() / sizeof(word)); 1023 memcpy(words.begin(), allBytes.begin(), words.size() * sizeof(word)); 1024 1025 kj::ArrayPtr<const word> segments[1] = { words }; 1026 SegmentArrayMessageReader message(segments, options); 1027 if (convertFrom == Format::CANONICAL) { 1028 KJ_REQUIRE(message.isCanonical()); 1029 } 1030 return writeConversion(message.getRoot<AnyStruct>(), output); 1031 } 1032 case Format::FLAT_PACKED: { 1033 auto allBytes = readAll(input); 1034 1035 auto words = kj::heapArray<word>(computeUnpackedSizeInWords(allBytes)); 1036 kj::ArrayInputStream input(allBytes); 1037 capnp::_::PackedInputStream unpacker(input); 1038 unpacker.read(words.asBytes().begin(), words.asBytes().size()); 1039 word dummy; 1040 KJ_ASSERT(unpacker.tryRead(&dummy, sizeof(dummy), sizeof(dummy)) == 0); 1041 1042 kj::ArrayPtr<const word> segments[1] = { words }; 1043 SegmentArrayMessageReader message(segments, options); 1044 return writeConversion(message.getRoot<AnyStruct>(), output); 1045 } 1046 case Format::TEXT: { 1047 auto text = readOneText(input); 1048 MallocMessageBuilder message; 1049 TextCodec codec; 1050 codec.setPrettyPrint(pretty); 1051 auto root = message.initRoot<DynamicStruct>(rootType); 1052 codec.decode(text, root); 1053 return writeConversion(root.asReader(), output); 1054 } 1055 case Format::JSON: { 1056 auto text = readOneJson(input); 1057 MallocMessageBuilder message; 1058 JsonCodec codec; 1059 codec.setPrettyPrint(pretty); 1060 codec.handleByAnnotation(rootType); 1061 auto root = message.initRoot<DynamicStruct>(rootType); 1062 codec.decode(text, root); 1063 return writeConversion(root.asReader(), output); 1064 } 1065 } 1066 1067 KJ_UNREACHABLE; 1068 } 1069 1070 void writeConversion(AnyStruct::Reader reader, kj::OutputStream& output) { 1071 switch (convertTo) { 1072 case Format::BINARY: { 1073 MallocMessageBuilder message( 1074 segmentSize == 0 ? SUGGESTED_FIRST_SEGMENT_WORDS : segmentSize, 1075 segmentSize == 0 ? SUGGESTED_ALLOCATION_STRATEGY : AllocationStrategy::FIXED_SIZE); 1076 message.setRoot(reader); 1077 capnp::writeMessage(output, message); 1078 return; 1079 } 1080 case Format::PACKED: { 1081 MallocMessageBuilder message( 1082 segmentSize == 0 ? SUGGESTED_FIRST_SEGMENT_WORDS : segmentSize, 1083 segmentSize == 0 ? SUGGESTED_ALLOCATION_STRATEGY : AllocationStrategy::FIXED_SIZE); 1084 message.setRoot(reader); 1085 capnp::writePackedMessage(output, message); 1086 return; 1087 } 1088 case Format::FLAT: { 1089 auto words = kj::heapArray<word>(reader.totalSize().wordCount + 1); 1090 memset(words.begin(), 0, words.asBytes().size()); 1091 copyToUnchecked(reader, words); 1092 output.write(words.begin(), words.asBytes().size()); 1093 return; 1094 } 1095 case Format::FLAT_PACKED: { 1096 auto words = kj::heapArray<word>(reader.totalSize().wordCount + 1); 1097 memset(words.begin(), 0, words.asBytes().size()); 1098 copyToUnchecked(reader, words); 1099 kj::BufferedOutputStreamWrapper buffered(output); 1100 capnp::_::PackedOutputStream packed(buffered); 1101 packed.write(words.begin(), words.asBytes().size()); 1102 return; 1103 } 1104 case Format::CANONICAL: { 1105 auto words = reader.canonicalize(); 1106 output.write(words.begin(), words.asBytes().size()); 1107 return; 1108 } 1109 case Format::TEXT: { 1110 TextCodec codec; 1111 codec.setPrettyPrint(pretty); 1112 auto text = codec.encode(reader.as<DynamicStruct>(rootType)); 1113 output.write({text.asBytes(), kj::StringPtr("\n").asBytes()}); 1114 return; 1115 } 1116 case Format::JSON: { 1117 JsonCodec codec; 1118 codec.setPrettyPrint(pretty); 1119 codec.handleByAnnotation(rootType); 1120 auto text = codec.encode(reader.as<DynamicStruct>(rootType)); 1121 output.write({text.asBytes(), kj::StringPtr("\n").asBytes()}); 1122 return; 1123 } 1124 } 1125 1126 KJ_UNREACHABLE; 1127 } 1128 1129 public: 1130 1131 // ===================================================================================== 1132 // "decode" command 1133 1134 kj::MainBuilder::Validity codeBinary() { 1135 if (packed) return "cannot be used with --packed"; 1136 if (flat) return "cannot be used with --flat"; 1137 binary = true; 1138 return true; 1139 } 1140 kj::MainBuilder::Validity codeFlat() { 1141 if (binary) return "cannot be used with --binary"; 1142 flat = true; 1143 return true; 1144 } 1145 kj::MainBuilder::Validity codePacked() { 1146 if (binary) return "cannot be used with --binary"; 1147 packed = true; 1148 return true; 1149 } 1150 kj::MainBuilder::Validity printShort() { 1151 pretty = false; 1152 return true; 1153 } 1154 kj::MainBuilder::Validity setQuiet() { 1155 quiet = true; 1156 return true; 1157 } 1158 kj::MainBuilder::Validity setSegmentSize(kj::StringPtr size) { 1159 if (flat) return "cannot be used with --flat"; 1160 char* end; 1161 segmentSize = strtol(size.cStr(), &end, 0); 1162 if (size.size() == 0 || *end != '\0') { 1163 return "not an integer"; 1164 } 1165 return true; 1166 } 1167 1168 kj::MainBuilder::Validity setRootType(kj::StringPtr input) { 1169 KJ_ASSERT(sourceFiles.size() == 1); 1170 1171 class CliArgumentErrorReporter: public ErrorReporter { 1172 public: 1173 void addError(uint32_t startByte, uint32_t endByte, kj::StringPtr message) override { 1174 if (startByte < endByte) { 1175 error = kj::str(startByte + 1, "-", endByte, ": ", message); 1176 } else if (startByte > 0) { 1177 error = kj::str(startByte + 1, ": ", message); 1178 } else { 1179 error = kj::str(message); 1180 } 1181 } 1182 1183 bool hadErrors() override { 1184 return error != nullptr; 1185 } 1186 1187 kj::MainBuilder::Validity getValidity() { 1188 KJ_IF_MAYBE(e, error) { 1189 return kj::mv(*e); 1190 } else { 1191 return true; 1192 } 1193 } 1194 1195 private: 1196 kj::Maybe<kj::String> error; 1197 }; 1198 1199 CliArgumentErrorReporter errorReporter; 1200 1201 capnp::MallocMessageBuilder tokenArena; 1202 auto lexedTokens = tokenArena.initRoot<capnp::compiler::LexedTokens>(); 1203 lex(input, lexedTokens, errorReporter); 1204 1205 CapnpParser parser(tokenArena.getOrphanage(), errorReporter); 1206 auto tokens = lexedTokens.asReader().getTokens(); 1207 CapnpParser::ParserInput parserInput(tokens.begin(), tokens.end()); 1208 1209 bool success = false; 1210 1211 if (parserInput.getPosition() == tokens.end()) { 1212 // Empty argument? 1213 errorReporter.addError(0, 0, "Couldn't parse type name."); 1214 } else { 1215 KJ_IF_MAYBE(expression, parser.getParsers().expression(parserInput)) { 1216 // The input is expected to contain a *single* expression. 1217 if (parserInput.getPosition() == tokens.end()) { 1218 // Hooray, now parse it. 1219 KJ_IF_MAYBE(compiledType, 1220 sourceFiles[0].compiled.evalType(expression->getReader(), errorReporter)) { 1221 KJ_IF_MAYBE(type, compiledType->getSchema()) { 1222 if (type->isStruct()) { 1223 rootType = type->asStruct(); 1224 success = true; 1225 } else { 1226 errorReporter.addError(0, 0, "Type is not a struct."); 1227 } 1228 } else { 1229 // Apparently named a file scope. 1230 errorReporter.addError(0, 0, "Type is not a struct."); 1231 } 1232 } 1233 } else { 1234 errorReporter.addErrorOn(parserInput.current(), "Couldn't parse type name."); 1235 } 1236 } else { 1237 auto best = parserInput.getBest(); 1238 if (best == tokens.end()) { 1239 errorReporter.addError(input.size(), input.size(), "Couldn't parse type name."); 1240 } else { 1241 errorReporter.addErrorOn(*best, "Couldn't parse type name."); 1242 } 1243 } 1244 } 1245 1246 KJ_ASSERT(success || errorReporter.hadErrors()); 1247 return errorReporter.getValidity(); 1248 } 1249 1250 kj::MainBuilder::Validity decode() { 1251 convertTo = Format::TEXT; 1252 convertFrom = formatFromDeprecatedFlags(Format::BINARY); 1253 return convert(); 1254 } 1255 1256 private: 1257 enum Plausibility { 1258 IMPOSSIBLE, 1259 IMPLAUSIBLE, 1260 WRONG_TYPE, 1261 PLAUSIBLE 1262 }; 1263 1264 bool plausibleOrWrongType(Plausibility p) { 1265 return p == PLAUSIBLE || p == WRONG_TYPE; 1266 } 1267 1268 Plausibility isPlausiblyFlat(kj::ArrayPtr<const byte> prefix, uint segmentCount = 1) { 1269 if (prefix.size() < 8) { 1270 // Not enough prefix to say. 1271 return PLAUSIBLE; 1272 } 1273 1274 if ((prefix[0] & 3) == 2) { 1275 // Far pointer. Verify the segment ID. 1276 uint32_t segmentId = prefix[4] | (prefix[5] << 8) 1277 | (prefix[6] << 16) | (prefix[7] << 24); 1278 if (segmentId == 0 || segmentId >= segmentCount) { 1279 return IMPOSSIBLE; 1280 } else { 1281 return PLAUSIBLE; 1282 } 1283 } 1284 1285 if ((prefix[0] & 3) != 0) { 1286 // Not a struct pointer. 1287 return IMPOSSIBLE; 1288 } 1289 if ((prefix[3] & 0x80) != 0) { 1290 if (prefix[0] == 0xff && prefix[1] == 0xff && prefix[2] == 0xff && prefix[3] == 0xff && 1291 prefix[4] == 0 && prefix[5] == 0 && prefix[6] == 0 && prefix[7] == 0) { 1292 // This is an empty struct with offset of -1. That's valid. 1293 } else { 1294 // Offset is negative (invalid). 1295 return IMPOSSIBLE; 1296 } 1297 } 1298 if ((prefix[3] & 0xe0) != 0) { 1299 // Offset is over a gigabyte (implausible). 1300 return IMPLAUSIBLE; 1301 } 1302 1303 uint data = prefix[4] | (prefix[5] << 8); 1304 uint pointers = prefix[6] | (prefix[7] << 8); 1305 1306 if (data + pointers > 2048) { 1307 // Root struct is huge (over 16 KiB). 1308 return IMPLAUSIBLE; 1309 } 1310 1311 auto structSchema = rootType.getProto().getStruct(); 1312 if ((data < structSchema.getDataWordCount() && pointers > structSchema.getPointerCount()) || 1313 (data > structSchema.getDataWordCount() && pointers < structSchema.getPointerCount())) { 1314 // Struct is neither older nor newer than the schema. 1315 return WRONG_TYPE; 1316 } 1317 1318 if (data > structSchema.getDataWordCount() && 1319 data - structSchema.getDataWordCount() > 128) { 1320 // Data section appears to have grown by 1k (128 words). This seems implausible. 1321 return WRONG_TYPE; 1322 } 1323 if (pointers > structSchema.getPointerCount() && 1324 pointers - structSchema.getPointerCount() > 128) { 1325 // Pointer section appears to have grown by 1k (128 words). This seems implausible. 1326 return WRONG_TYPE; 1327 } 1328 1329 return PLAUSIBLE; 1330 } 1331 1332 Plausibility isPlausiblyBinary(kj::ArrayPtr<const byte> prefix) { 1333 if (prefix.size() < 8) { 1334 // Not enough prefix to say. 1335 return PLAUSIBLE; 1336 } 1337 1338 uint32_t segmentCount = prefix[0] | (prefix[1] << 8) 1339 | (prefix[2] << 16) | (prefix[3] << 24); 1340 1341 // Actually, the bytes store segmentCount - 1. 1342 ++segmentCount; 1343 1344 if (segmentCount > 65536) { 1345 // While technically possible, this is so implausible that we should mark it impossible. 1346 // This helps to make sure we fail fast on packed input. 1347 return IMPOSSIBLE; 1348 } else if (segmentCount > 256) { 1349 // Implausible segment count. 1350 return IMPLAUSIBLE; 1351 } 1352 1353 uint32_t segment0Size = prefix[4] | (prefix[5] << 8) 1354 | (prefix[6] << 16) | (prefix[7] << 24); 1355 1356 if (segment0Size > (1 << 27)) { 1357 // Segment larger than 1G seems implausible. 1358 return IMPLAUSIBLE; 1359 } 1360 1361 uint32_t segment0Offset = 4 + segmentCount * 4; 1362 if (segment0Offset % 8 != 0) { 1363 segment0Offset += 4; 1364 } 1365 KJ_ASSERT(segment0Offset % 8 == 0); 1366 1367 if (prefix.size() < segment0Offset + 8) { 1368 // Segment 0 is past our prefix, so we can't check it. 1369 return PLAUSIBLE; 1370 } 1371 1372 return isPlausiblyFlat(prefix.slice(segment0Offset, prefix.size()), segmentCount); 1373 } 1374 1375 Plausibility isPlausiblyPacked(kj::ArrayPtr<const byte> prefix, 1376 kj::Function<Plausibility(kj::ArrayPtr<const byte>)> checkUnpacked) { 1377 kj::Vector<byte> unpacked; 1378 1379 // Try to unpack a prefix so that we can check it. 1380 const byte* pos = prefix.begin(); 1381 const byte* end = prefix.end(); 1382 if (end - pos > 1024) { 1383 // Don't bother unpacking more than 1k. 1384 end = pos + 1024; 1385 } 1386 while (pos < end) { 1387 byte tag = *pos++; 1388 for (uint i = 0; i < 8 && pos < end; i++) { 1389 if (tag & (1 << i)) { 1390 byte b = *pos++; 1391 if (b == 0) { 1392 // A zero byte should have been deflated away. 1393 return IMPOSSIBLE; 1394 } 1395 unpacked.add(b); 1396 } else { 1397 unpacked.add(0); 1398 } 1399 } 1400 1401 if (pos == end) { 1402 break; 1403 } 1404 1405 if (tag == 0) { 1406 uint count = *pos++ * 8; 1407 unpacked.addAll(kj::repeat(byte(0), count)); 1408 } else if (tag == 0xff) { 1409 uint count = *pos++ * 8; 1410 size_t available = end - pos; 1411 uint n = kj::min(count, available); 1412 unpacked.addAll(pos, pos + n); 1413 pos += n; 1414 } 1415 } 1416 1417 return checkUnpacked(unpacked); 1418 } 1419 1420 Plausibility isPlausiblyPacked(kj::ArrayPtr<const byte> prefix) { 1421 return isPlausiblyPacked(prefix, KJ_BIND_METHOD(*this, isPlausiblyBinary)); 1422 } 1423 1424 Plausibility isPlausiblyPackedFlat(kj::ArrayPtr<const byte> prefix) { 1425 return isPlausiblyPacked(prefix, [this](kj::ArrayPtr<const byte> prefix) { 1426 return isPlausiblyFlat(prefix); 1427 }); 1428 } 1429 1430 Plausibility isPlausiblyText(kj::ArrayPtr<const byte> prefix) { 1431 enum { PREAMBLE, COMMENT, BODY } state = PREAMBLE; 1432 1433 for (char c: prefix.asChars()) { 1434 switch (state) { 1435 case PREAMBLE: 1436 // Before opening parenthesis. 1437 switch (c) { 1438 case '(': state = BODY; continue; 1439 case '#': state = COMMENT; continue; 1440 case ' ': 1441 case '\n': 1442 case '\r': 1443 case '\t': 1444 case '\v': 1445 // whitespace 1446 break; 1447 default: 1448 // Not whitespace, not comment, not open parenthesis. Impossible! 1449 return IMPOSSIBLE; 1450 } 1451 break; 1452 case COMMENT: 1453 switch (c) { 1454 case '\n': state = PREAMBLE; continue; 1455 default: break; 1456 } 1457 break; 1458 case BODY: 1459 switch (c) { 1460 case '\"': 1461 case '\'': 1462 // String literal. Let's stop here before things get complicated. 1463 return PLAUSIBLE; 1464 default: 1465 break; 1466 } 1467 break; 1468 } 1469 1470 if ((static_cast<uint8_t>(c) < 0x20 && c != '\n' && c != '\r' && c != '\t' && c != '\v') 1471 || c == 0x7f) { 1472 // Unprintable character. 1473 return IMPOSSIBLE; 1474 } 1475 } 1476 1477 return PLAUSIBLE; 1478 } 1479 1480 Plausibility isPlausiblyJson(kj::ArrayPtr<const byte> prefix) { 1481 enum { PREAMBLE, COMMENT, BODY } state = PREAMBLE; 1482 1483 for (char c: prefix.asChars()) { 1484 switch (state) { 1485 case PREAMBLE: 1486 // Before opening parenthesis. 1487 switch (c) { 1488 case '{': state = BODY; continue; 1489 case '#': state = COMMENT; continue; 1490 case '/': state = COMMENT; continue; 1491 case ' ': 1492 case '\n': 1493 case '\r': 1494 case '\t': 1495 case '\v': 1496 // whitespace 1497 break; 1498 default: 1499 // Not whitespace, not comment, not open brace. Impossible! 1500 return IMPOSSIBLE; 1501 } 1502 break; 1503 case COMMENT: 1504 switch (c) { 1505 case '\n': state = PREAMBLE; continue; 1506 default: break; 1507 } 1508 break; 1509 case BODY: 1510 switch (c) { 1511 case '\"': 1512 // String literal. Let's stop here before things get complicated. 1513 return PLAUSIBLE; 1514 default: 1515 break; 1516 } 1517 break; 1518 } 1519 1520 if ((c > 0 && c < ' ' && c != '\n' && c != '\r' && c != '\t' && c != '\v') || c == 0x7f) { 1521 // Unprintable character. 1522 return IMPOSSIBLE; 1523 } 1524 } 1525 1526 return PLAUSIBLE; 1527 } 1528 1529 Plausibility isPlausibly(Format format, kj::ArrayPtr<const byte> prefix) { 1530 switch (format) { 1531 case Format::BINARY : return isPlausiblyBinary (prefix); 1532 case Format::PACKED : return isPlausiblyPacked (prefix); 1533 case Format::FLAT : return isPlausiblyFlat (prefix); 1534 case Format::FLAT_PACKED: return isPlausiblyPackedFlat(prefix); 1535 case Format::CANONICAL : return isPlausiblyFlat (prefix); 1536 case Format::TEXT : return isPlausiblyText (prefix); 1537 case Format::JSON : return isPlausiblyJson (prefix); 1538 } 1539 KJ_UNREACHABLE; 1540 } 1541 1542 kj::Maybe<Format> guessFormat(kj::ArrayPtr<const byte> prefix) { 1543 Format candidates[] = { 1544 Format::BINARY, 1545 Format::TEXT, 1546 Format::PACKED, 1547 Format::JSON, 1548 Format::FLAT, 1549 Format::FLAT_PACKED 1550 }; 1551 1552 for (Format candidate: candidates) { 1553 if (plausibleOrWrongType(isPlausibly(candidate, prefix))) { 1554 return candidate; 1555 } 1556 } 1557 1558 return nullptr; 1559 } 1560 1561 kj::MainBuilder::Validity checkPlausibility(Format format, kj::ArrayPtr<const byte> prefix) { 1562 switch (isPlausibly(format, prefix)) { 1563 case PLAUSIBLE: 1564 return true; 1565 1566 case IMPOSSIBLE: 1567 KJ_IF_MAYBE(guess, guessFormat(prefix)) { 1568 return kj::str( 1569 "The input is not in \"", toString(format), "\" format. It looks like it is in \"", 1570 toString(*guess), "\" format. Try that instead."); 1571 } else { 1572 return kj::str( 1573 "The input is not in \"", toString(format), "\" format."); 1574 } 1575 1576 case IMPLAUSIBLE: 1577 KJ_IF_MAYBE(guess, guessFormat(prefix)) { 1578 context.warning(kj::str( 1579 "*** WARNING ***\n" 1580 "The input data does not appear to be in \"", toString(format), "\" format. It\n" 1581 "looks like it may be in \"", toString(*guess), "\" format. I'll try to parse\n" 1582 "it in \"", toString(format), "\" format as you requested, but if it doesn't work,\n" 1583 "try \"", toString(format), "\" instead. Use --quiet to suppress this warning.\n" 1584 "*** END WARNING ***\n")); 1585 } else { 1586 context.warning(kj::str( 1587 "*** WARNING ***\n" 1588 "The input data does not appear to be in \"", toString(format), "\" format, nor\n" 1589 "in any other known format. I'll try to parse it in \"", toString(format), "\"\n" 1590 "format anyway, as you requested. Use --quiet to suppress this warning.\n" 1591 "*** END WARNING ***\n")); 1592 } 1593 return true; 1594 1595 case WRONG_TYPE: 1596 if (format == Format::FLAT && plausibleOrWrongType(isPlausiblyBinary(prefix))) { 1597 context.warning( 1598 "*** WARNING ***\n" 1599 "The input data does not appear to match the schema that you specified. I'll try\n" 1600 "to parse it anyway, but if it doesn't look right, please verify that you\n" 1601 "have the right schema. This could also be because the input is not in \"flat\"\n" 1602 "format; indeed, it looks like this input may be in regular binary format,\n" 1603 "so you might want to try \"binary\" instead. Use --quiet to suppress this\n" 1604 "warning.\n" 1605 "*** END WARNING ***\n"); 1606 } else if (format == Format::FLAT_PACKED && 1607 plausibleOrWrongType(isPlausiblyPacked(prefix))) { 1608 context.warning( 1609 "*** WARNING ***\n" 1610 "The input data does not appear to match the schema that you specified. I'll try\n" 1611 "to parse it anyway, but if it doesn't look right, please verify that you\n" 1612 "have the right schema. This could also be because the input is not in \"flat-packed\"\n" 1613 "format; indeed, it looks like this input may be in regular packed format,\n" 1614 "so you might want to try \"packed\" instead. Use --quiet to suppress this\n" 1615 "warning.\n" 1616 "*** END WARNING ***\n"); 1617 } else { 1618 context.warning( 1619 "*** WARNING ***\n" 1620 "The input data does not appear to be the type that you specified. I'll try\n" 1621 "to parse it anyway, but if it doesn't look right, please verify that you\n" 1622 "have the right type. Use --quiet to suppress this warning.\n" 1623 "*** END WARNING ***\n"); 1624 } 1625 return true; 1626 } 1627 1628 KJ_UNREACHABLE; 1629 } 1630 1631 public: 1632 // ----------------------------------------------------------------- 1633 1634 kj::MainBuilder::Validity encode() { 1635 convertFrom = Format::TEXT; 1636 convertTo = formatFromDeprecatedFlags(Format::BINARY); 1637 return convert(); 1638 } 1639 1640 kj::MainBuilder::Validity setEvalOutputFormat(kj::StringPtr format) { 1641 KJ_IF_MAYBE(f, parseFormatName(format)) { 1642 convertTo = *f; 1643 return true; 1644 } else { 1645 return kj::str("unknown format: ", format); 1646 } 1647 } 1648 1649 kj::MainBuilder::Validity evalConst(kj::StringPtr name) { 1650 convertTo = formatFromDeprecatedFlags(convertTo); 1651 1652 KJ_ASSERT(sourceFiles.size() == 1); 1653 1654 auto parser = kj::parse::sequence( 1655 kj::parse::many( 1656 kj::parse::sequence( 1657 kj::parse::identifier, 1658 kj::parse::many( 1659 kj::parse::sequence( 1660 kj::parse::exactChar<'['>(), 1661 kj::parse::integer, 1662 kj::parse::exactChar<']'>())), 1663 kj::parse::oneOf( 1664 kj::parse::endOfInput, 1665 kj::parse::sequence( 1666 kj::parse::exactChar<'.'>(), 1667 kj::parse::notLookingAt(kj::parse::endOfInput))))), 1668 kj::parse::endOfInput); 1669 1670 kj::parse::IteratorInput<char, const char*> input(name.begin(), name.end()); 1671 1672 kj::Array<kj::Tuple<kj::String, kj::Array<uint64_t>>> nameParts; 1673 KJ_IF_MAYBE(p, parser(input)) { 1674 nameParts = kj::mv(*p); 1675 } else { 1676 return "invalid syntax"; 1677 } 1678 1679 auto pos = nameParts.begin(); 1680 1681 // Traverse the path to find a schema. 1682 uint64_t scopeId = sourceFiles[0].id; 1683 bool stoppedAtSubscript = false; 1684 for (; pos != nameParts.end(); ++pos) { 1685 kj::StringPtr part = kj::get<0>(*pos); 1686 1687 KJ_IF_MAYBE(childId, compiler->lookup(scopeId, part)) { 1688 scopeId = *childId; 1689 1690 if (kj::get<1>(*pos).size() > 0) { 1691 stoppedAtSubscript = true; 1692 break; 1693 } 1694 } else { 1695 break; 1696 } 1697 } 1698 Schema schema = compiler->getLoader().get(scopeId); 1699 1700 // Evaluate this schema to a DynamicValue. 1701 DynamicValue::Reader value; 1702 word zeroWord[1]; 1703 memset(&zeroWord, 0, sizeof(zeroWord)); 1704 kj::ArrayPtr<const word> segments[1] = { kj::arrayPtr(zeroWord, 1) }; 1705 SegmentArrayMessageReader emptyMessage(segments); 1706 switch (schema.getProto().which()) { 1707 case schema::Node::CONST: 1708 value = schema.asConst(); 1709 break; 1710 1711 case schema::Node::STRUCT: 1712 if (pos == nameParts.end()) { 1713 return kj::str("'", schema.getShortDisplayName(), "' cannot be evaluated."); 1714 } 1715 1716 // Use the struct's default value. 1717 value = emptyMessage.getRoot<DynamicStruct>(schema.asStruct()); 1718 break; 1719 1720 default: 1721 if (stoppedAtSubscript) { 1722 return kj::str("'", schema.getShortDisplayName(), "' is not a list."); 1723 } else if (pos != nameParts.end()) { 1724 return kj::str("'", kj::get<0>(*pos), "' is not defined."); 1725 } else { 1726 return kj::str("'", schema.getShortDisplayName(), "' cannot be evaluated."); 1727 } 1728 } 1729 1730 // Traverse the rest of the path as struct fields. 1731 for (; pos != nameParts.end(); ++pos) { 1732 kj::StringPtr partName = kj::get<0>(*pos); 1733 1734 if (!stoppedAtSubscript) { 1735 if (value.getType() == DynamicValue::STRUCT) { 1736 auto structValue = value.as<DynamicStruct>(); 1737 KJ_IF_MAYBE(field, structValue.getSchema().findFieldByName(partName)) { 1738 value = structValue.get(*field); 1739 } else { 1740 return kj::str("'", kj::get<0>(pos[-1]), "' has no member '", partName, "'."); 1741 } 1742 } else { 1743 return kj::str("'", kj::get<0>(pos[-1]), "' is not a struct."); 1744 } 1745 } 1746 1747 auto& subscripts = kj::get<1>(*pos); 1748 for (uint i = 0; i < subscripts.size(); i++) { 1749 uint64_t subscript = subscripts[i]; 1750 if (value.getType() == DynamicValue::LIST) { 1751 auto listValue = value.as<DynamicList>(); 1752 if (subscript < listValue.size()) { 1753 value = listValue[subscript]; 1754 } else { 1755 return kj::str("'", partName, "[", kj::strArray(subscripts.slice(0, i + 1), "]["), 1756 "]' is out-of-bounds."); 1757 } 1758 } else { 1759 if (i > 0) { 1760 return kj::str("'", partName, "[", kj::strArray(subscripts.slice(0, i), "]["), 1761 "]' is not a list."); 1762 } else { 1763 return kj::str("'", partName, "' is not a list."); 1764 } 1765 } 1766 } 1767 1768 stoppedAtSubscript = false; 1769 } 1770 1771 // OK, we have a value. Print it. 1772 if (convertTo != Format::TEXT) { 1773 if (value.getType() != DynamicValue::STRUCT) { 1774 return "not a struct; binary output is only available on structs"; 1775 } 1776 1777 auto structValue = value.as<DynamicStruct>(); 1778 rootType = structValue.getSchema(); 1779 kj::FdOutputStream output(STDOUT_FILENO); 1780 writeConversion(structValue, output); 1781 context.exit(); 1782 } else { 1783 if (pretty && value.getType() == DynamicValue::STRUCT) { 1784 context.exitInfo(prettyPrint(value.as<DynamicStruct>()).flatten()); 1785 } else if (pretty && value.getType() == DynamicValue::LIST) { 1786 context.exitInfo(prettyPrint(value.as<DynamicList>()).flatten()); 1787 } else { 1788 context.exitInfo(kj::str(value)); 1789 } 1790 } 1791 1792 KJ_CLANG_KNOWS_THIS_IS_UNREACHABLE_BUT_GCC_DOESNT; 1793 } 1794 1795 public: 1796 // ===================================================================================== 1797 1798 void addError(const kj::ReadableDirectory& directory, kj::PathPtr path, 1799 SourcePos start, SourcePos end, 1800 kj::StringPtr message) override { 1801 auto file = getDisplayName(directory, path); 1802 1803 kj::String wholeMessage; 1804 if (end.line == start.line) { 1805 if (end.column == start.column) { 1806 wholeMessage = kj::str(file, ":", start.line + 1, ":", start.column + 1, 1807 ": error: ", message, "\n"); 1808 } else { 1809 wholeMessage = kj::str(file, ":", start.line + 1, ":", start.column + 1, 1810 "-", end.column + 1, ": error: ", message, "\n"); 1811 } 1812 } else { 1813 // The error spans multiple lines, so just report it on the first such line. 1814 wholeMessage = kj::str(file, ":", start.line + 1, ": error: ", message, "\n"); 1815 } 1816 1817 context.error(wholeMessage); 1818 hadErrors_ = true; 1819 } 1820 1821 bool hadErrors() override { 1822 return hadErrors_; 1823 } 1824 1825 private: 1826 kj::ProcessContext& context; 1827 kj::Own<kj::Filesystem> disk; 1828 ModuleLoader loader; 1829 kj::SpaceFor<Compiler> compilerSpace; 1830 bool compilerConstructed = false; 1831 kj::Own<Compiler> compiler; 1832 1833 Compiler::AnnotationFlag annotationFlag = Compiler::COMPILE_ANNOTATIONS; 1834 1835 uint compileEagerness = Compiler::NODE | Compiler::CHILDREN | 1836 Compiler::DEPENDENCIES | Compiler::DEPENDENCY_PARENTS; 1837 // By default we compile each explicitly listed schema in full, plus first-level dependencies 1838 // of those schemas, plus the parent nodes of any dependencies. This is what most code generators 1839 // require to function. 1840 1841 struct SourceDirectory { 1842 kj::Own<const kj::ReadableDirectory> dir; 1843 bool isSourcePrefix; 1844 }; 1845 1846 kj::HashMap<kj::Path, SourceDirectory> sourceDirectories; 1847 // For each import path and source prefix, tracks the directory object we opened for it. 1848 // 1849 // Use via getSourceDirectory(). 1850 1851 kj::HashMap<const kj::ReadableDirectory*, kj::String> dirPrefixes; 1852 // For each open directory object, maps to a path prefix to add when displaying this path in 1853 // error messages. This keeps track of the original directory name as given by the user, before 1854 // canonicalization. 1855 // 1856 // Use via getDisplayName(). 1857 1858 bool addStandardImportPaths = true; 1859 1860 Format convertFrom = Format::BINARY; 1861 Format convertTo = Format::BINARY; 1862 // For the "convert" command. 1863 1864 bool binary = false; 1865 bool flat = false; 1866 bool packed = false; 1867 bool pretty = true; 1868 bool quiet = false; 1869 uint segmentSize = 0; 1870 StructSchema rootType; 1871 // For the "decode" and "encode" commands. 1872 1873 struct SourceFile { 1874 uint64_t id; 1875 Compiler::ModuleScope compiled; 1876 kj::StringPtr name; 1877 Module* module; 1878 }; 1879 1880 kj::Vector<SourceFile> sourceFiles; 1881 1882 struct OutputDirective { 1883 kj::ArrayPtr<const char> name; 1884 kj::Maybe<kj::Path> dir; 1885 1886 KJ_DISALLOW_COPY(OutputDirective); 1887 OutputDirective(OutputDirective&&) = default; 1888 OutputDirective(kj::ArrayPtr<const char> name, kj::Maybe<kj::Path> dir) 1889 : name(name), dir(kj::mv(dir)) {} 1890 }; 1891 kj::Vector<OutputDirective> outputs; 1892 1893 bool hadErrors_ = false; 1894 1895 kj::Maybe<const kj::ReadableDirectory&> getSourceDirectory( 1896 kj::StringPtr pathStr, bool isSourcePrefix) { 1897 auto cwd = disk->getCurrentPath(); 1898 auto path = cwd.evalNative(pathStr); 1899 1900 if (path.size() == 0) return disk->getRoot(); 1901 1902 KJ_IF_MAYBE(sdir, sourceDirectories.find(path)) { 1903 sdir->isSourcePrefix = sdir->isSourcePrefix || isSourcePrefix; 1904 return *sdir->dir; 1905 } 1906 1907 if (path == cwd) { 1908 // Slight hack if the working directory is explicitly specified: 1909 // - We want to avoid opening a new copy of the working directory, as tryOpenSubdir() would 1910 // do. 1911 // - If isSourcePrefix is true, we need to add it to sourceDirectories to track that. 1912 // Otherwise we don't need to add it at all. 1913 // - We do not need to add it to dirPrefixes since the cwd is already handled in 1914 // getDisplayName(). 1915 auto& result = disk->getCurrent(); 1916 if (isSourcePrefix) { 1917 kj::Own<const kj::ReadableDirectory> fakeOwn(&result, kj::NullDisposer::instance); 1918 sourceDirectories.insert(kj::mv(path), { kj::mv(fakeOwn), isSourcePrefix }); 1919 } 1920 return result; 1921 } 1922 1923 KJ_IF_MAYBE(dir, disk->getRoot().tryOpenSubdir(path)) { 1924 auto& result = *dir->get(); 1925 sourceDirectories.insert(kj::mv(path), { kj::mv(*dir), isSourcePrefix }); 1926 #if _WIN32 1927 kj::String prefix = pathStr.endsWith("/") || pathStr.endsWith("\\") 1928 ? kj::str(pathStr) : kj::str(pathStr, '\\'); 1929 #else 1930 kj::String prefix = pathStr.endsWith("/") ? kj::str(pathStr) : kj::str(pathStr, '/'); 1931 #endif 1932 dirPrefixes.insert(&result, kj::mv(prefix)); 1933 return result; 1934 } else { 1935 return nullptr; 1936 } 1937 } 1938 1939 struct DirPathPair { 1940 const kj::ReadableDirectory& dir; 1941 kj::Path path; 1942 }; 1943 1944 DirPathPair interpretSourceFile(kj::StringPtr pathStr) { 1945 auto cwd = disk->getCurrentPath(); 1946 auto path = cwd.evalNative(pathStr); 1947 1948 KJ_REQUIRE(path.size() > 0); 1949 for (size_t i = path.size() - 1; i > 0; i--) { 1950 auto prefix = path.slice(0, i); 1951 auto remainder = path.slice(i, path.size()); 1952 1953 KJ_IF_MAYBE(sdir, sourceDirectories.find(prefix)) { 1954 if (sdir->isSourcePrefix) { 1955 return { *sdir->dir, remainder.clone() }; 1956 } 1957 } 1958 } 1959 1960 // No source prefix matched. Fall back to heuristic: try stripping the current directory, 1961 // otherwise don't strip anything. 1962 if (path.startsWith(cwd)) { 1963 return { disk->getCurrent(), path.slice(cwd.size(), path.size()).clone() }; 1964 } else { 1965 // Hmm, no src-prefix matched and the file isn't even in the current directory. This might 1966 // be OK if we aren't generating any output anyway, but otherwise the results will almost 1967 // certainly not be what the user wanted. Let's print a warning, unless the output directives 1968 // are ones which we know do not produce output files. This is a hack. 1969 for (auto& output: outputs) { 1970 auto name = kj::str(output.name); 1971 if (name != "-" && name != "capnp") { 1972 context.warning(kj::str(pathStr, 1973 ": File is not in the current directory and does not match any prefix defined with " 1974 "--src-prefix. Please pass an appropriate --src-prefix so I can figure out where to " 1975 "write the output for this file.")); 1976 break; 1977 } 1978 } 1979 1980 return { disk->getRoot(), kj::mv(path) }; 1981 } 1982 } 1983 1984 kj::String getDisplayName(const kj::ReadableDirectory& dir, kj::PathPtr path) { 1985 KJ_IF_MAYBE(prefix, dirPrefixes.find(&dir)) { 1986 return kj::str(*prefix, path.toNativeString()); 1987 } else if (&dir == &disk->getRoot()) { 1988 return path.toNativeString(true); 1989 } else if (&dir == &disk->getCurrent()) { 1990 return path.toNativeString(false); 1991 } else { 1992 KJ_FAIL_ASSERT("unrecognized directory"); 1993 } 1994 } 1995 }; 1996 1997 } // namespace compiler 1998 } // namespace capnp 1999 2000 KJ_MAIN(capnp::compiler::CompilerMain);