Skip to content

Commit 45aefd4

Browse files
breferraribhousel
authored andcommitted
fix(LCS): use Object.create(null) so prototype members don't shadow lookups
LCS stores equivalence classes in a plain object, so input elements that match Object.prototype member names (`constructor`, `__proto__`, `toString`, `hasOwnProperty`, etc.) hit the prototype-inherited member on the lookup and crash with: TypeError: equivalenceClasses[item].push is not a function `Object.create(null)` produces a dictionary with no prototype chain, so the lookup sees the key only if we wrote it. Zero API change. Regression test in test/LCS.test.js covers the common prototype names. Closes #86
1 parent 6824b46 commit 45aefd4

2 files changed

Lines changed: 26 additions & 1 deletion

File tree

src/diff3.mjs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,10 @@ export {
2222
//
2323
// Expects two arrays, finds longest common sequence
2424
function LCS(buffer1, buffer2) {
25-
let equivalenceClasses = {};
25+
// Use a prototype-less object so input elements that happen to match
26+
// `Object.prototype` members (`constructor`, `__proto__`, `toString`, ...)
27+
// don't shadow the lookup with inherited values.
28+
let equivalenceClasses = Object.create(null);
2629
for (let j = 0; j < buffer2.length; j++) {
2730
const item = buffer2[j];
2831
if (equivalenceClasses[item]) {

test/LCS.test.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,26 @@ describe('LCS', () => {
3131
assert.deepEqual(result.chain.chain.chain.chain.chain.chain, NULLRESULT);
3232
});
3333

34+
it('handles elements that match Object.prototype member names', () => {
35+
// Regression: a plain `{}` lookup table treats elements like 'constructor'
36+
// or '__proto__' as already-present (inherited from Object.prototype),
37+
// which used to crash with "equivalenceClasses[item].push is not a function".
38+
const prototypeNames = [
39+
'constructor',
40+
'__proto__',
41+
'toString',
42+
'hasOwnProperty',
43+
'valueOf',
44+
'isPrototypeOf',
45+
'propertyIsEnumerable',
46+
'__defineGetter__',
47+
];
48+
49+
for (const name of prototypeNames) {
50+
const result = Diff3.LCS([name], [name]);
51+
assert.deepEqual(result.buffer1index, 0);
52+
assert.deepEqual(result.buffer2index, 0);
53+
}
54+
});
55+
3456
});

0 commit comments

Comments
 (0)