protoc.js 54 KB

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