Skip to content
Closed
Show file tree
Hide file tree
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
26 changes: 22 additions & 4 deletions tools/metadata/src/evaluator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ export class Evaluator {
}
}
}

// We can fold a call to CONST_EXPR
if (isCallOf(callExpression, "CONST_EXPR") && callExpression.arguments.length === 1)
return this.isFoldableWorker(callExpression.arguments[0], folding);
Expand Down Expand Up @@ -255,18 +256,35 @@ export class Evaluator {
if (isCallOf(callExpression, "CONST_EXPR") && callExpression.arguments.length === 1) {
return args[0];
}
if (isCallOf(callExpression, 'forwardRef') && callExpression.arguments.length === 1) {
const firstArgument = callExpression.arguments[0];
if (firstArgument.kind == ts.SyntaxKind.ArrowFunction) {
const arrowFunction = <ts.ArrowFunction>firstArgument;
return this.evaluateNode(arrowFunction.body);
}
}
const expression = this.evaluateNode(callExpression.expression);
if (isDefined(expression) && args.every(isDefined)) {
const result: MetadataSymbolicCallExpression = {
__symbolic: "call",
expression: this.evaluateNode(callExpression.expression)
};
const result:
MetadataSymbolicCallExpression = {__symbolic: "call", expression: expression};
if (args && args.length) {
result.arguments = args;
}
return result;
}
break;
case ts.SyntaxKind.NewExpression:
const newExpression = <ts.NewExpression>node;
const newArgs = newExpression.arguments.map(arg => this.evaluateNode(arg));
const newTarget = this.evaluateNode(newExpression.expression);
if (isDefined(newTarget) && newArgs.every(isDefined)) {
const result: MetadataSymbolicCallExpression = {__symbolic: "new", expression: newTarget};
if (newArgs.length) {
result.arguments = newArgs;
}
return result;
}
break;
case ts.SyntaxKind.PropertyAccessExpression: {
const propertyAccessExpression = <ts.PropertyAccessExpression>node;
const expression = this.evaluateNode(propertyAccessExpression.expression);
Expand Down
7 changes: 4 additions & 3 deletions tools/metadata/src/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,14 +60,15 @@ export interface MetadataObject { [name: string]: MetadataValue; }
export interface MetadataArray { [name: number]: MetadataValue; }

export interface MetadataSymbolicExpression {
__symbolic: string; // "binary" | "call" | "index" | "pre" | "reference" | "select"
__symbolic: string; // "binary" | "call" | "index" | "new" | "pre" | "reference" | "select"
}
export function isMetadataSymbolicExpression(value: any): value is MetadataSymbolicExpression {
if (value) {
switch (value.__symbolic) {
case "binary":
case "call":
case "index":
case "new":
case "pre":
case "reference":
case "select":
Expand Down Expand Up @@ -101,13 +102,13 @@ export function isMetadataSymbolicIndexExpression(
}

export interface MetadataSymbolicCallExpression extends MetadataSymbolicExpression {
// __symbolic: "call";
// __symbolic: "call" | "new";
expression: MetadataValue;
arguments?: MetadataValue[];
}
export function isMetadataSymbolicCallExpression(
value: any): value is MetadataSymbolicCallExpression {
return value && value.__symbolic === "call";
return value && (value.__symbolic === "call" || value.__symbolic === "new");
}

export interface MetadataSymbolicPrefixExpression extends MetadataSymbolicExpression {
Expand Down
57 changes: 55 additions & 2 deletions tools/metadata/test/evaluator.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ describe('Evaluator', () => {
let evaluator: Evaluator;

beforeEach(() => {
host = new Host(FILES, ['expressions.ts']);
host = new Host(
FILES,
['expressions.ts', 'const_expr.ts', 'forwardRef.ts', 'classes.ts', 'newExpression.ts']);
service = ts.createLanguageService(host);
program = service.getProgram();
typeChecker = program.getTypeChecker();
Expand All @@ -25,6 +27,7 @@ describe('Evaluator', () => {
expectNoDiagnostics(service.getCompilerOptionsDiagnostics());
for (const sourceFile of program.getSourceFiles()) {
expectNoDiagnostics(service.getSyntacticDiagnostics(sourceFile.fileName));
expectNoDiagnostics(service.getSemanticDiagnostics(sourceFile.fileName));
}
});

Expand Down Expand Up @@ -101,6 +104,34 @@ describe('Evaluator', () => {
expect(evaluator.evaluateNode(findVar(expressions, 'recursiveB').initializer))
.toEqual({__symbolic: "reference", name: "recursiveA", module: "expressions.ts"});
});

it('should correctly handle special cases for CONST_EXPR', () => {
var const_expr = program.getSourceFile('const_expr.ts');
expect(evaluator.evaluateNode(findVar(const_expr, 'bTrue').initializer)).toEqual(true);
expect(evaluator.evaluateNode(findVar(const_expr, 'bFalse').initializer)).toEqual(false);
});

it('should resolve a forwardRef', () => {
var forwardRef = program.getSourceFile('forwardRef.ts');
expect(evaluator.evaluateNode(findVar(forwardRef, 'bTrue').initializer)).toEqual(true);
expect(evaluator.evaluateNode(findVar(forwardRef, 'bFalse').initializer)).toEqual(false);
});

it('should return new expressions', () => {
var newExpression = program.getSourceFile('newExpression.ts');
expect(evaluator.evaluateNode(findVar(newExpression, 'someValue').initializer))
.toEqual({
__symbolic: "new",
expression: {__symbolic: "reference", name: "Value", module: "classes.ts"},
arguments: ["name", 12]
});
expect(evaluator.evaluateNode(findVar(newExpression, 'complex').initializer))
.toEqual({
__symbolic: "new",
expression: {__symbolic: "reference", name: "Value", module: "classes.ts"},
arguments: ["name", 12]
});
});
});

const FILES: Directory = {
Expand All @@ -109,6 +140,11 @@ const FILES: Directory = {
return function(fn: Function) { }
}
`,
'classes.ts': `
export class Value {
constructor(public name: string, public value: any) {}
}
`,
'consts.ts': `
export var someName = 'some-name';
export var someBool = true;
Expand Down Expand Up @@ -159,5 +195,22 @@ const FILES: Directory = {
import {someName, someBool} from './consts';

@Pipe({name: someName, pure: someBool})
export class B {}`
export class B {}`,
'const_expr.ts': `
function CONST_EXPR(value: any) { return value; }
export var bTrue = CONST_EXPR(true);
export var bFalse = CONST_EXPR(false);
`,
'forwardRef.ts': `
function forwardRef(value: any) { return value; }
export var bTrue = forwardRef(() => true);
export var bFalse = forwardRef(() => false);
`,
'newExpression.ts': `
import {Value} from './classes';
function CONST_EXPR(value: any) { return value; }
function forwardRef(value: any) { return value; }
export const someValue = new Value("name", 12);
export const complex = CONST_EXPR(new Value("name", forwardRef(() => 12)));
`
}