reproto 0.0.4__py3-none-any.whl → 0.0.6__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.
Files changed (48) hide show
  1. .git/COMMIT_EDITMSG +1 -0
  2. .git/config +3 -0
  3. .git/index +0 -0
  4. .git/logs/HEAD +3 -1
  5. .git/logs/refs/heads/iyue +3 -1
  6. .git/logs/refs/remotes/gitlab/iyue +2 -0
  7. .git/logs/refs/remotes/origin/HEAD +1 -1
  8. .git/logs/refs/remotes/origin/iyue +2 -0
  9. .git/objects/09/a92517fe9eeb33d2fd7c979e01d163665f7abc +0 -0
  10. .git/objects/14/d323d58fb90c209a730c17e23fd82a6c735fc1 +0 -0
  11. .git/objects/18/89a4fb55eb3abdc528ce87f0cea039278c06fd +0 -0
  12. .git/objects/23/bfbca38e177bcbb423dd782c35b19d127a5ab0 +0 -0
  13. .git/objects/33/181441ab38eded005db356da89b54c7d29f452 +0 -0
  14. .git/objects/40/84f4567d983a977c49598b7d886e46b13ff50b +0 -0
  15. .git/objects/48/369b05749e384be9be58e5f943f3a0040d0f37 +0 -0
  16. .git/objects/7c/00eec7ae9ef5f94fc337e5c8f9793a2a48810d +0 -0
  17. .git/objects/a3/cedc28e563a1845a7860161b39b0fe58d5f0d3 +0 -0
  18. .git/objects/a5/b7e4e1b63bfb65288f6553687aaabcfb4d51b1 +0 -0
  19. .git/objects/d0/9c84ad2142a187bf26a1714b7041b62e404c8f +0 -0
  20. .git/objects/d9/6d7456245232b3e159dcf691f11e51224c557a +0 -0
  21. .git/objects/e8/2f42ea26b8bf4f0bc92c0648ac8f190f14226d +0 -0
  22. .git/objects/ed/fb5bbf938de738d1f658f80e23c675bb8b3fae +0 -0
  23. .git/objects/ef/4844af55f7f64b8dd24b81b5a8322a8c80208e +0 -0
  24. .git/objects/f2/2bfffda9fb6a7f37bed04b7c40c9466ef09454 +0 -0
  25. .git/objects/f5/18c69a6e1bf3052b79da01502b2837ea58f0f4 +0 -0
  26. .git/objects/pack/pack-289f7bb06603881c49190e6036de6390223baf77.idx +0 -0
  27. .git/objects/pack/{pack-55d9855fa45cb686f64bef472f9a7940ef78b8d6.pack → pack-289f7bb06603881c49190e6036de6390223baf77.pack} +0 -0
  28. .git/objects/pack/pack-289f7bb06603881c49190e6036de6390223baf77.rev +0 -0
  29. .git/packed-refs +1 -1
  30. .git/refs/heads/iyue +1 -1
  31. .git/refs/remotes/gitlab/iyue +1 -0
  32. .git/refs/remotes/origin/iyue +1 -0
  33. README.md +4 -4
  34. core/info_decoder.py +332 -10
  35. core/reconstructor.py +107 -3
  36. generation/proto_generator.py +34 -0
  37. main.py +2 -2
  38. parsing/enum_parser.py +4 -4
  39. parsing/java_parser.py +101 -1
  40. pyproject.toml +1 -1
  41. {reproto-0.0.4.dist-info → reproto-0.0.6.dist-info}/METADATA +5 -5
  42. reproto-0.0.6.dist-info/RECORD +75 -0
  43. utils/logger.py +2 -2
  44. .git/objects/pack/pack-55d9855fa45cb686f64bef472f9a7940ef78b8d6.idx +0 -0
  45. .git/objects/pack/pack-55d9855fa45cb686f64bef472f9a7940ef78b8d6.rev +0 -0
  46. reproto-0.0.4.dist-info/RECORD +0 -53
  47. {reproto-0.0.4.dist-info → reproto-0.0.6.dist-info}/WHEEL +0 -0
  48. {reproto-0.0.4.dist-info → reproto-0.0.6.dist-info}/entry_points.txt +0 -0
.git/COMMIT_EDITMSG ADDED
@@ -0,0 +1 @@
1
+ feat: rename reproto
.git/config CHANGED
@@ -10,3 +10,6 @@
10
10
  remote = origin
11
11
  merge = refs/heads/iyue
12
12
  vscode-merge-base = origin/iyue
