Skip to content

Commit 386cd46

Browse files
refactor: generate node subclasses without eval
1 parent c3037db commit 386cd46

File tree

1 file changed

+31
-18
lines changed

1 file changed

+31
-18
lines changed

index.js

Lines changed: 31 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -867,7 +867,8 @@ function initializeLanguageNodeClasses(language) {
867867
const nodeTypeInfo = language.nodeTypeInfo || [];
868868

869869
const nodeSubclasses = [];
870-
for (let id = 0, n = nodeTypeNamesById.length; id < n; id++) {
870+
871+
for (let id = 0, n = nodeTypeNamesById.length; id < n; ++id) {
871872
nodeSubclasses[id] = SyntaxNode;
872873

873874
const typeName = nodeTypeNamesById[id];
@@ -877,51 +878,63 @@ function initializeLanguageNodeClasses(language) {
877878
if (!typeInfo) continue;
878879

879880
const fieldNames = [];
880-
let classBody = '\n';
881+
const fieldGetters = {};
882+
881883
if (typeInfo.fields) {
882884
for (const fieldName in typeInfo.fields) {
883885
const fieldId = nodeFieldNamesById.indexOf(fieldName);
884886
if (fieldId === -1) continue;
885887
if (typeInfo.fields[fieldName].multiple) {
886888
const getterName = camelCase(fieldName) + 'Nodes';
887889
fieldNames.push(getterName);
888-
classBody += `
889-
get ${getterName}() {
890+
fieldGetters[getterName] = {
891+
get: function () {
890892
marshalNode(this);
891-
return unmarshalNodes(NodeMethods.childNodesForFieldId(this.tree, ${fieldId}), this.tree);
892-
}
893-
`.replace(/\s+/g, ' ') + '\n';
893+
return unmarshalNodes(
894+
NodeMethods.childNodesForFieldId(this.tree, fieldId),
895+
this.tree
896+
);
897+
},
898+
enumerable: true
899+
};
894900
} else {
895901
const getterName = camelCase(fieldName, false) + 'Node';
896902
fieldNames.push(getterName);
897-
classBody += `
898-
get ${getterName}() {
903+
fieldGetters[getterName] = {
904+
get: function () {
899905
marshalNode(this);
900-
return unmarshalNode(NodeMethods.childNodeForFieldId(this.tree, ${fieldId}), this.tree);
901-
}
902-
`.replace(/\s+/g, ' ') + '\n';
906+
return unmarshalNode(
907+
NodeMethods.childNodeForFieldId(this.tree, fieldId),
908+
this.tree
909+
);
910+
},
911+
enumerable: true
912+
};
903913
}
904914
}
905915
}
906916

907-
const className = camelCase(typeName, true) + 'Node';
908-
const nodeSubclass = eval(`class ${className} extends SyntaxNode {${classBody}}; ${className}`);
909-
Object.defineProperties(nodeSubclass.prototype, {
917+
nodeSubclasses[id] = new Function('SyntaxNode', `
918+
return class ${camelCase(typeName, true)}Node extends SyntaxNode {}
919+
`)(SyntaxNode);
920+
921+
Object.defineProperties(nodeSubclasses[id].prototype, {
910922
type: {
911923
value: typeName,
912924
enumerable: true
913925
},
914926
fields: {
915927
value: Object.freeze(fieldNames.sort()),
916928
enumerable: true
917-
}
929+
},
930+
...fieldGetters
918931
});
919-
nodeSubclasses[id] = nodeSubclass;
920932
}
921933

922-
language.nodeSubclasses = nodeSubclasses
934+
language.nodeSubclasses = nodeSubclasses;
923935
}
924936

937+
925938
function camelCase(name, upperCase) {
926939
name = name.replace(/_(\w)/g, (_match, letter) => letter.toUpperCase());
927940
if (upperCase) name = name[0].toUpperCase() + name.slice(1);

0 commit comments

Comments
 (0)