protoc.js 59 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611
  1. import * as fs from 'fs/promises';
  2. import * as path from 'path';
  3. const protoc = {};
  4. protoc.Object = class {
  5. constructor(parent, name) {
  6. this.parent = parent;
  7. this.name = name;
  8. this.options = new Map();
  9. }
  10. get fullName() {
  11. const path = [this.name];
  12. let context = this.parent;
  13. while (context) {
  14. if (context.name) {
  15. path.unshift(context.name);
  16. }
  17. context = context.parent;
  18. }
  19. return path.join('.');
  20. }
  21. };
  22. protoc.Namespace = class extends protoc.Object {
  23. constructor(parent, name) {
  24. super(parent, name);
  25. this.children = new Map();
  26. if (!(this instanceof protoc.Root || this instanceof protoc.PrimitiveType)) {
  27. if (parent.get(this.name)) {
  28. throw new protoc.Error(`Duplicate name '${this.name}' in '${parent.name}'.`);
  29. }
  30. parent.children.set(this.name, this);
  31. }
  32. }
  33. defineNamespace(path) {
  34. path = path.split('.');
  35. if (path && path.length > 0 && path[0] === '') {
  36. throw new protoc.Error('Invalid path.');
  37. }
  38. /* eslint-disable consistent-this */
  39. let current = this;
  40. /* eslint-enable consistent-this */
  41. while (path.length > 0) {
  42. const part = path.shift();
  43. if (current.children && current.children.get(part)) {
  44. current = current.children.get(part);
  45. if (current instanceof protoc.Namespace === false) {
  46. throw new protoc.Error('Invalid path.');
  47. }
  48. } else {
  49. current = new protoc.Namespace(current, part);
  50. }
  51. }
  52. return current;
  53. }
  54. defineType(name) {
  55. const parts = name.split('.');
  56. const typeName = parts.pop();
  57. const parent = this.defineNamespace(parts.join('.'));
  58. const type = parent.get(name);
  59. if (type) {
  60. if (type instanceof protoc.Type) {
  61. return type;
  62. }
  63. throw new protoc.Error('Invalid type');
  64. }
  65. return new protoc.Type(parent, typeName);
  66. }
  67. get(name) {
  68. return this.children.get(name) || null;
  69. }
  70. find(path, filterType, parentAlreadyChecked) {
  71. if (path.length === 0) {
  72. return this;
  73. }
  74. if (path[0] === '') {
  75. return this.root.find(path.slice(1), filterType);
  76. }
  77. let found = this.get(path[0]);
  78. if (found) {
  79. if (path.length === 1) {
  80. if (found instanceof filterType) {
  81. return found;
  82. }
  83. } else if (found instanceof protoc.Namespace && (found = found.find(path.slice(1), filterType, true))) {
  84. return found;
  85. }
  86. } else {
  87. for (const child of this.children.values()) {
  88. if (child instanceof protoc.Namespace && (found = child.find(path, filterType, true))) {
  89. return found;
  90. }
  91. }
  92. }
  93. if (!this.parent || parentAlreadyChecked) {
  94. return null;
  95. }
  96. return this.parent.find(path, filterType);
  97. }
  98. findType(path) {
  99. const type = this.find(path.split('.'), protoc.Type);
  100. if (!type) {
  101. throw new protoc.Error(`Type or enum '${path}' not found in '${this.name}'.`);
  102. }
  103. return type;
  104. }
  105. static isReservedId(reserved, id) {
  106. if (reserved) {
  107. for (let i = 0; i < reserved.length; ++i) {
  108. if (typeof reserved[i] !== 'string' && reserved[i][0] <= id && reserved[i][1] > id) {
  109. return true;
  110. }
  111. }
  112. }
  113. return false;
  114. }
  115. static isReservedName(reserved, name) {
  116. if (reserved) {
  117. for (let i = 0; i < reserved.length; ++i) {
  118. if (reserved[i] === name) {
  119. return true;
  120. }
  121. }
  122. }
  123. return false;
  124. }
  125. };
  126. protoc.Root = class extends protoc.Namespace {
  127. constructor(alias) {
  128. super(null, '');
  129. this.alias = alias;
  130. this._files = new Set();
  131. this._library = new Map();
  132. this._library.set('google/protobuf/any.proto', () => {
  133. const type = this.defineType('google.protobuf.Any');
  134. type.defineField('type_url', 1, 'string');
  135. type.defineField('value', 2, 'bytes');
  136. });
  137. this._library.set('google/protobuf/wrappers.proto', () => {
  138. const type = this.defineType('google.protobuf.BoolValue');
  139. type.defineField('value', 1, 'bool');
  140. });
  141. }
  142. async load(paths, files) {
  143. for (const file of files) {
  144. /* eslint-disable no-await-in-loop */
  145. const resolved = await this._resolve(file, '', paths);
  146. /* eslint-enable no-await-in-loop */
  147. if (resolved) {
  148. /* eslint-disable no-await-in-loop */
  149. await this._loadFile(paths, resolved);
  150. /* eslint-enable no-await-in-loop */
  151. } else {
  152. throw new protoc.Error(`File '${file}' not found.`);
  153. }
  154. }
  155. return this;
  156. }
  157. async _loadFile(paths, file, weak) {
  158. if (!this._files.has(file)) {
  159. this._files.add(file);
  160. if (this._library.has(file)) {
  161. const callback = this._library.get(file);
  162. callback();
  163. } else {
  164. try {
  165. await this._parseFile(paths, file);
  166. } catch (error) {
  167. if (!weak) {
  168. throw error;
  169. }
  170. }
  171. }
  172. }
  173. }
  174. async _parseFile(paths, file) {
  175. const source = await fs.readFile(file, 'utf-8');
  176. const parser = new protoc.Parser(source, file, this);
  177. const parsed = parser.parse();
  178. for (const item of parsed.imports) {
  179. /* eslint-disable no-await-in-loop */
  180. const resolved = await this._resolve(item, file, paths);
  181. /* eslint-enable no-await-in-loop */
  182. if (!resolved) {
  183. throw new protoc.Error(`File '${item}' not found.`);
  184. }
  185. /* eslint-disable no-await-in-loop */
  186. await this._loadFile(paths, resolved);
  187. /* eslint-enable no-await-in-loop */
  188. }
  189. for (const item of parsed.weakImports) {
  190. /* eslint-disable no-await-in-loop */
  191. const resolved = await this._resolve(item, file, paths);
  192. /* eslint-enable no-await-in-loop */
  193. if (resolved) {
  194. /* eslint-disable no-await-in-loop */
  195. await this._loadFile(paths, resolved);
  196. /* eslint-enable no-await-in-loop */
  197. }
  198. }
  199. }
  200. async _resolve(target, source, paths) {
  201. const file = path.resolve(source, target);
  202. const posix = file.split(path.sep).join(path.posix.sep);
  203. const index = posix.lastIndexOf('google/protobuf/');
  204. if (index > -1) {
  205. const name = posix.substring(index);
  206. if (this._library.has(name)) {
  207. return name;
  208. }
  209. }
  210. const access = async (path) => {
  211. try {
  212. await fs.access(path);
  213. return true;
  214. } catch {
  215. return false;
  216. }
  217. };
  218. const exists = await access(file);
  219. if (exists) {
  220. return file;
  221. }
  222. for (const dir of paths) {
  223. const file = path.resolve(dir, target);
  224. /* eslint-disable no-await-in-loop */
  225. const exists = await access(file);
  226. if (exists) {
  227. return file;
  228. }
  229. /* eslint-enable no-await-in-loop */
  230. }
  231. return null;
  232. }
  233. };
  234. protoc.Type = class extends protoc.Namespace {
  235. constructor(parent, name) {
  236. super(parent, name);
  237. this.fields = new Map();
  238. this.oneofs = new Map();
  239. this.extensions = [];
  240. this.reserved = [];
  241. }
  242. get(name) {
  243. return this.fields.get(name) || this.oneofs.get(name) || this.children.get(name) || null;
  244. }
  245. defineField(name, id, type, rule, extend) {
  246. return new protoc.Field(this, name, id, type, rule, extend);
  247. }
  248. };
  249. protoc.Enum = class extends protoc.Type {
  250. constructor(parent, name) {
  251. super(parent, name);
  252. this.valuesById = new Map();
  253. this.values = {};
  254. this.reserved = [];
  255. }
  256. add(name, id) {
  257. if (!Number.isInteger(id)) {
  258. throw new protoc.Error('Identifier must be an integer.');
  259. }
  260. if (this.values[name] !== undefined) {
  261. throw new protoc.Error(`Duplicate name '${name}' in '${this.name}'.`);
  262. }
  263. if (protoc.Namespace.isReservedId(this.reserved, id)) {
  264. throw new protoc.Error(`Identifier '${id}' is reserved in '${this.name}'.`);
  265. }
  266. if (protoc.Namespace.isReservedName(this.reserved, name)) {
  267. throw new protoc.Error(`Name '${name}' is reserved in '${this.name}'.`);
  268. }
  269. if (this.valuesById.has(id)) {
  270. if (!this.options.has('allow_alias')) {
  271. throw new protoc.Error(`Duplicate identifier '${id}' in '${this.name}'.`);
  272. }
  273. } else {
  274. this.valuesById.set(id, name);
  275. }
  276. this.values[name] = id;
  277. }
  278. };
  279. protoc.PrimitiveType = class extends protoc.Type {
  280. constructor(name, long, mapKey, packed, defaultValue) {
  281. super(null, name);
  282. this.long = long;
  283. this.mapKey = mapKey;
  284. this.packed = packed;
  285. this.defaultValue = defaultValue;
  286. }
  287. static get(name) {
  288. if (!this._map) {
  289. this._map = new Map();
  290. const register = (type) => this._map.set(type.name, type);
  291. register(new protoc.PrimitiveType('double', false, false, true, 0));
  292. register(new protoc.PrimitiveType('float', false, false, true, 0));
  293. register(new protoc.PrimitiveType('int32', false, true, true, 0));
  294. register(new protoc.PrimitiveType('uint32', false, true, true, 0));
  295. register(new protoc.PrimitiveType('sint32', false, true, true, 0));
  296. register(new protoc.PrimitiveType('fixed32', false, true, true, 0));
  297. register(new protoc.PrimitiveType('sfixed32', false, true, true, 0));
  298. register(new protoc.PrimitiveType('int64', true, true, true, 0));
  299. register(new protoc.PrimitiveType('uint64', true, true, true, 0));
  300. register(new protoc.PrimitiveType('sint64', true, true, true, 0));
  301. register(new protoc.PrimitiveType('fixed64', true, true, true, 0));
  302. register(new protoc.PrimitiveType('sfixed64', true, true, true, 0));
  303. register(new protoc.PrimitiveType('bool', false, true, true, false));
  304. register(new protoc.PrimitiveType('string', false, true, false, ''));
  305. register(new protoc.PrimitiveType('bytes', false, true, false, []));
  306. }
  307. return this._map.get(name);
  308. }
  309. };
  310. protoc.Field = class extends protoc.Object {
  311. constructor(parent, name, id, type, rule, extend) {
  312. super(parent instanceof protoc.OneOf ? parent.parent : parent, name);
  313. if (!Number.isInteger(id) || id < 0) {
  314. throw new protoc.Error('Identifier must be a non-negative integer.');
  315. }
  316. if (rule && rule !== 'required' && rule !== 'optional' && rule !== 'repeated') {
  317. throw new protoc.Error('Rule must be a string.');
  318. }
  319. this.id = id;
  320. this._type = type;
  321. this.rule = rule && rule !== 'optional' ? rule : undefined;
  322. this.extend = extend;
  323. this.required = rule === 'required';
  324. this.repeated = rule === 'repeated';
  325. if (parent instanceof protoc.OneOf) {
  326. this.partOf = parent;
  327. parent.oneof.set(this.name, this);
  328. parent = parent.parent;
  329. }
  330. if (parent.get(this.name)) {
  331. throw new protoc.Error(`Duplicate name '${this.name}' in '${parent.name}'.`);
  332. }
  333. if (protoc.Namespace.isReservedId(parent.reserved, this.id)) {
  334. throw new protoc.Error(`Identifier '${this.id}' is reserved in '${parent.name}'.`);
  335. }
  336. if (protoc.Namespace.isReservedName(parent.reserved, this.name)) {
  337. throw new protoc.Error(`Name '${this.name}' is reserved in '${parent.name}'.`);
  338. }
  339. parent.fields.set(this.name, this);
  340. }
  341. get type() {
  342. if (typeof this._type === 'string') {
  343. this._type = protoc.PrimitiveType.get(this._type) || this.parent.findType(this._type);
  344. }
  345. return this._type;
  346. }
  347. get defaultValue() {
  348. const type = this.type;
  349. let value = null;
  350. if (type instanceof protoc.PrimitiveType) {
  351. value = type.defaultValue;
  352. } else if (type instanceof protoc.Enum) {
  353. value = type.values[Object.keys(type.values)[0]];
  354. }
  355. if (this.options.has('default')) {
  356. value = this.options.get('default');
  357. if (type instanceof protoc.Enum && typeof value === 'string') {
  358. value = type.values[value];
  359. }
  360. }
  361. if (type === 'bytes' && typeof value === 'string') {
  362. throw new protoc.Error('Unsupported bytes field.');
  363. }
  364. return value;
  365. }
  366. };
  367. protoc.OneOf = class extends protoc.Object {
  368. constructor(parent, name) {
  369. super(parent, name);
  370. this.oneof = new Map();
  371. if (parent.get(this.name)) {
  372. throw new protoc.Error(`Duplicate name '${this.name}' in '${parent.name}'.`);
  373. }
  374. parent.oneofs.set(this.name, this);
  375. }
  376. };
  377. protoc.MapField = class extends protoc.Field {
  378. constructor(parent, name, id, keyType, type) {
  379. super(parent, name, id, type);
  380. this.keyType = protoc.PrimitiveType.get(keyType);
  381. }
  382. };
  383. protoc.Parser = class {
  384. constructor(text, file, root) {
  385. this._context = root;
  386. this._tokenizer = new protoc.Tokenizer(text, file);
  387. this._head = true;
  388. this._imports = [];
  389. this._weakImports = [];
  390. }
  391. parse() {
  392. let token = '';
  393. while ((token = this._tokenizer.next()) !== null) {
  394. switch (token) {
  395. case 'package':
  396. if (!this._head) {
  397. throw this._parseError(token);
  398. }
  399. this._parsePackage();
  400. break;
  401. case 'import':
  402. if (!this._head) {
  403. throw this._parseError(token);
  404. }
  405. this._parseImport();
  406. break;
  407. case 'syntax':
  408. if (!this._head) {
  409. throw this._parseError(token);
  410. }
  411. this._parseSyntax();
  412. break;
  413. case 'option':
  414. this._parseOption(this._context, token);
  415. this._tokenizer.expect(';');
  416. break;
  417. default:
  418. if (this._parseCommon(this._context, token)) {
  419. this._head = false;
  420. continue;
  421. }
  422. throw this._parseError(token);
  423. }
  424. }
  425. return { package: this._package, imports: this._imports, weakImports: this._weakImports };
  426. }
  427. _parseId(token, acceptNegative) {
  428. switch (token) {
  429. case 'max':
  430. case 'Max':
  431. case 'MAX':
  432. return 0x1fffffff;
  433. case '0':
  434. return 0;
  435. default: {
  436. if (!acceptNegative && token.charAt(0) === "-") {
  437. throw this._parseError(token, 'id');
  438. }
  439. if (/^-?[1-9][0-9]*$/.test(token)) {
  440. return parseInt(token, 10);
  441. }
  442. if (/^-?0[x][0-9a-fA-F]+$/.test(token)) {
  443. return parseInt(token, 16);
  444. }
  445. if (/^-?0[0-7]+$/.test(token)) {
  446. return parseInt(token, 8);
  447. }
  448. throw this._parseError(token, 'id');
  449. }
  450. }
  451. }
  452. _parsePackage() {
  453. if (this._package) {
  454. throw this._parseError("package");
  455. }
  456. this._package = this._tokenizer.next();
  457. if (!protoc.Parser._isTypeReference(this._package)) {
  458. throw this._parseError(this._package, 'name');
  459. }
  460. this._context = this._context.defineNamespace(this._package);
  461. this._tokenizer.expect(";");
  462. }
  463. _parseImport() {
  464. let token = this._tokenizer.peek();
  465. if (token === 'weak') {
  466. this._tokenizer.next();
  467. token = this._readString();
  468. this._tokenizer.expect(";");
  469. this._weakImports.push(token);
  470. } else {
  471. if (token === 'public') {
  472. this._tokenizer.next();
  473. }
  474. token = this._readString();
  475. this._tokenizer.expect(";");
  476. this._imports.push(token);
  477. }
  478. }
  479. _parseSyntax() {
  480. this._tokenizer.expect("=");
  481. this._syntax = this._readString();
  482. if (this._syntax !== 'proto2' && this._syntax !== 'proto3') {
  483. throw this._parseError(this._syntax, 'syntax');
  484. }
  485. this._tokenizer.expect(";");
  486. }
  487. _parseCommon(parent, token) {
  488. switch (token) {
  489. case 'option':
  490. this._parseOption(parent, token);
  491. this._tokenizer.expect(";");
  492. return true;
  493. case 'message':
  494. this._parseType(parent, token);
  495. return true;
  496. case 'enum':
  497. this._parseEnum(parent, token);
  498. return true;
  499. case 'extend':
  500. this._parseExtend(parent, token);
  501. return true;
  502. case 'service':
  503. throw new protoc.Error(`Keyword '${token}' is not supported ${this._tokenizer.location()}`);
  504. default:
  505. return false;
  506. }
  507. }
  508. _ifBlock(obj, ifCallback, elseCallback) {
  509. if (obj) {
  510. obj.file = this._file;
  511. }
  512. if (this._tokenizer.accept("{")) {
  513. let token = '';
  514. while ((token = this._tokenizer.next()) !== "}") {
  515. ifCallback(token);
  516. }
  517. this._tokenizer.accept(";");
  518. } else {
  519. if (elseCallback) {
  520. elseCallback();
  521. }
  522. this._tokenizer.expect(";");
  523. }
  524. }
  525. _parseType(parent, token) {
  526. token = this._tokenizer.next();
  527. if (!protoc.Parser._isName(token)) {
  528. throw this._parseError(token, 'type');
  529. }
  530. const type = new protoc.Type(parent, token);
  531. this._ifBlock(type, (token) => {
  532. if (this._parseCommon(type, token)) {
  533. return;
  534. }
  535. switch (token) {
  536. case 'map':
  537. this._parseMapField(type, token);
  538. break;
  539. case 'required':
  540. case 'optional':
  541. case 'repeated':
  542. this._parseField(type, token);
  543. break;
  544. case 'oneof':
  545. this._parseOneOf(type, token);
  546. break;
  547. case 'reserved':
  548. this._readRanges(type.reserved, true);
  549. break;
  550. case 'extensions':
  551. this._readRanges(type.extensions);
  552. break;
  553. default:
  554. if (this._syntax !== 'proto3' || !protoc.Parser._isTypeReference(token)) {
  555. throw this._parseError(token);
  556. }
  557. this._tokenizer.push(token);
  558. this._parseField(type, 'optional');
  559. break;
  560. }
  561. });
  562. }
  563. _parseField(parent, rule, extend) {
  564. const type = this._tokenizer.next();
  565. if (type === "group") {
  566. this._parseGroup(parent, rule);
  567. return;
  568. }
  569. if (!protoc.Parser._isTypeReference(type)) {
  570. throw this._parseError(type, 'type');
  571. }
  572. const name = this._tokenizer.next();
  573. if (!protoc.Parser._isName(name)) {
  574. throw this._parseError(name, 'name');
  575. }
  576. this._tokenizer.expect("=");
  577. const id = this._parseId(this._tokenizer.next());
  578. const field = new protoc.Field(parent, name, id, type, rule, extend);
  579. this._ifBlock(field, (token) => {
  580. if (token === "option") {
  581. this._parseOption(field, token);
  582. this._tokenizer.expect(";");
  583. } else {
  584. throw this._parseError(token);
  585. }
  586. }, () => {
  587. this._parseInlineOptions(field);
  588. });
  589. }
  590. _parseGroup(parent, rule) {
  591. let name = this._tokenizer.next();
  592. if (!protoc.Parser._isName(name)) {
  593. throw this._parseError(name, 'name');
  594. }
  595. const fieldName = name.charAt(0).toLowerCase() + name.substring(1);
  596. if (name === fieldName) {
  597. name = name.charAt(0).toUpperCase() + name.substring(1);
  598. }
  599. this._tokenizer.expect("=");
  600. const id = this._parseId(this._tokenizer.next());
  601. const type = new protoc.Type(name);
  602. type.group = true;
  603. const field = new protoc.Field(parent, fieldName, id, name, rule);
  604. field.file = this._file;
  605. this._ifBlock(type, (token) => {
  606. switch (token) {
  607. case "option":
  608. this._parseOption(type, token);
  609. this._tokenizer.expect(";");
  610. break;
  611. case "required":
  612. case "optional":
  613. case "repeated":
  614. this._parseField(type, token);
  615. break;
  616. default:
  617. throw this._parseError(token); // there are no groups with proto3 semantics
  618. }
  619. });
  620. parent.add(type).add(field);
  621. }
  622. _parseMapField(parent) {
  623. this._tokenizer.expect("<");
  624. const keyType = this._tokenizer.next();
  625. const resolvedKeyType = protoc.PrimitiveType.get(keyType);
  626. if (!resolvedKeyType || !resolvedKeyType.mapKey) {
  627. throw this._parseError(keyType, 'type');
  628. }
  629. this._tokenizer.expect(",");
  630. const valueType = this._tokenizer.next();
  631. if (!protoc.Parser._isTypeReference(valueType)) {
  632. throw this._parseError(valueType, 'type');
  633. }
  634. this._tokenizer.expect(">");
  635. const name = this._tokenizer.next();
  636. if (!protoc.Parser._isName(name)) {
  637. throw this._parseError(name, 'name');
  638. }
  639. this._tokenizer.expect("=");
  640. const id = this._parseId(this._tokenizer.next());
  641. const field = new protoc.MapField(parent, name, id, keyType, valueType);
  642. this._ifBlock(field, (token) => {
  643. if (token === "option") {
  644. this._parseOption(field, token);
  645. this._tokenizer.expect(";");
  646. } else {
  647. throw this._parseError(token);
  648. }
  649. }, () => {
  650. this._parseInlineOptions(field);
  651. });
  652. }
  653. _parseOneOf(parent, token) {
  654. token = this._tokenizer.next();
  655. if (!protoc.Parser._isName(token)) {
  656. throw this._parseError(token, 'name');
  657. }
  658. const oneof = new protoc.OneOf(parent, token);
  659. this._ifBlock(oneof, (token) => {
  660. if (token === "option") {
  661. this._parseOption(oneof, token);
  662. this._tokenizer.expect(";");
  663. } else {
  664. this._tokenizer.push(token);
  665. this._parseField(oneof, 'optional');
  666. }
  667. });
  668. }
  669. _parseEnum(parent, token) {
  670. token = this._tokenizer.next();
  671. if (!protoc.Parser._isName(token)) {
  672. throw this._parseError(token, 'name');
  673. }
  674. const obj = new protoc.Enum(parent, token);
  675. this._ifBlock(obj, (token) => {
  676. switch (token) {
  677. case "option":
  678. this._parseOption(obj, token);
  679. this._tokenizer.expect(";");
  680. break;
  681. case "reserved":
  682. this._readRanges(obj.reserved, true);
  683. break;
  684. default:
  685. this._parseEnumValue(obj, token);
  686. break;
  687. }
  688. });
  689. }
  690. _parseEnumValue(parent, token) {
  691. if (!protoc.Parser._isName(token)) {
  692. throw this._parseError(token, 'name');
  693. }
  694. this._tokenizer.expect("=");
  695. const value = this._parseId(this._tokenizer.next(), true);
  696. const dummy = {};
  697. this._ifBlock(dummy, (token) => {
  698. if (token === "option") {
  699. this._parseOption(dummy, token); // skip
  700. this._tokenizer.expect(";");
  701. } else {
  702. throw this._parseError(token);
  703. }
  704. }, () => {
  705. this._parseInlineOptions(dummy); // skip
  706. });
  707. parent.add(token, value);
  708. }
  709. _parseExtend(parent, token) {
  710. token = this._tokenizer.next();
  711. if (!protoc.Parser._isTypeReference(token)) {
  712. throw this._parseError(token, 'reference');
  713. }
  714. const reference = token;
  715. this._ifBlock(null, (token) => {
  716. switch (token) {
  717. case "required":
  718. case "repeated":
  719. case "optional":
  720. this._parseField(parent, token, reference);
  721. break;
  722. default:
  723. if (this._syntax === 'proto3' || !protoc.Parser._isTypeReference(token)) {
  724. throw this._parseError(token);
  725. }
  726. this._tokenizer.push(token);
  727. this._parseField(parent, 'optional', reference);
  728. break;
  729. }
  730. });
  731. }
  732. _parseOption(parent, token) {
  733. const custom = this._tokenizer.accept("(");
  734. token = this._tokenizer.next();
  735. if (!protoc.Parser._isTypeReference(token)) {
  736. throw this._parseError(token, 'name');
  737. }
  738. let name = token;
  739. if (custom) {
  740. this._tokenizer.expect(")");
  741. name = `(${name})`;
  742. token = this._tokenizer.peek();
  743. if (/^(?:\.[a-zA-Z_][a-zA-Z_0-9]*)+$/.test(token)) {
  744. name += token;
  745. this._tokenizer.next();
  746. }
  747. }
  748. this._tokenizer.expect("=");
  749. this._parseOptionValue(parent, name);
  750. }
  751. _parseOptionValue(parent, name) {
  752. if (this._tokenizer.accept('{')) {
  753. while (!this._tokenizer.accept('}')) {
  754. const token = this._tokenizer.next();
  755. if (!protoc.Parser._isName(token)) {
  756. throw this._parseError(token, 'name');
  757. }
  758. if (this._tokenizer.peek() === '{') {
  759. this._parseOptionValue(parent, `${name}.${token}`);
  760. } else {
  761. this._tokenizer.expect(':');
  762. if (this._tokenizer.peek() === '{') {
  763. this._parseOptionValue(parent, `${name}.${token}`);
  764. } else {
  765. parent.options.set(`${name}.${token}`, this._readValue());
  766. }
  767. }
  768. this._tokenizer.accept(',');
  769. }
  770. } else {
  771. parent.options.set(name, this._readValue());
  772. }
  773. }
  774. _parseInlineOptions(parent) {
  775. if (this._tokenizer.accept('[')) {
  776. do {
  777. this._parseOption(parent, 'option');
  778. }
  779. while (this._tokenizer.accept(','));
  780. this._tokenizer.expect(']');
  781. }
  782. return parent;
  783. }
  784. _readString() {
  785. const values = [];
  786. let token = '';
  787. do {
  788. if ((token = this._tokenizer.next()) !== '"' && token !== "'") {
  789. throw this._parseError(token);
  790. }
  791. values.push(this._tokenizer.next());
  792. this._tokenizer.expect(token);
  793. token = this._tokenizer.peek();
  794. }
  795. while (token === '"' || token === "'");
  796. return values.join('');
  797. }
  798. _readValue() {
  799. const token = this._tokenizer.next();
  800. switch (token) {
  801. case "'":
  802. case '"':
  803. this._tokenizer.push(token);
  804. return this._readString();
  805. case 'true':
  806. case 'TRUE':
  807. return true;
  808. case 'false':
  809. case 'FALSE':
  810. return false;
  811. default: {
  812. const value = this._parseNumber(token);
  813. if (value !== undefined) {
  814. return value;
  815. }
  816. if (protoc.Parser._isTypeReference(token)) {
  817. return token;
  818. }
  819. throw this._parseError(token, 'value');
  820. }
  821. }
  822. }
  823. _readRanges(target, acceptStrings) {
  824. do {
  825. let token = '';
  826. if (acceptStrings && ((token = this._tokenizer.peek()) === '"' || token === "'")) {
  827. target.push(this._readString());
  828. } else {
  829. const start = this._parseId(this._tokenizer.next());
  830. const end = this._tokenizer.accept('to') ? this._parseId(this._tokenizer.next()) : start;
  831. target.push([start, end]);
  832. }
  833. }
  834. while (this._tokenizer.accept(','));
  835. this._tokenizer.expect(';');
  836. }
  837. _parseNumber(token) {
  838. let sign = 1;
  839. if (token.charAt(0) === '-') {
  840. sign = -1;
  841. token = token.substring(1);
  842. }
  843. switch (token) {
  844. case 'inf':
  845. case 'INF':
  846. case 'Inf': {
  847. return sign * Infinity;
  848. }
  849. case 'nan':
  850. case 'NAN':
  851. case 'Nan':
  852. case 'NaN': {
  853. return NaN;
  854. }
  855. case '0': {
  856. return 0;
  857. }
  858. default: {
  859. if (/^[1-9][0-9]*$/.test(token)) {
  860. return sign * parseInt(token, 10);
  861. }
  862. if (/^0[x][0-9a-fA-F]+$/.test(token)) {
  863. return sign * parseInt(token, 16);
  864. }
  865. if (/^0[0-7]+$/.test(token)) {
  866. return sign * parseInt(token, 8);
  867. }
  868. if (/^(?![eE])[0-9]*(?:\.[0-9]*)?(?:[eE][+-]?[0-9]+)?$/.test(token)) {
  869. return sign * parseFloat(token);
  870. }
  871. return undefined;
  872. }
  873. }
  874. }
  875. static _isName(value) {
  876. return /^[a-zA-Z_][a-zA-Z_0-9]*$/.test(value);
  877. }
  878. static _isTypeReference(value) {
  879. return /^(?:\.?[a-zA-Z_][a-zA-Z_0-9]*)(?:\.[a-zA-Z_][a-zA-Z_0-9]*)*$/.test(value);
  880. }
  881. _parseError(token, name) {
  882. name = name || 'token';
  883. const location = this._tokenizer.location();
  884. return new protoc.Error(`Invalid ${name} '${token}' ${location}.`);
  885. }
  886. };
  887. protoc.Tokenizer = class {
  888. constructor(text, file) {
  889. this._text = text;
  890. this._file = file;
  891. this._position = 0;
  892. this._length = text.length;
  893. this._line = 1;
  894. this._stack = [];
  895. this._delimiter = null;
  896. }
  897. get file() {
  898. return this._file;
  899. }
  900. get line() {
  901. return this._line;
  902. }
  903. next() {
  904. if (this._stack.length > 0) {
  905. return this._stack.shift();
  906. }
  907. if (this._delimiter) {
  908. return this._readString();
  909. }
  910. let repeat = false;
  911. let prev = '';
  912. let curr = '';
  913. do {
  914. if (this._position === this._length) {
  915. return null;
  916. }
  917. repeat = false;
  918. while (/\s/.test(curr = this._get(this._position))) {
  919. if (curr === '\n') {
  920. this._line++;
  921. }
  922. this._position++;
  923. if (this._position === this._length) {
  924. return null;
  925. }
  926. }
  927. if (this._get(this._position) === '/') {
  928. this._position++;
  929. if (this._position === this._length) {
  930. throw this._readError('Invalid comment');
  931. }
  932. if (this._get(this._position) === '/') {
  933. while (this._get(++this._position) !== '\n') {
  934. if (this._position === this._length) {
  935. return null;
  936. }
  937. }
  938. this._position++;
  939. this._line++;
  940. repeat = true;
  941. } else if ((curr = this._get(this._position)) === '*') {
  942. do {
  943. if (curr === '\n') {
  944. this._line++;
  945. }
  946. this._position++;
  947. if (this._position === this._length) {
  948. throw this._readError('Invalid comment');
  949. }
  950. prev = curr;
  951. curr = this._get(this._position);
  952. } while (prev !== '*' || curr !== '/');
  953. this._position++;
  954. repeat = true;
  955. } else {
  956. return '/';
  957. }
  958. }
  959. }
  960. while (repeat);
  961. let end = this._position;
  962. const delimRe = /[\s{}=;:[\],'"()<>]/g;
  963. delimRe.lastIndex = 0;
  964. const delim = delimRe.test(this._get(end++));
  965. if (!delim) {
  966. while (end < this._length && !delimRe.test(this._get(end))) {
  967. end++;
  968. }
  969. }
  970. const position = this._position;
  971. this._position = end;
  972. const token = this._text.substring(position, this._position);
  973. if (token === '"' || token === "'") {
  974. this._delimiter = token;
  975. }
  976. return token;
  977. }
  978. peek() {
  979. if (!this._stack.length) {
  980. const token = this.next();
  981. if (token === null) {
  982. return null;
  983. }
  984. this.push(token);
  985. }
  986. return this._stack[0];
  987. }
  988. push(value) {
  989. this._stack.push(value);
  990. }
  991. expect(value) {
  992. const token = this.peek();
  993. if (token !== value) {
  994. throw this._readError(`Unexpected '${token}' instead of '${value}'`);
  995. }
  996. this.next();
  997. }
  998. accept(value) {
  999. const token = this.peek();
  1000. if (token === value) {
  1001. this.next();
  1002. return true;
  1003. }
  1004. return false;
  1005. }
  1006. _get(pos) {
  1007. return this._text.charAt(pos);
  1008. }
  1009. static _unescape(str) {
  1010. return str.replace(/\\(.?)/g, ($0, $1) => {
  1011. switch ($1) {
  1012. case '\\':
  1013. case '':
  1014. return $1;
  1015. case '0':
  1016. return '\0';
  1017. case 'r':
  1018. return '\r';
  1019. case 'n':
  1020. return '\n';
  1021. case 't':
  1022. return '\t';
  1023. default:
  1024. return '';
  1025. }
  1026. });
  1027. }
  1028. _readString() {
  1029. const re = this._delimiter === "'" ? /(?:'([^'\\]*(?:\\.[^'\\]*)*)')/g : /(?:"([^"\\]*(?:\\.[^"\\]*)*)")/g;
  1030. re.lastIndex = this._position - 1;
  1031. const match = re.exec(this._text);
  1032. if (!match) {
  1033. throw this._readError('Invalid string');
  1034. }
  1035. this._position = re.lastIndex;
  1036. this.push(this._delimiter);
  1037. this._delimiter = null;
  1038. return protoc.Tokenizer._unescape(match[1]);
  1039. }
  1040. _readError(message) {
  1041. const location = `at ${this._file}:${this._line}.`;
  1042. return new protoc.Error(`${message} ${location}`);
  1043. }
  1044. location() {
  1045. return `at ${this.file}:${this.line}.`;
  1046. }
  1047. };
  1048. protoc.Generator = class {
  1049. constructor(root, options) {
  1050. this._root = root;
  1051. this._options = options;
  1052. this._builder = new protoc.Generator.StringBuilder();
  1053. const scopes = Array.from(this._root.children.values()).map((child) => child.fullName);
  1054. const exports = new Set(scopes.map((scope) => scope.split('.')[0]));
  1055. this._builder.add('');
  1056. for (const value of exports) {
  1057. this._builder.add(`export const ${value} = {};`);
  1058. }
  1059. this._buildContent(this._root);
  1060. this._content = this._builder.toString();
  1061. }
  1062. get content() {
  1063. return this._content;
  1064. }
  1065. _buildContent(namespace) {
  1066. for (const child of namespace.children.values()) {
  1067. if (child instanceof protoc.Enum) {
  1068. this._builder.add('');
  1069. this._buildEnum(child);
  1070. } else if (child instanceof protoc.Type) {
  1071. this._builder.add('');
  1072. this._buildType(child);
  1073. } else if (child instanceof protoc.Namespace) {
  1074. const name = child.fullName.split('.').map((name) => protoc.Generator._escapeName(name)).join('.');
  1075. if (name.indexOf('.') !== -1) {
  1076. this._builder.add('');
  1077. this._builder.add(`${name} = {};`);
  1078. }
  1079. this._buildContent(child);
  1080. } else {
  1081. throw new protoc.Error('Unsupportd namespace child.');
  1082. }
  1083. }
  1084. }
  1085. _buildEnum(type) {
  1086. /* eslint-disable indent */
  1087. const name = type.fullName.split('.').map((name) => protoc.Generator._escapeName(name)).join('.');
  1088. this._builder.add(`${name} = {`);
  1089. this._builder.indent();
  1090. const keys = Object.keys(type.values);
  1091. for (let i = 0; i < keys.length; i++) {
  1092. const key = keys[i];
  1093. const value = type.values[key];
  1094. this._builder.add(`${JSON.stringify(key)}: ${value}${(i === keys.length - 1) ? '' : ','}`);
  1095. }
  1096. this._builder.outdent();
  1097. this._builder.add("};");
  1098. /* eslint-enable indent */
  1099. }
  1100. _buildType(type) {
  1101. const name = type.fullName.split('.').map((name) => protoc.Generator._escapeName(name)).join('.');
  1102. this._builder.add(`${name} = class ${protoc.Generator._escapeName(type.name)} {`);
  1103. this._builder.indent();
  1104. this._buildConstructor(type);
  1105. for (const oneof of type.oneofs.values()) {
  1106. /* eslint-disable indent */
  1107. this._builder.add('');
  1108. this._builder.add(`get ${oneof.name}() {`);
  1109. this._builder.indent();
  1110. this._builder.add(`${name}.${oneof.name}Set = ${name}.${oneof.name}Set || new Set([${Array.from(oneof.oneof.keys()).map(JSON.stringify).join(', ')}]);`);
  1111. this._builder.add(`return Object.keys(this).find((key) => ${name}.${oneof.name}Set.has(key) && this[key] !== null);`);
  1112. this._builder.outdent();
  1113. this._builder.add('}');
  1114. /* eslint-enable indent */
  1115. }
  1116. if (this._options.binary) {
  1117. this._builder.add('');
  1118. this._buildDecodeFunction(type);
  1119. }
  1120. if (this._options.text) {
  1121. this._builder.add('');
  1122. this._buildDecodeTextFunction(type);
  1123. }
  1124. if (this._options.json) {
  1125. this._builder.add('');
  1126. this._buildDecodeJsonFunction(type);
  1127. }
  1128. this._builder.outdent();
  1129. this._builder.add('};');
  1130. let first = true;
  1131. for (const field of type.fields.values()) {
  1132. if (field.partOf || field.repeated || field instanceof protoc.MapField) {
  1133. continue;
  1134. }
  1135. if (first) {
  1136. this._builder.add('');
  1137. first = false;
  1138. }
  1139. if (field.type.long) {
  1140. if (field.type.name === 'uint64' || field.type.name === 'fixed64') {
  1141. this._builder.add(`${name}.prototype${protoc.Generator._propertyReference(field.name)} = ${field.defaultValue}n;`);
  1142. } else {
  1143. this._builder.add(`${name}.prototype${protoc.Generator._propertyReference(field.name)} = ${field.defaultValue}n;`);
  1144. }
  1145. } else if (field.type.name === 'bytes') {
  1146. this._builder.add(`${name}.prototype${protoc.Generator._propertyReference(field.name)} = new Uint8Array(${JSON.stringify(Array.prototype.slice.call(field.defaultValue))});`);
  1147. } else {
  1148. this._builder.add(`${name}.prototype${protoc.Generator._propertyReference(field.name)} = ${JSON.stringify(field.defaultValue)};`);
  1149. }
  1150. }
  1151. this._buildContent(type);
  1152. }
  1153. _buildConstructor(type) {
  1154. const filter = (field) => field instanceof protoc.MapField || field.repeated;
  1155. const fields = Array.from(type.fields.values()).filter(filter);
  1156. if (fields.length === 0) {
  1157. return;
  1158. }
  1159. /* eslint-disable indent */
  1160. this._builder.add('');
  1161. this._builder.add('constructor() {');
  1162. this._builder.indent();
  1163. for (const field of fields) {
  1164. if (field instanceof protoc.MapField) {
  1165. this._builder.add(`this${protoc.Generator._propertyReference(field.name)} = {};`);
  1166. } else if (field.repeated) {
  1167. this._builder.add(`this${protoc.Generator._propertyReference(field.name)} = [];`);
  1168. }
  1169. }
  1170. this._builder.outdent();
  1171. this._builder.add('}');
  1172. /* eslint-enable indent */
  1173. }
  1174. _buildDecodeFunction(type) {
  1175. /* eslint-disable indent */
  1176. const fieldTypeName = (field) => `${field.type.fullName}`;
  1177. this._builder.add('static decode(reader, length) {');
  1178. this._builder.indent();
  1179. this._builder.add(`const message = new ${type.fullName}();`);
  1180. this._builder.add('const end = length === undefined ? reader.length : reader.position + length;');
  1181. this._builder.add("while (reader.position < end) {");
  1182. this._builder.indent();
  1183. this._builder.add("const tag = reader.uint32();");
  1184. if (type.group) {
  1185. this._builder.add("if ((tag&7) === 4)");
  1186. this._builder.indent();
  1187. this._builder.add("break;");
  1188. this._builder.outdent();
  1189. }
  1190. this._builder.add("switch (tag >>> 3) {");
  1191. this._builder.indent();
  1192. for (const field of type.fields.values()) {
  1193. const variable = `message${protoc.Generator._propertyReference(field.name)}`;
  1194. this._builder.add(`case ${field.id}:`);
  1195. this._builder.indent();
  1196. if (field instanceof protoc.MapField) {
  1197. const value = field.type instanceof protoc.PrimitiveType ?
  1198. `reader.${field.type.name}()` :
  1199. `${fieldTypeName(field)}.decode(reader, reader.uint32())`;
  1200. this._builder.add(`reader.entry(${variable}, () => reader.${field.keyType.name}(), () => ${value});`);
  1201. } else if (field.repeated) {
  1202. if (field.type.name === 'float' || field.type.name === 'double') {
  1203. this._builder.add(`${variable} = reader.${field.type.name}s(${variable}, tag);`);
  1204. } else if (field.type instanceof protoc.Enum) {
  1205. this._builder.add(`${variable} = reader.array(${variable}, () => reader.int32(), tag);`);
  1206. } else if (field.type instanceof protoc.PrimitiveType && field.type.packed) {
  1207. this._builder.add(`${variable} = reader.array(${variable}, () => reader.${field.type.name}(), tag);`);
  1208. } else if (field.type instanceof protoc.PrimitiveType) {
  1209. this._builder.add(`${variable}.push(reader.${field.type.name}());`);
  1210. } else if (field.type.group) {
  1211. this._builder.add(`${variable}.push(${fieldTypeName(field)}.decode(reader));`);
  1212. } else {
  1213. this._builder.add(`${variable}.push(${fieldTypeName(field)}.decode(reader, reader.uint32()));`);
  1214. }
  1215. } else if (field.type instanceof protoc.Enum) {
  1216. this._builder.add(`${variable} = reader.int32();`);
  1217. } else if (field.type instanceof protoc.PrimitiveType) {
  1218. this._builder.add(`${variable} = reader.${field.type.name}();`);
  1219. } else if (field.type.group) {
  1220. this._builder.add(`${variable} = ${fieldTypeName(field)}.decode(reader);`);
  1221. } else {
  1222. this._builder.add(`${variable} = ${fieldTypeName(field)}.decode(reader, reader.uint32());`);
  1223. }
  1224. this._builder.add('break;');
  1225. this._builder.outdent();
  1226. }
  1227. this._builder.add('default:');
  1228. this._builder.indent();
  1229. this._builder.add("reader.skipType(tag & 7);");
  1230. this._builder.add("break;");
  1231. this._builder.outdent();
  1232. this._builder.outdent();
  1233. this._builder.add("}");
  1234. this._builder.outdent();
  1235. this._builder.add('}');
  1236. for (const field of Array.from(type.fields.values()).filter((field) => field.required)) {
  1237. this._builder.add(`if (!Object.prototype.hasOwnProperty.call(message, '${field.name}')) {`);
  1238. this._builder.indent();
  1239. this._builder.add(`throw new Error("Expected '${field.name}'.");`);
  1240. this._builder.outdent();
  1241. this._builder.add('}');
  1242. }
  1243. this._builder.add('return message;');
  1244. this._builder.outdent();
  1245. this._builder.add('}');
  1246. /* eslint-enable indent */
  1247. }
  1248. _buildDecodeTextFunction(type) {
  1249. /* eslint-disable indent */
  1250. this._builder.add('static decodeText(reader) {');
  1251. this._builder.indent();
  1252. if (type.fullName === 'google.protobuf.Any') {
  1253. this._builder.add(`return reader.any(() => new ${type.fullName}());`);
  1254. } else {
  1255. this._builder.add(`const message = new ${type.fullName}();`);
  1256. this._builder.add('reader.start();');
  1257. this._builder.add('while (!reader.end()) {');
  1258. this._builder.indent();
  1259. this._builder.add('const tag = reader.tag();');
  1260. this._builder.add('switch (tag) {');
  1261. this._builder.indent();
  1262. for (const field of type.fields.values()) {
  1263. const variable = `message${protoc.Generator._propertyReference(field.name)}`;
  1264. this._builder.add(`case "${field.name}":`);
  1265. this._builder.indent();
  1266. // Map fields
  1267. if (field instanceof protoc.MapField) {
  1268. const value = field.type instanceof protoc.PrimitiveType ?
  1269. `reader.${field.type.name}()` :
  1270. `${field.type.fullName}.decodeText(reader)`;
  1271. this._builder.add(`reader.entry(${variable}, () => reader.${field.keyType.name}(), () => ${value});`);
  1272. } else if (field.repeated) { // Repeated fields
  1273. if (field.type instanceof protoc.Enum) {
  1274. this._builder.add(`reader.array(${variable}, () => reader.enum(${field.type.fullName}));`);
  1275. } else if (field.type instanceof protoc.PrimitiveType) {
  1276. this._builder.add(`reader.array(${variable}, () => reader.${field.type.name}());`);
  1277. } else if (field.type.fullName === 'google.protobuf.Any') {
  1278. this._builder.add(`reader.anyarray(${variable}, () => new ${field.type.fullName}());`);
  1279. } else {
  1280. this._builder.add(`${variable}.push(${field.type.fullName}.decodeText(reader));`);
  1281. }
  1282. // Non-repeated
  1283. } else if (field.type instanceof protoc.Enum) {
  1284. this._builder.add(`${variable} = reader.enum(${field.type.fullName});`);
  1285. } else if (field.type instanceof protoc.PrimitiveType) {
  1286. this._builder.add(`${variable} = reader.${field.type.name}();`);
  1287. } else {
  1288. this._builder.add(`${variable} = ${field.type.fullName}.decodeText(reader);`);
  1289. }
  1290. this._builder.add("break;");
  1291. this._builder.outdent();
  1292. }
  1293. this._builder.add("default:");
  1294. this._builder.indent();
  1295. this._builder.add("reader.field(tag, message);");
  1296. this._builder.add("break;");
  1297. this._builder.outdent();
  1298. this._builder.outdent();
  1299. this._builder.add('}');
  1300. this._builder.outdent();
  1301. this._builder.add('}');
  1302. for (const field of Array.from(type.fields.values()).filter((field) => field.required)) {
  1303. this._builder.add(`if (!Object.prototype.hasOwnProperty.call(message, "${field.name}")) {`);
  1304. this._builder.indent();
  1305. this._builder.add(`throw new Error("Expected '${field.name}'.");`);
  1306. this._builder.outdent();
  1307. this._builder.add('}');
  1308. }
  1309. this._builder.add('return message;');
  1310. }
  1311. this._builder.outdent();
  1312. this._builder.add('}');
  1313. /* eslint-enable indent */
  1314. }
  1315. _buildDecodeJsonFunction(type) {
  1316. /* eslint-disable indent */
  1317. this._builder.add(`static decodeJson(${type.fullName === 'google.protobuf.Any' || type.fields.size === 0 ? '' : 'obj'}) {`);
  1318. this._builder.indent();
  1319. if (type.fullName === 'google.protobuf.Any') {
  1320. this._builder.add("throw new Error('Any fields not implemented.');");
  1321. } else {
  1322. this._builder.add(`const message = new ${type.fullName}();`);
  1323. for (const field of type.fields.values()) {
  1324. const json = field.name.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
  1325. const source = `obj.${json}`;
  1326. const target = `message${protoc.Generator._propertyReference(field.name)}`;
  1327. if (!field.required) {
  1328. this._builder.add(`if ('${json}' in obj) {`);
  1329. this._builder.indent();
  1330. }
  1331. if (field instanceof protoc.MapField) {
  1332. this._builder.add(`for (const [key, value] of Object.entries(obj.${json})) {`);
  1333. this._builder.indent();
  1334. const value = field.type instanceof protoc.PrimitiveType ? `value` : `${field.type.fullName}.decodeJson(value)`;
  1335. this._builder.add(`${target}[key] = ${value};`);
  1336. this._builder.outdent();
  1337. this._builder.add(`}`);
  1338. } else if (field.repeated) {
  1339. if (field.type instanceof protoc.PrimitiveType) {
  1340. if (field.type.name === 'float' || field.type.name === 'double' || field.type.name === 'int32' || field.type.name === 'uint32') {
  1341. this._builder.add(`${target} = ${source}.map((obj) => Number(obj));`);
  1342. } else if (field.type.name === 'int64' || field.type.name === 'uint64' || field.type.name === 'fixed64' || field.type.name === 'sint64') {
  1343. this._builder.add(`${target} = ${source}.map((obj) => BigInt(obj));`);
  1344. } else if (field.type.name === 'bytes') {
  1345. this._builder.add(`${target} = ${source}.map((obj) => typeof obj === 'string' ? Uint8Array.from(atob(obj), (c) => c.charCodeAt(0)) : Uint8Array.from(obj));`);
  1346. } else if (field.type.name === 'string' || field.type.name === 'bool') {
  1347. this._builder.add(`${target} = ${source};`);
  1348. } else {
  1349. throw new protoc.Error(`Repeated primitive field type '${field.type.name}' not implemented.`);
  1350. }
  1351. } else if (field.type instanceof protoc.Enum) {
  1352. this._builder.add(`${target} = ${source}.map((key) => typeof key === 'string' ? ${field.type.fullName}[key] : key);`);
  1353. } else {
  1354. this._builder.add(`${target} = ${source}.map((obj) => ${field.type.fullName}.decodeJson(obj));`);
  1355. }
  1356. } else if (field.type instanceof protoc.PrimitiveType) {
  1357. if (field.type.name === 'float' || field.type.name === 'double' || field.type.name === 'int32' || field.type.name === 'uint32' || field.type.name === 'fixed32') {
  1358. this._builder.add(`${target} = Number(${source});`);
  1359. } else if (field.type.name === 'int64' || field.type.name === 'uint64' || field.type.name === 'fixed64' || field.type.name === 'sint64') {
  1360. this._builder.add(`${target} = BigInt(${source});`);
  1361. } else if (field.type.name === 'bytes') {
  1362. this._builder.add(`${target} = typeof ${source} === 'string' ? Uint8Array.from(atob(${source}), (c) => c.charCodeAt(0)) : Uint8Array.from(${source});`);
  1363. } else if (field.type.name === 'string' || field.type.name === 'bool') {
  1364. this._builder.add(`${target} = ${source};`);
  1365. } else {
  1366. throw new protoc.Error(`Primitive field type '${field.type.name}' not implemented.`);
  1367. }
  1368. } else if (field.type instanceof protoc.Enum) {
  1369. this._builder.add(`${target} = typeof ${source} === 'string' ? ${field.type.fullName}[${source}] : ${source};`);
  1370. } else {
  1371. this._builder.add(`${target} = ${field.type.fullName}.decodeJson(${source});`);
  1372. }
  1373. if (!field.required) {
  1374. this._builder.outdent();
  1375. this._builder.add('}');
  1376. }
  1377. }
  1378. this._builder.add('return message;');
  1379. }
  1380. this._builder.outdent();
  1381. this._builder.add('}');
  1382. /* eslint-enable indent */
  1383. }
  1384. static _isKeyword(name) {
  1385. return /^(?:do|if|in|for|let|new|try|var|case|else|enum|eval|false|null|this|true|void|with|break|catch|class|const|super|throw|while|yield|delete|export|import|public|return|static|switch|typeof|default|extends|finally|package|private|continue|debugger|function|arguments|interface|protected|implements|instanceof)$/.test(name);
  1386. }
  1387. static _escapeName(name) {
  1388. return protoc.Generator._isKeyword(name) ? `${name}_` : name;
  1389. }
  1390. static _propertyReference(name) {
  1391. if (!/^[$\w_]+$/.test(name)) {
  1392. return `["${name.replace(/\\/g, '\\\\').replace(/"/g, "\\\"")}"]`;
  1393. }
  1394. return `.${name}`;
  1395. }
  1396. };
  1397. protoc.Generator.StringBuilder = class {
  1398. constructor() {
  1399. this._indentation = '';
  1400. this._lines = [''];
  1401. this._newline = true;
  1402. }
  1403. indent() {
  1404. this._indentation += ' ';
  1405. }
  1406. outdent() {
  1407. if (this._indentation.length === 0) {
  1408. throw new protoc.Error('Invalid indentation.');
  1409. }
  1410. this._indentation = this._indentation.substring(0, this._indentation.length - 4);
  1411. }
  1412. add(text, newline) {
  1413. if (this._newline) {
  1414. if (text !== '') {
  1415. this._lines.push(this._indentation);
  1416. }
  1417. }
  1418. this._lines[this._lines.length - 1] = this._lines[this._lines.length - 1] + text + (newline === false ? '' : '\n');
  1419. this._newline = newline === false ? false : true;
  1420. }
  1421. toString() {
  1422. return this._lines.join('');
  1423. }
  1424. };
  1425. protoc.Error = class extends Error {
  1426. constructor(message) {
  1427. super(message);
  1428. this.name = 'Protocol Buffers Compiler Error';
  1429. }
  1430. };
  1431. const main = async (args) => {
  1432. const options = { verbose: false, root: 'default', out: '', text: false, paths: [], files: [] };
  1433. while (args.length > 0) {
  1434. const arg = args.shift();
  1435. switch (arg) {
  1436. case '--verbose':
  1437. options.verbose = true;
  1438. break;
  1439. case '--out':
  1440. options.out = args.shift();
  1441. break;
  1442. case '--root':
  1443. options.root = args.shift();
  1444. break;
  1445. case '--binary':
  1446. options.binary = true;
  1447. break;
  1448. case '--text':
  1449. options.text = true;
  1450. break;
  1451. case '--json':
  1452. options.json = true;
  1453. break;
  1454. case '--path':
  1455. options.paths.push(args.shift());
  1456. break;
  1457. default:
  1458. if (arg.startsWith('-')) {
  1459. throw new protoc.Error(`Invalid command line argument '${arg}'.`);
  1460. }
  1461. options.files.push(arg);
  1462. break;
  1463. }
  1464. }
  1465. try {
  1466. const root = new protoc.Root(options.root);
  1467. await root.load(options.paths, options.files);
  1468. const generator = new protoc.Generator(root, options);
  1469. if (options.out) {
  1470. await fs.writeFile(options.out, generator.content, 'utf-8');
  1471. }
  1472. } catch (error) {
  1473. if (error instanceof protoc.Error && !options.verbose) {
  1474. process.stderr.write(`${error.message}\n`);
  1475. } else {
  1476. process.stderr.write(`${error.stack}\n`);
  1477. }
  1478. process.exit(1);
  1479. }
  1480. process.exit(0);
  1481. };
  1482. if (typeof process === 'object' &&
  1483. Array.isArray(process.argv) && process.argv.length > 1 &&
  1484. path.basename(process.argv[1]) === 'protoc.js') {
  1485. const args = process.argv.slice(2);
  1486. await main(args);
  1487. }
  1488. export const Root = protoc.Root;