11import 'package:analyzer/dart/ast/ast.dart' ;
2+ import 'package:analyzer/dart/ast/visitor.dart' ;
23import 'package:collection/collection.dart' ;
34import 'package:dart_eval/dart_eval_bridge.dart' ;
45import 'package:dart_eval/src/eval/compiler/builtins.dart' ;
@@ -446,45 +447,135 @@ class Compiler implements BridgeDeclarationRegistry, EvalPluginRegistry {
446447 _ctx.topLevelGlobalIndices = _topLevelGlobalIndices;
447448
448449 try {
449- /// Compile statics first so we can infer their type
450- _topLevelDeclarationsMap.forEach ((key, value) {
451- final visibleInLibrary = visibleDeclarationsByIndex[key];
452- if (visibleInLibrary == null ) {
453- return ;
454- }
455- value.forEach ((name, tlDeclaration) {
456- if (tlDeclaration.isBridge || ! visibleInLibrary.containsKey (name)) {
457- return ;
458- }
459- final declaration = tlDeclaration.declaration! ;
460- _ctx.library = key;
450+ // Keep track of declarations we've already compiled to avoid processing them in the final pass.
451+ final alreadyCompiled = < AstNode > {};
452+
453+ // We iterate once to gather consts for sorting and compile non-const statics immediately.
454+ final constDeclarationsByContainer =
455+ < AstNode , List <_ConstDeclarationInfo >> {};
456+
457+ _topLevelDeclarationsMap.forEach ((libraryIndex, declarations) {
458+ declarations.forEach ((name, declOrBridge) {
459+ if (declOrBridge.isBridge) return ;
460+ final declaration = declOrBridge.declaration! ;
461+
462+ // Handle top-level variables
461463 if (declaration is VariableDeclaration &&
462- declaration.parent! .parent is TopLevelVariableDeclaration ) {
463- compileDeclaration (declaration, _ctx);
464- _ctx.resetStack ();
465- } else if (declaration is ClassDeclaration ) {
466- _ctx.currentClass = declaration;
467- for (final d in declaration.members
468- .whereType <FieldDeclaration >()
469- .where ((e) => e.isStatic)) {
470- compileFieldDeclaration (- 1 , d, _ctx, declaration);
464+ declaration.parent? .parent is TopLevelVariableDeclaration ) {
465+ final topLevelVarDecl =
466+ declaration.parent! .parent as TopLevelVariableDeclaration ;
467+ if (topLevelVarDecl.variables.isConst) {
468+ (constDeclarationsByContainer[topLevelVarDecl] ?? = []).add (
469+ _ConstDeclarationInfo (
470+ declaration, topLevelVarDecl, declOrBridge.sourceLib));
471+ } else {
472+ _ctx.library = declOrBridge.sourceLib;
473+ compileDeclaration (declaration, _ctx);
474+ alreadyCompiled.add (declaration);
471475 _ctx.resetStack ();
472476 }
473- _ctx.currentClass = null ;
474- } else if (declaration is EnumDeclaration ) {
477+ } else if (declaration is ClassDeclaration ) {
475478 _ctx.currentClass = declaration;
476- for (final d in declaration.members
477- .whereType <FieldDeclaration >()
478- .where ((e) => e.isStatic)) {
479- compileFieldDeclaration (- 1 , d, _ctx, declaration);
480- _ctx.resetStack ();
479+ for (final member
480+ in declaration.members.whereType <FieldDeclaration >()) {
481+ if (member.isStatic) {
482+ if (member.fields.isConst) {
483+ for (final fieldVar in member.fields.variables) {
484+ final fqName =
485+ '${declaration .name .lexeme }.${fieldVar .name .lexeme }' ;
486+ final fieldDeclOrBridge =
487+ _topLevelDeclarationsMap[libraryIndex]! [fqName];
488+ if (fieldDeclOrBridge != null ) {
489+ (constDeclarationsByContainer[declaration] ?? = []).add (
490+ _ConstDeclarationInfo (fieldVar, declaration,
491+ fieldDeclOrBridge.sourceLib));
492+ }
493+ }
494+ } else {
495+ _ctx.library = libraryIndex;
496+ compileFieldDeclaration (- 1 , member, _ctx, declaration);
497+ alreadyCompiled.add (member);
498+ _ctx.resetStack ();
499+ }
500+ }
481501 }
482502 _ctx.currentClass = null ;
483503 }
484504 });
485505 });
486506
487- /// Compile the rest of the declarations
507+ constDeclarationsByContainer.forEach ((container, constDecls) {
508+ if (constDecls.isEmpty) return ;
509+
510+ _ctx.library = constDecls.first.libraryIndex;
511+
512+ final fieldMap = {for (var d in constDecls) d.name: d};
513+ final fieldNames = fieldMap.keys.toSet ();
514+ final Map <String , Set <String >> adjList = {
515+ for (var name in fieldNames) name: {}
516+ };
517+ final Map <String , int > inDegrees = {
518+ for (var name in fieldNames) name: 0
519+ };
520+
521+ // Build dependency graph
522+ for (final constDeclInfo in constDecls) {
523+ final dependerName = constDeclInfo.name;
524+ final initializer = constDeclInfo.variable.initializer;
525+ if (initializer != null ) {
526+ final dependencyVisitor = _DependencyVisitor (fieldNames);
527+ initializer.visitChildren (dependencyVisitor);
528+ for (final dependencyName in dependencyVisitor.dependencies) {
529+ if (adjList[dependencyName]! .add (dependerName)) {
530+ inDegrees[dependerName] = (inDegrees[dependerName] ?? 0 ) + 1 ;
531+ }
532+ }
533+ }
534+ }
535+
536+ // Perform topological sort
537+ final sortedOrder = < _ConstDeclarationInfo > [];
538+ final queue = < String > [];
539+ fieldNames.where ((name) => inDegrees[name] == 0 ).forEach (queue.add);
540+
541+ while (queue.isNotEmpty) {
542+ final currentName = queue.removeAt (0 );
543+ sortedOrder.add (fieldMap[currentName]! );
544+ for (final neighborName in adjList[currentName]! ) {
545+ inDegrees[neighborName] = inDegrees[neighborName]! - 1 ;
546+ if (inDegrees[neighborName] == 0 ) {
547+ queue.add (neighborName);
548+ }
549+ }
550+ }
551+
552+ if (sortedOrder.length < constDecls.length) {
553+ final containerName = container is NamedCompilationUnitMember
554+ ? '"${container .name .lexeme }"'
555+ : 'the library' ;
556+ throw CompileError (
557+ 'Circular dependency detected in const declarations in $containerName .' );
558+ }
559+
560+ // Compile sorted consts
561+ for (final declInfo in sortedOrder) {
562+ final parentNode = declInfo.variable.parent! .parent! ;
563+ if (parentNode is TopLevelVariableDeclaration ) {
564+ compileDeclaration (declInfo.variable, _ctx);
565+ alreadyCompiled.add (declInfo.variable);
566+ } else if (parentNode is FieldDeclaration &&
567+ declInfo.parent is ClassDeclaration ) {
568+ _ctx.currentClass = declInfo.parent as ClassDeclaration ;
569+ compileFieldDeclaration (
570+ - 1 , parentNode, _ctx, declInfo.parent as ClassDeclaration );
571+ _ctx.currentClass = null ;
572+ alreadyCompiled.add (parentNode);
573+ }
574+ _ctx.resetStack ();
575+ }
576+ });
577+
578+ // Compile non-statics
488579 _topLevelDeclarationsMap.forEach ((key, value) {
489580 _ctx.topLevelDeclarationPositions[key] = {};
490581 _ctx.instanceDeclarationPositions[key] = {};
@@ -498,11 +589,18 @@ class Compiler implements BridgeDeclarationRegistry, EvalPluginRegistry {
498589 return ;
499590 }
500591 final declaration = tlDeclaration.declaration! ;
592+
593+ // We've already compiled all variable declarations that are top-level or static fields.
594+ // This pass is for compiling the bodies of classes and functions.
501595 if (declaration is ConstructorDeclaration ||
502596 declaration is MethodDeclaration ||
503597 declaration is VariableDeclaration ) {
598+ // This skips top-level vars that were already compiled.
504599 return ;
505600 }
601+
602+ if (alreadyCompiled.contains (declaration)) return ;
603+
506604 _ctx.library = key;
507605 compileDeclaration (declaration, _ctx);
508606 _ctx.resetStack ();
@@ -1216,3 +1314,31 @@ class _Import {
12161314 base .resolveUri (uri), import.prefix? .name, import.combinators);
12171315 }
12181316}
1317+
1318+ class _ConstDeclarationInfo {
1319+ _ConstDeclarationInfo (this .variable, this .parent, this .libraryIndex);
1320+
1321+ final VariableDeclaration variable;
1322+
1323+ final AstNode parent;
1324+
1325+ final int libraryIndex;
1326+
1327+ String get name => variable.name.lexeme;
1328+ }
1329+
1330+ class _DependencyVisitor extends RecursiveAstVisitor <void > {
1331+ _DependencyVisitor (this .availableNames);
1332+
1333+ final Set <String > availableNames;
1334+
1335+ final Set <String > dependencies = {};
1336+
1337+ @override
1338+ void visitSimpleIdentifier (SimpleIdentifier node) {
1339+ if (availableNames.contains (node.name)) {
1340+ dependencies.add (node.name);
1341+ }
1342+ super .visitSimpleIdentifier (node);
1343+ }
1344+ }
0 commit comments