feat(ivy): let ngtsc evaluate the spread operator in function calls (#29888)
Previously, ngtsc's static evaluator did not take spread operators into account when evaluating function calls, nor did it handle rest arguments correctly. This commit adds support for static evaluation of these language features. PR Close #29888
This commit is contained in:
parent
017bf0b794
commit
cb34514d05
|
@ -142,14 +142,7 @@ export class StaticInterpreter {
|
|||
for (let i = 0; i < node.elements.length; i++) {
|
||||
const element = node.elements[i];
|
||||
if (ts.isSpreadElement(element)) {
|
||||
const spread = this.visitExpression(element.expression, context);
|
||||
if (spread instanceof DynamicValue) {
|
||||
array.push(DynamicValue.fromDynamicInput(element.expression, spread));
|
||||
} else if (!Array.isArray(spread)) {
|
||||
throw new Error(`Unexpected value in spread expression: ${spread}`);
|
||||
} else {
|
||||
array.push(...spread);
|
||||
}
|
||||
array.push(...this.visitSpreadElement(element, context));
|
||||
} else {
|
||||
array.push(this.visitExpression(element, context));
|
||||
}
|
||||
|
@ -383,7 +376,7 @@ export class StaticInterpreter {
|
|||
|
||||
// If the call refers to a builtin function, attempt to evaluate the function.
|
||||
if (lhs instanceof BuiltinFn) {
|
||||
return lhs.evaluate(node.arguments.map(arg => this.visitExpression(arg, context)));
|
||||
return lhs.evaluate(this.evaluateFunctionArguments(node, context));
|
||||
}
|
||||
|
||||
if (!(lhs instanceof Reference)) {
|
||||
|
@ -432,17 +425,17 @@ export class StaticInterpreter {
|
|||
}
|
||||
const ret = body[0] as ts.ReturnStatement;
|
||||
|
||||
const args = this.evaluateFunctionArguments(node, context);
|
||||
const newScope: Scope = new Map<ts.ParameterDeclaration, ResolvedValue>();
|
||||
fn.parameters.forEach((param, index) => {
|
||||
let value: ResolvedValue = undefined;
|
||||
if (index < node.arguments.length) {
|
||||
const arg = node.arguments[index];
|
||||
value = this.visitExpression(arg, context);
|
||||
let arg = args[index];
|
||||
if (param.node.dotDotDotToken !== undefined) {
|
||||
arg = args.slice(index);
|
||||
}
|
||||
if (value === undefined && param.initializer !== null) {
|
||||
value = this.visitExpression(param.initializer, context);
|
||||
if (arg === undefined && param.initializer !== null) {
|
||||
arg = this.visitExpression(param.initializer, context);
|
||||
}
|
||||
newScope.set(param.node, value);
|
||||
newScope.set(param.node, arg);
|
||||
});
|
||||
|
||||
return ret.expression !== undefined ?
|
||||
|
@ -509,6 +502,29 @@ export class StaticInterpreter {
|
|||
return this.visitExpression(node.expression, context);
|
||||
}
|
||||
|
||||
private evaluateFunctionArguments(node: ts.CallExpression, context: Context): ResolvedValueArray {
|
||||
const args: ResolvedValueArray = [];
|
||||
for (const arg of node.arguments) {
|
||||
if (ts.isSpreadElement(arg)) {
|
||||
args.push(...this.visitSpreadElement(arg, context));
|
||||
} else {
|
||||
args.push(this.visitExpression(arg, context));
|
||||
}
|
||||
}
|
||||
return args;
|
||||
}
|
||||
|
||||
private visitSpreadElement(node: ts.SpreadElement, context: Context): ResolvedValueArray {
|
||||
const spread = this.visitExpression(node.expression, context);
|
||||
if (spread instanceof DynamicValue) {
|
||||
return [DynamicValue.fromDynamicInput(node.expression, spread)];
|
||||
} else if (!Array.isArray(spread)) {
|
||||
throw new Error(`Unexpected value in spread expression: ${spread}`);
|
||||
} else {
|
||||
return spread;
|
||||
}
|
||||
}
|
||||
|
||||
private stringNameFromPropertyName(node: ts.PropertyName, context: Context): string|undefined {
|
||||
if (ts.isIdentifier(node) || ts.isStringLiteral(node) || ts.isNumericLiteral(node)) {
|
||||
return node.text;
|
||||
|
|
|
@ -75,6 +75,12 @@ describe('ngtsc metadata', () => {
|
|||
expect(evaluate(`function foo(bar) { return bar; }`, 'foo("test")')).toEqual('test');
|
||||
});
|
||||
|
||||
it('function call spread works', () => {
|
||||
expect(evaluate(`function foo(a, ...b) { return [a, b]; }`, 'foo(1, ...[2, 3])')).toEqual([
|
||||
1, [2, 3]
|
||||
]);
|
||||
});
|
||||
|
||||
it('conditionals work', () => {
|
||||
expect(evaluate(`const x = false; const y = x ? 'true' : 'false';`, 'y')).toEqual('false');
|
||||
});
|
||||
|
@ -136,6 +142,7 @@ describe('ngtsc metadata', () => {
|
|||
expect(evaluate(`const a = [1, 2], b = 3, c = [4, 5];`, 'a[\'concat\'](b, c)')).toEqual([
|
||||
1, 2, 3, 4, 5
|
||||
]);
|
||||
expect(evaluate(`const a = [1, 2], b = [3, 4]`, 'a[\'concat\'](...b)')).toEqual([1, 2, 3, 4]);
|
||||
});
|
||||
|
||||
it('negation works', () => {
|
||||
|
|
Loading…
Reference in New Issue