[pkg/vm] Initial work on constant operating system fields and getters.

* Add an `targetOS` argument to `pkg/vm`'s `compileToKernel`,
  that contains the target operating system's name.

* Add a new `--target-os` command line argument for all binaries
  that use `compileToKernel` for clients to provide the target
  operating system, if known.

* Add a new`"vm:platform:const"` annotation to certain field and
  getters in the Platform class.

  This annotation is used to annotate static getters and fields with
  initializers where the getter body or field initializer must evaluate
  to a constant value if the target operating system is known. This
  annotation may be used outside the Platform class and in user code.

  For example, this annotation can be used on a static `String` field
  that is initialized with one value if `Platform.isWindows` is true
  and to a different value if `Platform.isWindows` is false.

  Note: If the const functions experimental flag is disabled, then
  any annotated static methods can only contain a single expression
  whose value is returned. If it is enabled, then the static method
  is evaluated as if it is a const function with the special
  handling of annotated static fields and getters above.

* Create a VM constant evaluator that evaluates uses of static getters
  and fields marked with the above annotations when a target operating
  system is provided.

* Use the new VM constant evaluator in the unreachable code elimination
  transformer.

TEST=pkg/vm/test/transformations/platform_use_transformer
     pkg/vm/test/transformations/unreachable_code_elimination

Change-Id: Ie381de70486a767fd7b1d515fd9e6bb58c6bf090
Bug: https://github.com/dart-lang/sdk/issues/31969
Cq-Include-Trybots: luci.dart.try:pkg-linux-release-try
CoreLibraryReviewExempt: Just adding vm-specific annotations.
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/274386
Commit-Queue: Tess Strickland <sstrickl@google.com>
Reviewed-by: Chloe Stefantsova <cstefantsova@google.com>
Reviewed-by: Alexander Markov <alexmarkov@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
This commit is contained in:
Tess Strickland 2023-02-10 16:29:50 +00:00 committed by Commit Queue
parent 130903685d
commit 2c0484c720
26 changed files with 784 additions and 30 deletions

View file

