What steps will reproduce the problem?
1. Execute the following code:
var hashTable = new Hashtable();
var xmlStr = '<root><child attrName="attrValue">nodeValue</child></root>';
var xmlDoc = null;
if (window['DOMParser']) // Firefox, Chrome, Safari, etc.
{
xmlDoc = new DOMParser().parseFromString(xmlStr, 'text/xml');
}
else // For IE
{
xmlDoc = new ActiveXObject('Microsoft.XMLDOM');
xmlDoc.async = false;
xmlDoc.loadXML(xmlStr);
}
var key = new Array(xmlDoc);
hashTable.put(key, 10);
var value = hashTable.get(key);
What is the expected output? What do you see instead?
In this case the value of value should 10, you will get the following exception
on IE 8:
TypeError: Object doesn't support this property or method
What version of the product are you using? On what operating system?
2.1 on Windows 7
Please provide any additional information below.
This happens because internally the function hashcode(obj) ends up executing
the following code block:
} else if (typeof obj.toString == FUNCTION) {
return obj.toString();
There are 2 problems here:
1. For arrays, Array.toString() method internally calls toString() on each
element of the array, and concates the comma separated values.
ActiveXObject('Microsoft.XMLDOM') does not have a toString() method, hence it
throws the TypeError exception.
2. On FireFox etc., the corresponding type is XMLDocument.
XMLDocument.toString() returns something like [Object Document], which clearly
is a bad hash function.
Fix:
The ideal fix is to add the right toString() methods to these objects. However
that is something that browsers need to handle. The alternative fix is to
update the hashObject(obj) code as follows:
function hashObject(obj) {
var hashCode;
if (typeof obj == "string") {
return obj;
} else if (typeof obj.hashCode == FUNCTION) {
// Check the hashCode method really has returned a string
hashCode = obj.hashCode();
return (typeof hashCode == "string") ? hashCode : hashObject(hashCode);
} else if (obj instanceof Array) {
return arrayToString(obj);
} else if (typeof obj.toString == FUNCTION) {
return obj.toString();
} else {
try {
return String(obj);
} catch (ex) {
// For host objects (such as ActiveObjects in IE) that have no toString() method and throw an error when
// passed to String()
return Object.prototype.toString.call(obj);
}
}
}
/**
* Patched Array.toString() that for XML data types, applies to correct toString transformation on it
* @param arrayObj Array object whose toString need to be computed
* @return Stringified contents of the Array
*/
function arrayToString(arrayObj)
{
// TODO: Handle Array.toString() where arrayObj is null ...
var strOut = "";
for (var i = 0; i < arrayObj.length; i++)
{
if (i != 0)
strOut += ",";
var obj = arrayObj[i];
if (window['ActiveXObject'] && obj instanceof ActiveXObject && obj['xml'] != undefined) // XML on IE
strOut += obj.xml;
else if (window['Document'] && obj instanceof Document && window['DOMParser']) // XML on Firefox, Chrome, Safari etc.
strOut += (new XMLSerializer()).serializeToString(obj);
else if (obj instanceof Array)
strOut += arrayToString(obj);
else
strOut += obj;
}
return strOut;
}