Say I have this function:
function test(){
return a + b + 1;
}
How can I dynamically figure out that it will require globals a and b to be able to run? E.g. something like get_dependencies(test) returns ['a', 'b']
There's no built-in way to do that in standard JavaScript, if you're trying to do it with JavaScript itself.
On nearly all (but not all) JavaScript engines, you can get a form of the source of a function from the function object's toString function, e.g.:
var testSource = test.toString();
...and then of course you could parse that. This is non-standard behavior (the result of calling toString on a function is not defined in the specification), but it's widely-supported. You'd still have to do the parsing to find the symbols.
For the parsing, you have a couple of options. You could try to separate the parser portion of JSLint out of the rest of it, or alternately the terribly-named UglifyJS compressor has a full JavaScript parser which is already separate from the compressor part (see parse-js.js; apparently there's a tiny bit of NodeJS-specific stuff you might want to remove).
You can use a Javascript 'lint' tool that will test your code for common mistakes or oddities.
Some can be found online:
http://www.jslint.com/
http://www.javascriptlint.com/online_lint.php (can also be downloaded)
In your case, you might want to isolate individual functions via a regular expression for example, and submit them to such a tool.
Related
I can't reconcile the following with any of the JavaScript documentation I've read. Can somebody please shed some light?
The following snippet is taken from file panelUI.js in the Mozilla repository.
const PanelUI = {
/** Panel events that we listen for. **/
get kEvents() ["popupshowing", "popupshown", "popuphiding", "popuphidden"],
// more properties...
_addEventListeners: function() {
for (let event of this.kEvents) {
this.panel.addEventListener(event, this);
}
// more code...
},
// more properties...
Everything I've read about JS defines a getter as essentially a function (or "a method that gets the value of a specific property" and "The get syntax binds an object property to a function that will be called when that property is looked up"), so I'm a bit baffled to see an array literal where I would expect to find the body of function kEvents().
What does it mean in JS to have a function name followed by an array literal (in general or as part of a get definition)?
How would you write code that is functionally equivalent to the above, but does not use this somehow odd syntax?
I assume this is a consequence of SpiderMonkey's non-standard and deprecated support for expression closures.
this isn't valid JavaScript in any way... unless Firefox is allowing it as an alternative syntax for some reason.
but if you tried to run this or similar code in a browser like chrome, or even trying to compile it using Babel and ES6, it fails.
How would you write code that is functionally equivalent to the above, but does not use this somehow odd syntax?
An "equivalent" syntax appears to be to wrap the data in curly braces and return it:
get kEvents() {
return ["popupshowing", "popupshown", "popuphiding", "popuphidden"];
},
I would guess that the example code returns the same array instance every time, whereas my code is going to generate a new array every time it's called.
I imagine that the listed line is a non-standard syntax that mozilla has implemented but that is not associated with any current spec. Oftentimes with these sorts of features the browser development community pushes a browser to implement a new feature to see if it's worthwhile for standardization. It could have been a proposed syntax that was later dropped as well
That all said, this is speculative, as I've never seen a standard with that syntax in it.
Will any JS minifiers (uglify, closure, etc) go through the step of adding a variable for long property names? I've tried both and can't find flags to do this, but just wondering if anyone knows something I don't :)
e.g.:
obj.longPropertyName = 42;
obj.longPropertyName++;
obj.longPropertyName++;
obj.longPropertyName++;
could be minified to:
var a='longPropertyName';
obj[a]=42;
obj[a]++;
obj[a]++;
obj[a]++;
[edit] To be clear, closure will do this (or rather it will reduce to obj.a), but it won't with default props like window.addEventListener
Yes, some minifier have the additional flag to obfuscate function- and property-names. Mostly, those obfuscations lead to very short property names (but not every time).
EDIT: The YUI compressor for example will do this by default, if you don't pass the --nomunge argument to it.
It looks like Google's Dart language doesn't allow you to call native js functions (say, using jQuery or my existing .js code).
Does CoffeeScript allow any of the above?
yes you can use any javascript library with coffescript, just include the lib the usual way and write your code in the coffeescript 'style', so for a jquery example:
$(function () {
$('.something').on('click', function () {
console.log('you clicked me');
});
});
becomes
$ ->
$(".button").on "click", ->
console.log('you clicked me');
A quick google found some ineresting blog's on the subject, coffeescript & jquery fun and using jquery with coffeescript.
There is also a pragmantic programmer book with a chapter focused on using jquery and backbone in coffeescript applications
n.b. as pointed out, remember that the coffeescript 'compiler' wont check that functions exist, only that the syntax is correct
You can use jQuery and native JavaScript functions. You simply need to write them in the correct CoffeeScript syntax.
Bear in mind that CoffeeScript is a source to source compiler. It will transpile CoffeeScript to JavaScript. It won't know whether any specified functions exist.
So if you wrote this CoffeeScript, it would compile just fine:
words = ["hello", "world"]
alert word.touppercase() for word in words
Note that touppercase() is undefined in JavaScript. It should be toUpperCase(). Nonetheless, CoffeeScript will output:
var word, words, _i, _len;
words = ["hello", "world"];
for (_i = 0, _len = words.length; _i < _len; _i++) {
word = words[_i];
alert(word.touppercase());
}
You would then run into the error as a JavaScript error once you ran this in your browser, not a CoffeeScript error. You can use the "Try CoffeeScript" link on CoffeeScript site to see how the translation takes place and try to run it. You can also try it in jsFiddle by changing the Panels option to use CoffeeScript instead of JavaScript.
CoffeeScript is JavaScript.
Or rather, more precisely, CoffeeScript's only purpose is to make writing JavaScript an easier, 'cleaner' experience. All the CoffeeScript code you write is compiled into Javascript.
The CoffeeScript compiler only checks your code's syntax. It never bothers to check and see if the variables and functions you're referencing actually exist (this would be impossible to do with running the file anyway). So you can certainly call 'native' JavaScript functions with your CoffeeScript code but that's simple because they come out the other side as simple JavaScript function calls.
You are also able to use javascript within your coffeescript. All you have to do is use back-ticks:
hello = `function() {console.log("hello")}`
However, there is almost never a good reason to do this. Although there is one scenario where this is useful and that is reusing the same variable name in nested functions.
For example:
parent = ->
outer = 2
changeOuter = ->
`var outer` ##scopes outer to changeOuter
outer = 1
changeOuter()
return outer ##returns 2 but would have returned 1
##if we did not re-scope the varibale
I'm seeing code in the following form - is such use of eval() safe?
function genericTakeAction(frm_name,id,pagenum,action)
{
var rset=eval("document."+frm_name);
var x=eval("document."+frm_name+".edit_key");
var y=eval("document."+frm_name+".cAction")
if(x)
x.value=id;
if(y)
y.value=action;
page_list(pagenum);
}
Its used as:
<a href="javaScript:;" onClick="genericTakeAction('frmSearch',
'<?php echo $rec_id;?>','<?php echo $pagenum?>','makeOpen')"
class='link6'>Make Open</a>
Whether it's right or wrong, it's needlessly complicated.
function genericTakeAction(frm_name,id,pagenum,action)
{
var rset = document[frm_name];
var x = rset.edit_key;
var y = rset.cAction;
if(x)
x.value=id;
if(y)
y.value=action;
page_list(pagenum);
}
This works because in JavaScript, you can access an object's properties in one of two ways: Either using dotted syntax and a literal identifier, e.g. x = obj.foo;, or using bracket syntax and a string identifier, e.g. x = obj["foo"];. (Note how foo was not in quotes in the first one, but was in quotes for the second; but both do exactly the same thing. Also note that since the property name is a string in the second case, you can use any expression that results in a string, so y = "f"; x = obj[y + "oo"]; also works.)
P.S. It's wrong
eval() is generally frowned upon because, as you are already aware, it is considered unsafe.
In the browser environment, however, it is less of an issue, because in fact, any user could eval() any code they wanted to, using tools like Firebug, etc.
There is still an issue, in that the eval() embedded in the code can be run without the user knowing that he was triggering an eval(), but it's still much less of an issue than in a server-side environment like PHP.
eval() is actually typically used as you've shown to run JSON code being returned from a server-side request. Newer browsers can import JSON more safely using a dedicated JSON parse() function, but older browsers do not have this function and are forced to use eval() for this. Most JSON libraries have eval() in their code somewhere for this reason, but will generally do some sanitisation of the input before running it through eval().
Even if it might look a little bit convoluted, as others have already mentioned, from a pure security perspective, you have to make sure that the 'frm_name' parameter of the genericTakeAction() function can never contain user-supplied data.
In your example, the 'frm_name' parameter contains the hard-coded literal 'frmSearch'. So it is ok as long as this genericTakeAction() function does not get called somewhere else with user-supplied data for the 'frm_name' parameter.
See http://en.wikipedia.org/wiki/Cross-site_scripting#Traditional_versus_DOM-based_vulnerabilities
I've recently tested UglifyJS and YUI Compressor and noticed something odd.
Both minifiers don't seem to change the names of object properties, only the names of variables and functions.
for instance if I have the following code:
var objName = {first:2, second:4};
alert(objName.first + " " + objName.second);
the names first and second remain unchanged in the minified version.
Why is that?
Since in javascript a new scope is created in a function, you can scope your code in an immediately invoked function.
// scoped
(function() {
var objName = {first:2, second:4};
alert(objName.first + " " + objName.second);
})();
Then using Google's Closure Compiler, if you turn on the "Advanced" optimization it will see that the properties are only used locally, and will obfuscate them.
// result
var a={a:2,b:4};alert(a.a+" "+a.b);
It's because it doesn't know where the object is going to be used. It could be used externally by other code and you wouldn't want your other code to have to change whenever you obfuscate it.
Edit So basically, it's like that to prevent obfuscation from breaking external/internal references to properties that may not be possible to figure out while obfuscating.
Since there are no well defined scoping rules around objects in JavaScript it's impossible to obfuscate the names in a way that is guaranteed to be correct.
For example, if you had the following function:
function f() {
return { first: 'foo', second: 'bar' };
}
In order to obfuscate the property names you would have to nail down all the places that f is called from. Since functions are first-class in JavaScript they can be assigned and passed around in arbitrary ways making it impossible to pin down where f is referenced without actually running the program.
Additionally, JavaScript doesn't have any way for you to specify intent around what's public API and what isn't. Even if the minimizer could reliably determine where the function is called from in the code you give it, there would be no way for it to make the same changes to code that it hasn't seen.
I guess that's because the minifiers would break the object properties. Consider this:
function getProp(ob,name) {
return ob[name];
}
var objName = {first: 2, second: 4};
var prop = getProp(objName, "second");
There's no way for the minifier to know the string literal "second" being an object property. The minified code could look like this then:
function a(b,c){return b[c]}var d={p1:2,p2:4};var e=a(d,"second")
Broken now.
The latest release of uglify (today) has object property mangling, see v2.4.19. It also supports reserved files for excluding both object properties and variables that you don't want mangled. Check it out.
The only public tool so far to obfuscate property and function names (afaik) is the Closure Compiler's Advanced mode. There are a lot of limitations and restrictions, but the end result is generally worth it.
As a passing note: the Dojo Toolkit is compatible (with some minor modifications) with the Closure Compiler in Advanced mode -- arguably the only large-scale public JavaScript library that can be fully obfuscated. So if you are looking at obfuscation to protect your IP, you should look into using Dojo for the task.
http://dojo-toolkit.33424.n3.nabble.com/file/n2636749/Using_the_Dojo_Toolkit_with_the_Closure_Compiler.pdf?by-user=t
Stephen
What about doing something like:
// scoped
(function() {
var objName = {first:2, second:4};
var vA = 'first';
var vB = 'second';
alert(objName[vA] + " " + objName[vB]);
})();
Once objName.first and/or objName.second are referenced enough times, this technique will start to save characters. I can't think of any reason that wouldn't work, but I can't find any minifiers that do it.