// 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. // @dart = 2.7 // Unit test of the [NativeBehavior.processSpecString] method. import 'package:expect/expect.dart'; import 'package:compiler/src/native/behavior.dart'; import 'package:compiler/src/diagnostics/diagnostic_listener.dart'; import 'package:compiler/src/diagnostics/messages.dart'; import 'package:compiler/src/elements/types.dart'; import 'package:compiler/src/universe/side_effects.dart' show SideEffects; import '../helpers/type_test_helper.dart'; const OBJECT = 'Object'; const NULL = 'Null'; class Listener extends DiagnosticReporter { String errorMessage; @override internalError(spannable, message) { errorMessage = message; throw "error"; } @override reportError(message, [infos = const []]) { errorMessage = '${message.message.arguments}'; // E.g. "{text: Duplicate tag 'new'.}" throw "error"; } @override DiagnosticMessage createMessage(spannable, messageKind, [arguments = const {}]) { return new DiagnosticMessage(null, spannable, MessageTemplate.TEMPLATES[messageKind].message(arguments, null)); } @override noSuchMethod(_) => null; } void test(DartTypes dartTypes, String specString, {List returns, List creates, SideEffects expectedSideEffects, NativeThrowBehavior expectedThrows, bool expectedNew, bool expectedGvn, bool expectError: false}) { List actualReturns = []; List actualCreates = []; SideEffects actualSideEffects; NativeThrowBehavior actualThrows; bool actualNew; bool actualGvn; Listener listener = new Listener(); try { NativeBehavior.processSpecString(dartTypes, listener, null, specString, setSideEffects: (effects) => actualSideEffects = effects, setThrows: (b) => actualThrows = b, setIsAllocation: (b) => actualNew = b, setUseGvn: (b) => actualGvn = b, lookupType: (t, {required}) => t, typesReturned: actualReturns, typesInstantiated: actualCreates, objectType: OBJECT, nullType: NULL); } catch (e) { Expect.isTrue(expectError, 'Unexpected error "$specString"'); Expect.isNotNull(listener.errorMessage, 'Error message expected.'); return; } Expect.isFalse(expectError, 'Missing error for "$specString".'); Expect.isNull(listener.errorMessage, 'Unexpected error.'); if (returns != null) { Expect.listEquals(returns, actualReturns, 'Unexpected returns.'); } if (creates != null) { Expect.listEquals(creates, actualCreates, 'Unexpected creates.'); } Expect.equals(expectedSideEffects, actualSideEffects); Expect.equals(expectedThrows, actualThrows); Expect.equals(expectedNew, actualNew); Expect.equals(expectedGvn, actualGvn); } void testWithSideEffects(DartTypes dartTypes, String specString, {List returns, List creates, bool expectError: false}) { void sideEffectsTest(String newSpecString, SideEffects expectedSideEffects, {bool sideEffectsExpectError}) { test(dartTypes, newSpecString, returns: returns, creates: creates, expectedSideEffects: expectedSideEffects, expectError: sideEffectsExpectError == null ? expectError : sideEffectsExpectError); } SideEffects emptySideEffects = new SideEffects.empty(); sideEffectsTest(specString + "effects:none;depends:none;", emptySideEffects); sideEffectsTest(specString + "depends:none;effects:none;", emptySideEffects); sideEffectsTest("effects:none;depends:none;" + specString, emptySideEffects); sideEffectsTest("depends:none;effects:none;" + specString, emptySideEffects); SideEffects effects = new SideEffects(); effects.clearChangesIndex(); effects.clearAllDependencies(); sideEffectsTest(specString + "effects:no-index;depends:none;", effects); effects = new SideEffects(); effects.clearAllSideEffects(); effects.clearDependsOnIndexStore(); sideEffectsTest(specString + "effects:none;depends:no-index;", effects); effects = new SideEffects(); effects.clearChangesInstanceProperty(); effects.clearChangesStaticProperty(); effects.clearAllDependencies(); sideEffectsTest( specString + "effects:no-instance,no-static;depends:none;", effects); effects = new SideEffects(); effects.clearAllSideEffects(); effects.clearDependsOnInstancePropertyStore(); effects.clearDependsOnStaticPropertyStore(); sideEffectsTest( specString + "effects:none;depends:no-instance,no-static;", effects); effects = new SideEffects(); effects.clearChangesInstanceProperty(); effects.clearChangesStaticProperty(); effects.clearDependsOnIndexStore(); sideEffectsTest( specString + "effects:no-instance,no-static;depends:no-index;", effects); effects = new SideEffects(); effects.clearChangesIndex(); effects.clearDependsOnInstancePropertyStore(); effects.clearDependsOnStaticPropertyStore(); sideEffectsTest( specString + "effects:no-index;depends:no-instance,no-static;", effects); effects = new SideEffects(); effects.clearChangesIndex(); sideEffectsTest(specString + "effects:no-index;depends:all;", effects); effects = new SideEffects(); effects.clearDependsOnIndexStore(); sideEffectsTest(specString + "effects:all;depends:no-index;", effects); effects = new SideEffects(); effects.clearChangesInstanceProperty(); effects.clearChangesStaticProperty(); sideEffectsTest( specString + "effects:no-instance,no-static;depends:all;", effects); effects = new SideEffects(); effects.clearDependsOnInstancePropertyStore(); effects.clearDependsOnStaticPropertyStore(); sideEffectsTest( specString + "effects:all;depends:no-instance,no-static;", effects); sideEffectsTest(specString + "effects:no-instance,no-static;", effects, sideEffectsExpectError: true); sideEffectsTest(specString + "depends:no-instance,no-static;", effects, sideEffectsExpectError: true); sideEffectsTest(specString + "effects:none;", effects, sideEffectsExpectError: true); sideEffectsTest(specString + "depends:all;", effects, sideEffectsExpectError: true); sideEffectsTest( specString + "effects:no-instance,no-static;depends:foo;", effects, sideEffectsExpectError: true); sideEffectsTest( specString + "effects:foo;depends:no-instance,no-static;", effects, sideEffectsExpectError: true); sideEffectsTest(specString + "effects:all;depends:foo", effects, sideEffectsExpectError: true); sideEffectsTest(specString + "effects:foo;depends:none;", effects, sideEffectsExpectError: true); sideEffectsTest(specString + "effects:;depends:none;", effects, sideEffectsExpectError: true); sideEffectsTest(specString + "effects:all;depends:;", effects, sideEffectsExpectError: true); } void main() async { var env = await TypeEnvironment.create(''); var types = env.types; test(types, 'void', returns: [], creates: []); test(types, '', returns: [OBJECT, NULL], creates: []); test(types, 'var', returns: [OBJECT, NULL], creates: []); test(types, 'A', returns: ['A'], creates: ['A']); test(types, 'A|B', returns: ['A', 'B'], creates: ['A', 'B']); test(types, 'A|B|C', returns: ['A', 'B', 'C'], creates: ['A', 'B', 'C']); test(types, 'returns:void;', returns: [], creates: []); test(types, 'returns:;', returns: [OBJECT, NULL], creates: []); test(types, 'returns:var;', returns: [OBJECT, NULL], creates: []); test(types, 'returns:A;', returns: ['A'], creates: ['A']); test(types, 'returns:A|B;', returns: ['A', 'B'], creates: ['A', 'B']); test(types, 'returns:A|B|C;', returns: ['A', 'B', 'C'], creates: ['A', 'B', 'C']); test(types, 'creates:void;', expectError: true); test(types, 'creates:;', creates: []); test(types, 'creates:var;', creates: []); test(types, 'creates:A;', returns: [], creates: ['A']); test(types, 'creates:A|B;', returns: [], creates: ['A', 'B']); test(types, 'creates:A|B|C;', returns: [], creates: ['A', 'B', 'C']); test(types, 'returns:void;creates:', returns: [], creates: []); test(types, 'returns:;creates:', returns: [OBJECT, NULL], creates: []); test(types, 'returns:var;creates:', returns: [OBJECT, NULL], creates: []); test(types, 'returns:A;creates:', returns: ['A'], creates: []); test(types, 'returns:A|B;creates:;', returns: ['A', 'B'], creates: []); test(types, 'returns:A|B|C;creates:;', returns: ['A', 'B', 'C'], creates: []); test(types, 'returns:void;creates:A;', returns: [], creates: ['A']); test(types, 'returns:;creates:A|B;', returns: [OBJECT, NULL], creates: ['A', 'B']); test(types, 'returns:var;creates:A|B|C;', returns: [OBJECT, NULL], creates: ['A', 'B', 'C']); test(types, 'returns:A; creates:A|B|C; ', returns: ['A'], creates: ['A', 'B', 'C']); test(types, ' returns:A|B; creates:A|C;', returns: ['A', 'B'], creates: ['A', 'C']); test(types, ' returns:A|B|C; creates:A; ', returns: ['A', 'B', 'C'], creates: ['A']); testWithSideEffects(types, 'returns:void;', returns: [], creates: []); testWithSideEffects(types, 'returns:void;', returns: [], creates: []); testWithSideEffects(types, 'returns:;', returns: [OBJECT, NULL], creates: []); testWithSideEffects(types, 'returns:var;', returns: [OBJECT, NULL], creates: []); testWithSideEffects(types, 'returns:A;', returns: ['A'], creates: ['A']); testWithSideEffects(types, 'returns:A|B;', returns: ['A', 'B'], creates: ['A', 'B']); testWithSideEffects(types, 'returns:A|B|C;', returns: ['A', 'B', 'C'], creates: ['A', 'B', 'C']); testWithSideEffects(types, 'returns: A| B |C ;', returns: ['A', 'B', 'C'], creates: ['A', 'B', 'C']); testWithSideEffects(types, 'creates:void;', expectError: true); testWithSideEffects(types, 'creates:;', creates: []); testWithSideEffects(types, 'creates:var;', creates: []); testWithSideEffects(types, 'creates:A;', returns: [], creates: ['A']); testWithSideEffects(types, 'creates:A|B;', returns: [], creates: ['A', 'B']); testWithSideEffects(types, 'creates:A|B|C;', returns: [], creates: ['A', 'B', 'C']); testWithSideEffects(types, 'returns:void;creates:;', returns: [], creates: []); testWithSideEffects(types, 'returns:;creates:;', returns: [OBJECT, NULL], creates: []); testWithSideEffects(types, 'returns:var;creates:;', returns: [OBJECT, NULL], creates: []); testWithSideEffects(types, 'returns:A;creates:;', returns: ['A'], creates: []); testWithSideEffects(types, 'returns:A|B;creates:;', returns: ['A', 'B'], creates: []); testWithSideEffects(types, 'returns:A|B|C;creates:;', returns: ['A', 'B', 'C'], creates: []); testWithSideEffects(types, 'returns:void;creates:A;', returns: [], creates: ['A']); testWithSideEffects(types, 'returns:;creates:A|B;', returns: [OBJECT, NULL], creates: ['A', 'B']); testWithSideEffects(types, 'returns:var;creates:A|B|C;', returns: [OBJECT, NULL], creates: ['A', 'B', 'C']); testWithSideEffects(types, 'returns:A; creates:A|B|C; ', returns: ['A'], creates: ['A', 'B', 'C']); testWithSideEffects(types, ' returns:A|B; creates:A|C;', returns: ['A', 'B'], creates: ['A', 'C']); testWithSideEffects(types, ' returns:A|B|C; creates:A; ', returns: ['A', 'B', 'C'], creates: ['A']); test(types, 'throws:may', expectedThrows: NativeThrowBehavior.MAY); test(types, 'throws:never', expectedThrows: NativeThrowBehavior.NEVER); test(types, 'throws:null(1)', expectedThrows: NativeThrowBehavior.NULL_NSM); test(types, 'throws:null(1)+may', expectedThrows: NativeThrowBehavior.NULL_NSM_THEN_MAY); test(types, 'new:true', expectedNew: true); test(types, 'new:false', expectedNew: false); test(types, 'returns:A;new:true', returns: ['A'], expectedNew: true); test(types, ' new : true ; returns:A;', returns: ['A'], expectedNew: true); test(types, 'new:true;returns:A;new:true', expectError: true); test(types, 'gvn:true', expectedGvn: true); test(types, 'gvn:false', expectedGvn: false); test(types, 'returns:A;gvn:true', returns: ['A'], expectedGvn: true); test(types, ' gvn : true ; returns:A;', returns: ['A'], expectedGvn: true); test(types, 'gvn:true;returns:A;gvn:true', expectError: true); test(types, 'gvn: true; new: true', expectError: true); test(types, 'gvn: true; new: false', expectedGvn: true, expectedNew: false); test(types, 'gvn: false; new: true', expectedGvn: false, expectedNew: true); test(types, 'gvn: false; new: false', expectedGvn: false, expectedNew: false); }