From 62815d26d7f8c190bd5ebe5b9262efb73928acf7 Mon Sep 17 00:00:00 2001 From: Alina Sireneva Date: Wed, 4 Oct 2023 02:45:11 +0300 Subject: [PATCH] fix(tl): support multiple usages of the same flag --- packages/tl-utils/src/codegen/writer.ts | 57 +++++++++++++------ .../tl-utils/tests/codegen/writer.spec.ts | 14 +++++ 2 files changed, 53 insertions(+), 18 deletions(-) diff --git a/packages/tl-utils/src/codegen/writer.ts b/packages/tl-utils/src/codegen/writer.ts index 8cc76205..9f74defd 100644 --- a/packages/tl-utils/src/codegen/writer.ts +++ b/packages/tl-utils/src/codegen/writer.ts @@ -1,6 +1,6 @@ import { calculateStaticSizes } from '../calculator' import { computeConstructorIdFromEntry } from '../ctor-id' -import { TL_PRIMITIVES, TlEntry } from '../types' +import { TL_PRIMITIVES, TlArgument, TlEntry } from '../types' import { snakeToCamel } from './utils' export interface WriterCodegenOptions { @@ -70,37 +70,58 @@ export function generateWriterCodeForTlEntry(entry: TlEntry, params = DEFAULT_OP if (!bare) ret += `w.uint(${entry.id});` const flagsFields: Record = {} + const fieldConditions: Record = {} entry.arguments.forEach((arg) => { if (arg.type === '#') { ret += `var ${arg.name}=${includeFlags ? `v.${arg.name}` : '0'};` - entry.arguments.forEach((arg1) => { - const predicate = arg1.typeModifiers?.predicate + const usedByArgs = entry.arguments.filter((a) => a.typeModifiers?.predicate?.startsWith(arg.name + '.')) + const indexUsage: Record = {} - let s + usedByArgs.forEach((arg1) => { + const index = arg1.typeModifiers!.predicate!.split('.')[1] + if (!indexUsage[index]) indexUsage[index] = [] + indexUsage[index].push(arg1) + }) - if (!predicate || (s = predicate.split('.'))[0] !== arg.name) { - return - } - - const arg1Name = snakeToCamel(arg1.name) - - const bitIndex = parseInt(s[1]) + Object.entries(indexUsage).forEach(([index, args]) => { + const bitIndex = parseInt(index) if (isNaN(bitIndex) || bitIndex < 0 || bitIndex > 32) { - throw new Error(`Invalid predicate: ${predicate} - invalid bit`) + throw new Error(`Invalid predicate: ${arg.name}.${bitIndex} - invalid bit`) } + const conditions: string[] = [] + args.forEach((arg1) => { + const arg1Name = snakeToCamel(arg1.name) + + if (arg1.type === 'true') { + conditions.push(`v.${arg1Name}===true`) + } else if (arg1.typeModifiers?.isVector || arg1.typeModifiers?.isBareVector) { + ret += `var _${arg1Name}=v.${arg1Name}&&v.${arg1Name}.length;` + conditions.push(`_${arg1Name}`) + } else { + ret += `var _${arg1Name}=v.${arg1Name}!==undefined;` + conditions.push(`_${arg1Name}`) + } + }) + const action = `${arg.name}|=${1 << bitIndex};` + let condition: string - if (arg1.type === 'true') { - ret += `if(v.${arg1Name}===true)${action}` - } else if (arg1.typeModifiers?.isVector || arg1.typeModifiers?.isBareVector) { - ret += `var _${arg1Name}=v.${arg1Name}&&v.${arg1Name}.length;if(_${arg1Name})${action}` + if (conditions.length > 1) { + condition = `_${arg.name}_${bitIndex}` + ret += `var ${condition}=${conditions.join('||')};` } else { - ret += `var _${arg1Name}=v.${arg1Name}!==undefined;if(_${arg1Name})${action}` + condition = conditions[0] } + + ret += `if(${condition})${action}` + + args.forEach((arg) => { + fieldConditions[arg.name] = condition + }) }) ret += `w.uint(${arg.name});` @@ -118,7 +139,7 @@ export function generateWriterCodeForTlEntry(entry: TlEntry, params = DEFAULT_OP if (arg.typeModifiers?.predicate) { if (type === 'true') return // included in flags - ret += `if(_${argName})` + ret += `if(${fieldConditions[arg.name]})` } else { accessor = `h(v,'${argName}')` } diff --git a/packages/tl-utils/tests/codegen/writer.spec.ts b/packages/tl-utils/tests/codegen/writer.spec.ts index c90a5f74..3d456370 100644 --- a/packages/tl-utils/tests/codegen/writer.spec.ts +++ b/packages/tl-utils/tests/codegen/writer.spec.ts @@ -74,6 +74,20 @@ describe('generateWriterCodeForTlEntry', () => { ) }) + it('generates code for constructors with multiple fields using the same flag', () => { + test( + 'inputMediaPoll#f94e5f1 flags:# solution:flags.1?string solution_entities:flags.1?Vector = InputMedia;', + 'var flags=0;', + 'var _solution=v.solution!==undefined;', + 'var _solutionEntities=v.solutionEntities&&v.solutionEntities.length;', + 'var _flags_1=_solution||_solutionEntities;', + 'if(_flags_1)flags|=2;', + 'w.uint(flags);', + 'if(_flags_1)w.string(v.solution);', + 'if(_flags_1)w.vector(w.object,v.solutionEntities);', + ) + }) + it('generates code for constructors with vector arguments', () => { test( 'contacts.resolvedPeer#7f077ad9 peer:Peer chats:Vector users:Vector = contacts.ResolvedPeer;',