• 标 题:一个速度非常快的Java编译器(Symantec Java Compiler)sj.exe的改进 (9千字)
  • 作 者:slangmgh
  • 时 间:2003-08-19 12:41:08
  • 链 接:http://bbs.pediy.com

这个编译器是Symantec Cafe 4.0带的,编译速度非常快,是javac.exe的100到1000倍,是jikes.exe的4-6倍。不过由于Symantec Cafe不再发展,所以这个编译器也不再更新,有一下问题需要改进。

sj.exe不支持JDK1.4以上,它会检查rt.jar中的类的版本,如下:

00415EF4   .  83FB 2D       CMP     EBX, 2D
00415EF7      74 29         JE      SHORT sj.00415F22

改成如下就可以跳过代码的检查:
00415EF4   .  83FB 2D       CMP     EBX, 2D
00415EF7   .  EB 29         JMP     SHORT sj11.00415F22

如果使用wjcompiler.dll,则修改如下地方
21EA4F53:7F 64->90 90

sj.exe在编译文件的时候,如果Classpath中包含的jar文件太大,如
Weblogic 7.0的jar文件,就会出现内存访问异常,经检查在下面的
代码中出现异常:

004A00C8  /$  89CA          MOV     EDXECX
004A00CA  |.  8B4424 04     MOV     EAX, [ESP+4]
004A00CE  |.  53            PUSH    EBX
004A00CF  |.  57            PUSH    EDI
004A00D0  |.  56            PUSH    ESI
004A00D1  |.  83C0 03       ADD     EAX, 3
004A00D4  |.  83E0 FC       AND     EAX, FFFFFFFC
004A00D7  |.  75 05         JNZ     SHORT sj11.004A00DE
004A00D9  |.  B8 04000000   MOV     EAX, 4
004A00DE  |>  89C6          MOV     ESIEAX
004A00E0  |.  F7D8          NEG     EAX
004A00E2  |.  01E0          ADD     EAXESP
004A00E4  |.  73 28         JNB     SHORT sj11.004A010E
004A00E6  |.  89C1          MOV     ECXEAX
004A00E8  |.  89F3          MOV     EBXESI
004A00EA  |>  851C19        /TEST    [ECX+EBX], EBX
004A00ED  |.  81EB 00100000 |SUB     EBX, 1000
004A00F3  |.^ 73 F5         \JNB     SHORT sj11.004A00EA
004A00F5  |.  8519          TEST    [ECX], EBX
004A00F7  |.  89E9          MOV     ECXEBP
004A00F9  |.  29E1          SUB     ECXESP
004A00FB  |.  2B0A          SUB     ECX, [EDX]
004A00FD  |.  0132          ADD     [EDX], ESI ;异常
004A00FF  |.  89C4          MOV     ESPEAX
004A0101  |.  01C8          ADD     EAXECX
004A0103  |.  89E7          MOV     EDIESP
004A0105  |.  01E6          ADD     ESIESP
004A0107  |.  C1E9 02       SHR     ECX, 2
004A010A  |.  F3:A5         REP     MOVSD
004A010C  |.  EB 02         JMP     SHORT sj11.004A0110
004A010E  |>  31C0          XOR     EAXEAX
004A0110  |>  5E            POP     ESI                                kernel32.77E7EB69
004A0111  |.  5F            POP     EDI                                kernel32.77E7EB69
004A0112  |.  5B            POP     EBX                                kernel32.77E7EB69
004A0113  \.  C3            RETN

经过分析,这段代码是类似在栈中分配内存,由于循环过多,最后导致在还没有
分配内存的栈中访问而导致异常,用以下方法可以解决这个问题,即通过在堆中
分配内存,这样会浪费一些内存,但是既然是编译器,运行完成后就退出,这些
内存马上能够被回收。

004A00C8  /$  FF7424 04     PUSH    DWORD PTR [ESP+4]                 /MemSize = 540000 (5505024.)
004A00CC  |.  6A 00         PUSH    0                                 |Flags = GMEM_FIXED
004A00CE  |.  E8 5FF80000   CALL           \GlobalAlloc
004A00D3  \.  C3            RETN


