tdesktop/Telegram/SourceFiles/mtproto/generate.py
John Preston 08167a6a91 Removed #include "stdafx.h" from all files.
Currently the build without implicitly included precompiled header
is not supported anyway (because Qt MOC source files do not include
stdafx.h, they include plain headers).

So when we decide to support building without implicitly included
precompiled headers we'll have to fix all the headers anyway.
2017-03-04 12:27:52 +03:00

990 lines
44 KiB
Python

'''
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014 John Preston, https://desktop.telegram.org
'''
import glob
import re
import binascii
# define some checked flag conversions
# the key flag type should be a subset of the value flag type
# with exact the same names, then the key flag can be implicitly
# casted to the value flag type
parentFlags = {};
parentFlagsList = [];
def addChildParentFlags(child, parent):
parentFlagsList.append(child);
parentFlags[child] = parent;
addChildParentFlags('MTPDmessageService', 'MTPDmessage');
addChildParentFlags('MTPDupdateShortMessage', 'MTPDmessage');
addChildParentFlags('MTPDupdateShortChatMessage', 'MTPDmessage');
addChildParentFlags('MTPDupdateShortSentMessage', 'MTPDmessage');
addChildParentFlags('MTPDreplyKeyboardHide', 'MTPDreplyKeyboardMarkup');
addChildParentFlags('MTPDreplyKeyboardForceReply', 'MTPDreplyKeyboardMarkup');
addChildParentFlags('MTPDinputPeerNotifySettings', 'MTPDpeerNotifySettings');
addChildParentFlags('MTPDpeerNotifySettings', 'MTPDinputPeerNotifySettings');
addChildParentFlags('MTPDchannelForbidden', 'MTPDchannel');
# this is a map (key flags -> map (flag name -> flag bit))
# each key flag of parentFlags should be a subset of the value flag here
parentFlagsCheck = {};
layer = '';
funcs = 0
types = 0;
consts = 0
funcsNow = 0
enums = [];
funcsDict = {};
funcsList = [];
typesDict = {};
TypesDict = {};
typesList = [];
boxed = {};
funcsText = '';
typesText = '';
dataTexts = '';
creatorProxyText = '';
inlineMethods = '';
textSerializeInit = '';
textSerializeMethods = '';
forwards = '';
forwTypedefs = '';
out = open('scheme_auto.h', 'w')
out.write('/*\n');
out.write('Created from \'/SourceFiles/mtproto/scheme.tl\' by \'/SourceFiles/mtproto/generate.py\' script\n\n');
out.write('WARNING! All changes made in this file will be lost!\n\n');
out.write('This file is part of Telegram Desktop,\n');
out.write('the official desktop version of Telegram messaging app, see https://telegram.org\n');
out.write('\n');
out.write('Telegram Desktop is free software: you can redistribute it and/or modify\n');
out.write('it under the terms of the GNU General Public License as published by\n');
out.write('the Free Software Foundation, either version 3 of the License, or\n');
out.write('(at your option) any later version.\n');
out.write('\n');
out.write('It is distributed in the hope that it will be useful,\n');
out.write('but WITHOUT ANY WARRANTY; without even the implied warranty of\n');
out.write('MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n');
out.write('GNU General Public License for more details.\n');
out.write('\n');
out.write('In addition, as a special exception, the copyright holders give permission\n');
out.write('to link the code of portions of this program with the OpenSSL library.\n');
out.write('\n');
out.write('Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE\n');
out.write('Copyright (c) 2014 John Preston, https://desktop.telegram.org\n');
out.write('*/\n');
out.write('#pragma once\n\n#include "mtproto/core_types.h"\n');
with open('scheme.tl') as f:
for line in f:
layerline = re.match(r'// LAYER (\d+)', line)
if (layerline):
layer = 'static constexpr mtpPrime CurrentLayer = ' + layerline.group(1) + ';';
nocomment = re.match(r'^(.*?)//', line)
if (nocomment):
line = nocomment.group(1);
if (re.match(r'\-\-\-functions\-\-\-', line)):
funcsNow = 1;
continue;
if (re.match(r'\-\-\-types\-\-\-', line)):
funcsNow = 0;
continue;
if (re.match(r'^\s*$', line)):
continue;
nametype = re.match(r'([a-zA-Z\.0-9_]+)#([0-9a-f]+)([^=]*)=\s*([a-zA-Z\.<>0-9_]+);', line);
if (not nametype):
if (not re.match(r'vector#1cb5c415 \{t:Type\} # \[ t \] = Vector t;', line)):
print('Bad line found: ' + line);
continue;
name = nametype.group(1);
nameInd = name.find('.');
if (nameInd >= 0):
Name = name[0:nameInd] + '_' + name[nameInd + 1:nameInd + 2].upper() + name[nameInd + 2:];
name = name.replace('.', '_');
else:
Name = name[0:1].upper() + name[1:];
typeid = nametype.group(2);
while (len(typeid) > 0 and typeid[0] == '0'):
typeid = typeid[1:];
if (len(typeid) == 0):
typeid = '0';
typeid = '0x' + typeid;
cleanline = nametype.group(1) + nametype.group(3) + '= ' + nametype.group(4);
cleanline = re.sub(r' [a-zA-Z0-9_]+\:flags\.[0-9]+\?true', '', cleanline);
cleanline = cleanline.replace('<', ' ').replace('>', ' ').replace(' ', ' ');
cleanline = re.sub(r'^ ', '', cleanline);
cleanline = re.sub(r' $', '', cleanline);
cleanline = cleanline.replace(':bytes ', ':string ');
cleanline = cleanline.replace('?bytes ', '?string ');
cleanline = cleanline.replace('{', '');
cleanline = cleanline.replace('}', '');
countTypeId = binascii.crc32(binascii.a2b_qp(cleanline));
if (countTypeId < 0):
countTypeId += 2 ** 32;
countTypeId = '0x' + re.sub(r'^0x|L$', '', hex(countTypeId));
if (typeid != countTypeId):
print('Warning: counted ' + countTypeId + ' mismatch with provided ' + typeid + ' (' + cleanline + ')');
continue;
params = nametype.group(3);
restype = nametype.group(4);
if (restype.find('<') >= 0):
templ = re.match(r'^([vV]ector<)([A-Za-z0-9\._]+)>$', restype);
if (templ):
vectemplate = templ.group(2);
if (re.match(r'^[A-Z]', vectemplate) or re.match(r'^[a-zA-Z0-9]+_[A-Z]', vectemplate)):
restype = templ.group(1) + 'MTP' + vectemplate.replace('.', '_') + '>';
elif (vectemplate == 'int' or vectemplate == 'long' or vectemplate == 'string'):
restype = templ.group(1) + 'MTP' + vectemplate.replace('.', '_') + '>';
else:
foundmeta = '';
for metatype in typesDict:
for typedata in typesDict[metatype]:
if (typedata[0] == vectemplate):
foundmeta = metatype;
break;
if (len(foundmeta) > 0):
break;
if (len(foundmeta) > 0):
ptype = templ.group(1) + 'MTP' + foundmeta.replace('.', '_') + '>';
else:
print('Bad vector param: ' + vectemplate);
continue;
else:
print('Bad template type: ' + restype);
continue;
resType = restype.replace('.', '_');
if (restype.find('.') >= 0):
parts = re.match(r'([a-z]+)\.([A-Z][A-Za-z0-9<>\._]+)', restype)
if (parts):
restype = parts.group(1) + '_' + parts.group(2)[0:1].lower() + parts.group(2)[1:];
else:
print('Bad result type name with dot: ' + restype);
continue;
else:
if (re.match(r'^[A-Z]', restype)):
restype = restype[:1].lower() + restype[1:];
else:
print('Bad result type name: ' + restype);
continue;
boxed[resType] = restype;
boxed[Name] = name;
enums.append('\tmtpc_' + name + ' = ' + typeid);
paramsList = params.strip().split(' ');
prms = {};
conditions = {};
trivialConditions = {}; # true type
prmsList = [];
conditionsList = [];
isTemplate = hasFlags = hasTemplate = '';
for param in paramsList:
if (re.match(r'^\s*$', param)):
continue;
templ = re.match(r'^{([A-Za-z]+):Type}$', param);
if (templ):
hasTemplate = templ.group(1);
continue;
pnametype = re.match(r'([a-z_][a-z0-9_]*):([A-Za-z0-9<>\._]+|![a-zA-Z]+|\#|[a-z_][a-z0-9_]*\.[0-9]+\?[A-Za-z0-9<>\._]+)$', param);
if (not pnametype):
print('Bad param found: "' + param + '" in line: ' + line);
continue;
pname = pnametype.group(1);
ptypewide = pnametype.group(2);
if (re.match(r'^!([a-zA-Z]+)$', ptypewide)):
if ('!' + hasTemplate == ptypewide):
isTemplate = pname;
ptype = 'TQueryType';
else:
print('Bad template param name: "' + param + '" in line: ' + line);
continue;
elif (ptypewide == '#'):
hasFlags = pname;
if funcsNow:
ptype = 'flags<MTP' + name + '::Flags>';
else:
ptype = 'flags<MTPD' + name + '::Flags>';
else:
ptype = ptypewide;
if (ptype.find('?') >= 0):
pmasktype = re.match(r'([a-z_][a-z0-9_]*)\.([0-9]+)\?([A-Za-z0-9<>\._]+)', ptype);
if (not pmasktype or pmasktype.group(1) != hasFlags):
print('Bad param found: "' + param + '" in line: ' + line);
continue;
ptype = pmasktype.group(3);
if (ptype.find('<') >= 0):
templ = re.match(r'^([vV]ector<)([A-Za-z0-9\._]+)>$', ptype);
if (templ):
vectemplate = templ.group(2);
if (re.match(r'^[A-Z]', vectemplate) or re.match(r'^[a-zA-Z0-9]+_[A-Z]', vectemplate)):
ptype = templ.group(1) + 'MTP' + vectemplate.replace('.', '_') + '>';
elif (vectemplate == 'int' or vectemplate == 'long' or vectemplate == 'string'):
ptype = templ.group(1) + 'MTP' + vectemplate.replace('.', '_') + '>';
else:
foundmeta = '';
for metatype in typesDict:
for typedata in typesDict[metatype]:
if (typedata[0] == vectemplate):
foundmeta = metatype;
break;
if (len(foundmeta) > 0):
break;
if (len(foundmeta) > 0):
ptype = templ.group(1) + 'MTP' + foundmeta.replace('.', '_') + '>';
else:
print('Bad vector param: ' + vectemplate);
continue;
else:
print('Bad template type: ' + ptype);
continue;
if (not pname in conditions):
conditionsList.append(pname);
conditions[pname] = pmasktype.group(2);
if (ptype == 'true'):
trivialConditions[pname] = 1;
elif (ptype.find('<') >= 0):
templ = re.match(r'^([vV]ector<)([A-Za-z0-9\._]+)>$', ptype);
if (templ):
vectemplate = templ.group(2);
if (re.match(r'^[A-Z]', vectemplate) or re.match(r'^[a-zA-Z0-9]+_[A-Z]', vectemplate)):
ptype = templ.group(1) + 'MTP' + vectemplate.replace('.', '_') + '>';
elif (vectemplate == 'int' or vectemplate == 'long' or vectemplate == 'string'):
ptype = templ.group(1) + 'MTP' + vectemplate.replace('.', '_') + '>';
else:
foundmeta = '';
for metatype in typesDict:
for typedata in typesDict[metatype]:
if (typedata[0] == vectemplate):
foundmeta = metatype;
break;
if (len(foundmeta) > 0):
break;
if (len(foundmeta) > 0):
ptype = templ.group(1) + 'MTP' + foundmeta.replace('.', '_') + '>';
else:
print('Bad vector param: ' + vectemplate);
continue;
else:
print('Bad template type: ' + ptype);
continue;
prmsList.append(pname);
prms[pname] = ptype.replace('.', '_');
if (isTemplate == '' and resType == 'X'):
print('Bad response type "X" in "' + name +'" in line: ' + line);
continue;
if funcsNow:
if (isTemplate != ''):
funcsText += '\ntemplate <typename TQueryType>';
funcsText += '\nclass MTP' + name + ' { // RPC method \'' + nametype.group(1) + '\'\n'; # class
funcsText += 'public:\n';
prmsStr = [];
prmsInit = [];
prmsNames = [];
if (hasFlags != ''):
funcsText += '\tenum class Flag : int32 {\n';
maxbit = 0;
parentFlagsCheck['MTP' + name] = {};
for paramName in conditionsList:
funcsText += '\t\tf_' + paramName + ' = (1 << ' + conditions[paramName] + '),\n';
parentFlagsCheck['MTP' + name][paramName] = conditions[paramName];
maxbit = max(maxbit, int(conditions[paramName]));
if (maxbit > 0):
funcsText += '\n';
funcsText += '\t\tMAX_FIELD = (1 << ' + str(maxbit) + '),\n';
funcsText += '\t};\n';
funcsText += '\tQ_DECLARE_FLAGS(Flags, Flag);\n';
funcsText += '\tfriend inline Flags operator~(Flag v) { return QFlag(~static_cast<int32>(v)); }\n';
funcsText += '\n';
if (len(conditions)):
for paramName in conditionsList:
if (paramName in trivialConditions):
funcsText += '\tbool is_' + paramName + '() const { return v' + hasFlags + '.v & Flag::f_' + paramName + '; }\n';
else:
funcsText += '\tbool has_' + paramName + '() const { return v' + hasFlags + '.v & Flag::f_' + paramName + '; }\n';
funcsText += '\n';
if (len(prms) > len(trivialConditions)):
for paramName in prmsList:
if (paramName in trivialConditions):
continue;
paramType = prms[paramName];
prmsInit.append('v' + paramName + '(_' + paramName + ')');
prmsNames.append('_' + paramName);
if (paramName == isTemplate):
ptypeFull = paramType;
else:
ptypeFull = 'MTP' + paramType;
funcsText += '\t' + ptypeFull + ' v' + paramName + ';\n';
if (paramType in ['int', 'Int', 'bool', 'Bool', 'flags<Flags>']):
prmsStr.append(ptypeFull + ' _' + paramName);
else:
prmsStr.append('const ' + ptypeFull + ' &_' + paramName);
funcsText += '\n';
funcsText += '\tMTP' + name + '() {\n\t}\n'; # constructor
funcsText += '\tMTP' + name + '(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_' + name + ') {\n\t\tread(from, end, cons);\n\t}\n'; # stream constructor
if (len(prms) > len(trivialConditions)):
funcsText += '\tMTP' + name + '(' + ', '.join(prmsStr) + ') : ' + ', '.join(prmsInit) + ' {\n\t}\n';
funcsText += '\n';
funcsText += '\tuint32 innerLength() const {\n'; # count size
size = [];
for k in prmsList:
v = prms[k];
if (k in conditionsList):
if (not k in trivialConditions):
size.append('(has_' + k + '() ? v' + k + '.innerLength() : 0)');
else:
size.append('v' + k + '.innerLength()');
if (not len(size)):
size.append('0');
funcsText += '\t\treturn ' + ' + '.join(size) + ';\n';
funcsText += '\t}\n';
funcsText += '\tmtpTypeId type() const {\n\t\treturn mtpc_' + name + ';\n\t}\n'; # type id
funcsText += '\tvoid read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_' + name + ') {\n'; # read method
for k in prmsList:
v = prms[k];
if (k in conditionsList):
if (not k in trivialConditions):
funcsText += '\t\tif (has_' + k + '()) { v' + k + '.read(from, end); } else { v' + k + ' = MTP' + v + '(); }\n';
else:
funcsText += '\t\tv' + k + '.read(from, end);\n';
funcsText += '\t}\n';
funcsText += '\tvoid write(mtpBuffer &to) const {\n'; # write method
for k in prmsList:
v = prms[k];
if (k in conditionsList):
if (not k in trivialConditions):
funcsText += '\t\tif (has_' + k + '()) v' + k + '.write(to);\n';
else:
funcsText += '\t\tv' + k + '.write(to);\n';
funcsText += '\t}\n';
if (isTemplate != ''):
funcsText += '\n\ttypedef typename TQueryType::ResponseType ResponseType;\n';
else:
funcsText += '\n\ttypedef MTP' + resType + ' ResponseType;\n'; # method return type
funcsText += '};\n'; # class ending
if (len(conditionsList)):
funcsText += 'Q_DECLARE_OPERATORS_FOR_FLAGS(MTP' + name + '::Flags)\n\n';
if (isTemplate != ''):
funcsText += 'template <typename TQueryType>\n';
funcsText += 'class MTP' + Name + ' : public MTPBoxed<MTP' + name + '<TQueryType> > {\n';
funcsText += 'public:\n';
funcsText += '\tMTP' + Name + '() {\n\t}\n';
funcsText += '\tMTP' + Name + '(const MTP' + name + '<TQueryType> &v) : MTPBoxed<MTP' + name + '<TQueryType> >(v) {\n\t}\n';
if (len(prms) > len(trivialConditions)):
funcsText += '\tMTP' + Name + '(' + ', '.join(prmsStr) + ') : MTPBoxed<MTP' + name + '<TQueryType> >(MTP' + name + '<TQueryType>(' + ', '.join(prmsNames) + ')) {\n\t}\n';
funcsText += '};\n';
else:
funcsText += 'class MTP' + Name + ' : public MTPBoxed<MTP' + name + '> {\n';
funcsText += 'public:\n';
funcsText += '\tMTP' + Name + '() {\n\t}\n';
funcsText += '\tMTP' + Name + '(const MTP' + name + ' &v) : MTPBoxed<MTP' + name + '>(v) {\n\t}\n';
funcsText += '\tMTP' + Name + '(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = 0) : MTPBoxed<MTP' + name + '>(from, end, cons) {\n\t}\n';
if (len(prms) > len(trivialConditions)):
funcsText += '\tMTP' + Name + '(' + ', '.join(prmsStr) + ') : MTPBoxed<MTP' + name + '>(MTP' + name + '(' + ', '.join(prmsNames) + ')) {\n\t}\n';
funcsText += '};\n';
funcs = funcs + 1;
if (not restype in funcsDict):
funcsList.append(restype);
funcsDict[restype] = [];
# TypesDict[restype] = resType;
funcsDict[restype].append([name, typeid, prmsList, prms, hasFlags, conditionsList, conditions, trivialConditions]);
else:
if (isTemplate != ''):
print('Template types not allowed: "' + resType + '" in line: ' + line);
continue;
if (not restype in typesDict):
typesList.append(restype);
typesDict[restype] = [];
TypesDict[restype] = resType;
typesDict[restype].append([name, typeid, prmsList, prms, hasFlags, conditionsList, conditions, trivialConditions]);
consts = consts + 1;
# text serialization: types and funcs
def addTextSerialize(lst, dct, dataLetter):
result = '';
for restype in lst:
v = dct[restype];
for data in v:
name = data[0];
prmsList = data[2];
prms = data[3];
hasFlags = data[4];
conditionsList = data[5];
conditions = data[6];
trivialConditions = data[7];
result += 'void _serialize_' + name + '(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 iflag) {\n';
if (len(conditions)):
result += '\tMTP' + dataLetter + name + '::Flags flag(iflag);\n\n';
if (len(prms)):
result += '\tif (stage) {\n';
result += '\t\tto.add(",\\n").addSpaces(lev);\n';
result += '\t} else {\n';
result += '\t\tto.add("{ ' + name + '");\n';
result += '\t\tto.add("\\n").addSpaces(lev);\n';
result += '\t}\n';
result += '\tswitch (stage) {\n';
stage = 0;
for k in prmsList:
v = prms[k];
result += '\tcase ' + str(stage) + ': to.add(" ' + k + ': "); ++stages.back(); ';
if (k == hasFlags):
result += 'if (start >= end) throw Exception("start >= end in flags"); else flags.back() = *start; ';
if (k in trivialConditions):
result += 'if (flag & MTP' + dataLetter + name + '::Flag::f_' + k + ') { ';
result += 'to.add("YES [ BY BIT ' + conditions[k] + ' IN FIELD ' + hasFlags + ' ]"); ';
result += '} else { to.add("[ SKIPPED BY BIT ' + conditions[k] + ' IN FIELD ' + hasFlags + ' ]"); } ';
else:
if (k in conditions):
result += 'if (flag & MTP' + dataLetter + name + '::Flag::f_' + k + ') { ';
result += 'types.push_back(';
vtypeget = re.match(r'^[Vv]ector<MTP([A-Za-z0-9\._]+)>', v);
if (vtypeget):
if (not re.match(r'^[A-Z]', v)):
result += 'mtpc_vector';
else:
result += '0';
restype = vtypeget.group(1);
try:
if boxed[restype]:
restype = 0;
except KeyError:
if re.match(r'^[A-Z]', restype):
restype = 0;
else:
restype = v;
try:
if boxed[restype]:
restype = 0;
except KeyError:
if re.match(r'^[A-Z]', restype):
restype = 0;
if (restype):
try:
conses = typesDict[restype];
if (len(conses) > 1):
print('Complex bare type found: "' + restype + '" trying to serialize "' + k + '" of type "' + v + '"');
continue;
if (vtypeget):
result += '); vtypes.push_back(';
result += 'mtpc_' + conses[0][0];
if (not vtypeget):
result += '); vtypes.push_back(0';
except KeyError:
if (vtypeget):
result += '); vtypes.push_back(';
if (re.match(r'^flags<', restype)):
result += 'mtpc_flags';
else:
result += 'mtpc_' + restype + '+0';
if (not vtypeget):
result += '); vtypes.push_back(0';
else:
result += '0); vtypes.push_back(0';
result += '); stages.push_back(0); flags.push_back(0); ';
if (k in conditions):
result += '} else { to.add("[ SKIPPED BY BIT ' + conditions[k] + ' IN FIELD ' + hasFlags + ' ]"); } ';
result += 'break;\n';
stage = stage + 1;
result += '\tdefault: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;\n';
result += '\t}\n';
else:
result += '\tto.add("{ ' + name + ' }"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back();\n';
result += '}\n\n';
return result;
# text serialization: types and funcs
def addTextSerializeInit(lst, dct):
result = '';
for restype in lst:
v = dct[restype];
for data in v:
name = data[0];
result += '\t\t_serializers.insert(mtpc_' + name + ', _serialize_' + name + ');\n';
return result;
textSerializeMethods += addTextSerialize(typesList, typesDict, 'D');
textSerializeInit += addTextSerializeInit(typesList, typesDict) + '\n';
textSerializeMethods += addTextSerialize(funcsList, funcsDict, '');
textSerializeInit += addTextSerializeInit(funcsList, funcsDict) + '\n';
for restype in typesList:
v = typesDict[restype];
resType = TypesDict[restype];
withData = 0;
creatorsText = '';
constructsText = '';
constructsInline = '';
forwards += 'class MTP' + restype + ';\n';
forwTypedefs += 'typedef MTPBoxed<MTP' + restype + '> MTP' + resType + ';\n';
withType = (len(v) > 1);
switchLines = '';
friendDecl = '';
getters = '';
reader = '';
writer = '';
sizeList = [];
sizeFast = '';
newFast = '';
sizeCases = '';
for data in v:
name = data[0];
typeid = data[1];
prmsList = data[2];
prms = data[3];
hasFlags = data[4];
conditionsList = data[5];
conditions = data[6];
trivialConditions = data[7];
dataText = '';
dataText += '\nclass MTPD' + name + ' : public mtpDataImpl<MTPD' + name + '> {\n'; # data class
dataText += 'public:\n';
sizeList = [];
creatorParams = [];
creatorParamsList = [];
readText = '';
writeText = '';
if (hasFlags != ''):
dataText += '\tenum class Flag : int32 {\n';
maxbit = 0;
parentFlagsCheck['MTPD' + name] = {};
for paramName in conditionsList:
dataText += '\t\tf_' + paramName + ' = (1 << ' + conditions[paramName] + '),\n';
parentFlagsCheck['MTPD' + name][paramName] = conditions[paramName];
maxbit = max(maxbit, int(conditions[paramName]));
if (maxbit > 0):
dataText += '\n';
dataText += '\t\tMAX_FIELD = (1 << ' + str(maxbit) + '),\n';
dataText += '\t};\n';
dataText += '\tQ_DECLARE_FLAGS(Flags, Flag);\n';
dataText += '\tfriend inline Flags operator~(Flag v) { return QFlag(~static_cast<int32>(v)); }\n';
dataText += '\n';
if (len(conditions)):
for paramName in conditionsList:
if (paramName in trivialConditions):
dataText += '\tbool is_' + paramName + '() const { return v' + hasFlags + '.v & Flag::f_' + paramName + '; }\n';
else:
dataText += '\tbool has_' + paramName + '() const { return v' + hasFlags + '.v & Flag::f_' + paramName + '; }\n';
dataText += '\n';
dataText += '\tMTPD' + name + '() {\n\t}\n'; # default constructor
switchLines += '\t\tcase mtpc_' + name + ': '; # for by-type-id type constructor
if (len(prms) > len(trivialConditions)):
switchLines += 'setData(new MTPD' + name + '()); ';
withData = 1;
getters += '\n\tMTPD' + name + ' &_' + name + '() {\n'; # splitting getter
if (withType):
getters += '\t\tt_assert(data != nullptr && _type == mtpc_' + name + ');\n';
else:
getters += '\t\tt_assert(data != nullptr);\n';
getters += '\t\tsplit();\n';
getters += '\t\treturn *(MTPD' + name + '*)data;\n';
getters += '\t}\n';
getters += '\tconst MTPD' + name + ' &c_' + name + '() const {\n'; # const getter
if (withType):
getters += '\t\tt_assert(data != nullptr && _type == mtpc_' + name + ');\n';
else:
getters += '\t\tt_assert(data != nullptr);\n';
getters += '\t\treturn *(const MTPD' + name + '*)data;\n';
getters += '\t}\n';
constructsText += '\texplicit MTP' + restype + '(MTPD' + name + ' *_data);\n'; # by-data type constructor
constructsInline += 'inline MTP' + restype + '::MTP' + restype + '(MTPD' + name + ' *_data) : mtpDataOwner(_data)';
if (withType):
constructsInline += ', _type(mtpc_' + name + ')';
constructsInline += ' {\n}\n';
dataText += '\tMTPD' + name + '('; # params constructor
prmsStr = [];
prmsInit = [];
for paramName in prmsList:
if (paramName in trivialConditions):
continue;
paramType = prms[paramName];
if (paramType in ['int', 'Int', 'bool', 'Bool']):
prmsStr.append('MTP' + paramType + ' _' + paramName);
creatorParams.append('MTP' + paramType + ' _' + paramName);
else:
prmsStr.append('const MTP' + paramType + ' &_' + paramName);
creatorParams.append('const MTP' + paramType + ' &_' + paramName);
creatorParamsList.append('_' + paramName);
prmsInit.append('v' + paramName + '(_' + paramName + ')');
if (withType):
readText += '\t\t';
writeText += '\t\t';
if (paramName in conditions):
readText += '\tif (v.has_' + paramName + '()) { v.v' + paramName + '.read(from, end); } else { v.v' + paramName + ' = MTP' + paramType + '(); }\n';
writeText += '\tif (v.has_' + paramName + '()) v.v' + paramName + '.write(to);\n';
sizeList.append('(v.has_' + paramName + '() ? v.v' + paramName + '.innerLength() : 0)');
else:
readText += '\tv.v' + paramName + '.read(from, end);\n';
writeText += '\tv.v' + paramName + '.write(to);\n';
sizeList.append('v.v' + paramName + '.innerLength()');
forwards += 'class MTPD' + name + ';\n'; # data class forward declaration
dataText += ', '.join(prmsStr) + ') : ' + ', '.join(prmsInit) + ' {\n\t}\n';
dataText += '\n';
for paramName in prmsList: # fields declaration
if (paramName in trivialConditions):
continue;
paramType = prms[paramName];
dataText += '\tMTP' + paramType + ' v' + paramName + ';\n';
sizeCases += '\t\tcase mtpc_' + name + ': {\n';
sizeCases += '\t\t\tconst MTPD' + name + ' &v(c_' + name + '());\n';
sizeCases += '\t\t\treturn ' + ' + '.join(sizeList) + ';\n';
sizeCases += '\t\t}\n';
sizeFast = '\tconst MTPD' + name + ' &v(c_' + name + '());\n\treturn ' + ' + '.join(sizeList) + ';\n';
newFast = 'new MTPD' + name + '()';
else:
sizeFast = '\treturn 0;\n';
switchLines += 'break;\n';
dataText += '};\n'; # class ending
if (len(prms) > len(trivialConditions)):
dataTexts += dataText; # add data class
if (not friendDecl):
friendDecl += '\tfriend class MTP::internal::TypeCreator;\n';
creatorProxyText += '\tinline static MTP' + restype + ' new_' + name + '(' + ', '.join(creatorParams) + ') {\n';
if (len(prms) > len(trivialConditions)): # creator with params
creatorProxyText += '\t\treturn MTP' + restype + '(new MTPD' + name + '(' + ', '.join(creatorParamsList) + '));\n';
else:
if (withType): # creator by type
creatorProxyText += '\t\treturn MTP' + restype + '(mtpc_' + name + ');\n';
else: # single creator
creatorProxyText += '\t\treturn MTP' + restype + '();\n';
creatorProxyText += '\t}\n';
if (len(conditionsList)):
creatorsText += 'Q_DECLARE_OPERATORS_FOR_FLAGS(MTPD' + name + '::Flags)\n';
creatorsText += 'inline MTP' + restype + ' MTP_' + name + '(' + ', '.join(creatorParams) + ') {\n';
creatorsText += '\treturn MTP::internal::TypeCreator::new_' + name + '(' + ', '.join(creatorParamsList) + ');\n';
creatorsText += '}\n';
if (withType):
reader += '\t\tcase mtpc_' + name + ': _type = cons; '; # read switch line
if (len(prms) > len(trivialConditions)):
reader += '{\n';
reader += '\t\t\tif (!data) setData(new MTPD' + name + '());\n';
reader += '\t\t\tMTPD' + name + ' &v(_' + name + '());\n';
reader += readText;
reader += '\t\t} break;\n';
writer += '\t\tcase mtpc_' + name + ': {\n'; # write switch line
writer += '\t\t\tconst MTPD' + name + ' &v(c_' + name + '());\n';
writer += writeText;
writer += '\t\t} break;\n';
else:
reader += 'break;\n';
else:
if (len(prms) > len(trivialConditions)):
reader += '\n\tif (!data) setData(new MTPD' + name + '());\n';
reader += '\tMTPD' + name + ' &v(_' + name + '());\n';
reader += readText;
writer += '\tconst MTPD' + name + ' &v(c_' + name + '());\n';
writer += writeText;
forwards += '\n';
typesText += '\nclass MTP' + restype; # type class declaration
if (withData):
typesText += ' : private mtpDataOwner'; # if has data fields
typesText += ' {\n';
typesText += 'public:\n';
typesText += '\tMTP' + restype + '()'; # default constructor
inits = [];
if (withType):
if (withData):
inits.append('mtpDataOwner(0)');
inits.append('_type(0)');
else:
if (withData):
inits.append('mtpDataOwner(' + newFast + ')');
if (withData and not withType):
typesText += ';\n';
inlineMethods += '\ninline MTP' + restype + '::MTP' + restype + '()';
if (inits):
inlineMethods += ' : ' + ', '.join(inits);
inlineMethods += ' {\n}\n';
else:
if (inits):
typesText += ' : ' + ', '.join(inits);
typesText += ' {\n\t}\n';
inits = [];
if (withData):
inits.append('mtpDataOwner(0)');
if (withType):
inits.append('_type(0)');
typesText += '\tMTP' + restype + '(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons';
if (not withType):
typesText += ' = mtpc_' + name;
typesText += ')'; # read constructor
if (inits):
typesText += ' : ' + ', '.join(inits);
typesText += ' {\n\t\tread(from, end, cons);\n\t}\n';
if (withData):
typesText += getters;
typesText += '\n\tuint32 innerLength() const;\n'; # size method
inlineMethods += '\ninline uint32 MTP' + restype + '::innerLength() const {\n';
if (withType and sizeCases):
inlineMethods += '\tswitch (_type) {\n';
inlineMethods += sizeCases;
inlineMethods += '\t}\n';
inlineMethods += '\treturn 0;\n';
else:
inlineMethods += sizeFast;
inlineMethods += '}\n';
typesText += '\tmtpTypeId type() const;\n'; # type id method
inlineMethods += 'inline mtpTypeId MTP' + restype + '::type() const {\n';
if (withType):
inlineMethods += '\tt_assert(_type != 0);\n';
inlineMethods += '\treturn _type;\n';
else:
inlineMethods += '\treturn mtpc_' + v[0][0] + ';\n';
inlineMethods += '}\n';
typesText += '\tvoid read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons'; # read method
if (not withType):
typesText += ' = mtpc_' + name;
typesText += ');\n';
inlineMethods += 'inline void MTP' + restype + '::read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons) {\n';
if (withData):
if (withType):
inlineMethods += '\tif (cons != _type) setData(0);\n';
else:
inlineMethods += '\tif (cons != mtpc_' + v[0][0] + ') throw mtpErrorUnexpected(cons, "MTP' + restype + '");\n';
if (withType):
inlineMethods += '\tswitch (cons) {\n'
inlineMethods += reader;
inlineMethods += '\t\tdefault: throw mtpErrorUnexpected(cons, "MTP' + restype + '");\n';
inlineMethods += '\t}\n';
else:
inlineMethods += reader;
inlineMethods += '}\n';
typesText += '\tvoid write(mtpBuffer &to) const;\n'; # write method
inlineMethods += 'inline void MTP' + restype + '::write(mtpBuffer &to) const {\n';
if (withType and writer != ''):
inlineMethods += '\tswitch (_type) {\n';
inlineMethods += writer;
inlineMethods += '\t}\n';
else:
inlineMethods += writer;
inlineMethods += '}\n';
typesText += '\n\ttypedef void ResponseType;\n'; # no response types declared
typesText += '\nprivate:\n'; # private constructors
if (withType): # by-type-id constructor
typesText += '\texplicit MTP' + restype + '(mtpTypeId type);\n';
inlineMethods += 'inline MTP' + restype + '::MTP' + restype + '(mtpTypeId type) : ';
if (withData):
inlineMethods += 'mtpDataOwner(0), ';
inlineMethods += '_type(type)';
inlineMethods += ' {\n';
inlineMethods += '\tswitch (type) {\n'; # type id check
inlineMethods += switchLines;
inlineMethods += '\t\tdefault: throw mtpErrorBadTypeId(type, "MTP' + restype + '");\n\t}\n';
inlineMethods += '}\n'; # by-type-id constructor end
if (withData):
typesText += constructsText;
inlineMethods += constructsInline;
if (friendDecl):
typesText += '\n' + friendDecl;
if (withType):
typesText += '\n\tmtpTypeId _type;\n'; # type field var
typesText += '};\n'; # type class ended
inlineMethods += creatorsText;
typesText += 'typedef MTPBoxed<MTP' + restype + '> MTP' + resType + ';\n'; # boxed type definition
for childName in parentFlagsList:
parentName = parentFlags[childName];
for flag in parentFlagsCheck[childName]:
if (not flag in parentFlagsCheck[parentName]):
print('Flag ' + flag + ' not found in ' + parentName + ' which should be a flags-parent of ' + childName);
error
elif (parentFlagsCheck[childName][flag] != parentFlagsCheck[parentName][flag]):
print('Flag ' + flag + ' has different value in ' + parentName + ' which should be a flags-parent of ' + childName);
error
inlineMethods += 'inline ' + parentName + '::Flags mtpCastFlags(' + childName + '::Flags flags) { return ' + parentName + '::Flags(QFlag(flags)); }\n';
inlineMethods += 'inline ' + parentName + '::Flags mtpCastFlags(MTPflags<' + childName + '::Flags> flags) { return mtpCastFlags(flags.v); }\n';
# manual types added here
textSerializeMethods += 'void _serialize_rpc_result(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 iflag) {\n';
textSerializeMethods += '\tif (stage) {\n';
textSerializeMethods += '\t\tto.add(",\\n").addSpaces(lev);\n';
textSerializeMethods += '\t} else {\n';
textSerializeMethods += '\t\tto.add("{ rpc_result");\n';
textSerializeMethods += '\t\tto.add("\\n").addSpaces(lev);\n';
textSerializeMethods += '\t}\n';
textSerializeMethods += '\tswitch (stage) {\n';
textSerializeMethods += '\tcase 0: to.add(" req_msg_id: "); ++stages.back(); types.push_back(mtpc_long); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;\n';
textSerializeMethods += '\tcase 1: to.add(" result: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;\n';
textSerializeMethods += '\tdefault: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;\n';
textSerializeMethods += '\t}\n';
textSerializeMethods += '}\n\n';
textSerializeInit += '\t\t_serializers.insert(mtpc_rpc_result, _serialize_rpc_result);\n';
textSerializeMethods += 'void _serialize_msg_container(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 iflag) {\n';
textSerializeMethods += '\tif (stage) {\n';
textSerializeMethods += '\t\tto.add(",\\n").addSpaces(lev);\n';
textSerializeMethods += '\t} else {\n';
textSerializeMethods += '\t\tto.add("{ msg_container");\n';
textSerializeMethods += '\t\tto.add("\\n").addSpaces(lev);\n';
textSerializeMethods += '\t}\n';
textSerializeMethods += '\tswitch (stage) {\n';
textSerializeMethods += '\tcase 0: to.add(" messages: "); ++stages.back(); types.push_back(mtpc_vector); vtypes.push_back(mtpc_core_message); stages.push_back(0); flags.push_back(0); break;\n';
textSerializeMethods += '\tdefault: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;\n';
textSerializeMethods += '\t}\n';
textSerializeMethods += '}\n\n';
textSerializeInit += '\t\t_serializers.insert(mtpc_msg_container, _serialize_msg_container);\n';
textSerializeMethods += 'void _serialize_core_message(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 iflag) {\n';
textSerializeMethods += '\tif (stage) {\n';
textSerializeMethods += '\t\tto.add(",\\n").addSpaces(lev);\n';
textSerializeMethods += '\t} else {\n';
textSerializeMethods += '\t\tto.add("{ core_message");\n';
textSerializeMethods += '\t\tto.add("\\n").addSpaces(lev);\n';
textSerializeMethods += '\t}\n';
textSerializeMethods += '\tswitch (stage) {\n';
textSerializeMethods += '\tcase 0: to.add(" msg_id: "); ++stages.back(); types.push_back(mtpc_long); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;\n';
textSerializeMethods += '\tcase 1: to.add(" seq_no: "); ++stages.back(); types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;\n';
textSerializeMethods += '\tcase 2: to.add(" bytes: "); ++stages.back(); types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;\n';
textSerializeMethods += '\tcase 3: to.add(" body: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;\n';
textSerializeMethods += '\tdefault: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;\n';
textSerializeMethods += '\t}\n';
textSerializeMethods += '}\n\n';
textSerializeInit += '\t\t_serializers.insert(mtpc_core_message, _serialize_core_message);\n';
textSerializeFull = '\nvoid mtpTextSerializeType(MTPStringLogger &to, const mtpPrime *&from, const mtpPrime *end, mtpPrime cons, uint32 level, mtpPrime vcons) {\n';
textSerializeFull += '\tif (_serializers.isEmpty()) initTextSerializers();\n\n';
textSerializeFull += '\tQVector<mtpTypeId> types, vtypes;\n';
textSerializeFull += '\tQVector<int32> stages, flags;\n';
textSerializeFull += '\ttypes.reserve(20); vtypes.reserve(20); stages.reserve(20); flags.reserve(20);\n';
textSerializeFull += '\ttypes.push_back(mtpTypeId(cons)); vtypes.push_back(mtpTypeId(vcons)); stages.push_back(0); flags.push_back(0);\n\n';
textSerializeFull += '\tconst mtpPrime *start = from;\n';
textSerializeFull += '\tmtpTypeId type = cons, vtype = vcons;\n';
textSerializeFull += '\tint32 stage = 0, flag = 0;\n\n';
textSerializeFull += '\twhile (!types.isEmpty()) {\n';
textSerializeFull += '\t\ttype = types.back();\n';
textSerializeFull += '\t\tvtype = vtypes.back();\n';
textSerializeFull += '\t\tstage = stages.back();\n';
textSerializeFull += '\t\tflag = flags.back();\n';
textSerializeFull += '\t\tif (!type) {\n';
textSerializeFull += '\t\t\tif (from >= end) {\n';
textSerializeFull += '\t\t\t\tthrow Exception("from >= end");\n';
textSerializeFull += '\t\t\t} else if (stage) {\n';
textSerializeFull += '\t\t\t\tthrow Exception("unknown type on stage > 0");\n';
textSerializeFull += '\t\t\t}\n';
textSerializeFull += '\t\t\ttypes.back() = type = *from;\n';
textSerializeFull += '\t\t\tstart = ++from;\n';
textSerializeFull += '\t\t}\n\n';
textSerializeFull += '\t\tint32 lev = level + types.size() - 1;\n';
textSerializeFull += '\t\tTextSerializers::const_iterator it = _serializers.constFind(type);\n';
textSerializeFull += '\t\tif (it != _serializers.cend()) {\n';
textSerializeFull += '\t\t\t(*it.value())(to, stage, lev, types, vtypes, stages, flags, start, end, flag);\n';
textSerializeFull += '\t\t} else {\n';
textSerializeFull += '\t\t\tmtpTextSerializeCore(to, from, end, type, lev, vtype);\n';
textSerializeFull += '\t\t\ttypes.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back();\n';
textSerializeFull += '\t\t}\n';
textSerializeFull += '\t}\n';
textSerializeFull += '}\n';
out.write('\n// Creator current layer and proxy class declaration\n');
out.write('namespace MTP {\nnamespace internal {\n\n' + layer + '\n\n');
out.write('class TypeCreator;\n\n} // namespace internal\n} // namespace MTP\n');
out.write('\n// Type id constants\nenum {\n' + ',\n'.join(enums) + '\n};\n');
out.write('\n// Type forward declarations\n' + forwards);
out.write('\n// Boxed types definitions\n' + forwTypedefs);
out.write('\n// Type classes definitions\n' + typesText);
out.write('\n// Type constructors with data\n' + dataTexts);
out.write('\n// RPC methods\n' + funcsText);
out.write('\n// Creator proxy class definition\nnamespace MTP {\nnamespace internal {\n\nclass TypeCreator {\npublic:\n' + creatorProxyText + '\t};\n\n} // namespace internal\n} // namespace MTP\n');
out.write('\n// Inline methods definition\n' + inlineMethods);
out.write('\n// Human-readable text serialization\nvoid mtpTextSerializeType(MTPStringLogger &to, const mtpPrime *&from, const mtpPrime *end, mtpPrime cons, uint32 level, mtpPrime vcons);\n');
outCpp = open('scheme_auto.cpp', 'w');
outCpp.write('/*\n');
outCpp.write('Created from \'/SourceFiles/mtproto/scheme.tl\' by \'/SourceFiles/mtproto/generate.py\' script\n\n');
outCpp.write('WARNING! All changes made in this file will be lost!\n\n');
outCpp.write('This file is part of Telegram Desktop,\n');
outCpp.write('the official desktop version of Telegram messaging app, see https://telegram.org\n');
outCpp.write('\n');
outCpp.write('Telegram Desktop is free software: you can redistribute it and/or modify\n');
outCpp.write('it under the terms of the GNU General Public License as published by\n');
outCpp.write('the Free Software Foundation, either version 3 of the License, or\n');
outCpp.write('(at your option) any later version.\n');
outCpp.write('\n');
outCpp.write('It is distributed in the hope that it will be useful,\n');
outCpp.write('but WITHOUT ANY WARRANTY; without even the implied warranty of\n');
outCpp.write('MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n');
outCpp.write('GNU General Public License for more details.\n');
outCpp.write('\n');
outCpp.write('Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE\n');
outCpp.write('Copyright (c) 2014 John Preston, https://desktop.telegram.org\n');
outCpp.write('*/\n');
outCpp.write('#include "mtproto/scheme_auto.h"\n\n');
outCpp.write('typedef QVector<mtpTypeId> Types;\ntypedef QVector<int32> StagesFlags;\n\n');
outCpp.write(textSerializeMethods);
outCpp.write('namespace {\n');
outCpp.write('\ttypedef void(*mtpTextSerializer)(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 iflag);\n');
outCpp.write('\ttypedef QMap<mtpTypeId, mtpTextSerializer> TextSerializers;\n\tTextSerializers _serializers;\n\n');
outCpp.write('\tvoid initTextSerializers() {\n');
outCpp.write(textSerializeInit);
outCpp.write('\t}\n}\n');
outCpp.write(textSerializeFull + '\n');
print('Done, written {0} constructors, {1} functions.'.format(consts, funcs));