Skip to content

Commit a9fc9ff

Browse files
nshahanCommit Queue
authored and
Commit Queue
committed
[ddc] Add module local caches for new types
- Provides fast access for types that are used multiple times in the same module. - Enable the existing type table cache when running with new types. - Add a similar cache for instantiated generic classes. This cache is used in the current type system as well to help keep the difference between types and classes more clear. Issue: #48585 Change-Id: I32103cf0c0bcf9b9771e789c0d04e63a4365a066 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/321320 Commit-Queue: Nicholas Shahan <[email protected]> Reviewed-by: Mark Zhou <[email protected]>
1 parent a10ee7a commit a9fc9ff

File tree

4 files changed

+77
-35
lines changed

4 files changed

+77
-35
lines changed

pkg/dev_compiler/lib/src/kernel/compiler.dart

Lines changed: 55 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,11 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
172172
/// Table of named and possibly hoisted types.
173173
late TypeTable _typeTable;
174174

175+
/// Table of instantiated generic class references.
176+
///
177+
/// Provides a cache for the instantiated generic types local to a module.
178+
late TypeTable _genericClassTable;
179+
175180
/// The global extension type table.
176181
// TODO(jmesserly): rename to `_nativeTypes`
177182
final NativeTypeSet _extensionTypes;
@@ -367,6 +372,7 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
367372
_assertInteropMethod = sdk.getTopLevelMember(
368373
'dart:_runtime', 'assertInterop') as Procedure,
369374
_futureOrNormalizer = FutureOrNormalizer(_coreTypes),
375+
_extensionTypeEraser = ExtensionTypeEraser(),
370376
_typeRecipeGenerator = TypeRecipeGenerator(_coreTypes, _hierarchy),
371377
_extensionIndex =
372378
ExtensionIndex(_coreTypes, _staticTypeContext.typeEnvironment);
@@ -390,6 +396,8 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
390396

391397
final FutureOrNormalizer _futureOrNormalizer;
392398

399+
final ExtensionTypeEraser _extensionTypeEraser;
400+
393401
/// Module can be emitted only once, and the compiler can be reused after
394402
/// only in incremental mode, for expression compilation only.
395403
js_ast.Program emitModule(Component component) {
@@ -451,7 +459,8 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
451459
}
452460

453461
_nullableInference.allowNotNullDeclarations = isBuildingSdk;
454-
_typeTable = TypeTable(runtimeCall);
462+
_typeTable = TypeTable('T', runtimeCall);
463+
_genericClassTable = TypeTable('G', runtimeCall);
455464

456465
// Collect all class/type Element -> Node mappings
457466
// in case we need to forward declare any classes.
@@ -616,6 +625,10 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
616625
items.addAll(_typeTable.dischargeBoundTypes());
617626
_ticker?.logMs('Emitted type table');
618627

628+
// Emit the hoisted instantiated generic class table cache variables
629+
items.addAll(_genericClassTable.dischargeBoundTypes());
630+
_ticker?.logMs('Emitted instantiated generic class table');
631+
619632
var module = finishModule(items, _options.moduleName,
620633
header: generateCompilationHeader());
621634
_ticker?.logMs('Finished emitting module');
@@ -1057,8 +1070,13 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
10571070
body = js_ast.Statement.from([body, varianceStatement]);
10581071
}
10591072

1060-
var typeConstructor = js.call('(#) => { #; #; return #; }',
1061-
[jsFormals, _typeTable.dischargeFreeTypes(formals), body, className]);
1073+
var typeConstructor = js.call('(#) => { #; #; #; return #; }', [
1074+
jsFormals,
1075+
_typeTable.dischargeFreeTypes(formals),
1076+
_genericClassTable.dischargeFreeTypes(formals),
1077+
body,
1078+
className
1079+
]);
10621080

10631081
var genericArgs = [
10641082
typeConstructor,
@@ -1268,6 +1286,7 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
12681286
if (t is RecordType) {
12691287
return t.positional.any(defer) || t.named.any((n) => defer(n.type));
12701288
}
1289+
if (t is ExtensionType) return defer(t.typeErasure);
12711290
return false;
12721291
}
12731292

@@ -1631,7 +1650,10 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
16311650
_classEmittingSignatures = c;
16321651

