Pack200 / Network Transfer Format Spec SourceDebugExtension 属性的格式规范

Pack200 / Network Transfer Format Spec format specification for SourceDebugExtension attribute

如果您尝试使用 pack200 打包 spring-context 5.0.1.RELEASE JAR,打包程序会抱怨它不知道 class 属性 SourceDebugExtension在其中的几个 classes 中使用,它们是从 Kotlin classes.

编译而来的

JSR-045 将此属性定义为

The SourceDebugExtension attribute is an optional attribute in the attributes table of the ClassFile structure. There can be no more than one SourceDebugExtension attribute in the attributes table of a given ClassFile structure.

The SourceDebugExtension attribute has the following format:


    SourceDebugExtension_attribute {
       u2 attribute_name_index;
       u4 attribute_length;
       u1 debug_extension[attribute_length];
    }

The items of the SourceDebugExtension_attribute structure are as follows:

attribute_name_index
    The value of the attribute_name_index item must be a valid index into the constant_pool table. The constant_pool entry at that index must be a CONSTANT_Utf8_info structure representing the string "SourceDebugExtension".

attribute_length
    The value of the attribute_length item indicates the length of the attribute, excluding the initial six bytes. The value of the attribute_length item is thus the number of bytes in the debug_extension[] item.

debug_extension[]
    The debug_extension array holds a string, which must be in UTF-8 format. There is no terminating zero byte.

    The string in the debug_extension item will be interpreted as extended debugging information. The content of this string has no semantic effect on the Java Virtual Machine.

Network Transfer Format Spec定义了如何定义此类属性的格式,以便pack200可以处理它们。


Pack200 允许跳过具有这些属性的文件或丢弃这些属性或根据网络传输格式规范定义它们的格式。不幸的是,我没有得到正确的格式说明符来正确解析属性。需要由格式说明符匹配的实际数据的示例 Hexdump - 即 debug_extension[] 的值是

00000b90:                   53 4d  41 50 0a 42 65 61 6e 44 ;      SMAP.BeanD
00000ba0: 65 66 69 6e 69 74 69 6f  6e 44 73 6c 2e 6b 74 0a ;efinitionDsl.kt.
00000bb0: 4b 6f 74 6c 69 6e 0a 2a  53 20 4b 6f 74 6c 69 6e ;Kotlin.*S Kotlin
00000bc0: 0a 2a 46 0a 2b 20 31 20  42 65 61 6e 44 65 66 69 ;.*F.+ 1 BeanDefi
00000bd0: 6e 69 74 69 6f 6e 44 73  6c 2e 6b 74 0a 6f 72 67 ;nitionDsl.kt.org
00000be0: 2f 73 70 72 69 6e 67 66  72 61 6d 65 77 6f 72 6b ;/springframework
00000bf0: 2f 63 6f 6e 74 65 78 74  2f 73 75 70 70 6f 72 74 ;/context/support
00000c00: 2f 42 65 61 6e 44 65 66  69 6e 69 74 69 6f 6e 44 ;/BeanDefinitionD
00000c10: 73 6c 24 62 65 61 6e 24  31 24 63 75 73 74 6f 6d ;sl$bean$custom
00000c20: 69 7a 65 72 24 31 0a 2a  4c 0a 31 23 31 2c 32 37 ;izer.*L.1#1,27
00000c30: 33 3a 31 0a 2a 45 0a                             ;3:1.*E.

很遗憾,我还没有找到正确的格式。我希望这里有人已经这样做了,或者更幸运地找到了正确的格式。

最后我自己找到了一个可行的解决方案。

格式有点棘手,因为 SourceDebugExtension 属性被定义为直接 UTF-8 字符串,没有像 [=13=] 这样的任何终止字符,并且在格式字符串中你不能定义像 "take all remaining bytes" 或 "do until the end of the byte array is reached".

但在阅读了格式字符串的可能性和 SourceDebugExtension 属性内容的格式之后,我想出了一种在大多数情况下都应该有效的格式。

SourceDebugExtension 属性带有已解析的 SMAP。在这种情况下,已解决很重要,因为在未解决的 SMAP 中,可能会嵌入已经包含结束部分的 SMAP,这会使它变得有点复杂,但并非不可能。在已解析的 SMAP 中,您总是在最后 <line terminator>*E<line terminator>,其中 <line terminator> 可能是通常的嫌疑人 \r\n\r\n,并且此序列不可能出现如果已解决,请在 SMAP 中更早。

现在我们可以使用具有递归自调用的联合布局元素来构建以下格式字符串,该字符串在大多数情况下都能正确匹配 SMAP。此格式字符串唯一假定的是,如果在 *E 之前找到了行终止符 \r\n,那么在它之后也需要它,并且如果只有 \r\n之前找到,后面只有\r\n。哪个不重要,重要的只是\r\n。如果发生这种情况,打包就会失败,并抱怨没有处理一个字节。但是如果我们检查两个字符并且只剩下一个字符,我们会得到一个 ArrayIndexOutOfBoundsException,我认为这是不太可能的情况,不同的行终止符混合在一起。

所以这是我目前的做法:

[TB(10)[TB(42)[TB(69)[TB(13,10)[]()[(0)]]()[(0)]]()[(0)]](13)[TB(10)[TB(42)[TB(69)[TB(13)[TB(10)[]()[(0)]]()[(0)]]()[(0)]]()[(0)]](42)[TB(69)[TB(13,10)[]()[(0)]]()[(0)]]()[(0)]]()[(0)]]

为了更好地理解此处具有一些间距和语义内容的相同格式。这样就不能直接使用了。它必须通过 com.sun.java.util.jar.pack.Attribute#normalizeLayoutString 传递,这是包私有 class 中的 public static 方法,因此通常无法访问。如果您使用反射或 Groovy 为您完成或将方法体复制到您自己的方法中,您当然可以在您的代码中使用此版本。

[
   # covered likely cases:
   # \n*E\n
   # \r\n*E\r\n
   # \r*E\r
   #
   # covered unlikely cases:
   # \n*E\r
   # \r*E\n
   #
   # uncovered unlikely cases:
   # \n*E\r\n
   # \r*E\r\n
   # \r\n*E\r
   # \r\n*E\n
   TB
      (\\n) [
         # covered cases:
         # \n*E\r
         # \n*E\n
         TB
            (\*) [
               TB
                  (\E) [
                     TB
                        (\\r, \\n) []
                        () [(0)]
                  ]
                  () [(0)]
            ]
            () [(0)]
      ]
      (\\r) [
         # covered cases:
         # \r\n*E\r\n
         # \r*E\r
         # \r*E\n
         TB
            (\\n) [
               # covered cases:
               # \r\n*E\r\n
               TB
                  (\*) [
                     TB
                        (\E) [
                           TB
                              (\\r) [
                                 TB
                                    (\\n) []
                                    () [(0)]
                              ]
                              () [(0)]
                        ]
                        () [(0)]
                  ]
                  () [(0)]
            ]
            (\*) [
               # covered cases:
               # \r*E\r
               # \r*E\n
               TB
                  (\E) [
                     TB
                        (\\r, \\n) []
                        () [(0)]
                  ]
                  () [(0)]
            ]
            () [(0)]
      ]
      () [(0)]
]