Rewrite sort compiler to avoid eval, too!

This commit is contained in:
David Glasser
2013-02-04 16:42:18 -08:00
parent 0567a730b1
commit 3bdf41add1
3 changed files with 58 additions and 66 deletions

View File

@@ -13,7 +13,6 @@ Package.on_use(function (api, where) {
api.add_files([
'minimongo.js',
'selector.js',
'sort.js',
'uuid.js',
'modify.js',
'diff.js'

View File

@@ -543,4 +543,62 @@ LocalCollection._selectorIsId = function (selector) {
return (typeof selector === "string") || (typeof selector === "number");
};
// Give a sort spec, which can be in any of these forms:
// {"key1": 1, "key2": -1}
// [["key1", "asc"], ["key2", "desc"]]
// ["key1", ["key2", "desc"]]
//
// (.. with the first form being dependent on the key enumeration
// behavior of your javascript VM, which usually does what you mean in
// this case if the key names don't look like integers ..)
//
// return a function that takes two objects, and returns -1 if the
// first object comes first in order, 1 if the second object comes
// first, or 0 if neither object comes before the other.
LocalCollection._compileSort = function (spec) {
var sortSpecParts = [];
if (spec instanceof Array) {
for (var i = 0; i < spec.length; i++) {
if (typeof spec[i] === "string") {
sortSpecParts.push({
lookup: makeLookupFunction(spec[i]),
ascending: true
});
} else {
sortSpecParts.push({
lookup: makeLookupFunction(spec[i][0]),
ascending: spec[i][1] !== "desc"
});
}
}
} else if (typeof spec === "object") {
for (var key in spec) {
sortSpecParts.push({
lookup: makeLookupFunction(key),
ascending: spec[key] >= 0
});
}
} else {
throw Error("Bad sort specification: ", JSON.stringify(spec));
}
if (sortSpecParts.length === 0)
return function () {return 0;};
return function (a, b) {
for (var i = 0; i < sortSpecParts.length; ++i) {
var specPart = sortSpecParts[i];
var aValue = specPart.lookup(a);
var bValue = specPart.lookup(b);
var compare = LocalCollection._f._cmp(aValue, bValue);
if (compare !== 0)
return specPart.ascending ? compare : -compare;
};
return 0;
};
};
})();

View File

@@ -1,65 +0,0 @@
// Give a sort spec, which can be in any of these forms:
// {"key1": 1, "key2": -1}
// [["key1", "asc"], ["key2", "desc"]]
// ["key1", ["key2", "desc"]]
//
// (.. with the first form being dependent on the key enumeration
// behavior of your javascript VM, which usually does what you mean in
// this case if the key names don't look like integers ..)
//
// return a function that takes two objects, and returns -1 if the
// first object comes first in order, 1 if the second object comes
// first, or 0 if neither object comes before the other.
LocalCollection._compileSort = function (spec) {
var keys = [];
var asc = [];
if (spec instanceof Array) {
for (var i = 0; i < spec.length; i++) {
if (typeof spec[i] === "string") {
keys.push(spec[i]);
asc.push(true);
} else {
keys.push(spec[i][0]);
asc.push(spec[i][1] !== "desc");
}
}
} else if (typeof spec === "object") {
for (key in spec) {
keys.push(key);
asc.push(!(spec[key] < 0));
}
} else {
throw Error("Bad sort specification: ", JSON.stringify(spec));
}
if (keys.length === 0)
return function () {return 0;};
// eval() does not return a value in IE8, nor does the spec say it
// should. Assign to a local to get the value, instead.
var _func;
var code = "_func = (function(c){return function(a,b){var x;";
for (var i = 0; i < keys.length; i++) {
// handle dotted subpaths. Make sure to avoid dereferencing
// undefined if a subkey doesn't exist.
var splittedKeys = keys[i].split(".");
var keyString = "";
var aCode = "a";
var bCode = "b";
for(var o = 0; o < splittedKeys.length; o++) {
keyString = keyString + "[" + JSON.stringify(splittedKeys[o]) + "]";
aCode += '&&a' + keyString;
bCode += '&&b' + keyString;
}
if (i !== 0) {
code += "if(x!==0)return x;";
}
code += "x=" + (asc[i] ? "" : "-") +
"c(" + aCode + "," + bCode + ");";
}
code += "return x;};})";
eval(code);
return _func(LocalCollection._f._cmp);
};