16331652
var interfaces = c.implementedTypes.toList()..addAll(c.onClause);
1634-
if (interfaces.isNotEmpty) {
1653+
if (interfaces.isNotEmpty &&
1654+
// New runtime types don't use this data structure to lookup interfaces
1655+
// a class implements.
1656+
!_options.newRuntimeTypes) {
16351657
body.add(js.statement('#[#] = () => [#];', [
16361658
className,
16371659
runtimeCall('implements'),
@@ -3260,6 +3282,7 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
32603282
if (type is TypedefType) {
32613283
return type.typeArguments.every(_canEmitTypeAtTopLevel);
32623284
}
3285+
if (type is ExtensionType) return _canEmitTypeAtTopLevel(type.typeErasure);
32633286
return true;
32643287
}
32653288

@@ -3304,6 +3327,8 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
33043327
// An environment with a single type parameter can be simplified to
33053328
// just that parameter.
33063329
env = _emitTypeParameter(environment.parameters.single);
3330+
// Skip a no-op evaluation and just return the parameter.
3331+
if (recipe == '0') return env;
33073332
} else {
33083333
var environmentTypes = environment.parameters;
33093334
// Create a dummy interface type to "hold" type arguments.
@@ -3334,16 +3359,15 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
33343359
}
33353360
}
33363361

3337-
// TODO(nshahan) Avoid calling _emitType when we actually want a
3338-
// reference to an rti that already exists in scope.
3339-
if (type is TypeParameterType && type.isPotentiallyNonNullable) {
3340-
return _emitTypeParameterType(type, emitNullability: false);
3341-
}
3342-
var normalizedType = _futureOrNormalizer.normalize(type);
3362+
var normalizedType =
3363+
_futureOrNormalizer.normalize(_extensionTypeEraser.erase(type));
33433364
try {
33443365
var result = _typeRecipeGenerator.recipeInEnvironment(
33453366
normalizedType, _currentTypeEnvironment);
3346-
return evalInEnvironment(result.requiredEnvironment, result.recipe);
3367+
var typeRep =
3368+
evalInEnvironment(result.requiredEnvironment, result.recipe);
3369+
if (_cacheTypes) typeRep = _typeTable.nameType(normalizedType, typeRep);
3370+
return typeRep;
33473371
} on UnsupportedError catch (e) {
33483372
_typeCompilationError(normalizedType, e.message ?? 'Unknown Error');
33493373
}
@@ -3496,7 +3520,10 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
34963520
if (args.any((a) => a != const DynamicType())) {
34973521
jsArgs = args.map(_emitType);
34983522
}
3499-
if (jsArgs != null) return _emitGenericClassType(type, jsArgs);
3523+
if (jsArgs != null) {
3524+
return _genericClassTable.nameType(
3525+
type, _emitGenericClassType(type, jsArgs));
3526+
}
35003527
return _emitTopLevelNameNoExternalInterop(type.classNode);
35013528
}
35023529

@@ -3567,11 +3594,12 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
35673594
bool get _emittingClassExtends =>
35683595
_currentClass != null && identical(_currentClass, _classEmittingExtends);
35693596

3570-
bool get _cacheTypes =>
3571-
!_emittingDeferredType &&
3572-
!_emittingClassExtends &&
3573-
!_emittingClassSignatures ||
3574-
_currentFunction != null;
3597+
bool get _cacheTypes => _options.newRuntimeTypes
3598+
? !_emittingDeferredType && !_emittingClassExtends
3599+
: !_emittingDeferredType &&
3600+
!_emittingClassExtends &&
3601+
!_emittingClassSignatures ||
3602+
_currentFunction != null;
35753603

35763604
js_ast.Expression _emitGenericClassType(
35773605
InterfaceType t, Iterable<js_ast.Expression> typeArgs) {
@@ -3853,6 +3881,7 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
38533881
var body = js_ast.Block([
38543882
...extensionSymbols,
38553883
..._typeTable.dischargeBoundTypes(),
3884+
..._genericClassTable.dischargeBoundTypes(),
38563885
...symbolContainer.emit(),
38573886
..._emitConstTable(),
38583887
..._uriContainer.emit(),
@@ -6851,23 +6880,21 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
68516880
js_ast.Expression _emitMapImplType(InterfaceType type, {bool? identity}) {
68526881
var typeArgs = type.typeArguments;
68536882
if (typeArgs.isEmpty) {
6854-
return _emitInterfaceType(type, emitNullability: false);
6883+
return _emitClassRef(type);
68556884
}
68566885
identity ??= _typeRep.isPrimitive(typeArgs[0]);
68576886
var c = identity ? _identityHashMapImplClass : _linkedHashMapImplClass;
6858-
return _emitInterfaceType(InterfaceType(c, Nullability.legacy, typeArgs),
6859-
emitNullability: false);
6887+
return _emitClassRef(InterfaceType(c, Nullability.legacy, typeArgs));
68606888
}
68616889

68626890
js_ast.Expression _emitSetImplType(InterfaceType type, {bool? identity}) {
68636891
var typeArgs = type.typeArguments;
68646892
if (typeArgs.isEmpty) {
6865-
return _emitInterfaceType(type, emitNullability: false);
6893+
return _emitClassRef(type);
68666894
}
68676895
identity ??= _typeRep.isPrimitive(typeArgs[0]);
68686896
var c = identity ? _identityHashSetImplClass : _linkedHashSetImplClass;
6869-
return _emitInterfaceType(InterfaceType(c, Nullability.legacy, typeArgs),
6870-
emitNullability: false);
6897+
return _emitClassRef(InterfaceType(c, Nullability.legacy, typeArgs));
68716898
}
68726899

68736900
js_ast.Expression _emitObjectLiteral(Arguments node, Member ctor) {
@@ -7199,9 +7226,8 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
71997226
if (itemType == const DynamicType()) return list;
72007227

72017228
// Call `new JSArray<E>.of(list)`
7202-
var arrayType = _emitInterfaceType(
7203-
InterfaceType(_jsArrayClass, Nullability.legacy, [itemType]),
7204-
emitNullability: false);
7229+
var arrayType = _emitClassRef(
7230+
InterfaceType(_jsArrayClass, Nullability.nonNullable, [itemType]));
72057231
return js.call('#.of(#)', [arrayType, list]);
72067232
}
72077233

@@ -7217,10 +7243,8 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
72177243
js_ast.Expression visitSetLiteral(SetLiteral node) {
72187244
// TODO(markzipan): remove const check when we use front-end const eval
72197245
if (!node.isConst) {
7220-
var setType = _emitInterfaceType(
7221-
InterfaceType(
7222-
_linkedHashSetClass, Nullability.legacy, [node.typeArgument]),
7223-
emitNullability: false);
7246+
var setType = _emitClassRef(InterfaceType(
7247+
_linkedHashSetClass, Nullability.legacy, [node.typeArgument]));
72247248
if (node.expressions.isEmpty) {
72257249
return js.call('#.new()', [setType]);
72267250
}
@@ -7674,8 +7698,7 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
76747698
var type = node
76757699
.getType(_staticTypeContext)
76767700
.withDeclaredNullability(Nullability.nonNullable);
7677-
var classRef =
7678-
_emitInterfaceType(type as InterfaceType, emitNullability: false);
7701+
var classRef = _emitClassRef(type as InterfaceType);
76797702
var prototype = js.call('#.prototype', [classRef]);
76807703
var properties = [
76817704
if (_options.newRuntimeTypes && type.typeArguments.isNotEmpty)

pkg/dev_compiler/lib/src/kernel/js_typerep.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ class JSTypeRep extends SharedJSTypeRep<DartType> {
6060
if (type == const DynamicType() || type == const VoidType()) {
6161
return JSType.jsUnknown;
6262
}
63+
if (type is ExtensionType) typeFor(type.typeErasure);
6364
return JSType.jsObject;
6465
}
6566

pkg/dev_compiler/lib/src/kernel/kernel_helpers.dart

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import 'dart:collection';
66
import 'package:collection/collection.dart';
77
import 'package:kernel/core_types.dart';
88
import 'package:kernel/kernel.dart' hide Pattern;
9+
import 'package:kernel/src/replacement_visitor.dart';
910

1011
Constructor? unnamedConstructor(Class c) =>
1112
c.constructors.firstWhereOrNull((c) => c.name.text == '');
@@ -290,6 +291,7 @@ class LabelContinueFinder extends RecursiveVisitor<void> {
290291
/// code if used in an assert.
291292
bool isKnownDartTypeImplementor(DartType t) {
292293
return t is DynamicType ||
294+
t is ExtensionType ||
293295
t is FunctionType ||
294296
t is FutureOrType ||
295297
t is InterfaceType ||
@@ -366,3 +368,15 @@ class InterfaceTypeExtractor extends RecursiveVisitor<DartType> {
366368
return _found;
367369
}
368370
}
371+
372+
class ExtensionTypeEraser extends ReplacementVisitor {
373+
const ExtensionTypeEraser();
374+
375+
/// Erases all `ExtensionType` nodes found in [type].
376+
DartType erase(DartType type) =>
377+
type.accept1(this, Variance.unrelated) ?? type;
378+
379+
@override
380+
DartType? visitExtensionType(ExtensionType node, int variance) =>
381+
node.typeErasure.accept1(this, Variance.unrelated) ?? node.typeErasure;
382+
}

pkg/dev_compiler/lib/src/kernel/type_table.dart

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ Set<TypeParameter> freeTypeParameters(DartType t) {
3535
} else if (t is RecordType) {
3636
t.positional.forEach((p) => find(p));
3737
t.named.forEach((n) => find(n.type));
38+
} else if (t is ExtensionType) {
39+
find(t.typeErasure);
3840
}
3941
}
4042

@@ -102,6 +104,7 @@ String _typeString(DartType type, {bool flat = false}) {
102104
}
103105
return 'Rec${nullability}Of$elements';
104106
}
107+
if (type is ExtensionType) return _typeString(type.typeErasure);
105108
return 'invalid';
106109
}
107110

@@ -120,12 +123,13 @@ class TypeTable {
120123
final _unboundTypeIds = HashMap<DartType, js_ast.Identifier>();
121124

122125
/// Holds JS type generators keyed by their underlying DartType.
123-
final typeContainer = ModuleItemContainer<DartType>.asObject('T',
124-
keyToString: (DartType t) => escapeIdentifier(_typeString(t))!);
126+
final ModuleItemContainer<DartType> typeContainer;
125127

126128
final js_ast.Expression Function(String, [List<Object>]) _runtimeCall;
127129

128-
TypeTable(this._runtimeCall);
130+
TypeTable(String name, this._runtimeCall)
131+
: typeContainer = ModuleItemContainer<DartType>.asObject(name,
132+
keyToString: (DartType t) => escapeIdentifier(_typeString(t))!);
129133

130134
/// Returns true if [type] is already recorded in the table.
131135
bool _isNamed(DartType type) =>

0 commit comments

Comments
 (0)