Skip to content

Commit

Permalink
Version 3.2.0-52.0.dev
Browse files Browse the repository at this point in the history
Merge b11aec8 into dev
  • Loading branch information
Dart CI committed Aug 9, 2023
2 parents 875827b + b11aec8 commit 1071557
Show file tree
Hide file tree
Showing 7 changed files with 341 additions and 102 deletions.
214 changes: 214 additions & 0 deletions pkg/dart2wasm/lib/list_factory_specializer.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
// Copyright (c) 2023, 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;

/// Replaces invocation of `List` factory constructors with factories of
/// dart2wasm-specific classes.
///
/// ```
/// List.empty() => _List.empty()
/// List.empty(growable: false) => _List.empty()
/// List.empty(growable: true) => _GrowableList.empty()
/// List.filled(n, null, growable: true) => _GrowableList(n)
/// List.filled(n, x, growable: true) => _GrowableList.filled(n, x)
/// List.filled(n, null) => _List(n)
/// List.filled(n, x) => _List.filled(n, x)
/// List.generate(n, y) => _GrowableList.generate(n, y)
/// List.generate(n, y, growable: false) => _List.generate(n, y)
/// ```
class ListFactorySpecializer {
final Map<Member, TreeNode Function(StaticInvocation node)> _transformers =
{};

final Procedure _fixedListEmptyFactory;
final Procedure _fixedListFactory;
final Procedure _fixedListFilledFactory;
final Procedure _fixedListGenerateFactory;
final Procedure _growableListEmptyFactory;
final Procedure _growableListFactory;
final Procedure _growableListFilledFactory;
final Procedure _growableListGenerateFactory;
final Procedure _listEmptyFactory;
final Procedure _listFilledFactory;
final Procedure _listGenerateFactory;

ListFactorySpecializer(CoreTypes coreTypes)
: _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') {
_transformers[_listFilledFactory] = _transformListFilledFactory;
_transformers[_listEmptyFactory] = _transformListEmptyFactory;
_transformers[_listGenerateFactory] = _transformListGenerateFactory;
}

TreeNode transformStaticInvocation(StaticInvocation invocation) {
final target = invocation.target;
final transformer = _transformers[target];
if (transformer != null) {
return transformer(invocation);
}
return invocation;
}

// List.filled(n, null, growable: true) => _GrowableList(n)
// List.filled(n, x, growable: true) => _GrowableList.filled(n, x)
// List.filled(n, null) => _List(n)
// List.filled(n, x) => _List.filled(n, x)
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 = _isNullConstant(fill);

// Null when the argument is not a constant or a `bool` literal, e.g.
// `List.filled(..., growable: f())`.
final bool? growable =
_getConstantOptionalArgument(args, 'growable', false);

if (growable == null) {
return node;
}

if (growable) {
if (fillingWithNull) {
// List.filled(n, null, growable: true) => _GrowableList(n)
return StaticInvocation(
_growableListFactory, Arguments([length], types: args.types))
..fileOffset = node.fileOffset;
} else {
// List.filled(n, x, growable: true) => _GrowableList.filled(n, x)
return StaticInvocation(_growableListFilledFactory,
Arguments([length, fill], types: args.types))
..fileOffset = node.fileOffset;
}
} else {
if (fillingWithNull) {
// List.filled(n, null, growable: false) => _List(n)
return StaticInvocation(
_fixedListFactory, Arguments([length], types: args.types))
..fileOffset = node.fileOffset;
} else {
// List.filled(n, x, growable: false) => _List.filled(n, x)
return StaticInvocation(_fixedListFilledFactory,
Arguments([length, fill], types: args.types))
..fileOffset = node.fileOffset;
}
}
}

// List.empty() => _List.empty()
// List.empty(growable: false) => _List.empty()
// List.empty(growable: true) => _GrowableList.empty()
TreeNode _transformListEmptyFactory(StaticInvocation node) {
final args = node.arguments;
assert(args.positional.isEmpty);
final bool? growable =
_getConstantOptionalArgument(args, 'growable', false);
if (growable == null) {
return node;
}
if (growable) {
// List.empty(growable: true) => _GrowableList.empty()
return StaticInvocation(
_growableListEmptyFactory, Arguments([], types: args.types))
..fileOffset = node.fileOffset;
} else {
// List.empty() => _List.empty()
// List.empty(growable: false) => _List.empty()
return StaticInvocation(
_fixedListEmptyFactory, Arguments([], types: args.types))
..fileOffset = node.fileOffset;
}
}

