Handle synthetic nodes use to throw exceptions

- Implement:
- - _genericNoSuchMethod
- - _unresolvedConstructorError
- - _malformedTypeError

- Temporarily change NoSuchMethod.toString to avoid a closure.
- Temporarily provide argument to StringBuffer to work around bug.

BUG=
R=sigmund@google.com

Review URL: https://codereview.chromium.org/2545153002 .
This commit is contained in:
Stephen Adams
2016-12-02 16:11:26 -08:00
parent ab6bc6dbfd
commit 3e7aaeebe4
13 changed files with 150 additions and 31 deletions
@@ -748,6 +748,10 @@ class JavaScriptBackend extends Backend {
element == coreClasses.stringClass) {
// TODO(johnniwinther): Avoid these.
return true;
} else if (element == helpers.genericNoSuchMethod ||
element == helpers.unresolvedConstructorError ||
element == helpers.malformedTypeError) {
return true;
}
return false;
}
@@ -215,6 +215,9 @@ class BackendHelpers {
return element;
}
Element findCoreHelper(String name) =>
compiler.commonElements.coreLibrary.implementation.localLookup(name);
ConstructorElement _findConstructor(ClassElement cls, String name) {
cls.ensureResolved(resolution);
ConstructorElement constructor = cls.lookupConstructor(name);
@@ -633,6 +636,18 @@ class BackendHelpers {
return findHelper('throwNoSuchMethod');
}
Element get genericNoSuchMethod =>
_genericNoSuchMethod ??= findCoreHelper('_genericNoSuchMethod');
MethodElement _genericNoSuchMethod;
Element get unresolvedConstructorError => _unresolvedConstructorError ??=
findCoreHelper('_unresolvedConstructorError');
MethodElement _unresolvedConstructorError;
Element get malformedTypeError =>
_malformedTypeError ??= findCoreHelper('_malformedTypeError');
MethodElement _malformedTypeError;
Element get createRuntimeType {
return findHelper('createRuntimeType');
}
@@ -160,13 +160,21 @@ class BackendImpacts {
BackendImpact _throwNoSuchMethod;
BackendImpact get throwNoSuchMethod {
return _throwNoSuchMethod ??= new BackendImpact(staticUses: [
helpers.throwNoSuchMethod
], otherImpacts: [
// Also register the types of the arguments passed to this method.
_needsList('Needed to encode the arguments for throw NoSuchMethodError.'),
_needsString('Needed to encode the name for throw NoSuchMethodError.')
]);
return _throwNoSuchMethod ??= new BackendImpact(
staticUses: compiler.options.useKernel
? [
helpers.genericNoSuchMethod,
helpers.unresolvedConstructorError,
]
: [
helpers.throwNoSuchMethod,
],
otherImpacts: [
// Also register the types of the arguments passed to this method.
_needsList(
'Needed to encode the arguments for throw NoSuchMethodError.'),
_needsString('Needed to encode the name for throw NoSuchMethodError.')
]);
}
BackendImpact _stringValues;
@@ -427,8 +435,14 @@ class BackendImpacts {
BackendImpact _malformedTypeCheck;
BackendImpact get malformedTypeCheck {
return _malformedTypeCheck ??=
new BackendImpact(staticUses: [helpers.throwTypeError]);
return _malformedTypeCheck ??= new BackendImpact(
staticUses: compiler.options.useKernel
? [
helpers.malformedTypeError,
]
: [
helpers.throwTypeError,
]);
}
BackendImpact _genericTypeCheck;
@@ -267,4 +267,9 @@ class NoSuchMethodRegistry {
}
}
enum NsmCategory { DEFAULT, THROWING, NOT_APPLICABLE, OTHER, }
enum NsmCategory {
DEFAULT,
THROWING,
NOT_APPLICABLE,
OTHER,
}
+4
View File
@@ -74,6 +74,10 @@ class Kernel {
final Map<ir.Node, Element> nodeToElement = <ir.Node, Element>{};
final Map<ir.Node, Node> nodeToAst = <ir.Node, Node>{};
final Map<ir.Node, Node> nodeToAstOperator = <ir.Node, Node>{};
// Synthetic nodes are nodes we generated that do not correspond to
// [ast.Node]s. A node should be in one of nodeToAst or syntheticNodes but not
// both.
final Set<ir.Node> syntheticNodes = new Set<ir.Node>();
/// FIFO queue of work that needs to be completed before the returned AST
/// nodes are correct.
+20 -8
View File
@@ -57,11 +57,15 @@ abstract class UnresolvedVisitor {
ir.Expression buildThrowNoSuchMethodError(ir.Procedure exceptionBuilder,
ir.Expression receiver, String memberName, ir.Arguments callArguments,
[Element candidateTarget]) {
ir.Expression memberNameArg = new ir.SymbolLiteral(memberName);
ir.Expression positional = new ir.ListLiteral(callArguments.positional);
ir.Expression named = new ir.MapLiteral(callArguments.named.map((e) {
return new ir.MapEntry(new ir.SymbolLiteral(e.name), e.value);
}).toList());
ir.Expression memberNameArg =
markSynthetic(new ir.SymbolLiteral(memberName));
ir.Expression positional =
markSynthetic(new ir.ListLiteral(callArguments.positional));
ir.Expression named =
markSynthetic(new ir.MapLiteral(callArguments.named.map((e) {
return new ir.MapEntry(
markSynthetic(new ir.SymbolLiteral(e.name)), e.value);
}).toList()));
if (candidateTarget is FunctionElement) {
// Ensure [candidateTarget] has been resolved.
possiblyErroneousFunctionToIr(candidateTarget);
@@ -72,13 +76,15 @@ abstract class UnresolvedVisitor {
candidateTarget.hasFunctionSignature) {
List<ir.Expression> existingArgumentsList = <ir.Expression>[];
candidateTarget.functionSignature.forEachParameter((param) {
existingArgumentsList.add(new ir.StringLiteral(param.name));
existingArgumentsList
.add(markSynthetic(new ir.StringLiteral(param.name)));
});
existingArguments = new ir.ListLiteral(existingArgumentsList);
existingArguments =
markSynthetic(new ir.ListLiteral(existingArgumentsList));
} else {
existingArguments = new ir.NullLiteral();
}
return new ir.Throw(new ir.StaticInvocation(
ir.Expression construction = markSynthetic(new ir.StaticInvocation(
exceptionBuilder,
new ir.Arguments(<ir.Expression>[
receiver,
@@ -87,6 +93,12 @@ abstract class UnresolvedVisitor {
named,
existingArguments
])));
return new ir.Throw(construction);
}
ir.Expression markSynthetic(ir.Expression expression) {
kernel.syntheticNodes.add(expression);
return expression;
}
/// Throws a NoSuchMethodError for an unresolved getter named [name].
+3 -3
View File
@@ -796,7 +796,7 @@ class KernelSsaBuilder extends ir.Visitor with GraphBuilder {
HInstruction setListRuntimeTypeInfoIfNeeded(
HInstruction object, ir.ListLiteral listLiteral) {
InterfaceType type = localsHandler
.substInContext(elements.getType(astAdapter.getNode(listLiteral)));
.substInContext(astAdapter.getDartTypeOfListLiteral(listLiteral));
if (!backend.classNeedsRti(type.element) || type.treatAsRaw) {
return object;
}
@@ -827,7 +827,7 @@ class KernelSsaBuilder extends ir.Visitor with GraphBuilder {
setListRuntimeTypeInfoIfNeeded(listInstruction, listLiteral);
}
TypeMask type = astAdapter.typeOfNewList(targetElement, listLiteral);
TypeMask type = astAdapter.typeOfListLiteral(targetElement, listLiteral);
if (!type.containsAll(compiler.closedWorld)) {
listInstruction.instructionType = type;
}
@@ -866,7 +866,7 @@ class KernelSsaBuilder extends ir.Visitor with GraphBuilder {
assert(constructor.kind == ir.ProcedureKind.Factory);
InterfaceType type = localsHandler
.substInContext(elements.getType(astAdapter.getNode(mapLiteral)));
.substInContext(astAdapter.getDartTypeOfMapLiteral(mapLiteral));
ir.Class cls = constructor.enclosingClass;
@@ -72,6 +72,9 @@ class KernelAstAdapter {
_compiler.globalInference.results.resultOf(e);
ConstantValue getConstantForSymbol(ir.SymbolLiteral node) {
if (kernel.syntheticNodes.contains(node)) {
return _backend.constantSystem.createSymbol(_compiler, node.value);
}
ast.Node astNode = getNode(node);
ConstantValue constantValue = _backend.constants
.getConstantValueForNode(astNode, _resolvedAst.elements);
@@ -103,6 +106,16 @@ class KernelAstAdapter {
return result;
}
ast.Node getNodeOrNull(ir.Node node) {
return _nodeToAst[node];
}
void assertNodeIsSynthetic(ir.Node node) {
assert(invariant(
CURRENT_ELEMENT_SPANNABLE, kernel.syntheticNodes.contains(node),
message: "No synthetic marker found for $node"));
}
Local getLocal(ir.VariableDeclaration variable) {
// If this is a synthetic local, return the synthetic local
if (variable.name == null) {
@@ -206,7 +219,12 @@ class KernelAstAdapter {
return _resultOf(_target).typeOfSend(getNode(send));
}
TypeMask typeOfNewList(Element owner, ir.ListLiteral listLiteral) {
TypeMask typeOfListLiteral(Element owner, ir.ListLiteral listLiteral) {
ast.Node node = getNodeOrNull(listLiteral);
if (node == null) {
assertNodeIsSynthetic(listLiteral);
return _compiler.closedWorld.commonMasks.growableListType;
}
return _resultOf(owner).typeOfNewList(getNode(listLiteral)) ??
_compiler.closedWorld.commonMasks.dynamicType;
}
@@ -404,6 +422,21 @@ class KernelAstAdapter {
return types.map(getDartType).toList();
}
DartType getDartTypeOfListLiteral(ir.ListLiteral list) {
ast.Node node = getNodeOrNull(list);
if (node != null) return elements.getType(node);
assertNodeIsSynthetic(list);
return _compiler.coreTypes.listType(getDartType(list.typeArgument));
}
DartType getDartTypeOfMapLiteral(ir.MapLiteral literal) {
ast.Node node = getNodeOrNull(literal);
if (node != null) return elements.getType(node);
assertNodeIsSynthetic(literal);
return _compiler.coreTypes
.mapType(getDartType(literal.keyType), getDartType(literal.valueType));
}
DartType getFunctionReturnType(ir.FunctionNode node) {
return getDartType(node.returnType);
}
@@ -382,7 +382,7 @@ class _JsonDecoderSink extends _StringSinkConversionSink {
final Sink<Object> _sink;
_JsonDecoderSink(this._reviver, this._sink)
: super(new StringBuffer());
: super(new StringBuffer(''));
void close() {
super.close();
@@ -20,7 +20,8 @@ import 'dart:_js_helper' show checkInt,
patch_startup,
Primitives,
stringJoinUnchecked,
getTraceFromException;
getTraceFromException,
RuntimeError;
import 'dart:_foreign_helper' show JS;
@@ -548,7 +549,7 @@ class StringBuffer {
class NoSuchMethodError {
@patch
String toString() {
StringBuffer sb = new StringBuffer();
StringBuffer sb = new StringBuffer('');
String comma = '';
if (_arguments != null) {
for (var argument in _arguments) {
@@ -620,7 +621,7 @@ class _Uri {
// Encode the string into bytes then generate an ASCII only string
// by percent encoding selected bytes.
StringBuffer result = new StringBuffer();
StringBuffer result = new StringBuffer('');
var bytes = encoding.encode(text);
for (int i = 0; i < bytes.length; i++) {
int byte = bytes[i];
@@ -667,3 +668,30 @@ class StackTrace {
}
}
}
// Called from kernel generated code.
_genericNoSuchMethod(receiver, memberName, positionalArguments, namedArguments,
existingArguments) {
return new NoSuchMethodError(
receiver,
memberName,
positionalArguments,
namedArguments);
}
// Called from kernel generated code.
_unresolvedConstructorError(receiver, memberName, positionalArguments,
namedArguments, existingArguments) {
// TODO(sra): Generate an error that reads:
//
// No constructor '$memberName' declared in class '$receiver'.
return new NoSuchMethodError(
receiver,
memberName,
positionalArguments,
namedArguments);
}
// Called from kernel generated code.
_malformedTypeError(message) => new RuntimeError(message);
+1 -1
View File
@@ -188,7 +188,7 @@ String joinArguments(var types, int startIndex,
assert(isJsArray(types));
bool firstArgument = true;
bool allDynamic = true;
StringBuffer buffer = new StringBuffer();
StringBuffer buffer = new StringBuffer('');
for (int index = startIndex; index < getLength(types); index++) {
if (firstArgument) {
firstArgument = false;
@@ -144,7 +144,7 @@ stringReplaceAllUnchecked(receiver, pattern, replacement) {
if (receiver == "") {
return replacement;
} else {
StringBuffer result = new StringBuffer();
StringBuffer result = new StringBuffer('');
int length = receiver.length;
result.write(replacement);
for (int i = 0; i < length; i++) {
@@ -184,7 +184,7 @@ stringReplaceAllFuncUnchecked(receiver, pattern, onMatch, onNonMatch) {
if (pattern is! Pattern) {
throw new ArgumentError.value(pattern, 'pattern', 'is not a Pattern');
}
StringBuffer buffer = new StringBuffer();
StringBuffer buffer = new StringBuffer('');
int startIndex = 0;
for (Match match in pattern.allMatches(receiver)) {
buffer.write(onNonMatch(receiver.substring(startIndex, match.start)));
@@ -197,7 +197,7 @@ stringReplaceAllFuncUnchecked(receiver, pattern, onMatch, onNonMatch) {
stringReplaceAllEmptyFuncUnchecked(receiver, onMatch, onNonMatch) {
// Pattern is the empty string.
StringBuffer buffer = new StringBuffer();
StringBuffer buffer = new StringBuffer('');
int length = receiver.length;
int i = 0;
buffer.write(onNonMatch(""));
@@ -229,7 +229,7 @@ stringReplaceAllStringFuncUnchecked(receiver, pattern, onMatch, onNonMatch) {
return stringReplaceAllEmptyFuncUnchecked(receiver, onMatch, onNonMatch);
}
int length = receiver.length;
StringBuffer buffer = new StringBuffer();
StringBuffer buffer = new StringBuffer('');
int startIndex = 0;
while (startIndex < length) {
int position = stringIndexOfStringUnchecked(receiver, pattern, startIndex);
@@ -101,6 +101,10 @@ const Map<String, String> DEFAULT_CORE_LIBRARY = const <String, String>{
'Symbol': 'class Symbol { final name; const Symbol(this.name); }',
'Type': 'class Type {}',
'Pattern': 'abstract class Pattern {}',
'_genericNoSuchMethod': '_genericNoSuchMethod(a,b,c,d,e) {}',
'_unresolvedConstructorError': '_unresolvedConstructorError(a,b,c,d,e) {}',
'_malformedTypeError': '_malformedTypeError(message) {}',
};
const String DEFAULT_PATCH_CORE_SOURCE = r'''