In JavaScript, functions can be easily serialized (string), which is the source of the function. But actually the internal implementation of the operation (the engine implementation) is not as simple as you might think. Two techniques for serialization of functions have been used in SpiderMonkey: one is to use the reverse compiler (Decompiler) to compile the function of the bytecode back to the source code string, the other is to compile the function of the code before the source code to compress and store the function, and then unzip the restore .
How to serialize a function
In SpiderMonkey, there are three methods or functions that can serialize a function: Function.prototype.tostring,function.prototype.tosource, Uneval. Only the ToString method is standard, which is common to all engines. But the rules of the Function.prototype.toString method in the ES standard (ES5 15.3.4.2) have only a few words, that is, basically no standard, The engine decides how it should be implemented.
the role of function serialization
The primary role of function serialization should be to redefine the function by using the source code of the serialized generated function.
Copy Code code as follows:
function A () {
...
Alert ("a")
...
}
A ()//May eject "a" when executed
A = eval ("(" + a.tostring (). Replace (' Alert ("a") ', ' Alert ("B") ') + ")"
A ()//May eject "B" when executing
You might think, "I have written so many years of JavaScript, how not to meet this demand. Indeed, if it is their own site, the full control of the JS file, do not need to use this type of patching to modify the function, directly modify it. But if the source file is not something you can control, It is quite possible to do so. For example, there are Greasemonkey scripts that you might want to disable or modify a function in a Web site. Firefox extensions: You need to modify a function of Firefox itself (it can be said that Firefox is written in JS). I wrote it myself.
examples of Firefox scripts:
Copy Code code as follows:
Location = = "Chrome://browser/content/browser.xul" && eval ("gurlbar.handlecommand=" + GURLBar.handleCommand.toString (). Replace (/^\s* (load.+);/gm, "/^javascript:/.test (URL) | | (content.location== ' About:blank ' | | content.location== ' About:newtab ')? $1:gbrowser.loadonetab (Url,{postdata:postdata,inbackground:false, Allowthirdpartyfixup:true}) (")")
The purpose of this code is to allow Firefox to open the page in the new tab instead of the current tab when you enter the address bar. The implementation is to read the source code of the Gurlbar.handlecommand function with the ToString method, and then pass it to the Eval with a regular replacement, This function has been redefined.
Why not directly define the way, that is, to rewrite the function directly:
Gurlbar.handlecommand = function () {...//change the original functions to a small place}
The reason why we can't do this is because we have to consider compatibility, we should change the source code of this function as small as possible. If so, Firefox's Gurlbar.handlecommand source code changes, This script doesn't work. For example, Firefox3 and Firefox4 have this function, but the content of the function is very different, but if the replacement of the key words, as long as the replacement of this keyword has not changed, there will not be incompatible phenomenon.
anti-compile byte code
In SpiderMonkey, a function is compiled into a byte code (bytecode) after being parsed, meaning that it is stored in memory that is not the original function source. There is an inverse compiler in SpiderMonkey, whose main function is to decompile the function's bytecode into the form of the function source code.
In Firefox16 and earlier versions, SpiderMonkey used this method, and if you're using these versions of Firefox, you can try the following code:
Copy Code code as follows:
Alert (function () {
"String";
Comments
Return 1 + 2 + 3
}.tostring ())
The returned string is
function () {
return 6;
}
output is completely different from other browsers:
1. The literal values of the original value that are meaningless are deleted at compile time, in this case "string".
You might think: "It doesn't seem to be a problem, but these values have little meaning for the function to run." Wait, did you forget something, the string "use strict" in strict mode?
In versions that do not support strict mode, such as Firefox3.6, this "use strict" is no different from any other string, and will be deleted when compiled. After the SpiderMonkey is implemented in strict mode, the string is also ignored when compiling. Strict ", but in the reverse compilation will be judged, if this function in strict mode, it will be in the function body of the first line to add" use strict ", the following is the corresponding engine source code.
Static Jsbool
Copy Code code as follows:
Decompilebody (Jsprinter *jp, JSScript *script, Jsbytecode *pc)
{
/* Print a strict Mode code directive, if needed. */
if (Script->strictmodecode &&!jp->strict) {
if (Jp->fun && (Jp->fun->flags & jsfun_expr_closure)) {
/*
* We have no syntax for strict function expressions;
* At least give a hint.
*/
js_printf (JP, "\t/* use strict * *");
} else {
js_printf (JP, "\t\" use strict\ "; \ n");
}
Jp->strict = true;
}
Jsbytecode *end = Script->code + script->length;
Return Decompilecode (JP, script, PC, END-PC, 0);
}
2. Annotations are also deleted at compile time
This doesn't seem to have much effect, but some people are willing to use function annotations to implement multi-line strings, which are not available in versions prior to Firefox 17.
Copy Code code as follows:
function HereDoc (f) {
Return f.tostring (). Replace (/^.+\s/, ""). Replace (/.+$/, "");
}
var string = HereDoc (function () {/*
I
You
He
*/});
Console.log (String)
I
You
He
3. The operation of the literal values of the original value will be performed at compile time.
This is a kind of optimization, "High-performance JavaScript" mentioned:
the drawbacks of anti-compilation
Due to the advent of new technologies (such as rigorous mode) and the modification of other related bugs, the implementation of this part of the counter compiler often needs to be changed, and the changes may create new bugs, and I personally have encountered a bug. It is probably in the Firefox10 about the time, the specific question is not clear, Anyway, it's about the question of whether the parentheses are to be retained at the time of the decompile:
Copy Code code as follows:
> (function (a,b,c) {return (a+b) +c}). ToString ()
"Function (A, B, c) {
Return a + B + C;
}"
At the time of the decompile, the parentheses in (a+b) are omitted, because the addition binding law is left to right, so it doesn't matter. But the bug I encountered was this:
Copy Code code as follows:
> (function (a,b,c) {return a + (B+C)}). ToString ()
"Function (A, B, c) {
Return a + B + C;
}"
This is not the case, a+b+c is not equal to A + (B+C), for example, in a=1,b=2,c= "3", A+b+c equals "33", and A + (b+c) equals "123".
With regard to the anti-compiler, Mozilla engineer Luke Wagner noted that the counter compilers are a big hindrance to their ability to implement some of the new features, and that there are often bugs:
Not to pile on, but I too have felt a immense from the ' drag in ' last year. Testing coverage is also poor and no non-trivial change inevitably produces fuzz bugs. The sooner we remove this drag the sooner we start reaping the benefits. In Particular,i would be a much better time to remove it than over doing significant frontend/bytecode hacking for NE W language features.
Brendan Eich also said that the counter compiler does have a lot of undesirable:
I have no love for the Decompiler and it has been hacked over for years. Storage function Source Code
From the Firefox17, SpiderMonkey changed to the second implementation, and other browsers should be the way to do it. The string obtained by the function serialization is completely consistent with the source code, including whitespace, annotations, and so on. In that case, most of the problems should be gone. It seems that I have another question. or about strict mode.
Like what:
Copy Code code as follows:
(function A () {
"Use strict";
Alert ("A");
}) + ""
Of course, the return of the source code should also have "use strict", all browsers are so implemented:
Copy Code code as follows:
function A () {
"Use strict";
Alert ("A");
}
But what if it does:
Copy Code code as follows:
(function A () {
"Use strict";
return function B () {
Alert ("B")
}
})() + ""
Internal function B is also in strict mode, output B function source should not add "use strict" it. Test:
It says, Firefox17 before the Firefox4 version is by judging whether the current function is in strict mode to determine the output does not output "use strict", function B inherits the strict mode of function A, so there will be "use strict".
At the same time, the function of the source code is indented strict, because in the decompile, SpiderMonkey will be to decompile the source code to format, even before the source code is completely not indented also does not matter:
Copy Code code as follows:
function B () {
"Use strict";
Alert ("B");
}
Does the version after Firefox17 have "use strict"? Because it is directly to save the function source code, and function B does not have the word "use strict". The result of the test is that it will add "use strict", just indent a bit of a problem, Because there is no format for this step.
Copy Code code as follows:
function B () {
"Use strict";
Alert ("B")
}
SpiderMonkey the latest version of the Jsfun.cpp source has a corresponding annotation
If you have a "use strict" in one of the upper functions of a function, this function inherits the strict mode of the upper function.
We also insert "use strict" in the function body of this intrinsic function.
This ensures that if the return value of this function's ToString method is evaluated again,
The regenerated function will have the same semantics as the original function.
The difference is that other browsers do not have a "use strict":
Copy Code code as follows:
function B () {
Alert ("B")
}
Although this will not have much impact, but I think the implementation of Firefox is more reasonable.