Files
sdk/pkg/dev_compiler/test/expression_compiler/expression_compiler_suite.dart
T
Nicholas Shahan 559b8552a8 [ddc,frontend_server] Fix expression compilation after recompile
Update the last known good component after a recompile reject back to
the expected state before the recompile.

Adds tests for expression compilation after recompile accept and
recompile reject cases with `--target=dartdevc`. Similar to existing
tests for the VM.

Removes calls to `_generator.accept()` and
`component.computeCanonicalNames()` from `compileExpressionToJs()`.
These were added very early in the prototype implementation and it is no
longer clear why they would be needed.

Added some additional test cases for expression compilation involving
import resolution because the change that originally added the
`component.computeCanonicalNames()` call implies it was needed for that
reason. See: https://dart-review.googlesource.com/c/sdk/+/138010

TEST=pkg/frontend_server/test/frontend_server_test.dart,pkg/dev_compiler/test/expression_compiler/expression_compiler_test.dart

Change-Id: I9c04bd46e56b33dc654d00fb330de29c3c643a5b
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/434522
Reviewed-by: Alexander Markov <alexmarkov@google.com>
Commit-Queue: Nicholas Shahan <nshahan@google.com>
Reviewed-by: Nate Biggs <natebiggs@google.com>
Reviewed-by: Jens Johansen <jensj@google.com>
2025-06-24 14:14:42 -07:00

160 lines
4.4 KiB
Dart

// 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' show Directory, File;
import 'package:test/test.dart';
import '../shared_test_options.dart';
import 'test_compiler.dart';
typedef AdditionalLibrary = ({String name, String source});
class ExpressionCompilerTestDriver {
final SetupCompilerOptions setup;
late Directory _testRootDirectory;
String source;
late Uri input;
late Uri output;
late Uri packages;
late int line;
final List<AdditionalLibrary> additionalLibraries;
ExpressionCompilerTestDriver(
this.setup,
this.source, {
this.additionalLibraries = const [],
}) {
line = _getEvaluationLine(source);
var systemTempDir = Directory.systemTemp;
_testRootDirectory = systemTempDir.createTempSync('expression_eval_test');
var testPackageDirectory = Directory.fromUri(
_testRootDirectory.uri.resolve('test_package/'),
);
output = testPackageDirectory.uri.resolve('test.js');
input = testPackageDirectory.uri.resolve('test.dart');
File.fromUri(input)
..createSync(recursive: true)
..writeAsStringSync(source);
packages = _testRootDirectory.uri.resolve('package_config.json');
var packageDescriptors = [_createPackageDescriptor('test_package')];
for (var library in additionalLibraries) {
var name = library.name;
var source = library.source;
var additionalPackageDirectory = Directory.fromUri(
_testRootDirectory.uri.resolve('$name/'),
);
var additionalFile = additionalPackageDirectory.uri.resolve('$name.dart');
File.fromUri(additionalFile)
..createSync(recursive: true)
..writeAsStringSync(source);
packageDescriptors.add(_createPackageDescriptor(name));
}
File.fromUri(packages)
..createSync()
..writeAsStringSync('''
{
"configVersion": 2,
"packages": [
${packageDescriptors.join(',\n ')}
]
}
''');
}
/// Creates a `packages_config.json` entry for a test package called [name].
static String _createPackageDescriptor(String name) =>
'''{
"name": "$name",
"rootUri": "./$name",
"packageUri": "./"
}''';
void delete() {
_testRootDirectory.deleteSync(recursive: true);
}
Future<TestExpressionCompiler> createCompiler() =>
TestExpressionCompiler.init(
setup,
input: input,
output: output,
packages: packages,
);
Future<TestCompilationResult> compile({
required TestExpressionCompiler compiler,
required Map<String, String> scope,
required String expression,
}) async {
return compiler.compileExpression(
libraryUri: input,
line: line,
column: 1,
scope: scope,
expression: expression,
);
}
void checkResult(
TestCompilationResult result, {
String? expectedError,
dynamic expectedResult,
}) {
var success = expectedError == null;
var message = success ? expectedResult! : expectedError;
expect(
result,
const TypeMatcher<TestCompilationResult>()
.having((r) => r.result!, 'result', _matches(message))
.having((r) => r.isSuccess, 'isSuccess', success),
);
}
Future<void> check({
TestExpressionCompiler? compiler,
required Map<String, String> scope,
required String expression,
String? expectedError,
dynamic expectedResult,
}) async {
compiler ??= await createCompiler();
var result = await compile(
compiler: compiler,
scope: scope,
expression: expression,
);
checkResult(
result,
expectedError: expectedError,
expectedResult: expectedResult,
);
}
Matcher _matches(dynamic matcher) {
if (matcher is Matcher) return matcher;
if (matcher is! String) throw StateError('Unsupported matcher $matcher');
var unIndented = RegExp.escape(matcher).replaceAll(RegExp('[ ]+'), '[ ]*');
return matches(RegExp(unIndented, multiLine: true));
}
static int _getEvaluationLine(String source) {
var placeholderRegExp = RegExp(r'// Breakpoint');
var lines = source.split('\n');
for (var line = 0; line < lines.length; line++) {
var content = lines[line];
if (placeholderRegExp.firstMatch(content) != null) {
return line + 1;
}
}
return -1;
}
}