Skip to content

Commit 484c5df

Browse files
committed
Improved Identifier scanning, function length calculator + tests
1 parent f1011fd commit 484c5df

File tree

5 files changed

+246
-10
lines changed

5 files changed

+246
-10
lines changed

src/util/gen.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,12 @@ export function BreakStatement(label?: string) {
171171
};
172172
}
173173

174-
export function Property(key: Node, value: Node, computed = false) {
174+
export function Property(
175+
key: Node,
176+
value: Node,
177+
computed = false,
178+
kind: "init" | "set" | "get" = "init"
179+
) {
175180
if (!key) {
176181
throw new Error("key is undefined");
177182
}
@@ -183,7 +188,7 @@ export function Property(key: Node, value: Node, computed = false) {
183188
key: key,
184189
computed: computed,
185190
value: value,
186-
kind: "init",
191+
kind: kind,
187192
method: false,
188193
shorthand: false,
189194
};

src/util/identifiers.ts

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,23 @@ export function validateChain(object: Node, parents: Node[]) {
3030
}
3131
}
3232

33+
function objectPatternCheck(object: Node, parents: Node[]) {
34+
var objectPatternIndex = parents.findIndex((x) => x.type === "ObjectPattern");
35+
if (objectPatternIndex == -1) {
36+
return true;
37+
}
38+
39+
var property = parents[objectPatternIndex].properties.find(
40+
(property) => parents[objectPatternIndex - 2] === property
41+
);
42+
43+
if (property.key === (parents[objectPatternIndex - 3] || object)) {
44+
return false;
45+
}
46+
47+
return true;
48+
}
49+
3350
/**
3451
* Returns detailed information about the given Identifier node.
3552
* @param object
@@ -74,7 +91,22 @@ export function getIdentifierInfo(object: Node, parents: Node[]) {
7491
var isVariableDeclaration =
7592
varIndex != -1 &&
7693
parents[varIndex].id == (parents[varIndex - 1] || object) &&
77-
parents.find((x) => x.type == "VariableDeclaration");
94+
parents.find((x) => x.type == "VariableDeclaration") &&
95+
objectPatternCheck(object, parents);
96+
97+
// Assignment pattern check!
98+
if (isVariableDeclaration) {
99+
var slicedParents = parents.slice(0, varIndex - 1);
100+
var i = 0;
101+
for (var parent of slicedParents) {
102+
var childNode = slicedParents[i - 1] || object;
103+
if (parent.type === "AssignmentPattern" && parent.right === childNode) {
104+
isVariableDeclaration = false;
105+
break;
106+
}
107+
i++;
108+
}
109+
}
78110

79111
var forIndex = parents.findIndex((x) => x.type == "ForStatement");
80112
var isForInitializer =
@@ -87,6 +119,12 @@ export function getIdentifierInfo(object: Node, parents: Node[]) {
87119
functionIndex != -1 &&
88120
parents[functionIndex].type == "FunctionDeclaration" &&
89121
parents[functionIndex].id == object;
122+
123+
var isNamedFunctionExpression =
124+
functionIndex != -1 &&
125+
parents[functionIndex].type === "FunctionExpression" &&
126+
parents[functionIndex].id === object;
127+
90128
var isAFunctionParameter = isFunctionParameter(object, parents);
91129

92130
var isClauseParameter = false;
@@ -112,7 +150,9 @@ export function getIdentifierInfo(object: Node, parents: Node[]) {
112150

113151
var isAssignmentLeft =
114152
assignmentIndex !== -1 &&
115-
parents[assignmentIndex].left === (parents[assignmentIndex - 1] || object);
153+
parents[assignmentIndex].left ===
154+
(parents[assignmentIndex - 1] || object) &&
155+
objectPatternCheck(object, parents);
116156
var isAssignmentValue =
117157
assignmentIndex !== -1 &&
118158
parents[assignmentIndex].right === (parents[assignmentIndex - 1] || object);
@@ -272,6 +312,7 @@ export function getIdentifierInfo(object: Node, parents: Node[]) {
272312
isDefined:
273313
isVariableDeclaration ||
274314
isFunctionDeclaration ||
315+
isNamedFunctionExpression ||
275316
isAFunctionParameter ||
276317
isClassDeclaration ||
277318
isClauseParameter ||

src/util/insert.ts

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,13 +145,13 @@ export function getAllDefiningContexts(o: Node, p: Node[]): Node[] {
145145
// Get Function
146146
var fn = getFunction(o, p);
147147

148-
contexts.push(fn.body);
148+
// contexts.push(fn.body);
149149
}
150150

151151
if (info.isClauseParameter) {
152152
var catchClause = p.find((x) => x.type === "CatchClause");
153153
if (catchClause) {
154-
contexts.push(catchClause.body);
154+
return [catchClause];
155155
}
156156
}
157157

@@ -376,3 +376,27 @@ export function isForInitialize(
376376

377377
return false;
378378
}
379+
380+
/**
381+
* Computes the `function.length` property given the parameter nodes.
382+
*
383+
* @param params
384+
* @returns
385+
*/
386+
export function computeFunctionLength(params: Node[]): number {
387+
var count = 0;
388+
389+
for (var parameterNode of params) {
390+
if (
391+
parameterNode.type === "Identifier" ||
392+
parameterNode.type === "ObjectPattern" ||
393+
parameterNode.type === "ArrayPattern"
394+
) {
395+
count++;
396+
} else {
397+
break;
398+
}
399+
}
400+
401+
return count;
402+
}

test/util/identifiers.test.ts

Lines changed: 113 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
import parseJS from "../../src/parser";
1+
import { ok } from "assert";
2+
import parseJS, { parseSync } from "../../src/parser";
3+
import traverse from "../../src/traverse";
4+
import { Location, Node } from "../../src/util/gen";
25
import {
36
getFunctionParameters,
47
getIdentifierInfo,
@@ -41,6 +44,115 @@ describe("getIdentifierInfo", () => {
4144
getIdentifierInfo({ type: "Literal", value: true }, []);
4245
}).toThrow();
4346
});
47+
48+
function findIdentifier(tree: Node, identifierName: string) {
49+
var searchLocation: Location;
50+
51+
traverse(tree, (o, p) => {
52+
if (o.type === "Identifier" && o.name === identifierName) {
53+
ok(!searchLocation);
54+
searchLocation = [o, p];
55+
}
56+
});
57+
58+
ok(searchLocation);
59+
return searchLocation;
60+
}
61+
62+
test("Variant #4: Variable declaration assignment pattern", async () => {
63+
var tree = parseSync(`
64+
var [ definedIdentifier = nonDefinedIdentifier ] = [];
65+
`);
66+
67+
var definedIdentifier = findIdentifier(tree, "definedIdentifier");
68+
var definedInfo = getIdentifierInfo(
69+
definedIdentifier[0],
70+
definedIdentifier[1]
71+
);
72+
expect(definedInfo.spec.isDefined).toStrictEqual(true);
73+
expect(definedInfo.spec.isReferenced).toStrictEqual(true);
74+
75+
var nonDefinedIdentifier = findIdentifier(tree, "nonDefinedIdentifier");
76+
var nonDefinedInfo = getIdentifierInfo(
77+
nonDefinedIdentifier[0],
78+
nonDefinedIdentifier[1]
79+
);
80+
expect(nonDefinedInfo.spec.isDefined).toStrictEqual(false);
81+
expect(nonDefinedInfo.spec.isReferenced).toStrictEqual(true);
82+
});
83+
84+
test("Variant #5: Function parameter assignment pattern", async () => {
85+
var tree = parseSync(`
86+
function myFunction(definedIdentifier = nonDefinedIdentifier) {
87+
88+
}
89+
`);
90+
91+
var myFunction = findIdentifier(tree, "myFunction");
92+
var myFunctionInfo = getIdentifierInfo(myFunction[0], myFunction[1]);
93+
94+
expect(myFunctionInfo.isFunctionDeclaration).toStrictEqual(true);
95+
expect(myFunctionInfo.spec.isDefined).toStrictEqual(true);
96+
97+
var definedIdentifier = findIdentifier(tree, "definedIdentifier");
98+
var definedInfo = getIdentifierInfo(
99+
definedIdentifier[0],
100+
definedIdentifier[1]
101+
);
102+
expect(definedInfo.spec.isDefined).toStrictEqual(true);
103+
expect(definedInfo.spec.isReferenced).toStrictEqual(true);
104+
105+
var nonDefinedIdentifier = findIdentifier(tree, "nonDefinedIdentifier");
106+
var nonDefinedInfo = getIdentifierInfo(
107+
nonDefinedIdentifier[0],
108+
nonDefinedIdentifier[1]
109+
);
110+
expect(nonDefinedInfo.spec.isDefined).toStrictEqual(false);
111+
expect(nonDefinedInfo.spec.isReferenced).toStrictEqual(true);
112+
});
113+
114+
test("Variant #6: Object pattern", async () => {
115+
var tree = parseSync(`
116+
var { nonDefinedIdentifier: definedIdentifier } = {};
117+
118+
( { nonModifiedIdentifier: modifiedIdentifier } = {} );
119+
`);
120+
121+
var definedIdentifier = findIdentifier(tree, "definedIdentifier");
122+
var definedInfo = getIdentifierInfo(
123+
definedIdentifier[0],
124+
definedIdentifier[1]
125+
);
126+
expect(definedInfo.spec.isDefined).toStrictEqual(true);
127+
expect(definedInfo.spec.isReferenced).toStrictEqual(true);
128+
129+
var nonDefinedIdentifier = findIdentifier(tree, "nonDefinedIdentifier");
130+
var nonDefinedInfo = getIdentifierInfo(
131+
nonDefinedIdentifier[0],
132+
nonDefinedIdentifier[1]
133+
);
134+
expect(nonDefinedInfo.spec.isDefined).toStrictEqual(false);
135+
expect(nonDefinedInfo.spec.isReferenced).toStrictEqual(false);
136+
137+
var modifiedIdentifier = findIdentifier(tree, "modifiedIdentifier");
138+
var modifiedInfo = getIdentifierInfo(
139+
modifiedIdentifier[0],
140+
modifiedIdentifier[1]
141+
);
142+
expect(modifiedInfo.spec.isDefined).toStrictEqual(false);
143+
expect(modifiedInfo.spec.isModified).toStrictEqual(true);
144+
expect(modifiedInfo.spec.isReferenced).toStrictEqual(true);
145+
146+
var nonModifiedIdentifier = findIdentifier(tree, "nonModifiedIdentifier");
147+
var nonModifiedInfo = getIdentifierInfo(
148+
nonModifiedIdentifier[0],
149+
nonModifiedIdentifier[1]
150+
);
151+
152+
expect(nonModifiedInfo.spec.isDefined).toStrictEqual(false);
153+
expect(nonModifiedInfo.spec.isModified).toStrictEqual(false);
154+
expect(nonModifiedInfo.spec.isReferenced).toStrictEqual(false);
155+
});
44156
});
45157

