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,136 @@ 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+
455+ final constDeclarationsByContainer =
456+ < AstNode , List <_ConstDeclarationInfo >> {};
457+
458+ _topLevelDeclarationsMap.forEach ((libraryIndex, declarations) {
459+ declarations.forEach ((name, declOrBridge) {
460+ if (declOrBridge.isBridge) return ;
461+ final declaration = declOrBridge.declaration! ;
462+
463+ // Handle top-level variables
461464 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);
465+ declaration.parent? .parent is TopLevelVariableDeclaration ) {
466+ final topLevelVarDecl =
467+ declaration.parent! .parent as TopLevelVariableDeclaration ;
468+ if (topLevelVarDecl.variables.isConst) {
469+ (constDeclarationsByContainer[topLevelVarDecl] ?? = []).add (
470+ _ConstDeclarationInfo (
471+ declaration, topLevelVarDecl, declOrBridge.sourceLib));
472+ } else {
473+ _ctx.library = declOrBridge.sourceLib;
474+ compileDeclaration (declaration, _ctx);
475+ alreadyCompiled.add (declaration);
471476 _ctx.resetStack ();
472477 }
473- _ctx.currentClass = null ;
474- } else if (declaration is EnumDeclaration ) {
478+ } else if (declaration is ClassDeclaration ) {
475479 _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 ();
480+ for (final member
481+ in declaration.members.whereType <FieldDeclaration >()) {
482+ if (member.isStatic) {
483+ if (member.fields.isConst) {
484+ for (final fieldVar in member.fields.variables) {
485+ final fqName =
486+ '${declaration .name .lexeme }.${fieldVar .name .lexeme }' ;
487+ final fieldDeclOrBridge =
488+ _topLevelDeclarationsMap[libraryIndex]! [fqName];
489+ if (fieldDeclOrBridge != null ) {
490+ (constDeclarationsByContainer[declaration] ?? = []).add (
491+ _ConstDeclarationInfo (fieldVar, declaration,
492+ fieldDeclOrBridge.sourceLib));
493+ }
494+ }
495+ } else {
496+ _ctx.library = libraryIndex;
497+ compileFieldDeclaration (- 1 , member, _ctx, declaration);
498+ alreadyCompiled.add (member);
499+ _ctx.resetStack ();
500+ }
501+ }
481502 }
482503 _ctx.currentClass = null ;
483504 }
484505 });
485506 });
486507
487- /// Compile the rest of the declarations
508+ constDeclarationsByContainer.forEach ((container, constDecls) {
509+ if (constDecls.isEmpty) return ;
510+
511+ _ctx.library = constDecls.first.libraryIndex;
512+
513+ final fieldMap = {for (var d in constDecls) d.name: d};
514+ final fieldNames = fieldMap.keys.toSet ();
515+ final Map <String , Set <String >> adjList = {
516+ for (var name in fieldNames) name: {}
517+ };
518+ final Map <String , int > inDegrees = {
519+ for (var name in fieldNames) name: 0
520+ };
521+
522+ // Build dependency graph
523+ for (final constDeclInfo in constDecls) {
524+ final dependerName = constDeclInfo.name;
525+ final initializer = constDeclInfo.variable.initializer;
526+ if (initializer != null ) {
527+ final dependencyVisitor = _DependencyVisitor (fieldNames);
528+ initializer.visitChildren (dependencyVisitor);
529+ for (final dependencyName in dependencyVisitor.dependencies) {
530+ if (adjList[dependencyName]! .add (dependerName)) {
531+ inDegrees[dependerName] = (inDegrees[dependerName] ?? 0 ) + 1 ;
532+ }
533+ }
534+ }
535+ }
536+
537+ // Perform topological sort
538+ final sortedOrder = < _ConstDeclarationInfo > [];
539+ final queue = < String > [];
540+ fieldNames.where ((name) => inDegrees[name] == 0 ).forEach (queue.add);
541+
542+ while (queue.isNotEmpty) {
543+ final currentName = queue.removeAt (0 );
544+ sortedOrder.add (fieldMap[currentName]! );
545+ for (final neighborName in adjList[currentName]! ) {
546+ inDegrees[neighborName] = inDegrees[neighborName]! - 1 ;
547+ if (inDegrees[neighborName] == 0 ) {
548+ queue.add (neighborName);
549+ }
550+ }
551+ }
552+
553+ if (sortedOrder.length < constDecls.length) {
554+ final containerName = container is NamedCompilationUnitMember
555+ ? '"${container .name .lexeme }"'
556+ : 'the library' ;
557+ throw CompileError (
558+ 'Circular dependency detected in const declarations in $containerName .' );
559+ }
560+
561+ // Compile sorted consts
562+ for (final declInfo in sortedOrder) {
563+ final parentNode = declInfo.variable.parent! .parent! ;
564+ if (parentNode is TopLevelVariableDeclaration ) {
565+ compileDeclaration (declInfo.variable, _ctx);
566+ alreadyCompiled.add (declInfo.variable);
567+ } else if (parentNode is FieldDeclaration &&
568+ declInfo.parent is ClassDeclaration ) {
569+ _ctx.currentClass = declInfo.parent as ClassDeclaration ;
570+ compileFieldDeclaration (
571+ - 1 , parentNode, _ctx, declInfo.parent as ClassDeclaration );
572+ _ctx.currentClass = null ;
573+ alreadyCompiled.add (parentNode);
574+ }
575+ _ctx.resetStack ();
576+ }
577+ });
578+
579+ // Compile non-statics
488580 _topLevelDeclarationsMap.forEach ((key, value) {
489581 _ctx.topLevelDeclarationPositions[key] = {};
490582 _ctx.instanceDeclarationPositions[key] = {};
@@ -498,11 +590,18 @@ class Compiler implements BridgeDeclarationRegistry, EvalPluginRegistry {
498590 return ;
499591 }
500592 final declaration = tlDeclaration.declaration! ;
593+
594+ // We've already compiled all variable declarations that are top-level or static fields.
595+ // This pass is for compiling the bodies of classes and functions.
501596 if (declaration is ConstructorDeclaration ||
502597 declaration is MethodDeclaration ||
503598 declaration is VariableDeclaration ) {
599+ // This skips top-level vars that were already compiled.
504600 return ;
505601 }
602+
603+ if (alreadyCompiled.contains (declaration)) return ;
604+
506605 _ctx.library = key;
507606 compileDeclaration (declaration, _ctx);
508607 _ctx.resetStack ();
@@ -1216,3 +1315,41 @@ class _Import {
12161315 base .resolveUri (uri), import.prefix? .name, import.combinators);
12171316 }
12181317}
1318+
1319+ // Helper class to hold const declarations for sorting
1320+ class _ConstDeclarationInfo {
1321+ _ConstDeclarationInfo (this .variable, this .parent, this .libraryIndex);
1322+
1323+ /// The VariableDeclaration itself (e.g., `b = 1.0` ).
1324+ final VariableDeclaration variable;
1325+
1326+ /// The parent node (either a TopLevelVariableDeclaration or a ClassDeclaration).
1327+ final AstNode parent;
1328+
1329+ /// The library index where this declaration is defined.
1330+ final int libraryIndex;
1331+
1332+ String get name => variable.name.lexeme;
1333+ }
1334+
1335+ // Add this class at the end of compiler.dart
1336+
1337+ /// A simple visitor to find identifiers within an expression that match a given set of names.
1338+ class _DependencyVisitor extends RecursiveAstVisitor <void > {
1339+ _DependencyVisitor (this .availableNames);
1340+
1341+ /// The set of all possible dependency names we are looking for (e.g., all static const fields in the class).
1342+ final Set <String > availableNames;
1343+
1344+ /// The set of dependencies found in the visited expression.
1345+ final Set <String > dependencies = {};
1346+
1347+ @override
1348+ void visitSimpleIdentifier (SimpleIdentifier node) {
1349+ // If this identifier is one of the fields we are analyzing, it's a dependency.
1350+ if (availableNames.contains (node.name)) {
1351+ dependencies.add (node.name);
1352+ }
1353+ super .visitSimpleIdentifier (node);
1354+ }
1355+ }
0 commit comments