reproto 0.1.1__py3-none-any.whl → 0.1.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- .git/COMMIT_EDITMSG +29 -1
- .git/index +0 -0
- .git/logs/HEAD +4 -0
- .git/logs/refs/heads/iyue +4 -0
- .git/logs/refs/remotes/gitlab/iyue +4 -0
- .git/logs/refs/remotes/origin/iyue +4 -0
- .git/objects/00/81a7e1ec3251cc192ed2b73d5be897593fb872 +0 -0
- .git/objects/00/885cebf557ff261574deb93cb96449d06db01c +0 -0
- .git/objects/02/33f5b60263e5dc6d041c0b223a0724eb650faa +0 -0
- .git/objects/03/3fab23ccc74b707a31f6f37dc5df42c100aac0 +0 -0
- .git/objects/07/557fbd1d149ce51af8e98e8ca8590757a89dfa +0 -0
- .git/objects/07/b71df33935c14ddf99e3480c04eca4685028bc +0 -0
- .git/objects/0b/e20ea749c6ae1075ae98f9426e00b0a4a235af +0 -0
- .git/objects/12/f9cc4ad0b4f0af4f7bae379f281b2cebe7cc7f +0 -0
- .git/objects/13/5f50fea51c72f977f48d06a7584aba0f61e260 +0 -0
- .git/objects/1b/97584ebada3e93d7a2cfa4a1aae0b79c414d20 +0 -0
- .git/objects/1b/f0c1ad9586578e8332d061f7648dcb041ec063 +0 -0
- .git/objects/1e/46816d16e7c9db7575f1964403c3daa105be5c +0 -0
- .git/objects/24/d78e796570a8572a03bc1dd26608a7cfb506f8 +2 -0
- .git/objects/27/24208ca2442e8ab9b453d0eb230fa41243b270 +0 -0
- .git/objects/2a/fe93d8bcbeab9e136d8b6766604c32b3610314 +0 -0
- .git/objects/2d/2b812ca27c477f9e1b2c2706a5eb795ffcf8eb +0 -0
- .git/objects/2d/8a0da260a710010ae62be134ac1cea6ceecfd1 +0 -0
- .git/objects/2d/e8ecbb5ab5de1a032bef3f4606ce5fa7c6c4e8 +0 -0
- .git/objects/30/9347e5681d80bd3c7949882e27090dd9070d16 +0 -0
- .git/objects/33/633c9df669ff8cf38638717937a54990814268 +0 -0
- .git/objects/39/993e3600bf4ab82aa361b738ee97a108787450 +0 -0
- .git/objects/3b/ab663710fd6b43d9372313fced9043c4cb07dd +0 -0
- .git/objects/3e/b3f1273caf6814dfa69325ccbd9fd1340cf20a +0 -0
- .git/objects/3f/b8830f516342a0ae1cb7c34b65016827cb9570 +4 -0
- .git/objects/41/f40c22247de377be99e30784229f3f128508a2 +0 -0
- .git/objects/45/fbc774dedb61c7c205ea732f59a8dca8d13555 +3 -0
- .git/objects/48/cb11e75518a53be14146018214110986fade67 +0 -0
- .git/objects/4f/c6dc41f9c0a1a8e0eedd3ba49c43d78d0dbaba +0 -0
- .git/objects/59/4c23f158ccbd0a4288f9ea046d06160195afbf +0 -0
- .git/objects/5b/d4d87753b79e9157817f0c2e6964a731052854 +0 -0
- .git/objects/60/5cb6fd6a9f8894ad4d43a9b8e4785c1b3b0e17 +1 -0
- .git/objects/60/f61a0ea50091eac8d344c86597375cbdfc2785 +0 -0
- .git/objects/63/ddda2a403efaab3f4c6597b3a73a7b1147adb5 +0 -0
- .git/objects/65/0189fe083bd711e45d463b229a72be619abad2 +0 -0
- .git/objects/66/663db35bfec8ef5f1a5b1c840fde1bb62a0eb8 +0 -0
- .git/objects/66/6c5c5fc30435228116fa08c9d821bebaaa8926 +0 -0
- .git/objects/7c/ef0adfb28fd774bc78061c6f088e1ef9b050f6 +0 -0
- .git/objects/7d/dc129188a10c68ab756ef2cacb292c76920403 +0 -0
- .git/objects/80/17038e0f7818a44a742f77c86f4f88ed768fcd +0 -0
- .git/objects/87/c7db6c91c17a2df84b56d30bd24a0f6b9dbdd9 +0 -0
- .git/objects/8a/0ed0ed8886fbc823e8d2258fa6d18699e94e25 +0 -0
- .git/objects/8d/d857b3d0ab3f5cd2e9173d532ef86e30df8eda +0 -0
- .git/objects/92/984cf67b2c25d435468a8218daa26ba0466054 +0 -0
- .git/objects/93/140b54b1fb9116ee214afee8abf2c72a232487 +0 -0
- .git/objects/93/68f2c32f83054ab072b7c9686d8baa0bad7f12 +4 -0
- .git/objects/9a/5ad062be9f6e001f4237a598a08981aba731e6 +0 -0
- .git/objects/9e/23448ac58f907d9d123c32bdccedbb3d6741b5 +0 -0
- .git/objects/a0/d192999af7e2cbfa6a9ccd04d720a04e5a06d5 +0 -0
- .git/objects/a1/655e0cb323c300562f97dcc67d5a446908c8ec +0 -0
- .git/objects/a5/38cc82cef7c49500d3522220f0f60a9ebc1ae6 +0 -0
- .git/objects/a9/41063a7ce89c353fa24378ec7c3f12f08f9df8 +0 -0
- .git/objects/a9/cc7923c34a4c97c5711d6309672f41d46c612a +0 -0
- .git/objects/ac/5c983d949d8c928bb022badf801e45e75e785e +0 -0
- .git/objects/af/c9cc15629847447063e86a82b8b56abb4fc08f +0 -0
- .git/objects/b0/82ca2c1b5a03edff25da3c2b2b573d049877e9 +0 -0
- .git/objects/b1/db1c131cf32916028342c0037ce8eb57a8eb26 +0 -0
- .git/objects/b2/8334b94392b8af397a05ed702690fa6c9ab1ca +0 -0
- .git/objects/b8/7c89dcfce9e244ff5ef6a4bd394de12e8c8092 +0 -0
- .git/objects/bc/e98bdb71c8681acb460195fdcbbe5d36290976 +0 -0
- .git/objects/c1/87d5e047eca86cfd8d444be2987aaa3f62c4d6 +0 -0
- .git/objects/c4/c2da96b0bb8db2acb0e6615cf340c7e51af26b +0 -0
- .git/objects/c5/13a96e7584636b20b12280c029750d5bc3da1e +0 -0
- .git/objects/c7/c34283697bd3cce07db53953eda25ee7cc371e +0 -0
- .git/objects/c9/d60d922a04b87587cd67b0abf9fe5a7b7b76cd +0 -0
- .git/objects/d2/69b1676dbf32f76a7c405d0b4ea6a70ac3a626 +0 -0
- .git/objects/d3/5a918b1d9125ad35d60e08b181323df3246f1a +0 -0
- .git/objects/d8/eaf86669fbfd10497570c1784db1ed2696b588 +0 -0
- .git/objects/d9/3bd435c8c7ad4efb83dff04d5450fabb9e3faf +0 -0
- .git/objects/d9/90e6d553577d37ebce8b28b3015ecbde038b42 +0 -0
- .git/objects/da/13cc15bcd8ee39c81f36dee7f179a569ecab0b +0 -0
- .git/objects/e3/27755808d88c7ae5c06c229cf18bd0519646df +0 -0
- .git/objects/e4/4c1d8a90207ac082d8ab7ff0db66708e2ebc31 +0 -0
- .git/objects/e5/83e7c40be934d16a1fa2e973487b395d930f42 +0 -0
- .git/objects/ed/1ae867d5e63195845afc58d88c38ecbdea97df +0 -0
- .git/objects/ef/f44e5099da27f7fb1ef14bb34902ccf4250b89 +0 -0
- .git/objects/f5/1be495b96272fa2e47f30071aed35ac1f0dd2c +0 -0
- .git/objects/f8/ed595d25bd9d500e765a792c513878f7ddb1f7 +0 -0
- .git/objects/fd/0bc07dc3c95e6168ab6d367d9eca139ac1e539 +0 -0
- .git/refs/heads/iyue +1 -1
- .git/refs/remotes/gitlab/iyue +1 -1
- .git/refs/remotes/origin/iyue +1 -1
- .gitignore +2 -1
- README.md +104 -190
- core/__init__.py +23 -0
- core/info_decoder.py +520 -10
- core/reconstructor.py +159 -21
- generation/__init__.py +17 -0
- generation/proto_generator.py +62 -16
- include/google/protobuf/any.proto +162 -0
- include/google/protobuf/api.proto +207 -0
- include/google/protobuf/compiler/plugin.proto +180 -0
- include/google/protobuf/cpp_features.proto +67 -0
- include/google/protobuf/descriptor.proto +1417 -0
- include/google/protobuf/duration.proto +115 -0
- include/google/protobuf/empty.proto +51 -0
- include/google/protobuf/field_mask.proto +245 -0
- include/google/protobuf/go_features.proto +80 -0
- include/google/protobuf/java_features.proto +130 -0
- include/google/protobuf/source_context.proto +48 -0
- include/google/protobuf/struct.proto +95 -0
- include/google/protobuf/timestamp.proto +144 -0
- include/google/protobuf/type.proto +193 -0
- include/google/protobuf/wrappers.proto +157 -0
- main.py +53 -56
- models/__init__.py +31 -24
- parsing/__init__.py +22 -0
- parsing/enum_parser.py +10 -2
- parsing/java_parser.py +302 -13
- pyproject.toml +1 -1
- reproto-0.1.3.dist-info/METADATA +209 -0
- {reproto-0.1.1.dist-info → reproto-0.1.3.dist-info}/RECORD +125 -31
- utils/__init__.py +40 -0
- utils/builtin_proto.py +269 -0
- utils/file_cache.py +8 -1
- utils/report_utils.py +71 -0
- utils/type_index.py +8 -1
- utils/type_utils.py +39 -6
- core/bytecode_parser.py +0 -274
- reproto-0.1.1.dist-info/METADATA +0 -295
- {reproto-0.1.1.dist-info → reproto-0.1.3.dist-info}/WHEEL +0 -0
- {reproto-0.1.1.dist-info → reproto-0.1.3.dist-info}/entry_points.txt +0 -0
core/info_decoder.py
CHANGED
@@ -16,9 +16,19 @@ import re
|
|
16
16
|
from typing import Optional, List, Dict, Tuple
|
17
17
|
from pathlib import Path
|
18
18
|
|
19
|
-
|
20
|
-
|
21
|
-
|
19
|
+
# 智能导入:同时支持相对导入(包环境)和绝对导入(开发环境)
|
20
|
+
try:
|
21
|
+
# 相对导入(包环境)
|
22
|
+
from ..models.message_definition import MessageDefinition, FieldDefinition, OneofDefinition
|
23
|
+
from ..utils.logger import get_logger
|
24
|
+
from ..utils.type_utils import type_mapper, naming_converter
|
25
|
+
from ..parsing.java_parser import JavaParser
|
26
|
+
except ImportError:
|
27
|
+
# 绝对导入(开发环境)
|
28
|
+
from models.message_definition import MessageDefinition, FieldDefinition, OneofDefinition
|
29
|
+
from utils.logger import get_logger
|
30
|
+
from utils.type_utils import type_mapper, naming_converter
|
31
|
+
from parsing.java_parser import JavaParser
|
22
32
|
|
23
33
|
|
24
34
|
class InfoDecoder:
|
@@ -40,7 +50,6 @@ class InfoDecoder:
|
|
40
50
|
self.java_source_analyzer = java_source_analyzer
|
41
51
|
|
42
52
|
# 导入JavaParser
|
43
|
-
from parsing.java_parser import JavaParser
|
44
53
|
self.java_parser = JavaParser()
|
45
54
|
|
46
55
|
# Protobuf字段类型映射表
|
@@ -93,12 +102,19 @@ class InfoDecoder:
|
|
93
102
|
# 创建消息定义
|
94
103
|
message_def = self._create_message_definition(class_name)
|
95
104
|
|
105
|
+
# 存储当前处理的类名,供依赖推断使用
|
106
|
+
self._current_processing_class = class_name
|
107
|
+
|
96
108
|
# 提取字段标签(如果有Java文件路径)
|
97
109
|
field_tags = None
|
98
110
|
if java_file_path:
|
111
|
+
# 存储当前Java文件路径,供其他方法使用
|
112
|
+
self._current_java_file_path = java_file_path
|
99
113
|
field_tags = self.java_parser.extract_field_tags(java_file_path)
|
100
114
|
if field_tags:
|
101
115
|
self.logger.info(f" 🏷️ 从Java源码提取到 {len(field_tags)} 个字段标签")
|
116
|
+
else:
|
117
|
+
self._current_java_file_path = None
|
102
118
|
|
103
119
|
# 解析字段信息
|
104
120
|
self._parse_fields(message_def, bytes_data, objects, field_tags)
|
@@ -177,9 +193,14 @@ class InfoDecoder:
|
|
177
193
|
# 检查是否包含oneof字段(通过查找'<'字符,ord=60)
|
178
194
|
oneof_positions = [i for i, byte_val in enumerate(bytes_data) if byte_val == 60]
|
179
195
|
|
196
|
+
self.logger.info(f" 🔍 字节码长度: {len(bytes_data)}, oneof_positions: {oneof_positions}")
|
197
|
+
self.logger.info(f" 🔍 字节码内容: {[f'{b:02x}' for b in bytes_data[:20]]}...")
|
198
|
+
|
180
199
|
if oneof_positions:
|
200
|
+
self.logger.info(f" 🎯 检测到oneof结构,调用_parse_oneof_fields")
|
181
201
|
self._parse_oneof_fields(message_def, bytes_data, objects, oneof_positions)
|
182
202
|
else:
|
203
|
+
self.logger.info(f" 🎯 未检测到oneof结构,调用_parse_regular_fields")
|
183
204
|
self._parse_regular_fields(message_def, bytes_data, objects, field_tags)
|
184
205
|
|
185
206
|
except Exception as e:
|
@@ -204,19 +225,20 @@ class InfoDecoder:
|
|
204
225
|
self.logger.info(f" 📊 Objects数组: {objects}")
|
205
226
|
|
206
227
|
# 如果有字段标签,优先使用Java源码信息
|
228
|
+
self.logger.info(f" 🔍 field_tags类型: {type(field_tags)}, 值: {field_tags}, 布尔值: {bool(field_tags)}")
|
207
229
|
if field_tags:
|
208
230
|
self.logger.info(f" 🏷️ 使用Java源码字段标签: {field_tags}")
|
209
231
|
self._parse_fields_with_java_tags(message_def, bytes_data, objects, field_tags)
|
210
232
|
else:
|
211
233
|
# 回退到字节码解析
|
212
|
-
self.logger.info(f" 🔍
|
234
|
+
self.logger.info(f" 🔍 回退到字节码解析,field_tags为: {field_tags}")
|
213
235
|
self._parse_fields_from_bytecode(message_def, bytes_data, objects, field_start)
|
214
236
|
|
215
237
|
self.logger.info(f" 📊 字段解析完成,共解析 {len(message_def.fields)} 个字段")
|
216
238
|
|
217
239
|
def _parse_fields_with_java_tags(self, message_def: MessageDefinition, bytes_data: List[int], objects: List[str], field_tags: dict) -> None:
|
218
240
|
"""
|
219
|
-
使用Java
|
241
|
+
使用Java源码提取的字段标签解析字段,同时处理objects数组中的类引用
|
220
242
|
|
221
243
|
Args:
|
222
244
|
message_def: 消息定义对象
|
@@ -224,7 +246,48 @@ class InfoDecoder:
|
|
224
246
|
objects: 对象数组
|
225
247
|
field_tags: Java源码提取的字段标签映射
|
226
248
|
"""
|
249
|
+
self.logger.info(f" 🔍 开始_parse_fields_with_java_tags")
|
250
|
+
self.logger.info(f" 📊 Objects数组: {objects}")
|
251
|
+
self.logger.info(f" 📊 字段标签: {field_tags}")
|
252
|
+
|
253
|
+
# 首先检查是否有oneof结构
|
254
|
+
oneof_field = None
|
255
|
+
class_refs = []
|
256
|
+
|
257
|
+
for obj in objects:
|
258
|
+
if not obj.endswith('_') and obj not in ['action', 'actionCase', 'result', 'resultCase'] and len(obj) > 2:
|
259
|
+
class_refs.append(obj)
|
260
|
+
elif obj.endswith('_') and obj.rstrip('_') + 'Case_' in objects:
|
261
|
+
oneof_field = obj
|
262
|
+
|
263
|
+
# 分离普通字段和oneof相关的对象
|
264
|
+
oneof_related_objects = set()
|
265
|
+
if oneof_field and class_refs:
|
266
|
+
# 标记oneof相关的对象
|
267
|
+
oneof_related_objects.add(oneof_field) # action_
|
268
|
+
oneof_related_objects.add(oneof_field.rstrip('_') + 'Case_') # actionCase_
|
269
|
+
oneof_related_objects.update(class_refs) # SkipRecovery, InstallationInfo
|
270
|
+
self.logger.info(f" 🎯 检测到oneof结构: {oneof_field},包含类引用: {class_refs}")
|
271
|
+
|
272
|
+
# 特殊处理:如果oneof字段的名称与field_tags中的某些字段名相似,
|
273
|
+
# 说明这些field_tags可能是错误的(来自Java常量的错误转换)
|
274
|
+
# 例如:result_ oneof 但 field_tags 中有 singlesearchresult_, bulksearChresult_
|
275
|
+
oneof_base_name = oneof_field.rstrip('_').lower()
|
276
|
+
for field_name in list(field_tags.keys()):
|
277
|
+
field_base_name = field_name.rstrip('_').lower()
|
278
|
+
# 如果字段名包含oneof的基础名称,或者包含类引用的名称,很可能是错误的字段标签
|
279
|
+
if (oneof_base_name in field_base_name or
|
280
|
+
any(class_ref.lower() in field_base_name for class_ref in class_refs)):
|
281
|
+
oneof_related_objects.add(field_name)
|
282
|
+
self.logger.debug(f" 🔍 标记疑似错误字段标签: {field_name} (与oneof {oneof_field} 或类引用相关)")
|
283
|
+
|
284
|
+
# 处理普通字段(从field_tags中提取,排除oneof相关的字段)
|
227
285
|
for field_name_raw, field_tag in field_tags.items():
|
286
|
+
# 跳过oneof相关的字段
|
287
|
+
if field_name_raw in oneof_related_objects:
|
288
|
+
self.logger.debug(f" ⏭️ 跳过oneof相关字段: {field_name_raw}")
|
289
|
+
continue
|
290
|
+
|
228
291
|
# 清理字段名
|
229
292
|
field_name = self._clean_field_name(field_name_raw)
|
230
293
|
|
@@ -297,9 +360,6 @@ class InfoDecoder:
|
|
297
360
|
# 特殊情况处理:根据字段名修正类型
|
298
361
|
field_type_name = self._refine_field_type(field_name, field_type_name, 0) # 使用0作为占位符
|
299
362
|
|
300
|
-
# 确定字段规则(基于Java类型判断是否为repeated)
|
301
|
-
# 已经在上面确定了rule,这里不需要重复处理
|
302
|
-
|
303
363
|
# 创建字段定义
|
304
364
|
field_def = FieldDefinition(
|
305
365
|
name=field_name,
|
@@ -310,6 +370,418 @@ class InfoDecoder:
|
|
310
370
|
|
311
371
|
message_def.fields.append(field_def)
|
312
372
|
self.logger.info(f" ✅ 添加字段: {field_name} = {field_tag} ({rule} {field_type_name})")
|
373
|
+
|
374
|
+
# 最后处理objects数组中的类引用,检测oneof结构
|
375
|
+
self._parse_oneof_from_objects(message_def, objects, field_tags)
|
376
|
+
|
377
|
+
def _parse_oneof_from_objects(self, message_def: MessageDefinition, objects: List[str], field_tags: dict) -> None:
|
378
|
+
"""
|
379
|
+
从objects数组中解析oneof结构和类引用
|
380
|
+
|
381
|
+
Args:
|
382
|
+
message_def: 消息定义对象
|
383
|
+
objects: 对象数组
|
384
|
+
field_tags: 已知的字段标签映射
|
385
|
+
"""
|
386
|
+
# 查找类引用(以.class结尾的对象)
|
387
|
+
class_refs = []
|
388
|
+
oneof_field = None
|
389
|
+
|
390
|
+
# 首先识别已经作为字段类型的类引用,避免重复处理
|
391
|
+
# 通过Java源码分析结果来识别已使用的类引用
|
392
|
+
used_class_refs = set()
|
393
|
+
|
394
|
+
# 从已解析的字段中提取使用的类引用
|
395
|
+
for field in message_def.fields:
|
396
|
+
# 如果字段类型不是基础类型,就是类引用
|
397
|
+
if (field.type_name not in ['string', 'int32', 'int64', 'long', 'int', 'bool', 'double', 'float', 'bytes'] and
|
398
|
+
not field.type_name.startswith('google.protobuf.') and
|
399
|
+
not field.type_name.startswith('repeated ') and
|
400
|
+
not field.type_name.startswith('map<')):
|
401
|
+
|
402
|
+
# 提取类名(去掉包名部分)
|
403
|
+
class_name = field.type_name.split('.')[-1]
|
404
|
+
used_class_refs.add(class_name)
|
405
|
+
self.logger.debug(f" 📝 从已解析字段 {field.name} 中识别类引用: {class_name}")
|
406
|
+
|
407
|
+
# 识别连续的类引用(oneof选项)
|
408
|
+
consecutive_class_refs = []
|
409
|
+
for i, obj in enumerate(objects):
|
410
|
+
if not obj.endswith('_') and obj not in ['action', 'actionCase', 'result', 'resultCase'] and len(obj) > 2:
|
411
|
+
consecutive_class_refs.append(i)
|
412
|
+
|
413
|
+
# 如果有多个连续的类引用,它们很可能是oneof选项
|
414
|
+
is_oneof_group = len(consecutive_class_refs) > 1
|
415
|
+
if is_oneof_group:
|
416
|
+
# 检查是否连续
|
417
|
+
for i in range(len(consecutive_class_refs) - 1):
|
418
|
+
if consecutive_class_refs[i+1] - consecutive_class_refs[i] == 1:
|
419
|
+
# 连续的类引用,很可能是oneof选项
|
420
|
+
self.logger.debug(f" 🔍 检测到连续类引用,可能是oneof选项: {[objects[idx] for idx in consecutive_class_refs]}")
|
421
|
+
break
|
422
|
+
|
423
|
+
for i, obj in enumerate(objects):
|
424
|
+
# 检查是否是类引用(不以_结尾且不是基础字段名)
|
425
|
+
if not obj.endswith('_') and obj not in ['action', 'actionCase'] and len(obj) > 2:
|
426
|
+
# 跳过已经作为字段类型的类引用
|
427
|
+
if obj in used_class_refs:
|
428
|
+
self.logger.debug(f" ⏭️ 跳过已用作字段类型的类引用: {obj}")
|
429
|
+
continue
|
430
|
+
# 这可能是一个独立的类引用(用于oneof)
|
431
|
+
class_refs.append((i, obj))
|
432
|
+
self.logger.info(f" 🔍 发现独立类引用: {obj}")
|
433
|
+
elif obj.endswith('_') and obj.rstrip('_') + 'Case_' in objects:
|
434
|
+
# 发现oneof字段(通过检查是否有对应的Case字段)
|
435
|
+
oneof_field = obj
|
436
|
+
self.logger.info(f" 🔍 发现oneof字段: {obj}")
|
437
|
+
|
438
|
+
if class_refs and oneof_field:
|
439
|
+
# 这是一个oneof结构
|
440
|
+
self._create_oneof_structure(message_def, oneof_field, class_refs, field_tags)
|
441
|
+
elif class_refs:
|
442
|
+
# 有类引用但没有明确的oneof字段,可能是直接的消息字段
|
443
|
+
self._create_message_fields_from_class_refs(message_def, class_refs, field_tags)
|
444
|
+
|
445
|
+
def _create_oneof_structure(self, message_def: MessageDefinition, oneof_field: str, class_refs: List[tuple], field_tags: dict) -> None:
|
446
|
+
"""
|
447
|
+
创建oneof结构
|
448
|
+
|
449
|
+
Args:
|
450
|
+
message_def: 消息定义对象
|
451
|
+
oneof_field: oneof字段名(如"action_")
|
452
|
+
class_refs: 类引用列表[(索引, 类名)]
|
453
|
+
field_tags: 字段标签映射
|
454
|
+
"""
|
455
|
+
from models.message_definition import OneofDefinition
|
456
|
+
|
457
|
+
# 创建oneof定义
|
458
|
+
oneof_name = self._clean_field_name(oneof_field)
|
459
|
+
oneof_def = OneofDefinition(name=oneof_name)
|
460
|
+
|
461
|
+
# 收集已使用的字段标签
|
462
|
+
used_tags = set()
|
463
|
+
for field in message_def.fields:
|
464
|
+
used_tags.add(field.tag)
|
465
|
+
for oneof in message_def.oneofs:
|
466
|
+
for field in oneof.fields:
|
467
|
+
used_tags.add(field.tag)
|
468
|
+
|
469
|
+
# 为每个类引用创建oneof字段
|
470
|
+
for _, class_name in class_refs:
|
471
|
+
# 查找对应的字段标签
|
472
|
+
field_tag = self._find_tag_for_class(class_name, field_tags, used_tags)
|
473
|
+
if field_tag is None:
|
474
|
+
self.logger.warning(f" ⚠️ 无法找到类 {class_name} 的字段标签")
|
475
|
+
continue
|
476
|
+
|
477
|
+
# 生成字段名:SkipRecovery -> skip_recovery
|
478
|
+
field_name = self._class_name_to_field_name(class_name)
|
479
|
+
|
480
|
+
# 为oneof字段生成正确的类型名
|
481
|
+
# 如果是内部类(包含$),需要使用完整的类名来生成类型名
|
482
|
+
full_class_name = self._infer_full_dependency_class_name(class_name)
|
483
|
+
if '$' in full_class_name:
|
484
|
+
# 对于内部类,使用完整的类名部分(如Service$SkipRecovery)
|
485
|
+
class_part = full_class_name.split('.')[-1] # Service$SkipRecovery
|
486
|
+
clean_class_name = class_part.replace('$', '') # ServiceSkipRecovery
|
487
|
+
else:
|
488
|
+
# 对于普通类,直接清理$符号
|
489
|
+
clean_class_name = class_name.replace('$', '')
|
490
|
+
|
491
|
+
# 创建字段定义
|
492
|
+
field_def = FieldDefinition(
|
493
|
+
name=field_name,
|
494
|
+
type_name=clean_class_name,
|
495
|
+
tag=field_tag,
|
496
|
+
rule='optional'
|
497
|
+
)
|
498
|
+
|
499
|
+
# 保存完整的类名信息,用于导入路径生成
|
500
|
+
field_def.full_class_name = self._infer_full_dependency_class_name(class_name)
|
501
|
+
|
502
|
+
oneof_def.fields.append(field_def)
|
503
|
+
self.logger.info(f" ✅ 添加oneof字段: {field_name} = {field_tag} ({clean_class_name})")
|
504
|
+
|
505
|
+
# 记录依赖类
|
506
|
+
self._record_dependency_class(class_name)
|
507
|
+
|
508
|
+
if oneof_def.fields:
|
509
|
+
message_def.oneofs.append(oneof_def)
|
510
|
+
self.logger.info(f" 🎯 创建oneof: {oneof_name} (包含 {len(oneof_def.fields)} 个字段)")
|
511
|
+
|
512
|
+
def _create_message_fields_from_class_refs(self, message_def: MessageDefinition, class_refs: List[tuple], field_tags: dict) -> None:
|
513
|
+
"""
|
514
|
+
从类引用创建普通消息字段
|
515
|
+
|
516
|
+
Args:
|
517
|
+
message_def: 消息定义对象
|
518
|
+
class_refs: 类引用列表[(索引, 类名)]
|
519
|
+
field_tags: 字段标签映射
|
520
|
+
"""
|
521
|
+
# 收集已使用的字段标签
|
522
|
+
used_tags = set()
|
523
|
+
for field in message_def.fields:
|
524
|
+
used_tags.add(field.tag)
|
525
|
+
for oneof in message_def.oneofs:
|
526
|
+
for field in oneof.fields:
|
527
|
+
used_tags.add(field.tag)
|
528
|
+
|
529
|
+
for _, class_name in class_refs:
|
530
|
+
# 查找对应的字段标签
|
531
|
+
field_tag = self._find_tag_for_class(class_name, field_tags, used_tags)
|
532
|
+
if field_tag is None:
|
533
|
+
self.logger.warning(f" ⚠️ 无法找到类 {class_name} 的字段标签")
|
534
|
+
continue
|
535
|
+
|
536
|
+
# 生成字段名
|
537
|
+
field_name = self._class_name_to_field_name(class_name)
|
538
|
+
|
539
|
+
# 为oneof字段生成正确的类型名
|
540
|
+
# 如果是内部类(包含$),需要使用完整的类名来生成类型名
|
541
|
+
full_class_name = self._infer_full_dependency_class_name(class_name)
|
542
|
+
if '$' in full_class_name:
|
543
|
+
# 对于内部类,使用完整的类名部分(如Service$SkipRecovery)
|
544
|
+
class_part = full_class_name.split('.')[-1] # Service$SkipRecovery
|
545
|
+
clean_class_name = class_part.replace('$', '') # ServiceSkipRecovery
|
546
|
+
else:
|
547
|
+
# 对于普通类,直接清理$符号
|
548
|
+
clean_class_name = class_name.replace('$', '')
|
549
|
+
|
550
|
+
# 创建字段定义
|
551
|
+
field_def = FieldDefinition(
|
552
|
+
name=field_name,
|
553
|
+
type_name=clean_class_name,
|
554
|
+
tag=field_tag,
|
555
|
+
rule='optional'
|
556
|
+
)
|
557
|
+
|
558
|
+
# 保存完整的类名信息,用于导入路径生成
|
559
|
+
field_def.full_class_name = full_class_name
|
560
|
+
|
561
|
+
message_def.fields.append(field_def)
|
562
|
+
self.logger.info(f" ✅ 添加消息字段: {field_name} = {field_tag} ({clean_class_name})")
|
563
|
+
|
564
|
+
# 记录依赖类
|
565
|
+
self._record_dependency_class(class_name)
|
566
|
+
|
567
|
+
def _find_tag_for_class(self, class_name: str, field_tags: dict, used_tags: set = None) -> Optional[int]:
|
568
|
+
"""
|
569
|
+
为类名查找对应的字段标签,完全基于Java源码分析
|
570
|
+
|
571
|
+
Args:
|
572
|
+
class_name: 类名(如"SkipRecovery")
|
573
|
+
field_tags: 字段标签映射
|
574
|
+
used_tags: 已使用的字段标签集合
|
575
|
+
|
576
|
+
Returns:
|
577
|
+
字段标签,如果找不到则返回None
|
578
|
+
"""
|
579
|
+
if used_tags is None:
|
580
|
+
used_tags = set()
|
581
|
+
# 完全基于Java源码分析,智能推断字段标签
|
582
|
+
|
583
|
+
# 1. 直接匹配:类名转换为字段名
|
584
|
+
direct_field_name = self._to_snake_case(class_name) + '_'
|
585
|
+
if direct_field_name in field_tags:
|
586
|
+
self.logger.debug(f" 🎯 直接匹配类 {class_name}: {direct_field_name} -> {field_tags[direct_field_name]}")
|
587
|
+
return field_tags[direct_field_name]
|
588
|
+
|
589
|
+
# 2. 小写匹配
|
590
|
+
lowercase_field_name = class_name.lower() + '_'
|
591
|
+
if lowercase_field_name in field_tags:
|
592
|
+
self.logger.debug(f" 🎯 小写匹配类 {class_name}: {lowercase_field_name} -> {field_tags[lowercase_field_name]}")
|
593
|
+
return field_tags[lowercase_field_name]
|
594
|
+
|
595
|
+
# 3. 智能模式匹配:处理各种命名约定
|
596
|
+
# 移除常见后缀并尝试匹配
|
597
|
+
class_variants = [class_name]
|
598
|
+
if class_name.endswith('Result'):
|
599
|
+
class_variants.append(class_name[:-6]) # 移除Result
|
600
|
+
if class_name.endswith('Info'):
|
601
|
+
class_variants.append(class_name[:-4]) # 移除Info
|
602
|
+
if class_name.endswith('Data'):
|
603
|
+
class_variants.append(class_name[:-4]) # 移除Data
|
604
|
+
|
605
|
+
for variant in class_variants:
|
606
|
+
for suffix in ['_', 'result_', 'info_', 'data_']:
|
607
|
+
test_field_name = variant.lower() + suffix
|
608
|
+
if test_field_name in field_tags:
|
609
|
+
self.logger.debug(f" 🎯 变体匹配类 {class_name}: {test_field_name} -> {field_tags[test_field_name]}")
|
610
|
+
return field_tags[test_field_name]
|
611
|
+
|
612
|
+
# 4. 模糊匹配:在字段名中查找类名
|
613
|
+
class_lower = class_name.lower()
|
614
|
+
for field_name, tag in field_tags.items():
|
615
|
+
# 跳过已使用的标签
|
616
|
+
if tag in used_tags:
|
617
|
+
self.logger.debug(f" ⏭️ 跳过已使用的标签: {field_name} -> {tag}")
|
618
|
+
continue
|
619
|
+
|
620
|
+
field_clean = field_name.lower().rstrip('_')
|
621
|
+
if class_lower == field_clean or class_lower in field_clean:
|
622
|
+
self.logger.debug(f" 🎯 模糊匹配类 {class_name}: {field_name} -> {tag}")
|
623
|
+
return tag
|
624
|
+
|
625
|
+
# 5. 使用Java源码分析器获取更精确的信息
|
626
|
+
if self.java_source_analyzer:
|
627
|
+
tag = self._get_class_field_tag_from_source(class_name)
|
628
|
+
if tag is not None:
|
629
|
+
self.logger.debug(f" 🎯 源码分析匹配类 {class_name}: -> {tag}")
|
630
|
+
return tag
|
631
|
+
|
632
|
+
return None
|
633
|
+
|
634
|
+
def _get_class_field_tag_from_source(self, class_name: str) -> Optional[int]:
|
635
|
+
"""
|
636
|
+
从Java源码中获取类对应的字段标签
|
637
|
+
|
638
|
+
Args:
|
639
|
+
class_name: 类名
|
640
|
+
|
641
|
+
Returns:
|
642
|
+
字段标签,如果找不到则返回None
|
643
|
+
"""
|
644
|
+
if not self.java_source_analyzer:
|
645
|
+
return None
|
646
|
+
|
647
|
+
try:
|
648
|
+
# 尝试通过Java源码分析器获取字段标签
|
649
|
+
# 查找形如 CLASSNAME_FIELD_NUMBER 的常量
|
650
|
+
possible_constant_names = [
|
651
|
+
f"{class_name.upper()}_FIELD_NUMBER",
|
652
|
+
f"{self._to_snake_case(class_name).upper()}_FIELD_NUMBER",
|
653
|
+
f"{class_name.upper()}",
|
654
|
+
f"{class_name.upper()}_NUMBER",
|
655
|
+
# 处理缩写情况,如 SkipRecovery -> SKIP_FIELD_NUMBER
|
656
|
+
f"{class_name.upper()[:4]}_FIELD_NUMBER", # 前4个字符
|
657
|
+
f"{class_name.upper()[:5]}_FIELD_NUMBER", # 前5个字符
|
658
|
+
f"{class_name.upper()[:6]}_FIELD_NUMBER", # 前6个字符
|
659
|
+
# 处理常见的缩写模式
|
660
|
+
f"{class_name.replace('Recovery', '').upper()}_FIELD_NUMBER", # 移除Recovery
|
661
|
+
f"{class_name.replace('Info', '').upper()}_FIELD_NUMBER", # 移除Info
|
662
|
+
f"{class_name.replace('Data', '').upper()}_FIELD_NUMBER", # 移除Data
|
663
|
+
f"{class_name.replace('Result', '').upper()}_FIELD_NUMBER", # 移除Result
|
664
|
+
]
|
665
|
+
|
666
|
+
for constant_name in possible_constant_names:
|
667
|
+
# 尝试从Java源码中提取常量值
|
668
|
+
tag = self.java_source_analyzer._extract_constant_value(constant_name)
|
669
|
+
if tag is not None:
|
670
|
+
self.logger.debug(f" 🎯 从源码获取字段标签: {class_name} -> {constant_name} = {tag}")
|
671
|
+
return tag
|
672
|
+
|
673
|
+
return None
|
674
|
+
|
675
|
+
except Exception as e:
|
676
|
+
self.logger.debug(f" ⚠️ 源码分析失败: {e}")
|
677
|
+
return None
|
678
|
+
|
679
|
+
def _to_snake_case(self, camel_str: str) -> str:
|
680
|
+
"""
|
681
|
+
将驼峰命名转换为蛇形命名
|
682
|
+
|
683
|
+
Args:
|
684
|
+
camel_str: 驼峰命名字符串
|
685
|
+
|
686
|
+
Returns:
|
687
|
+
蛇形命名字符串
|
688
|
+
"""
|
689
|
+
# 处理$符号
|
690
|
+
camel_str = camel_str.replace('$', '_')
|
691
|
+
|
692
|
+
# 在大写字母前插入下划线
|
693
|
+
result = re.sub(r'([a-z0-9])([A-Z])', r'\1_\2', camel_str)
|
694
|
+
|
695
|
+
# 转换为小写
|
696
|
+
result = result.lower()
|
697
|
+
|
698
|
+
# 清理连续的下划线
|
699
|
+
result = re.sub(r'_+', '_', result)
|
700
|
+
|
701
|
+
# 移除首尾下划线
|
702
|
+
return result.strip('_')
|
703
|
+
|
704
|
+
def _class_name_to_field_name(self, class_name: str) -> str:
|
705
|
+
"""
|
706
|
+
将类名转换为字段名
|
707
|
+
|
708
|
+
Args:
|
709
|
+
class_name: 类名(如"SkipRecovery")
|
710
|
+
|
711
|
+
Returns:
|
712
|
+
字段名(如"skip_recovery")
|
713
|
+
"""
|
714
|
+
# 移除$符号并转换为snake_case
|
715
|
+
clean_name = class_name.replace('$', '')
|
716
|
+
return self._to_snake_case(clean_name)
|
717
|
+
|
718
|
+
def _record_dependency_class(self, class_name: str) -> None:
|
719
|
+
"""
|
720
|
+
记录依赖类,用于后续处理
|
721
|
+
|
722
|
+
Args:
|
723
|
+
class_name: 类名
|
724
|
+
"""
|
725
|
+
# 记录依赖类到实例变量中,供重构器获取
|
726
|
+
if not hasattr(self, 'discovered_dependencies'):
|
727
|
+
self.discovered_dependencies = []
|
728
|
+
|
729
|
+
# 构造完整的类名,智能处理内部类情况
|
730
|
+
full_class_name = self._infer_full_dependency_class_name(class_name)
|
731
|
+
|
732
|
+
if full_class_name not in self.discovered_dependencies:
|
733
|
+
self.discovered_dependencies.append(full_class_name)
|
734
|
+
self.logger.info(f" 📦 记录依赖类: {full_class_name}")
|
735
|
+
|
736
|
+
def _infer_full_dependency_class_name(self, class_name: str) -> str:
|
737
|
+
"""
|
738
|
+
推断依赖类的完整类名,特别处理内部类情况
|
739
|
+
|
740
|
+
Args:
|
741
|
+
class_name: 简单类名(如SkipRecovery)
|
742
|
+
|
743
|
+
Returns:
|
744
|
+
完整的类名
|
745
|
+
"""
|
746
|
+
# 如果已经是完整类名,直接返回
|
747
|
+
if '.' in class_name:
|
748
|
+
return class_name
|
749
|
+
|
750
|
+
# 尝试从当前处理的类推断包名和外部类
|
751
|
+
current_class = getattr(self, '_current_processing_class', None)
|
752
|
+
if current_class and '$' in current_class:
|
753
|
+
# 当前类是内部类,依赖类可能是同一外部类的其他内部类
|
754
|
+
# 如:com.example.Service$CompleteRequest -> com.example.Service$SkipRecovery
|
755
|
+
parts = current_class.split('$')
|
756
|
+
if len(parts) >= 2:
|
757
|
+
outer_class = parts[0] # com.example.Service
|
758
|
+
full_class_name = f"{outer_class}${class_name}"
|
759
|
+
self.logger.debug(f" 🔍 推断内部类依赖: {class_name} -> {full_class_name}")
|
760
|
+
return full_class_name
|
761
|
+
|
762
|
+
# 如果当前类有包名,使用相同的包名
|
763
|
+
if current_class and '.' in current_class:
|
764
|
+
# 提取包名部分
|
765
|
+
last_dot = current_class.rfind('.')
|
766
|
+
if last_dot != -1:
|
767
|
+
package_name = current_class[:last_dot]
|
768
|
+
full_class_name = f"{package_name}.{class_name}"
|
769
|
+
self.logger.debug(f" 🔍 推断包级依赖: {class_name} -> {full_class_name}")
|
770
|
+
return full_class_name
|
771
|
+
|
772
|
+
# 最后的备选方案:使用默认包名
|
773
|
+
full_class_name = f"com.truecaller.accountonboarding.v1.{class_name}"
|
774
|
+
self.logger.debug(f" 🔍 使用默认包名: {class_name} -> {full_class_name}")
|
775
|
+
return full_class_name
|
776
|
+
|
777
|
+
def get_discovered_dependencies(self) -> List[str]:
|
778
|
+
"""
|
779
|
+
获取在解析过程中发现的依赖类
|
780
|
+
|
781
|
+
Returns:
|
782
|
+
依赖类名列表
|
783
|
+
"""
|
784
|
+
return getattr(self, 'discovered_dependencies', [])
|
313
785
|
|
314
786
|
def _determine_field_rule(self, field_type_byte: int, field_type_name: str = None, java_type: str = None) -> str:
|
315
787
|
"""
|
@@ -766,7 +1238,45 @@ class InfoDecoder:
|
|
766
1238
|
def _parse_oneof_fields(self, message_def: MessageDefinition, bytes_data: List[int],
|
767
1239
|
objects: List[str], oneof_positions: List[int]) -> None:
|
768
1240
|
"""
|
769
|
-
解析oneof
|
1241
|
+
解析oneof字段(增强版,支持Java源码字段标签)
|
1242
|
+
|
1243
|
+
Args:
|
1244
|
+
message_def: 消息定义对象
|
1245
|
+
bytes_data: 字节数组
|
1246
|
+
objects: 对象数组
|
1247
|
+
oneof_positions: oneof标记位置列表
|
1248
|
+
"""
|
1249
|
+
self.logger.info(f" 🎯 开始解析oneof字段")
|
1250
|
+
self.logger.info(f" 📊 Objects数组: {objects}")
|
1251
|
+
self.logger.info(f" 📊 oneof_positions: {oneof_positions}")
|
1252
|
+
|
1253
|
+
# 首先尝试从Java源码获取字段标签
|
1254
|
+
field_tags = None
|
1255
|
+
if hasattr(self, 'java_parser') and self.java_parser:
|
1256
|
+
try:
|
1257
|
+
# 获取当前类的Java文件路径
|
1258
|
+
java_file_path = getattr(self, '_current_java_file_path', None)
|
1259
|
+
if java_file_path:
|
1260
|
+
field_tags = self.java_parser.extract_field_tags(java_file_path)
|
1261
|
+
if field_tags:
|
1262
|
+
self.logger.info(f" 🏷️ 获取到字段标签: {field_tags}")
|
1263
|
+
except Exception as e:
|
1264
|
+
self.logger.debug(f" ⚠️ 获取字段标签失败: {e}")
|
1265
|
+
|
1266
|
+
# 如果有字段标签,使用新的解析逻辑
|
1267
|
+
if field_tags:
|
1268
|
+
self.logger.info(f" 🎯 使用Java源码字段标签解析oneof")
|
1269
|
+
# 先处理普通字段
|
1270
|
+
self._parse_fields_with_java_tags(message_def, bytes_data, objects, field_tags)
|
1271
|
+
else:
|
1272
|
+
# 回退到旧的字节码解析逻辑
|
1273
|
+
self.logger.info(f" 🎯 使用字节码解析oneof")
|
1274
|
+
self._parse_oneof_fields_legacy(message_def, bytes_data, objects, oneof_positions)
|
1275
|
+
|
1276
|
+
def _parse_oneof_fields_legacy(self, message_def: MessageDefinition, bytes_data: List[int],
|
1277
|
+
objects: List[str], oneof_positions: List[int]) -> None:
|
1278
|
+
"""
|
1279
|
+
传统的oneof字段解析方法(作为备用)
|
770
1280
|
|
771
1281
|
Args:
|
772
1282
|
message_def: 消息定义对象
|