// List.generate(n, y) => _GrowableList.generate(n, y)
// List.generate(n, y, growable: false) => _List.generate(n, y)
TreeNode _transformListGenerateFactory(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) {
// List.generate(n, y) => _GrowableList.generate(n, y)
return StaticInvocation(_growableListGenerateFactory,
Arguments([length, generator], types: args.types))
..fileOffset = node.fileOffset;
} else {
// List.generate(n, y, growable: false) => _List.generate(n, y)
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 = _unwrapFinalVariableGet(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;
}

bool _isNullConstant(Expression value) {
value = _unwrapFinalVariableGet(value);
return value is NullLiteral ||
(value is ConstantExpression && value.constant is NullConstant);
}

// Front-end can create extra temporary variables ("Let v = e, call(v)") to
// hoist expressions when rearraning named parameters. Unwrap such variables
// and return their initializers.
Expression _unwrapFinalVariableGet(Expression expr) {
if (expr is VariableGet) {
final variable = expr.variable;
if (variable.isFinal) {
final initializer = variable.initializer;
if (initializer != null) {
return initializer;
}
}
}
return expr;
}
13 changes: 12 additions & 1 deletion pkg/dart2wasm/lib/transformers.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import 'package:kernel/target/targets.dart';
import 'package:kernel/type_environment.dart';
import 'package:kernel/type_algebra.dart';

import 'package:dart2wasm/list_factory_specializer.dart';

void transformLibraries(List<Library> libraries, CoreTypes coreTypes,
ClassHierarchy hierarchy, DiagnosticReporter diagnosticReporter) {
final transformer =
Expand Down Expand Up @@ -42,6 +44,8 @@ class _WasmTransformer extends Transformer {
Nullability.nonNullable,
[coreTypes.boolNonNullableRawType]);

final ListFactorySpecializer _listFactorySpecializer;

StaticTypeContext get typeContext =>
_cachedTypeContext ??= StaticTypeContext(_currentMember!, env);

Expand All @@ -54,7 +58,8 @@ class _WasmTransformer extends Transformer {
.getClass('dart:core', '_Type')
.getThisType(coreTypes, Nullability.nonNullable),
_wasmBaseClass = coreTypes.index.getClass('dart:_wasm', '_WasmBase'),
_coreLibrary = coreTypes.index.getLibrary('dart:core');
_coreLibrary = coreTypes.index.getLibrary('dart:core'),
_listFactorySpecializer = ListFactorySpecializer(coreTypes);

@override
defaultMember(Member node) {
Expand Down Expand Up @@ -696,6 +701,12 @@ class _WasmTransformer extends Transformer {
return result;
}
}

@override
TreeNode visitStaticInvocation(StaticInvocation node) {
node.transformChildren(this);
return _listFactorySpecializer.transformStaticInvocation(node);
}
}

class _AsyncStarFrame {
Expand Down
22 changes: 13 additions & 9 deletions pkg/wasm_builder/lib/src/builder/instructions.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1163,14 +1163,16 @@ class InstructionsBuilder with Builder<ir.Instructions> {

/// Emit a `br_on_cast` instruction.
void br_on_cast(Label label, ir.RefType inputType, ir.RefType targetType) {
assert(_verifyCast(inputType, targetType, inputType, trace: [
'br_on_cast',
label,
if (inputType.nullable) 'null',
inputType.heapType,
if (targetType.nullable) 'null',
targetType.heapType
]));
assert(_verifyCast(inputType, targetType,
inputType.withNullability(inputType.nullable && !targetType.nullable),
trace: [
'br_on_cast',
label,
if (inputType.nullable) 'null',
inputType.heapType,
if (targetType.nullable) 'null',
targetType.heapType
]));
assert(_verifyBranchTypes(label, 1, [targetType]));
_add(ir.BrOnCast(_labelIndex(label), inputType, targetType));
}
Expand All @@ -1186,7 +1188,9 @@ class InstructionsBuilder with Builder<ir.Instructions> {
if (targetType.nullable) 'null',
targetType.heapType
]));
assert(_verifyBranchTypes(label, 1, [inputType]));
assert(_verifyBranchTypes(label, 1, [
inputType.withNullability(inputType.nullable && !targetType.nullable)
]));
_add(ir.BrOnCastFail(_labelIndex(label), inputType, targetType));
}

Expand Down
Loading

0 comments on commit 1071557

Please sign in to comment.