46158
describe("validateChain", () => {

test/util/insert.test.ts

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1+
import { ok } from "assert";
12
import { compileJsSync } from "../../src/compiler";
2-
import parseJS from "../../src/parser";
3-
import { isBlock } from "../../src/traverse";
4-
import { Identifier } from "../../src/util/gen";
3+
import parseJS, { parseSync } from "../../src/parser";
4+
import traverse, { isBlock } from "../../src/traverse";
5+
import { Identifier, Location } from "../../src/util/gen";
56
import {
67
deleteDeclaration,
78
isVarContext,
@@ -10,6 +11,7 @@ import {
1011
getContexts,
1112
getLexContext,
1213
getVarContext,
14+
computeFunctionLength,
1315
} from "../../src/util/insert";
1416

1517
it("isBlock() should be true for block statements and program", async () => {
@@ -86,3 +88,55 @@ it("should throw when missing parameters", () => {
8688
expect(() => getLexContext(Identifier("test"), [])).toThrow();
8789
expect(() => getVarContext(Identifier("test"), [])).toThrow();
8890
});
91+
92+
test("computeFunctionLength", () => {
93+
var tree = parseSync(`
94+
function zeroParameters(){}; // 0
95+
function oneParameter(a){}; // 1
96+
function twoParameter(a,b){}; // 2
97+
function restParameter1(...a){}; // 0
98+
function restParameter2(a,b,...c){}; // 2
99+
function defaultValue(a,b,c=1,d){}; // 2
100+
function arrayPattern([a],[b = 2],[[c]]){}; // 3
101+
function objectPattern({a},{b = 2},{c, d}){}; // 3
102+
function mixed(a,{b},[c = 3],d,e=5,f,...g){}; // 4
103+
`);
104+
105+
function getFunction(searchName: string): Location {
106+
var searchLocation: Location;
107+
traverse(tree, (o, p) => {
108+
if (o.type === "FunctionDeclaration" && o.id.name === searchName) {
109+
ok(!searchLocation);
110+
searchLocation = [o, p];
111+
}
112+
});
113+
114+
ok(searchLocation);
115+
return searchLocation;
116+
}
117+
118+
expect(
119+
computeFunctionLength(getFunction("zeroParameters")[0].params)
120+
).toStrictEqual(0);
121+
expect(
122+
computeFunctionLength(getFunction("oneParameter")[0].params)
123+
).toStrictEqual(1);
124+
expect(
125+
computeFunctionLength(getFunction("twoParameter")[0].params)
126+
).toStrictEqual(2);
127+
expect(
128+
computeFunctionLength(getFunction("restParameter1")[0].params)
129+
).toStrictEqual(0);
130+
expect(
131+
computeFunctionLength(getFunction("restParameter2")[0].params)
132+
).toStrictEqual(2);
133+
expect(
134+
computeFunctionLength(getFunction("arrayPattern")[0].params)
135+
).toStrictEqual(3);
136+
expect(
137+
computeFunctionLength(getFunction("objectPattern")[0].params)
138+
).toStrictEqual(3);
139+
expect(computeFunctionLength(getFunction("mixed")[0].params)).toStrictEqual(
140+
4
141+
);
142+
});

0 commit comments

Comments
 (0)