Minify

From Second Life Wiki
Jump to: navigation, search
KBnote.png Note: The minifer has been superseded by LSL Minifier on the Wizardry and Steamworks page. The minifier has been recoded in PHP and the source can be found on the minify page.


Introduction

I have seen several script makers obfuscating the code to prevent reverse-engineering in case the script does get distributed without their knowledge. However, under certain circumstances, obfuscated code can also lead to a performance gain. More precisely, when running code under an interpreter, the interpreter performs an identifier name lookup while it is reading the code. So, for example, if you have a lot of lookups, for example in a loop, the time needed for that lookup can matter since longer variable names will require more time.

When it comes to transferring code or storing code, the syntactic length of the code matters as well since more text has to be fetched. This is applies in particular to environments with a high relative traffic, for example, web development where the browser has to fetch the contents of the page before it can be rendered.

In what concerns SecondLife and Mono, there should not be any significant performance increase. You can check the section below if you want some benchmarking. I personally use this method for production items since it just looks good, it is a compact form of code compression, it adds an additional check to see if my code is consistent, it also does global LSL constant interpolations and also strips out comments which I do not really need in the final version of a code. If you do use this technique, please make sure that you have an original version of the script since variables names, function names, global LSL constants and comments are entirely ripped out in the minified version.

Sample Output

This is, for example, what the Chatbot Thousand Lines of Code script looks like after it has been passed through the minifer and also with hard wrapped lines:

integer z=7;integer x=1;string y="\n";string w="\"";string v="\\";list
u=[w,"(",")","<",">","[","]","/","*","%",v];list x1(){string
t=llUnescapeURL("%09");string s=llUnescapeURL("%0D");return [t,y,s,"
",",",";"];}list r=["integer","float","key","vector","rotation","list"];list
q=[1,0,3.14159274,6.28318548,1.57079637,0.01745329238,57.29578,1.414213538,
NULL_KEY,-1,EOF,ZERO_VECTOR,ZERO_ROTATION,1,3,2,1,8,5,7,6,0];list
p=["1","0","3.14159274","6.28318548","1.57079637","0.01745329238","57.29578","1.
414213538","NULL_KEY","-1","EOF","ZERO_VECTOR","ZERO_ROTATION","1","3","2","1","
8","5","7","6","0"];list q2(string o){if(0<=llSubStringIndex(o,".")){return
[(float)o];}else
if(0<=llSubStringIndex("0123456789",llGetSubString(o,0,0))){return
[(integer)o];}else {integer n=llListFindList(p,[o]);if(0<=n){return
llList2List(q,n,n);}else {return [o];}}}list p2(list
m){if(llGetListEntryType(m,0)==5){return
[llList2Vector(m,0)+llList2Vector(m,1)];}return [];}list o2(list
m){if(llGetListEntryType(m,0)==5){return
[llList2Vector(m,0)-llList2Vector(m,1)];}return [];}list n2(list
m){if(llGetListEntryType(m,0)==6){return
[llList2Rot(m,0)*llList2Rot(m,1)];}return [];}list m2(list
m){if(llGetListEntryType(m,0)==6){return
[llList2Rot(m,0)/llList2Rot(m,1)];}return [];}list l2(list m){return [];}list
k2(list m){integer l=llGetListLength(m);if(l==3){vector
k;k.x=llList2Float(m,0);k.y=llList2Float(m,1);k.z=llList2Float(m,2);return
[k];}if(l==4){rotation
j;j.x=llList2Float(m,0);j.y=llList2Float(m,1);j.z=llList2Float(m,2);j.s=
llList2Float(m,2);return [j];}return [];}list j2(string i,list h){list
g=h;if("llDie"==i){llDie();}if("<>"==i){return k2(g);}else if("+"==i){return
p2(g);}else if("-"==i){return o2(g);}else if("*"==i){return n2(g);}else
if("/"==i){return m2(g);}else if("%"==i){return
l2(g);}if("llEscapeURL"==i){return [llEscapeURL(llList2String(g,0))];}else
if("llEuler2Rot"==i){return [llEuler2Rot(llList2Vector(g,0))];}else
if("llGetAgentInfo"==i){return [llGetAgentInfo(llList2Key(g,0))];}else
if("llGetAgentSize"==i){return [llGetAgentSize(llList2Key(g,0))];}else
if("llGetFreeMemory"==i){return [llGetFreeMemory()];}else
if("llGetLinkKey"==i){return [llGetLinkKey(llList2Integer(g,0))];}else
if("llGetNumberOfPrims"==i){return [llGetNumberOfPrims()];}else
if("llGetOwner"==i){return [llGetOwner()];}else if("llGetPos"==i){return
[llGetPos()];}else if("llGetLocalRot"==i){return
[llGetLocalRot()];}if("llGetRegionName"==i){return [llGetRegionName()];}else
if("llGetRot"==i){return [llGetRot()];}else if("llGetSunDirection"==i){return
[llGetSunDirection()];}else if("llKey2Name"==i){return
[llKey2Name(llList2Key(g,0))];}else if("llRequestAgentData"==i){return
[llRequestAgentData(llList2Key(g,0),llList2Integer(g,1))];}else
if("llRequestSimulatorData"==i){return
[llRequestSimulatorData(llList2String(g,0),llList2Integer(g,1))];}else
if("llRot2Euler"==i){return [llRot2Euler(llList2Rot(g,0))];}else
if("llRotBetween"==i){return
[llRotBetween(llList2Vector(g,0),llList2Vector(g,1))];}else
if("llUnescapeURL"==i){return [llUnescapeURL(llList2String(g,0))];}else
if("llVecNorm"==i){return [llVecNorm(llList2Vector(g,0))];}integer
f=1;if("llApplyImpulse"==i){llApplyImpulse(llList2Vector(g,0),llList2Integer(g,1
));}else
if("llApplyRotationalImpulse"==i){llApplyRotationalImpulse(llList2Vector(g,0),
llList2Integer(g,1));}else
if("llDialog"==i){llDialog(llList2Key(g,0),llList2String(g,1),b2(g,2),
llList2Integer(g,3));}else
if("llSetAlpha"==i){llSetAlpha(llList2Float(g,0),llList2Integer(g,1));}else
if("llSetBuoyancy"==i){llSetBuoyancy(llList2Float(g,0));}else
if("llSetColor"==i){llSetColor(llList2Vector(g,0),llList2Integer(g,1));}else
if("llSetLocalRot"==i){llSetLocalRot(llList2Rot(g,0));}else
if("llSetPos"==i){llSetPos(llList2Vector(g,0));}else
if("llSetRot"==i){llSetRot(llList2Rot(g,0));}else
if("llSetScale"==i){llSetScale(llList2Vector(g,0));}else
if("llSetStatus"==i){llSetStatus(llList2Integer(g,0),llList2Integer(g,1));}else
if("llSetText"==i){llSetText(llList2String(g,0),llList2Vector(g,1),llList2Float(
g,2));}else
if("llSitTarget"==i){llSitTarget(llList2Vector(g,0),llList2Rot(g,1));}else
if("llSleep"==i){llSleep(llList2Float(g,0));}else {f=0;}if(f){return [];}return
[];}list h2(list e,integer d,integer c){integer
b=llGetListLength(e);if(d<0){d+=b;if(d<0){d=0;}}if(c<0){c+=b;if(c<0){c=0;}}if(d<
c){return llList2List(e,d,c-1);}return [];}string g2(string a,integer d,integer
c){integer
b=llStringLength(a);if(d<0){d+=b;if(d<0){d=0;}}if(c<0){c+=b;if(c<0){c=0;}}if(d<c
){return llGetSubString(a,d,c-1);}return "";}list f2(list z1,list x1,list
u){list y1=[];integer n;integer w1=llGetListLength(z1);for(n=0;n<w1;++n){string
v1=llList2String(z1,n);y1+=llParseString2List(v1,x1,u);}return y1;}list
e2(string a,list x1,list u){list y1=[a];integer n;integer
u1=llGetListLength(u);for(n=0;n<u1;n+=8){list
t1=llList2List(u,n,n+8-1);y1=f2(y1,[],t1);}integer
s1=llGetListLength(x1);for(n=0;n<s1;n+=8){list
t1=llList2List(x1,n,n+8-1);y1=f2(y1,t1,[]);}return y1;}list d2(list y1){list
m=[];integer r1=llGetListLength(y1);integer n=0;while(n<r1){string
o=llList2String(y1,n++);if(o==w){string q1="";do
{o=llList2String(y1,n++);if(o!=w){q1+=o;}}while((o!=w)&&(n<r1));m+=w+q1+w;}else
if((o=="/")&&(llList2String(y1,n)=="/")){return m;}else
if(0<=llListFindList(x1(),[o])){;}else if(0<=llListFindList(u,[o])){m+=o;}else
{m+=q2(o);}}return m;}string c2(string i,list m){string a=i+"(";integer
p1=-1;integer n;integer o1=llGetListLength(m);for(n=0;n<o1;++n){list
q1=llList2List(m,n,n);string
o=(string)q1;if(o=="["){p1=0;}if((n!=(p1+1))&&(o!="]")){a+=",
";}a+=o;}a+=");";return a;}list b2(list h,integer n){integer
l=llList2Integer(h,n);integer
n1=-1;while(0<l--){n1-=llList2Integer(h,n1+0);}integer
m1=llList2Integer(h,n1+0);integer l1=(m1-1);list e=h2(h,n1-l1,n1);return e;}list
z3(string i,list k1){list j1=[];list i1=[];integer l=0;integer p1=-1;integer
n;integer h1=llGetListLength(k1);for(n=0;n<h1;++n){list
g1=llList2List(k1,n,n);string o=(string)g1;if((o=="]")&&(0<=p1)){integer
l1=llGetListLength(j1)-p1;list
e=h2(j1,p1,llGetListLength(j1));j1=h2(j1,0,p1)+l;++l;i1=(l1+1)+i1;i1=e+i1;p1=-1;
}if(o=="["){p1=llGetListLength(j1);}else if(o=="]"){p1=-1;}else
if(llGetListEntryType(g1,0)==3){string
a=o;if(llGetSubString(o,0,0)==w){a=g2(o,1,-1);}j1+=a;}else {j1+=g1;}}return
j1+i1;}list x3(list f1){list e1=[];integer d=0;integer d1=0;integer
b=llGetListLength(f1);if(1<llGetListLength(f1)){d=1;d1=b-2;}integer
n;for(n=d;n<=d1;++n){integer
c1=llGetListEntryType(f1,n);if((c1==3)||(c1==4)){list
b1=llList2List(f1,n,n);string o=(string)b1;e1+=w+o+w;}else {list
b1=llList2List(f1,n,n);e1+=b1;}}if(1<b){return "["+e1+"]";}return e1;}list
y3(list m){list f1=[];list a1=[];integer n;integer
o1=llGetListLength(m);for(n=0;n<o1;++n){list q1=llList2List(m,n,n);string
o=(string)q1;if((o=="(")||(o=="<")){a1+=llGetListLength(f1);}else
if((o==")")||(o==">")){integer d=llList2Integer(a1,-1);a1=h2(a1,0,-1);list
k1=h2(f1,d,llGetListLength(f1));f1=h2(f1,0,d);string
i="<>";if(o==")"){i="()";list
z2=llList2List(k1,0,0);if((llGetListLength(k1)!=1)||(llListFindList(r,z2)<0)){i=
llList2String(f1,-1);f1=h2(f1,0,-1);}if(llGetSubString(i,0,0)==w){i=g2(i,1,-1);}
}if(x&&(llListFindList(["()","<>"],[i])<0)){llOwnerSay(c2(i,k1));}list
h=z3(i,k1);list x2=j2(i,h);list e1=x3(x2);f1+=e1;}else {f1+=q1;}}return
f1;}default{state_entry(){llListen(z,"",llGetOwner(),"");}dataserver(key
y2,string w2){llOwnerSay(c2("dataserver",x3([y2])+x3([w2])));}listen(integer
v2,string u2,key t2,string s2){llOwnerSay("// "+s2);list
y1=e2(s2,[],x1()+u);list m=d2(y1);list
f1=y3(m);if(f1!=[]){llOwnerSay(g2(c2("",f1),1,-2));}}}

And the sample output when I tested it:

Object: // llGetFreeMemory()
Object: llGetFreeMemory();
Object: 28670
Object: // llDialog(llGetOwner(), "A clarifying demo?", ["No", "Yes"], 7); // chat some Q & A
Object: llGetOwner();
Object: llDialog("1ad33407-a792-476d-a5e3-06007c0802bf", "A clarifying demo?", [, "No", "Yes"], 7);
Object: // No
Object: No
Object: // llSetColor(<1, 1, 1>, ALL_SIDES); // lighten
Object: llSetColor(<1.000000, 1.000000, 1.000000>, ALL_SIDES);
Object: // llSetText("look at me green", <0.0, 1.0, 0.0>, 1.0); // label
Object: llSetText("look at me green", <0.000000, 1.000000, 0.000000>, 1.000000);

Limitations & Bugs

Please be careful that any LSL script that does not compile in its original form, will not be generated properly in the minified version. In some ways, the minifer works as a lexer but it does not build syntax trees to check for consistency. Internally, it does check for bracketing and quotes using a stack - however that is only for internal use and the minifer will not report if an syntax error is found.