Freeze cached Sim objects

We're hunting ~~wabbits~~ validator bugs.

Honestly, this has been a long time coming, but Object.freeze perf
used to not be good enough for us to use it here. Here's hoping!
This commit is contained in:
Guangcong Luo 2023-12-07 01:14:32 -05:00
parent ea967d403a
commit 9cd64cba15
9 changed files with 32 additions and 15 deletions

View File

@ -153,7 +153,7 @@ export abstract class Database<Pool extends mysql.Pool | pg.Pool = mysql.Pool |
const [query, values] = this._resolveSQL(sql);
return this._queryExec(query, values);
}
getTable<Row>(name: string, primaryKeyName: keyof Row & string | null = null) {
getTable<Row>(name: string, primaryKeyName: keyof Row & string | null = null): DatabaseTable<Row, this> {
return new DatabaseTable<Row, this>(this, name, primaryKeyName);
}
close() {

View File

@ -335,6 +335,22 @@ export function deepClone(obj: any): any {
return clone;
}
export function deepFreeze<T>(obj: T): T {
if (obj === null || typeof obj !== 'object') return obj;
// support objects with reference loops
if (Object.isFrozen(obj)) return obj;
Object.freeze(obj);
if (Array.isArray(obj)) {
for (const elem of obj) deepFreeze(elem);
return obj;
}
for (const key of Object.keys(obj)) {
deepFreeze((obj as any)[key]);
}
return obj;
}
export function levenshtein(s: string, t: string, l: number): number {
// Original levenshtein distance function by James Westgate, turned out to be the fastest
const d: number[][] = [];

View File

@ -97,7 +97,7 @@ export class DexAbilities {
});
}
if (ability.exists) this.abilityCache.set(id, ability);
if (ability.exists) this.abilityCache.set(id, this.dex.deepFreeze(ability));
return ability;
}

View File

@ -673,7 +673,7 @@ export class DexConditions {
condition = new Condition({name: id, exists: false});
}
this.conditionCache.set(id, condition);
this.conditionCache.set(id, this.dex.deepFreeze(condition));
return condition;
}
}

View File

@ -178,7 +178,7 @@ export class DexNatures {
nature = new Nature({name: id, exists: false});
}
if (nature.exists) this.natureCache.set(id, nature);
if (nature.exists) this.natureCache.set(id, this.dex.deepFreeze(nature));
return nature;
}
@ -188,7 +188,7 @@ export class DexNatures {
for (const id in this.dex.data.Natures) {
natures.push(this.getByID(id as ID));
}
this.allCache = natures;
this.allCache = Object.freeze(natures);
return this.allCache;
}
}
@ -278,7 +278,7 @@ export class DexTypes {
type = new TypeInfo({name: typeName, id, exists: false, effectType: 'EffectType'});
}
if (type.exists) this.typeCache.set(id, type);
if (type.exists) this.typeCache.set(id, this.dex.deepFreeze(type));
return type;
}
@ -302,7 +302,7 @@ export class DexTypes {
for (const id in this.dex.data.TypeChart) {
types.push(this.getByID(id as ID));
}
this.allCache = types;
this.allCache = Object.freeze(types);
return this.allCache;
}
}

View File

@ -202,7 +202,7 @@ export class DexItems {
item = new Item({name: id, exists: false});
}
if (item.exists) this.itemCache.set(id, item);
if (item.exists) this.itemCache.set(id, this.dex.deepFreeze(item));
return item;
}
@ -212,7 +212,7 @@ export class DexItems {
for (const id in this.dex.data.Items) {
items.push(this.getByID(id as ID));
}
this.allCache = items;
this.allCache = Object.freeze(items);
return this.allCache;
}
}

View File

@ -654,7 +654,7 @@ export class DexMoves {
name: id, exists: false,
});
}
if (move.exists) this.moveCache.set(id, move);
if (move.exists) this.moveCache.set(id, this.dex.deepFreeze(move));
return move;
}
@ -664,7 +664,7 @@ export class DexMoves {
for (const id in this.dex.data.Moves) {
moves.push(this.getByID(id as ID));
}
this.allCache = moves;
this.allCache = Object.freeze(moves);
return this.allCache;
}
}

View File

@ -397,7 +397,7 @@ export class DexSpecies {
}
}
}
this.speciesCache.set(id, species);
this.speciesCache.set(id, this.dex.deepFreeze(species));
return species;
}
@ -516,7 +516,7 @@ export class DexSpecies {
exists: false, tier: 'Illegal', doublesTier: 'Illegal', natDexTier: 'Illegal', isNonstandard: 'Custom',
});
}
if (species.exists) this.speciesCache.set(id, species);
if (species.exists) this.speciesCache.set(id, this.dex.deepFreeze(species));
return species;
}
@ -531,7 +531,7 @@ export class DexSpecies {
return new Learnset({exists: false});
}
learnsetData = new Learnset(this.dex.data.Learnsets[id]);
this.learnsetCache.set(id, learnsetData);
this.learnsetCache.set(id, this.dex.deepFreeze(learnsetData));
return learnsetData;
}
@ -545,7 +545,7 @@ export class DexSpecies {
for (const id in this.dex.data.Pokedex) {
species.push(this.getByID(id as ID));
}
this.allCache = species;
this.allCache = Object.freeze(species);
return this.allCache;
}
}

View File

@ -122,6 +122,7 @@ export class ModdedDex {
textCache: TextTableData | null;
deepClone = Utils.deepClone;
deepFreeze = Utils.deepFreeze;
readonly formats: DexFormats;
readonly abilities: DexAbilities;