c878108cc8
All non-pubspec changes generated by `dart format pkg/compiler`. Change-Id: Iadaa70974816d96ccb47813fe72d5a2bb83d6bda Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/400181 Reviewed-by: Nate Biggs <natebiggs@google.com> Commit-Queue: Mayank Patke <fishythefish@google.com>
338 lines
10 KiB
Dart
338 lines
10 KiB
Dart
// Copyright (c) 2019, 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 'dart:io';
|
|
import 'package:_fe_analyzer_shared/src/testing/features.dart';
|
|
import 'package:expect/async_helper.dart';
|
|
import 'package:compiler/src/closure.dart';
|
|
import 'package:compiler/src/common.dart';
|
|
import 'package:compiler/src/common/codegen.dart';
|
|
import 'package:compiler/src/compiler.dart';
|
|
import 'package:compiler/src/elements/entities.dart';
|
|
import 'package:compiler/src/js/js.dart' as js;
|
|
import 'package:compiler/src/js_emitter/model.dart';
|
|
import 'package:compiler/src/js_model/element_map.dart';
|
|
import 'package:compiler/src/js_model/js_world.dart';
|
|
import 'package:kernel/ast.dart' as ir;
|
|
import '../equivalence/id_equivalence.dart';
|
|
import '../equivalence/id_equivalence_helper.dart';
|
|
import '../helpers/program_lookup.dart';
|
|
|
|
main(List<String> args) {
|
|
asyncTest(() async {
|
|
Directory dataDir = Directory.fromUri(
|
|
Platform.script.resolve('model_data'),
|
|
);
|
|
await checkTests(
|
|
dataDir,
|
|
const ModelDataComputer(),
|
|
args: args,
|
|
testedConfigs: allInternalConfigs,
|
|
);
|
|
});
|
|
}
|
|
|
|
class ModelDataComputer extends DataComputer<Features> {
|
|
const ModelDataComputer();
|
|
|
|
/// Compute type inference data for [member] from kernel based inference.
|
|
///
|
|
/// Fills [actualMap] with the data.
|
|
@override
|
|
void computeMemberData(
|
|
Compiler compiler,
|
|
MemberEntity member,
|
|
Map<Id, ActualData<Features>> actualMap, {
|
|
bool verbose = false,
|
|
}) {
|
|
JClosedWorld closedWorld = compiler.backendClosedWorldForTesting!;
|
|
JsToElementMap elementMap = closedWorld.elementMap;
|
|
MemberDefinition definition = elementMap.getMemberDefinition(member);
|
|
ModelIrComputer(
|
|
compiler.reporter,
|
|
actualMap,
|
|
elementMap,
|
|
member,
|
|
compiler,
|
|
closedWorld.closureDataLookup,
|
|
).run(definition.node);
|
|
}
|
|
|
|
@override
|
|
DataInterpreter<Features> get dataValidator =>
|
|
const FeaturesDataInterpreter(wildcard: '*');
|
|
}
|
|
|
|
class Tags {
|
|
static const String needsCheckedSetter = 'checked';
|
|
static const String getterFlags = 'get';
|
|
static const String setterFlags = 'set';
|
|
static const String parameterCount = 'params';
|
|
static const String call = 'calls';
|
|
static const String parameterStub = 'stubs';
|
|
static const String callStubCall = 'stubCalls';
|
|
static const String callStubAccesses = 'stubAccesses';
|
|
static const String isEmitted = 'emitted';
|
|
static const String isElided = 'elided';
|
|
static const String assignment = 'assign';
|
|
static const String isLazy = 'lazy';
|
|
static const String propertyAccess = 'access';
|
|
static const String switchCase = 'switch';
|
|
}
|
|
|
|
/// AST visitor for computing inference data for a member.
|
|
class ModelIrComputer extends IrDataExtractor<Features> {
|
|
final JsToElementMap _elementMap;
|
|
final ClosureData _closureDataLookup;
|
|
final ProgramLookup _programLookup;
|
|
|
|
ModelIrComputer(
|
|
DiagnosticReporter reporter,
|
|
Map<Id, ActualData<Features>> actualMap,
|
|
this._elementMap,
|
|
MemberEntity member,
|
|
Compiler compiler,
|
|
this._closureDataLookup,
|
|
) : _programLookup = ProgramLookup(compiler.backendStrategy),
|
|
super(reporter, actualMap);
|
|
|
|
void registerCalls(
|
|
Features features,
|
|
String tag,
|
|
js.Node node, {
|
|
String prefix = '',
|
|
Set<js.PropertyAccess>? handledAccesses,
|
|
}) {
|
|
forEachNode(
|
|
node,
|
|
onCall: (js.Call node) {
|
|
js.Node target = undefer(node.target);
|
|
if (target is js.PropertyAccess) {
|
|
js.Node selector = undefer(target.selector);
|
|
bool fixedNameCall = false;
|
|
String? name;
|
|
if (selector is js.Name) {
|
|
name = selector.key;
|
|
} else if (selector is js.LiteralString) {
|
|
/// Call to fixed backend name, so we include the argument
|
|
/// values to test encoding of optional parameters in native
|
|
/// methods.
|
|
name = selector.value;
|
|
fixedNameCall = true;
|
|
}
|
|
if (name != null) {
|
|
if (fixedNameCall) {
|
|
String arguments = node.arguments.map(js.nodeToString).join(',');
|
|
features.addElement(tag, '${prefix}${name}(${arguments})');
|
|
} else {
|
|
features.addElement(
|
|
tag,
|
|
'${prefix}${name}(${node.arguments.length})',
|
|
);
|
|
}
|
|
handledAccesses?.add(target);
|
|
}
|
|
}
|
|
},
|
|
);
|
|
}
|
|
|
|
void registerAccesses(
|
|
Features features,
|
|
String tag,
|
|
js.Node code, {
|
|
String prefix = '',
|
|
Set<js.PropertyAccess>? handledAccesses,
|
|
}) {
|
|
forEachNode(
|
|
code,
|
|
onPropertyAccess: (js.PropertyAccess node) {
|
|
if (handledAccesses?.contains(node) ?? false) {
|
|
return;
|
|
}
|
|
|
|
js.Node receiver = undefer(node.receiver);
|
|
String? receiverName;
|
|
if (receiver is js.VariableUse) {
|
|
receiverName = receiver.name;
|
|
if (receiverName == receiverName.toUpperCase() &&
|
|
receiverName != r'$') {
|
|
// Skip holder access.
|
|
receiverName = null;
|
|
}
|
|
} else if (receiver is js.This) {
|
|
receiverName = 'this';
|
|
}
|
|
|
|
js.Node selector = undefer(node.selector);
|
|
String? name;
|
|
if (selector is js.Name) {
|
|
name = selector.key;
|
|
} else if (selector is js.LiteralString) {
|
|
/// Call to fixed backend name, so we include the argument
|
|
/// values to test encoding of optional parameters in native
|
|
/// methods.
|
|
name = selector.value;
|
|
}
|
|
|
|
if (receiverName != null && name != null) {
|
|
features.addElement(tag, '${prefix}${name}');
|
|
}
|
|
},
|
|
);
|
|
}
|
|
|
|
Features? getMemberValue(MemberEntity member) {
|
|
if (member is FieldEntity) {
|
|
Field? field = _programLookup.getField(member);
|
|
if (field != null) {
|
|
Features features = Features();
|
|
if (field.needsCheckedSetter) {
|
|
features.add(Tags.needsCheckedSetter);
|
|
}
|
|
if (field.isElided) {
|
|
features.add(Tags.isElided);
|
|
} else {
|
|
features.add(Tags.isEmitted);
|
|
}
|
|
void registerFlags(String tag, int flags) {
|
|
switch (flags) {
|
|
case 0:
|
|
break;
|
|
case 1:
|
|
features.add(tag, value: 'simple');
|
|
break;
|
|
case 2:
|
|
features.add(tag, value: 'intercepted');
|
|
break;
|
|
case 3:
|
|
features.add(tag, value: 'interceptedThis');
|
|
break;
|
|
}
|
|
}
|
|
|
|
registerFlags(Tags.getterFlags, field.getterFlags);
|
|
registerFlags(Tags.setterFlags, field.setterFlags);
|
|
|
|
Class cls = _programLookup.getClass(member.enclosingClass!)!;
|
|
for (StubMethod stub in cls.callStubs) {
|
|
if (stub.element == member) {
|
|
registerCalls(
|
|
features,
|
|
Tags.callStubCall,
|
|
stub.code,
|
|
prefix: '${stub.name!.key}:',
|
|
);
|
|
registerAccesses(
|
|
features,
|
|
Tags.callStubAccesses,
|
|
stub.code,
|
|
prefix: '${stub.name!.key}:',
|
|
);
|
|
}
|
|
}
|
|
|
|
return features;
|
|
}
|
|
StaticField? staticField = _programLookup.getStaticField(member);
|
|
if (staticField != null) {
|
|
Features features = Features();
|
|
features.add(Tags.isEmitted);
|
|
if (staticField.isLazy) {
|
|
features.add(Tags.isLazy);
|
|
}
|
|
return features;
|
|
}
|
|
} else if (member is FunctionEntity) {
|
|
Method? method = _programLookup.getMethod(member);
|
|
if (method != null) {
|
|
Features features = Features();
|
|
js.Expression code = method.code;
|
|
if (code is js.Fun) {
|
|
features[Tags.parameterCount] = '${code.params.length}';
|
|
}
|
|
|
|
Set<js.PropertyAccess> handledAccesses = Set();
|
|
|
|
registerCalls(
|
|
features,
|
|
Tags.call,
|
|
code,
|
|
handledAccesses: handledAccesses,
|
|
);
|
|
if (method is DartMethod) {
|
|
for (ParameterStubMethod stub in method.parameterStubs) {
|
|
registerCalls(
|
|
features,
|
|
Tags.parameterStub,
|
|
stub.code,
|
|
prefix: '${stub.name!.key}:',
|
|
handledAccesses: handledAccesses,
|
|
);
|
|
}
|
|
}
|
|
|
|
forEachNode(
|
|
code,
|
|
onAssignment: (js.Assignment node) {
|
|
final leftHandSide = undefer(node.leftHandSide);
|
|
if (leftHandSide is js.PropertyAccess) {
|
|
js.Node selector = undefer(leftHandSide.selector);
|
|
String? name;
|
|
if (selector is js.Name) {
|
|
name = selector.key;
|
|
} else if (selector is js.LiteralString) {
|
|
name = selector.value;
|
|
}
|
|
if (name != null) {
|
|
features.addElement(Tags.assignment, '${name}');
|
|
handledAccesses.add(leftHandSide);
|
|
}
|
|
}
|
|
},
|
|
);
|
|
|
|
registerAccesses(
|
|
features,
|
|
Tags.propertyAccess,
|
|
code,
|
|
handledAccesses: handledAccesses,
|
|
);
|
|
|
|
forEachNode(
|
|
code,
|
|
onSwitch: (js.Switch node) {
|
|
features.add(Tags.switchCase);
|
|
},
|
|
);
|
|
|
|
return features;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
@override
|
|
Features? computeMemberValue(Id id, ir.Member node) {
|
|
return getMemberValue(_elementMap.getMember(node));
|
|
}
|
|
|
|
@override
|
|
Features? computeNodeValue(Id id, ir.TreeNode node) {
|
|
if (node is ir.FunctionExpression || node is ir.FunctionDeclaration) {
|
|
ClosureRepresentationInfo info = _closureDataLookup.getClosureInfo(
|
|
node as ir.LocalFunction,
|
|
);
|
|
return getMemberValue(info.callMethod!);
|
|
}
|
|
return null;
|
|
}
|
|
}
|
|
|
|
js.Node undefer(js.Node node) {
|
|
if (node is js.DeferredExpression) return undefer(node.value);
|
|
if (node is ModularName) return undefer(node.value);
|
|
return node;
|
|
}
|