blob: dd96bffdec246c76e2d0b29a1a4ca5c6201909dc [file] [log] [blame]
// Copyright (c) 2012, 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;
import 'package:compiler/src/ssa/metrics.dart';
import '../common.dart';
import '../common/codegen.dart' show CodegenResult, CodegenRegistry;
import '../common/elements.dart' show CommonElements, JElementEnvironment;
import '../common/metrics.dart';
import '../common/tasks.dart' show CompilerTask, Measurer;
import '../elements/entities.dart';
import '../elements/types.dart';
import '../inferrer/types.dart';
import '../io/source_information.dart';
import '../js/js.dart' as js;
import '../js/rewrite_async.dart';
import '../js_backend/backend.dart' show FunctionCompiler;
import '../js_backend/codegen_inputs.dart' show CodegenInputs;
import '../js_backend/namer.dart' show ModularNamer;
import '../js_backend/namer.dart' show ModularNamerImpl;
import '../js_backend/type_reference.dart' show TypeReference;
import '../js_emitter/code_emitter_task.dart' show ModularEmitter;
import '../js_emitter/startup_emitter/emitter.dart' show ModularEmitterImpl;
import '../js_model/elements.dart';
import '../js_model/type_recipe.dart' show TypeExpressionRecipe;
import '../js_model/js_strategy.dart';
import '../js_model/js_world.dart' show JClosedWorld;
import '../options.dart';
import '../universe/call_structure.dart' show CallStructure;
import '../universe/use.dart' show StaticUse;
import 'codegen.dart';
import 'nodes.dart';
import 'optimize.dart';
class SsaFunctionCompiler implements FunctionCompiler {
final CompilerOptions _options;
final DiagnosticReporter _reporter;
final SsaMetrics _metrics;
final SsaCodeGeneratorTask generator;
final SsaBuilderTask _builder;
final SsaOptimizerTask optimizer;
final SourceInformationStrategy sourceInformationStrategy;
late final GlobalTypeInferenceResults _globalInferenceResults;
late final GlobalTypeInferenceResults _trivialInferenceResults;
late final CodegenInputs _codegen;
SsaFunctionCompiler(
this._options,
this._reporter,
this._metrics,
JsBackendStrategy backendStrategy,
Measurer measurer,
this.sourceInformationStrategy,
) : generator = SsaCodeGeneratorTask(
measurer,
_options,
sourceInformationStrategy,
),
_builder = SsaBuilderTask(
measurer,
backendStrategy,
sourceInformationStrategy,
_metrics,
),
optimizer = SsaOptimizerTask(measurer, _options);
@override
void initialize(
GlobalTypeInferenceResults globalInferenceResults,
CodegenInputs codegen,
) {
_globalInferenceResults = globalInferenceResults;
_trivialInferenceResults = TrivialGlobalTypeInferenceResults(
globalInferenceResults.closedWorld,
_globalInferenceResults.globalLocalsMap,
);
_codegen = codegen;
_builder.onCodegenStart(globalInferenceResults.closedWorld);
}
/// Generates JavaScript code for [member].
/// Using the ssa builder, optimizer and code generator.
@override
CodegenResult compile(MemberEntity member) {
// We don't have inference data for stubs. We could use some inferred types
// for the target function but stubs are so simple that this usually doesn't
// produce better code. Deserializing inference results is also expensive so
// we avoid it here.
final inferenceResults = member is JParameterStub
? _trivialInferenceResults
: _globalInferenceResults;
JClosedWorld closedWorld = _globalInferenceResults.closedWorld;
CodegenRegistry registry = CodegenRegistry(
closedWorld.elementEnvironment,
member,
);
ModularNamer namer = ModularNamerImpl(
registry,
closedWorld.commonElements,
_codegen.fixedNames,
);
ModularEmitter emitter = ModularEmitterImpl(namer, registry, _options);
if (member is ConstructorEntity &&
member.enclosingClass == closedWorld.commonElements.jsNullClass) {
// Work around a problem compiling JSNull's constructor.
return registry.close(null);
}
final graph = _builder.build(
member,
inferenceResults,
_codegen,
registry,
namer,
emitter,
);
if (graph == null) {
return registry.close(null);
}
optimizer.optimize(
member,
graph,
_codegen,
closedWorld,
inferenceResults,
registry,
_metrics,
);
js.Expression result = generator.generateCode(
member,
graph,
_codegen,
closedWorld,
registry,
namer,
emitter,
);
if (graph.needsAsyncRewrite) {
SourceInformationBuilder sourceInformationBuilder =
sourceInformationStrategy.createBuilderForContext(member);
result = _rewriteAsync(
_codegen,
closedWorld.commonElements,
closedWorld.elementEnvironment,
registry,
namer,
emitter,
member as FunctionEntity,
result,
graph.asyncElementType,
sourceInformationBuilder.buildAsyncBody(),
sourceInformationBuilder.buildAsyncExit(),
);
_codegen.tracer.traceJavaScriptText(
'JavaScript.rewrite',
result.debugPrint,
);
}
if (result.sourceInformation == null) {
result = result.withSourceInformation(
sourceInformationStrategy.buildSourceMappedMarker(),
);
}
return registry.close(result as js.Fun);
}
js.Expression _rewriteAsync(
CodegenInputs codegen,
CommonElements commonElements,
JElementEnvironment elementEnvironment,
CodegenRegistry registry,
ModularNamer namer,
ModularEmitter emitter,
FunctionEntity element,
js.Expression code,
DartType? asyncTypeParameter,
SourceInformation? bodySourceInformation,
SourceInformation? exitSourceInformation,
) {
if (element.asyncMarker == AsyncMarker.sync) return code;
late final AsyncRewriterBase rewriter;
js.Name name = namer.methodPropertyName(
element is JGeneratorBody ? element.function : element,
);
switch (element.asyncMarker) {
case AsyncMarker.async:
rewriter = _makeAsyncRewriter(
codegen,
commonElements,
elementEnvironment,
registry,
namer,
emitter,
element,
code,
asyncTypeParameter,
name,
);
break;
case AsyncMarker.syncStar:
rewriter = _makeSyncStarRewriter(
codegen,
commonElements,
elementEnvironment,
registry,
namer,
emitter,
element,
code,
asyncTypeParameter,
name,
);
break;
case AsyncMarker.asyncStar:
rewriter = _makeAsyncStarRewriter(
codegen,
commonElements,
elementEnvironment,
registry,
namer,
emitter,
element,
code,
asyncTypeParameter,
name,
);
break;
case AsyncMarker.sync:
throw StateError('Cannot rewrite sync method as async.');
}
return rewriter.rewrite(
code as js.Fun,
bodySourceInformation,
exitSourceInformation,
);
}
/// Returns an optional expression that evaluates [type]. Returns `null` if
/// the type expression is determined by the outside context and should be
/// added as a function parameter to the rewritten code.
// TODO(sra): We could also return an empty list if the generator takes no
// type (e.g. due to rtiNeed optimization).
List<js.Expression>? _fetchItemType(
CommonElements commonElements,
CodegenRegistry registry,
DartType? type,
) {
if (type == null) return null;
registry.registerStaticUse(
StaticUse.staticInvoke(commonElements.findType, CallStructure.oneArg),
);
return [TypeReference(TypeExpressionRecipe(type))];
}
AsyncRewriter _makeAsyncRewriter(
CodegenInputs codegen,
CommonElements commonElements,
JElementEnvironment elementEnvironment,
CodegenRegistry registry,
ModularNamer namer,
ModularEmitter emitter,
FunctionEntity element,
js.Expression code,
DartType? elementType,
js.Name name,
) {
FunctionEntity startFunction = commonElements.asyncHelperStartSync;
FunctionEntity completerFactory = commonElements.asyncAwaitCompleterFactory;
final itemTypeExpression = _fetchItemType(
commonElements,
registry,
elementType,
);
AsyncRewriter rewriter = AsyncRewriter(
_reporter,
element,
asyncStart: emitter.staticFunctionAccess(startFunction),
asyncAwait: emitter.staticFunctionAccess(commonElements.asyncHelperAwait),
asyncReturn: emitter.staticFunctionAccess(
commonElements.asyncHelperReturn,
),
asyncRethrow: emitter.staticFunctionAccess(
commonElements.asyncHelperRethrow,
),
wrapBody: emitter.staticFunctionAccess(commonElements.wrapBody),
completerFactory: emitter.staticFunctionAccess(completerFactory),
completerFactoryTypeArguments: itemTypeExpression,
safeVariableName: namer.safeVariablePrefixForAsyncRewrite,
bodyName: namer.deriveAsyncBodyName(name),
);
registry.registerStaticUse(
StaticUse.staticInvoke(completerFactory, CallStructure.unnamed(0, 1), [
elementEnvironment.getFunctionAsyncOrSyncStarElementType(element),
]),
);
return rewriter;
}
SyncStarRewriter _makeSyncStarRewriter(
CodegenInputs codegen,
CommonElements commonElements,
JElementEnvironment elementEnvironment,
CodegenRegistry registry,
ModularNamer namer,
ModularEmitter emitter,
FunctionEntity element,
js.Expression code,
DartType? asyncTypeParameter,
js.Name name,
) {
SyncStarRewriter rewriter = SyncStarRewriter(
_reporter,
element,
iteratorCurrentValueProperty: namer.instanceFieldPropertyName(
commonElements.syncStarIteratorCurrentField,
),
iteratorDatumProperty: namer.instanceFieldPropertyName(
commonElements.syncStarIteratorDatumField,
),
yieldStarSelector: namer.instanceMethodName(
commonElements.syncStarIteratorYieldStarMethod,
),
safeVariableName: namer.safeVariablePrefixForAsyncRewrite,
bodyName: namer.deriveAsyncBodyName(name),
);
return rewriter;
}
AsyncStarRewriter _makeAsyncStarRewriter(
CodegenInputs codegen,
CommonElements commonElements,
JElementEnvironment elementEnvironment,
CodegenRegistry registry,
ModularNamer namer,
ModularEmitter emitter,
FunctionEntity element,
js.Expression code,
DartType? asyncTypeParameter,
js.Name name,
) {
final itemTypeExpression = _fetchItemType(
commonElements,
registry,
asyncTypeParameter,
);
AsyncStarRewriter rewriter = AsyncStarRewriter(
_reporter,
element,
asyncStarHelper: emitter.staticFunctionAccess(
commonElements.asyncStarHelper,
),
streamOfController: emitter.staticFunctionAccess(
commonElements.streamOfController,
),
wrapBody: emitter.staticFunctionAccess(commonElements.wrapBody),
newController: emitter.staticFunctionAccess(
commonElements.asyncStarStreamControllerFactory,
),
newControllerTypeArguments: itemTypeExpression,
safeVariableName: namer.safeVariablePrefixForAsyncRewrite,
yieldExpression: emitter.staticFunctionAccess(commonElements.yieldSingle),
yieldStarExpression: emitter.staticFunctionAccess(
commonElements.yieldStar,
),
bodyName: namer.deriveAsyncBodyName(name),
);
registry.registerStaticUse(
StaticUse.staticInvoke(
commonElements.asyncStarStreamControllerFactory,
CallStructure.unnamed(1, 1),
[elementEnvironment.getFunctionAsyncOrSyncStarElementType(element)],
),
);
return rewriter;
}
@override
List<CompilerTask> get tasks {
return [_builder, optimizer, generator];
}
}
abstract class SsaBuilder {
/// Creates the [HGraph] for [member] or returns `null` if no code is needed
/// for [member].
HGraph? build(
MemberEntity member,
GlobalTypeInferenceResults globalInferenceResults,
CodegenInputs codegen,
CodegenRegistry registry,
ModularNamer namer,
ModularEmitter emitter,
);
}
class SsaBuilderTask extends CompilerTask {
final JsBackendStrategy _backendStrategy;
final SourceInformationStrategy _sourceInformationFactory;
late SsaBuilder _builder;
final SsaMetrics _ssaMetrics;
@override
Metrics metrics = Metrics.none();
SsaBuilderTask(
super.measurer,
this._backendStrategy,
this._sourceInformationFactory,
this._ssaMetrics,
);
@override
String get name => 'SSA builder';
void onCodegenStart(JClosedWorld closedWorld) {
_builder = _backendStrategy.createSsaBuilder(
this,
closedWorld,
_sourceInformationFactory,
);
metrics = _ssaMetrics;
}
/// Creates the [HGraph] for [member] or returns `null` if no code is needed
/// for [member].
HGraph? build(
MemberEntity member,
GlobalTypeInferenceResults globalInferenceResults,
CodegenInputs codegen,
CodegenRegistry registry,
ModularNamer namer,
ModularEmitter emitter,
) {
return _builder.build(
member,
globalInferenceResults,
codegen,
registry,
namer,
emitter,
);
}
}