Monday, February 05, 2007

ขั้นตอนการ compress javascript ของ dojo

มีคนเขียนมาถามว่า Dojo ใช้วิธีอะไรในการ compress javascript file
ลองไปดู build file ของเขากัน
<target name="-rhino-compress"
unless="nostrip">
<copy overwrite="true" file="${srcFile}" tofile="${dstFile}.uncompressed.js" />
<java jar="./lib/custom_rhino.jar" fork="true" output="${dstFile}">
<arg value="-c" />
<arg value="${srcFile}" />
</java>
</target>


จะเขาใช้ rhino เข้ามาช่วยครับ
แต่ไม่ใช่ rhino ธรรมดานะครับ เขาใช้ custom ของเขาเอง

คือตัว rhino มันคือ javascript interpreter
ซึ่งภายในต้องมี class ที่ทำหน้าที่ parse javascript อยู่แล้ว
เขาก็เลย hack เจ้า class นี้
เพิ่มส่วน writer ที่ write ออกมาในรูปแบบที่ต้องการ
(หลักการเดียวกันกับพวก pretty printer แต่แทนที่จะออกมาสวย
ก็ออกมาไม่สวยแทน)

เราสามารถเอา rhino-custom.jar ของเขามาใช้ใน project เรา
โดยเรียกใช้แบบนี้
java -jar custom_rhino.jar -c infile.js > outfile.js 2>&1


ลองดูตัวอย่าง code custom rhino ที่เขาเขียน
ตอนที่ print out เขาจะ check token ก่อน ว่าเป็นประเภทไหน
ถ้าเป็น NAME ก็จะทำการ compress
    case Token.NAME:

if(Token.OBJECTLIT == source.charAt(jumpPos)){
i = printSourceString(source, i + 1, false, result);
}else{
i = tm.printCompressed( source, i + 1, false, result, prevToken,
inArgsList, braceNesting);
}
continue;

case Token.STRING:
... # a lot of case

จะเห็นว่าเรียกใช้ method printCompressed
ตามไปดู printCompressed จะเห็นว่าเขาเช็คก่อน
ว่าตัวแปรที่กำลังจะ print นั้นเป็น ตัวแปรที่พึ่งประกาศใหม่
หรือตัวแปรที่ถูกอ้างใช้
public int printCompressed(String     source, 
int offset,
boolean asQuotedString,
StringBuffer sb,
int prevToken,
boolean inArgsList,
int currentLevel){

....
// ถ้ามี var นำหน้า หรือ อยู่ใน argement list
// ให้สร้าง mapping ขึ้นมาใหม่
if(((prevToken == Token.VAR)&&(!hasLocalTokenMapping(sourceStr)))||(inArgsList)){
newMapping = true;
}

str = this.getMappedToken(str, newMapping);
....


}


private String getMappedToken(String token, boolean newMapping){
String nt = null;
HashMap tokens = (HashMap)scopeReplacedTokens.get(scopeReplacedTokens.size()-1);
if(newMapping){
lastTokenCount++;
// ตั้งชื่อตัวแปร โดยมี _ นำหน้า
// แล้วตามด้วย เลข running ที่แปลงให้เป็น hex
nt = new String("_"+Integer.toHexString(lastTokenCount));
if(nt.length() >= token.length()){
nt = token;
}

tokens.put(token, nt);
return nt;
}
if(hasTokenMapping(token)){
return getTokenMapping(token);
}else{
return token;
}
}

Related link from Roti

1 comment:

bact' said...

ถ้าเอา huffman's ไปผสมกับการตั้งชื่อตัวแปร
ก็ต้องนับก่อน ว่าตัวแปรไหนใช้บ่อย ก็ตั้งให้มันสั้น ๆ :P