在进行对UTF-8编码方式的源文件进行编译的时候,不能成功。UTF-8的编码方式
通过sj -j .65001的方式来编译,其中65001是Windows中的CP_UTF8。通过检查
代码发现sj使用GetCPInof来获取编码的最大字节数,而在Windows中,CP_UTF8
编码返回的最大字节数是4,而实际应当是3,因此需要把这里修改掉,原代码如下:
004A351D   |.  51              PUSH    ECX                               /pCPInfo = 0012F434
004A351E   |.  52              PUSH    EDX                               |CodePage = FDE9
004A351F   |.  FF15 88284000   CALL    [<&KERNEL32.GetCPInfo>]           \GetCPInfo
004A3525   |.  85C0            TEST    EAXEAX
004A3527   |.  0F84 79010000   JE      sj111.004A36A6
004A352D   |.  66:8B4C24 1C    MOV     CX, [ESP+1C]
004A3532   |.  68 02020000     PUSH    202
004A3537   |.  81E1 FFFF0000   AND     ECX, 0FFFF
004A353D   |.  890D 2C144D00   MOV     [4D142C], ECX ; 保存最大字节数

修改后代码为:
004A351D   |.  51              PUSH    ECX                               /pCPInfo = 0012F434
004A351E   |.  52              PUSH    EDX                               |CodePage = FDE9
004A351F   |.  FF15 88284000   CALL    [<&KERNEL32.GetCPInfo>]           \GetCPInfo
004A3525   |.  85C0            TEST    EAXEAX
004A3527   |.  0F84 79010000   JE      sj111.004A36A6
004A352D   |.  66:8B4C24 1C    MOV     CX, [ESP+1C]
004A3532   |.  68 02020000     PUSH    202
004A3537   |.  81E1 FFFF0000   AND     ECX, 0FFFF
004A353D       E9 6EC50000     JMP     sj111.004AFAB0 ; 修改后的代码
004A3542       90              NOP

以下代码判断编码方式是否为65001(0xFDE9),如果是就将最大字节数改成3。
004AFAB0       60              PUSHAD
004AFAB1       A1 30144D00     MOV     EAX, [4D1430] ; CodePage
004AFAB6       3D E9FD0000     CMP     EAX, 0FDE9
004AFABB       75 0C           JNZ     SHORT sj111.004AFAC9
004AFABD       C705 2C144D00 0>MOV     DWORD PTR [4D142C], 3 ; MaxBytes
004AFAC7       EB 06           JMP     SHORT sj111.004AFACF
004AFAC9       890D 2C144D00   MOV     [4D142C], ECX
004AFACF       61              POPAD
004AFAD0     ^ E9 6D3AFFFF     JMP     sj111.004A3542

