Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ast-node-cache #1339

Closed
wants to merge 3 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 43 additions & 46 deletions packages/@glimmer/syntax/lib/v2-a/objects/node.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,47 @@
import { assign } from '@glimmer/util';

import { SourceSpan } from '../../source/span';

export interface BaseNodeFields {
loc: SourceSpan;
}

class BaseNodeClass {
declare _loc?: SourceSpan;
declare loc: SourceSpan;
constructor(fields: object & { loc: SourceSpan }) {
Object.defineProperty(this, 'loc', {
get() {
return this._loc ?? fields.loc;
},
set(value) {
this._loc = value;
},
});
assign(this, fields);
}
}

const nodeKlassCache: Record<string, unknown> = {};

function cachedClass<T>(nodeName: T) {
if (!(nodeName in nodeKlassCache)) {
nodeKlassCache[(nodeName as unknown) as string] = class NodeKlass extends BaseNodeClass {
type: T = nodeName;
};
}

return nodeKlassCache[(nodeName as unknown) as string];
}

function klassForNode<T>(nodeName: T | undefined) {
if (nodeName === undefined) {
return BaseNodeClass;
} else {
return cachedClass<T>(nodeName);
}
}

/**
* This is a convenience function for creating ASTv2 nodes, with an optional name and the node's
* options.
Expand Down Expand Up @@ -39,7 +77,6 @@ export function node<T extends string>(
): {
fields<Fields extends object>(): TypedNodeConstructor<T, Fields & BaseNodeFields>;
};

export function node<T extends string>(
name?: T
):
Expand All @@ -49,43 +86,13 @@ export function node<T extends string>(
| {
fields<Fields extends object>(): NodeConstructor<Fields & BaseNodeFields>;
} {
if (name !== undefined) {
const type = name;
return {
fields<Fields extends object>(): TypedNodeConstructor<T, BaseNodeFields & Fields> {
return class {
readonly loc: SourceSpan;
readonly type: T;

constructor(fields: BaseNodeFields & Fields) {
this.type = type;
this.loc = fields.loc;
copy(fields, (this as unknown) as ConstructingTypedNode<Fields>);
}
} as TypedNodeConstructor<T, BaseNodeFields & Fields>;
},
};
} else {
return {
fields<Fields>(): NodeConstructor<Fields & BaseNodeFields> {
return class {
readonly loc: SourceSpan;

constructor(fields: BaseNodeFields & Fields) {
this.loc = fields.loc;

copy(fields, (this as unknown) as ConstructingNode<Fields>);
}
} as NodeConstructor<BaseNodeFields & Fields>;
},
};
}
return {
fields<Fields>(): NodeConstructor<Fields & BaseNodeFields> {
return klassForNode<T>(name) as NodeConstructor<Fields & BaseNodeFields>;
},
};
}

type ConstructingTypedNode<Fields> = Fields & BaseNodeFields;

type ConstructingNode<Fields> = BaseNodeFields & Fields;

export interface NodeConstructor<Fields> {
new (fields: Fields): Readonly<Fields>;
}
Expand All @@ -95,13 +102,3 @@ type TypedNode<T extends string, Fields> = { type: T } & Readonly<Fields>;
export interface TypedNodeConstructor<T extends string, Fields> {
new (options: Fields): TypedNode<T, Fields>;
}

function keys<O extends object>(object: O): (keyof O)[] {
return Object.keys(object) as (keyof O)[];
}

function copy<O extends object>(object1: O, object2: O) {
for (let key of keys(object1)) {
object2[key] = object1[key];
}
}