60d418034a
* Reformat pkg/dev_compiler. * Re-flow some comments to keep them within 80 characters. Change-Id: I0849ce113ecf3dce4a2b4405c2b09a17dee4df76 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/433081 Reviewed-by: Mark Zhou <markzipan@google.com> Commit-Queue: Nicholas Shahan <nshahan@google.com>
209 lines
6.6 KiB
Dart
209 lines
6.6 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:async';
|
|
import 'dart:convert';
|
|
import 'dart:io' show File;
|
|
|
|
import 'package:dev_compiler/src/command/command.dart';
|
|
import 'package:dev_compiler/src/command/options.dart' show Options;
|
|
import 'package:dev_compiler/src/compiler/module_builder.dart';
|
|
import 'package:dev_compiler/src/kernel/compiler.dart' show ProgramCompiler;
|
|
import 'package:dev_compiler/src/kernel/compiler_new.dart'
|
|
show LibraryBundleCompiler;
|
|
import 'package:dev_compiler/src/kernel/expression_compiler.dart'
|
|
show ExpressionCompiler;
|
|
import 'package:dev_compiler/src/kernel/module_metadata.dart';
|
|
import 'package:front_end/src/api_unstable/ddc.dart' as fe;
|
|
import 'package:kernel/ast.dart' show Component, Library;
|
|
import 'package:path/path.dart' as p;
|
|
import 'package:source_maps/source_maps.dart' as source_maps;
|
|
|
|
import '../shared_test_options.dart';
|
|
|
|
class TestCompilationResult {
|
|
final String? result;
|
|
final bool isSuccess;
|
|
|
|
TestCompilationResult(this.result, this.isSuccess);
|
|
}
|
|
|
|
class TestExpressionCompiler {
|
|
final SetupCompilerOptions setup;
|
|
final Component component;
|
|
final ExpressionCompiler compiler;
|
|
final ModuleMetadata? metadata;
|
|
final source_maps.SingleMapping sourceMap;
|
|
|
|
TestExpressionCompiler._(
|
|
this.setup,
|
|
this.component,
|
|
this.compiler,
|
|
this.metadata,
|
|
this.sourceMap,
|
|
);
|
|
|
|
static Future<TestExpressionCompiler> init(
|
|
SetupCompilerOptions setup, {
|
|
required Uri input,
|
|
required Uri output,
|
|
Uri? packages,
|
|
Map<String, bool> experiments = const {},
|
|
}) async {
|
|
setup.diagnosticMessages.clear();
|
|
setup.errors.clear();
|
|
// Initialize the incremental compiler and module component.
|
|
// TODO: extend this for multi-module compilations by storing separate
|
|
// compilers/components/names per module.
|
|
setup.options.packagesFileUri = packages;
|
|
setup.options.explicitExperimentalFlags.addAll(
|
|
fe.parseExperimentalFlags(
|
|
experiments,
|
|
onError: (message) => throw Exception(message),
|
|
),
|
|
);
|
|
var frontend = DevelopmentIncrementalCompiler(setup.options, input);
|
|
var compilerResult = await frontend.computeDelta();
|
|
var component = compilerResult.component;
|
|
component.computeCanonicalNames();
|
|
// Initialize DDC.
|
|
var moduleName = p.basenameWithoutExtension(output.toFilePath());
|
|
|
|
var classHierarchy = compilerResult.classHierarchy;
|
|
var compilerOptions = Options(
|
|
replCompile: true,
|
|
moduleName: moduleName,
|
|
experiments: experiments,
|
|
emitDebugMetadata: true,
|
|
canaryFeatures: setup.canaryFeatures,
|
|
enableAsserts: setup.enableAsserts,
|
|
moduleFormats: [setup.moduleFormat],
|
|
);
|
|
var coreTypes = compilerResult.coreTypes;
|
|
|
|
final importToSummary = Map<Library, Component>.identity();
|
|
final summaryToModule = Map<Component, String>.identity();
|
|
for (var lib in component.libraries) {
|
|
importToSummary[lib] = component;
|
|
}
|
|
summaryToModule[component] = moduleName;
|
|
|
|
var kernel2jsCompiler = compilerOptions.emitLibraryBundle
|
|
? LibraryBundleCompiler(
|
|
component,
|
|
classHierarchy,
|
|
compilerOptions,
|
|
importToSummary,
|
|
summaryToModule,
|
|
coreTypes: coreTypes,
|
|
)
|
|
: ProgramCompiler(
|
|
component,
|
|
classHierarchy,
|
|
compilerOptions,
|
|
importToSummary,
|
|
summaryToModule,
|
|
coreTypes: coreTypes,
|
|
);
|
|
var module = kernel2jsCompiler.emitModule(component);
|
|
|
|
var moduleFormat = compilerOptions.emitLibraryBundle
|
|
? ModuleFormat.ddcLibraryBundle
|
|
: setup.moduleFormat;
|
|
|
|
// Perform a full compile, writing the compiled JS + sourcemap.
|
|
var code = jsProgramToCode(
|
|
module,
|
|
moduleFormat,
|
|
inlineSourceMap: compilerOptions.inlineSourceMap,
|
|
buildSourceMap: compilerOptions.sourceMap,
|
|
emitDebugMetadata: compilerOptions.emitDebugMetadata,
|
|
emitDebugSymbols: compilerOptions.emitDebugSymbols,
|
|
jsUrl: '$output',
|
|
mapUrl: '$output.map',
|
|
compiler: kernel2jsCompiler,
|
|
component: component,
|
|
);
|
|
var codeBytes = utf8.encode(code.code);
|
|
var sourceMapBytes = utf8.encode(json.encode(code.sourceMap));
|
|
|
|
File(output.toFilePath()).writeAsBytesSync(codeBytes);
|
|
File('${output.toFilePath()}.map').writeAsBytesSync(sourceMapBytes);
|
|
|
|
// Save the expression compiler for future compilation.
|
|
var compiler = ExpressionCompiler(
|
|
setup.options,
|
|
moduleFormat,
|
|
setup.errors,
|
|
frontend,
|
|
kernel2jsCompiler,
|
|
component,
|
|
);
|
|
|
|
if (setup.errors.isNotEmpty) {
|
|
throw Exception('Compilation failed with: ${setup.errors}');
|
|
}
|
|
setup.diagnosticMessages.clear();
|
|
setup.errors.clear();
|
|
|
|
var sourceMap = source_maps.SingleMapping.fromJson(code.sourceMap!);
|
|
return TestExpressionCompiler._(
|
|
setup,
|
|
component,
|
|
compiler,
|
|
code.metadata,
|
|
sourceMap,
|
|
);
|
|
}
|
|
|
|
// Line and column are 1-based.
|
|
Future<TestCompilationResult> compileExpression({
|
|
required Uri libraryUri,
|
|
Uri? scriptUri,
|
|
required int line,
|
|
required int column,
|
|
required Map<String, String> scope,
|
|
required String expression,
|
|
}) async {
|
|
// clear previous errors
|
|
setup.errors.clear();
|
|
|
|
// At this time, no metadata is stored for the Dart SDK. This is one of the
|
|
// reasons debugging and expression evaluation support is so limited within
|
|
// the SDK. When requesting evaluation of library level expressions, we can
|
|
// ignore the lack of metadata and simply use the URI directly.
|
|
String importUri;
|
|
if (libraryUri.isScheme('dart')) {
|
|
importUri = libraryUri.toString();
|
|
} else {
|
|
var libraryMetadata = metadataForLibraryUri(libraryUri);
|
|
importUri = libraryMetadata.importUri;
|
|
}
|
|
var jsExpression = await compiler.compileExpressionToJs(
|
|
importUri,
|
|
scriptUri?.toString(),
|
|
line,
|
|
column,
|
|
scope,
|
|
expression,
|
|
);
|
|
if (setup.errors.isNotEmpty) {
|
|
jsExpression = setup.errors.toString().replaceAll(
|
|
RegExp(r'org-dartlang-debug:synthetic_debug_expression:[0-9]*:[0-9]*:'),
|
|
'',
|
|
);
|
|
|
|
return TestCompilationResult(jsExpression, false);
|
|
}
|
|
|
|
return TestCompilationResult(jsExpression, true);
|
|
}
|
|
|
|
LibraryMetadata metadataForLibraryUri(Uri libraryUri) => metadata!
|
|
.libraries
|
|
.entries
|
|
.firstWhere((entry) => entry.value.fileUri == '$libraryUri')
|
|
.value;
|
|
}
|