|  | // Copyright (c) 2015, 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. | 
|  |  | 
|  | import 'package:expect/expect.dart'; | 
|  | import 'package:compiler/src/js/js.dart' as js; | 
|  | import 'package:compiler/src/js/placeholder_safety.dart'; | 
|  |  | 
|  | void test(String source, int expected, {List notNull = const []}) { | 
|  | var predicate = (int pos) => !notNull.contains(pos); | 
|  | js.Template template = js.js.parseForeignJS(source); | 
|  | int actual = PlaceholderSafetyAnalysis.analyze(template.ast, predicate); | 
|  | Expect.equals(expected, actual, 'source: "$source", notNull: $notNull'); | 
|  | } | 
|  |  | 
|  | void main() { | 
|  | test('0', 0); | 
|  |  | 
|  | test('#.x', 1); | 
|  | test('!!#.x', 1); | 
|  | test('#.x + 1', 1); | 
|  | test('1 + #.x', 1); | 
|  | test('#[#] + 2', 2); | 
|  | test('2 + #[#]', 2); | 
|  | test('(# & #) >>> 0', 2); | 
|  |  | 
|  | test('#.a + #.b + #.c', 1); | 
|  | test('#.b + #.b + #.c', 2, notNull: [0]); | 
|  | test('#.c + #.b + #.c', 3, notNull: [0, 1]); | 
|  | test('#.d + #.b + #.c', 1, notNull: [1]); | 
|  |  | 
|  | test('typeof(#) == "string"', 1); | 
|  | test('"object" === typeof #', 1); | 
|  |  | 
|  | test('# == 1 || # == 2 || # == 3', 1); | 
|  | test('# != 1 && # != 2 && # != 3', 1); | 
|  |  | 
|  | test('#.x == 1 || # == 1', 1); | 
|  | test('# == 1 || #.x == 1', 1); | 
|  |  | 
|  | test('(# || 1, #)', 1); // Could also be 2. | 
|  |  | 
|  | test('(#, null.a, #)', 1); | 
|  | test('(#, undefined.a, #)', 1); | 
|  | test('(#, (void 0).a, #)', 1); | 
|  | test('(#, "a".a, #)', 2); | 
|  | test('((#, "a").a, #)', 2); | 
|  |  | 
|  | test('#[#][#][#][#]', 2); | 
|  | test('#[#][#][#][#]', 3, notNull: [0]); | 
|  | test('#[#][#][#][#]', 3, notNull: [0, 1, 2, 3]); | 
|  |  | 
|  | test('#.a = #', 2); | 
|  | test('#.a.b = #', 1); | 
|  | test('#[1] = #', 2); | 
|  | test('#[1][1] = #', 1); | 
|  |  | 
|  | test('#.a = #.a + #.a + #.a', 2); | 
|  | test('#.a = #.a + #.a + #.a', 2, notNull: [0]); | 
|  | test('#.a = #.a + #.a + #.a', 3, notNull: [1]); | 
|  | test('#.a = #.a + #.a + #.a', 4, notNull: [1, 2]); | 
|  |  | 
|  | test('#()', 1); | 
|  | test('#(#, #)', 3); | 
|  | test('#.f(#, #)', 1); | 
|  | test('#.f(#, #)', 3, notNull: [0]); | 
|  |  | 
|  | test('(#.a+=1, #)', 1); | 
|  | test('(#.a++, #)', 1); | 
|  | test('(++#.a, #)', 1); | 
|  |  | 
|  | test('new Array(#)', 1); | 
|  | test('new Date(#)', 1); | 
|  | test('new Function(#)', 1); | 
|  | test('new RegExp(#)', 1); | 
|  | test('new xxx(#)', 0); | 
|  | test('String(#)', 1); | 
|  | test('# in #', 2); | 
|  |  | 
|  | test('Object.keys(#)', 1); | 
|  |  | 
|  | test('typeof #', 1); | 
|  | test('typeof #.foo', 1); | 
|  | test('typeof foo.#', 0); | 
|  | test('typeof Array.#', 1); | 
|  |  | 
|  | test('throw #', 1); | 
|  | test('throw #.x', 1); | 
|  |  | 
|  | test('(function(){})()', 0); | 
|  | test('(function(a,b){#})(#, #)', 0); | 
|  | // Placeholders in an immediate call are ok. | 
|  | test('(function(a,b){a++;b++;return a+b})(#, #)', 2); | 
|  |  | 
|  | test('(() => {})()', 0); | 
|  | test('((a,b) => {#})(#, #)', 0); | 
|  | // Placeholders in an immediate call are ok. | 
|  | test('((a,b) => {a++;b++;return a+b})(#, #)', 2); | 
|  |  | 
|  | test('# ? # : #', 1); | 
|  | test('(# ? 1 : #, #)', 1); | 
|  | test('(# ? # : 2, #)', 1); | 
|  | test('(# ? 1 : 2, #)', 1); // Could also be 4. | 
|  |  | 
|  | test('{A:#, B:#, C:#}', 3); | 
|  | test('[#,#,#,#]', 4); | 
|  | test('[,,,,#,#,,,]', 2); | 
|  |  | 
|  | test('function(){return #;}', 0); | 
|  | test('{A: function() {return #;}}', 0); | 
|  | test('{#() {return #;}}', 0); | 
|  | } |