create-function.js
2.21 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
/**
* Create a function from a string
*/
var evalFunction = (function () {
/* jshint unused: false */
// shadow global symbols
var evalFunction;
var createFunction;
return function (contextSrc, functionSrc) {
/* jshint evil: true */
var src = '(function () {\n' +
contextSrc +
'return ' + functionSrc + ';' +
'}())';
return eval(src);
};
}());
var createFunction = (function () {
/**
* Check that we can create a single function from the passed source and
* return checked source.
*
* This function is intended to prevent this sort of thing:
* createFunction('function yo() { } foo()')
* which would run foo in our context.
*
* This check is intended to find mistakes, not defend against malice.
*/
function getCleanSource(src) {
var newlineToken = '_!crlf!_';
var newlineTokenPattern = new RegExp(newlineToken, 'g');
var parsed, name, args, body;
// parse the function source into name, arguments, and body
// currently, names must by alphanumeric.
parsed = src.replace(/\r?\n/g, newlineToken).
match(/^function\s+([a-zA-Z_]\w*)?\s*\(([^)]*)\)\s*\{(.*)\}$/);
if (!parsed) {
throw 'Cannot parse function: ' +
src.replace(newlineTokenPattern, '\n');
}
name = parsed[1] || '';
args = parsed[2].split(/\s*,\s*/);
body = parsed[3].replace(newlineTokenPattern, '\n');
return ['function', name, '(', args.join(', '), ')', '{', body, '}'].join(' ');
}
function parseFunction(fnSource) {
var context = {};
var contextSource = '';
if (fnSource instanceof Array) {
context = fnSource[0];
fnSource = fnSource[1];
}
for (var k in context) {
contextSource += [
'var', k, '=', JSON.stringify(context[k]) + ';\n'
].join(' ');
}
fnSource = getCleanSource(fnSource);
return evalFunction(contextSource, fnSource);
}
return function (fn) {
return fn ? parseFunction(fn) : void undefined;
};
}());
module.exports = createFunction;