diff --git a/src/services/compiler/bloomFilter.ts b/src/services/bloomFilter.ts
similarity index 62%
rename from src/services/compiler/bloomFilter.ts
rename to src/services/bloomFilter.ts
index 44d72bf5bfa..860b77f6bc7 100644
--- a/src/services/compiler/bloomFilter.ts
+++ b/src/services/bloomFilter.ts
@@ -1,7 +1,5 @@
-///
-
-module TypeScript {
+module ts {
export class BloomFilter {
private bitArray: boolean[];
private hashFunctionCount: number;
@@ -40,7 +38,7 @@ module TypeScript {
}
// m = ceil((n * log(p)) / log(1.0 / (pow(2.0, log(2.0)))))
- static computeM(expectedCount: number): number {
+ private static computeM(expectedCount: number): number {
var p: number = BloomFilter.falsePositiveProbability;
var n: number = expectedCount;
@@ -50,7 +48,7 @@ module TypeScript {
}
// k = round(log(2.0) * m / n)
- static computeK(expectedCount: number): number {
+ private static computeK(expectedCount: number): number {
var n: number = expectedCount;
var m: number = BloomFilter.computeM(expectedCount);
@@ -78,14 +76,12 @@ module TypeScript {
* Murmur hash is public domain. Actual code is included below as reference.
*/
private computeHash(key: string, seed: number): number {
- return Hash.computeMurmur2StringHashCode(key, seed);
+ return BloomFilter.computeMurmur2StringHashCode(key, seed);
}
- public addKeys(keys: ts.Map) {
- for (var name in keys) {
- if (ts.lookUp(keys, name)) {
- this.add(name);
- }
+ public addKeys(keys: string[]) {
+ for (var i = 0, n = keys.length; i < n; i++) {
+ this.add(keys[i]);
}
}
@@ -114,7 +110,7 @@ module TypeScript {
&& this.hashFunctionCount === filter.hashFunctionCount;
}
- static isEquivalent(array1: boolean[], array2: boolean[]): boolean {
+ private static isEquivalent(array1: boolean[], array2: boolean[]): boolean {
if (array1.length !== array2.length) {
return false;
}
@@ -127,5 +123,64 @@ module TypeScript {
return true;
}
+
+ private static integerMultiplyLow32Bits(n1: number, n2: number): number {
+ var n1Low16 = n1 & 0x0000ffff;
+ var n1High16 = n1 >>> 16;
+
+ var n2Low16 = n2 & 0x0000ffff;
+ var n2High16 = n2 >>> 16;
+
+ var resultLow32 = (((n1 & 0xffff0000) * n2) >>> 0) + (((n1 & 0x0000ffff) * n2) >>> 0) >>> 0;
+ return resultLow32;
+ }
+
+ private static computeMurmur2StringHashCode(key: string, seed: number): number {
+ // 'm' and 'r' are mixing constants generated offline.
+ // They're not really 'magic', they just happen to work well.
+
+ var m: number = 0x5bd1e995;
+ var r: number = 24;
+
+ // Initialize the hash to a 'random' value
+
+ var numberOfCharsLeft = key.length;
+ var h = Math.abs(seed ^ numberOfCharsLeft);
+
+ // Mix 4 bytes at a time into the hash. NOTE: 4 bytes is two chars, so we iterate
+ // through the string two chars at a time.
+ var index = 0;
+ while (numberOfCharsLeft >= 2) {
+ var c1 = key.charCodeAt(index);
+ var c2 = key.charCodeAt(index + 1);
+
+ var k = Math.abs(c1 | (c2 << 16));
+
+ k = BloomFilter.integerMultiplyLow32Bits(k, m);
+ k ^= k >> r;
+ k = BloomFilter.integerMultiplyLow32Bits(k, m);
+
+ h = BloomFilter.integerMultiplyLow32Bits(h, m);
+ h ^= k;
+
+ index += 2;
+ numberOfCharsLeft -= 2;
+ }
+
+ // Handle the last char (or 2 bytes) if they exist. This happens if the original string had
+ // odd length.
+ if (numberOfCharsLeft === 1) {
+ h ^= key.charCodeAt(index);
+ h = BloomFilter.integerMultiplyLow32Bits(h, m);
+ }
+
+ // Do a few final mixes of the hash to ensure the last few bytes are well-incorporated.
+
+ h ^= h >> 13;
+ h = BloomFilter.integerMultiplyLow32Bits(h, m);
+ h ^= h >> 15;
+
+ return h;
+ }
}
-}
+}
\ No newline at end of file
diff --git a/src/services/compiler/references.ts b/src/services/compiler/references.ts
index 464af87ec50..07ec0fa6043 100644
--- a/src/services/compiler/references.ts
+++ b/src/services/compiler/references.ts
@@ -18,7 +18,6 @@
/////
/////
/////
-/////
/////
/////
/////
diff --git a/src/services/core/hash.ts b/src/services/core/hash.ts
deleted file mode 100644
index ef117df8025..00000000000
--- a/src/services/core/hash.ts
+++ /dev/null
@@ -1,112 +0,0 @@
-///
-
-module TypeScript {
- export class Hash {
- // This table uses FNV1a as a string hash
- private static FNV_BASE = 2166136261;
- private static FNV_PRIME = 16777619;
-
- private static computeFnv1aCharArrayHashCode(text: number[], start: number, len: number): number {
- var hashCode = Hash.FNV_BASE;
- var end = start + len;
-
- for (var i = start; i < end; i++) {
- hashCode = IntegerUtilities.integerMultiplyLow32Bits(hashCode ^ text[i], Hash.FNV_PRIME);
- }
-
- return hashCode;
- }
-
- public static computeSimple31BitCharArrayHashCode(key: number[], start: number, len: number): number {
- // Start with an int.
- var hash = 0;
-
- for (var i = 0; i < len; i++) {
- var ch = key[start + i];
-
- // Left shift keeps things as a 32bit int. And we're only doing two adds. Chakra and
- // V8 recognize this as not needing to go past the 53 bits needed for the float
- // mantissa. Or'ing with 0 keeps this 32 bits.
- hash = ((((hash << 5) - hash) | 0) + ch) | 0;
- }
-
- // Ensure we fit in 31 bits. That way if/when this gets stored, it won't require any heap
- // allocation.
- return hash & 0x7FFFFFFF;
- }
-
- public static computeSimple31BitStringHashCode(key: string): number {
- // Start with an int.
- var hash = 0;
-
- var start = 0;
- var len = key.length;
-
- for (var i = 0; i < len; i++) {
- var ch = key.charCodeAt(start + i);
-
- // Left shift keeps things as a 32bit int. And we're only doing two adds. Chakra and
- // V8 recognize this as not needing to go past the 53 bits needed for the float
- // mantissa. Or'ing with 0 keeps this 32 bits.
- hash = ((((hash << 5) - hash) | 0) + ch) | 0;
- }
-
- // Ensure we fit in 31 bits. That way if/when this gets stored, it won't require any heap
- // allocation.
- return hash & 0x7FFFFFFF;
- }
-
- public static computeMurmur2StringHashCode(key: string, seed: number): number {
- // 'm' and 'r' are mixing constants generated offline.
- // They're not really 'magic', they just happen to work well.
-
- var m: number = 0x5bd1e995;
- var r: number = 24;
-
- // Initialize the hash to a 'random' value
-
- var numberOfCharsLeft = key.length;
- var h = Math.abs(seed ^ numberOfCharsLeft);
-
- // Mix 4 bytes at a time into the hash. NOTE: 4 bytes is two chars, so we iterate
- // through the string two chars at a time.
- var index = 0;
- while (numberOfCharsLeft >= 2) {
- var c1 = key.charCodeAt(index);
- var c2 = key.charCodeAt(index + 1);
-
- var k = Math.abs(c1 | (c2 << 16));
-
- k = IntegerUtilities.integerMultiplyLow32Bits(k, m);
- k ^= k >> r;
- k = IntegerUtilities.integerMultiplyLow32Bits(k, m);
-
- h = IntegerUtilities.integerMultiplyLow32Bits(h, m);
- h ^= k;
-
- index += 2;
- numberOfCharsLeft -= 2;
- }
-
- // Handle the last char (or 2 bytes) if they exist. This happens if the original string had
- // odd length.
- if (numberOfCharsLeft === 1) {
- h ^= key.charCodeAt(index);
- h = IntegerUtilities.integerMultiplyLow32Bits(h, m);
- }
-
- // Do a few final mixes of the hash to ensure the last few bytes are well-incorporated.
-
- h ^= h >> 13;
- h = IntegerUtilities.integerMultiplyLow32Bits(h, m);
- h ^= h >> 15;
-
- return h;
- }
-
- public static combine(value: number, currentHash: number): number {
- // Ensure we stay within 31 bits.
- return (((currentHash << 5) + currentHash) + value) & 0x7FFFFFFF;
- }
- }
-}
\ No newline at end of file
diff --git a/src/services/core/references.ts b/src/services/core/references.ts
index fcfd10126ec..8dcded411a6 100644
--- a/src/services/core/references.ts
+++ b/src/services/core/references.ts
@@ -6,7 +6,6 @@
///
///
///
-///
///
///
///
diff --git a/src/services/services.ts b/src/services/services.ts
index 9d52d93ad0c..9a88490ac38 100644
--- a/src/services/services.ts
+++ b/src/services/services.ts
@@ -11,7 +11,7 @@
///
///
///
-///
+///
///
///
@@ -71,7 +71,7 @@ module ts {
export interface SourceFile {
getSourceUnit(): TypeScript.SourceUnitSyntax;
getSyntaxTree(): TypeScript.SyntaxTree;
- getBloomFilter(): TypeScript.BloomFilter;
+ getBloomFilter(): BloomFilter;
update(scriptSnapshot: TypeScript.IScriptSnapshot, version: number, isOpen: boolean, textChangeRange: TypeScript.TextChangeRange): SourceFile;
}
@@ -324,7 +324,7 @@ module ts {
public isOpen: boolean;
public languageVersion: ScriptTarget;
- private bloomFilter: TypeScript.BloomFilter;
+ private bloomFilter: BloomFilter;
private syntaxTree: TypeScript.SyntaxTree;
private scriptSnapshot: TypeScript.IScriptSnapshot;
@@ -356,61 +356,27 @@ module ts {
return TypeScript.isDTSFile(this.filename);
}
- private static isNameOfPropertyDeclaration(node: TypeScript.ISyntaxElement): boolean {
- if (node.kind() !== TypeScript.SyntaxKind.IdentifierName && node.kind() !== TypeScript.SyntaxKind.StringLiteral && node.kind() !== TypeScript.SyntaxKind.NumericLiteral) {
- return false;
- }
-
- switch (node.parent.kind()) {
- case TypeScript.SyntaxKind.VariableDeclarator:
- return (node.parent).propertyName === node;
-
- case TypeScript.SyntaxKind.PropertySignature:
- return (node.parent).propertyName === node;
-
- case TypeScript.SyntaxKind.SimplePropertyAssignment:
- return (node.parent).propertyName === node;
-
- case TypeScript.SyntaxKind.EnumElement:
- return (node.parent).propertyName === node;
-
- case TypeScript.SyntaxKind.ModuleDeclaration:
- return (node.parent).name === node;
- }
-
- return false;
- }
-
- private static isElementAccessLiteralIndexer(node: TypeScript.ISyntaxElement): boolean {
- return (node.kind() === TypeScript.SyntaxKind.StringLiteral || node.kind() === TypeScript.SyntaxKind.NumericLiteral) &&
- node.parent.kind() === TypeScript.SyntaxKind.ElementAccessExpression && ( node.parent).argumentExpression === node;
- }
-
- public getBloomFilter(): TypeScript.BloomFilter {
+ public getBloomFilter(): BloomFilter {
if (!this.bloomFilter) {
- var identifiers = TypeScript.createIntrinsicsObject();
- var pre = function (cur: TypeScript.ISyntaxElement) {
- if (TypeScript.ASTHelpers.isValidAstNode(cur)) {
- if (cur.kind() === TypeScript.SyntaxKind.IdentifierName ||
- SourceFileObject.isNameOfPropertyDeclaration(cur) ||
- SourceFileObject.isElementAccessLiteralIndexer(cur)) {
- var nodeText = TypeScript.tokenValueText((cur));
+ var identifiers: string[] = [];
- identifiers[nodeText] = true;
- }
- }
- };
+ forEachChild(this, function visit (node: Node) {
+ switch (node.kind) {
+ case SyntaxKind.Identifier:
+ identifiers.push((node).text);
+ return undefined;
+ case SyntaxKind.StringLiteral:
+ case SyntaxKind.NumericLiteral:
+ if (isNameOfPropertyDeclaration(node) || isLiteralIndexOfIndexAccess(node)) {
+ identifiers.push((node).text);
+ }
+ return undefined;
+ default:
+ return forEachChild(node, visit);
+ };
+ });
- TypeScript.getAstWalkerFactory().simpleWalk(this.getSourceUnit(), pre, null, identifiers);
-
- var identifierCount = 0;
- for (var name in identifiers) {
- if (identifiers[name]) {
- identifierCount++;
- }
- }
-
- this.bloomFilter = new TypeScript.BloomFilter(identifierCount);
+ this.bloomFilter = new BloomFilter(identifiers.length);
this.bloomFilter.addKeys(identifiers);
}
return this.bloomFilter;
@@ -2375,7 +2341,7 @@ module ts {
// type to the search set
if (isNameOfPropertyAssignment(location)) {
var symbolFromContextualType = getPropertySymbolFromContextualType(location);
- if (symbolFromContextualType) result.push(symbolFromContextualType);
+ if (symbolFromContextualType) result.push(typeInfoResolver.getRootSymbol(symbolFromContextualType));
}
// Add symbol of properties/methods of the same name in base classes and implemented interfaces definitions
@@ -2429,7 +2395,7 @@ module ts {
// compare to our searchSymbol
if (isNameOfPropertyAssignment(referenceLocation)) {
var symbolFromContextualType = getPropertySymbolFromContextualType(referenceLocation);
- if (searchSymbols.indexOf(symbolFromContextualType) >= 0) {
+ if (symbolFromContextualType && searchSymbols.indexOf(typeInfoResolver.getRootSymbol(symbolFromContextualType)) >= 0) {
return true;
}
}
diff --git a/tests/cases/fourslash/referencesBloomFilters.ts b/tests/cases/fourslash/referencesBloomFilters.ts
new file mode 100644
index 00000000000..ea7dcfaaf07
--- /dev/null
+++ b/tests/cases/fourslash/referencesBloomFilters.ts
@@ -0,0 +1,20 @@
+///
+
+// Ensure BloomFilter building logic is correct, by having one reference per file
+
+// @Filename: declaration.ts
+////var container = { /*1*/searchProp : 1 };
+
+// @Filename: expression.ts
+////function blah() { return (1 + 2 + container./*2*/searchProp()) === 2; };
+
+// @Filename: stringIndexer.ts
+////function blah2() { container[/*3*/"searchProp"] };
+
+// @Filename: redeclaration.ts
+////container = { /*4*/"searchProp" : 18 };
+
+test.markers().forEach(m => {
+ goTo.position(m.position, m.fileName);
+ verify.referencesCountIs(4);
+});
\ No newline at end of file
diff --git a/tests/cases/fourslash/referencesBloomFilters2.ts b/tests/cases/fourslash/referencesBloomFilters2.ts
new file mode 100644
index 00000000000..7599e2fe96f
--- /dev/null
+++ b/tests/cases/fourslash/referencesBloomFilters2.ts
@@ -0,0 +1,20 @@
+///
+
+// Ensure BloomFilter building logic is correct, by having one reference per file
+
+// @Filename: declaration.ts
+////var container = { /*1*/42 : 1 };
+
+// @Filename: expression.ts
+////function blah() { return (container[/*2*/42]) === 2; };
+
+// @Filename: stringIndexer.ts
+////function blah2() { container[/*3*/"42"] };
+
+// @Filename: redeclaration.ts
+////container = { /*4*/"42" : 18 };
+
+test.markers().forEach(m => {
+ goTo.position(m.position, m.fileName);
+ verify.referencesCountIs(4);
+});
\ No newline at end of file
diff --git a/tests/cases/fourslash/referencesBloomFilters3.ts b/tests/cases/fourslash/referencesBloomFilters3.ts
new file mode 100644
index 00000000000..7056bab2393
--- /dev/null
+++ b/tests/cases/fourslash/referencesBloomFilters3.ts
@@ -0,0 +1,16 @@
+///
+
+// Ensure BloomFilter building logic is correct, by having one reference per file
+
+
+// @Filename: declaration.ts
+////enum Test { /*1*/"42" = 1 };
+
+// @Filename: expression.ts
+////(Test[/*2*/42]);
+
+
+test.markers().forEach(m => {
+ goTo.position(m.position, m.fileName);
+ verify.referencesCountIs(2);
+});
\ No newline at end of file