Files
sdk/pkg/kernel/lib/target/vm.dart
T
Samir Jindel 1a23ff68a5 Fix incorrect handling of NSM forwarders and pull all logic into CFE. (Take 2)
The behavioral difference is that named and optional arguments are filled in
with their default values in the `Invocation` object passed to `noSuchMethod`.

On the implementation side we make NSM forwarders concrete and fill in their
bodies in the CFE. The custom (and somewhat hacky) VM support is no longer
needed, and Dart2JS can benefit from this implementation as well.

According to discussion on #33031 we will be able to re-land this soon without
breaking Mockito.

Prior failures on precompiler bots are fixed in Patchset 2.

Change-Id: If1b7fe4cf6da5ef38f330e1ad226121bcfc958a1
Reviewed-on: https://dart-review.googlesource.com/54401
Commit-Queue: Samir Jindel <sjindel@google.com>
Reviewed-by: Dmitry Stefantsov <dmitryas@google.com>
2018-05-15 11:23:32 +00:00

290 lines
9.9 KiB
Dart

// 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.
library kernel.target.vm;
import 'dart:core' hide MapEntry;
import '../ast.dart';
import '../class_hierarchy.dart';
import '../core_types.dart';
import '../transformations/mixin_full_resolution.dart' as transformMixins
show transformLibraries;
import '../transformations/continuation.dart' as transformAsync
show transformLibraries, transformProcedure;
import 'targets.dart';
/// Specializes the kernel IR to the Dart VM.
class VmTarget extends Target {
final TargetFlags flags;
VmTarget(this.flags);
@override
bool get strongMode => flags.strongMode;
@override
bool get enableNoSuchMethodForwarders => true;
@override
String get name => 'vm';
// This is the order that bootstrap libraries are loaded according to
// `runtime/vm/object_store.h`.
@override
List<String> get extraRequiredLibraries => const <String>[
'dart:async',
'dart:collection',
'dart:convert',
'dart:developer',
'dart:_internal',
'dart:isolate',
'dart:math',
// The library dart:mirrors may be ignored by the VM, e.g. when built in
// PRODUCT mode.
'dart:mirrors',
'dart:profiler',
'dart:typed_data',
'dart:vmservice_io',
'dart:_vmservice',
'dart:_builtin',
'dart:nativewrappers',
'dart:io',
'dart:cli',
];
@override
void performModularTransformationsOnLibraries(
CoreTypes coreTypes, ClassHierarchy hierarchy, List<Library> libraries,
{void logger(String msg)}) {
transformMixins.transformLibraries(this, coreTypes, hierarchy, libraries,
doSuperResolution: false /* resolution is done in Dart VM */);
logger?.call("Transformed mixin applications");
// TODO(kmillikin): Make this run on a per-method basis.
transformAsync.transformLibraries(coreTypes, libraries, flags.syncAsync);
logger?.call("Transformed async methods");
}
@override
void performGlobalTransformations(CoreTypes coreTypes, Component component,
{void logger(String msg)}) {}
@override
void performTransformationsOnProcedure(
CoreTypes coreTypes, ClassHierarchy hierarchy, Procedure procedure,
{void logger(String msg)}) {
transformAsync.transformProcedure(coreTypes, procedure, flags.syncAsync);
logger?.call("Transformed async functions");
}
@override
Expression instantiateInvocation(CoreTypes coreTypes, Expression receiver,
String name, Arguments arguments, int offset, bool isSuper) {
// See [_InvocationMirror]
// (../../../../runtime/lib/invocation_mirror_patch.dart).
// The _InvocationMirror._withoutType constructor takes the following arguments:
// * Method name (a string).
// * List of type arguments.
// * List of positional arguments.
// * List of named arguments.
// * Whether it's a super invocation or not.
return new ConstructorInvocation(
coreTypes.invocationMirrorWithoutTypeConstructor,
new Arguments(<Expression>[
new StringLiteral(name)..fileOffset = offset,
_fixedLengthList(
coreTypes,
coreTypes.typeClass.rawType,
arguments.types.map((t) => new TypeLiteral(t)).toList(),
arguments.fileOffset),
_fixedLengthList(coreTypes, const DynamicType(), arguments.positional,
arguments.fileOffset),
new StaticInvocation(
coreTypes.mapUnmodifiable,
new Arguments([
new MapLiteral(new List<MapEntry>.from(
arguments.named.map((NamedExpression arg) {
return new MapEntry(
new SymbolLiteral(arg.name)..fileOffset = arg.fileOffset,
arg.value)
..fileOffset = arg.fileOffset;
})), keyType: coreTypes.symbolClass.rawType)
..isConst = (arguments.named.length == 0)
..fileOffset = arguments.fileOffset
], types: [
coreTypes.symbolClass.rawType,
new DynamicType()
]))
..fileOffset = arguments.fileOffset,
new BoolLiteral(isSuper)..fileOffset = arguments.fileOffset
]))
..fileOffset = offset;
}
@override
Expression instantiateNoSuchMethodError(CoreTypes coreTypes,
Expression receiver, String name, Arguments arguments, int offset,
{bool isMethod: false,
bool isGetter: false,
bool isSetter: false,
bool isField: false,
bool isLocalVariable: false,
bool isDynamic: false,
bool isSuper: false,
bool isStatic: false,
bool isConstructor: false,
bool isTopLevel: false}) {
int type = _invocationType(
isMethod: isMethod,
isGetter: isGetter,
isSetter: isSetter,
isField: isField,
isLocalVariable: isLocalVariable,
isDynamic: isDynamic,
isSuper: isSuper,
isStatic: isStatic,
isConstructor: isConstructor,
isTopLevel: isTopLevel);
return new ConstructorInvocation(
coreTypes.noSuchMethodErrorDefaultConstructor,
new Arguments(<Expression>[
receiver,
new ConstructorInvocation(
coreTypes.invocationMirrorWithTypeConstructor,
new Arguments(<Expression>[
new SymbolLiteral(name)..fileOffset = offset,
new IntLiteral(type)..fileOffset = offset,
_fixedLengthList(
coreTypes,
coreTypes.typeClass.rawType,
arguments.types.map((t) => new TypeLiteral(t)).toList(),
arguments.fileOffset),
_fixedLengthList(coreTypes, const DynamicType(),
arguments.positional, arguments.fileOffset),
new StaticInvocation(
coreTypes.mapUnmodifiable,
new Arguments([
new MapLiteral(new List<MapEntry>.from(
arguments.named.map((NamedExpression arg) {
return new MapEntry(
new SymbolLiteral(arg.name)
..fileOffset = arg.fileOffset,
arg.value)
..fileOffset = arg.fileOffset;
})), keyType: coreTypes.symbolClass.rawType)
..isConst = (arguments.named.length == 0)
..fileOffset = arguments.fileOffset
], types: [
coreTypes.symbolClass.rawType,
new DynamicType()
]))
..fileOffset = offset
]))
]));
}
int _invocationType(
{bool isMethod: false,
bool isGetter: false,
bool isSetter: false,
bool isField: false,
bool isLocalVariable: false,
bool isDynamic: false,
bool isSuper: false,
bool isStatic: false,
bool isConstructor: false,
bool isTopLevel: false}) {
// This is copied from [_InvocationMirror](
// ../../../../../../runtime/lib/invocation_mirror_patch.dart).
// Constants describing the invocation type.
// _FIELD cannot be generated by regular invocation mirrors.
const int _METHOD = 0;
const int _GETTER = 1;
const int _SETTER = 2;
const int _FIELD = 3;
const int _LOCAL_VAR = 4;
// ignore: UNUSED_LOCAL_VARIABLE
const int _KIND_SHIFT = 0;
const int _KIND_BITS = 3;
// ignore: UNUSED_LOCAL_VARIABLE
const int _KIND_MASK = (1 << _KIND_BITS) - 1;
// These values, except _DYNAMIC and _SUPER, are only used when throwing
// NoSuchMethodError for compile-time resolution failures.
const int _DYNAMIC = 0;
const int _SUPER = 1;
const int _STATIC = 2;
const int _CONSTRUCTOR = 3;
const int _TOP_LEVEL = 4;
const int _LEVEL_SHIFT = _KIND_BITS;
const int _LEVEL_BITS = 3;
// ignore: UNUSED_LOCAL_VARIABLE
const int _LEVEL_MASK = (1 << _LEVEL_BITS) - 1;
int type = -1;
// For convenience, [isGetter] and [isSetter] takes precedence over
// [isMethod].
if (isGetter) {
type = _GETTER;
} else if (isSetter) {
type = _SETTER;
} else if (isMethod) {
type = _METHOD;
} else if (isField) {
type = _FIELD;
} else if (isLocalVariable) {
type = _LOCAL_VAR;
}
if (isDynamic) {
type |= (_DYNAMIC << _LEVEL_SHIFT);
} else if (isSuper) {
type |= (_SUPER << _LEVEL_SHIFT);
} else if (isStatic) {
type |= (_STATIC << _LEVEL_SHIFT);
} else if (isConstructor) {
type |= (_CONSTRUCTOR << _LEVEL_SHIFT);
} else if (isTopLevel) {
type |= (_TOP_LEVEL << _LEVEL_SHIFT);
}
return type;
}
Expression _fixedLengthList(CoreTypes coreTypes, DartType typeArgument,
List<Expression> elements, int offset) {
// TODO(ahe): It's possible that it would be better to create a fixed-length
// list first, and then populate it. That would create fewer objects. But as
// this is currently only used in (statically resolved) no-such-method
// handling, the current approach seems sufficient.
// The 0-element list must be exactly 'const[]'.
if (elements.length == 0) {
return new ListLiteral([], typeArgument: typeArgument)..isConst = true;
}
return new StaticInvocation(
coreTypes.listUnmodifiableConstructor,
new Arguments([
new ListLiteral(elements, typeArgument: typeArgument)
..fileOffset = offset
], types: [
new DynamicType()
]));
}
// TODO(sigmund,ahe): limit this to `dart-ext` libraries only (see
// https://github.com/dart-lang/sdk/issues/29763).
@override
bool enableNative(Uri uri) => true;
@override
bool get nativeExtensionExpectsString => true;
}