blob: 193ef81e47d5baaa757b0e7fe288a01b99d4a5ba [file] [edit]
// Copyright (c) 2026, 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:cfg/ir/global_context.dart';
import 'package:cfg/ir/instructions.dart';
import 'package:cfg/ir/source_position.dart';
import 'package:kernel/ast.dart' as ast;
class ExceptionHandler._(
final int index,
final int outerIndex,
final bool isSynthetic,
final bool needsStackTrace,
final List<ast.DartType> guardTypes,
) {
late final int pcOffset;
bool get hasCatchAll => guardTypes.any(_isCatchAll);
static bool _isCatchAll(ast.DartType guardType) => switch (guardType) {
ast.DynamicType() => true,
ast.VoidType() => true,
ast.InterfaceType() =>
guardType.classNode == GlobalContext.instance.coreTypes.objectClass,
ast.FutureOrType() => _isCatchAll(guardType.typeArgument),
ast.ExtensionType() => _isCatchAll(guardType.extensionTypeErasure),
_ => false,
};
}
/// Metadata describing exception handlers.
class ExceptionHandlers {
/// Whether this code has a top-level async exception handler.
final bool hasAsyncHandler;
final Map<CatchBlock, ExceptionHandler> _handlers = {};
ExceptionHandlers({required this.hasAsyncHandler});
Iterable<ExceptionHandler> get handlers => _handlers.values;
ExceptionHandler getHandler(CatchBlock catchBlock) =>
_handlers[catchBlock] ??= _createHandler(catchBlock);
ExceptionHandler _createHandler(CatchBlock catchBlock) {
final outerHandler = catchBlock.exceptionHandler;
final outerIndex = outerHandler != null
? getHandler(outerHandler).index
: -1;
var needsStackTrace = false;
for (final instr in catchBlock) {
if (instr is! Parameter) break;
if (instr.variable.isStackTraceVariable) {
needsStackTrace = instr.hasUses;
break;
}
}
return ExceptionHandler._(
_handlers.length,
outerIndex,
catchBlock.isSynthetic,
needsStackTrace,
catchBlock.guardTypes,
);
}
@override
int get hashCode =>
_handlers.isEmpty ? hasAsyncHandler.hashCode : identityHashCode(this);
@override
bool operator ==(Object other) =>
identical(this, other) ||
(_handlers.isEmpty &&
other is ExceptionHandlers &&
other._handlers.isEmpty &&
this.hasAsyncHandler == other.hasAsyncHandler);
}
class CallSite(
final int pcOffset,
final int exceptionHandlerIndex,
final SourcePosition sourcePosition,
);
/// Metadata describing call sites (aka PC descriptors).
class PcDescriptors {
final List<CallSite> callSites = [];
void add(CallSite cs) {
assert(callSites.isEmpty || callSites.last.pcOffset < cs.pcOffset);
callSites.add(cs);
}
@override
int get hashCode => callSites.isEmpty ? 37 : identityHashCode(this);
@override
bool operator ==(Object other) =>
identical(this, other) ||
(callSites.isEmpty && other is PcDescriptors && other.callSites.isEmpty);
}
class ExceptionSite(final int pcOffset) {
// TODO: add moves
}
/// Metadata describing how to move values from an exception site to the exception handler.
class CatchEntryMoves {
final List<ExceptionSite> exceptionSites = [];
void add(ExceptionSite es) {
exceptionSites.add(es);
}
@override
int get hashCode => exceptionSites.isEmpty ? 31 : identityHashCode(this);
@override
bool operator ==(Object other) =>
identical(this, other) ||
(exceptionSites.isEmpty &&
other is CatchEntryMoves &&
other.exceptionSites.isEmpty);
}
class CodeSourcePosition(
final int pcOffset,
final SourcePosition sourcePosition,
);
/// Metadata describing source positions.
class CodeSourceMap {
final List<CodeSourcePosition> sourcePositions = [];
void add(CodeSourcePosition sp) {
if (sourcePositions.isNotEmpty) {
assert(sourcePositions.last.pcOffset < sp.pcOffset);
if (sourcePositions.last.sourcePosition == sp.sourcePosition) {
return;
}
}
sourcePositions.add(sp);
}
@override
int get hashCode => sourcePositions.isEmpty ? 23 : identityHashCode(this);
@override
bool operator ==(Object other) =>
identical(this, other) ||
(sourcePositions.isEmpty &&
other is CodeSourceMap &&
other.sourcePositions.isEmpty);
}