Files
sdk/pkg/dart2wasm/lib/reference_extensions.dart
T
Ömer Sinan Ağacan 636232628b [dart2wasm] Implement missing features in dynamic invocations
This reimplements dynamic call code generation to add support for type
checking, named parameters (optional and required), and fixes a few
related bugs on the way.

Currently we do not try to be as efficient as possible. The goal with
this patch is to implement it correctly.

Summary of the changes:

- For every dynamic access kind and member name, we generate a new
  "forwarder" function. Dynamic gets, sets, and invocations are compiled
  to calls to the forwarders with the right access kind (invocation,
  get, set) and member name.

  For example, if the program has dynamic invocation of a member "f", we
  create an "invocation forwarder for f". If it has a dynamic get of a
  member "x", we generate "getter forwarder for x".

- Forwarder functions take 4 arguments:

  - Receiver of the invocation, get, or set.

  - A Dart list for type arguments in the invocation. For gets and sets
    the list is empty.

  - A Dart list for positional arguments in the invocation. For gets the
    list is empty. For sets, the list only has one element.

  - A Dart list for named arguments. For gets and sets the list is
    empty. The list has alternating elements of type `Symbol` and
    `Object?`, for the name and value of the named parameters.

- A forwarder function compares receiver class ID with the potential
  targets of the call. When it finds a match, it compares the callee
  "shape" with the parameters passed in the call site.

  As it compares the shapes it adjusts argument lists:

  - Creates default values for missing optional positional and named
    arguments

  - Reorders the named argument list to match order expected by the
    callee

  If it can't find a matching class ID and a member with the right name
  and shape, it calls `noSuchMethod` on the receiver.

  If it finds a matching class ID and a member, it calls the "type
  checker" for the member, passing the original receiver and adjusted
  argument lists.

- A "type checker" implements argument type checking for a member, and
  it's a member of the same class as the member it's checking types
  for. This is to allow accessing class-bound type parameters when
  generating type checking code.

- Type checking is implemented using `_isSubtype` on arguments in the
  lists.

- When type checking is successful a type checker calls the original
  member, passing the arguments as expected by the member.

  If type checking is unsuccessful it throws a type error.

Most of the changes are for generating Wasm functions that compare
shapes, adjusts argument lists, and checks types.

Changes to members:

- `Translator.dynamics` fields is renamed to
  `Translator.dynamicForwarders`

- New field `Translator.dynamicForwarderFunctionType` added for the Wasm
  function type of forwarder and type checker functions.

- Two new code gen utilities added:

  - `Translator.indexList`: generates code that indexes a Dart list

  - `Translator.getListLength`: generates code that gets length of a
    Dart list

- New `Reference` extensions added to get type checker function
  references of members

- New runtime library `named_parameters` implements two helper functions
  for dealing with named argument lists

- The library `dynamic_dispatch` is replaced by `dynamic_forwarders`,
  which consists of two classes:

  - `DynamicForwarders`: maintains mapping from call kind (get, set,
    invocation) and member name to forwarder functions.

  - `Forwarder`: a single forwarder, implements code generation for
    forwarder functions.

- `CodeGenerator` gets 3 new members:

  - `_callForwader` generates call to a forwarder

  - `_generateFieldSetterTypeCheckerMethod` generates code for a type
    checker of a setter function.

  - `_generateProcedureTypeCheckerMethod` generates code for a type
    checker of a method.

Fixes #50367

Change-Id: I2b9d84237c8517bd217166d8acb67e025f0498fb
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/272261
Reviewed-by: Joshua Litt <joshualitt@google.com>
Commit-Queue: Ömer Ağacan <omersa@google.com>
Reviewed-by: Aske Simon Christensen <askesc@google.com>
2022-12-08 10:45:12 +00:00

77 lines
2.7 KiB
Dart

// Copyright (c) 2022, 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';
// Extend references with flags to more easily identify getters and setters.
extension GetterSetterReference on Reference {
bool get isImplicitGetter {
Member member = asMember;
return member is Field && member.getterReference == this;
}
bool get isImplicitSetter {
Member member = asMember;
return member is Field && member.setterReference == this;
}
bool get isGetter {
Member member = asMember;
return (member is Procedure && member.isGetter) || isImplicitGetter;
}
bool get isSetter {
Member member = asMember;
return (member is Procedure && member.isSetter) || isImplicitSetter;
}
}
// Extend procedures with a tearOffReference that refers to the tear-off
// implementation for that procedure. This enables a Reference to refer to any
// implementation relating to a member, including its tear-off, which it can't
// do in plain kernel.
// Also add an asyncInnerReference that refers to the inner, suspendable
// body of an async function, which returns the value to be put into a future.
// This can be called directly from other async functions if the result is
// directly awaited.
// Use Expandos to avoid keeping the procedure alive.
final Expando<Reference> _tearOffReference = Expando();
final Expando<Reference> _asyncInnerReference = Expando();
final Expando<Reference> _typeCheckerReference = Expando();
extension CustomReference on Member {
Reference get tearOffReference =>
_tearOffReference[this] ??= Reference()..node = this;
Reference get asyncInnerReference =>
_asyncInnerReference[this] ??= Reference()..node = this;
Reference get typeCheckerReference =>
_typeCheckerReference[this] ??= Reference()..node = this;
}
extension IsCustomReference on Reference {
bool get isTearOffReference => _tearOffReference[asMember] == this;
bool get isAsyncInnerReference => _asyncInnerReference[asMember] == this;
bool get isTypeCheckerReference => _typeCheckerReference[asMember] == this;
}
extension ReferenceAs on Member {
Reference referenceAs({required bool getter, required bool setter}) {
assert(!getter || !setter); // members cannot be both setter and getter
Member member = this;
return member is Field
? setter
? member.setterReference!
: member.getterReference
: getter && member is Procedure && member.kind == ProcedureKind.Method
? member.tearOffReference
: member.reference;
}
}