blob: 6c55ad373cf27e16c0c7dce0498bd65e0e4dbd5f [file] [log] [blame]
// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
library dart2js.js_backend.patch_resolver;
import '../common.dart';
import '../common/resolution.dart' show Resolution;
import '../common/tasks.dart' show CompilerTask;
import '../compiler.dart' show Compiler;
import '../elements/resolution_types.dart';
import '../elements/elements.dart';
import '../elements/modelx.dart';
import '../tree/tree.dart';
class PatchResolverTask extends CompilerTask {
final Compiler compiler;
PatchResolverTask(Compiler compiler)
: compiler = compiler,
super(compiler.measurer);
DiagnosticReporter get reporter => compiler.reporter;
Resolution get resolution => compiler.resolution;
String get name => 'JavaScript patch resolver';
FunctionElement resolveExternalFunction(FunctionElementX element) {
if (element.isPatched) {
FunctionElementX patch = element.patch;
reporter.withCurrentElement(patch, () {
patch.computeType(resolution);
});
checkMatchingPatchSignatures(element, patch);
element = patch;
} else {
if (element.isConstructor) {
// Note: currently we allow a couple external methods without a patch,
// namely the *.fromEnvironment const constructors in int, bool, and
// String. In the future we might also represent native DOM methods in
// dart:html this way.
ConstructorElementX constructor = element;
if (constructor.isFromEnvironmentConstructor) return element;
}
reporter.reportErrorMessage(
element, MessageKind.PATCH_EXTERNAL_WITHOUT_IMPLEMENTATION);
}
return element;
}
void checkMatchingPatchParameters(FunctionElement origin,
List<Element> originParameters, List<Element> patchParameters) {
bool isUnnamedListConstructor = origin is ConstructorElement &&
resolution.commonElements.isUnnamedListConstructor(origin);
assert(originParameters.length == patchParameters.length);
for (int index = 0; index < originParameters.length; index++) {
ParameterElementX originParameter = originParameters[index];
ParameterElementX patchParameter = patchParameters[index];
// TODO(johnniwinther): Remove the conditional patching when we never
// resolve the same method twice.
if (!originParameter.isPatched) {
originParameter.applyPatch(patchParameter);
} else {
assert(originParameter.patch == patchParameter,
failedAt(origin, "Inconsistent repatch of $originParameter."));
}
ResolutionDartType originParameterType =
originParameter.computeType(resolution);
ResolutionDartType patchParameterType =
patchParameter.computeType(resolution);
if (originParameterType != patchParameterType) {
reporter.reportError(
reporter.createMessage(
originParameter, MessageKind.PATCH_PARAMETER_TYPE_MISMATCH, {
'methodName': origin.name,
'parameterName': originParameter.name,
'originParameterType': originParameterType,
'patchParameterType': patchParameterType
}),
<DiagnosticMessage>[
reporter.createMessage(
patchParameter,
MessageKind.PATCH_POINT_TO_PARAMETER,
{'parameterName': patchParameter.name}),
]);
} else {
// Hack: Use unparser to test parameter equality. This only works
// because we are restricting patch uses and the approach cannot be used
// elsewhere.
// The node contains the type, so there is a potential overlap.
// Therefore we only check the text if the types are identical.
String originParameterText = originParameter.node.toString();
String patchParameterText = patchParameter.node.toString();
if (originParameterText != patchParameterText
// We special case the list constructor because of the
// optional parameter.
&&
!isUnnamedListConstructor) {
reporter.reportError(
reporter.createMessage(
originParameter, MessageKind.PATCH_PARAMETER_MISMATCH, {
'methodName': origin.name,
'originParameter': originParameterText,
'patchParameter': patchParameterText
}),
<DiagnosticMessage>[
reporter.createMessage(
patchParameter,
MessageKind.PATCH_POINT_TO_PARAMETER,
{'parameterName': patchParameter.name}),
]);
}
}
}
}
void checkMatchingPatchSignatures(
FunctionElement origin, FunctionElement patch) {
// TODO(johnniwinther): Show both origin and patch locations on errors.
FunctionExpression originTree = origin.node;
FunctionSignature originSignature = origin.functionSignature;
FunctionExpression patchTree = patch.node;
FunctionSignature patchSignature = patch.functionSignature;
if ('${originTree.typeVariables}' != '${patchTree.typeVariables}') {
reporter.withCurrentElement(patch, () {
Node errorNode = patchTree.typeVariables != null
? patchTree.typeVariables
: patchTree;
reporter.reportError(
reporter.createMessage(
errorNode,
MessageKind.PATCH_TYPE_VARIABLES_MISMATCH,
{'methodName': origin.name}),
[reporter.createMessage(origin, MessageKind.THIS_IS_THE_METHOD)]);
});
}
if (originSignature.type.returnType != patchSignature.type.returnType) {
reporter.withCurrentElement(patch, () {
Node errorNode =
patchTree.returnType != null ? patchTree.returnType : patchTree;
reporter.reportErrorMessage(
errorNode, MessageKind.PATCH_RETURN_TYPE_MISMATCH, {
'methodName': origin.name,
'originReturnType': originSignature.type.returnType,
'patchReturnType': patchSignature.type.returnType
});
});
}
if (originSignature.requiredParameterCount !=
patchSignature.requiredParameterCount) {
reporter.withCurrentElement(patch, () {
reporter.reportErrorMessage(
patchTree, MessageKind.PATCH_REQUIRED_PARAMETER_COUNT_MISMATCH, {
'methodName': origin.name,
'originParameterCount': originSignature.requiredParameterCount,
'patchParameterCount': patchSignature.requiredParameterCount
});
});
} else {
checkMatchingPatchParameters(origin, originSignature.requiredParameters,
patchSignature.requiredParameters);
}
if (originSignature.optionalParameterCount != 0 &&
patchSignature.optionalParameterCount != 0) {
if (originSignature.optionalParametersAreNamed !=
patchSignature.optionalParametersAreNamed) {
reporter.withCurrentElement(patch, () {
reporter.reportErrorMessage(
patchTree,
MessageKind.PATCH_OPTIONAL_PARAMETER_NAMED_MISMATCH,
{'methodName': origin.name});
});
}
}
if (originSignature.optionalParameterCount !=
patchSignature.optionalParameterCount) {
reporter.withCurrentElement(patch, () {
reporter.reportErrorMessage(
patchTree, MessageKind.PATCH_OPTIONAL_PARAMETER_COUNT_MISMATCH, {
'methodName': origin.name,
'originParameterCount': originSignature.optionalParameterCount,
'patchParameterCount': patchSignature.optionalParameterCount
});
});
} else {
checkMatchingPatchParameters(origin, originSignature.optionalParameters,
patchSignature.optionalParameters);
}
}
}