| // Copyright (c) 2018, 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:kernel/ast.dart'; |
| import 'package:kernel/core_types.dart' show CoreTypes; |
| import 'package:vm/transformations/specializer/factory_specializer.dart'; |
| |
| /// Replaces invocation of List factory constructors with |
| /// factories of VM-specific classes. |
| /// |
| /// new List() => new _GrowableList(0) |
| /// new List(n) => new _List(n) |
| /// new List.empty() => new _List.empty() |
| /// new List.empty(growable: false) => new _List.empty() |
| /// new List.empty(growable: true) => new _GrowableList.empty() |
| /// new List.filled(n, null, growable: true) => new _GrowableList(n) |
| /// new List.filled(n, x, growable: true) => new _GrowableList.filled(n, x) |
| /// new List.filled(n, null) => new _List(n) |
| /// new List.filled(n, x) => new _List.filled(n, x) |
| /// new List.generate(n, y) => new _GrowableList.generate(n, y) |
| /// new List.generate(n, y, growable: false) => new _List.generate(n, y) |
| /// |
| class ListFactorySpecializer extends BaseSpecializer { |
| final Procedure _defaultListFactory; |
| final Procedure _listEmptyFactory; |
| final Procedure _listFilledFactory; |
| final Procedure _listGenerateFactory; |
| final Procedure _growableListFactory; |
| final Procedure _growableListEmptyFactory; |
| final Procedure _growableListFilledFactory; |
| final Procedure _growableListGenerateFactory; |
| final Procedure _fixedListFactory; |
| final Procedure _fixedListEmptyFactory; |
| final Procedure _fixedListFilledFactory; |
| final Procedure _fixedListGenerateFactory; |
| |
| ListFactorySpecializer(CoreTypes coreTypes) |
| : _defaultListFactory = |
| coreTypes.index.getProcedure('dart:core', 'List', ''), |
| _listEmptyFactory = |
| coreTypes.index.getProcedure('dart:core', 'List', 'empty'), |
| _listFilledFactory = |
| coreTypes.index.getProcedure('dart:core', 'List', 'filled'), |
| _listGenerateFactory = |
| coreTypes.index.getProcedure('dart:core', 'List', 'generate'), |
| _growableListFactory = |
| coreTypes.index.getProcedure('dart:core', '_GrowableList', ''), |
| _growableListEmptyFactory = |
| coreTypes.index.getProcedure('dart:core', '_GrowableList', 'empty'), |
| _growableListFilledFactory = coreTypes.index |
| .getProcedure('dart:core', '_GrowableList', 'filled'), |
| _growableListGenerateFactory = coreTypes.index |
| .getProcedure('dart:core', '_GrowableList', 'generate'), |
| _fixedListFactory = |
| coreTypes.index.getProcedure('dart:core', '_List', ''), |
| _fixedListEmptyFactory = |
| coreTypes.index.getProcedure('dart:core', '_List', 'empty'), |
| _fixedListFilledFactory = |
| coreTypes.index.getProcedure('dart:core', '_List', 'filled'), |
| _fixedListGenerateFactory = |
| coreTypes.index.getProcedure('dart:core', '_List', 'generate') { |
| assert(_defaultListFactory.isFactory); |
| assert(_listEmptyFactory.isFactory); |
| assert(_listFilledFactory.isFactory); |
| assert(_listGenerateFactory.isFactory); |
| assert(_growableListFactory.isFactory); |
| assert(_growableListEmptyFactory.isFactory); |
| assert(_growableListFilledFactory.isFactory); |
| assert(_growableListGenerateFactory.isFactory); |
| assert(_fixedListFactory.isFactory); |
| assert(_fixedListEmptyFactory.isFactory); |
| assert(_fixedListFilledFactory.isFactory); |
| assert(_fixedListGenerateFactory.isFactory); |
| transformers.addAll({ |
| _defaultListFactory: transformDefaultFactory, |
| _listEmptyFactory: transformListEmptyFactory, |
| _listFilledFactory: transformListFilledFactory, |
| _listGenerateFactory: transformListGeneratorFactory, |
| }); |
| } |
| |
| TreeNode transformDefaultFactory(StaticInvocation node) { |
| final args = node.arguments; |
| if (args.positional.isEmpty) { |
| return StaticInvocation(_growableListFactory, |
| Arguments([new IntLiteral(0)], types: args.types)) |
| ..fileOffset = node.fileOffset; |
| } else { |
| return StaticInvocation(_fixedListFactory, args) |
| ..fileOffset = node.fileOffset; |
| } |
| } |
| |
| TreeNode transformListEmptyFactory(StaticInvocation node) { |
| final args = node.arguments; |
| assert(args.positional.length == 0); |
| final bool? growable = |
| _getConstantOptionalArgument(args, 'growable', false); |
| if (growable == null) { |
| return node; |
| } |
| if (growable) { |
| return StaticInvocation( |
| _growableListEmptyFactory, Arguments([], types: args.types)) |
| ..fileOffset = node.fileOffset; |
| } else { |
| return StaticInvocation( |
| _fixedListEmptyFactory, Arguments([], types: args.types)) |
| ..fileOffset = node.fileOffset; |
| } |
| } |
| |
| TreeNode transformListFilledFactory(StaticInvocation node) { |
| final args = node.arguments; |
| assert(args.positional.length == 2); |
| final length = args.positional[0]; |
| final fill = args.positional[1]; |
| final fillingWithNull = fill is NullLiteral || |
| (fill is ConstantExpression && fill.constant is NullConstant); |
| final bool? growable = |
| _getConstantOptionalArgument(args, 'growable', false); |
| if (growable == null) { |
| return node; |
| } |
| if (growable) { |
| if (fillingWithNull) { |
| return StaticInvocation( |
| _growableListFactory, Arguments([length], types: args.types)) |
| ..fileOffset = node.fileOffset; |
| } else { |
| return StaticInvocation(_growableListFilledFactory, |
| Arguments([length, fill], types: args.types)) |
| ..fileOffset = node.fileOffset; |
| } |
| } else { |
| if (fillingWithNull) { |
| return StaticInvocation( |
| _fixedListFactory, Arguments([length], types: args.types)) |
| ..fileOffset = node.fileOffset; |
| } else { |
| return StaticInvocation(_fixedListFilledFactory, |
| Arguments([length, fill], types: args.types)) |
| ..fileOffset = node.fileOffset; |
| } |
| } |
| } |
| |
| TreeNode transformListGeneratorFactory(StaticInvocation node) { |
| final args = node.arguments; |
| assert(args.positional.length == 2); |
| final length = args.positional[0]; |
| final generator = args.positional[1]; |
| final bool? growable = _getConstantOptionalArgument(args, 'growable', true); |
| if (growable == null) { |
| return node; |
| } |
| if (growable) { |
| return StaticInvocation(_growableListGenerateFactory, |
| Arguments([length, generator], types: args.types)) |
| ..fileOffset = node.fileOffset; |
| } else { |
| return StaticInvocation(_fixedListGenerateFactory, |
| Arguments([length, generator], types: args.types)) |
| ..fileOffset = node.fileOffset; |
| } |
| } |
| |
| /// Returns constant value of the only optional argument in [args], |
| /// or null if it is not a constant. Returns [defaultValue] if optional |
| /// argument is not passed. Argument is asserted to have the given [name]. |
| bool? _getConstantOptionalArgument( |
| Arguments args, String name, bool defaultValue) { |
| if (args.named.isEmpty) { |
| return defaultValue; |
| } |
| final namedArg = args.named.single; |
| assert(namedArg.name == name); |
| final value = namedArg.value; |
| if (value is BoolLiteral) { |
| return value.value; |
| } else if (value is ConstantExpression) { |
| final constant = value.constant; |
| if (constant is BoolConstant) { |
| return constant.value; |
| } |
| } |
| return null; |
| } |
| } |