Skip to content

Commit ad8bc78

Browse files
committed
fix(bug): fixes an issue with evaluating a ClassMember such as a MethodDeclaration, PropertyDeclaration, or a GetAccessorDeclaration directly. Fixes #7
1 parent d9133cb commit ad8bc78

9 files changed

Lines changed: 147 additions & 11 deletions

File tree

src/interpreter/evaluator/evaluate-get-accessor-declaration.ts

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import {IEvaluatorOptions} from "./i-evaluator-options";
2-
import {GetAccessorDeclaration} from "typescript";
2+
import {GetAccessorDeclaration, isClassLike} from "typescript";
33
import {LexicalEnvironment, pathInLexicalEnvironmentEquals, setInLexicalEnvironment} from "../lexical-environment/lexical-environment";
44
import {cloneLexicalEnvironment} from "../lexical-environment/clone-lexical-environment";
55
import {IndexLiteral, IndexLiteralKey, Literal} from "../literal/literal";
@@ -11,13 +11,26 @@ import {inStaticContext} from "../util/static/in-static-context";
1111
/**
1212
* Evaluates, or attempts to evaluate, a GetAccessorDeclaration, before setting it on the given parent
1313
* @param {IEvaluatorOptions<GetAccessorDeclaration>} options
14-
* @param {IndexLiteral} parent
14+
* @param {IndexLiteral} [parent]
1515
*/
16-
export function evaluateGetAccessorDeclaration ({node, environment, evaluate, stack, reporting, statementTraversalStack}: IEvaluatorOptions<GetAccessorDeclaration>, parent: IndexLiteral): void {
16+
export function evaluateGetAccessorDeclaration ({node, environment, evaluate, stack, reporting, statementTraversalStack}: IEvaluatorOptions<GetAccessorDeclaration>, parent?: IndexLiteral): void {
1717

1818
const nameResult = (evaluate.nodeWithValue(node.name, environment, statementTraversalStack)) as IndexLiteralKey;
1919
const isStatic = inStaticContext(node);
2020

21+
if (parent == null) {
22+
let updatedParent: Function & IndexLiteral;
23+
if (isClassLike(node.parent)) {
24+
evaluate.declaration(node.parent, environment, statementTraversalStack);
25+
updatedParent = stack.pop() as Function & IndexLiteral;
26+
}
27+
else {
28+
updatedParent = evaluate.expression(node.parent, environment, statementTraversalStack) as Function & IndexLiteral;
29+
}
30+
stack.push(isStatic ? updatedParent[nameResult] : updatedParent.prototype[nameResult]);
31+
return;
32+
}
33+
2134
/**
2235
* An implementation of the get accessor
2336
*/

src/interpreter/evaluator/evaluate-method-declaration.ts

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import {IEvaluatorOptions} from "./i-evaluator-options";
2-
import {isIdentifier, MethodDeclaration, SyntaxKind} from "typescript";
2+
import {isClassLike, isIdentifier, MethodDeclaration, SyntaxKind} from "typescript";
33
import {getFromLexicalEnvironment, LexicalEnvironment, pathInLexicalEnvironmentEquals, setInLexicalEnvironment} from "../lexical-environment/lexical-environment";
44
import {cloneLexicalEnvironment} from "../lexical-environment/clone-lexical-environment";
55
import {IndexLiteral, IndexLiteralKey, Literal} from "../literal/literal";
@@ -15,13 +15,25 @@ import {hasModifier} from "../util/modifier/has-modifier";
1515
/**
1616
* Evaluates, or attempts to evaluate, a MethodDeclaration, before setting it on the given parent
1717
* @param {IEvaluatorOptions<MethodDeclaration>} options
18-
* @param {IndexLiteral} parent
18+
* @param {IndexLiteral} [parent]
1919
*/
20-
export function evaluateMethodDeclaration ({node, environment, evaluate, stack, statementTraversalStack, reporting, ...rest}: IEvaluatorOptions<MethodDeclaration>, parent: IndexLiteral): void {
21-
20+
export function evaluateMethodDeclaration ({node, environment, evaluate, stack, statementTraversalStack, reporting, ...rest}: IEvaluatorOptions<MethodDeclaration>, parent?: IndexLiteral): void {
2221
const nameResult = (evaluate.nodeWithValue(node.name, environment, statementTraversalStack)) as IndexLiteralKey;
2322
const isStatic = inStaticContext(node);
2423

24+
if (parent == null) {
25+
let updatedParent: Function & IndexLiteral;
26+
if (isClassLike(node.parent)) {
27+
evaluate.declaration(node.parent, environment, statementTraversalStack);
28+
updatedParent = stack.pop() as Function & IndexLiteral;
29+
}
30+
else {
31+
updatedParent = evaluate.expression(node.parent, environment, statementTraversalStack) as Function & IndexLiteral;
32+
}
33+
stack.push(isStatic ? updatedParent[nameResult] : updatedParent.prototype[nameResult]);
34+
return;
35+
}
36+
2537
const _methodDeclaration = hasModifier(node, SyntaxKind.AsyncKeyword)
2638
? async function methodDeclaration (this: Literal, ...args: Literal[]) {
2739

src/interpreter/evaluator/evaluate-node.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import {
2-
isArrayLiteralExpression, isArrowFunction, isAsExpression, isAwaitExpression, isBigIntLiteral, isBinaryExpression, isBlock, isBreakStatement, isCallExpression, isClassDeclaration, isClassExpression, isComputedPropertyName, isConditionalExpression, isConstructorDeclaration, isContinueStatement, isElementAccessExpression, isEnumDeclaration, isExpressionStatement, isForInStatement, isForOfStatement, isForStatement, isFunctionDeclaration, isFunctionExpression, isIdentifier, isIfStatement, isImportDeclaration, isImportEqualsDeclaration, isModuleDeclaration, isNewExpression, isNonNullExpression, isNumericLiteral, isObjectLiteralExpression, isParenthesizedExpression, isPostfixUnaryExpression, isPrefixUnaryExpression, isPropertyAccessExpression, isRegularExpressionLiteral, isReturnStatement, isSourceFile, isSpreadElement, isStringLiteralLike, isSwitchStatement, isTemplateExpression, isThrowStatement, isTryStatement, isTypeAssertion, isTypeOfExpression, isVariableDeclaration, isVariableDeclarationList, isVariableStatement, isVoidExpression, isWhileStatement, Node
2+
isArrayLiteralExpression, isArrowFunction, isAsExpression, isAwaitExpression, isBigIntLiteral, isBinaryExpression, isBlock, isBreakStatement, isCallExpression, isClassDeclaration, isClassExpression, isComputedPropertyName, isConditionalExpression, isConstructorDeclaration, isContinueStatement, isElementAccessExpression, isEnumDeclaration, isExpressionStatement, isForInStatement, isForOfStatement, isForStatement, isFunctionDeclaration, isFunctionExpression, isGetAccessorDeclaration, isIdentifier, isIfStatement, isImportDeclaration, isImportEqualsDeclaration, isMethodDeclaration, isModuleDeclaration, isNewExpression, isNonNullExpression, isNumericLiteral, isObjectLiteralExpression, isParenthesizedExpression, isPostfixUnaryExpression, isPrefixUnaryExpression, isPropertyAccessExpression, isPropertyDeclaration, isRegularExpressionLiteral, isReturnStatement, isSourceFile, isSpreadElement, isStringLiteralLike, isSwitchStatement, isTemplateExpression, isThrowStatement, isTryStatement, isTypeAssertion, isTypeOfExpression, isVariableDeclaration, isVariableDeclarationList, isVariableStatement, isVoidExpression, isWhileStatement, Node
33
} from "typescript";
44
import {IEvaluatorOptions} from "./i-evaluator-options";
55
import {evaluateVariableDeclaration} from "./evaluate-variable-declaration";
@@ -63,6 +63,9 @@ import {evaluateThrowStatement} from "./evaluate-throw-statement";
6363
import {evaluateImportEqualsDeclaration} from "./evaluate-import-equals-declaration";
6464
import {evaluateAwaitExpression} from "./evaluate-await-expression";
6565
import {evaluateConditionalExpression} from "./evaluate-conditional-expression";
66+
import {evaluateMethodDeclaration} from "./evaluate-method-declaration";
67+
import {evaluatePropertyDeclaration} from "./evaluate-property-declaration";
68+
import {evaluateGetAccessorDeclaration} from "./evaluate-get-accessor-declaration";
6669

6770
/**
6871
* Will get a literal value for the given Node. If it doesn't succeed, the value will be 'undefined'
@@ -123,6 +126,18 @@ export function evaluateNode ({node, ...rest}: IEvaluatorOptions<Node>): unknown
123126
return evaluateTemplateExpression({node, ...rest});
124127
}
125128

129+
else if (isMethodDeclaration(node)) {
130+
return evaluateMethodDeclaration({node, ...rest});
131+
}
132+
133+
else if (isPropertyDeclaration(node)) {
134+
return evaluatePropertyDeclaration({node, ...rest});
135+
}
136+
137+
else if (isGetAccessorDeclaration(node)) {
138+
return evaluateGetAccessorDeclaration({node, ...rest});
139+
}
140+
126141
else if (isArrayLiteralExpression(node)) {
127142
return evaluateArrayLiteralExpression({node, ...rest});
128143
}

src/interpreter/evaluator/evaluate-property-declaration.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,26 @@
11
import {IEvaluatorOptions} from "./i-evaluator-options";
22
import {PropertyDeclaration} from "typescript";
33
import {IndexLiteral, IndexLiteralKey} from "../literal/literal";
4+
import {inStaticContext} from "../util/static/in-static-context";
45

56
/**
67
* Evaluates, or attempts to evaluate, a PropertyDeclaration, before applying it on the given parent
78
* @param {IEvaluatorOptions<PropertyDeclaration>} options
8-
* @param {IndexLiteral} parent
9+
* @param {IndexLiteral} [parent]
910
* @returns {Promise<void>}
1011
*/
11-
export function evaluatePropertyDeclaration ({environment, node, evaluate, statementTraversalStack, stack}: IEvaluatorOptions<PropertyDeclaration>, parent: IndexLiteral): void {
12+
export function evaluatePropertyDeclaration ({environment, node, evaluate, statementTraversalStack, stack}: IEvaluatorOptions<PropertyDeclaration>, parent?: IndexLiteral): void {
1213
// Compute the property name
1314
const propertyNameResult = (evaluate.nodeWithValue(node.name, environment, statementTraversalStack)) as IndexLiteralKey;
1415

16+
if (parent == null) {
17+
evaluate.declaration(node.parent, environment, statementTraversalStack);
18+
const updatedParent = stack.pop() as Function&IndexLiteral;
19+
const isStatic = inStaticContext(node);
20+
stack.push(isStatic ? updatedParent[propertyNameResult] : updatedParent.prototype[propertyNameResult]);
21+
return;
22+
}
23+
1524
parent[propertyNameResult] = node.initializer == null
1625
? undefined
1726
: evaluate.expression(node.initializer, environment, statementTraversalStack);
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import * as ts from "typescript";
2+
import {isIdentifier} from "typescript";
23

34
/**
45
* Returns true if the given Node is an Expression.
@@ -7,5 +8,5 @@ import * as ts from "typescript";
78
* @return {node is Expression}
89
*/
910
export function isExpression (node: ts.Node): node is ts.Expression {
10-
return (ts as unknown as {isExpressionNode (node: ts.Node): boolean}).isExpressionNode(node);
11+
return (ts as unknown as {isExpressionNode (node: ts.Node): boolean}).isExpressionNode(node) || isIdentifier(node);
1112
}

test/environment/node.test.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,23 @@ test("Can handle the '__dirname' and '__filename' meta properties in a Node envi
2626
else {
2727
t.deepEqual(result.value, {dirname: "/Users/someone/development/foo", filename: "/Users/someone/development/foo/bar.ts"});
2828
}
29+
});
30+
31+
test("Can handle 'process.cwd()' in a Node environment. #1", t => {
32+
const {evaluate} = prepareTest(
33+
// language=TypeScript
34+
`
35+
(() => {
36+
return process.cwd();
37+
})();
38+
`,
39+
"(() =>"
40+
);
41+
42+
const result = evaluate();
43+
44+
if (!result.success) t.fail(result.reason.stack);
45+
else {
46+
t.deepEqual(result.value, process.cwd());
47+
}
2948
});
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import {test} from "ava";
2+
import {prepareTest} from "../setup";
3+
4+
test("Can evaluate and retrieve a GetAccessorDeclaration. #1", t => {
5+
const {evaluate} = prepareTest(
6+
// language=TypeScript
7+
`
8+
class Foo {
9+
get something () {
10+
return 2;
11+
}
12+
}
13+
`,
14+
"get"
15+
);
16+
17+
const result = evaluate();
18+
19+
if (!result.success) t.fail(result.reason.stack);
20+
else {
21+
t.deepEqual(result.value, 2);
22+
}
23+
});
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import {test} from "ava";
2+
import {prepareTest} from "../setup";
3+
4+
test("Can evaluate and retrieve a MethodDeclaration. #1", t => {
5+
const {evaluate} = prepareTest(
6+
// language=TypeScript
7+
`
8+
class Foo {
9+
add (a: number, b: number): number {
10+
return a + b;
11+
}
12+
}
13+
`,
14+
"add ("
15+
);
16+
17+
const result = evaluate();
18+
19+
if (!result.success) t.fail(result.reason.stack);
20+
else {
21+
t.true(typeof result.value === "function");
22+
}
23+
});
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import {test} from "ava";
2+
import {prepareTest} from "../setup";
3+
4+
test("Can evaluate and retrieve a PropertyDeclaration. #1", t => {
5+
const {evaluate} = prepareTest(
6+
// language=TypeScript
7+
`
8+
class Foo {
9+
private someInstanceProp = 2;
10+
}
11+
`,
12+
"someInstanceProp"
13+
);
14+
15+
const result = evaluate();
16+
17+
if (!result.success) t.fail(result.reason.stack);
18+
else {
19+
t.deepEqual(result.value, 2);
20+
}
21+
});

0 commit comments

Comments
 (0)