Skip to content

Commit

Permalink
fix(bug): fixes an issue with evaluating a ClassMember such as a Meth…
Browse files Browse the repository at this point in the history
…odDeclaration, PropertyDeclaration, or a GetAccessorDeclaration directly. Fixes #7
  • Loading branch information
wessberg committed Dec 30, 2018
1 parent d9133cb commit ad8bc78
Show file tree
Hide file tree
Showing 9 changed files with 147 additions and 11 deletions.
19 changes: 16 additions & 3 deletions src/interpreter/evaluator/evaluate-get-accessor-declaration.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {IEvaluatorOptions} from "./i-evaluator-options";
import {GetAccessorDeclaration} from "typescript";
import {GetAccessorDeclaration, isClassLike} from "typescript";
import {LexicalEnvironment, pathInLexicalEnvironmentEquals, setInLexicalEnvironment} from "../lexical-environment/lexical-environment";
import {cloneLexicalEnvironment} from "../lexical-environment/clone-lexical-environment";
import {IndexLiteral, IndexLiteralKey, Literal} from "../literal/literal";
Expand All @@ -11,13 +11,26 @@ import {inStaticContext} from "../util/static/in-static-context";
/**
* Evaluates, or attempts to evaluate, a GetAccessorDeclaration, before setting it on the given parent
* @param {IEvaluatorOptions<GetAccessorDeclaration>} options
* @param {IndexLiteral} parent
* @param {IndexLiteral} [parent]
*/
export function evaluateGetAccessorDeclaration ({node, environment, evaluate, stack, reporting, statementTraversalStack}: IEvaluatorOptions<GetAccessorDeclaration>, parent: IndexLiteral): void {
export function evaluateGetAccessorDeclaration ({node, environment, evaluate, stack, reporting, statementTraversalStack}: IEvaluatorOptions<GetAccessorDeclaration>, parent?: IndexLiteral): void {

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

if (parent == null) {
let updatedParent: Function & IndexLiteral;
if (isClassLike(node.parent)) {
evaluate.declaration(node.parent, environment, statementTraversalStack);
updatedParent = stack.pop() as Function & IndexLiteral;
}
else {
updatedParent = evaluate.expression(node.parent, environment, statementTraversalStack) as Function & IndexLiteral;
}
stack.push(isStatic ? updatedParent[nameResult] : updatedParent.prototype[nameResult]);
return;
}

/**
* An implementation of the get accessor
*/
Expand Down
20 changes: 16 additions & 4 deletions src/interpreter/evaluator/evaluate-method-declaration.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {IEvaluatorOptions} from "./i-evaluator-options";
import {isIdentifier, MethodDeclaration, SyntaxKind} from "typescript";
import {isClassLike, isIdentifier, MethodDeclaration, SyntaxKind} from "typescript";
import {getFromLexicalEnvironment, LexicalEnvironment, pathInLexicalEnvironmentEquals, setInLexicalEnvironment} from "../lexical-environment/lexical-environment";
import {cloneLexicalEnvironment} from "../lexical-environment/clone-lexical-environment";
import {IndexLiteral, IndexLiteralKey, Literal} from "../literal/literal";
Expand All @@ -15,13 +15,25 @@ import {hasModifier} from "../util/modifier/has-modifier";
/**
* Evaluates, or attempts to evaluate, a MethodDeclaration, before setting it on the given parent
* @param {IEvaluatorOptions<MethodDeclaration>} options
* @param {IndexLiteral} parent
* @param {IndexLiteral} [parent]
*/
export function evaluateMethodDeclaration ({node, environment, evaluate, stack, statementTraversalStack, reporting, ...rest}: IEvaluatorOptions<MethodDeclaration>, parent: IndexLiteral): void {

export function evaluateMethodDeclaration ({node, environment, evaluate, stack, statementTraversalStack, reporting, ...rest}: IEvaluatorOptions<MethodDeclaration>, parent?: IndexLiteral): void {
const nameResult = (evaluate.nodeWithValue(node.name, environment, statementTraversalStack)) as IndexLiteralKey;
const isStatic = inStaticContext(node);

if (parent == null) {
let updatedParent: Function & IndexLiteral;
if (isClassLike(node.parent)) {
evaluate.declaration(node.parent, environment, statementTraversalStack);
updatedParent = stack.pop() as Function & IndexLiteral;
}
else {
updatedParent = evaluate.expression(node.parent, environment, statementTraversalStack) as Function & IndexLiteral;
}
stack.push(isStatic ? updatedParent[nameResult] : updatedParent.prototype[nameResult]);
return;
}

const _methodDeclaration = hasModifier(node, SyntaxKind.AsyncKeyword)
? async function methodDeclaration (this: Literal, ...args: Literal[]) {

Expand Down
17 changes: 16 additions & 1 deletion src/interpreter/evaluator/evaluate-node.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {
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
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
} from "typescript";
import {IEvaluatorOptions} from "./i-evaluator-options";
import {evaluateVariableDeclaration} from "./evaluate-variable-declaration";
Expand Down Expand Up @@ -63,6 +63,9 @@ import {evaluateThrowStatement} from "./evaluate-throw-statement";
import {evaluateImportEqualsDeclaration} from "./evaluate-import-equals-declaration";
import {evaluateAwaitExpression} from "./evaluate-await-expression";
import {evaluateConditionalExpression} from "./evaluate-conditional-expression";
import {evaluateMethodDeclaration} from "./evaluate-method-declaration";
import {evaluatePropertyDeclaration} from "./evaluate-property-declaration";
import {evaluateGetAccessorDeclaration} from "./evaluate-get-accessor-declaration";

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

else if (isMethodDeclaration(node)) {
return evaluateMethodDeclaration({node, ...rest});
}

else if (isPropertyDeclaration(node)) {
return evaluatePropertyDeclaration({node, ...rest});
}

else if (isGetAccessorDeclaration(node)) {
return evaluateGetAccessorDeclaration({node, ...rest});
}

else if (isArrayLiteralExpression(node)) {
return evaluateArrayLiteralExpression({node, ...rest});
}
Expand Down
13 changes: 11 additions & 2 deletions src/interpreter/evaluator/evaluate-property-declaration.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,26 @@
import {IEvaluatorOptions} from "./i-evaluator-options";
import {PropertyDeclaration} from "typescript";
import {IndexLiteral, IndexLiteralKey} from "../literal/literal";
import {inStaticContext} from "../util/static/in-static-context";

/**
* Evaluates, or attempts to evaluate, a PropertyDeclaration, before applying it on the given parent
* @param {IEvaluatorOptions<PropertyDeclaration>} options
* @param {IndexLiteral} parent
* @param {IndexLiteral} [parent]
* @returns {Promise<void>}
*/
export function evaluatePropertyDeclaration ({environment, node, evaluate, statementTraversalStack, stack}: IEvaluatorOptions<PropertyDeclaration>, parent: IndexLiteral): void {
export function evaluatePropertyDeclaration ({environment, node, evaluate, statementTraversalStack, stack}: IEvaluatorOptions<PropertyDeclaration>, parent?: IndexLiteral): void {
// Compute the property name
const propertyNameResult = (evaluate.nodeWithValue(node.name, environment, statementTraversalStack)) as IndexLiteralKey;

if (parent == null) {
evaluate.declaration(node.parent, environment, statementTraversalStack);
const updatedParent = stack.pop() as Function&IndexLiteral;
const isStatic = inStaticContext(node);
stack.push(isStatic ? updatedParent[propertyNameResult] : updatedParent.prototype[propertyNameResult]);
return;
}

parent[propertyNameResult] = node.initializer == null
? undefined
: evaluate.expression(node.initializer, environment, statementTraversalStack);
Expand Down
3 changes: 2 additions & 1 deletion src/interpreter/util/expression/is-expression.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as ts from "typescript";
import {isIdentifier} from "typescript";

/**
* Returns true if the given Node is an Expression.
Expand All @@ -7,5 +8,5 @@ import * as ts from "typescript";
* @return {node is Expression}
*/
export function isExpression (node: ts.Node): node is ts.Expression {
return (ts as unknown as {isExpressionNode (node: ts.Node): boolean}).isExpressionNode(node);
return (ts as unknown as {isExpressionNode (node: ts.Node): boolean}).isExpressionNode(node) || isIdentifier(node);
}
19 changes: 19 additions & 0 deletions test/environment/node.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,23 @@ test("Can handle the '__dirname' and '__filename' meta properties in a Node envi
else {
t.deepEqual(result.value, {dirname: "/Users/someone/development/foo", filename: "/Users/someone/development/foo/bar.ts"});
}
});

test("Can handle 'process.cwd()' in a Node environment. #1", t => {
const {evaluate} = prepareTest(
// language=TypeScript
`
(() => {
return process.cwd();
})();
`,
"(() =>"
);

const result = evaluate();

if (!result.success) t.fail(result.reason.stack);
else {
t.deepEqual(result.value, process.cwd());
}
});
23 changes: 23 additions & 0 deletions test/get-accessor-declaration/get-accessor-declaration.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import {test} from "ava";
import {prepareTest} from "../setup";

test("Can evaluate and retrieve a GetAccessorDeclaration. #1", t => {
const {evaluate} = prepareTest(
// language=TypeScript
`
class Foo {
get something () {
return 2;
}
}
`,
"get"
);

const result = evaluate();

if (!result.success) t.fail(result.reason.stack);
else {
t.deepEqual(result.value, 2);
}
});
23 changes: 23 additions & 0 deletions test/method-declaration/method-declaration.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import {test} from "ava";
import {prepareTest} from "../setup";

test("Can evaluate and retrieve a MethodDeclaration. #1", t => {
const {evaluate} = prepareTest(
// language=TypeScript
`
class Foo {
add (a: number, b: number): number {
return a + b;
}
}
`,
"add ("
);

const result = evaluate();

if (!result.success) t.fail(result.reason.stack);
else {
t.true(typeof result.value === "function");
}
});
21 changes: 21 additions & 0 deletions test/property-declaration/property-declaration.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import {test} from "ava";
import {prepareTest} from "../setup";

test("Can evaluate and retrieve a PropertyDeclaration. #1", t => {
const {evaluate} = prepareTest(
// language=TypeScript
`
class Foo {
private someInstanceProp = 2;
}
`,
"someInstanceProp"
);

const result = evaluate();

if (!result.success) t.fail(result.reason.stack);
else {
t.deepEqual(result.value, 2);
}
});

0 comments on commit ad8bc78

Please sign in to comment.