style(compiler): reformat partial evaluator source tree (#36461)
PR Close #36461
This commit is contained in:
parent
aecf9de738
commit
f9f6e2e1b3
@ -12,7 +12,9 @@ import {DynamicValue} from './dynamic';
|
|||||||
import {KnownFn, ResolvedValue, ResolvedValueArray} from './result';
|
import {KnownFn, ResolvedValue, ResolvedValueArray} from './result';
|
||||||
|
|
||||||
export class ArraySliceBuiltinFn extends KnownFn {
|
export class ArraySliceBuiltinFn extends KnownFn {
|
||||||
constructor(private lhs: ResolvedValueArray) { super(); }
|
constructor(private lhs: ResolvedValueArray) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
evaluate(node: ts.CallExpression, args: ResolvedValueArray): ResolvedValue {
|
evaluate(node: ts.CallExpression, args: ResolvedValueArray): ResolvedValue {
|
||||||
if (args.length === 0) {
|
if (args.length === 0) {
|
||||||
@ -24,7 +26,9 @@ export class ArraySliceBuiltinFn extends KnownFn {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class ArrayConcatBuiltinFn extends KnownFn {
|
export class ArrayConcatBuiltinFn extends KnownFn {
|
||||||
constructor(private lhs: ResolvedValueArray) { super(); }
|
constructor(private lhs: ResolvedValueArray) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
evaluate(node: ts.CallExpression, args: ResolvedValueArray): ResolvedValue {
|
evaluate(node: ts.CallExpression, args: ResolvedValueArray): ResolvedValue {
|
||||||
const result: ResolvedValueArray = [...this.lhs];
|
const result: ResolvedValueArray = [...this.lhs];
|
||||||
|
@ -17,7 +17,7 @@ import {ResolvedValue} from './result';
|
|||||||
|
|
||||||
export type ForeignFunctionResolver =
|
export type ForeignFunctionResolver =
|
||||||
(node: Reference<ts.FunctionDeclaration|ts.MethodDeclaration|ts.FunctionExpression>,
|
(node: Reference<ts.FunctionDeclaration|ts.MethodDeclaration|ts.FunctionExpression>,
|
||||||
args: ReadonlyArray<ts.Expression>) => ts.Expression | null;
|
args: ReadonlyArray<ts.Expression>) => ts.Expression|null;
|
||||||
|
|
||||||
export class PartialEvaluator {
|
export class PartialEvaluator {
|
||||||
constructor(
|
constructor(
|
||||||
@ -31,7 +31,8 @@ export class PartialEvaluator {
|
|||||||
originatingFile: sourceFile,
|
originatingFile: sourceFile,
|
||||||
absoluteModuleName: null,
|
absoluteModuleName: null,
|
||||||
resolutionContext: sourceFile.fileName,
|
resolutionContext: sourceFile.fileName,
|
||||||
scope: new Map<ts.ParameterDeclaration, ResolvedValue>(), foreignFunctionResolver,
|
scope: new Map<ts.ParameterDeclaration, ResolvedValue>(),
|
||||||
|
foreignFunctionResolver,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -261,7 +261,7 @@ export class StaticInterpreter {
|
|||||||
} else if (ts.isVariableDeclaration(node)) {
|
} else if (ts.isVariableDeclaration(node)) {
|
||||||
return this.visitVariableDeclaration(node, context);
|
return this.visitVariableDeclaration(node, context);
|
||||||
} else if (ts.isParameter(node) && context.scope.has(node)) {
|
} else if (ts.isParameter(node) && context.scope.has(node)) {
|
||||||
return context.scope.get(node) !;
|
return context.scope.get(node)!;
|
||||||
} else if (ts.isExportAssignment(node)) {
|
} else if (ts.isExportAssignment(node)) {
|
||||||
return this.visitExpression(node.expression, context);
|
return this.visitExpression(node.expression, context);
|
||||||
} else if (ts.isEnumDeclaration(node)) {
|
} else if (ts.isEnumDeclaration(node)) {
|
||||||
@ -337,7 +337,8 @@ export class StaticInterpreter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const declContext = {
|
const declContext = {
|
||||||
...context, ...joinModuleContext(context, node, decl),
|
...context,
|
||||||
|
...joinModuleContext(context, node, decl),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Visit both concrete and inline declarations.
|
// Visit both concrete and inline declarations.
|
||||||
@ -354,7 +355,7 @@ export class StaticInterpreter {
|
|||||||
const strIndex = `${rhs}`;
|
const strIndex = `${rhs}`;
|
||||||
if (lhs instanceof Map) {
|
if (lhs instanceof Map) {
|
||||||
if (lhs.has(strIndex)) {
|
if (lhs.has(strIndex)) {
|
||||||
return lhs.get(strIndex) !;
|
return lhs.get(strIndex)!;
|
||||||
} else {
|
} else {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
@ -501,7 +502,7 @@ export class StaticInterpreter {
|
|||||||
return DynamicValue.fromUnsupportedSyntax(node);
|
return DynamicValue.fromUnsupportedSyntax(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
const op = UNARY_OPERATORS.get(operatorKind) !;
|
const op = UNARY_OPERATORS.get(operatorKind)!;
|
||||||
const value = this.visitExpression(node.operand, context);
|
const value = this.visitExpression(node.operand, context);
|
||||||
if (value instanceof DynamicValue) {
|
if (value instanceof DynamicValue) {
|
||||||
return DynamicValue.fromDynamicInput(node, value);
|
return DynamicValue.fromDynamicInput(node, value);
|
||||||
@ -516,7 +517,7 @@ export class StaticInterpreter {
|
|||||||
return DynamicValue.fromUnsupportedSyntax(node);
|
return DynamicValue.fromUnsupportedSyntax(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
const opRecord = BINARY_OPERATORS.get(tokenKind) !;
|
const opRecord = BINARY_OPERATORS.get(tokenKind)!;
|
||||||
let lhs: ResolvedValue, rhs: ResolvedValue;
|
let lhs: ResolvedValue, rhs: ResolvedValue;
|
||||||
if (opRecord.literal) {
|
if (opRecord.literal) {
|
||||||
lhs = literal(this.visitExpression(node.left, context), node.left);
|
lhs = literal(this.visitExpression(node.left, context), node.left);
|
||||||
@ -621,7 +622,7 @@ function joinModuleContext(existing: Context, node: ts.Node, decl: Declaration):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function owningModule(context: Context, override: OwningModule | null = null): OwningModule|null {
|
function owningModule(context: Context, override: OwningModule|null = null): OwningModule|null {
|
||||||
let specifier = context.absoluteModuleName;
|
let specifier = context.absoluteModuleName;
|
||||||
if (override !== null) {
|
if (override !== null) {
|
||||||
specifier = override.specifier;
|
specifier = override.specifier;
|
||||||
|
@ -21,8 +21,8 @@ import {DynamicValue} from './dynamic';
|
|||||||
* non-primitive value, or a special `DynamicValue` type which indicates the value was not
|
* non-primitive value, or a special `DynamicValue` type which indicates the value was not
|
||||||
* available statically.
|
* available statically.
|
||||||
*/
|
*/
|
||||||
export type ResolvedValue = number | boolean | string | null | undefined | Reference | EnumValue |
|
export type ResolvedValue = number|boolean|string|null|undefined|Reference|EnumValue|
|
||||||
ResolvedValueArray | ResolvedValueMap | ResolvedModule | KnownFn | DynamicValue<unknown>;
|
ResolvedValueArray|ResolvedValueMap|ResolvedModule|KnownFn|DynamicValue<unknown>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An array of `ResolvedValue`s.
|
* An array of `ResolvedValue`s.
|
||||||
@ -54,12 +54,14 @@ export class ResolvedModule {
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.evaluate(this.exports.get(name) !);
|
return this.evaluate(this.exports.get(name)!);
|
||||||
}
|
}
|
||||||
|
|
||||||
getExports(): ResolvedValueMap {
|
getExports(): ResolvedValueMap {
|
||||||
const map = new Map<string, ResolvedValue>();
|
const map = new Map<string, ResolvedValue>();
|
||||||
this.exports.forEach((decl, name) => { map.set(name, this.evaluate(decl)); });
|
this.exports.forEach((decl, name) => {
|
||||||
|
map.set(name, this.evaluate(decl));
|
||||||
|
});
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,11 +42,13 @@ runInEachFileSystem(() => {
|
|||||||
expect(value).toEqual('test');
|
expect(value).toEqual('test');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('map access works',
|
it('map access works', () => {
|
||||||
() => { expect(evaluate('const obj = {a: "test"};', 'obj.a')).toEqual('test'); });
|
expect(evaluate('const obj = {a: "test"};', 'obj.a')).toEqual('test');
|
||||||
|
});
|
||||||
|
|
||||||
it('resolves undefined property access',
|
it('resolves undefined property access', () => {
|
||||||
() => { expect(evaluate('const obj: any = {}', 'obj.bar')).toEqual(undefined); });
|
expect(evaluate('const obj: any = {}', 'obj.bar')).toEqual(undefined);
|
||||||
|
});
|
||||||
|
|
||||||
it('function calls work', () => {
|
it('function calls work', () => {
|
||||||
expect(evaluate(`function foo(bar) { return bar; }`, 'foo("test")')).toEqual('test');
|
expect(evaluate(`function foo(bar) { return bar; }`, 'foo("test")')).toEqual('test');
|
||||||
@ -68,7 +70,9 @@ runInEachFileSystem(() => {
|
|||||||
expect(evaluate(`const x = false; const y = x ? 'true' : 'false';`, 'y')).toEqual('false');
|
expect(evaluate(`const x = false; const y = x ? 'true' : 'false';`, 'y')).toEqual('false');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('addition works', () => { expect(evaluate(`const x = 1 + 2;`, 'x')).toEqual(3); });
|
it('addition works', () => {
|
||||||
|
expect(evaluate(`const x = 1 + 2;`, 'x')).toEqual(3);
|
||||||
|
});
|
||||||
|
|
||||||
it('static property on class works', () => {
|
it('static property on class works', () => {
|
||||||
expect(evaluate(`class Foo { static bar = 'test'; }`, 'Foo.bar')).toEqual('test');
|
expect(evaluate(`class Foo { static bar = 'test'; }`, 'Foo.bar')).toEqual('test');
|
||||||
@ -148,19 +152,22 @@ runInEachFileSystem(() => {
|
|||||||
expect(evaluate('const a: any = 3, b = 3;', 'a !== b')).toEqual(false);
|
expect(evaluate('const a: any = 3, b = 3;', 'a !== b')).toEqual(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('parentheticals work',
|
it('parentheticals work', () => {
|
||||||
() => { expect(evaluate(`const a = 3, b = 4;`, 'a * (a + b)')).toEqual(21); });
|
expect(evaluate(`const a = 3, b = 4;`, 'a * (a + b)')).toEqual(21);
|
||||||
|
});
|
||||||
|
|
||||||
it('array access works',
|
it('array access works', () => {
|
||||||
() => { expect(evaluate(`const a = [1, 2, 3];`, 'a[1] + a[0]')).toEqual(3); });
|
expect(evaluate(`const a = [1, 2, 3];`, 'a[1] + a[0]')).toEqual(3);
|
||||||
|
});
|
||||||
|
|
||||||
it('array access out of bounds is `undefined`', () => {
|
it('array access out of bounds is `undefined`', () => {
|
||||||
expect(evaluate(`const a = [1, 2, 3];`, 'a[-1]')).toEqual(undefined);
|
expect(evaluate(`const a = [1, 2, 3];`, 'a[-1]')).toEqual(undefined);
|
||||||
expect(evaluate(`const a = [1, 2, 3];`, 'a[3]')).toEqual(undefined);
|
expect(evaluate(`const a = [1, 2, 3];`, 'a[3]')).toEqual(undefined);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('array `length` property access works',
|
it('array `length` property access works', () => {
|
||||||
() => { expect(evaluate(`const a = [1, 2, 3];`, 'a[\'length\'] + 1')).toEqual(4); });
|
expect(evaluate(`const a = [1, 2, 3];`, 'a[\'length\'] + 1')).toEqual(4);
|
||||||
|
});
|
||||||
|
|
||||||
it('array `slice` function works', () => {
|
it('array `slice` function works', () => {
|
||||||
expect(evaluate(`const a = [1, 2, 3];`, 'a[\'slice\']()')).toEqual([1, 2, 3]);
|
expect(evaluate(`const a = [1, 2, 3];`, 'a[\'slice\']()')).toEqual([1, 2, 3]);
|
||||||
@ -185,10 +192,13 @@ runInEachFileSystem(() => {
|
|||||||
expect(evaluate('const a = false;', 'a')).toEqual(false);
|
expect(evaluate('const a = false;', 'a')).toEqual(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('supports undefined',
|
it('supports undefined', () => {
|
||||||
() => { expect(evaluate('const a = undefined;', 'a')).toEqual(undefined); });
|
expect(evaluate('const a = undefined;', 'a')).toEqual(undefined);
|
||||||
|
});
|
||||||
|
|
||||||
it('supports null', () => { expect(evaluate('const a = null;', 'a')).toEqual(null); });
|
it('supports null', () => {
|
||||||
|
expect(evaluate('const a = null;', 'a')).toEqual(null);
|
||||||
|
});
|
||||||
|
|
||||||
it('resolves unknown binary operators as dynamic value', () => {
|
it('resolves unknown binary operators as dynamic value', () => {
|
||||||
const value = evaluate('declare const window: any;', '"location" in window');
|
const value = evaluate('declare const window: any;', '"location" in window');
|
||||||
@ -308,7 +318,7 @@ runInEachFileSystem(() => {
|
|||||||
]);
|
]);
|
||||||
const checker = program.getTypeChecker();
|
const checker = program.getTypeChecker();
|
||||||
const result = getDeclaration(program, _('/entry.ts'), 'target$', ts.isVariableDeclaration);
|
const result = getDeclaration(program, _('/entry.ts'), 'target$', ts.isVariableDeclaration);
|
||||||
const expr = result.initializer !;
|
const expr = result.initializer!;
|
||||||
const evaluator = makeEvaluator(checker);
|
const evaluator = makeEvaluator(checker);
|
||||||
const resolved = evaluator.evaluate(expr);
|
const resolved = evaluator.evaluate(expr);
|
||||||
if (!(resolved instanceof Reference)) {
|
if (!(resolved instanceof Reference)) {
|
||||||
@ -338,7 +348,7 @@ runInEachFileSystem(() => {
|
|||||||
]);
|
]);
|
||||||
const checker = program.getTypeChecker();
|
const checker = program.getTypeChecker();
|
||||||
const result = getDeclaration(program, _('/entry.ts'), 'target$', ts.isVariableDeclaration);
|
const result = getDeclaration(program, _('/entry.ts'), 'target$', ts.isVariableDeclaration);
|
||||||
const expr = result.initializer !;
|
const expr = result.initializer!;
|
||||||
const evaluator = makeEvaluator(checker);
|
const evaluator = makeEvaluator(checker);
|
||||||
const resolved = evaluator.evaluate(expr);
|
const resolved = evaluator.evaluate(expr);
|
||||||
if (!(resolved instanceof Reference)) {
|
if (!(resolved instanceof Reference)) {
|
||||||
@ -348,7 +358,7 @@ runInEachFileSystem(() => {
|
|||||||
expect(ts.isFunctionDeclaration(resolved.node)).toBe(true);
|
expect(ts.isFunctionDeclaration(resolved.node)).toBe(true);
|
||||||
const reference = resolved.getIdentityIn(getSourceFileOrError(program, _('/entry.ts')));
|
const reference = resolved.getIdentityIn(getSourceFileOrError(program, _('/entry.ts')));
|
||||||
expect(reference).not.toBeNull();
|
expect(reference).not.toBeNull();
|
||||||
expect(reference !.getSourceFile()).toEqual(getSourceFileOrError(program, _('/entry.ts')));
|
expect(reference!.getSourceFile()).toEqual(getSourceFileOrError(program, _('/entry.ts')));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('reads values from default exports', () => {
|
it('reads values from default exports', () => {
|
||||||
@ -434,8 +444,9 @@ runInEachFileSystem(() => {
|
|||||||
.toEqual('test');
|
.toEqual('test');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('template expressions work',
|
it('template expressions work', () => {
|
||||||
() => { expect(evaluate('const a = 2, b = 4;', '`1${a}3${b}5`')).toEqual('12345'); });
|
expect(evaluate('const a = 2, b = 4;', '`1${a}3${b}5`')).toEqual('12345');
|
||||||
|
});
|
||||||
|
|
||||||
it('enum resolution works', () => {
|
it('enum resolution works', () => {
|
||||||
const result = evaluate(
|
const result = evaluate(
|
||||||
@ -469,7 +480,7 @@ runInEachFileSystem(() => {
|
|||||||
]);
|
]);
|
||||||
const checker = program.getTypeChecker();
|
const checker = program.getTypeChecker();
|
||||||
const result = getDeclaration(program, _('/entry.ts'), 'target$', ts.isVariableDeclaration);
|
const result = getDeclaration(program, _('/entry.ts'), 'target$', ts.isVariableDeclaration);
|
||||||
const expr = result.initializer !as ts.ObjectLiteralExpression;
|
const expr = result.initializer! as ts.ObjectLiteralExpression;
|
||||||
const prop = expr.properties[0] as ts.ShorthandPropertyAssignment;
|
const prop = expr.properties[0] as ts.ShorthandPropertyAssignment;
|
||||||
const evaluator = makeEvaluator(checker);
|
const evaluator = makeEvaluator(checker);
|
||||||
const resolved = evaluator.evaluate(prop.name);
|
const resolved = evaluator.evaluate(prop.name);
|
||||||
@ -487,13 +498,13 @@ runInEachFileSystem(() => {
|
|||||||
]);
|
]);
|
||||||
const checker = program.getTypeChecker();
|
const checker = program.getTypeChecker();
|
||||||
const result = getDeclaration(program, _('/entry.ts'), 'target$', ts.isVariableDeclaration);
|
const result = getDeclaration(program, _('/entry.ts'), 'target$', ts.isVariableDeclaration);
|
||||||
const expr = result.initializer !as ts.ObjectLiteralExpression;
|
const expr = result.initializer! as ts.ObjectLiteralExpression;
|
||||||
const evaluator = makeEvaluator(checker);
|
const evaluator = makeEvaluator(checker);
|
||||||
const resolved = evaluator.evaluate(expr);
|
const resolved = evaluator.evaluate(expr);
|
||||||
if (!(resolved instanceof Map)) {
|
if (!(resolved instanceof Map)) {
|
||||||
return fail('Should have resolved to a Map');
|
return fail('Should have resolved to a Map');
|
||||||
}
|
}
|
||||||
const value = resolved.get('value') !;
|
const value = resolved.get('value')!;
|
||||||
if (!(value instanceof DynamicValue)) {
|
if (!(value instanceof DynamicValue)) {
|
||||||
return fail(`Should have resolved 'value' to a DynamicValue`);
|
return fail(`Should have resolved 'value' to a DynamicValue`);
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,7 @@ export function makeExpression(code: string, expr: string, supportingFiles: Test
|
|||||||
const decl =
|
const decl =
|
||||||
getDeclaration(program, absoluteFrom('/entry.ts'), 'target$', ts.isVariableDeclaration);
|
getDeclaration(program, absoluteFrom('/entry.ts'), 'target$', ts.isVariableDeclaration);
|
||||||
return {
|
return {
|
||||||
expression: decl.initializer !,
|
expression: decl.initializer!,
|
||||||
host,
|
host,
|
||||||
options,
|
options,
|
||||||
checker,
|
checker,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user