| // Copyright (c) 2016, 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 'dart:async'; |
| |
| import 'package:analyzer/dart/analysis/declared_variables.dart'; |
| import 'package:analyzer/dart/ast/ast.dart'; |
| import 'package:analyzer/dart/ast/standard_resolution_map.dart'; |
| import 'package:analyzer/dart/ast/token.dart'; |
| import 'package:analyzer/dart/element/element.dart'; |
| import 'package:analyzer/error/error.dart'; |
| import 'package:analyzer/error/listener.dart'; |
| import 'package:analyzer/src/context/context.dart'; |
| import 'package:analyzer/src/dart/element/element.dart'; |
| import 'package:analyzer/src/error/codes.dart'; |
| import 'package:analyzer/src/generated/constant.dart'; |
| import 'package:analyzer/src/generated/engine.dart'; |
| import 'package:analyzer/src/generated/parser.dart'; |
| import 'package:analyzer/src/generated/resolver.dart'; |
| import 'package:analyzer/src/generated/source.dart'; |
| import 'package:analyzer/src/generated/source_io.dart'; |
| import 'package:analyzer/src/generated/testing/ast_test_factory.dart'; |
| import 'package:analyzer/src/generated/testing/test_type_provider.dart'; |
| import 'package:analyzer/src/task/dart.dart'; |
| import 'package:path/path.dart'; |
| import 'package:test/test.dart'; |
| import 'package:test_reflective_loader/test_reflective_loader.dart'; |
| |
| import '../../../generated/resolver_test_case.dart'; |
| import '../../../generated/test_support.dart'; |
| |
| main() { |
| defineReflectiveSuite(() { |
| defineReflectiveTests(ConstantValueComputerTest); |
| defineReflectiveTests(ConstantVisitorTest); |
| defineReflectiveTests(StrongConstantValueComputerTest); |
| }); |
| } |
| |
| /** |
| * Implementation of [ConstantEvaluationValidator] used during unit tests; |
| * verifies that any nodes referenced during constant evaluation are present in |
| * the dependency graph. |
| */ |
| class ConstantEvaluationValidator_ForTest |
| implements ConstantEvaluationValidator { |
| final InternalAnalysisContext context; |
| ConstantValueComputer computer; |
| ConstantEvaluationTarget _nodeBeingEvaluated; |
| |
| ConstantEvaluationValidator_ForTest(this.context); |
| |
| @override |
| void beforeComputeValue(ConstantEvaluationTarget constant) { |
| _nodeBeingEvaluated = constant; |
| } |
| |
| @override |
| void beforeGetConstantInitializers(ConstructorElement constructor) => |
| _checkPathTo(constructor); |
| |
| @override |
| void beforeGetEvaluationResult(ConstantEvaluationTarget constant) => |
| _checkPathTo(constant); |
| |
| @override |
| void beforeGetFieldEvaluationResult(FieldElementImpl field) => |
| _checkPathTo(field); |
| |
| @override |
| void beforeGetParameterDefault(ParameterElement parameter) => |
| _checkPathTo(parameter); |
| |
| void _checkPathTo(ConstantEvaluationTarget target) { |
| if (computer.referenceGraph.containsPath(_nodeBeingEvaluated, target)) { |
| return; // pass |
| } |
| // print a nice error message on failure |
| StringBuffer out = new StringBuffer(); |
| out.writeln("missing path in constant dependency graph"); |
| out.writeln("from $_nodeBeingEvaluated to $target"); |
| for (var s in context.analysisCache.sources) { |
| String text = context.getContents(s).data; |
| if (text != "") { |
| out.writeln(''' |
| === ${s.shortName} |
| $text'''); |
| } |
| } |
| fail(out.toString()); |
| } |
| } |
| |
| @reflectiveTest |
| class ConstantValueComputerTest extends ResolverTestCase { |
| test_annotation_constConstructor() async { |
| CompilationUnit compilationUnit = await resolveSource(r''' |
| class A { |
| final int i; |
| const A(this.i); |
| } |
| |
| class C { |
| @A(5) |
| f() {} |
| } |
| '''); |
| EvaluationResultImpl result = |
| _evaluateAnnotation(compilationUnit, "C", "f"); |
| Map<String, DartObjectImpl> annotationFields = _assertType(result, 'A'); |
| _assertIntField(annotationFields, 'i', 5); |
| } |
| |
| test_annotation_constConstructor_named() async { |
| CompilationUnit compilationUnit = await resolveSource(r''' |
| class A { |
| final int i; |
| const A.named(this.i); |
| } |
| |
| class C { |
| @A.named(5) |
| f() {} |
| } |
| '''); |
| EvaluationResultImpl result = |
| _evaluateAnnotation(compilationUnit, "C", "f"); |
| Map<String, DartObjectImpl> annotationFields = _assertType(result, 'A'); |
| _assertIntField(annotationFields, 'i', 5); |
| } |
| |
| test_annotation_constConstructor_noArgs() async { |
| // Failing to pass arguments to an annotation which is a constant |
| // constructor is illegal, but shouldn't crash analysis. |
| CompilationUnit compilationUnit = await resolveSource(r''' |
| class A { |
| final int i; |
| const A(this.i); |
| } |
| |
| class C { |
| @A |
| f() {} |
| } |
| '''); |
| _evaluateAnnotation(compilationUnit, "C", "f"); |
| } |
| |
| test_annotation_constConstructor_noArgs_named() async { |
| // Failing to pass arguments to an annotation which is a constant |
| // constructor is illegal, but shouldn't crash analysis. |
| CompilationUnit compilationUnit = await resolveSource(r''' |
| class A { |
| final int i; |
| const A.named(this.i); |
| } |
| |
| class C { |
| @A.named |
| f() {} |
| } |
| '''); |
| _evaluateAnnotation(compilationUnit, "C", "f"); |
| } |
| |
| test_annotation_nonConstConstructor() async { |
| // Calling a non-const constructor from an annotation that is illegal, but |
| // shouldn't crash analysis. |
| CompilationUnit compilationUnit = await resolveSource(r''' |
| class A { |
| final int i; |
| A(this.i); |
| } |
| |
| class C { |
| @A(5) |
| f() {} |
| } |
| '''); |
| _evaluateAnnotation(compilationUnit, "C", "f"); |
| } |
| |
| test_annotation_staticConst() async { |
| CompilationUnit compilationUnit = await resolveSource(r''' |
| class C { |
| static const int i = 5; |
| |
| @i |
| f() {} |
| } |
| '''); |
| EvaluationResultImpl result = |
| _evaluateAnnotation(compilationUnit, "C", "f"); |
| expect(_assertValidInt(result), 5); |
| } |
| |
| test_annotation_staticConst_args() async { |
| // Applying arguments to an annotation that is a static const is |
| // illegal, but shouldn't crash analysis. |
| CompilationUnit compilationUnit = await resolveSource(r''' |
| class C { |
| static const int i = 5; |
| |
| @i(1) |
| f() {} |
| } |
| '''); |
| _evaluateAnnotation(compilationUnit, "C", "f"); |
| } |
| |
| test_annotation_staticConst_otherClass() async { |
| CompilationUnit compilationUnit = await resolveSource(r''' |
| class A { |
| static const int i = 5; |
| } |
| |
| class C { |
| @A.i |
| f() {} |
| } |
| '''); |
| EvaluationResultImpl result = |
| _evaluateAnnotation(compilationUnit, "C", "f"); |
| expect(_assertValidInt(result), 5); |
| } |
| |
| test_annotation_staticConst_otherClass_args() async { |
| // Applying arguments to an annotation that is a static const is |
| // illegal, but shouldn't crash analysis. |
| CompilationUnit compilationUnit = await resolveSource(r''' |
| class A { |
| static const int i = 5; |
| } |
| |
| class C { |
| @A.i(1) |
| f() {} |
| } |
| '''); |
| _evaluateAnnotation(compilationUnit, "C", "f"); |
| } |
| |
| test_annotation_topLevelVariable() async { |
| CompilationUnit compilationUnit = await resolveSource(r''' |
| const int i = 5; |
| class C { |
| @i |
| f() {} |
| } |
| '''); |
| EvaluationResultImpl result = |
| _evaluateAnnotation(compilationUnit, "C", "f"); |
| expect(_assertValidInt(result), 5); |
| } |
| |
| test_annotation_topLevelVariable_args() async { |
| // Applying arguments to an annotation that is a top-level variable is |
| // illegal, but shouldn't crash analysis. |
| CompilationUnit compilationUnit = await resolveSource(r''' |
| const int i = 5; |
| class C { |
| @i(1) |
| f() {} |
| } |
| '''); |
| _evaluateAnnotation(compilationUnit, "C", "f"); |
| } |
| |
| test_computeValues_cycle() async { |
| TestLogger logger = new TestLogger(); |
| AnalysisEngine.instance.logger = logger; |
| try { |
| Source source = addSource(r''' |
| const int a = c; |
| const int b = a; |
| const int c = b;'''); |
| LibraryElement libraryElement = resolve2(source); |
| CompilationUnit unit = |
| analysisContext.resolveCompilationUnit(source, libraryElement); |
| analysisContext.computeErrors(source); |
| expect(unit, isNotNull); |
| ConstantValueComputer computer = _makeConstantValueComputer(); |
| computer.add(unit); |
| computer.computeValues(); |
| NodeList<CompilationUnitMember> members = unit.declarations; |
| expect(members, hasLength(3)); |
| _validate(false, (members[0] as TopLevelVariableDeclaration).variables); |
| _validate(false, (members[1] as TopLevelVariableDeclaration).variables); |
| _validate(false, (members[2] as TopLevelVariableDeclaration).variables); |
| } finally { |
| AnalysisEngine.instance.logger = Logger.NULL; |
| } |
| } |
| |
| test_computeValues_dependentVariables() async { |
| Source source = addSource(r''' |
| const int b = a; |
| const int a = 0;'''); |
| LibraryElement libraryElement = resolve2(source); |
| CompilationUnit unit = |
| analysisContext.resolveCompilationUnit(source, libraryElement); |
| expect(unit, isNotNull); |
| ConstantValueComputer computer = _makeConstantValueComputer(); |
| computer.add(unit); |
| computer.computeValues(); |
| NodeList<CompilationUnitMember> members = unit.declarations; |
| expect(members, hasLength(2)); |
| _validate(true, (members[0] as TopLevelVariableDeclaration).variables); |
| _validate(true, (members[1] as TopLevelVariableDeclaration).variables); |
| } |
| |
| test_computeValues_empty() async { |
| ConstantValueComputer computer = _makeConstantValueComputer(); |
| computer.computeValues(); |
| } |
| |
| test_computeValues_multipleSources() async { |
| Source librarySource = addNamedSource("/lib.dart", r''' |
| library lib; |
| part 'part.dart'; |
| const int c = b; |
| const int a = 0;'''); |
| Source partSource = addNamedSource("/part.dart", r''' |
| part of lib; |
| const int b = a; |
| const int d = c;'''); |
| LibraryElement libraryElement = resolve2(librarySource); |
| CompilationUnit libraryUnit = |
| analysisContext.resolveCompilationUnit(librarySource, libraryElement); |
| expect(libraryUnit, isNotNull); |
| CompilationUnit partUnit = |
| analysisContext.resolveCompilationUnit(partSource, libraryElement); |
| expect(partUnit, isNotNull); |
| ConstantValueComputer computer = _makeConstantValueComputer(); |
| computer.add(libraryUnit); |
| computer.add(partUnit); |
| computer.computeValues(); |
| NodeList<CompilationUnitMember> libraryMembers = libraryUnit.declarations; |
| expect(libraryMembers, hasLength(2)); |
| _validate( |
| true, (libraryMembers[0] as TopLevelVariableDeclaration).variables); |
| _validate( |
| true, (libraryMembers[1] as TopLevelVariableDeclaration).variables); |
| NodeList<CompilationUnitMember> partMembers = libraryUnit.declarations; |
| expect(partMembers, hasLength(2)); |
| _validate(true, (partMembers[0] as TopLevelVariableDeclaration).variables); |
| _validate(true, (partMembers[1] as TopLevelVariableDeclaration).variables); |
| } |
| |
| test_computeValues_singleVariable() async { |
| Source source = addSource("const int a = 0;"); |
| LibraryElement libraryElement = resolve2(source); |
| CompilationUnit unit = |
| analysisContext.resolveCompilationUnit(source, libraryElement); |
| expect(unit, isNotNull); |
| ConstantValueComputer computer = _makeConstantValueComputer(); |
| computer.add(unit); |
| computer.computeValues(); |
| NodeList<CompilationUnitMember> members = unit.declarations; |
| expect(members, hasLength(1)); |
| _validate(true, (members[0] as TopLevelVariableDeclaration).variables); |
| } |
| |
| test_computeValues_value_depends_on_enum() async { |
| Source source = addSource(''' |
| enum E { id0, id1 } |
| const E e = E.id0; |
| '''); |
| LibraryElement libraryElement = resolve2(source); |
| CompilationUnit unit = |
| analysisContext.resolveCompilationUnit(source, libraryElement); |
| expect(unit, isNotNull); |
| ConstantValueComputer computer = _makeConstantValueComputer(); |
| computer.add(unit); |
| computer.computeValues(); |
| TopLevelVariableDeclaration declaration = unit.declarations |
| .firstWhere((member) => member is TopLevelVariableDeclaration); |
| _validate(true, declaration.variables); |
| } |
| |
| test_dependencyOnConstructor() async { |
| // x depends on "const A()" |
| await _assertProperDependencies(r''' |
| class A { |
| const A(); |
| } |
| const x = const A();'''); |
| } |
| |
| test_dependencyOnConstructorArgument() async { |
| // "const A(x)" depends on x |
| await _assertProperDependencies(r''' |
| class A { |
| const A(this.next); |
| final A next; |
| } |
| const A x = const A(null); |
| const A y = const A(x);'''); |
| } |
| |
| test_dependencyOnConstructorArgument_unresolvedConstructor() async { |
| // "const A.a(x)" depends on x even if the constructor A.a can't be found. |
| await _assertProperDependencies(r''' |
| class A { |
| } |
| const int x = 1; |
| const A y = const A.a(x);''', |
| [CompileTimeErrorCode.CONST_WITH_UNDEFINED_CONSTRUCTOR]); |
| } |
| |
| test_dependencyOnConstructorInitializer() async { |
| // "const A()" depends on x |
| await _assertProperDependencies(r''' |
| const int x = 1; |
| class A { |
| const A() : v = x; |
| final int v; |
| }'''); |
| } |
| |
| test_dependencyOnExplicitSuperConstructor() async { |
| // b depends on B() depends on A() |
| await _assertProperDependencies(r''' |
| class A { |
| const A(this.x); |
| final int x; |
| } |
| class B extends A { |
| const B() : super(5); |
| } |
| const B b = const B();'''); |
| } |
| |
| test_dependencyOnExplicitSuperConstructorParameters() async { |
| // b depends on B() depends on i |
| await _assertProperDependencies(r''' |
| class A { |
| const A(this.x); |
| final int x; |
| } |
| class B extends A { |
| const B() : super(i); |
| } |
| const B b = const B(); |
| const int i = 5;'''); |
| } |
| |
| test_dependencyOnFactoryRedirect() async { |
| // a depends on A.foo() depends on A.bar() |
| await _assertProperDependencies( |
| r''' |
| const A a = const A.foo(); |
| class A { |
| factory const A.foo() = A.bar; |
| const A.bar(); |
| }''', |
| Parser.useFasta |
| ? const <ErrorCode>[ParserErrorCode.CONST_AFTER_FACTORY] |
| : const <ErrorCode>[]); |
| } |
| |
| test_dependencyOnFactoryRedirectWithTypeParams() async { |
| await _assertProperDependencies(r''' |
| class A { |
| const factory A(int a) = B<int>; |
| } |
| |
| class B<T> implements A { |
| final T x; |
| const B(this.x); |
| } |
| |
| const A a = const A(10);'''); |
| } |
| |
| test_dependencyOnImplicitSuperConstructor() async { |
| // b depends on B() depends on A() |
| await _assertProperDependencies(r''' |
| class A { |
| const A() : x = 5; |
| final int x; |
| } |
| class B extends A { |
| const B(); |
| } |
| const B b = const B();'''); |
| } |
| |
| test_dependencyOnInitializedFinal() async { |
| // a depends on A() depends on A.x |
| await _assertProperDependencies(''' |
| class A { |
| const A(); |
| final int x = 1; |
| } |
| const A a = const A(); |
| '''); |
| } |
| |
| test_dependencyOnInitializedNonStaticConst() async { |
| // Even though non-static consts are not allowed by the language, we need |
| // to handle them for error recovery purposes. |
| // a depends on A() depends on A.x |
| await _assertProperDependencies(''' |
| class A { |
| const A(); |
| const int x = 1; |
| } |
| const A a = const A(); |
| ''', [CompileTimeErrorCode.CONST_INSTANCE_FIELD]); |
| } |
| |
| test_dependencyOnNonFactoryRedirect() async { |
| // a depends on A.foo() depends on A.bar() |
| await _assertProperDependencies(r''' |
| const A a = const A.foo(); |
| class A { |
| const A.foo() : this.bar(); |
| const A.bar(); |
| }'''); |
| } |
| |
| test_dependencyOnNonFactoryRedirect_arg() async { |
| // a depends on A.foo() depends on b |
| await _assertProperDependencies(r''' |
| const A a = const A.foo(); |
| const int b = 1; |
| class A { |
| const A.foo() : this.bar(b); |
| const A.bar(x) : y = x; |
| final int y; |
| }'''); |
| } |
| |
| test_dependencyOnNonFactoryRedirect_defaultValue() async { |
| // a depends on A.foo() depends on A.bar() depends on b |
| await _assertProperDependencies(r''' |
| const A a = const A.foo(); |
| const int b = 1; |
| class A { |
| const A.foo() : this.bar(); |
| const A.bar([x = b]) : y = x; |
| final int y; |
| }'''); |
| } |
| |
| test_dependencyOnNonFactoryRedirect_toMissing() async { |
| // a depends on A.foo() which depends on nothing, since A.bar() is |
| // missing. |
| await _assertProperDependencies(r''' |
| const A a = const A.foo(); |
| class A { |
| const A.foo() : this.bar(); |
| }''', [CompileTimeErrorCode.REDIRECT_GENERATIVE_TO_MISSING_CONSTRUCTOR]); |
| } |
| |
| test_dependencyOnNonFactoryRedirect_toNonConst() async { |
| // a depends on A.foo() which depends on nothing, since A.bar() is |
| // non-const. |
| await _assertProperDependencies(r''' |
| const A a = const A.foo(); |
| class A { |
| const A.foo() : this.bar(); |
| A.bar(); |
| }'''); |
| } |
| |
| test_dependencyOnNonFactoryRedirect_unnamed() async { |
| // a depends on A.foo() depends on A() |
| await _assertProperDependencies(r''' |
| const A a = const A.foo(); |
| class A { |
| const A.foo() : this(); |
| const A(); |
| }'''); |
| } |
| |
| test_dependencyOnOptionalParameterDefault() async { |
| // a depends on A() depends on B() |
| await _assertProperDependencies(r''' |
| class A { |
| const A([x = const B()]) : b = x; |
| final B b; |
| } |
| class B { |
| const B(); |
| } |
| const A a = const A();'''); |
| } |
| |
| test_dependencyOnVariable() async { |
| // x depends on y |
| await _assertProperDependencies(r''' |
| const x = y + 1; |
| const y = 2;'''); |
| } |
| |
| test_final_initialized_at_declaration() async { |
| CompilationUnit compilationUnit = await resolveSource(''' |
| class A { |
| final int i = 123; |
| const A(); |
| } |
| |
| const A a = const A(); |
| '''); |
| EvaluationResultImpl result = |
| _evaluateTopLevelVariable(compilationUnit, 'a'); |
| Map<String, DartObjectImpl> fields = _assertType(result, "A"); |
| expect(fields, hasLength(1)); |
| _assertIntField(fields, "i", 123); |
| } |
| |
| test_fromEnvironment_bool_default_false() async { |
| expect(_assertValidBool(await _check_fromEnvironment_bool(null, "false")), |
| false); |
| } |
| |
| test_fromEnvironment_bool_default_overridden() async { |
| expect(_assertValidBool(await _check_fromEnvironment_bool("false", "true")), |
| false); |
| } |
| |
| test_fromEnvironment_bool_default_parseError() async { |
| expect( |
| _assertValidBool( |
| await _check_fromEnvironment_bool("parseError", "true")), |
| true); |
| } |
| |
| test_fromEnvironment_bool_default_true() async { |
| expect(_assertValidBool(await _check_fromEnvironment_bool(null, "true")), |
| true); |
| } |
| |
| test_fromEnvironment_bool_false() async { |
| expect(_assertValidBool(await _check_fromEnvironment_bool("false", null)), |
| false); |
| } |
| |
| test_fromEnvironment_bool_parseError() async { |
| expect( |
| _assertValidBool(await _check_fromEnvironment_bool("parseError", null)), |
| false); |
| } |
| |
| test_fromEnvironment_bool_true() async { |
| expect(_assertValidBool(await _check_fromEnvironment_bool("true", null)), |
| true); |
| } |
| |
| test_fromEnvironment_bool_undeclared() async { |
| _assertValidUnknown(await _check_fromEnvironment_bool(null, null)); |
| } |
| |
| test_fromEnvironment_int_default_overridden() async { |
| expect( |
| _assertValidInt(await _check_fromEnvironment_int("234", "123")), 234); |
| } |
| |
| test_fromEnvironment_int_default_parseError() async { |
| expect( |
| _assertValidInt(await _check_fromEnvironment_int("parseError", "123")), |
| 123); |
| } |
| |
| test_fromEnvironment_int_default_undeclared() async { |
| expect(_assertValidInt(await _check_fromEnvironment_int(null, "123")), 123); |
| } |
| |
| test_fromEnvironment_int_ok() async { |
| expect(_assertValidInt(await _check_fromEnvironment_int("234", null)), 234); |
| } |
| |
| test_fromEnvironment_int_parseError() async { |
| _assertValidNull(await _check_fromEnvironment_int("parseError", null)); |
| } |
| |
| test_fromEnvironment_int_parseError_nullDefault() async { |
| _assertValidNull(await _check_fromEnvironment_int("parseError", "null")); |
| } |
| |
| test_fromEnvironment_int_undeclared() async { |
| _assertValidUnknown(await _check_fromEnvironment_int(null, null)); |
| } |
| |
| test_fromEnvironment_int_undeclared_nullDefault() async { |
| _assertValidNull(await _check_fromEnvironment_int(null, "null")); |
| } |
| |
| test_fromEnvironment_string_default_overridden() async { |
| expect( |
| _assertValidString(await _check_fromEnvironment_string("abc", "'def'")), |
| "abc"); |
| } |
| |
| test_fromEnvironment_string_default_undeclared() async { |
| expect( |
| _assertValidString(await _check_fromEnvironment_string(null, "'def'")), |
| "def"); |
| } |
| |
| test_fromEnvironment_string_empty() async { |
| expect( |
| _assertValidString(await _check_fromEnvironment_string("", null)), ""); |
| } |
| |
| test_fromEnvironment_string_ok() async { |
| expect(_assertValidString(await _check_fromEnvironment_string("abc", null)), |
| "abc"); |
| } |
| |
| test_fromEnvironment_string_undeclared() async { |
| _assertValidUnknown(await _check_fromEnvironment_string(null, null)); |
| } |
| |
| test_fromEnvironment_string_undeclared_nullDefault() async { |
| _assertValidNull(await _check_fromEnvironment_string(null, "null")); |
| } |
| |
| test_getConstructor_redirectingFactory() async { |
| CompilationUnit compilationUnit = await resolveSource(r''' |
| class A { |
| factory const A() = B; |
| } |
| |
| class B implements A { |
| const B(); |
| } |
| |
| class C { |
| @A() |
| f() {} |
| } |
| '''); |
| EvaluationResultImpl result = |
| _evaluateAnnotation(compilationUnit, "C", "f"); |
| expect(result.value.getInvocation().constructor.isFactory, isTrue); |
| } |
| |
| test_getConstructor_withArgs() async { |
| CompilationUnit compilationUnit = await resolveSource(r''' |
| class A { |
| final int i; |
| const A(this.i); |
| } |
| |
| class C { |
| @A(5) |
| f() {} |
| } |
| '''); |
| EvaluationResultImpl result = |
| _evaluateAnnotation(compilationUnit, "C", "f"); |
| ConstructorInvocation invocation = result.value.getInvocation(); |
| expect(invocation.constructor, isNotNull); |
| expect(invocation.positionalArguments, hasLength(1)); |
| expect(invocation.positionalArguments.single.toIntValue(), 5); |
| expect(invocation.namedArguments, isEmpty); |
| } |
| |
| test_getConstructor_withNamedArgs() async { |
| CompilationUnit compilationUnit = await resolveSource(r''' |
| class A { |
| final int i; |
| const A({this.i}); |
| } |
| |
| class C { |
| @A(i: 5) |
| f() {} |
| } |
| '''); |
| EvaluationResultImpl result = |
| _evaluateAnnotation(compilationUnit, "C", "f"); |
| ConstructorInvocation invocation = result.value.getInvocation(); |
| expect(invocation.constructor, isNotNull); |
| expect(invocation.positionalArguments, isEmpty); |
| expect(invocation.namedArguments, isNotEmpty); |
| expect(invocation.namedArguments['i'].toIntValue(), 5); |
| } |
| |
| test_instanceCreationExpression_computedField() async { |
| CompilationUnit compilationUnit = await resolveSource(r''' |
| const foo = const A(4, 5); |
| class A { |
| const A(int i, int j) : k = 2 * i + j; |
| final int k; |
| }'''); |
| EvaluationResultImpl result = |
| _evaluateTopLevelVariable(compilationUnit, "foo"); |
| Map<String, DartObjectImpl> fields = _assertType(result, "A"); |
| expect(fields, hasLength(1)); |
| _assertIntField(fields, "k", 13); |
| } |
| |
| test_instanceCreationExpression_computedField_namedOptionalWithDefault() async { |
| await _checkInstanceCreationOptionalParams(false, true, true); |
| } |
| |
| test_instanceCreationExpression_computedField_namedOptionalWithoutDefault() async { |
| await _checkInstanceCreationOptionalParams(false, true, false); |
| } |
| |
| test_instanceCreationExpression_computedField_unnamedOptionalWithDefault() async { |
| await _checkInstanceCreationOptionalParams(false, false, true); |
| } |
| |
| test_instanceCreationExpression_computedField_unnamedOptionalWithoutDefault() async { |
| await _checkInstanceCreationOptionalParams(false, false, false); |
| } |
| |
| test_instanceCreationExpression_computedField_usesConstConstructor() async { |
| CompilationUnit compilationUnit = await resolveSource(r''' |
| const foo = const A(3); |
| class A { |
| const A(int i) : b = const B(4); |
| final int b; |
| } |
| class B { |
| const B(this.k); |
| final int k; |
| }'''); |
| EvaluationResultImpl result = |
| _evaluateTopLevelVariable(compilationUnit, "foo"); |
| Map<String, DartObjectImpl> fieldsOfA = _assertType(result, "A"); |
| expect(fieldsOfA, hasLength(1)); |
| Map<String, DartObjectImpl> fieldsOfB = |
| _assertFieldType(fieldsOfA, "b", "B"); |
| expect(fieldsOfB, hasLength(1)); |
| _assertIntField(fieldsOfB, "k", 4); |
| } |
| |
| test_instanceCreationExpression_computedField_usesStaticConst() async { |
| CompilationUnit compilationUnit = await resolveSource(r''' |
| const foo = const A(3); |
| class A { |
| const A(int i) : k = i + B.bar; |
| final int k; |
| } |
| class B { |
| static const bar = 4; |
| }'''); |
| EvaluationResultImpl result = |
| _evaluateTopLevelVariable(compilationUnit, "foo"); |
| Map<String, DartObjectImpl> fields = _assertType(result, "A"); |
| expect(fields, hasLength(1)); |
| _assertIntField(fields, "k", 7); |
| } |
| |
| test_instanceCreationExpression_computedField_usesTopLevelConst() async { |
| CompilationUnit compilationUnit = await resolveSource(r''' |
| const foo = const A(3); |
| const bar = 4; |
| class A { |
| const A(int i) : k = i + bar; |
| final int k; |
| }'''); |
| EvaluationResultImpl result = |
| _evaluateTopLevelVariable(compilationUnit, "foo"); |
| Map<String, DartObjectImpl> fields = _assertType(result, "A"); |
| expect(fields, hasLength(1)); |
| _assertIntField(fields, "k", 7); |
| } |
| |
| test_instanceCreationExpression_explicitSuper() async { |
| CompilationUnit compilationUnit = await resolveSource(r''' |
| const foo = const B(4, 5); |
| class A { |
| const A(this.x); |
| final int x; |
| } |
| class B extends A { |
| const B(int x, this.y) : super(x * 2); |
| final int y; |
| }'''); |
| EvaluationResultImpl result = |
| _evaluateTopLevelVariable(compilationUnit, "foo"); |
| Map<String, DartObjectImpl> fields = _assertType(result, "B"); |
| expect(fields, hasLength(2)); |
| _assertIntField(fields, "y", 5); |
| Map<String, DartObjectImpl> superclassFields = |
| _assertFieldType(fields, GenericState.SUPERCLASS_FIELD, "A"); |
| expect(superclassFields, hasLength(1)); |
| _assertIntField(superclassFields, "x", 8); |
| } |
| |
| test_instanceCreationExpression_fieldFormalParameter() async { |
| CompilationUnit compilationUnit = await resolveSource(r''' |
| const foo = const A(42); |
| class A { |
| int x; |
| const A(this.x) |
| }'''); |
| EvaluationResultImpl result = |
| _evaluateTopLevelVariable(compilationUnit, "foo"); |
| Map<String, DartObjectImpl> fields = _assertType(result, "A"); |
| expect(fields, hasLength(1)); |
| _assertIntField(fields, "x", 42); |
| } |
| |
| test_instanceCreationExpression_fieldFormalParameter_namedOptionalWithDefault() async { |
| await _checkInstanceCreationOptionalParams(true, true, true); |
| } |
| |
| test_instanceCreationExpression_fieldFormalParameter_namedOptionalWithoutDefault() async { |
| await _checkInstanceCreationOptionalParams(true, true, false); |
| } |
| |
| test_instanceCreationExpression_fieldFormalParameter_unnamedOptionalWithDefault() async { |
| await _checkInstanceCreationOptionalParams(true, false, true); |
| } |
| |
| test_instanceCreationExpression_fieldFormalParameter_unnamedOptionalWithoutDefault() async { |
| await _checkInstanceCreationOptionalParams(true, false, false); |
| } |
| |
| test_instanceCreationExpression_implicitSuper() async { |
| CompilationUnit compilationUnit = await resolveSource(r''' |
| const foo = const B(4); |
| class A { |
| const A() : x = 3; |
| final int x; |
| } |
| class B extends A { |
| const B(this.y); |
| final int y; |
| }'''); |
| EvaluationResultImpl result = |
| _evaluateTopLevelVariable(compilationUnit, "foo"); |
| Map<String, DartObjectImpl> fields = _assertType(result, "B"); |
| expect(fields, hasLength(2)); |
| _assertIntField(fields, "y", 4); |
| Map<String, DartObjectImpl> superclassFields = |
| _assertFieldType(fields, GenericState.SUPERCLASS_FIELD, "A"); |
| expect(superclassFields, hasLength(1)); |
| _assertIntField(superclassFields, "x", 3); |
| } |
| |
| test_instanceCreationExpression_nonFactoryRedirect() async { |
| CompilationUnit compilationUnit = await resolveSource(r''' |
| const foo = const A.a1(); |
| class A { |
| const A.a1() : this.a2(); |
| const A.a2() : x = 5; |
| final int x; |
| }'''); |
| Map<String, DartObjectImpl> aFields = |
| _assertType(_evaluateTopLevelVariable(compilationUnit, "foo"), "A"); |
| _assertIntField(aFields, 'x', 5); |
| } |
| |
| test_instanceCreationExpression_nonFactoryRedirect_arg() async { |
| CompilationUnit compilationUnit = await resolveSource(r''' |
| const foo = const A.a1(1); |
| class A { |
| const A.a1(x) : this.a2(x + 100); |
| const A.a2(x) : y = x + 10; |
| final int y; |
| }'''); |
| Map<String, DartObjectImpl> aFields = |
| _assertType(_evaluateTopLevelVariable(compilationUnit, "foo"), "A"); |
| _assertIntField(aFields, 'y', 111); |
| } |
| |
| test_instanceCreationExpression_nonFactoryRedirect_cycle() async { |
| // It is an error to have a cycle in non-factory redirects; however, we |
| // need to make sure that even if the error occurs, attempting to evaluate |
| // the constant will terminate. |
| CompilationUnit compilationUnit = await resolveSource(r''' |
| const foo = const A(); |
| class A { |
| const A() : this.b(); |
| const A.b() : this(); |
| }'''); |
| _assertValidUnknown(_evaluateTopLevelVariable(compilationUnit, "foo")); |
| } |
| |
| test_instanceCreationExpression_nonFactoryRedirect_defaultArg() async { |
| CompilationUnit compilationUnit = await resolveSource(r''' |
| const foo = const A.a1(); |
| class A { |
| const A.a1() : this.a2(); |
| const A.a2([x = 100]) : y = x + 10; |
| final int y; |
| }'''); |
| Map<String, DartObjectImpl> aFields = |
| _assertType(_evaluateTopLevelVariable(compilationUnit, "foo"), "A"); |
| _assertIntField(aFields, 'y', 110); |
| } |
| |
| test_instanceCreationExpression_nonFactoryRedirect_toMissing() async { |
| CompilationUnit compilationUnit = await resolveSource(r''' |
| const foo = const A.a1(); |
| class A { |
| const A.a1() : this.a2(); |
| }'''); |
| // We don't care what value foo evaluates to (since there is a compile |
| // error), but we shouldn't crash, and we should figure |
| // out that it evaluates to an instance of class A. |
| _assertType(_evaluateTopLevelVariable(compilationUnit, "foo"), "A"); |
| } |
| |
| test_instanceCreationExpression_nonFactoryRedirect_toNonConst() async { |
| CompilationUnit compilationUnit = await resolveSource(r''' |
| const foo = const A.a1(); |
| class A { |
| const A.a1() : this.a2(); |
| A.a2(); |
| }'''); |
| // We don't care what value foo evaluates to (since there is a compile |
| // error), but we shouldn't crash, and we should figure |
| // out that it evaluates to an instance of class A. |
| _assertType(_evaluateTopLevelVariable(compilationUnit, "foo"), "A"); |
| } |
| |
| test_instanceCreationExpression_nonFactoryRedirect_unnamed() async { |
| CompilationUnit compilationUnit = await resolveSource(r''' |
| const foo = const A.a1(); |
| class A { |
| const A.a1() : this(); |
| const A() : x = 5; |
| final int x; |
| }'''); |
| Map<String, DartObjectImpl> aFields = |
| _assertType(_evaluateTopLevelVariable(compilationUnit, "foo"), "A"); |
| _assertIntField(aFields, 'x', 5); |
| } |
| |
| test_instanceCreationExpression_redirect() async { |
| CompilationUnit compilationUnit = await resolveSource(r''' |
| const foo = const A(); |
| class A { |
| const factory A() = B; |
| } |
| class B implements A { |
| const B(); |
| }'''); |
| _assertType(_evaluateTopLevelVariable(compilationUnit, "foo"), "B"); |
| } |
| |
| test_instanceCreationExpression_redirect_cycle() async { |
| // It is an error to have a cycle in factory redirects; however, we need |
| // to make sure that even if the error occurs, attempting to evaluate the |
| // constant will terminate. |
| CompilationUnit compilationUnit = await resolveSource(r''' |
| const foo = const A(); |
| class A { |
| const factory A() = A.b; |
| const factory A.b() = A; |
| }'''); |
| _assertValidUnknown(_evaluateTopLevelVariable(compilationUnit, "foo")); |
| } |
| |
| test_instanceCreationExpression_redirect_external() async { |
| CompilationUnit compilationUnit = await resolveSource(r''' |
| const foo = const A(); |
| class A { |
| external const factory A(); |
| }'''); |
| _assertValidUnknown(_evaluateTopLevelVariable(compilationUnit, "foo")); |
| } |
| |
| test_instanceCreationExpression_redirect_generic() async { |
| CompilationUnit compilationUnit = await resolveSource(r''' |
| const foo = const A<int>(); |
| class A<T> { |
| const A() : this._(); |
| const A._(); |
| } |
| '''); |
| _assertType(_evaluateTopLevelVariable(compilationUnit, 'foo'), 'A<int>'); |
| } |
| |
| test_instanceCreationExpression_redirect_nonConst() async { |
| // It is an error for a const factory constructor redirect to a non-const |
| // constructor; however, we need to make sure that even if the error |
| // attempting to evaluate the constant won't cause a crash. |
| CompilationUnit compilationUnit = await resolveSource(r''' |
| const foo = const A(); |
| class A { |
| const factory A() = A.b; |
| A.b(); |
| }'''); |
| _assertValidUnknown(_evaluateTopLevelVariable(compilationUnit, "foo")); |
| } |
| |
| test_instanceCreationExpression_redirectWithTypeParams() async { |
| CompilationUnit compilationUnit = await resolveSource(r''' |
| class A { |
| const factory A(var a) = B<int>; |
| } |
| |
| class B<T> implements A { |
| final T x; |
| const B(this.x); |
| } |
| |
| const A a = const A(10);'''); |
| EvaluationResultImpl result = |
| _evaluateTopLevelVariable(compilationUnit, "a"); |
| Map<String, DartObjectImpl> fields = _assertType(result, "B<int>"); |
| expect(fields, hasLength(1)); |
| _assertIntField(fields, "x", 10); |
| } |
| |
| test_instanceCreationExpression_redirectWithTypeSubstitution() async { |
| // To evaluate the redirection of A<int>, |
| // A's template argument (T=int) must be substituted |
| // into B's template argument (B<U> where U=T) to get B<int>. |
| CompilationUnit compilationUnit = await resolveSource(r''' |
| class A<T> { |
| const factory A(var a) = B<T>; |
| } |
| |
| class B<U> implements A { |
| final U x; |
| const B(this.x); |
| } |
| |
| const A<int> a = const A<int>(10);'''); |
| EvaluationResultImpl result = |
| _evaluateTopLevelVariable(compilationUnit, "a"); |
| Map<String, DartObjectImpl> fields = _assertType(result, "B<int>"); |
| expect(fields, hasLength(1)); |
| _assertIntField(fields, "x", 10); |
| } |
| |
| test_instanceCreationExpression_symbol() async { |
| CompilationUnit compilationUnit = |
| await resolveSource("const foo = const Symbol('a');"); |
| EvaluationResultImpl evaluationResult = |
| _evaluateTopLevelVariable(compilationUnit, "foo"); |
| expect(evaluationResult.value, isNotNull); |
| DartObjectImpl value = evaluationResult.value; |
| expect(value.type, typeProvider.symbolType); |
| expect(value.toSymbolValue(), "a"); |
| } |
| |
| test_instanceCreationExpression_withSupertypeParams_explicit() async { |
| await _checkInstanceCreation_withSupertypeParams(true); |
| } |
| |
| test_instanceCreationExpression_withSupertypeParams_implicit() async { |
| await _checkInstanceCreation_withSupertypeParams(false); |
| } |
| |
| test_instanceCreationExpression_withTypeParams() async { |
| CompilationUnit compilationUnit = await resolveSource(r''' |
| class C<E> { |
| const C(); |
| } |
| const c_int = const C<int>(); |
| const c_num = const C<num>();'''); |
| EvaluationResultImpl c_int = |
| _evaluateTopLevelVariable(compilationUnit, "c_int"); |
| _assertType(c_int, "C<int>"); |
| DartObjectImpl c_int_value = c_int.value; |
| EvaluationResultImpl c_num = |
| _evaluateTopLevelVariable(compilationUnit, "c_num"); |
| _assertType(c_num, "C<num>"); |
| DartObjectImpl c_num_value = c_num.value; |
| expect(c_int_value == c_num_value, isFalse); |
| } |
| |
| test_isValidSymbol() async { |
| expect(ConstantEvaluationEngine.isValidPublicSymbol(""), isTrue); |
| expect(ConstantEvaluationEngine.isValidPublicSymbol("foo"), isTrue); |
| expect(ConstantEvaluationEngine.isValidPublicSymbol("foo.bar"), isTrue); |
| expect(ConstantEvaluationEngine.isValidPublicSymbol("foo\$"), isTrue); |
| expect(ConstantEvaluationEngine.isValidPublicSymbol("foo\$bar"), isTrue); |
| expect(ConstantEvaluationEngine.isValidPublicSymbol("iff"), isTrue); |
| expect(ConstantEvaluationEngine.isValidPublicSymbol("gif"), isTrue); |
| expect(ConstantEvaluationEngine.isValidPublicSymbol("if\$"), isTrue); |
| expect(ConstantEvaluationEngine.isValidPublicSymbol("\$if"), isTrue); |
| expect(ConstantEvaluationEngine.isValidPublicSymbol("foo="), isTrue); |
| expect(ConstantEvaluationEngine.isValidPublicSymbol("foo.bar="), isTrue); |
| expect(ConstantEvaluationEngine.isValidPublicSymbol("foo.+"), isTrue); |
| expect(ConstantEvaluationEngine.isValidPublicSymbol("void"), isTrue); |
| expect(ConstantEvaluationEngine.isValidPublicSymbol("_foo"), isFalse); |
| expect(ConstantEvaluationEngine.isValidPublicSymbol("_foo.bar"), isFalse); |
| expect(ConstantEvaluationEngine.isValidPublicSymbol("foo._bar"), isFalse); |
| expect(ConstantEvaluationEngine.isValidPublicSymbol("if"), isFalse); |
| expect(ConstantEvaluationEngine.isValidPublicSymbol("if.foo"), isFalse); |
| expect(ConstantEvaluationEngine.isValidPublicSymbol("foo.if"), isFalse); |
| expect(ConstantEvaluationEngine.isValidPublicSymbol("foo=.bar"), isFalse); |
| expect(ConstantEvaluationEngine.isValidPublicSymbol("foo."), isFalse); |
| expect(ConstantEvaluationEngine.isValidPublicSymbol("+.foo"), isFalse); |
| expect(ConstantEvaluationEngine.isValidPublicSymbol("void.foo"), isFalse); |
| expect(ConstantEvaluationEngine.isValidPublicSymbol("foo.void"), isFalse); |
| } |
| |
| test_length_of_improperly_typed_string_expression() async { |
| // Since type annotations are ignored in unchecked mode, the improper |
| // types on s1 and s2 shouldn't prevent us from evaluating i to |
| // 'alpha'.length. |
| CompilationUnit compilationUnit = await resolveSource(''' |
| const int s1 = 'alpha'; |
| const int s2 = 'beta'; |
| const int i = (true ? s1 : s2).length; |
| '''); |
| ConstTopLevelVariableElementImpl element = |
| findTopLevelDeclaration(compilationUnit, 'i').declaredElement; |
| EvaluationResultImpl result = element.evaluationResult; |
| expect(_assertValidInt(result), 5); |
| } |
| |
| test_length_of_improperly_typed_string_identifier() async { |
| // Since type annotations are ignored in unchecked mode, the improper type |
| // on s shouldn't prevent us from evaluating i to 'alpha'.length. |
| CompilationUnit compilationUnit = await resolveSource(''' |
| const int s = 'alpha'; |
| const int i = s.length; |
| '''); |
| ConstTopLevelVariableElementImpl element = |
| findTopLevelDeclaration(compilationUnit, 'i').declaredElement; |
| EvaluationResultImpl result = element.evaluationResult; |
| expect(_assertValidInt(result), 5); |
| } |
| |
| test_non_static_const_initialized_at_declaration() async { |
| // Even though non-static consts are not allowed by the language, we need |
| // to handle them for error recovery purposes. |
| CompilationUnit compilationUnit = await resolveSource(''' |
| class A { |
| const int i = 123; |
| const A(); |
| } |
| |
| const A a = const A(); |
| '''); |
| EvaluationResultImpl result = |
| _evaluateTopLevelVariable(compilationUnit, 'a'); |
| Map<String, DartObjectImpl> fields = _assertType(result, "A"); |
| expect(fields, hasLength(1)); |
| _assertIntField(fields, "i", 123); |
| } |
| |
| test_symbolLiteral_void() async { |
| CompilationUnit compilationUnit = |
| await resolveSource("const voidSymbol = #void;"); |
| VariableDeclaration voidSymbol = |
| findTopLevelDeclaration(compilationUnit, "voidSymbol"); |
| EvaluationResultImpl voidSymbolResult = |
| (voidSymbol.declaredElement as VariableElementImpl).evaluationResult; |
| DartObjectImpl value = voidSymbolResult.value; |
| expect(value.type, typeProvider.symbolType); |
| expect(value.toSymbolValue(), "void"); |
| } |
| |
| Map<String, DartObjectImpl> _assertFieldType( |
| Map<String, DartObjectImpl> fields, |
| String fieldName, |
| String expectedType) { |
| DartObjectImpl field = fields[fieldName]; |
| expect(field.type.displayName, expectedType); |
| return field.fields; |
| } |
| |
| void _assertIntField( |
| Map<String, DartObjectImpl> fields, String fieldName, int expectedValue) { |
| DartObjectImpl field = fields[fieldName]; |
| expect(field.type.name, "int"); |
| expect(field.toIntValue(), expectedValue); |
| } |
| |
| void _assertNullField(Map<String, DartObjectImpl> fields, String fieldName) { |
| DartObjectImpl field = fields[fieldName]; |
| expect(field.isNull, isTrue); |
| } |
| |
| Future<Null> _assertProperDependencies(String sourceText, |
| [List<ErrorCode> expectedErrorCodes = const <ErrorCode>[]]) async { |
| Source source = addSource(sourceText); |
| LibraryElement element = resolve2(source); |
| CompilationUnit unit = |
| analysisContext.resolveCompilationUnit(source, element); |
| expect(unit, isNotNull); |
| ConstantValueComputer computer = _makeConstantValueComputer(); |
| computer.add(unit); |
| computer.computeValues(); |
| await computeAnalysisResult(source); |
| assertErrors(source, expectedErrorCodes); |
| } |
| |
| Map<String, DartObjectImpl> _assertType( |
| EvaluationResultImpl result, String typeName) { |
| expect(result.value, isNotNull); |
| DartObjectImpl value = result.value; |
| expect(value.type.displayName, typeName); |
| return value.fields; |
| } |
| |
| bool _assertValidBool(EvaluationResultImpl result) { |
| expect(result.value, isNotNull); |
| DartObjectImpl value = result.value; |
| expect(value.type, typeProvider.boolType); |
| bool boolValue = value.toBoolValue(); |
| expect(boolValue, isNotNull); |
| return boolValue; |
| } |
| |
| int _assertValidInt(EvaluationResultImpl result) { |
| expect(result, isNotNull); |
| expect(result.value, isNotNull); |
| DartObjectImpl value = result.value; |
| expect(value.type, typeProvider.intType); |
| return value.toIntValue(); |
| } |
| |
| void _assertValidNull(EvaluationResultImpl result) { |
| expect(result.value, isNotNull); |
| DartObjectImpl value = result.value; |
| expect(value.type, typeProvider.nullType); |
| } |
| |
| String _assertValidString(EvaluationResultImpl result) { |
| expect(result.value, isNotNull); |
| DartObjectImpl value = result.value; |
| expect(value.type, typeProvider.stringType); |
| return value.toStringValue(); |
| } |
| |
| void _assertValidUnknown(EvaluationResultImpl result) { |
| expect(result.value, isNotNull); |
| DartObjectImpl value = result.value; |
| expect(value.isUnknown, isTrue); |
| } |
| |
| Future<EvaluationResultImpl> _check_fromEnvironment_bool( |
| String valueInEnvironment, String defaultExpr) async { |
| String envVarName = "x"; |
| String varName = "foo"; |
| if (valueInEnvironment != null) { |
| if (enableNewAnalysisDriver) { |
| driver.declaredVariables = |
| new DeclaredVariables.fromMap({envVarName: valueInEnvironment}); |
| } else { |
| (analysisContext2 as AnalysisContextImpl).declaredVariables = |
| new DeclaredVariables.fromMap({envVarName: valueInEnvironment}); |
| } |
| } |
| String defaultArg = |
| defaultExpr == null ? "" : ", defaultValue: $defaultExpr"; |
| CompilationUnit compilationUnit = await resolveSource( |
| "const $varName = const bool.fromEnvironment('$envVarName'$defaultArg);"); |
| return _evaluateTopLevelVariable(compilationUnit, varName); |
| } |
| |
| Future<EvaluationResultImpl> _check_fromEnvironment_int( |
| String valueInEnvironment, String defaultExpr) async { |
| String envVarName = "x"; |
| String varName = "foo"; |
| if (valueInEnvironment != null) { |
| if (enableNewAnalysisDriver) { |
| driver.declaredVariables = |
| new DeclaredVariables.fromMap({envVarName: valueInEnvironment}); |
| } else { |
| (analysisContext2 as AnalysisContextImpl).declaredVariables = |
| new DeclaredVariables.fromMap({envVarName: valueInEnvironment}); |
| } |
| } |
| String defaultArg = |
| defaultExpr == null ? "" : ", defaultValue: $defaultExpr"; |
| CompilationUnit compilationUnit = await resolveSource( |
| "const $varName = const int.fromEnvironment('$envVarName'$defaultArg);"); |
| return _evaluateTopLevelVariable(compilationUnit, varName); |
| } |
| |
| Future<EvaluationResultImpl> _check_fromEnvironment_string( |
| String valueInEnvironment, String defaultExpr) async { |
| String envVarName = "x"; |
| String varName = "foo"; |
| if (valueInEnvironment != null) { |
| if (enableNewAnalysisDriver) { |
| driver.declaredVariables = |
| new DeclaredVariables.fromMap({envVarName: valueInEnvironment}); |
| } else { |
| (analysisContext2 as AnalysisContextImpl).declaredVariables = |
| new DeclaredVariables.fromMap({envVarName: valueInEnvironment}); |
| } |
| } |
| String defaultArg = |
| defaultExpr == null ? "" : ", defaultValue: $defaultExpr"; |
| CompilationUnit compilationUnit = await resolveSource( |
| "const $varName = const String.fromEnvironment('$envVarName'$defaultArg);"); |
| return _evaluateTopLevelVariable(compilationUnit, varName); |
| } |
| |
| Future<Null> _checkInstanceCreation_withSupertypeParams( |
| bool isExplicit) async { |
| String superCall = isExplicit ? " : super()" : ""; |
| CompilationUnit compilationUnit = await resolveSource(""" |
| class A<T> { |
| const A(); |
| } |
| class B<T, U> extends A<T> { |
| const B()$superCall; |
| } |
| class C<T, U> extends A<U> { |
| const C()$superCall; |
| } |
| const b_int_num = const B<int, num>(); |
| const c_int_num = const C<int, num>();"""); |
| EvaluationResultImpl b_int_num = |
| _evaluateTopLevelVariable(compilationUnit, "b_int_num"); |
| Map<String, DartObjectImpl> b_int_num_fields = |
| _assertType(b_int_num, "B<int, num>"); |
| _assertFieldType(b_int_num_fields, GenericState.SUPERCLASS_FIELD, "A<int>"); |
| EvaluationResultImpl c_int_num = |
| _evaluateTopLevelVariable(compilationUnit, "c_int_num"); |
| Map<String, DartObjectImpl> c_int_num_fields = |
| _assertType(c_int_num, "C<int, num>"); |
| _assertFieldType(c_int_num_fields, GenericState.SUPERCLASS_FIELD, "A<num>"); |
| } |
| |
| Future<Null> _checkInstanceCreationOptionalParams( |
| bool isFieldFormal, bool isNamed, bool hasDefault) async { |
| String fieldName = "j"; |
| String paramName = isFieldFormal ? fieldName : "i"; |
| String formalParam = |
| "${isFieldFormal ? "this." : "int "}$paramName${hasDefault ? " = 3" : ""}"; |
| CompilationUnit compilationUnit = await resolveSource(""" |
| const x = const A(); |
| const y = const A(${isNamed ? '$paramName: ' : ''}10); |
| class A { |
| const A(${isNamed ? "{$formalParam}" : "[$formalParam]"})${isFieldFormal ? "" : " : $fieldName = $paramName"}; |
| final int $fieldName; |
| }"""); |
| EvaluationResultImpl x = _evaluateTopLevelVariable(compilationUnit, "x"); |
| Map<String, DartObjectImpl> fieldsOfX = _assertType(x, "A"); |
| expect(fieldsOfX, hasLength(1)); |
| if (hasDefault) { |
| _assertIntField(fieldsOfX, fieldName, 3); |
| } else { |
| _assertNullField(fieldsOfX, fieldName); |
| } |
| EvaluationResultImpl y = _evaluateTopLevelVariable(compilationUnit, "y"); |
| Map<String, DartObjectImpl> fieldsOfY = _assertType(y, "A"); |
| expect(fieldsOfY, hasLength(1)); |
| _assertIntField(fieldsOfY, fieldName, 10); |
| } |
| |
| /** |
| * Search [compilationUnit] for a class named [className], containing a |
| * method [methodName], with exactly one annotation. Return the constant |
| * value of the annotation. |
| */ |
| EvaluationResultImpl _evaluateAnnotation( |
| CompilationUnit compilationUnit, String className, String memberName) { |
| for (CompilationUnitMember member in compilationUnit.declarations) { |
| if (member is ClassDeclaration && member.name.name == className) { |
| for (ClassMember classMember in member.members) { |
| if (classMember is MethodDeclaration && |
| classMember.name.name == memberName) { |
| expect(classMember.metadata, hasLength(1)); |
| ElementAnnotationImpl elementAnnotation = |
| classMember.metadata[0].elementAnnotation; |
| return elementAnnotation.evaluationResult; |
| } |
| } |
| } |
| } |
| fail('Class member not found'); |
| } |
| |
| EvaluationResultImpl _evaluateTopLevelVariable( |
| CompilationUnit compilationUnit, String name) { |
| VariableDeclaration varDecl = |
| findTopLevelDeclaration(compilationUnit, name); |
| ConstTopLevelVariableElementImpl varElement = varDecl.declaredElement; |
| return varElement.evaluationResult; |
| } |
| |
| ConstantValueComputer _makeConstantValueComputer() { |
| ConstantEvaluationValidator_ForTest validator = |
| new ConstantEvaluationValidator_ForTest(analysisContext2); |
| validator.computer = new ConstantValueComputer( |
| analysisContext2.typeProvider, |
| analysisContext2.declaredVariables, |
| validator, |
| analysisContext2.typeSystem); |
| return validator.computer; |
| } |
| |
| void _validate(bool shouldBeValid, VariableDeclarationList declarationList) { |
| for (VariableDeclaration declaration in declarationList.variables) { |
| VariableElementImpl element = |
| declaration.declaredElement as VariableElementImpl; |
| expect(element, isNotNull); |
| EvaluationResultImpl result = element.evaluationResult; |
| if (shouldBeValid) { |
| expect(result.value, isNotNull); |
| } else { |
| expect(result.value, isNull); |
| } |
| } |
| } |
| } |
| |
| @reflectiveTest |
| class ConstantVisitorTest extends ResolverTestCase { |
| test_visitBinaryExpression_questionQuestion_notNull_notNull() async { |
| Expression left = AstTestFactory.string2('a'); |
| Expression right = AstTestFactory.string2('b'); |
| Expression expression = AstTestFactory.binaryExpression( |
| left, TokenType.QUESTION_QUESTION, right); |
| |
| GatheringErrorListener errorListener = new GatheringErrorListener(); |
| ErrorReporter errorReporter = |
| new ErrorReporter(errorListener, _dummySource()); |
| DartObjectImpl result = _evaluate(expression, errorReporter); |
| expect(result, isNotNull); |
| expect(result.isNull, isFalse); |
| expect(result.toStringValue(), 'a'); |
| errorListener.assertNoErrors(); |
| } |
| |
| test_visitBinaryExpression_questionQuestion_null_notNull() async { |
| Expression left = AstTestFactory.nullLiteral(); |
| Expression right = AstTestFactory.string2('b'); |
| Expression expression = AstTestFactory.binaryExpression( |
| left, TokenType.QUESTION_QUESTION, right); |
| |
| GatheringErrorListener errorListener = new GatheringErrorListener(); |
| ErrorReporter errorReporter = |
| new ErrorReporter(errorListener, _dummySource()); |
| DartObjectImpl result = _evaluate(expression, errorReporter); |
| expect(result, isNotNull); |
| expect(result.isNull, isFalse); |
| expect(result.toStringValue(), 'b'); |
| errorListener.assertNoErrors(); |
| } |
| |
| test_visitBinaryExpression_questionQuestion_null_null() async { |
| Expression left = AstTestFactory.nullLiteral(); |
| Expression right = AstTestFactory.nullLiteral(); |
| Expression expression = AstTestFactory.binaryExpression( |
| left, TokenType.QUESTION_QUESTION, right); |
| |
| GatheringErrorListener errorListener = new GatheringErrorListener(); |
| ErrorReporter errorReporter = |
| new ErrorReporter(errorListener, _dummySource()); |
| DartObjectImpl result = _evaluate(expression, errorReporter); |
| expect(result, isNotNull); |
| expect(result.isNull, isTrue); |
| errorListener.assertNoErrors(); |
| } |
| |
| test_visitConditionalExpression_false() async { |
| Expression thenExpression = AstTestFactory.integer(1); |
| Expression elseExpression = AstTestFactory.integer(0); |
| ConditionalExpression expression = AstTestFactory.conditionalExpression( |
| AstTestFactory.booleanLiteral(false), thenExpression, elseExpression); |
| GatheringErrorListener errorListener = new GatheringErrorListener(); |
| ErrorReporter errorReporter = |
| new ErrorReporter(errorListener, _dummySource()); |
| _assertValue(0, _evaluate(expression, errorReporter)); |
| errorListener.assertNoErrors(); |
| } |
| |
| test_visitConditionalExpression_nonBooleanCondition() async { |
| Expression thenExpression = AstTestFactory.integer(1); |
| Expression elseExpression = AstTestFactory.integer(0); |
| NullLiteral conditionExpression = AstTestFactory.nullLiteral(); |
| ConditionalExpression expression = AstTestFactory.conditionalExpression( |
| conditionExpression, thenExpression, elseExpression); |
| GatheringErrorListener errorListener = new GatheringErrorListener(); |
| ErrorReporter errorReporter = |
| new ErrorReporter(errorListener, _dummySource()); |
| DartObjectImpl result = _evaluate(expression, errorReporter); |
| expect(result, isNull); |
| errorListener |
| .assertErrorsWithCodes([CompileTimeErrorCode.CONST_EVAL_TYPE_BOOL]); |
| } |
| |
| test_visitConditionalExpression_nonConstantElse() async { |
| Expression thenExpression = AstTestFactory.integer(1); |
| Expression elseExpression = AstTestFactory.identifier3("x"); |
| ConditionalExpression expression = AstTestFactory.conditionalExpression( |
| AstTestFactory.booleanLiteral(true), thenExpression, elseExpression); |
| GatheringErrorListener errorListener = new GatheringErrorListener(); |
| ErrorReporter errorReporter = |
| new ErrorReporter(errorListener, _dummySource()); |
| DartObjectImpl result = _evaluate(expression, errorReporter); |
| expect(result, isNull); |
| errorListener |
| .assertErrorsWithCodes([CompileTimeErrorCode.INVALID_CONSTANT]); |
| } |
| |
| test_visitConditionalExpression_nonConstantThen() async { |
| Expression thenExpression = AstTestFactory.identifier3("x"); |
| Expression elseExpression = AstTestFactory.integer(0); |
| ConditionalExpression expression = AstTestFactory.conditionalExpression( |
| AstTestFactory.booleanLiteral(true), thenExpression, elseExpression); |
| GatheringErrorListener errorListener = new GatheringErrorListener(); |
| ErrorReporter errorReporter = |
| new ErrorReporter(errorListener, _dummySource()); |
| DartObjectImpl result = _evaluate(expression, errorReporter); |
| expect(result, isNull); |
| errorListener |
| .assertErrorsWithCodes([CompileTimeErrorCode.INVALID_CONSTANT]); |
| } |
| |
| test_visitConditionalExpression_true() async { |
| Expression thenExpression = AstTestFactory.integer(1); |
| Expression elseExpression = AstTestFactory.integer(0); |
| ConditionalExpression expression = AstTestFactory.conditionalExpression( |
| AstTestFactory.booleanLiteral(true), thenExpression, elseExpression); |
| GatheringErrorListener errorListener = new GatheringErrorListener(); |
| ErrorReporter errorReporter = |
| new ErrorReporter(errorListener, _dummySource()); |
| _assertValue(1, _evaluate(expression, errorReporter)); |
| errorListener.assertNoErrors(); |
| } |
| |
| test_visitSimpleIdentifier_className() async { |
| CompilationUnit compilationUnit = await resolveSource(''' |
| const a = C; |
| class C {} |
| '''); |
| DartObjectImpl result = _evaluateConstant(compilationUnit, 'a', null); |
| expect(result.type, typeProvider.typeType); |
| expect(result.toTypeValue().name, 'C'); |
| } |
| |
| test_visitSimpleIdentifier_dynamic() async { |
| CompilationUnit compilationUnit = await resolveSource(''' |
| const a = dynamic; |
| '''); |
| DartObjectImpl result = _evaluateConstant(compilationUnit, 'a', null); |
| expect(result.type, typeProvider.typeType); |
| expect(result.toTypeValue(), typeProvider.dynamicType); |
| } |
| |
| test_visitSimpleIdentifier_inEnvironment() async { |
| CompilationUnit compilationUnit = await resolveSource(r''' |
| const a = b; |
| const b = 3;'''); |
| Map<String, DartObjectImpl> environment = new Map<String, DartObjectImpl>(); |
| DartObjectImpl six = |
| new DartObjectImpl(typeProvider.intType, new IntState(6)); |
| environment["b"] = six; |
| _assertValue(6, _evaluateConstant(compilationUnit, "a", environment)); |
| } |
| |
| test_visitSimpleIdentifier_notInEnvironment() async { |
| CompilationUnit compilationUnit = await resolveSource(r''' |
| const a = b; |
| const b = 3;'''); |
| Map<String, DartObjectImpl> environment = new Map<String, DartObjectImpl>(); |
| DartObjectImpl six = |
| new DartObjectImpl(typeProvider.intType, new IntState(6)); |
| environment["c"] = six; |
| _assertValue(3, _evaluateConstant(compilationUnit, "a", environment)); |
| } |
| |
| test_visitSimpleIdentifier_withoutEnvironment() async { |
| CompilationUnit compilationUnit = await resolveSource(r''' |
| const a = b; |
| const b = 3;'''); |
| _assertValue(3, _evaluateConstant(compilationUnit, "a", null)); |
| } |
| |
| void _assertValue(int expectedValue, DartObjectImpl result) { |
| expect(result, isNotNull); |
| expect(result.type.name, "int"); |
| expect(result.toIntValue(), expectedValue); |
| } |
| |
| NonExistingSource _dummySource() { |
| String path = '/test.dart'; |
| return new NonExistingSource(path, toUri(path), UriKind.FILE_URI); |
| } |
| |
| DartObjectImpl _evaluate(Expression expression, ErrorReporter errorReporter) { |
| TestTypeProvider typeProvider = new TestTypeProvider(); |
| return expression.accept(new ConstantVisitor( |
| new ConstantEvaluationEngine(typeProvider, new DeclaredVariables(), |
| typeSystem: new StrongTypeSystemImpl(typeProvider)), |
| errorReporter)); |
| } |
| |
| DartObjectImpl _evaluateConstant(CompilationUnit compilationUnit, String name, |
| Map<String, DartObjectImpl> lexicalEnvironment) { |
| Source source = |
| resolutionMap.elementDeclaredByCompilationUnit(compilationUnit).source; |
| Expression expression = |
| findTopLevelConstantExpression(compilationUnit, name); |
| GatheringErrorListener errorListener = new GatheringErrorListener(); |
| ErrorReporter errorReporter = new ErrorReporter(errorListener, source); |
| DartObjectImpl result = expression.accept(new ConstantVisitor( |
| new ConstantEvaluationEngine(typeProvider, new DeclaredVariables(), |
| typeSystem: typeSystem), |
| errorReporter, |
| lexicalEnvironment: lexicalEnvironment)); |
| errorListener.assertNoErrors(); |
| return result; |
| } |
| } |
| |
| @reflectiveTest |
| class StrongConstantValueComputerTest extends ConstantValueComputerTest { |
| void setUp() { |
| super.setUp(); |
| reset(); |
| } |
| } |