Check that invocations have well-formed targets in kernel verifier.

This also fixes some issues in the frontend and transformers that
generated calls without the correct number of type arguments.

BUG=
R=kmillikin@google.com

Review URL: https://codereview.chromium.org/2533793005 .
This commit is contained in:
Asger Feldthaus 2016-11-30 08:24:03 +01:00
parent ddc6af7556
commit 440813c67b
5 changed files with 240 additions and 5 deletions

View file

@ -1677,6 +1677,15 @@ class ExpressionBuilder
return element;
}
/// Forces the list of type arguments to have the specified length. If the
/// length was changed, all type arguments are changed to `dynamic`.
void _coerceTypeArgumentArity(List<ast.DartType> typeArguments, int arity) {
if (typeArguments.length != arity) {
typeArguments.length = arity;
typeArguments.fillRange(0, arity, const ast.DynamicType());
}
}
ast.Expression visitInstanceCreationExpression(
InstanceCreationExpression node) {
ConstructorElement element = node.staticElement;
@ -1729,6 +1738,8 @@ class ExpressionBuilder
if (classElement.isEnum) {
return scope.emitCompileTimeError(CompileTimeErrorCode.INSTANTIATE_ENUM);
}
_coerceTypeArgumentArity(
arguments.types, classElement.typeParameters.length);
if (element.isFactory) {
ast.Member target = scope.resolveConcreteMethod(element);
if (target is ast.Procedure &&

View file

@ -528,7 +528,8 @@ abstract class AsyncRewriterBase extends ContinuationRewriterBase {
var iteratorVariable = new VariableDeclaration(':for-iterator',
initializer: new ConstructorInvocation(
helper.streamIteratorConstructor,
new Arguments(<Expression>[stmt.iterable])));
new Arguments(<Expression>[stmt.iterable],
types: [const DynamicType()])));
// await iterator.moveNext()
var condition = new AwaitExpression(new MethodInvocation(
@ -733,8 +734,8 @@ class AsyncFunctionRewriter extends AsyncRewriterBase {
// var :completer = new Completer.sync();
completerVariable = new VariableDeclaration(":completer",
initializer: new StaticInvocation(
helper.completerConstructor, new Arguments([])),
initializer: new StaticInvocation(helper.completerConstructor,
new Arguments([], types: [const DynamicType()])),
isFinal: true);
statements.add(completerVariable);
@ -746,7 +747,8 @@ class AsyncFunctionRewriter extends AsyncRewriterBase {
// new Future.microtask(:async_op);
var newMicrotaskStatement = new ExpressionStatement(new StaticInvocation(
helper.futureMicrotaskConstructor,
new Arguments([new VariableGet(nestedClosureVariable)])));
new Arguments([new VariableGet(nestedClosureVariable)],
types: [const DynamicType()])));
statements.add(newMicrotaskStatement);
// return :completer.future;

View file

@ -311,7 +311,8 @@ class SuperCallResolutionTransformer extends Transformer {
return new StaticInvocation(
_listFrom,
new Arguments([new ListLiteral(list)],
named: [new NamedExpression("growable", new BoolLiteral(false))]));
named: [new NamedExpression("growable", new BoolLiteral(false))],
types: [const DynamicType()]));
}
/// Check that a call to the targetFunction is legal given the arguments.

View file

@ -245,6 +245,147 @@ class VerifyingVisitor extends RecursiveVisitor {
visitChildren(node);
}
@override
visitStaticGet(StaticGet node) {
visitChildren(node);
if (node.target == null) {
throw 'StaticGet without target found in $context.';
}
if (!node.target.hasGetter) {
throw 'StaticGet to ${node.target} without getter found in $context';
}
if (node.target.isInstanceMember) {
throw 'StaticGet to ${node.target} that is not static found in $context';
}
}
@override
visitStaticSet(StaticSet node) {
visitChildren(node);
if (node.target == null) {
throw 'StaticSet without target found in $context.';
}
if (!node.target.hasSetter) {
throw 'StaticSet to ${node.target} without setter found in $context';
}
if (node.target.isInstanceMember) {
throw 'StaticSet to ${node.target} that is not static found in $context';
}
}
@override
visitStaticInvocation(StaticInvocation node) {
visitChildren(node);
if (node.target == null) {
throw 'StaticInvocation without target found in $context.';
}
if (node.target.isInstanceMember) {
throw 'StaticInvocation to ${node.target} that is not static found in '
'$context';
}
if (!areArgumentsCompatible(node.arguments, node.target.function)) {
throw 'StaticInvocation with incompatible arguments to '
'${node.target} found in $context';
}
if (node.arguments.types.length !=
node.target.function.typeParameters.length) {
throw 'Wrong number of type arguments provided in StaticInvocation '
'to ${node.target} found in $context';
}
}
@override
visitDirectPropertyGet(DirectPropertyGet node) {
visitChildren(node);
if (node.target == null) {
throw 'DirectPropertyGet without target found in $context.';
}
if (!node.target.hasGetter) {
throw 'DirectPropertyGet to ${node.target} without getter found in '
'$context';
}
if (!node.target.isInstanceMember) {
throw 'DirectPropertyGet to ${node.target} that is static found in '
'$context';
}
}
@override
visitDirectPropertySet(DirectPropertySet node) {
visitChildren(node);
if (node.target == null) {
throw 'DirectPropertySet without target found in $context.';
}
if (!node.target.hasSetter) {
throw 'DirectPropertyGet to ${node.target} without setter found in '
'$context';
}
if (!node.target.isInstanceMember) {
throw 'DirectPropertySet to ${node.target} that is static found in '
'$context';
}
}
@override
visitDirectMethodInvocation(DirectMethodInvocation node) {
visitChildren(node);
if (node.target == null) {
throw 'DirectMethodInvocation without target found in $context.';
}
if (!node.target.isInstanceMember) {
throw 'DirectMethodInvocation to ${node.target} that is static found in '
'$context';
}
if (!areArgumentsCompatible(node.arguments, node.target.function)) {
throw 'DirectMethodInvocation with incompatible arguments to '
'${node.target} found in $context';
}
if (node.arguments.types.length !=
node.target.function.typeParameters.length) {
throw 'Wrong number of type arguments provided in DirectMethodInvocation '
'to ${node.target} found in $context';
}
}
@override
visitConstructorInvocation(ConstructorInvocation node) {
visitChildren(node);
if (node.target == null) {
throw 'ConstructorInvocation without target found in $context.';
}
if (node.target.enclosingClass.isAbstract) {
throw 'ConstructorInvocation to abstract class found in $context';
}
if (!areArgumentsCompatible(node.arguments, node.target.function)) {
throw 'ConstructorInvocation with incompatible arguments to '
'${node.target} found in $context';
}
if (node.arguments.types.length !=
node.target.enclosingClass.typeParameters.length) {
throw 'Wrong number of type arguments provided in ConstructorInvocation '
'to ${node.target} found in $context';
}
}
bool areArgumentsCompatible(Arguments arguments, FunctionNode function) {
if (arguments.positional.length < function.requiredParameterCount) {
return false;
}
if (arguments.positional.length > function.positionalParameters.length) {
return false;
}
namedLoop:
for (int i = 0; i < arguments.named.length; ++i) {
var argument = arguments.named[i];
String name = argument.name;
for (int j = 0; j < function.namedParameters.length; ++j) {
if (function.namedParameters[j].name == name) continue namedLoop;
}
return false;
}
return true;
}
@override
defaultMemberReference(Member node) {
if (node.transformerFlags & TransformerFlag.seenByVerifier == 0) {

View file

@ -125,6 +125,86 @@ main() {
procedure.function = new FunctionNode(new EmptyStatement());
return procedure;
});
negativeTest('StaticGet without target', () {
return new StaticGet(null);
});
negativeTest('StaticSet without target', () {
return new StaticSet(null, new NullLiteral());
});
negativeTest('StaticInvocation without target', () {
return new StaticInvocation(null, new Arguments.empty());
});
positiveTest('Correct StaticInvocation', () {
var method = new Procedure(new Name('test'), ProcedureKind.Method, null,
isStatic: true);
method.function = new FunctionNode(
new ReturnStatement(
new StaticInvocation(method, new Arguments([new NullLiteral()]))),
positionalParameters: [new VariableDeclaration('p')])..parent = method;
return new Class(
name: 'Test',
supertype: objectClass.asRawSupertype,
procedures: [method]);
});
negativeTest('StaticInvocation with too many parameters', () {
var method = new Procedure(new Name('test'), ProcedureKind.Method, null,
isStatic: true);
method.function = new FunctionNode(new ReturnStatement(
new StaticInvocation(method, new Arguments([new NullLiteral()]))))
..parent = method;
return new Class(
name: 'Test',
supertype: objectClass.asRawSupertype,
procedures: [method]);
});
negativeTest('StaticInvocation with too few parameters', () {
var method = new Procedure(new Name('test'), ProcedureKind.Method, null,
isStatic: true);
method.function = new FunctionNode(
new ReturnStatement(
new StaticInvocation(method, new Arguments.empty())),
positionalParameters: [new VariableDeclaration('p')])..parent = method;
return new Class(
name: 'Test',
supertype: objectClass.asRawSupertype,
procedures: [method]);
});
negativeTest('StaticInvocation with unmatched named parameter', () {
var method = new Procedure(new Name('test'), ProcedureKind.Method, null,
isStatic: true);
method.function = new FunctionNode(new ReturnStatement(new StaticInvocation(
method,
new Arguments([],
named: [new NamedExpression('p', new NullLiteral())]))))
..parent = method;
return new Class(
name: 'Test',
supertype: objectClass.asRawSupertype,
procedures: [method]);
});
negativeTest('StaticInvocation with missing type argument', () {
var method = new Procedure(new Name('test'), ProcedureKind.Method, null,
isStatic: true);
method.function = new FunctionNode(
new ReturnStatement(
new StaticInvocation(method, new Arguments.empty())),
typeParameters: [makeTypeParameter()])..parent = method;
return new Class(
name: 'Test',
supertype: objectClass.asRawSupertype,
procedures: [method]);
});
negativeTest('ConstructorInvocation with missing type argument', () {
var constructor = new Constructor(null);
constructor.function = new FunctionNode(new ReturnStatement(
new ConstructorInvocation(constructor, new Arguments.empty())))
..parent = constructor;
return new Class(
name: 'Test',
typeParameters: [makeTypeParameter()],
supertype: objectClass.asRawSupertype,
constructors: [constructor]);
});
}
checkHasError(Program program) {