diff --git a/pkg/dev_compiler/lib/src/kernel/compiler.dart b/pkg/dev_compiler/lib/src/kernel/compiler.dart index 6076be4cde5..1fb8297465c 100644 --- a/pkg/dev_compiler/lib/src/kernel/compiler.dart +++ b/pkg/dev_compiler/lib/src/kernel/compiler.dart @@ -4744,6 +4744,12 @@ class ProgramCompiler extends Object return _emitConstMap(node.keyType, node.valueType, entries); } + @override + visitInstanceCreation(InstanceCreation node) { + // Only occurs inside unevaluated constants. + throw new UnsupportedError("Instance creation"); + } + @override visitIsExpression(IsExpression node) { return _emitIsExpression(node.operand, node.type); diff --git a/pkg/kernel/binary.md b/pkg/kernel/binary.md index 91d9ae17b56..76c12be3680 100644 --- a/pkg/kernel/binary.md +++ b/pkg/kernel/binary.md @@ -139,7 +139,7 @@ type CanonicalName { type ComponentFile { UInt32 magic = 0x90ABCDEF; - UInt32 formatVersion = 22; + UInt32 formatVersion = 23; List problemsAsJson; // Described in problems.md. Library[] libraries; UriSource sourceMap; @@ -720,6 +720,15 @@ type MapConcatenation extends Expression { List maps; } +type InstanceCreation extends Expression { + Byte tag = 114; + FileOffset fileOffset; + CanonicalNameReference class; + List typeArguments; + List<[FieldReference, Expression]> fieldValues; + List asserts; +} + type IsExpression extends Expression { Byte tag = 37; FileOffset fileOffset; diff --git a/pkg/kernel/lib/ast.dart b/pkg/kernel/lib/ast.dart index 8e41e89e74b..b1a9807c1f4 100644 --- a/pkg/kernel/lib/ast.dart +++ b/pkg/kernel/lib/ast.dart @@ -3314,6 +3314,56 @@ class MapConcatenation extends Expression { } } +/// Create an instance directly from the field values. +/// +/// This expression arises from const constructor calls when one or more field +/// initializing expressions, field initializers or assert initializers contain +/// unevaluated expressions. They only ever occur within unevaluated constants +/// in constant expressions. +class InstanceCreation extends Expression { + final Reference classReference; + final List typeArguments; + final Map fieldValues; + final List asserts; + + InstanceCreation( + this.classReference, this.typeArguments, this.fieldValues, this.asserts); + + Class get classNode => classReference.asClass; + + DartType getStaticType(TypeEnvironment types) { + return typeArguments.isEmpty + ? classNode.rawType + : new InterfaceType(classNode, typeArguments); + } + + accept(ExpressionVisitor v) => v.visitInstanceCreation(this); + accept1(ExpressionVisitor1 v, arg) => v.visitInstanceCreation(this, arg); + + visitChildren(Visitor v) { + classReference.asClass.acceptReference(v); + visitList(typeArguments, v); + for (final Reference reference in fieldValues.keys) { + reference.asField.acceptReference(v); + } + for (final Expression value in fieldValues.values) { + value.accept(v); + } + visitList(asserts, v); + } + + transformChildren(Transformer v) { + fieldValues.forEach((Reference fieldRef, Expression value) { + Expression transformed = value.accept(v); + if (transformed != null && !identical(value, transformed)) { + fieldValues[fieldRef] = transformed; + transformed.parent = this; + } + }); + transformList(asserts, v, this); + } +} + /// Expression of form `x is T`. class IsExpression extends Expression { Expression operand; diff --git a/pkg/kernel/lib/binary/ast_from_binary.dart b/pkg/kernel/lib/binary/ast_from_binary.dart index 6306cef493c..ad088c0194f 100644 --- a/pkg/kernel/lib/binary/ast_from_binary.dart +++ b/pkg/kernel/lib/binary/ast_from_binary.dart @@ -1534,6 +1534,26 @@ class BinaryBuilder { return new MapConcatenation(readExpressionList(), keyType: keyType, valueType: valueType) ..fileOffset = offset; + case Tag.InstanceCreation: + int offset = readOffset(); + Reference classReference = readClassReference(); + List typeArguments = readDartTypeList(); + int fieldValueCount = readUInt(); + Map fieldValues = {}; + for (int i = 0; i < fieldValueCount; i++) { + final Reference fieldRef = + readCanonicalNameReference().getReference(); + final Expression value = readExpression(); + fieldValues[fieldRef] = value; + } + int assertCount = readUInt(); + List asserts = new List(assertCount); + for (int i = 0; i < assertCount; i++) { + asserts[i] = readStatement(); + } + return new InstanceCreation( + classReference, typeArguments, fieldValues, asserts) + ..fileOffset = offset; case Tag.IsExpression: int offset = readOffset(); return new IsExpression(readExpression(), readDartType()) diff --git a/pkg/kernel/lib/binary/ast_to_binary.dart b/pkg/kernel/lib/binary/ast_to_binary.dart index a0619f62fe0..2b9baba1d88 100644 --- a/pkg/kernel/lib/binary/ast_to_binary.dart +++ b/pkg/kernel/lib/binary/ast_to_binary.dart @@ -1502,6 +1502,20 @@ class BinaryPrinter implements Visitor, BinarySink { writeNodeList(node.maps); } + @override + void visitInstanceCreation(InstanceCreation node) { + writeByte(Tag.InstanceCreation); + writeOffset(node.fileOffset); + writeNonNullReference(node.classReference); + writeNodeList(node.typeArguments); + writeUInt30(node.fieldValues.length); + node.fieldValues.forEach((Reference fieldRef, Expression value) { + writeNonNullReference(fieldRef); + writeNode(value); + }); + writeNodeList(node.asserts); + } + @override void visitIsExpression(IsExpression node) { writeByte(Tag.IsExpression); diff --git a/pkg/kernel/lib/binary/tag.dart b/pkg/kernel/lib/binary/tag.dart index 9650fa76d5f..42200e184cb 100644 --- a/pkg/kernel/lib/binary/tag.dart +++ b/pkg/kernel/lib/binary/tag.dart @@ -53,6 +53,7 @@ class Tag { static const int ListConcatenation = 111; static const int SetConcatenation = 112; static const int MapConcatenation = 113; + static const int InstanceCreation = 114; static const int IsExpression = 37; static const int AsExpression = 38; static const int StringLiteral = 39; @@ -129,6 +130,7 @@ class Tag { /// 111 is occupied by [ListConcatenation] (expression). /// 112 is occupied by [SetConcatenation] (expression). /// 113 is occupied by [MapConcatenation] (expression). + /// 114 is occupied by [InstanceCreation] (expression). static const int SpecializedTagHighBit = 0x80; // 10000000 static const int SpecializedTagMask = 0xF8; // 11111000 @@ -145,7 +147,7 @@ class Tag { /// Internal version of kernel binary format. /// Bump it when making incompatible changes in kernel binaries. /// Keep in sync with runtime/vm/kernel_binary.h, pkg/kernel/binary.md. - static const int BinaryFormatVersion = 22; + static const int BinaryFormatVersion = 23; } abstract class ConstantTag { diff --git a/pkg/kernel/lib/clone.dart b/pkg/kernel/lib/clone.dart index 4105a43ee45..fd3bb4dde86 100644 --- a/pkg/kernel/lib/clone.dart +++ b/pkg/kernel/lib/clone.dart @@ -205,6 +205,18 @@ class CloneVisitor implements TreeVisitor { keyType: visitType(node.keyType), valueType: visitType(node.valueType)); } + visitInstanceCreation(InstanceCreation node) { + final Map fieldValues = {}; + node.fieldValues.forEach((Reference fieldRef, Expression value) { + fieldValues[fieldRef] = clone(value); + }); + return new InstanceCreation( + node.classReference, + node.typeArguments.map(visitType).toList(), + fieldValues, + node.asserts.map(clone).toList()); + } + visitIsExpression(IsExpression node) { return new IsExpression(clone(node.operand), visitType(node.type)); } diff --git a/pkg/kernel/lib/text/ast_to_text.dart b/pkg/kernel/lib/text/ast_to_text.dart index 8d957f41e43..5862b835476 100644 --- a/pkg/kernel/lib/text/ast_to_text.dart +++ b/pkg/kernel/lib/text/ast_to_text.dart @@ -1302,6 +1302,39 @@ class Printer extends Visitor { } } + visitInstanceCreation(InstanceCreation node) { + write('${node.classNode}'); + if (node.typeArguments.isNotEmpty) { + writeSymbol('<'); + writeList(node.typeArguments, writeType); + writeSymbol('>'); + } + write(' {'); + bool first = true; + node.fieldValues.forEach((Reference fieldRef, Expression value) { + if (!first) { + writeComma(); + } + write('${fieldRef.asField.name}: '); + writeExpression(value); + first = false; + }); + for (AssertStatement assert_ in node.asserts) { + if (!first) { + writeComma(); + } + write('assert('); + writeExpression(assert_.condition); + if (assert_.message != null) { + writeComma(); + writeExpression(assert_.message); + } + write(')'); + } + + write('}'); + } + visitIsExpression(IsExpression node) { writeExpression(node.operand, Precedence.BITWISE_OR); writeSpaced('is'); @@ -2014,10 +2047,10 @@ class Printer extends Visitor { node.fieldValues.forEach((Reference fieldRef, Constant constant) { final String name = syntheticNames.nameConstant(constant); if (!first) { - first = false; sb.write(', '); } sb.write('${fieldRef.asField.name}: $name'); + first = false; }); sb.write('}'); endLine(sb.toString()); diff --git a/pkg/kernel/lib/text/text_serialization_verifier.dart b/pkg/kernel/lib/text/text_serialization_verifier.dart index b9f26624c97..3fed367ccb4 100644 --- a/pkg/kernel/lib/text/text_serialization_verifier.dart +++ b/pkg/kernel/lib/text/text_serialization_verifier.dart @@ -389,6 +389,12 @@ class TextSerializationVerifier implements Visitor { node.visitChildren(this); } + @override + void visitInstanceCreation(InstanceCreation node) { + storeLastSeenUriAndOffset(node); + node.visitChildren(this); + } + @override void visitSymbolConstant(SymbolConstant node) { storeLastSeenUriAndOffset(node); diff --git a/pkg/kernel/lib/type_checker.dart b/pkg/kernel/lib/type_checker.dart index 7f04bcea39e..f81ec6698bd 100644 --- a/pkg/kernel/lib/type_checker.dart +++ b/pkg/kernel/lib/type_checker.dart @@ -712,6 +712,18 @@ class TypeCheckingVisitor return type; } + @override + DartType visitInstanceCreation(InstanceCreation node) { + Substitution substitution = Substitution.fromPairs( + node.classNode.typeParameters, node.typeArguments); + node.fieldValues.forEach((Reference fieldRef, Expression value) { + DartType fieldType = substitution.substituteType(fieldRef.asField.type); + DartType valueType = visitExpression(value); + checkAssignable(node, fieldType, valueType); + }); + return new InterfaceType(node.classNode, node.typeArguments); + } + @override DartType visitStringLiteral(StringLiteral node) { return environment.stringType; diff --git a/pkg/kernel/lib/visitor.dart b/pkg/kernel/lib/visitor.dart index 388f3cf0940..b1a99386c3b 100644 --- a/pkg/kernel/lib/visitor.dart +++ b/pkg/kernel/lib/visitor.dart @@ -41,6 +41,7 @@ abstract class ExpressionVisitor { R visitListConcatenation(ListConcatenation node) => defaultExpression(node); R visitSetConcatenation(SetConcatenation node) => defaultExpression(node); R visitMapConcatenation(MapConcatenation node) => defaultExpression(node); + R visitInstanceCreation(InstanceCreation node) => defaultExpression(node); R visitIsExpression(IsExpression node) => defaultExpression(node); R visitAsExpression(AsExpression node) => defaultExpression(node); R visitSymbolLiteral(SymbolLiteral node) => defaultExpression(node); @@ -167,6 +168,7 @@ class TreeVisitor R visitListConcatenation(ListConcatenation node) => defaultExpression(node); R visitSetConcatenation(SetConcatenation node) => defaultExpression(node); R visitMapConcatenation(MapConcatenation node) => defaultExpression(node); + R visitInstanceCreation(InstanceCreation node) => defaultExpression(node); R visitIsExpression(IsExpression node) => defaultExpression(node); R visitAsExpression(AsExpression node) => defaultExpression(node); R visitSymbolLiteral(SymbolLiteral node) => defaultExpression(node); @@ -510,6 +512,8 @@ abstract class ExpressionVisitor1 { defaultExpression(node, arg); R visitMapConcatenation(MapConcatenation node, T arg) => defaultExpression(node, arg); + R visitInstanceCreation(InstanceCreation node, T arg) => + defaultExpression(node, arg); R visitIsExpression(IsExpression node, T arg) => defaultExpression(node, arg); R visitAsExpression(AsExpression node, T arg) => defaultExpression(node, arg); R visitSymbolLiteral(SymbolLiteral node, T arg) => diff --git a/runtime/vm/compiler/frontend/constant_evaluator.cc b/runtime/vm/compiler/frontend/constant_evaluator.cc index 002b8f8fe6e..09ae0acb302 100644 --- a/runtime/vm/compiler/frontend/constant_evaluator.cc +++ b/runtime/vm/compiler/frontend/constant_evaluator.cc @@ -97,6 +97,7 @@ RawInstance* ConstantEvaluator::EvaluateExpression(intptr_t offset, case kListConcatenation: case kSetConcatenation: case kMapConcatenation: + case kInstanceCreation: // These only occur inside unevaluated constants, so if we decide to // remove support for late evaluation of environment constants from // dill files in the VM, an implementation here will not be necessary. diff --git a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc index 9d780c77fc2..47a2281e853 100644 --- a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc +++ b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc @@ -1159,8 +1159,9 @@ Fragment StreamingFlowGraphBuilder::BuildExpression(TokenPosition* position) { case kListConcatenation: case kSetConcatenation: case kMapConcatenation: - // Collection concatenation operations are removed by the constant - // evaluator. + case kInstanceCreation: + // Collection concatenation and instance creation operations are removed + // by the constant evaluator. UNREACHABLE(); break; case kIsExpression: diff --git a/runtime/vm/compiler/frontend/kernel_fingerprints.cc b/runtime/vm/compiler/frontend/kernel_fingerprints.cc index 1a88f1ba42f..be15d45fde5 100644 --- a/runtime/vm/compiler/frontend/kernel_fingerprints.cc +++ b/runtime/vm/compiler/frontend/kernel_fingerprints.cc @@ -450,8 +450,9 @@ void KernelFingerprintHelper::CalculateExpressionFingerprint() { case kListConcatenation: case kSetConcatenation: case kMapConcatenation: - // Collection concatenation operations are removed by the constant - // evaluator. + case kInstanceCreation: + // Collection concatenation and instance creation operations are removed + // by the constant evaluator. UNREACHABLE(); break; case kIsExpression: diff --git a/runtime/vm/compiler/frontend/kernel_translation_helper.cc b/runtime/vm/compiler/frontend/kernel_translation_helper.cc index 5fd9a775cde..971ac05cf7d 100644 --- a/runtime/vm/compiler/frontend/kernel_translation_helper.cc +++ b/runtime/vm/compiler/frontend/kernel_translation_helper.cc @@ -2237,8 +2237,9 @@ void KernelReaderHelper::SkipExpression() { case kListConcatenation: case kSetConcatenation: case kMapConcatenation: - // Collection concatenation operations are removed by the constant - // evaluator. + case kInstanceCreation: + // Collection concatenation and instance creation operations are removed + // by the constant evaluator. UNREACHABLE(); break; case kIsExpression: diff --git a/runtime/vm/compiler/frontend/scope_builder.cc b/runtime/vm/compiler/frontend/scope_builder.cc index e23bf10e29b..ea837d5b69d 100644 --- a/runtime/vm/compiler/frontend/scope_builder.cc +++ b/runtime/vm/compiler/frontend/scope_builder.cc @@ -765,8 +765,9 @@ void ScopeBuilder::VisitExpression() { case kListConcatenation: case kSetConcatenation: case kMapConcatenation: - // Collection concatenation operations are removed by the constant - // evaluator. + case kInstanceCreation: + // Collection concatenation and instance creation operations are removed + // by the constant evaluator. UNREACHABLE(); break; case kIsExpression: diff --git a/runtime/vm/kernel_binary.h b/runtime/vm/kernel_binary.h index ab22ff4057e..96c4986db88 100644 --- a/runtime/vm/kernel_binary.h +++ b/runtime/vm/kernel_binary.h @@ -20,7 +20,7 @@ static const uint32_t kMagicProgramFile = 0x90ABCDEFu; // Both version numbers are inclusive. static const uint32_t kMinSupportedKernelFormatVersion = 18; -static const uint32_t kMaxSupportedKernelFormatVersion = 22; +static const uint32_t kMaxSupportedKernelFormatVersion = 23; // Keep in sync with package:kernel/lib/binary/tag.dart #define KERNEL_TAG_LIST(V) \ @@ -65,6 +65,7 @@ static const uint32_t kMaxSupportedKernelFormatVersion = 22; V(ListConcatenation, 111) \ V(SetConcatenation, 112) \ V(MapConcatenation, 113) \ + V(InstanceCreation, 114) \ V(IsExpression, 37) \ V(AsExpression, 38) \ V(StringLiteral, 39) \