From 0ce490561c33a17b41db1405373f4a2711cd97da Mon Sep 17 00:00:00 2001 From: Vijay Menon Date: Wed, 20 May 2015 09:24:16 -0700 Subject: [PATCH] Type check binary expressions Fixes #188 R=leafp@google.com Review URL: https://codereview.chromium.org/1137543005 --- .../lib/runtime/dart/_interceptors.js | 4 +- .../lib/runtime/dart/_js_helper.js | 26 +++++----- pkg/dev_compiler/lib/runtime/dart/core.js | 4 +- pkg/dev_compiler/lib/src/checker/checker.dart | 49 ++++++++++++++++-- pkg/dev_compiler/lib/src/dart_sdk.dart | 6 ++- .../test/checker/checker_test.dart | 50 ++++++++++++------- .../test/checker/inferred_type_test.dart | 6 +-- .../test/codegen/expect/fieldtest.js | 2 +- pkg/dev_compiler/test/codegen/fieldtest.dart | 2 +- .../dart_codegen/expect/core/exceptions.dart | 4 +- 10 files changed, 107 insertions(+), 46 deletions(-) diff --git a/pkg/dev_compiler/lib/runtime/dart/_interceptors.js b/pkg/dev_compiler/lib/runtime/dart/_interceptors.js index eae9f75af124..2d3a82c0125a 100644 --- a/pkg/dev_compiler/lib/runtime/dart/_interceptors.js +++ b/pkg/dev_compiler/lib/runtime/dart/_interceptors.js @@ -446,8 +446,8 @@ var _js_embedded_names = dart.import(_js_embedded_names); } static _bitCount(i) { i = dart.as(dart.dsend(JSInt._shru(i, 0), '-', dart.dsend(JSInt._shru(i, 1), '&', 1431655765)), core.int); - i = (dart.notNull(i) & 858993459)['+'](dart.dsend(JSInt._shru(i, 2), '&', 858993459)); - i = 252645135 & i['+'](JSInt._shru(i, 4)); + i = (dart.notNull(i) & 858993459)['+'](dart.as(dart.dsend(JSInt._shru(i, 2), '&', 858993459), core.num)); + i = 252645135 & dart.notNull(dart.notNull(i) + dart.notNull(dart.as(JSInt._shru(i, 4), core.num))); i = dart.notNull(i) + dart.notNull(dart.as(JSInt._shru(i, 8), core.int)); i = dart.notNull(i) + dart.notNull(dart.as(JSInt._shru(i, 16), core.int)); return dart.notNull(i) & 63; diff --git a/pkg/dev_compiler/lib/runtime/dart/_js_helper.js b/pkg/dev_compiler/lib/runtime/dart/_js_helper.js index 37366a073f5d..5f563a6433e5 100644 --- a/pkg/dev_compiler/lib/runtime/dart/_js_helper.js +++ b/pkg/dev_compiler/lib/runtime/dart/_js_helper.js @@ -123,7 +123,7 @@ var _isolate_helper = dart.lazyImport(_isolate_helper); forEach(f) { dart.as(f, dart.functionType(dart.void, [K, V])); let keys = this[_keys]; - for (let i = 0; i['<'](dart.dload(keys, 'length')); i = dart.notNull(i) + 1) { + for (let i = 0; dart.notNull(i) < dart.notNull(dart.as(dart.dload(keys, 'length'), core.num)); i = dart.notNull(i) + 1) { let key = dart.dindex(keys, i); f(dart.as(key, K), dart.as(this[_fetch](key), V)); } @@ -432,7 +432,7 @@ var _isolate_helper = dart.lazyImport(_isolate_helper); let context = window; let fun = function() { }; - for (let i = 0; i['<'](dart.dload(tags, 'length')); i = dart.notNull(i) + 1) { + for (let i = 0; dart.notNull(i) < dart.notNull(dart.as(dart.dload(tags, 'length'), core.num)); i = dart.notNull(i) + 1) { let tag = dart.dindex(tags, i); let proto = dart.dcall(exports.prototypeForTagFunction, tag); if (proto != null) { @@ -445,7 +445,7 @@ var _isolate_helper = dart.lazyImport(_isolate_helper); } } } - for (let i = 0; i['<'](dart.dload(tags, 'length')); i = dart.notNull(i) + 1) { + for (let i = 0; dart.notNull(i) < dart.notNull(dart.as(dart.dload(tags, 'length'), core.num)); i = dart.notNull(i) + 1) { let tag = tags[i]; if (/^[A-Za-z_]/.test(tag)) { let interceptorClass = propertyGet(map, tag); @@ -1318,7 +1318,7 @@ var _isolate_helper = dart.lazyImport(_isolate_helper); if (hasNoField(s, `${_foreign_helper.JS_FUNCTION_TYPE_TAG()}`)) return false; if (hasField(s, `${_foreign_helper.JS_FUNCTION_TYPE_VOID_RETURN_TAG()}`)) { - if (dart.dsend(hasNoField(t, `${_foreign_helper.JS_FUNCTION_TYPE_VOID_RETURN_TAG()}`), '&&', hasField(t, `${_foreign_helper.JS_FUNCTION_TYPE_RETURN_TYPE_TAG()}`))) { + if (dart.notNull(dart.as(hasNoField(t, `${_foreign_helper.JS_FUNCTION_TYPE_VOID_RETURN_TAG()}`), core.bool)) && dart.notNull(dart.as(hasField(t, `${_foreign_helper.JS_FUNCTION_TYPE_RETURN_TYPE_TAG()}`), core.bool))) { return false; } } else if (hasNoField(t, `${_foreign_helper.JS_FUNCTION_TYPE_VOID_RETURN_TAG()}`)) { @@ -2010,8 +2010,8 @@ var _isolate_helper = dart.lazyImport(_isolate_helper); if (dart.dsend(i, '<=', 65535)) { a[core.$add](dart.as(i, core.int)); } else if (dart.dsend(i, '<=', 1114111)) { - a[core.$add]((55296)['+'](dart.dsend(dart.dsend(dart.dsend(i, '-', 65536), '>>', 10), '&', 1023))); - a[core.$add]((56320)['+'](dart.dsend(i, '&', 1023))); + a[core.$add]((55296)['+'](dart.as(dart.dsend(dart.dsend(dart.dsend(i, '-', 65536), '>>', 10), '&', 1023), core.num))); + a[core.$add]((56320)['+'](dart.as(dart.dsend(i, '&', 1023), core.num))); } else { throw new core.ArgumentError(i); } @@ -2030,14 +2030,14 @@ var _isolate_helper = dart.lazyImport(_isolate_helper); return Primitives._fromCharCodeApply(dart.as(charCodes, core.List$(core.int))); } static stringFromCharCode(charCode) { - if ((0)['<='](charCode)) { + if (0 <= dart.notNull(dart.as(charCode, core.num))) { if (dart.dsend(charCode, '<=', 65535)) { return String.fromCharCode(charCode); } if (dart.dsend(charCode, '<=', 1114111)) { let bits = dart.dsend(charCode, '-', 65536); - let low = (56320)['|'](dart.dsend(bits, '&', 1023)); - let high = (55296)['|'](dart.dsend(bits, '>>', 10)); + let low = (56320)['|'](dart.as(dart.dsend(bits, '&', 1023), core.int)); + let high = (55296)['|'](dart.as(dart.dsend(bits, '>>', 10), core.int)); return String.fromCharCode(high, low); } } @@ -2082,10 +2082,10 @@ var _isolate_helper = dart.lazyImport(_isolate_helper); } else { value = new Date(years, jsMonth, day, hours, minutes, seconds, milliseconds).valueOf(); } - if (dart.dsend(dart.dload(value, 'isNaN'), '||', dart.dsend(value, '<', -dart.notNull(MAX_MILLISECONDS_SINCE_EPOCH)))['||'](dart.dsend(value, '>', MAX_MILLISECONDS_SINCE_EPOCH))) { + if (dart.notNull(dart.as(dart.dload(value, 'isNaN'), core.bool)) || dart.notNull(dart.as(dart.dsend(value, '<', -dart.notNull(MAX_MILLISECONDS_SINCE_EPOCH)), core.bool)) || dart.notNull(dart.as(dart.dsend(value, '>', MAX_MILLISECONDS_SINCE_EPOCH), core.bool))) { return null; } - if (dart.dsend(dart.dsend(years, '<=', 0), '||', dart.dsend(years, '<', 100))) + if (dart.notNull(dart.as(dart.dsend(years, '<=', 0), core.bool)) || dart.notNull(dart.as(dart.dsend(years, '<', 100), core.bool))) return Primitives.patchUpY2K(value, years, isUtc); return value; } @@ -3642,7 +3642,7 @@ var _isolate_helper = dart.lazyImport(_isolate_helper); static listToRti(list) { list = list; let result = []; - for (let i = 0; i['<'](dart.dload(list, 'length')); i = dart.notNull(i) + 1) { + for (let i = 0; dart.notNull(i) < dart.notNull(dart.as(dart.dload(list, 'length'), core.num)); i = dart.notNull(i) + 1) { result.push(dart.dsend(dart.dindex(list, i), 'toRti')); } return result; @@ -3721,7 +3721,7 @@ var _isolate_helper = dart.lazyImport(_isolate_helper); dart.fn(buildNamedFunctionType, RuntimeFunctionType, [dart.dynamic, dart.dynamic, dart.dynamic]); function buildInterfaceType(rti, typeArguments) { let name = dart.as(rti.name, core.String); - if ((typeArguments == null)['||'](dart.dload(typeArguments, 'isEmpty'))) { + if (dart.notNull(typeArguments == null) || dart.notNull(dart.as(dart.dload(typeArguments, 'isEmpty'), core.bool))) { return new RuntimeTypePlain(name); } return new RuntimeTypeGeneric(name, dart.as(typeArguments, core.List$(RuntimeType)), null); diff --git a/pkg/dev_compiler/lib/runtime/dart/core.js b/pkg/dev_compiler/lib/runtime/dart/core.js index c2aae7f86066..0b3ca2418ff9 100644 --- a/pkg/dev_compiler/lib/runtime/dart/core.js +++ b/pkg/dev_compiler/lib/runtime/dart/core.js @@ -1073,7 +1073,7 @@ var convert = dart.lazyImport(convert); } return report; } - if (offset != -1 && (dart.notNull(offset) < 0 || offset['>'](dart.dload(this.source, 'length')))) { + if (offset != -1 && (dart.notNull(offset) < 0 || dart.notNull(offset) > dart.notNull(dart.as(dart.dload(this.source, 'length'), num)))) { offset = -1; } if (offset == -1) { @@ -1106,7 +1106,7 @@ var convert = dart.lazyImport(convert); report = dart.notNull(report) + ` (at character ${dart.notNull(offset) + 1})\n`; } let lineEnd = dart.as(dart.dload(this.source, 'length'), int); - for (let i = offset; i['<'](dart.dload(this.source, 'length')); i = dart.notNull(i) + 1) { + for (let i = offset; dart.notNull(i) < dart.notNull(dart.as(dart.dload(this.source, 'length'), num)); i = dart.notNull(i) + 1) { let char = dart.as(dart.dsend(this.source, 'codeUnitAt', i), int); if (char == 10 || char == 13) { lineEnd = i; diff --git a/pkg/dev_compiler/lib/src/checker/checker.dart b/pkg/dev_compiler/lib/src/checker/checker.dart index f96fa7d1c5c3..b114ba08c440 100644 --- a/pkg/dev_compiler/lib/src/checker/checker.dart +++ b/pkg/dev_compiler/lib/src/checker/checker.dart @@ -705,14 +705,57 @@ class CodeChecker extends RecursiveAstVisitor { node.visitChildren(this); } + visitBinaryExpression(BinaryExpression node) { + var op = node.operator; + if (op.isUserDefinableOperator) { + if (_rules.isDynamicTarget(node.leftOperand)) { + // Dynamic invocation + // TODO(vsm): Move this logic to the resolver? + if (op.type != TokenType.EQ_EQ && op.type != TokenType.BANG_EQ) { + _recordDynamicInvoke(node); + } + } else { + var element = node.staticElement; + // Method invocation. + if (element is MethodElement) { + var type = element.type as FunctionType; + assert(type.normalParameterTypes.length == 1); + node.rightOperand = + checkArgument(node.rightOperand, type.normalParameterTypes[0]); + } else { + // TODO(vsm): Assert that the analyzer found an error here? + } + } + } else { + // Non-method operator. + switch (op.type) { + case TokenType.AMPERSAND_AMPERSAND: + case TokenType.BAR_BAR: + var boolType = _rules.provider.boolType; + node.leftOperand = checkArgument(node.leftOperand, boolType); + node.rightOperand = checkArgument(node.rightOperand, boolType); + break; + case TokenType.BANG_EQ: + break; + default: + assert(false); + } + } + node.visitChildren(this); + } + DartType getType(TypeName name) { return (name == null) ? _rules.provider.dynamicType : name.type; } Expression checkAssignment(Expression expr, DartType type) { - final staticInfo = _rules.checkAssignment(expr, type, _constantContext); - _recordMessage(staticInfo); - if (staticInfo is Conversion) expr = staticInfo; + if (expr is ParenthesizedExpression) { + expr.expression = checkAssignment(expr.expression, type); + } else { + final staticInfo = _rules.checkAssignment(expr, type, _constantContext); + _recordMessage(staticInfo); + if (staticInfo is Conversion) expr = staticInfo; + } return expr; } diff --git a/pkg/dev_compiler/lib/src/dart_sdk.dart b/pkg/dev_compiler/lib/src/dart_sdk.dart index f6c9a3527e53..6818f952ae46 100644 --- a/pkg/dev_compiler/lib/src/dart_sdk.dart +++ b/pkg/dev_compiler/lib/src/dart_sdk.dart @@ -113,18 +113,22 @@ final Map mockSdkSources = { int get hashCode {} Type get runtimeType {} String toString(){} + bool ==(other){} } class Function {} class StackTrace {} class Symbol {} class Type {} - class String {} + class String { + String operator +(String other) {} + } class bool {} class num { num operator +(num other) {} } class int extends num { + bool operator<(num other) {} int operator-() {} } class double extends num {} diff --git a/pkg/dev_compiler/test/checker/checker_test.dart b/pkg/dev_compiler/test/checker/checker_test.dart index 11e724f52998..a274abe83843 100644 --- a/pkg/dev_compiler/test/checker/checker_test.dart +++ b/pkg/dev_compiler/test/checker/checker_test.dart @@ -108,8 +108,8 @@ void main() { class A { String x = "hello world"; - void baz1(y) => x + y; - static baz2(y) => y + y; + void baz1(y) => x + /*info:DynamicCast*/y; + static baz2(y) => /*info:DynamicInvoke*/y + y; } void foo(String str) { @@ -979,8 +979,8 @@ void main() { f = /*severe:StaticTypeError*/new B(); f = i2i; f = /*warning:DownCastComposite*/n2n; - f = /*warning:DownCastComposite*/(i2i as Object); - f = /*warning:DownCastComposite*/(n2n as Function); + f = /*warning:DownCastComposite*/i2i as Object; + f = /*warning:DownCastComposite*/n2n as Function; } { N2N f; @@ -988,8 +988,8 @@ void main() { f = new B(); f = /*warning:DownCastComposite*/i2i; f = n2n; - f = /*warning:DownCastComposite*/(i2i as Object); - f = /*warning:DownCastComposite*/(n2n as Function); + f = /*warning:DownCastComposite*/i2i as Object; + f = /*warning:DownCastComposite*/n2n as Function; } { A f; @@ -997,8 +997,8 @@ void main() { f = /*severe:StaticTypeError*/new B(); f = /*severe:StaticTypeError*/i2i; f = /*severe:StaticTypeError*/n2n; - f = /*warning:DownCastImplicit*/(i2i as Object); - f = /*warning:DownCastImplicit*/(n2n as Function); + f = /*warning:DownCastImplicit*/i2i as Object; + f = /*warning:DownCastImplicit*/n2n as Function; } { B f; @@ -1006,8 +1006,8 @@ void main() { f = new B(); f = /*severe:StaticTypeError*/i2i; f = /*severe:StaticTypeError*/n2n; - f = /*warning:DownCastImplicit*/(i2i as Object); - f = /*warning:DownCastImplicit*/(n2n as Function); + f = /*warning:DownCastImplicit*/i2i as Object; + f = /*warning:DownCastImplicit*/n2n as Function; } { Function f; @@ -1015,7 +1015,7 @@ void main() { f = new B(); f = i2i; f = n2n; - f = /*warning:DownCastImplicit*/(i2i as Object); + f = /*warning:DownCastImplicit*/i2i as Object; f = (n2n as Function); } } @@ -1042,10 +1042,10 @@ void main() { typedef dynamic D(t1, t2); void main() { - F f1 = (x, y) => x + y; - F f2 = /*warning:ClosureWrapLiteral*/(x, y) => x + y; - D f3 = (x, y) => x + y; - Function f4 = (x, y) => x + y; + F f1 = (x, y) => /*info:DynamicInvoke*/x + y; + F f2 = /*warning:ClosureWrapLiteral*/(x, y) => /*info:DynamicInvoke*/x + y; + D f3 = (x, y) => /*info:DynamicInvoke*/x + y; + Function f4 = (x, y) => /*info:DynamicInvoke*/x + y; f2 = /*warning:ClosureWrap*/f1; f1 = (int x, int y) => x + y; f2 = /*severe:StaticTypeError*/(int x) => -x; @@ -1955,12 +1955,12 @@ void main() { B b = new B(); var c = foo(); a = a * b; - a = a * /*pass should be warning:DownCastImplicit*/c; + a = a * /*info:DynamicCast*/c; a = a / b; a = a ~/ b; a = a % b; a = a + b; - a = a + /*pass should be severe:StaticTypeError*/a; + a = a + /*severe:StaticTypeError*/a; a = a - b; b = /*severe:StaticTypeError*/b - b; a = a << b; @@ -1968,7 +1968,21 @@ void main() { a = a & b; a = a ^ b; a = a | b; - c = (/*pass should be info:DynamicInvoke*/c + b); + c = (/*info:DynamicInvoke*/c + b); + + String x = 'hello'; + int y = 42; + x = x + x; + x = x + /*info:DynamicCast*/c; + x = x + /*severe:StaticTypeError*/y; + + bool p = true; + p = p && p; + p = p && /*info:DynamicCast*/c; + p = (/*info:DynamicCast*/c) && p; + p = (/*info:DynamicCast*/c) && /*info:DynamicCast*/c; + p = (/*severe:StaticTypeError*/y) && p; + p = c == y; } ''' }); diff --git a/pkg/dev_compiler/test/checker/inferred_type_test.dart b/pkg/dev_compiler/test/checker/inferred_type_test.dart index 3efac2a8bebe..4b953751357f 100644 --- a/pkg/dev_compiler/test/checker/inferred_type_test.dart +++ b/pkg/dev_compiler/test/checker/inferred_type_test.dart @@ -669,7 +669,7 @@ void main() { dynamic a = new A(); A b = /*info:DynamicCast*/a; print(/*info:DynamicInvoke*/a.x); - print((/*info:DynamicInvoke*/a.x) + 2); + print(/*info:DynamicInvoke*/(/*info:DynamicInvoke*/a.x) + 2); } ''' }); @@ -1232,7 +1232,7 @@ void main() { } { int f(int x) {}; - A = new A(f); + A a = /*info:InferredTypeAllocation*/new A(f); } } ''' @@ -1507,7 +1507,7 @@ void main() { } { Function2 l0 = /*info:InferredTypeClosure*/(x) => x; - Function2 l1 = /*info:InferredTypeClosure*/(x) => x+1; + Function2 l1 = /*info:InferredTypeClosure*/(x) => /*info:DynamicInvoke should be pass*/x+1; Function2 l2 = /*info:InferredTypeClosure should be severe:StaticTypeError*/(x) => x; Function2 l3 = /*info:InferredTypeClosure should be severe:StaticTypeError*/(x) => /*info:DynamicInvoke should be pass*/x.substring(3); Function2 l4 = /*info:InferredTypeClosure*/(x) => /*info:DynamicInvoke should be pass*/x.substring(3); diff --git a/pkg/dev_compiler/test/codegen/expect/fieldtest.js b/pkg/dev_compiler/test/codegen/expect/fieldtest.js index c9b7decfaad0..d4b23b5d0e6e 100644 --- a/pkg/dev_compiler/test/codegen/expect/fieldtest.js +++ b/pkg/dev_compiler/test/codegen/expect/fieldtest.js @@ -73,7 +73,7 @@ var core = dart.import(core); class Generic extends core.Object { foo(t) { dart.as(t, T); - return core.print(dart.notNull(Generic$().bar) + dart.notNull(t)); + return core.print(dart.notNull(Generic$().bar) + dart.notNull(dart.as(t, core.String))); } } dart.setSignature(Generic, { diff --git a/pkg/dev_compiler/test/codegen/fieldtest.dart b/pkg/dev_compiler/test/codegen/fieldtest.dart index 77ceff397076..a6b140e72df5 100644 --- a/pkg/dev_compiler/test/codegen/fieldtest.dart +++ b/pkg/dev_compiler/test/codegen/fieldtest.dart @@ -46,7 +46,7 @@ class Derived extends BaseWithGetter { } class Generic { - foo(T t) => print(bar + t); + foo(T t) => print(bar + (t as String)); static String bar = 'hello'; } diff --git a/pkg/dev_compiler/test/dart_codegen/expect/core/exceptions.dart b/pkg/dev_compiler/test/dart_codegen/expect/core/exceptions.dart index 16ee85150c08..f13894cd2e61 100644 --- a/pkg/dev_compiler/test/dart_codegen/expect/core/exceptions.dart +++ b/pkg/dev_compiler/test/dart_codegen/expect/core/exceptions.dart @@ -24,7 +24,7 @@ if (offset != -1) { } return report; } - if (offset != -1 && (offset < 0 || offset > source.length)) { + if (offset != -1 && (offset < 0 || offset > DEVC$RT.cast(source.length, dynamic, num, "DynamicCast", """line 108, column 49 of dart:core/exceptions.dart: """, source.length is num, true))) { offset = -1; } if (offset == -1) { @@ -59,7 +59,7 @@ report += " (at line $lineNum, character ${offset - lineStart + 1})\n"; report += " (at character ${offset + 1})\n"; } int lineEnd = DEVC$RT.cast(source.length, dynamic, int, "DynamicCast", """line 141, column 19 of dart:core/exceptions.dart: """, source.length is int, true); - for (int i = offset; i < source.length; i++) { + for (int i = offset; i < DEVC$RT.cast(source.length, dynamic, num, "DynamicCast", """line 142, column 30 of dart:core/exceptions.dart: """, source.length is num, true); i++) { int char = ((__x5) => DEVC$RT.cast(__x5, dynamic, int, "DynamicCast", """line 143, column 18 of dart:core/exceptions.dart: """, __x5 is int, true))(source.codeUnitAt(i)); if (char == 0x0a || char == 0x0d) { lineEnd = i;