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:
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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].
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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'''
|
||||
|
||||
Reference in New Issue
Block a user