然后在进行编码转换的时候需要去掉一个判断,原代码如下:
004A4810   |.  19ED            SBB     EBPEBP
004A4812   |.  31C9            XOR     ECXECX
004A4814   |.  8A0E            MOV     CL, [ESI]
004A4816   |.  45              INC     EBP
004A4817   |.  F6444A 01 80    TEST    BYTE PTR [EDX+ECX*2+1], 80 ; 判断是否单字节
004A481C   |.  74 3B           JE      SHORT sj111.004A4859
004A481E   |.  8B0D 2C144D00   MOV     ECX, [4D142C]
004A4824   |.  83F9 02         CMP     ECX, 2
004A4827   |.  7C 59           JL      SHORT sj111.004A4882
004A4829   |.  8B15 2C144D00   MOV     EDX, [4D142C]
004A482F   |.  3B5424 1C       CMP     EDX, [ESP+1C]                      sj111.004D145C
004A4833   |.  77 4D           JA      SHORT sj111.004A4882
004A4835   |.  55              PUSH    EBP                               /WideBufSize = 394660 (3753568.)
004A4836   |.  53              PUSH    EBX                               |WideCharBuf = 00000002
004A4837   |.  51              PUSH    ECX                               |StringSize = 12F434 (1242164.)
004A4838   |.  8B0D D8474D00   MOV     ECX, [4D47D8]                     |
004A483E   |.  8B15 30144D00   MOV     EDX, [4D1430]                     |
004A4844   |.  56              PUSH    ESI                               |StringToMap = NULL
004A4845   |.  51              PUSH    ECX                               |Options = 0
004A4846   |.  52              PUSH    EDX                               |CodePage = FDE9
004A4847   |.  FF15 74284000   CALL    [<&KERNEL32.MultiByteToWideChar>>; \MultiByteToWideChar
004A484D   |.  85C0            TEST    EAXEAX

可以将004A481C的代码NOP掉,所有的编码都以多字节来处理。
还有在进行转换的时候需要将Options设置为0,这是个全局变量,在4D47D8。

在将UNICODE转换成UTF-8的时候,有一段代码需要修改,原代码如下:
0049FFA1   |.  51              PUSH    ECX                          /pDefaultCharUsed = 00145B87
0049FFA2   |.  56              PUSH    ESI                          |pDefaultChar = NULL
0049FFA3   |.  52              PUSH    EDX                          |MultiByteCount = 7FFA0002 (2147090434.)
0049FFA4   |.  55              PUSH    EBP                          |MultiByteStr = 00145B80
0049FFA5   |.  6A FF           PUSH    -1                           |WideCharCount = FFFFFFFF (-1.)
0049FFA7   |.  8B3D 30144D00   MOV     EDI, [4D1430]                |
0049FFAD   |.  8B5C24 34       MOV     EBX, [ESP+34]                |sj11.004BEEB5
0049FFB1   |.  53              PUSH    EBX                          |WideCharStr = "tt.java"
0049FFB2   |.  68 20020000     PUSH    220                          |Options = WC_COMPOSITECHECK|WC_SEPCHARS
0049FFB7   |.  57              PUSH    EDI                          |CodePage = CP_ACP
0049FFB8   |.  FF15 CC284000   CALL    [<&KERNEL32.WideCharToMulti>; \WideCharToMultiByte

需要将0049FFA7的CodePage修改成CP_ACP,修改后代码如下:
0049FFA1   |.  51              PUSH    ECX                          /pDefaultCharUsed = 00145B87
0049FFA2   |.  56              PUSH    ESI                          |pDefaultChar = NULL
0049FFA3   |.  52              PUSH    EDX                          |MultiByteCount = 7FFA0002 (2147090434.)
0049FFA4   |.  55              PUSH    EBP                          |MultiByteStr = 00145B80
0049FFA5   |.  6A FF           PUSH    -1                           |WideCharCount = FFFFFFFF (-1.)
0049FFA7       E9 34FB0000     JMP     sj11.004AFAE0
0049FFAC       90              NOP
0049FFAD   |.  8B5C24 34       MOV     EBX, [ESP+34]                |sj11.004BEEB5
0049FFB1   |.  53              PUSH    EBX                          |WideCharStr = "tt.java"
0049FFB2   |.  68 20020000     PUSH    220                          |Options = WC_COMPOSITECHECK|WC_SEPCHARS
0049FFB7   |.  57              PUSH    EDI                          |CodePage = CP_ACP
0049FFB8   |.  FF15 CC284000   CALL    [<&KERNEL32.WideCharToMulti>; \WideCharToMultiByte

以下代码判断编码方式是否为65001(0xFDE9),如果是就将CodePage设置为CP_ACP
004AFAE0       60              PUSHAD
004AFAE1       A1 30144D00     MOV     EAX, [4D1430]
004AFAE6       3D E9FD0000     CMP     EAX, 0FDE9
004AFAEB       75 05           JNZ     SHORT sj11.004AFAF2
004AFAED       61              POPAD
004AFAEE       33FF            XOR     EDIEDI
004AFAF0       EB 07           JMP     SHORT sj11.004AFAF9
004AFAF2       61              POPAD
004AFAF3       8B3D 30144D00   MOV     EDI, [4D1430]
004AFAF9     ^ E9 AF04FFFF     JMP     sj11.0049FFAD