@ -58,12 +58,14 @@ Future<ProcessResult> generateAotKernel(
String? packages,
List<String> defines,
{String enableExperiment = '',
String? targetOS,
List<String> extraGenKernelOptions = const []}) {
return Process.run(dart, [
genKernel,
'--platform',
platformDill,
if (enableExperiment.isNotEmpty) '--enable-experiment=$enableExperiment',
if (targetOS != null) '--target-os=$targetOS',
'--aot',
'-Ddart.vm.product=true',
...(defines.map((d) => '-D$d')),

View file

@ -25,6 +25,7 @@ Future<void> generateNative({
String? outputFile,
String? debugFile,
String? packages,
String? targetOS,
required List<String> defines,
String enableExperiment = '',
bool enableAsserts = false,
@ -53,6 +54,9 @@ Future<void> generateNative({
debugFile != null ? path.canonicalize(path.normalize(debugFile)) : null;
if (verbose) {
if (targetOS != null) {
print('Specializing Platform getters for target OS $targetOS.');
}
print('Compiling $sourcePath to $outputPath using format $kind:');
print('Generating AOT kernel dill.');
}
@ -61,6 +65,7 @@ Future<void> generateNative({
final kernelResult = await generateAotKernel(Platform.executable, genKernel,
productPlatformDill, sourcePath, kernelFile, packages, defines,
enableExperiment: enableExperiment,
targetOS: targetOS,
extraGenKernelOptions: [
'--invocation-modes=compile',
'--verbosity=$verbosity',

View file

@ -10,6 +10,7 @@ import 'package:dart2native/generate.dart';
import 'package:front_end/src/api_prototype/compiler_options.dart'
show Verbosity;
import 'package:path/path.dart' as path;
import 'package:vm/target_os.dart'; // For possible --target-os values.
import '../core.dart';
import '../experiments.dart';
@ -281,6 +282,9 @@ Remove debugging information from the output and save it separately to the speci
hide: true,
valueHelp: 'opt1,opt2,...',
)
..addOption('target-os',
help: 'Compile to a specific target operating system.',
allowed: TargetOS.names)
..addExperimentalFlags(verbose: verbose);
}
@ -327,6 +331,7 @@ Remove debugging information from the output and save it separately to the speci
verbose: verbose,
verbosity: args['verbosity'],
extraOptions: args['extra-gen-snapshot-options'],
targetOS: args['target-os'],
);
return 0;
} catch (e, st) {

View file

@ -25,6 +25,7 @@ dependencies:
pub: any
telemetry: any
usage: any
vm: any
yaml: any
# Use 'any' constraints here; we get our versions from the DEPS file.

View file

@ -35,6 +35,7 @@ import 'package:_fe_analyzer_shared/src/exhaustiveness/exhaustive.dart';
import 'package:_fe_analyzer_shared/src/exhaustiveness/space.dart';
import 'package:_fe_analyzer_shared/src/exhaustiveness/static_type.dart';
import '../../base/nnbd_mode.dart';
import '../fasta_codes.dart';
import 'constant_int_folder.dart';
@ -177,7 +178,18 @@ void transformProcedure(
enum EvaluationMode {
weak,
agnostic,
strong,
strong;
static EvaluationMode fromNnbdMode(NnbdMode nnbdMode) {
switch (nnbdMode) {
case NnbdMode.Weak:
return EvaluationMode.weak;
case NnbdMode.Strong:
return EvaluationMode.strong;
case NnbdMode.Agnostic:
return EvaluationMode.agnostic;
}
}
}
class ConstantWeakener extends ComputeOnceConstantVisitor<Constant?> {
@ -3144,7 +3156,7 @@ class ConstantEvaluator implements ExpressionVisitor<Constant> {
/// This compute the constant as seen in the current evaluation mode even when
/// the constant is defined in a library compiled with the agnostic evaluation
/// mode.
Constant _evaluateExpressionInContext(Member member, Expression expression) {
Constant evaluateExpressionInContext(Member member, Expression expression) {
StaticTypeContext? oldStaticTypeContext = _staticTypeContext;
_staticTypeContext = new StaticTypeContext(member, typeEnvironment);
Constant constant = _evaluateSubexpression(expression);
@ -3166,7 +3178,7 @@ class ConstantEvaluator implements ExpressionVisitor<Constant> {
visitedLibraries.add(target.enclosingLibrary);
if (target is Field) {
if (target.isConst) {
return _evaluateExpressionInContext(target, target.initializer!);
return evaluateExpressionInContext(target, target.initializer!);
}
return createEvaluationErrorConstant(
node,
@ -3241,7 +3253,7 @@ class ConstantEvaluator implements ExpressionVisitor<Constant> {
Constant _getFromEnvironmentDefaultValue(Procedure target) {
VariableDeclaration variable = target.function.namedParameters
.singleWhere((v) => v.name == 'defaultValue');
return _evaluateExpressionInContext(target, variable.initializer!);
return evaluateExpressionInContext(target, variable.initializer!);
}
Constant _handleFromEnvironment(

View file

@ -1618,7 +1618,6 @@ class KernelTarget extends TargetImplementation {
_getConstantEvaluationMode();
constants.EvaluationMode _getConstantEvaluationMode() {
constants.EvaluationMode evaluationMode;
// If nnbd is not enabled we will use weak evaluation mode. This is needed
// because the SDK might be agnostic and therefore needs to be weakened
// for legacy mode.
@ -1627,18 +1626,7 @@ class KernelTarget extends TargetImplementation {
loader.nnbdMode == NnbdMode.Weak,
"Non-weak nnbd mode found without experiment enabled: "
"${loader.nnbdMode}.");
switch (loader.nnbdMode) {
case NnbdMode.Weak:
evaluationMode = constants.EvaluationMode.weak;
break;
case NnbdMode.Strong:
evaluationMode = constants.EvaluationMode.strong;
break;
case NnbdMode.Agnostic:
evaluationMode = constants.EvaluationMode.agnostic;
break;
}
return evaluationMode;
return constants.EvaluationMode.fromNnbdMode(loader.nnbdMode);
}
void verify() {

View file

@ -6,7 +6,7 @@ name: front_end
publish_to: none
environment:
sdk: '>=2.13.0 <3.0.0'
sdk: '>=2.17.0 <3.0.0'
# Use 'any' constraints here; we get our versions from the DEPS file.
dependencies:

View file

@ -35,6 +35,7 @@ import 'package:usage/uuid/uuid.dart';
import 'package:vm/incremental_compiler.dart' show IncrementalCompiler;
import 'package:vm/kernel_front_end.dart';
import 'package:vm/target_os.dart'; // For possible --target-os values.
import 'src/javascript_bundle.dart';
@ -51,6 +52,9 @@ ArgParser argParser = ArgParser(allowTrailingOptions: true)
..addFlag('aot',
help: 'Run compiler in AOT mode (enables whole-program transformations)',
defaultsTo: false)
..addOption('target-os',
help: 'Compile to a specific target operating system.',
allowed: TargetOS.names)
..addFlag('support-mirrors',
help: 'Whether dart:mirrors is supported. By default dart:mirrors is '
'supported when --aot and --minimal-kernel are not used.',
@ -513,6 +517,13 @@ class FrontendCompiler implements CompilerInterface {
}
}
if (options['target-os'] != null) {
if (!options['aot']) {
print('Error: --target-os option must be used with --aot');
return false;
}
}
if (options['support-mirrors'] == true) {
if (options['aot']) {
print('Error: --support-mirrors option cannot be used with --aot');
@ -592,6 +603,7 @@ class FrontendCompiler implements CompilerInterface {
includePlatform: options['link-platform'],
deleteToStringPackageUris: options['delete-tostring-package-uri'],
aot: options['aot'],
targetOS: options['target-os'],
useGlobalTypeFlowAnalysis: options['tfa'],
useRapidTypeAnalysis: options['rta'],
environmentDefines: environmentDefines,

View file

@ -214,6 +214,8 @@ class CoreTypes {
late final Procedure objectEquals =
index.getProcedure('dart:core', 'Object', '==');
late final Class? platformClass = index.tryGetClass('dart:io', 'Platform');
late final Class pragmaClass = index.getClass('dart:core', 'pragma');
late final Field pragmaName = index.getField('dart:core', 'pragma', 'name');

View file

@ -46,6 +46,7 @@ import 'package:kernel/target/targets.dart' show Target, TargetFlags, getTarget;
import 'package:package_config/package_config.dart' show loadPackageConfigUri;
import 'http_filesystem.dart' show HttpAwareFileSystem;
import 'target_os.dart';
import 'native_assets/synthesizer.dart';
import 'target/install.dart' show installAdditionalTargets;
import 'transformations/devirtualization.dart' as devirtualization
@ -61,6 +62,7 @@ import 'transformations/obfuscation_prohibitions_annotator.dart'
import 'transformations/call_site_annotator.dart' as call_site_annotator;
import 'transformations/unreachable_code_elimination.dart'
as unreachable_code_elimination;
import 'transformations/vm_constant_evaluator.dart' as vm_constant_evaluator;
import 'transformations/deferred_loading.dart' as deferred_loading;
import 'transformations/to_string_transformer.dart' as to_string_transformer;
@ -111,6 +113,9 @@ void declareCompilerOptions(ArgParser args) {
help:
'Enable global type flow analysis and related transformations in AOT mode.',
defaultsTo: true);
args.addOption('target-os',
help: 'Compile for a specific target operating system when in AOT mode.',
allowed: TargetOS.names);
args.addFlag('rta',
help: 'Use rapid type analysis for faster compilation in AOT mode.',
defaultsTo: true);
@ -193,6 +198,7 @@ Future<int> runCompiler(ArgResults options, String usage) async {
final String? depfile = options['depfile'];
final String? fromDillFile = options['from-dill'];
final List<String>? fileSystemRoots = options['filesystem-root'];
final String? targetOS = options['target-os'];
final bool aot = options['aot'];
final bool tfa = options['tfa'];
final bool rta = options['rta'];
@ -305,6 +311,7 @@ Future<int> runCompiler(ArgResults options, String usage) async {
useProtobufTreeShakerV2: useProtobufTreeShakerV2,
minimalKernel: minimalKernel,
treeShakeWriteOnlyFields: treeShakeWriteOnlyFields,
targetOS: targetOS,
fromDillFile: fromDillFile);
errorPrinter.printCompilationMessages();
@ -406,6 +413,7 @@ Future<KernelCompilationResults> compileToKernel(
bool useProtobufTreeShakerV2 = false,
bool minimalKernel = false,
bool treeShakeWriteOnlyFields = false,
String? targetOS = null,
String? fromDillFile = null,
}) async {
// Replace error handler to detect if there are compilation errors.
@ -450,6 +458,9 @@ Future<KernelCompilationResults> compileToKernel(
if ((aot || minimalKernel) && component != null) {
await runGlobalTransformations(target, component, useGlobalTypeFlowAnalysis,
enableAsserts, useProtobufTreeShakerV2, errorDetector,
environmentDefines: options.environmentDefines,
nnbdMode: options.nnbdMode,
targetOS: targetOS,
minimalKernel: minimalKernel,
treeShakeWriteOnlyFields: treeShakeWriteOnlyFields,
useRapidTypeAnalysis: useRapidTypeAnalysis);
@ -512,7 +523,10 @@ Future runGlobalTransformations(
ErrorDetector errorDetector,
{bool minimalKernel = false,
bool treeShakeWriteOnlyFields = false,
bool useRapidTypeAnalysis = true}) async {
bool useRapidTypeAnalysis = true,
NnbdMode nnbdMode = NnbdMode.Weak,
Map<String, String>? environmentDefines,
String? targetOS}) async {
assert(!target.flags.supportMirrors);
if (errorDetector.hasCompilationErrors) return;
@ -526,9 +540,14 @@ Future runGlobalTransformations(
// when building a platform dill file for VM/JIT case.
mixin_deduplication.transformComponent(component);
// Unreachable code elimination transformation should be performed
// before type flow analysis so TFA won't take unreachable code into account.
unreachable_code_elimination.transformComponent(component, enableAsserts);
// Perform unreachable code elimination, which should be performed before
// type flow analysis so TFA won't take unreachable code into account.
final os = targetOS != null ? TargetOS.fromString(targetOS)! : null;
final evaluator = vm_constant_evaluator.VMConstantEvaluator.create(
target, component, os, nnbdMode,
environmentDefines: environmentDefines, coreTypes: coreTypes);
unreachable_code_elimination.transformComponent(
component, enableAsserts, evaluator);
if (useGlobalTypeFlowAnalysis) {
globalTypeFlow.transformComponent(target, coreTypes, component,

29
pkg/vm/lib/target_os.dart Normal file
View file

@ -0,0 +1,29 @@
// 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.
enum TargetOS {
android('android', '/'),
fuchsia('fuchsia', '/'),
iOS('ios', '/'),
linux('linux', '/'),
macOS('macos', '/'),
windows('windows', '\\');
final String name;
final String pathSeparator;
const TargetOS(this.name, this.pathSeparator);
static final Iterable<String> names = values.map((v) => v.name);
static TargetOS? fromString(String s) {
for (final os in values) {
if (os.name == s) return os;
}
return null;
}
@override
String toString() => name;
}

View file

@ -3,20 +3,38 @@
// BSD-style license that can be found in the LICENSE file.
import 'package:kernel/ast.dart';
import 'package:kernel/type_environment.dart' show StaticTypeContext;
import 'vm_constant_evaluator.dart' show VMConstantEvaluator;
/// Simple unreachable code elimination: removes asserts and if statements
/// with constant conditions. Does a very limited constant folding of
/// logical expressions.
Component transformComponent(Component component, bool enableAsserts) {
new SimpleUnreachableCodeElimination(enableAsserts)
///
/// Also performs some additional constant evaluation via [evaluator], which is
/// applied to certain types of expressions (currently only StaticGet).
Component transformComponent(
Component component, bool enableAsserts, VMConstantEvaluator evaluator) {
SimpleUnreachableCodeElimination(enableAsserts, evaluator)
.visitComponent(component, null);
return component;
}
class SimpleUnreachableCodeElimination extends RemovingTransformer {
final bool enableAsserts;
final VMConstantEvaluator constantEvaluator;
StaticTypeContext? _staticTypeContext;
SimpleUnreachableCodeElimination(this.enableAsserts);
SimpleUnreachableCodeElimination(this.enableAsserts, this.constantEvaluator);
@override
TreeNode defaultMember(Member node, TreeNode? removalSentinel) {
_staticTypeContext =
StaticTypeContext(node, constantEvaluator.typeEnvironment);
final result = super.defaultMember(node, removalSentinel);
_staticTypeContext = null;
return result;
}
bool _isBoolConstant(Expression node) =>
node is BoolLiteral ||
@ -115,6 +133,13 @@ class SimpleUnreachableCodeElimination extends RemovingTransformer {
if (target is Field && target.isConst) {
throw 'StaticGet from const field $target should be evaluated by front-end: $node';
}
if (constantEvaluator.transformerShouldEvaluateExpression(node)) {
final context = _staticTypeContext!;
final result = constantEvaluator.evaluate(context, node);
assert(result is! UnevaluatedConstant);
return new ConstantExpression(result, node.getStaticType(context))
..fileOffset = node.fileOffset;
}
return node;
}

View file

@ -0,0 +1,179 @@
// 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/class_hierarchy.dart' show ClassHierarchy;
import 'package:kernel/core_types.dart' show CoreTypes;
import 'package:kernel/target/targets.dart'
show ConstantsBackend, DartLibrarySupport, Target;
import 'package:kernel/type_environment.dart';
import 'package:front_end/src/base/nnbd_mode.dart';
import 'package:front_end/src/fasta/kernel/constant_evaluator.dart'
show
AbortConstant,
ConstantEvaluator,
ErrorReporter,
EvaluationMode,
SimpleErrorReporter;
import '../target_os.dart';
/// Evaluates uses of static fields and getters using VM-specific and
/// platform-specific knowledge.
///
/// [targetOS] represents the target operating system and is used when
/// evaluating static fields and getters annotated with "vm:platform-const".
///
/// If [enableConstFunctions] is false, then only getters that return the
/// result of a single expression can be evaluated.
class VMConstantEvaluator extends ConstantEvaluator {
final TargetOS? _targetOS;
final Map<String, Constant> _constantFields = {};
final Class? _platformClass;
final Class _pragmaClass;
final Field _pragmaName;
VMConstantEvaluator(
DartLibrarySupport dartLibrarySupport,
ConstantsBackend backend,
Component component,
Map<String, String>? environmentDefines,
TypeEnvironment typeEnvironment,
ErrorReporter errorReporter,
this._targetOS,
{bool enableTripleShift = false,
bool enableConstFunctions = false,
bool errorOnUnevaluatedConstant = false,
EvaluationMode evaluationMode = EvaluationMode.weak})
: _platformClass = typeEnvironment.coreTypes.platformClass,
_pragmaClass = typeEnvironment.coreTypes.pragmaClass,
_pragmaName = typeEnvironment.coreTypes.pragmaName,
super(dartLibrarySupport, backend, component, environmentDefines,
typeEnvironment, errorReporter,
enableTripleShift: enableTripleShift,
enableConstFunctions: enableConstFunctions,
errorOnUnevaluatedConstant: errorOnUnevaluatedConstant,
evaluationMode: evaluationMode) {
// Only add Platform fields if the Platform class is part of the component
// being evaluated.
final os = _targetOS;
if (os != null && _platformClass != null) {
_constantFields['_operatingSystem'] = StringConstant(os.name);
_constantFields['_pathSeparator'] = StringConstant(os.pathSeparator);
}
}
static VMConstantEvaluator create(
Target target, Component component, TargetOS? targetOS, NnbdMode nnbdMode,
{bool evaluateAnnotations = true,
bool enableTripleShift = false,
bool enableConstFunctions = false,
bool enableConstructorTearOff = false,
bool errorOnUnevaluatedConstant = false,
Map<String, String>? environmentDefines,
CoreTypes? coreTypes,
ClassHierarchy? hierarchy}) {
coreTypes ??= CoreTypes(component);
hierarchy ??= ClassHierarchy(component, coreTypes);
final typeEnvironment = TypeEnvironment(coreTypes, hierarchy);
// Use the empty environment if unevaluated constants are not supported,
// as passing null for environmentDefines in this case is an error.
environmentDefines ??=
target.constantsBackend.supportsUnevaluatedConstants ? null : {};
return VMConstantEvaluator(
target.dartLibrarySupport,
target.constantsBackend,
component,
environmentDefines,
typeEnvironment,
SimpleErrorReporter(),
targetOS,
enableTripleShift: enableTripleShift,
enableConstFunctions: enableConstFunctions,
errorOnUnevaluatedConstant: errorOnUnevaluatedConstant,
evaluationMode: EvaluationMode.fromNnbdMode(nnbdMode));
}
/// Used for methods and fields with initializers where the method body or
/// field initializer must evaluate to a constant value when a target
/// operating system is provided.
static const _constPragmaName = "vm:platform-const";
bool _hasAnnotation(Annotatable node, String name) {
for (final annotation in node.annotations) {
if (annotation is ConstantExpression) {
final constant = annotation.constant;
if (constant is InstanceConstant &&
constant.classNode == _pragmaClass &&
constant.fieldValues[_pragmaName.fieldReference] ==
StringConstant(name)) {
return true;
}
}
}
return false;
}
bool _isPlatformConst(Member node) => _hasAnnotation(node, _constPragmaName);
bool transformerShouldEvaluateExpression(Expression node) =>
_targetOS != null && node is StaticGet && _isPlatformConst(node.target);
@override
Constant visitStaticGet(StaticGet node) {
assert(_targetOS != null);
final target = node.target;
// This visitor can be called recursively while evaluating an abstraction
// over the Platform getters and fields, so check that the visited node has
// an appropriately annotated target.
if (!_isPlatformConst(target)) return super.visitStaticGet(node);
return withNewEnvironment(() {
final nameText = target.name.text;
visitedLibraries.add(target.enclosingLibrary);
// First, check for the fields in Platform whose initializers should not
// be evaluated, but instead uses of the field should just be replaced
// directly with an already calculated constant.
if (target is Field && target.enclosingClass == _platformClass) {
final constant = _constantFields[nameText];
if (constant != null) {
return canonicalize(constant);
}
}
late Constant result;
if (target is Field && target.initializer != null) {
result = evaluateExpressionInContext(target, target.initializer!);
} else if (target is Procedure && target.isGetter) {
final body = target.function.body!;
// If const functions are enabled, execute the getter as if it were
// a const function. Otherwise the annotated getter must be a single
// return statement whose expression is evaluated.
if (enableConstFunctions) {
result = executeBody(body);
} else if (body is ReturnStatement) {
if (body.expression == null) {
return canonicalize(NullConstant());
}
result = evaluateExpressionInContext(target, body.expression!);
} else {
throw "Cannot evaluate method '$nameText' since it contains more "
"than a single return statement.";
}
}
if (result is AbortConstant) {
throw "The body or initialization of member '$nameText' does not "
"evaluate to a constant value for the specified target operating "
"system '$_targetOS'.";
}
return result;
});
}
}

View file

@ -4,7 +4,7 @@ description: VM specific Dart code and helper scripts
publish_to: none
environment:
sdk: '>=2.15.0 <3.0.0'
sdk: '>=2.17.0 <3.0.0'
# Use 'any' constraints here; we get our versions from the DEPS file.
dependencies:

View file

@ -4,6 +4,7 @@
import 'dart:io';
import 'package:front_end/src/base/nnbd_mode.dart';
import 'package:kernel/target/targets.dart';
import 'package:kernel/ast.dart';
import 'package:kernel/kernel.dart';
@ -12,6 +13,7 @@ import 'package:test/test.dart';
import 'package:vm/target/vm.dart' show VmTarget;
import 'package:vm/transformations/unreachable_code_elimination.dart'
show transformComponent;
import 'package:vm/transformations/vm_constant_evaluator.dart';
import '../common_test_utils.dart';
@ -26,7 +28,11 @@ runTestCase(Uri source) async {
'test.define.isFalse': 'false'
});
component = transformComponent(component, /* enableAsserts = */ false);
// Do not perform constant evaluation for a specific target operating system.
final evaluator = VMConstantEvaluator.create(
target, component, /* targetOS = */ null, NnbdMode.Strong);
component =
transformComponent(component, /* enableAsserts = */ false, evaluator);
verifyComponent(component);
final actual = kernelLibraryToString(component.mainMethod!.enclosingLibrary);

View file

@ -0,0 +1,59 @@
// 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 'dart:io';
import 'package:front_end/src/base/nnbd_mode.dart';
import 'package:kernel/target/targets.dart';
import 'package:kernel/ast.dart';
import 'package:kernel/kernel.dart';
import 'package:kernel/verifier.dart';
import 'package:path/path.dart' as path;
import 'package:test/test.dart';
import 'package:vm/target_os.dart';
import 'package:vm/target/vm.dart' show VmTarget;
import 'package:vm/transformations/unreachable_code_elimination.dart'
show transformComponent;
import 'package:vm/transformations/vm_constant_evaluator.dart';
import '../common_test_utils.dart';
final String pkgVmDir = Platform.script.resolve('../..').toFilePath();
runTestCase(Uri source, TargetOS os) async {
final target = new VmTarget(new TargetFlags());
Component component = await compileTestCaseToKernelProgram(source,
target: target,
environmentDefines: {
'test.define.isTrue': 'true',
'test.define.isFalse': 'false'
});
final evaluator =
VMConstantEvaluator.create(target, component, os, NnbdMode.Strong);
final enableAsserts = false;
component = transformComponent(component, enableAsserts, evaluator);
verifyComponent(component);
final actual = kernelLibraryToString(component.mainMethod!.enclosingLibrary);
final postfix = '.${os.name}';
compareResultWithExpectationsFile(source, actual, expectFilePostfix: postfix);
}
main() {
group('platform-use-transformation', () {
final testCasesPath = path.join(
pkgVmDir, 'testcases', 'transformations', 'vm_constant_evaluator');
for (var entry in Directory(testCasesPath)
.listSync(recursive: true, followLinks: false)
.reversed) {
if (entry.path.endsWith(".dart")) {
for (final os in TargetOS.values) {
test('${entry.path}.${os.name}', () => runTestCase(entry.uri, os));
}
}
}
});
}

View file

@ -0,0 +1,82 @@
// 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 'dart:io';
void testAndroid(int i) {
final b = Platform.isAndroid;
print(b);
if (Platform.isAndroid) {
final os = Platform.operatingSystem;
print(os);
final sep = Platform.pathSeparator;
print(sep);
}
}
void testFuchsia(int i) {
final b = Platform.isFuchsia;
print(b);
if (Platform.isFuchsia) {
final os = Platform.operatingSystem;
print(os);
final sep = Platform.pathSeparator;
print(sep);
}
}
void testIOS(int i) {
final b = Platform.isIOS;
print(b);
if (Platform.isIOS) {
final os = Platform.operatingSystem;
print(os);
final sep = Platform.pathSeparator;
print(sep);
}
}
void testLinux(int i) {
final b = Platform.isLinux;
print(b);
if (Platform.isLinux) {
final os = Platform.operatingSystem;
print(os);
final sep = Platform.pathSeparator;
print(sep);
}
}
void testMacOS(int i) {
final b = Platform.isMacOS;
print(b);
if (Platform.isMacOS) {
final os = Platform.operatingSystem;
print(os);
final sep = Platform.pathSeparator;
print(sep);
}
}
void testWindows(int i) {
final b = Platform.isWindows;
print(b);
if (Platform.isWindows) {
final os = Platform.operatingSystem;
print(os);
final sep = Platform.pathSeparator;
print(sep);
}
}
main(List<String> args) {
if (args.isEmpty) return;
final i = int.parse(args[0]);
testAndroid(i);
testFuchsia(i);
testIOS(i);
testLinux(i);
testMacOS(i);
testWindows(i);
}

View file

@ -0,0 +1,53 @@
library #lib /*isNonNullableByDefault*/;
import self as self;
import "dart:core" as core;
import "dart:io";
static method testAndroid(core::int i) → void {
final core::bool b = #C1;
core::print(b);
{
final core::String os = #C2;
core::print(os);
final core::String sep = #C3;
core::print(sep);
}
}
static method testFuchsia(core::int i) → void {
final core::bool b = #C4;
core::print(b);
}
static method testIOS(core::int i) → void {
final core::bool b = #C4;
core::print(b);
}
static method testLinux(core::int i) → void {
final core::bool b = #C4;
core::print(b);
}
static method testMacOS(core::int i) → void {
final core::bool b = #C4;
core::print(b);
}
static method testWindows(core::int i) → void {
final core::bool b = #C4;
core::print(b);
}
static method main(core::List<core::String> args) → dynamic {
if(args.{core::Iterable::isEmpty}{core::bool})
return;
final core::int i = core::int::parse(args.{core::List::[]}(0){(core::int) → core::String});
self::testAndroid(i);
self::testFuchsia(i);
self::testIOS(i);
self::testLinux(i);
self::testMacOS(i);
self::testWindows(i);
}
constants {
#C1 = true
#C2 = "android"
#C3 = "/"
#C4 = false
}

View file

@ -0,0 +1,53 @@
library #lib /*isNonNullableByDefault*/;
import self as self;
import "dart:core" as core;
import "dart:io";
static method testAndroid(core::int i) → void {
final core::bool b = #C1;
core::print(b);
}
static method testFuchsia(core::int i) → void {
final core::bool b = #C2;
core::print(b);
{
final core::String os = #C3;
core::print(os);
final core::String sep = #C4;
core::print(sep);
}
}
static method testIOS(core::int i) → void {
final core::bool b = #C1;
core::print(b);
}
static method testLinux(core::int i) → void {
final core::bool b = #C1;
core::print(b);
}
static method testMacOS(core::int i) → void {
final core::bool b = #C1;
core::print(b);
}
static method testWindows(core::int i) → void {
final core::bool b = #C1;
core::print(b);
}
static method main(core::List<core::String> args) → dynamic {
if(args.{core::Iterable::isEmpty}{core::bool})
return;
final core::int i = core::int::parse(args.{core::List::[]}(0){(core::int) → core::String});
self::testAndroid(i);
self::testFuchsia(i);
self::testIOS(i);
self::testLinux(i);
self::testMacOS(i);
self::testWindows(i);
}
constants {
#C1 = false
#C2 = true
#C3 = "fuchsia"
#C4 = "/"
}

View file

@ -0,0 +1,53 @@
library #lib /*isNonNullableByDefault*/;
import self as self;
import "dart:core" as core;
import "dart:io";
static method testAndroid(core::int i) → void {
final core::bool b = #C1;
core::print(b);
}
static method testFuchsia(core::int i) → void {
final core::bool b = #C1;
core::print(b);
}
static method testIOS(core::int i) → void {
final core::bool b = #C2;
core::print(b);
{
final core::String os = #C3;
core::print(os);
final core::String sep = #C4;
core::print(sep);
}
}
static method testLinux(core::int i) → void {
final core::bool b = #C1;
core::print(b);
}
static method testMacOS(core::int i) → void {
final core::bool b = #C1;
core::print(b);
}
static method testWindows(core::int i) → void {
final core::bool b = #C1;
core::print(b);
}
static method main(core::List<core::String> args) → dynamic {
if(args.{core::Iterable::isEmpty}{core::bool})
return;
final core::int i = core::int::parse(args.{core::List::[]}(0){(core::int) → core::String});
self::testAndroid(i);
self::testFuchsia(i);
self::testIOS(i);
self::testLinux(i);
self::testMacOS(i);
self::testWindows(i);
}
constants {
#C1 = false
#C2 = true
#C3 = "ios"
#C4 = "/"
}

View file

@ -0,0 +1,53 @@
library #lib /*isNonNullableByDefault*/;
import self as self;
import "dart:core" as core;
import "dart:io";
static method testAndroid(core::int i) → void {
final core::bool b = #C1;
core::print(b);
}
static method testFuchsia(core::int i) → void {
final core::bool b = #C1;
core::print(b);
}
static method testIOS(core::int i) → void {
final core::bool b = #C1;
core::print(b);
}
static method testLinux(core::int i) → void {
final core::bool b = #C2;
core::print(b);
{
final core::String os = #C3;
core::print(os);
final core::String sep = #C4;
core::print(sep);
}
}
static method testMacOS(core::int i) → void {
final core::bool b = #C1;
core::print(b);
}
static method testWindows(core::int i) → void {
final core::bool b = #C1;
core::print(b);
}
static method main(core::List<core::String> args) → dynamic {
if(args.{core::Iterable::isEmpty}{core::bool})
return;
final core::int i = core::int::parse(args.{core::List::[]}(0){(core::int) → core::String});
self::testAndroid(i);
self::testFuchsia(i);
self::testIOS(i);
self::testLinux(i);
self::testMacOS(i);
self::testWindows(i);
}
constants {
#C1 = false
#C2 = true
#C3 = "linux"
#C4 = "/"
}

View file

@ -0,0 +1,53 @@
library #lib /*isNonNullableByDefault*/;
import self as self;
import "dart:core" as core;
import "dart:io";
static method testAndroid(core::int i) → void {
final core::bool b = #C1;
core::print(b);
}
static method testFuchsia(core::int i) → void {
final core::bool b = #C1;
core::print(b);
}
static method testIOS(core::int i) → void {
final core::bool b = #C1;
core::print(b);
}
static method testLinux(core::int i) → void {
final core::bool b = #C1;
core::print(b);
}
static method testMacOS(core::int i) → void {
final core::bool b = #C2;
core::print(b);
{
final core::String os = #C3;
core::print(os);
final core::String sep = #C4;
core::print(sep);
}
}
static method testWindows(core::int i) → void {
final core::bool b = #C1;
core::print(b);
}
static method main(core::List<core::String> args) → dynamic {
if(args.{core::Iterable::isEmpty}{core::bool})
return;
final core::int i = core::int::parse(args.{core::List::[]}(0){(core::int) → core::String});
self::testAndroid(i);
self::testFuchsia(i);
self::testIOS(i);
self::testLinux(i);
self::testMacOS(i);
self::testWindows(i);
}
constants {
#C1 = false
#C2 = true
#C3 = "macos"
#C4 = "/"
}

View file

@ -0,0 +1,53 @@
library #lib /*isNonNullableByDefault*/;
import self as self;
import "dart:core" as core;
import "dart:io";
static method testAndroid(core::int i) → void {
final core::bool b = #C1;
core::print(b);
}
static method testFuchsia(core::int i) → void {
final core::bool b = #C1;
core::print(b);
}
static method testIOS(core::int i) → void {
final core::bool b = #C1;
core::print(b);
}
static method testLinux(core::int i) → void {
final core::bool b = #C1;
core::print(b);
}
static method testMacOS(core::int i) → void {
final core::bool b = #C1;
core::print(b);
}
static method testWindows(core::int i) → void {
final core::bool b = #C2;
core::print(b);
{
final core::String os = #C3;
core::print(os);
final core::String sep = #C4;
core::print(sep);
}
}
static method main(core::List<core::String> args) → dynamic {
if(args.{core::Iterable::isEmpty}{core::bool})
return;
final core::int i = core::int::parse(args.{core::List::[]}(0){(core::int) → core::String});
self::testAndroid(i);
self::testFuchsia(i);
self::testIOS(i);
self::testLinux(i);
self::testMacOS(i);
self::testWindows(i);
}
constants {
#C1 = false
#C2 = true
#C3 = "windows"
#C4 = "\\"
}

View file

@ -12,7 +12,8 @@ These pragmas are part of the VM's API and are safe for use in external code.
| `vm:notify-debugger-on-exception` | Marks a function that catches exceptions, making the VM treat any caught exception as if they were uncaught. This can be used to notify an attached debugger during debugging, without pausing the app during regular execution. |
| `vm:external-name` | Allows to specify an external (native) name for an `external` function. This name is used to lookup native implementation via native resolver associated with the current library through embedding APIs. This is a replacement for legacy VM specific `native "name"` syntax. |
| `vm:invisible` | Allows to mark a function as invisible so it will not appear on stack traces. |
| `vm:always-consider-inlining` | Marks a function which particularly benefits from inlining and specialization in context of the caller (for example, when concrete types of arguments are known). Inliner will not give up after one failed inlining attempt and will continue trying to inline this function.
| `vm:always-consider-inlining` | Marks a function which particularly benefits from inlining and specialization in context of the caller (for example, when concrete types of arguments are known). Inliner will not give up after one failed inlining attempt and will continue trying to inline this function. |
| `vm:platform-const` | Marks a static getter or a static field with an initializer where the getter body or field initializer evaluates to a constant value if the target operating system is known. |
## Unsafe pragmas for general use

View file

@ -60,7 +60,9 @@ part of dart.io;
/// ```
class Platform {
static final _numberOfProcessors = _Platform.numberOfProcessors;
@pragma("vm:platform-const")
static final _pathSeparator = _Platform.pathSeparator;
@pragma("vm:platform-const")
static final _operatingSystem = _Platform.operatingSystem;
static final _operatingSystemVersion = _Platform.operatingSystemVersion;
static final _localHostname = _Platform.localHostname;
@ -71,6 +73,7 @@ class Platform {
/// The path separator used by the operating system to separate
/// components in file paths.
@pragma("vm:platform-const")
static String get pathSeparator => _pathSeparator;
/// Get the name of the current locale.
@ -106,6 +109,7 @@ class Platform {
///
/// Note that this list may change over time so platform-specific logic
/// should be guarded by the appropriate boolean getter e.g. [isMacOS].
@pragma("vm:platform-const")
static String get operatingSystem => _operatingSystem;
/// A string representing the version of the operating system or platform.
@ -134,26 +138,32 @@ class Platform {
/// This value is `false` if the operating system is a specialized
/// version of Linux that identifies itself by a different name,
/// for example Android (see [isAndroid]).
@pragma("vm:platform-const")
static final bool isLinux = (_operatingSystem == "linux");
/// Whether the operating system is a version of
/// [macOS](https://en.wikipedia.org/wiki/MacOS).
@pragma("vm:platform-const")
static final bool isMacOS = (_operatingSystem == "macos");
/// Whether the operating system is a version of
/// [Microsoft Windows](https://en.wikipedia.org/wiki/Microsoft_Windows).
@pragma("vm:platform-const")
static final bool isWindows = (_operatingSystem == "windows");
/// Whether the operating system is a version of
/// [Android](https://en.wikipedia.org/wiki/Android_%28operating_system%29).
@pragma("vm:platform-const")
static final bool isAndroid = (_operatingSystem == "android");
/// Whether the operating system is a version of
/// [iOS](https://en.wikipedia.org/wiki/IOS).
@pragma("vm:platform-const")
static final bool isIOS = (_operatingSystem == "ios");
/// Whether the operating system is a version of
/// [Fuchsia](https://en.wikipedia.org/wiki/Google_Fuchsia).
@pragma("vm:platform-const")
static final bool isFuchsia = (_operatingSystem == "fuchsia");
/// The environment for this process as a map from string key to string value.

View file

@ -82,8 +82,7 @@ class _Platform {
if (_environmentCache == null) {
var env = _environment();
if (env is Iterable<Object?>) {
var isWindows = operatingSystem == 'windows';
var result = isWindows
var result = Platform.isWindows
? new _CaseInsensitiveStringMap<String>()
: new Map<String, String>();
for (var environmentEntry in env) {