一個速度非常快的Java編譯器(Symantec Java Compiler)sj.exe的改進 (9千字)
這個編譯器是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 EDX, ECX
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 ESI, EAX
004A00E0 |. F7D8 NEG EAX
004A00E2 |. 01E0 ADD EAX, ESP
004A00E4 |. 73 28 JNB SHORT sj11.004A010E
004A00E6 |. 89C1 MOV ECX, EAX
004A00E8 |. 89F3 MOV EBX, ESI
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 ECX, EBP
004A00F9 |. 29E1 SUB ECX, ESP
004A00FB |. 2B0A SUB ECX, [EDX]
004A00FD |. 0132 ADD [EDX], ESI ;異常
004A00FF |. 89C4 MOV ESP, EAX
004A0101 |. 01C8 ADD EAX, ECX
004A0103 |. 89E7 MOV EDI, ESP
004A0105 |. 01E6 ADD ESI, ESP
004A0107 |. C1E9 02 SHR ECX, 2
004A010A |. F3:A5 REP MOVSD
004A010C |. EB 02 JMP SHORT sj11.004A0110
004A010E |> 31C0 XOR EAX, EAX
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 EAX, EAX
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 EAX, EAX
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 EBP, EBP
004A4812 |. 31C9 XOR ECX, ECX
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 EAX, EAX
可以將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 EDI, EDI
004AFAF0 EB 07 JMP SHORT sj11.004AFAF9
004AFAF2 61 POPAD
004AFAF3 8B3D 30144D00 MOV EDI, [4D1430]
004AFAF9 ^ E9 AF04FFFF JMP sj11.0049FFAD
相關文章
- 一個Java反彙編器的修改 (7千字)2015-11-15Java
- java編譯器的一些感悟2015-07-16Java編譯
- java程式中編譯另一個java程式2007-03-19Java編譯
- Java動態編譯優化——提升編譯速度(N倍)2018-12-06Java編譯優化
- [譯] Kotlin VS Java:編譯速度大比拼2016-11-29KotlinJava編譯
- Java11改進的垃圾回收器2024-03-05Java
- 【譯】Visual Studio 的 Razor 編輯器的改進2021-03-09
- Java反編譯器剖析2014-02-07Java編譯
- Java9新特性系列(Stream改進)2019-01-18Java
- 編譯程式(compiler)的簡單分析2018-08-07編譯Compile
- Java的指令碼機制、編譯器API2021-11-21Java指令碼編譯API
- (ecj)Eclipse的Java編譯器分析之一——ecj介紹2014-01-18EclipseJava編譯
- 改進JAVA字串分解的方法2004-10-19Java字串
- 用java寫一個lisp 直譯器2022-02-07JavaLisp
- javascript編寫一個簡單的編譯器2017-11-01JavaScript編譯
- NDK clang編譯器的一個bug2020-05-10編譯
- ASPNet_Compiler的編譯過程2008-05-01Compile編譯
- java編寫一個埠掃描器2016-03-13Java
- IDEA報錯java: 編譯失敗: 內部 java 編譯器錯誤2024-06-27IdeaJava編譯
- 如何將一個Java檔案編譯成class2024-07-15Java編譯
- 一些改進模型速度/精度的工程方法2020-04-06模型
- Java編譯與反編譯2021-03-20Java編譯
- 請教java 的反反編譯2003-04-10Java編譯
- 從編譯原理看一個直譯器的實現2017-06-29編譯原理
- 【譯】.NET 7 中的效能改進(一)2023-02-19
- 利用 Docker 構建一個簡單的 java 開發編譯環境2021-01-01DockerJava編譯
- 深入剖析Java即時編譯器(上)2019-03-13Java編譯
- Android加快編譯速度的另一種方法2018-10-15Android編譯
- 使用ant編譯Java檔案(一)2007-09-01編譯Java
- Java編譯和執行的命令2024-08-02Java編譯
- Java程式碼的編譯與反編譯那些事兒2019-05-09Java編譯
- 實現一個簡單的 JavaScript 編譯器2019-02-22JavaScript編譯
- Java中的編譯器外掛開發與應用2024-07-20Java編譯
- Java程式碼編譯和執行的整個過程2016-02-13Java編譯
- 如何提高 Xcode 的編譯速度2018-06-14XCode編譯
- Tomcat 改伺服器編碼(Java 修改字串編碼格式)2013-11-30Tomcat伺服器Java字串編碼
- JWebAssembly:Java 位元組碼到 WebAssembly 編譯器2022-11-01WebJava編譯
- ☕【Java技術指南】「編譯器專題」深入分析探究“靜態編譯器”(JAVA\IDEA\ECJ編譯器)是否可以實現程式碼優化?2021-10-14Java編譯Idea優化