Skip to content

Commit

Permalink
chore(jsii-reflect): use Maps instead of Records
Browse files Browse the repository at this point in the history
Instead of using objects as maps, use actual `Map` as this has better
performance characteristics (results in fewer shadow classes being
generated, and is friendlier with the optimizer).
  • Loading branch information
RomainMuller committed Jul 22, 2022
1 parent 29be798 commit a7b6d14
Show file tree
Hide file tree
Showing 7 changed files with 105 additions and 119 deletions.
67 changes: 31 additions & 36 deletions packages/jsii-reflect/lib/assembly.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ import { Type } from './type';
import { TypeSystem } from './type-system';

export class Assembly extends ModuleLike {
private _typeCache?: { [fqn: string]: Type };
private _submoduleCache?: { [fqn: string]: Submodule };
private _dependencyCache?: { [name: string]: Dependency };
private _typeCache?: Map<string, Type>;
private _submoduleCache?: Map<string, Submodule>;
private _dependencyCache?: Map<string, Dependency>;

public constructor(system: TypeSystem, public readonly spec: jsii.Assembly) {
super(system);
Expand Down Expand Up @@ -124,13 +124,11 @@ export class Assembly extends ModuleLike {
* Dependencies on other assemblies (with semver), the key is the JSII assembly name.
*/
public get dependencies(): readonly Dependency[] {
return Object.keys(this._dependencies).map(
(name) => this._dependencies[name],
);
return Array.from(this._dependencies.values());
}

public findDependency(name: string) {
const dep = this._dependencies[name];
const dep = this._dependencies.get(name);
if (!dep) {
throw new Error(`Dependency ${name} not found for assembly ${this.name}`);
}
Expand All @@ -156,7 +154,7 @@ export class Assembly extends ModuleLike {
*/
public get submodules(): readonly Submodule[] {
const { submodules } = this._analyzeTypes();
return Object.entries(submodules)
return Array.from(submodules.entries())
.filter(([name, _]) => name.split('.').length === 2)
.map(([_, submodule]) => submodule);
}
Expand All @@ -166,14 +164,14 @@ export class Assembly extends ModuleLike {
*/
public get allSubmodules(): readonly Submodule[] {
const { submodules } = this._analyzeTypes();
return Object.values(submodules);
return Array.from(submodules.values());
}

/**
* All types, even those in submodules and nested submodules.
*/
public get types(): readonly Type[] {
return Object.values(this.typeMap);
return Array.from(this.typeMap.values());
}

/**
Expand Down Expand Up @@ -201,26 +199,25 @@ export class Assembly extends ModuleLike {
jsii.validateAssembly(this.spec);
}

protected get submoduleMap(): Readonly<Record<string, Submodule>> {
protected get submoduleMap(): ReadonlyMap<string, Submodule> {
return this._analyzeTypes().submodules;
}

/**
* All types in the root of the assembly
*/
protected get typeMap(): Readonly<Record<string, Type>> {
protected get typeMap(): ReadonlyMap<string, Type> {
return this._analyzeTypes().types;
}

private get _dependencies() {
if (!this._dependencyCache) {
this._dependencyCache = {};
this._dependencyCache = new Map();
if (this.spec.dependencies) {
for (const name of Object.keys(this.spec.dependencies)) {
this._dependencyCache[name] = new Dependency(
this.system,
this._dependencyCache.set(
name,
this.spec.dependencies[name],
new Dependency(this.system, name, this.spec.dependencies[name]),
);
}
}
Expand All @@ -231,7 +228,7 @@ export class Assembly extends ModuleLike {

private _analyzeTypes() {
if (!this._typeCache || !this._submoduleCache) {
this._typeCache = {};
this._typeCache = new Map();

const submoduleBuilders = this.discoverSubmodules();

Expand Down Expand Up @@ -264,9 +261,9 @@ export class Assembly extends ModuleLike {

if (submodule != null) {
const moduleName = `${this.spec.name}.${submodule}`;
submoduleBuilders[moduleName].addType(type);
submoduleBuilders.get(moduleName)!.addType(type);
} else {
this._typeCache[fqn] = type;
this._typeCache.set(fqn, type);
}
}

Expand All @@ -279,18 +276,16 @@ export class Assembly extends ModuleLike {
* Return a builder for all submodules in this assembly (so that we can
* add types into the objects).
*/
private discoverSubmodules(): Record<string, SubmoduleBuilder> {
private discoverSubmodules(): Map<string, SubmoduleBuilder> {
const system = this.system;

const ret: Record<string, SubmoduleBuilder> = {};
const ret = new Map<string, SubmoduleBuilder>();
for (const [submoduleName, submoduleSpec] of Object.entries(
this.spec.submodules ?? {},
)) {
ret[submoduleName] = new SubmoduleBuilder(
system,
submoduleSpec,
ret.set(
submoduleName,
ret,
new SubmoduleBuilder(system, submoduleSpec, submoduleName, ret),
);
}
return ret;
Expand All @@ -306,15 +301,15 @@ export class Assembly extends ModuleLike {
* to translate
*/
class SubmoduleBuilder {
private readonly types: Record<string, Type> = {};
private readonly types = new Map<string, Type>();

private _built?: Submodule;

public constructor(
private readonly system: TypeSystem,
private readonly spec: jsii.Submodule,
private readonly fullName: string,
private readonly allModuleBuilders: Record<string, SubmoduleBuilder>,
private readonly allModuleBuilders: Map<string, SubmoduleBuilder>,
) {}

/**
Expand Down Expand Up @@ -344,27 +339,27 @@ class SubmoduleBuilder {
* Return all the builders from the map that are nested underneath ourselves.
*/
private findSubmoduleBuilders() {
const ret: Record<string, SubmoduleBuilder> = {};
for (const [k, child] of Object.entries(this.allModuleBuilders)) {
const ret = new Map<string, SubmoduleBuilder>();
for (const [k, child] of this.allModuleBuilders) {
if (child.isChildOf(this)) {
ret[k] = child;
ret.set(k, child);
}
}
return ret;
}

public addType(type: Type) {
this.types[type.fqn] = type;
this.types.set(type.fqn, type);
}
}

function mapValues<A, B>(
xs: Record<string, A>,
xs: ReadonlyMap<string, A>,
fn: (x: A) => B,
): Record<string, B> {
const ret: Record<string, B> = {};
for (const [k, v] of Object.entries(xs)) {
ret[k] = fn(v);
): Map<string, B> {
const ret = new Map<string, B>();
for (const [k, v] of xs) {
ret.set(k, fn(v));
}
return ret;
}
49 changes: 22 additions & 27 deletions packages/jsii-reflect/lib/class.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import { Method } from './method';
import { Property } from './property';
import { ReferenceType } from './reference-type';
import { TypeSystem } from './type-system';
import { indexBy } from './util';

export class ClassType extends ReferenceType {
public constructor(
Expand Down Expand Up @@ -76,15 +75,15 @@ export class ClassType extends ReferenceType {
* @param inherited include all properties inherited from base classes (default: false)
*/
public getProperties(inherited = false): { [name: string]: Property } {
return this._getProperties(inherited, this);
return Object.fromEntries(this._getProperties(inherited, this));
}

/**
* List all methods in this class.
* @param inherited include all methods inherited from base classes (default: false)
*/
public getMethods(inherited = false): { [name: string]: Method } {
return this._getMethods(inherited, this);
return Object.fromEntries(this._getMethods(inherited, this));
}

/**
Expand Down Expand Up @@ -118,39 +117,35 @@ export class ClassType extends ReferenceType {
private _getProperties(
inherited: boolean,
parentType: ReferenceType,
): { [name: string]: Property } {
const base =
): Map<string, Property> {
const result =
inherited && this.base
? this.base._getProperties(inherited, parentType)
: {};
return Object.assign(
base,
indexBy(
(this.spec.properties ?? []).map(
(p) => new Property(this.system, this.assembly, parentType, this, p),
),
(p) => p.name,
),
);
: new Map<string, Property>();
for (const p of this.spec.properties ?? []) {
result.set(
p.name,
new Property(this.system, this.assembly, parentType, this, p),
);
}
return result;
}

private _getMethods(
inherited: boolean,
parentType: ReferenceType,
): { [name: string]: Method } {
const base =
): Map<string, Method> {
const result =
inherited && this.base
? this.base._getMethods(inherited, parentType)
: {};
return Object.assign(
base,
indexBy(
(this.spec.methods ?? []).map(
(m) => new Method(this.system, this.assembly, parentType, this, m),
),
(m) => m.name,
),
);
: new Map<string, Method>();
for (const m of this.spec.methods ?? []) {
result.set(
m.name,
new Method(this.system, this.assembly, parentType, this, m),
);
}
return result;
}
}

Expand Down
56 changes: 29 additions & 27 deletions packages/jsii-reflect/lib/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { Method } from './method';
import { Property } from './property';
import { ReferenceType } from './reference-type';
import { TypeSystem } from './type-system';
import { indexBy } from './util';

export class InterfaceType extends ReferenceType {
/** Caches the result of `getInterfaces`. */
Expand Down Expand Up @@ -62,15 +61,15 @@ export class InterfaceType extends ReferenceType {
* @param inherited include all properties inherited from base classes (default: false)
*/
public getProperties(inherited = false): { [name: string]: Property } {
return this._getProperties(inherited, this);
return Object.fromEntries(this._getProperties(inherited, this));
}

/**
* List all methods in this class.
* @param inherited include all methods inherited from base classes (default: false)
*/
public getMethods(inherited = false): { [name: string]: Method } {
return this._getMethods(inherited, this);
return Object.fromEntries(this._getMethods(inherited, this));
}

public isDataType() {
Expand All @@ -84,42 +83,45 @@ export class InterfaceType extends ReferenceType {
private _getProperties(
inherited: boolean,
parentType: ReferenceType,
): { [name: string]: Property } {
const base: { [name: string]: Property } = {};
): Map<string, Property> {
const result = new Map<string, Property>();
if (inherited) {
for (const parent of this.getInterfaces()) {
Object.assign(base, parent._getProperties(inherited, parentType));
for (const [key, value] of parent._getProperties(
inherited,
parentType,
)) {
result.set(key, value);
}
}
}
return Object.assign(
base,
indexBy(
(this.spec.properties ?? []).map(
(p) => new Property(this.system, this.assembly, parentType, this, p),
),
(p) => p.name,
),
);
for (const p of this.spec.properties ?? []) {
result.set(
p.name,
new Property(this.system, this.assembly, parentType, this, p),
);
}
return result;
}

private _getMethods(
inherited: boolean,
parentType: ReferenceType,
): { [name: string]: Method } {
const base: { [name: string]: Property } = {};
): Map<string, Method> {
const methods = new Map<string, Method>();
if (inherited) {
for (const parent of this.getInterfaces()) {
Object.assign(base, parent._getMethods(inherited, parentType));
for (const [key, value] of parent._getMethods(inherited, parentType)) {
methods.set(key, value);
}
}
}
return Object.assign(
base,
indexBy(
(this.spec.methods ?? []).map(
(m) => new Method(this.system, this.assembly, parentType, this, m),
),
(m) => m.name,
),
);
for (const m of this.spec.methods ?? []) {
methods.set(
m.name,
new Method(this.system, this.assembly, parentType, this, m),
);
}
return methods;
}
}
Loading

0 comments on commit a7b6d14

Please sign in to comment.