13
+ [remote "gitlab"]
14
+ url = git@ys1231.cn:tools/generate_protobuf.git
15
+ fetch = +refs/heads/*:refs/remotes/gitlab/*
.git/index CHANGED
Binary file
.git/logs/HEAD CHANGED
@@ -1 +1,3 @@
1
- 0000000000000000000000000000000000000000 514d3a743bced9f5c012a21032b49b81daec78ff iyue <ys1231@126.com> 1750903749 +0800 clone: from github.com:ys1231/reproto.git
1
+ 0000000000000000000000000000000000000000 2e11d561668286f3d6d48f399ffab1943a5cdcb2 iyue <ys1231@126.com> 1750922365 +0800 clone: from github.com:ys1231/reproto.git
2
+ 2e11d561668286f3d6d48f399ffab1943a5cdcb2 f22bfffda9fb6a7f37bed04b7c40c9466ef09454 iyue <ys1231@126.com> 1750925875 +0800 commit: feat: 完善Protobuf类型推断系统,支持map和未知类型处理
3
+ f22bfffda9fb6a7f37bed04b7c40c9466ef09454 e82f42ea26b8bf4f0bc92c0648ac8f190f14226d iyue <ys1231@126.com> 1750927650 +0800 commit: feat: rename reproto
.git/logs/refs/heads/iyue CHANGED
@@ -1 +1,3 @@
1
- 0000000000000000000000000000000000000000 514d3a743bced9f5c012a21032b49b81daec78ff iyue <ys1231@126.com> 1750903749 +0800 clone: from github.com:ys1231/reproto.git
1
+ 0000000000000000000000000000000000000000 2e11d561668286f3d6d48f399ffab1943a5cdcb2 iyue <ys1231@126.com> 1750922365 +0800 clone: from github.com:ys1231/reproto.git
2
+ 2e11d561668286f3d6d48f399ffab1943a5cdcb2 f22bfffda9fb6a7f37bed04b7c40c9466ef09454 iyue <ys1231@126.com> 1750925875 +0800 commit: feat: 完善Protobuf类型推断系统,支持map和未知类型处理
3
+ f22bfffda9fb6a7f37bed04b7c40c9466ef09454 e82f42ea26b8bf4f0bc92c0648ac8f190f14226d iyue <ys1231@126.com> 1750927650 +0800 commit: feat: rename reproto
@@ -0,0 +1,2 @@
1
+ 0000000000000000000000000000000000000000 f22bfffda9fb6a7f37bed04b7c40c9466ef09454 iyue <ys1231@126.com> 1750926057 +0800 update by push
2
+ f22bfffda9fb6a7f37bed04b7c40c9466ef09454 e82f42ea26b8bf4f0bc92c0648ac8f190f14226d iyue <ys1231@126.com> 1750927662 +0800 update by push
@@ -1 +1 @@
1
- 0000000000000000000000000000000000000000 514d3a743bced9f5c012a21032b49b81daec78ff iyue <ys1231@126.com> 1750903749 +0800 clone: from github.com:ys1231/reproto.git
1
+ 0000000000000000000000000000000000000000 2e11d561668286f3d6d48f399ffab1943a5cdcb2 iyue <ys1231@126.com> 1750922365 +0800 clone: from github.com:ys1231/reproto.git
@@ -0,0 +1,2 @@
1
+ 2e11d561668286f3d6d48f399ffab1943a5cdcb2 f22bfffda9fb6a7f37bed04b7c40c9466ef09454 iyue <ys1231@126.com> 1750925918 +0800 update by push
2
+ f22bfffda9fb6a7f37bed04b7c40c9466ef09454 e82f42ea26b8bf4f0bc92c0648ac8f190f14226d iyue <ys1231@126.com> 1750927669 +0800 update by push
.git/packed-refs CHANGED
@@ -1,2 +1,2 @@
1
1
  # pack-refs with: peeled fully-peeled sorted
2
- 514d3a743bced9f5c012a21032b49b81daec78ff refs/remotes/origin/iyue
2
+ 2e11d561668286f3d6d48f399ffab1943a5cdcb2 refs/remotes/origin/iyue
.git/refs/heads/iyue CHANGED
@@ -1 +1 @@
1
- 514d3a743bced9f5c012a21032b49b81daec78ff
1
+ e82f42ea26b8bf4f0bc92c0648ac8f190f14226d
@@ -0,0 +1 @@
1
+ e82f42ea26b8bf4f0bc92c0648ac8f190f14226d
@@ -0,0 +1 @@
1
+ e82f42ea26b8bf4f0bc92c0648ac8f190f14226d
README.md CHANGED
@@ -36,7 +36,7 @@ python main.py <java_sources_dir> <root_class> <output_dir>
36
36
  pip install -e .
37
37
 
38
38
  # 使用命令行工具
39
- proto-reconstructor <java_sources_dir> <root_class> <output_dir>
39
+ reproto <java_sources_dir> <root_class> <output_dir>
40
40
  ```
41
41
 
42
42
  ## 📖 使用方法
@@ -87,7 +87,7 @@ Java源码 → 字节码提取 → 类型解码 → 依赖发现 → 源码验
87
87
  ## 📁 项目结构
88
88
 
89
89
  ```
90
- proto_reconstructor/
90
+ reproto/
91
91
  ├── main.py # 主程序入口
92
92
  ├── core/ # 核心组件
93
93
  │ ├── reconstructor.py # 主协调器
@@ -188,8 +188,8 @@ reproto/
188
188
  # 指定日志目录
189
189
  python main.py sources/ com.example.Model output/ --log-dir ./my_logs
190
190
 
191
- # 日志文件格式: proto_reconstructor-YYYY-MM-DD-HH-MM-SS.log
192
- # 例如: proto_reconstructor-2024-01-15-14-30-25.log
191
+ # 日志文件格式: reproto-YYYY-MM-DD-HH-MM-SS.log
192
+ # 例如: reproto-2024-01-15-14-30-25.log
193
193
  ```
194
194
 
195
195
  ### 输出格式
core/info_decoder.py CHANGED
@@ -36,8 +36,10 @@ class InfoDecoder:
36
36
  """
37
37
  self.logger = get_logger("info_decoder")
38
38
 
39
- # 字节码到Protobuf类型的映射表(逆向工程的核心成果)
39
+ # Protobuf字段类型映射表
40
+ # 键:字节码中的类型值,值:对应的protobuf字段类型
40
41
  self.type_mapping = {
42
+ 0: 'double', # 64位浮点数 (double) - 基于ContactAddress.latitude_和longitude_的分析
41
43
  1: 'float', # FLOAT
42
44
  2: 'int64', # INT64
43
45
  3: 'int32', # INT32
@@ -48,12 +50,16 @@ class InfoDecoder:
48
50
  27: 'message', # REPEATED MESSAGE
49
51
  39: 'int32', # REPEATED INT32 (packed)
50
52
  44: 'enum', # PACKED ENUM
51
- 520: 'string', # STRING = 520)
53
+ 50: 'map', # Map字段 - 基于BulkSearchResult.contacts的分析
54
+ 520: 'string', # UTF-8字符串
52
55
  538: 'string', # REPEATED STRING (Ț = 538)
53
56
  }
54
57
 
55
58
  # Java源码分析器
56
59
  self.java_source_analyzer = java_source_analyzer
60
+
61
+ # 统计未知类型(用于持续改进)
62
+ self.unknown_types_stats = {} # {byte_code: count}
57
63
 
58
64
  def decode_message_info(self, class_name: str, info_string: str, objects: List[str]) -> Optional[MessageDefinition]:
59
65
  """
@@ -151,7 +157,7 @@ class InfoDecoder:
151
157
 
152
158
  Args:
153
159
  message_def: 消息定义对象
154
- bytes_data: 字节数组
160
+ bytes_data: 字节码数据
155
161
  objects: 对象数组
156
162
  """
157
163
  # 跳过前10个字节的元数据
@@ -163,11 +169,17 @@ class InfoDecoder:
163
169
  field_tag = bytes_data[i]
164
170
  field_type_byte = bytes_data[i + 1]
165
171
 
166
- # 查找类型映射
172
+ # 查找类型映射,对未知类型进行智能处理
167
173
  if field_type_byte not in self.type_mapping:
168
- continue
174
+ # 统计未知类型
175
+ self.unknown_types_stats[field_type_byte] = self.unknown_types_stats.get(field_type_byte, 0) + 1
169
176
 
170
- field_type = self.type_mapping[field_type_byte]
177
+ # 记录未知类型,但不跳过字段
178
+ self.logger.warning(f" ⚠️ 发现未知字节码类型: {field_type_byte} (0x{field_type_byte:02x})")
179
+ field_type = self._analyze_unknown_type_with_source_priority(field_type_byte, objects, object_index)
180
+ self.logger.info(f" 🔍 推断未知类型: {field_type_byte} -> {field_type}")
181
+ else:
182
+ field_type = self.type_mapping[field_type_byte]
171
183
 
172
184
  # 从对象数组获取字段信息
173
185
  field_info = self._extract_field_info(objects, object_index, field_type)
@@ -218,15 +230,20 @@ class InfoDecoder:
218
230
  # 确定字段类型名
219
231
  field_type_name = field_type # 默认使用基础类型
220
232
 
221
- # 对于消息类型和枚举类型,检查objects数组中是否有具体的类型引用
222
- if field_type in ['message', 'enum']:
233
+ # 对于消息类型、枚举类型和map类型,检查objects数组中是否有具体的类型引用
234
+ if field_type in ['message', 'enum', 'map']:
223
235
  if object_index < len(objects):
224
236
  next_obj = objects[object_index]
225
237
  if self._is_type_reference(next_obj):
226
238
  # 直接使用objects数组中的类型引用,这是最准确的信息源
227
- field_type_name = self._clean_type_reference(next_obj)
239
+ if field_type == 'map':
240
+ # 对于map类型,从MapEntry引用中推断键值类型
241
+ field_type_name = self._extract_map_type_from_entry(next_obj, field_name_raw)
242
+ self.logger.info(f" 🗺️ 从MapEntry获取map类型: {field_name} -> {field_type_name}")
243
+ else:
244
+ field_type_name = self._clean_type_reference(next_obj)
245
+ self.logger.info(f" 🔗 从objects数组获取类型: {field_name} -> {field_type_name}")
228
246
  object_index += 1
229
- self.logger.info(f" 🔗 从objects数组获取类型: {field_name} -> {field_type_name}")
230
247
  else:
231
248
  # 没有显式引用,优先从Java源码中获取真实类型
232
249
  real_type = self._get_real_field_type_from_source(field_name_raw, field_type)
@@ -241,6 +258,9 @@ class InfoDecoder:
241
258
  elif field_type == 'message':
242
259
  field_type_name = self._infer_message_type_from_field_name(field_name_raw)
243
260
  self.logger.info(f" 🔍 推断消息类型: {field_name} -> {field_type_name}")
261
+ elif field_type == 'map':
262
+ field_type_name = self._infer_map_type_from_source(field_name_raw)
263
+ self.logger.info(f" 🔍 推断map类型: {field_name} -> {field_type_name}")
244
264
  else:
245
265
  # objects数组已结束,优先从Java源码中获取真实类型
246
266
  real_type = self._get_real_field_type_from_source(field_name_raw, field_type)
@@ -255,6 +275,9 @@ class InfoDecoder:
255
275
  elif field_type == 'message':
256
276
  field_type_name = self._infer_message_type_from_field_name(field_name_raw)
257
277
  self.logger.info(f" 🔍 推断消息类型: {field_name} -> {field_type_name}")
278
+ elif field_type == 'map':
279
+ field_type_name = self._infer_map_type_from_source(field_name_raw)
280
+ self.logger.info(f" 🔍 推断map类型: {field_name} -> {field_type_name}")
258
281
 
259
282
  return field_name, field_type_name, object_index
260
283
 
@@ -486,6 +509,305 @@ class InfoDecoder:
486
509
  if oneof_def.fields:
487
510
  message_def.oneofs.append(oneof_def)
488
511
 
512
+ def _extract_map_type_from_entry(self, entry_ref: str, field_name_raw: str) -> str:
513
+ """
514
+ 从MapEntry引用中提取map的键值类型
515
+
516
+ Args:
517
+ entry_ref: MapEntry引用,如 "qux.f107553a"
518
+ field_name_raw: 原始字段名,用于推断类型
519
+
520
+ Returns:
521
+ map类型字符串,如 "map<string, Contact>"
522
+ """
523
+ try:
524
+ # 优先从Java源码中获取真实的map类型
525
+ if self.java_source_analyzer:
526
+ real_type = self.java_source_analyzer.get_field_type(field_name_raw, 'map')
527
+ if real_type and real_type.startswith('map<'):
528
+ return real_type
529
+
530
+ # 如果无法从源码获取,进行智能推断
531
+ return self._infer_map_type_from_source(field_name_raw)
532
+
533
+ except Exception as e:
534
+ self.logger.warning(f" ⚠️ 从MapEntry提取类型失败: {e}")
535
+ return self._infer_map_type_from_source(field_name_raw)
536
+
537
+ def _infer_map_type_from_source(self, field_name_raw: str) -> str:
538
+ """
539
+ 从字段名推断map类型
540
+
541
+ Args:
542
+ field_name_raw: 原始字段名(如 contacts_)
543
+
544
+ Returns:
545
+ 推断的map类型字符串
546
+ """
547
+ # 移除末尾的下划线
548
+ clean_name = field_name_raw.rstrip('_')
549
+
550
+ # 基于字段名的通用推断规则
551
+ if clean_name.lower().endswith('map') or clean_name.lower().endswith('mapping'):
552
+ # xxxMap -> map<string, Xxx>
553
+ base_name = clean_name[:-3] if clean_name.lower().endswith('map') else clean_name[:-7]
554
+ value_type = self._camel_to_pascal_case(base_name) if base_name else 'string'
555
+ return f"map<string, {value_type}>"
556
+ elif clean_name.lower() in ['contacts', 'users', 'profiles']:
557
+ # 常见的复数形式字段,推断为实体映射
558
+ singular = clean_name[:-1] if clean_name.endswith('s') else clean_name
559
+ value_type = self._camel_to_pascal_case(singular)
560
+ return f"map<string, {value_type}>"
561
+ elif clean_name.lower().endswith('tags'):
562
+ # xxxTags -> map<string, string> (标签通常是字符串到字符串的映射)
563
+ return "map<string, string>"
564
+ elif clean_name.lower().endswith('ids'):
565
+ # xxxIds -> map<string, string> (ID映射)
566
+ return "map<string, string>"
567
+ else:
568
+ # 默认推断:字段名作为值类型
569
+ value_type = self._camel_to_pascal_case(clean_name)
570
+ return f"map<string, {value_type}>"
571
+
572
+ def _analyze_unknown_type_with_source_priority(self, field_type_byte: int, objects: List[str], object_index: int) -> str:
573
+ """
574
+ 分析未知字节码类型,进行智能推断,优先使用Java源码分析结果
575
+
576
+ Args:
577
+ field_type_byte: 未知的字节码类型
578
+ objects: 对象数组
579
+ object_index: 当前对象索引
580
+
581
+ Returns:
582
+ 推断的字段类型
583
+ """
584
+ # 分析字节码的结构
585
+ wire_type = field_type_byte & 7 # 低3位是wire type
586
+ field_number = field_type_byte >> 3 # 高位是field number
587
+
588
+ self.logger.debug(f" 🔬 字节码分析: byte={field_type_byte}, wire_type={wire_type}, field_number={field_number}")
589
+
590
+ # 第一步:尝试从Java源码获取真实类型
591
+ java_type = None
592
+ if object_index < len(objects) and self.java_source_analyzer:
593
+ field_name_raw = objects[object_index]
594
+ try:
595
+ java_type = self._get_java_field_type_for_unknown(field_name_raw)
596
+ if java_type:
597
+ self.logger.info(f" ✅ Java源码分析: {field_name_raw} -> {java_type}")
598
+ except Exception as e:
599
+ self.logger.debug(f" ⚠️ Java源码分析失败: {e}")
600
+
601
+ # 第二步:基于wire type进行字节码推断
602
+ bytecode_type = self._analyze_unknown_type_by_wire_type(wire_type, objects, object_index, field_type_byte)
603
+
604
+ # 第三步:交叉校验和最终决策
605
+ final_type = self._cross_validate_types(java_type, bytecode_type, wire_type, field_type_byte)
606
+
607
+ if java_type and java_type != final_type:
608
+ self.logger.info(f" 🔄 类型校验: Java({java_type}) vs 字节码({bytecode_type}) -> 最终({final_type})")
609
+
610
+ return final_type
611
+
612
+ def _get_java_field_type_for_unknown(self, field_name_raw: str) -> Optional[str]:
613
+ """
614
+ 从Java源码中获取未知字段的真实类型
615
+
616
+ Args:
617
+ field_name_raw: 原始字段名(如 latitude_)
618
+
619
+ Returns:
620
+ Java字段的proto类型,如果无法获取则返回None
621
+ """
622
+ if not self.java_source_analyzer:
623
+ return None
624
+
625
+ try:
626
+ # 获取Java字段的原始类型
627
+ java_raw_type = self.java_source_analyzer.get_raw_field_type(field_name_raw)
628
+ if not java_raw_type:
629
+ return None
630
+
631
+ # 将Java类型转换为proto类型
632
+ proto_type = self._java_type_to_proto_type(java_raw_type)
633
+ return proto_type
634
+
635
+ except Exception as e:
636
+ self.logger.debug(f" ⚠️ 获取Java字段类型失败: {e}")
637
+ return None
638
+
639
+ def _java_type_to_proto_type(self, java_type: str) -> str:
640
+ """
641
+ 将Java类型转换为proto类型
642
+
643
+ Args:
644
+ java_type: Java类型字符串
645
+
646
+ Returns:
647
+ 对应的proto类型
648
+ """
649
+ # 基础类型映射
650
+ type_mapping = {
651
+ 'boolean': 'bool',
652
+ 'byte': 'int32',
653
+ 'short': 'int32',
654
+ 'int': 'int32',
655
+ 'long': 'int64',
656
+ 'float': 'float',
657
+ 'double': 'double',
658
+ 'String': 'string',
659
+ 'ByteString': 'bytes',
660
+ }
661
+
662
+ # 直接映射
663
+ if java_type in type_mapping:
664
+ return type_mapping[java_type]
665
+
666
+ # 处理复杂类型
667
+ if java_type.startswith('MapFieldLite<'):
668
+ return 'map'
669
+ elif java_type.startswith('Internal.ProtobufList<') or java_type.startswith('List<'):
670
+ return 'message' # repeated message
671
+ elif java_type.endswith('[]'):
672
+ return 'message' # repeated
673
+ elif '.' in java_type and java_type.split('.')[-1][0].isupper():
674
+ # 看起来像是类名,可能是message或enum
675
+ return 'message' # 默认为message,具体类型由其他逻辑确定
676
+
677
+ # 默认返回string
678
+ return 'string'
679
+
680
+ def _analyze_unknown_type_by_wire_type(self, wire_type: int, objects: List[str], object_index: int, field_type_byte: int) -> str:
681
+ """
682
+ 基于wire type分析未知字节码类型
683
+
684
+ Args:
685
+ wire_type: wire type (0-5)
686
+ objects: 对象数组
687
+ object_index: 当前对象索引
688
+ field_type_byte: 原始字节码类型
689
+
690
+ Returns:
691
+ 推断的字段类型
692
+ """
693
+ if wire_type == 0:
694
+ # VARINT: int32, int64, uint32, uint64, sint32, sint64, bool, enum
695
+ return self._infer_varint_type(objects, object_index)
696
+ elif wire_type == 1:
697
+ # 64-BIT: fixed64, sfixed64, double
698
+ return 'double' # 默认为double(比int64更常见)
699
+ elif wire_type == 2:
700
+ # LENGTH_DELIMITED: string, bytes, embedded messages, packed repeated fields
701
+ return self._infer_length_delimited_type(objects, object_index, field_type_byte)
702
+ elif wire_type == 5:
703
+ # 32-BIT: fixed32, sfixed32, float
704
+ return 'float' # 默认为float
705
+ else:
706
+ # 其他未知wire type
707
+ self.logger.warning(f" ⚠️ 未知wire type: {wire_type}")
708
+ return self._fallback_type_inference(objects, object_index)
709
+
710
+ def _cross_validate_types(self, java_type: Optional[str], bytecode_type: str, wire_type: int, field_type_byte: int) -> str:
711
+ """
712
+ 交叉校验Java类型和字节码类型,返回最终类型
713
+
714
+ Args:
715
+ java_type: Java源码分析得到的类型
716
+ bytecode_type: 字节码分析得到的类型
717
+ wire_type: wire type
718
+ field_type_byte: 原始字节码类型
719
+
720
+ Returns:
721
+ 最终确定的字段类型
722
+ """
723
+ # 如果没有Java类型信息,使用字节码推断
724
+ if not java_type:
725
+ return bytecode_type
726
+
727
+ # 如果Java类型和字节码类型一致,直接返回
728
+ if java_type == bytecode_type:
729
+ return java_type
730
+
731
+ # 类型不一致时的校验逻辑
732
+ if wire_type == 0: # VARINT
733
+ # 对于VARINT类型,Java源码更准确
734
+ if java_type in ['bool', 'int32', 'int64', 'uint32', 'uint64', 'sint32', 'sint64']:
735
+ return java_type
736
+ elif java_type == 'message': # 可能是enum
737
+ return 'enum' if bytecode_type == 'enum' else java_type
738
+ elif wire_type == 1: # 64-BIT
739
+ # 对于64位类型,Java源码更准确
740
+ if java_type in ['double', 'fixed64', 'sfixed64']:
741
+ return java_type
742
+ elif wire_type == 2: # LENGTH_DELIMITED
743
+ # 对于长度分隔类型,Java源码更准确
744
+ if java_type in ['string', 'bytes', 'message', 'map']:
745
+ return java_type
746
+ elif wire_type == 5: # 32-BIT
747
+ # 对于32位类型,Java源码更准确
748
+ if java_type in ['float', 'fixed32', 'sfixed32']:
749
+ return java_type
750
+
751
+ # 默认优先使用Java类型
752
+ self.logger.info(f" 🔧 类型冲突,优先使用Java类型: {java_type} (字节码推断: {bytecode_type})")
753
+ return java_type
754
+
755
+ def _infer_varint_type(self, objects: List[str], object_index: int) -> str:
756
+ """推断VARINT类型字段"""
757
+ # 检查objects数组中是否有类型提示
758
+ if object_index < len(objects):
759
+ field_name = objects[object_index].rstrip('_')
760
+
761
+ # 基于字段名推断
762
+ if any(keyword in field_name.lower() for keyword in ['type', 'status', 'mode', 'enum']):
763
+ return 'enum'
764
+ elif field_name.lower() in ['count', 'size', 'length', 'number']:
765
+ return 'int32'
766
+ elif field_name.lower().endswith('_id') or field_name.lower() == 'id':
767
+ return 'int64'
768
+ elif field_name.lower() in ['enabled', 'visible', 'active', 'valid']:
769
+ return 'bool'
770
+
771
+ return 'int32' # 默认为int32
772
+
773
+ def _infer_length_delimited_type(self, objects: List[str], object_index: int, field_type_byte: int) -> str:
774
+ """推断LENGTH_DELIMITED类型字段"""
775
+ # 检查是否可能是map类型(基于已知的map类型字节码模式)
776
+ if field_type_byte == 50 or field_type_byte in range(48, 60): # 扩展map类型的可能范围
777
+ return 'map'
778
+
779
+ # 检查objects数组中是否有类型提示
780
+ if object_index < len(objects):
781
+ field_name = objects[object_index].rstrip('_')
782
+
783
+ # 基于字段名推断
784
+ if field_name.lower().endswith('map') or field_name.lower().endswith('mapping'):
785
+ return 'map'
786
+ elif field_name.lower() in ['name', 'title', 'description', 'text', 'url', 'email']:
787
+ return 'string'
788
+ elif field_name.lower().endswith('data') or field_name.lower().endswith('bytes'):
789
+ return 'bytes'
790
+ elif field_name.lower().endswith('s') and len(field_name) > 2:
791
+ # 复数形式,可能是repeated字段
792
+ return 'message' # repeated message
793
+
794
+ return 'string' # 默认为string
795
+
796
+ def _fallback_type_inference(self, objects: List[str], object_index: int) -> str:
797
+ """兜底类型推断"""
798
+ if object_index < len(objects):
799
+ field_name = objects[object_index].rstrip('_')
800
+
801
+ # 基于字段名的通用推断
802
+ if any(keyword in field_name.lower() for keyword in ['id', 'count', 'size', 'number']):
803
+ return 'int32'
804
+ elif any(keyword in field_name.lower() for keyword in ['name', 'title', 'text', 'url']):
805
+ return 'string'
806
+ elif field_name.lower().endswith('s'):
807
+ return 'message' # 可能是repeated字段
808
+
809
+ return 'string' # 最终兜底
810
+
489
811
  @staticmethod
490
812
  def _to_snake_case(camel_str: str) -> str:
491
813
  """
core/reconstructor.py CHANGED
@@ -15,6 +15,7 @@ from collections import deque
15
15
  from typing import Set, Dict, List, Optional
16
16
 
17
17
  from parsing.java_parser import JavaParser
18
+ from parsing.enum_parser import EnumParser
18
19
  from core.info_decoder import InfoDecoder
19
20
  from generation.proto_generator import ProtoGenerator
20
21
  from models.message_definition import MessageDefinition, EnumDefinition, EnumValueDefinition
@@ -28,19 +29,44 @@ class JavaSourceAnalyzer:
28
29
  self.sources_dir = sources_dir
29
30
  self._current_class_content = None
30
31
  self._current_class_name = None
32
+ # 初始化JavaParser用于字段类型解析
33
+ self.java_parser = JavaParser()
31
34
 
32
35
  def set_current_class(self, class_name: str):
33
36
  """设置当前分析的类"""
34
37
  self._current_class_name = class_name
35
38
  self._current_class_content = self._load_class_content(class_name)
36
39
 
40
+ def get_raw_field_type(self, field_name_raw: str) -> Optional[str]:
41
+ """
42
+ 获取字段的原始Java类型
43
+
44
+ Args:
45
+ field_name_raw: 原始字段名(如 latitude_)
46
+
47
+ Returns:
48
+ 字段的Java原始类型,如果找不到则返回None
49
+ """
50
+ if not self._current_class_name:
51
+ return None
52
+
53
+ # 构建Java文件路径
54
+ file_path = self._current_class_name.replace('.', '/') + '.java'
55
+ java_file_path = self.sources_dir / file_path
56
+
57
+ if not java_file_path.exists():
58
+ return None
59
+
60
+ # 使用JavaParser获取字段类型
61
+ return self.java_parser.get_raw_field_type(java_file_path, field_name_raw)
62
+
37
63
  def get_field_type(self, field_name_raw: str, expected_type: str) -> Optional[str]:
38
64
  """
39
65
  从Java源码中获取字段的真实类型
40
66
 
41
67
  Args:
42
68
  field_name_raw: 原始字段名(如 id_)
43
- expected_type: 期望的基础类型(message 或 enum
69
+ expected_type: 期望的基础类型(message、enummap
44
70
 
45
71
  Returns:
46
72
  真实的类型名,如果无法获取则返回None
@@ -51,6 +77,12 @@ class JavaSourceAnalyzer:
51
77
  # 清理字段名
52
78
  field_name = field_name_raw.rstrip('_')
53
79
 
80
+ # 对于map类型,特殊处理MapFieldLite声明
81
+ if expected_type == 'map':
82
+ map_type = self._get_map_type_from_field(field_name)
83
+ if map_type:
84
+ return map_type
85
+
54
86
  # 对于枚举类型,优先从setter方法中获取类型
55
87
  if expected_type == 'enum':
56
88
  setter_type = self._get_type_from_setter(field_name)
@@ -82,6 +114,63 @@ class JavaSourceAnalyzer:
82
114
 
83
115
  return None
84
116
 
117
+ def _get_map_type_from_field(self, field_name: str) -> Optional[str]:
118
+ """
119
+ 从MapFieldLite字段声明中获取map的键值类型
120
+
121
+ Args:
122
+ field_name: 字段名(如 contacts)
123
+
124
+ Returns:
125
+ map类型字符串,如 "map<string, Contact>"
126
+ """
127
+ # 查找MapFieldLite字段声明:private MapFieldLite<String, Contact> contacts_ = ...
128
+ pattern = rf'private\s+MapFieldLite<([^,]+),\s*([^>]+)>\s+{re.escape(field_name)}_\s*='
129
+ matches = re.findall(pattern, self._current_class_content)
130
+
131
+ if matches:
132
+ key_type, value_type = matches[0]
133
+ key_type = key_type.strip()
134
+ value_type = value_type.strip()
135
+
136
+ # 转换Java类型到protobuf类型
137
+ proto_key_type = self._java_type_to_proto_type(key_type)
138
+ proto_value_type = self._java_type_to_proto_type(value_type)
139
+
140
+ return f"map<{proto_key_type}, {proto_value_type}>"
141
+
142
+ return None
143
+
144
+ def _java_type_to_proto_type(self, java_type: str) -> str:
145
+ """
146
+ 将Java类型转换为protobuf类型
147
+
148
+ Args:
149
+ java_type: Java类型名
150
+
151
+ Returns:
152
+ protobuf类型名
153
+ """
154
+ # 基础类型映射
155
+ basic_types = {
156
+ 'String': 'string',
157
+ 'Integer': 'int32',
158
+ 'Long': 'int64',
159
+ 'Boolean': 'bool',
160
+ 'Float': 'float',
161
+ 'Double': 'double',
162
+ 'ByteString': 'bytes'
163
+ }
164
+
165
+ if java_type in basic_types:
166
+ return basic_types[java_type]
167
+
168
+ # 对于其他类型,去掉包名,只保留类名
169
+ if '.' in java_type:
170
+ return java_type.split('.')[-1]
171
+
172
+ return java_type
173
+
85
174
  def _get_type_from_setter(self, field_name: str) -> Optional[str]:
86
175
  """
87
176
  从setter方法中获取字段的真实类型(特别适用于枚举类型)
@@ -189,16 +278,19 @@ class ProtoReconstructor:
189
278
  # 广度优先处理所有依赖类
190
279
  self._process_all_classes()
191
280
 
192
- # 生成最终的proto文件
281
+ # 生成最终的proto文件
193
282
  self._generate_all_proto_files()
194
283
 
284
+ # 报告未知类型统计
285
+ self._report_unknown_types()
286
+
195
287
  # 返回处理结果
196
288
  results = {}
197
289
  for class_name, message_def in self.message_definitions.items():
198
290
  results[class_name] = message_def
199
291
  for class_name, enum_def in self.enum_definitions.items():
200
292
  results[class_name] = enum_def
201
-
293
+
202
294
  return results
203
295
 
204
296
  def _process_all_classes(self) -> None:
@@ -666,6 +758,18 @@ class ProtoReconstructor:
666
758
 
667
759
  return self.output_dir / package_path / proto_name
668
760
 
761
+ def _report_unknown_types(self) -> None:
762
+ """报告未知字节码类型的统计信息"""
763
+ if not self.info_decoder.unknown_types_stats:
764
+ return
765
+
766
+ self.logger.warning("📊 发现未知字节码类型统计:")
767
+ for byte_code, count in sorted(self.info_decoder.unknown_types_stats.items()):
768
+ wire_type = byte_code & 7
769
+ self.logger.warning(f" 类型 {byte_code} (0x{byte_code:02x}, wire_type={wire_type}): {count} 次")
770
+
771
+ self.logger.warning("💡 建议: 请将这些信息反馈给开发者,以便完善类型映射表")
772
+
669
773
  @staticmethod
670
774
  def _to_snake_case(camel_str: str) -> str:
671
775
  """
@@ -262,6 +262,23 @@ class ProtoGenerator:
262
262
  if field.type_name in basic_types:
263
263
  return None
264
264
 
265
+ # 处理map类型:map<string, Contact> -> 提取值类型Contact
266
+ if field.type_name.startswith('map<'):
267
+ # 解析map类型:map<key_type, value_type>
268
+ import re
269
+ match = re.match(r'map<([^,]+),\s*([^>]+)>', field.type_name)
270
+ if match:
271
+ key_type, value_type = match.groups()
272
+ key_type = key_type.strip()
273
+ value_type = value_type.strip()
274
+
275
+ # 只处理值类型的导入(键类型通常是基础类型)
276
+ if value_type not in basic_types:
277
+ full_class_name = self._resolve_full_class_name(value_type, current_package, all_messages)
278
+ if full_class_name:
279
+ return self._class_name_to_import_path(full_class_name)
280
+ return None
281
+
265
282
  # 解析完整类名
266
283
  full_class_name = self._resolve_full_class_name(field.type_name, current_package, all_messages)
267
284
  if full_class_name:
@@ -325,6 +342,23 @@ class ProtoGenerator:
325
342
  if basic_type:
326
343
  return basic_type
327
344
 
345
+ # 处理map类型:map<string, Contact> -> map<string, Contact>
346
+ if field.type_name.startswith('map<'):
347
+ # 解析map类型并清理值类型名
348
+ import re
349
+ match = re.match(r'map<([^,]+),\s*([^>]+)>', field.type_name)
350
+ if match:
351
+ key_type, value_type = match.groups()
352
+ key_type = key_type.strip()
353
+ value_type = value_type.strip()
354
+
355
+ # 如果值类型是完整类名,提取简单类型名
356
+ if '.' in value_type:
357
+ value_type = value_type.split('.')[-1]
358
+
359
+ return f"map<{key_type}, {value_type}>"
360
+ return field.type_name
361
+
328
362
  # 枚举类型:根据字段名生成枚举类型名
329
363
  if field.type_name == 'enum':
330
364
  return self._generate_enum_type_name(field.name)
main.py CHANGED
@@ -6,10 +6,10 @@ Protobuf重构器 - 命令行入口
6
6
  支持任意Android应用,完全基于Java字节码推断
7
7
 
8
8
  Usage:
9
- python -m proto_reconstructor.main <java_sources_dir> <root_class> <output_dir> [--log-dir LOG_DIR]
9
+ python -m reproto.main <java_sources_dir> <root_class> <output_dir> [--log-dir LOG_DIR]
10
10
 
11
11
  Example:
12
- python -m proto_reconstructor.main ./out_jadx/sources com.example.Model ./protos_generated --log-dir ./logs
12
+ python -m reproto.main ./out_jadx/sources com.example.Model ./protos_generated --log-dir ./logs
13
13
 
14
14
  Author: AI Assistant
15
15
  """
parsing/enum_parser.py CHANGED
@@ -8,8 +8,8 @@
8
8
  import re
9
9
  import os
10
10
  from typing import List, Optional, Dict, Tuple
11
- from ..models.message_definition import EnumDefinition, EnumValue
12
- from ..utils.logger import get_logger
11
+ from models.message_definition import EnumDefinition, EnumValueDefinition
12
+ from utils.logger import get_logger
13
13
 
14
14
 
15
15
  class EnumParser:
@@ -129,7 +129,7 @@ class EnumParser:
129
129
 
130
130
  return enum_def
131
131
 
132
- def _extract_enum_values(self, content: str) -> List[EnumValue]:
132
+ def _extract_enum_values(self, content: str) -> List[EnumValueDefinition]:
133
133
  """
134
134
  从Java内容中提取枚举值
135
135
 
@@ -161,7 +161,7 @@ class EnumParser:
161
161
 
162
162
  try:
163
163
  value = int(value_str)
164
- enum_values.append(EnumValue(name=name, value=value))
164
+ enum_values.append(EnumValueDefinition(name=name, value=value))
165
165
  except ValueError:
166
166
  continue
167
167
 
parsing/java_parser.py CHANGED
@@ -253,4 +253,104 @@ class JavaParser:
253
253
  # 按数值排序
254
254
  enum_values.sort(key=lambda x: x[1])
255
255
 
256
- return enum_values
256
+ return enum_values
257
+
258
+ def get_raw_field_type(self, java_file_path: Path, field_name_raw: str) -> Optional[str]:
259
+ """
260
+ 从Java文件中获取指定字段的原始类型
261
+
262
+ Args:
263
+ java_file_path: Java文件路径
264
+ field_name_raw: 原始字段名(如 latitude_)
265
+
266
+ Returns:
267
+ 字段的Java原始类型,如果找不到则返回None
268
+ """
269
+ try:
270
+ # 读取Java文件内容
271
+ content = java_file_path.read_text(encoding='utf-8')
272
+
273
+ # 查找字段声明
274
+ field_type = self._extract_field_type_from_content(content, field_name_raw)
275
+ return field_type
276
+
277
+ except Exception as e:
278
+ self.logger.debug(f"获取字段类型失败 {java_file_path} - {field_name_raw}: {e}")
279
+ return None
280
+
281
+ def _extract_field_type_from_content(self, content: str, field_name_raw: str) -> Optional[str]:
282
+ """
283
+ 从Java文件内容中提取指定字段的类型
284
+
285
+ Args:
286
+ content: Java文件内容
287
+ field_name_raw: 原始字段名(如 latitude_)
288
+
289
+ Returns:
290
+ 字段的Java类型,如果找不到则返回None
291
+ """
292
+ # 构建字段声明的正则表达式模式
293
+ # 匹配: private Type fieldName_ = ...;
294
+ # 或: private Type fieldName_;
295
+
296
+ # 转义字段名中的特殊字符
297
+ escaped_field_name = re.escape(field_name_raw)
298
+
299
+ # 字段声明模式
300
+ patterns = [
301
+ # 标准字段声明: private Type fieldName_ = value;
302
+ rf'private\s+([^\s]+(?:<[^>]*>)?(?:\[\])?)\s+{escaped_field_name}\s*=',
303
+ # 简单字段声明: private Type fieldName_;
304
+ rf'private\s+([^\s]+(?:<[^>]*>)?(?:\[\])?)\s+{escaped_field_name}\s*;',
305
+ # 其他访问修饰符
306
+ rf'(?:public|protected|package)\s+([^\s]+(?:<[^>]*>)?(?:\[\])?)\s+{escaped_field_name}\s*[=;]',
307
+ # 无访问修饰符
308
+ rf'([^\s]+(?:<[^>]*>)?(?:\[\])?)\s+{escaped_field_name}\s*[=;]',
309
+ ]
310
+
311
+ for pattern in patterns:
312
+ matches = re.finditer(pattern, content, re.MULTILINE)
313
+ for match in matches:
314
+ field_type = match.group(1).strip()
315
+
316
+ # 清理类型字符串
317
+ cleaned_type = self._clean_field_type(field_type)
318
+ if cleaned_type:
319
+ self.logger.debug(f"找到字段类型: {field_name_raw} -> {cleaned_type}")
320
+ return cleaned_type
321
+
322
+ self.logger.debug(f"未找到字段类型: {field_name_raw}")
323
+ return None
324
+
325
+ def _clean_field_type(self, field_type: str) -> Optional[str]:
326
+ """
327
+ 清理和标准化字段类型字符串
328
+
329
+ Args:
330
+ field_type: 原始字段类型字符串
331
+
332
+ Returns:
333
+ 清理后的字段类型,如果无效则返回None
334
+ """
335
+ if not field_type:
336
+ return None
337
+
338
+ # 移除多余的空白字符
339
+ field_type = field_type.strip()
340
+
341
+ # 跳过明显不是类型的字符串
342
+ if field_type in ['private', 'public', 'protected', 'static', 'final', 'volatile', 'transient']:
343
+ return None
344
+
345
+ # 处理泛型类型,保留完整的泛型信息
346
+ # 例如: MapFieldLite<String, Contact> 保持不变
347
+
348
+ # 处理数组类型
349
+ # 例如: String[] 保持不变
350
+
351
+ # 处理完全限定类名,提取简单类名
352
+ if '.' in field_type and not field_type.startswith('java.'):
353
+ # 对于非java包的类,保留完整路径以便后续处理
354
+ pass
355
+
356
+ return field_type
pyproject.toml CHANGED
@@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"
4
4
 
5
5
  [tool.poetry]
6
6
  name = "reproto"
7
- version = "0.0.4"
7
+ version = "0.0.6"
8
8
  description = "一个强大的逆向工程工具, 能够从任何使用Google Protobuf Lite的Android应用中自动重构出完整的.proto文件结构."
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: reproto
3
- Version: 0.0.4
3
+ Version: 0.0.6
4
4
  Summary: 一个强大的逆向工程工具, 能够从任何使用Google Protobuf Lite的Android应用中自动重构出完整的.proto文件结构.
5
5
  License: MIT
6
6
  Author: iyue
@@ -51,7 +51,7 @@ python main.py <java_sources_dir> <root_class> <output_dir>
51
51
  pip install -e .
52
52
 
53
53
  # 使用命令行工具
54
- proto-reconstructor <java_sources_dir> <root_class> <output_dir>
54
+ reproto <java_sources_dir> <root_class> <output_dir>
55
55
  ```
56
56
 
57
57
  ## 📖 使用方法
@@ -102,7 +102,7 @@ Java源码 → 字节码提取 → 类型解码 → 依赖发现 → 源码验
102
102
  ## 📁 项目结构
103
103
 
104
104
  ```
105
- proto_reconstructor/
105
+ reproto/
106
106
  ├── main.py # 主程序入口
107
107
  ├── core/ # 核心组件
108
108
  │ ├── reconstructor.py # 主协调器
@@ -203,8 +203,8 @@ reproto/
203
203
  # 指定日志目录
204
204
  python main.py sources/ com.example.Model output/ --log-dir ./my_logs
205
205
 
206
- # 日志文件格式: proto_reconstructor-YYYY-MM-DD-HH-MM-SS.log
207
- # 例如: proto_reconstructor-2024-01-15-14-30-25.log
206
+ # 日志文件格式: reproto-YYYY-MM-DD-HH-MM-SS.log
207
+ # 例如: reproto-2024-01-15-14-30-25.log
208
208
  ```
209
209
 
210
210
  ### 输出格式
@@ -0,0 +1,75 @@
1
+ .git/COMMIT_EDITMSG,sha256=Hea4DGHC9RyGOOEnEt-_pfDdB3L7RtWaxnPnBx2rnlk,21
2
+ .git/HEAD,sha256=ly8yuQLWQE8njkYAapHLPJ1xVg6_fMgQjCoHqKvFIdw,21
3
+ .git/config,sha256=9ZAySfDWBdI-K9eOXyrEQOhiX68AiaP0Z4CLzH07H10,399
4
+ .git/description,sha256=hatsFj1DoX6pz3eIMIvKFGbxsKjRzJLibpv2PaQGKu4,73
5
+ .git/hooks/applypatch-msg.sample,sha256=AiNJeguLAzqlijpSG4YphpOGz3qw4vEBlj0yiqYhk_c,478
6
+ .git/hooks/commit-msg.sample,sha256=H3TV6SkpebVz69WXQdRsuT_zkazdCD00C5Q3B1PZJDc,896
7
+ .git/hooks/fsmonitor-watchman.sample,sha256=4FSZZOk4l7UZvY4zPAN-Uf_w-IuhPghqMxWSv4AfodA,4726
8
+ .git/hooks/post-update.sample,sha256=gXZa8trvMjBh3LxeYfwWSBy3SzusmtihdLGGUjWG9sU,189
9
+ .git/hooks/pre-applypatch.sample,sha256=4VxbRp6j4KaVvqbyyCvPjmKCEHSTnd2Ft34AB_8WVHU,424
10
+ .git/hooks/pre-commit.sample,sha256=Vxhbe58FI516tS2wRfW4nrMTSNeyF36rIU9euHLhlxs,1649
11
+ .git/hooks/pre-merge-commit.sample,sha256=04JacDN5QOu9ClwHKYThMkWSDN-ImL0iXI0npt_Jy1M,416
12
+ .git/hooks/pre-push.sample,sha256=7M6cfgTT9d2ditqBdT3R1UmpY0smdwBCtY3aACF9CGo,1374
13
+ .git/hooks/pre-rebase.sample,sha256=T-vOhneQBSM4B29OZsxH77FIedGAl9HWHIJhhZ6qp7M,4898
14
+ .git/hooks/pre-receive.sample,sha256=pMPSuce7P9jRRBwxvU7nGlldZrRPz0ndsxAlIyAWmYk,544
15
+ .git/hooks/prepare-commit-msg.sample,sha256=6d3KpBif3dJe2X_Ix4nsp7bKFjkLI5KuMnbwyOGqRhk,1492
16
+ .git/hooks/push-to-checkout.sample,sha256=pT0HQXmLKHxt16-mSu5HPzBeZdP0lGO7nXQI7DsSv18,2783
17
+ .git/hooks/sendemail-validate.sample,sha256=ROv8kj3FRmvACWAvDs8Ge5xlRZq_6IaN3Em3jmztepI,2308
18
+ .git/hooks/update.sample,sha256=jV8vqD4QPPCLV-qmdSHfkZT0XL28s32lKtWGCXoU0QY,3650
19
+ .git/index,sha256=YTfUxO97azpqKz93n54JOz0EQrxW2d7pC4DiyZIneSA,1991
20
+ .git/info/exclude,sha256=ZnH-g7egfIky7okWTR8nk7IxgFjri5jcXAbuClo7DsE,240
21
+ .git/logs/HEAD,sha256=DPBwJI-_VLEGt3MjtwCVyFMm-ib4-HXdOfihB3ghdKQ,514
22
+ .git/logs/refs/heads/iyue,sha256=DPBwJI-_VLEGt3MjtwCVyFMm-ib4-HXdOfihB3ghdKQ,514
23
+ .git/logs/refs/remotes/gitlab/iyue,sha256=QBjy4D_3oChB5vr-wT4YakTTJnYxfU2yqdXbi2IPJBM,272
24
+ .git/logs/refs/remotes/origin/HEAD,sha256=4vCPTgC0N80jhmkFXNUlYvHtGllYJkQTGZEn6LlqZgs,163
25
+ .git/logs/refs/remotes/origin/iyue,sha256=p8CXhH9pCCyFVXjXK6XHxgAPZmVmm6KmcdrZ5aFAxgA,272
26
+ .git/objects/09/a92517fe9eeb33d2fd7c979e01d163665f7abc,sha256=wyFL5jJ16f0xCpL1MB5EiJvs2opieE0hMEmyDpUV3ps,9356
27
+ .git/objects/14/d323d58fb90c209a730c17e23fd82a6c735fc1,sha256=rTCuhPM5LYusLIMDv7CLC557SlRFvvbBzgr4jwrjMVg,426
28
+ .git/objects/18/89a4fb55eb3abdc528ce87f0cea039278c06fd,sha256=Uz8TPtDNnPWR5pfsIx7tMDCDFBdWLIkaPcnYPIJ12oM,2880
29
+ .git/objects/23/bfbca38e177bcbb423dd782c35b19d127a5ab0,sha256=hYW2b3t3bVGNh4pqyyA7DMlyF4LfNZco5NwedWMwwuI,3700
30
+ .git/objects/33/181441ab38eded005db356da89b54c7d29f452,sha256=iIM6a2aGs_TGZjxHKnkYYyZj_hA77nAF7r2lMOtN1VA,2021
31
+ .git/objects/40/84f4567d983a977c49598b7d886e46b13ff50b,sha256=R3w6EvxpJRcVuJvbCslrGDK07kcOjECGGosj5tO-JBo,426
32
+ .git/objects/48/369b05749e384be9be58e5f943f3a0040d0f37,sha256=KP9dXx-XniZuIEXESlrL_MWWi55zGiFr7xtmp3oD67o,8551
33
+ .git/objects/7c/00eec7ae9ef5f94fc337e5c8f9793a2a48810d,sha256=Ju-Yuy5EARcIThU4vkHrZuHQWu3MjXSZYGtBDTWR18c,96
34
+ .git/objects/a3/cedc28e563a1845a7860161b39b0fe58d5f0d3,sha256=Vee0H8dhSj4ERNDSAnRU6vebs0tbY1-lgYr1Nz5X7bc,122
35
+ .git/objects/a5/b7e4e1b63bfb65288f6553687aaabcfb4d51b1,sha256=9tl9xb48EdvQk9GIg6mEzXSwqUgD6yEVZx-sVxaQVKw,1064
36
+ .git/objects/d0/9c84ad2142a187bf26a1714b7041b62e404c8f,sha256=twhhaZbfU_mgu5LIlt9ZeGjp_PT_gri_9H231H5nrNc,2172
37
+ .git/objects/d9/6d7456245232b3e159dcf691f11e51224c557a,sha256=3w45hxiT2rn6ljSdgo36fHMMWZt30r5C1FPvbnjq9tc,597
38
+ .git/objects/e8/2f42ea26b8bf4f0bc92c0648ac8f190f14226d,sha256=xtbEwloZnqXdNdViAevzyprosfPQ1P--xAu6URGlaYI,155
39
+ .git/objects/ed/fb5bbf938de738d1f658f80e23c675bb8b3fae,sha256=MOYiuXKTxnd9YcqU3-HUVUq2g9fPU1-AKZOQ-3xsWCk,4237
40
+ .git/objects/ef/4844af55f7f64b8dd24b81b5a8322a8c80208e,sha256=hO8yllVMOtIE4HeOvwKSqQUByH6lvaA4L-RYrSU4vlg,167
41
+ .git/objects/f2/2bfffda9fb6a7f37bed04b7c40c9466ef09454,sha256=goQNyEV3NqzS28mMXuus1SWzwb8yXnvEPef14Ugzh5Q,750
42
+ .git/objects/f5/18c69a6e1bf3052b79da01502b2837ea58f0f4,sha256=r98SFbCvw7yeAYTjzkw-2VMhOVbpm79FLZQiC5A1-pw,123
43
+ .git/objects/pack/pack-289f7bb06603881c49190e6036de6390223baf77.idx,sha256=KRv1SBEe9bYL9d5V301LwPOBqFs3tMnB0dl4PriMMIw,2416
44
+ .git/objects/pack/pack-289f7bb06603881c49190e6036de6390223baf77.pack,sha256=R4oMXhr-NcPAUBXStCbAaUvGUDW_SBECmPIbiB9Gy48,42003
45
+ .git/objects/pack/pack-289f7bb06603881c49190e6036de6390223baf77.rev,sha256=22_3sqyTNB5IbwAK5oCqNz0UxDDqoZ5jcF7p_44Lalo,244
46
+ .git/packed-refs,sha256=4H0m4wd6q98wZkBk7WfvVeTBuuxQdTE65XxsswYP-oQ,112
47
+ .git/refs/heads/iyue,sha256=fqogai84mA-74wSv8UR7XVOxAU72CKbFfMD-1g_-tW0,41
48
+ .git/refs/remotes/gitlab/iyue,sha256=fqogai84mA-74wSv8UR7XVOxAU72CKbFfMD-1g_-tW0,41
49
+ .git/refs/remotes/origin/HEAD,sha256=G8pFPTbCqnJ2IkCzz9J-STqOXWU6TrlXfpt1wx5bUWE,30
50
+ .git/refs/remotes/origin/iyue,sha256=fqogai84mA-74wSv8UR7XVOxAU72CKbFfMD-1g_-tW0,41
51
+ .gitignore,sha256=ThRUL1E38Z_Q4Lww0xYYl9ziFaPUvvwwqxbmp2cuwco,2068
52
+ .python-version,sha256=NxOs_9lT1XG8y-FjlRru-YinX5RcBJt_ulPwgDESZ_o,7
53
+ ARCHITECTURE.md,sha256=JALnUdREwdLRAmcUit5CGAPLpeZ6UfoYpAQkxil3NJc,8322
54
+ README.md,sha256=t7zYDpLSOC7VDFPlQKOOoqe09GN38wLl0fbbJUT5sNg,6019
55
+ core/__init__.py,sha256=ajz1GSNU9xYVrFEDSz6Xwg7amWQ_yvW75tQa1ZvRIWc,3
56
+ core/bytecode_parser.py,sha256=87ZGhnyBNGP-gRjIyRUcGfWS2HR1YA_e1OBKafLEEDc,11532
57
+ core/info_decoder.py,sha256=DgXDlMaNudOYXKJiPKfE3PeIrzN3Da-PlsQkkrnV2Hs,33959
58
+ core/reconstructor.py,sha256=eOJg6rx0Gkb3bzeuIKB_myt3mC61dsn7AP30EAJyqfg,29377
59
+ generation/__init__.py,sha256=ajz1GSNU9xYVrFEDSz6Xwg7amWQ_yvW75tQa1ZvRIWc,3
60
+ generation/proto_generator.py,sha256=nd9rkjoOAJiY-aOSJz8RQd0z5WLN28zOi_vavwvpjes,16131
61
+ main.py,sha256=1qRMdQjetlqowe8HsEh46MCbat3NMx1ym0Nb9MOEFuE,5272
62
+ models/__init__.py,sha256=WScv63rvEl65y5CWjpb6__hvjNvjpCkl6lz1Z2u0IYc,811
63
+ models/message_definition.py,sha256=AszUZnNPSBn9SMXne5ORDBiGZz1W2pcYmU8ftGC3Mks,4873
64
+ parsing/__init__.py,sha256=ajz1GSNU9xYVrFEDSz6Xwg7amWQ_yvW75tQa1ZvRIWc,3
65
+ parsing/enum_parser.py,sha256=4BjMk1NIYgt2FDZUjTZyOvFfC1uJp_MBZipVdk1tqes,6175
66
+ parsing/java_parser.py,sha256=6J16XlLIV-qktPDT1ccY70BngpP9VhwlXH0Z76TWHqM,11714
67
+ pyproject.toml,sha256=ZBqGZxCm8ldtItBhMb5pxKEvnP1IHrvhPWhHKpRkzR0,901
68
+ requirements.txt,sha256=cQGj3IS6Kj88jbwj_jeKkokMnG-fEezWve91mfW4CJs,96
69
+ utils/__init__.py,sha256=ajz1GSNU9xYVrFEDSz6Xwg7amWQ_yvW75tQa1ZvRIWc,3
70
+ utils/file_utils.py,sha256=N1Ei7hmyeOkIyZJFEXyNbtTrfeVF2hP-U8evXAVW2MA,4085
71
+ utils/logger.py,sha256=473DfzFVXzdGpiRTJGY6bBd-V5G80_P07gQqcvDChpQ,2447
72
+ reproto-0.0.6.dist-info/METADATA,sha256=_RJz-0nYSnt5nxAkqCQoZPf8t-ybLJYOGzqQsYTT3r0,6577
73
+ reproto-0.0.6.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
74
+ reproto-0.0.6.dist-info/entry_points.txt,sha256=6Oro9lK_2DXDgHiB3andNuIE78wxfooqacqp8yY1C-g,37
75
+ reproto-0.0.6.dist-info/RECORD,,
utils/logger.py CHANGED
@@ -35,9 +35,9 @@ class LoggerManager:
35
35
  log_path = Path(log_dir)
36
36
  log_path.mkdir(exist_ok=True)
37
37
 
38
- # 生成日志文件名:proto_reconstructor-YYYY-MM-DD-HH-MM-SS.log
38
+ # 生成日志文件名:reproto-YYYY-MM-DD-HH-MM-SS.log
39
39
  timestamp = datetime.now().strftime("%Y-%m-%d-%H-%M-%S")
40
- log_file = log_path / f"proto_reconstructor-{timestamp}.log"
40
+ log_file = log_path / f"reproto-{timestamp}.log"
41
41
 
42
42
  # 控制台输出 - 彩色格式
43
43
  logger.add(
@@ -1,53 +0,0 @@
1
- .git/HEAD,sha256=ly8yuQLWQE8njkYAapHLPJ1xVg6_fMgQjCoHqKvFIdw,21
2
- .git/config,sha256=6mE3z55UjVFEIsUWmiN6Bw_IJPK92slDZMSanpih0ok,287
3
- .git/description,sha256=hatsFj1DoX6pz3eIMIvKFGbxsKjRzJLibpv2PaQGKu4,73
4
- .git/hooks/applypatch-msg.sample,sha256=AiNJeguLAzqlijpSG4YphpOGz3qw4vEBlj0yiqYhk_c,478
5
- .git/hooks/commit-msg.sample,sha256=H3TV6SkpebVz69WXQdRsuT_zkazdCD00C5Q3B1PZJDc,896
6
- .git/hooks/fsmonitor-watchman.sample,sha256=4FSZZOk4l7UZvY4zPAN-Uf_w-IuhPghqMxWSv4AfodA,4726
7
- .git/hooks/post-update.sample,sha256=gXZa8trvMjBh3LxeYfwWSBy3SzusmtihdLGGUjWG9sU,189
8
- .git/hooks/pre-applypatch.sample,sha256=4VxbRp6j4KaVvqbyyCvPjmKCEHSTnd2Ft34AB_8WVHU,424
9
- .git/hooks/pre-commit.sample,sha256=Vxhbe58FI516tS2wRfW4nrMTSNeyF36rIU9euHLhlxs,1649
10
- .git/hooks/pre-merge-commit.sample,sha256=04JacDN5QOu9ClwHKYThMkWSDN-ImL0iXI0npt_Jy1M,416
11
- .git/hooks/pre-push.sample,sha256=7M6cfgTT9d2ditqBdT3R1UmpY0smdwBCtY3aACF9CGo,1374
12
- .git/hooks/pre-rebase.sample,sha256=T-vOhneQBSM4B29OZsxH77FIedGAl9HWHIJhhZ6qp7M,4898
13
- .git/hooks/pre-receive.sample,sha256=pMPSuce7P9jRRBwxvU7nGlldZrRPz0ndsxAlIyAWmYk,544
14
- .git/hooks/prepare-commit-msg.sample,sha256=6d3KpBif3dJe2X_Ix4nsp7bKFjkLI5KuMnbwyOGqRhk,1492
15
- .git/hooks/push-to-checkout.sample,sha256=pT0HQXmLKHxt16-mSu5HPzBeZdP0lGO7nXQI7DsSv18,2783
16
- .git/hooks/sendemail-validate.sample,sha256=ROv8kj3FRmvACWAvDs8Ge5xlRZq_6IaN3Em3jmztepI,2308
17
- .git/hooks/update.sample,sha256=jV8vqD4QPPCLV-qmdSHfkZT0XL28s32lKtWGCXoU0QY,3650
18
- .git/index,sha256=uNpkl4Ngnok-FuwXpx-733gwa0mDR3EJQHNSWlKGVY4,1991
19
- .git/info/exclude,sha256=ZnH-g7egfIky7okWTR8nk7IxgFjri5jcXAbuClo7DsE,240
20
- .git/logs/HEAD,sha256=8inae4CFVxR4Psim5jd6PmrnSmipVxT3rS8zFAe0Cnc,163
21
- .git/logs/refs/heads/iyue,sha256=8inae4CFVxR4Psim5jd6PmrnSmipVxT3rS8zFAe0Cnc,163
22
- .git/logs/refs/remotes/origin/HEAD,sha256=8inae4CFVxR4Psim5jd6PmrnSmipVxT3rS8zFAe0Cnc,163
23
- .git/objects/pack/pack-55d9855fa45cb686f64bef472f9a7940ef78b8d6.idx,sha256=dM0mT5EXOaEswaA71NFzz6Fh3B9iF9IN1SXVxeD80aw,2304
24
- .git/objects/pack/pack-55d9855fa45cb686f64bef472f9a7940ef78b8d6.pack,sha256=RkehkvasbBDavK-cSTnrVcGGYGUiWgUxZVhjsDbYrT4,41437
25
- .git/objects/pack/pack-55d9855fa45cb686f64bef472f9a7940ef78b8d6.rev,sha256=rIyIFQiRY9AcV_xf0UA4im0PAeyvHtrkew59RQ5QaFU,228
26
- .git/packed-refs,sha256=NZVl-0AkdTQlT4H1r599fFDdUKwCjFwzQYxAYwUrSvg,112
27
- .git/refs/heads/iyue,sha256=G3DrpPS-kI_DVAakJCukrkaQcp3JNpkozc-HEtV7qEI,41
28
- .git/refs/remotes/origin/HEAD,sha256=G8pFPTbCqnJ2IkCzz9J-STqOXWU6TrlXfpt1wx5bUWE,30
29
- .gitignore,sha256=ThRUL1E38Z_Q4Lww0xYYl9ziFaPUvvwwqxbmp2cuwco,2068
30
- .python-version,sha256=NxOs_9lT1XG8y-FjlRru-YinX5RcBJt_ulPwgDESZ_o,7
31
- ARCHITECTURE.md,sha256=JALnUdREwdLRAmcUit5CGAPLpeZ6UfoYpAQkxil3NJc,8322
32
- README.md,sha256=P71PU1DNUaLUAR15ja18AdZL0gobBh7xrUJeVZdlCbc,6067
33
- core/__init__.py,sha256=ajz1GSNU9xYVrFEDSz6Xwg7amWQ_yvW75tQa1ZvRIWc,3
34
- core/bytecode_parser.py,sha256=87ZGhnyBNGP-gRjIyRUcGfWS2HR1YA_e1OBKafLEEDc,11532
35
- core/info_decoder.py,sha256=n_U7c4syhDkfez5WXOxXi-CVQ3AMUISpGqixcHqzHHA,19214
36
- core/reconstructor.py,sha256=9mNV_6YRLNjlSGxzrS4lU3yE8bzu-mwCD3zmw973nIk,25666
37
- generation/__init__.py,sha256=ajz1GSNU9xYVrFEDSz6Xwg7amWQ_yvW75tQa1ZvRIWc,3
38
- generation/proto_generator.py,sha256=en-BAgikOzkDH7gdagX97iNIb5pTBN0ieVt5MnuL_BI,14535
39
- main.py,sha256=3hQ-_YmW5Weq14h040V48_W5DSb-xTXpMMb6vRjCbF8,5296
40
- models/__init__.py,sha256=WScv63rvEl65y5CWjpb6__hvjNvjpCkl6lz1Z2u0IYc,811
41
- models/message_definition.py,sha256=AszUZnNPSBn9SMXne5ORDBiGZz1W2pcYmU8ftGC3Mks,4873
42
- parsing/__init__.py,sha256=ajz1GSNU9xYVrFEDSz6Xwg7amWQ_yvW75tQa1ZvRIWc,3
43
- parsing/enum_parser.py,sha256=tBflR9bjC8LRe4cWw19i_ibCedWWB7ICQtbOQdpiWVc,6149
44
- parsing/java_parser.py,sha256=Rk5_S02rqlqNU3OF7BMK57NynFAoxIHINyhnA6rUuhI,7897
45
- pyproject.toml,sha256=Rw3OlS4oKb05Fas7SFxlmZRSttTWoQELZ2U0jgN8PQg,901
46
- requirements.txt,sha256=cQGj3IS6Kj88jbwj_jeKkokMnG-fEezWve91mfW4CJs,96
47
- utils/__init__.py,sha256=ajz1GSNU9xYVrFEDSz6Xwg7amWQ_yvW75tQa1ZvRIWc,3
48
- utils/file_utils.py,sha256=N1Ei7hmyeOkIyZJFEXyNbtTrfeVF2hP-U8evXAVW2MA,4085
49
- utils/logger.py,sha256=TaDXUHq5BQAsolswwHDeFUbXAfgL_fhXgxAcs901xmQ,2471
50
- reproto-0.0.4.dist-info/METADATA,sha256=h0ISgdD0h4xYwQ6zGoX_DsEnEmS6NyKg8oKdjh2VEjk,6625
51
- reproto-0.0.4.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
52
- reproto-0.0.4.dist-info/entry_points.txt,sha256=6Oro9lK_2DXDgHiB3andNuIE78wxfooqacqp8yY1C-g,37
53
- reproto-0.0.4.dist-info/RECORD,,