eval method
Evaluates an expression or statement in the context of previously executed code.
This method allows you to execute additional code in the same environment
as a previous execute() call, similar to a REPL experience.
Important: You must call execute() at least once before calling eval()
to establish the execution context.
expression The Dart expression or statement to evaluate.
Example:
final d4rt = D4rt();
// First, set up the context
d4rt.execute(source: '''
var counter = 0;
void increment() { counter++; }
int getCounter() => counter;
''', name: 'getCounter');
// Now use eval to interact with the established context
d4rt.eval('increment()');
d4rt.eval('increment()');
final result = d4rt.eval('getCounter()'); // Returns 2
// You can also define new functions
d4rt.eval('int double(int x) => x * 2;');
final doubled = d4rt.eval('double(counter)'); // Returns 4
Implementation
dynamic eval(String expression) {
if (_visitor == null || !_hasExecutedOnce) {
throw RuntimeD4rtException(
'eval() requires an existing execution context. Call execute() first.',
);
}
Logger.debug("[D4rt.eval] Evaluating: $expression");
final executionEnvironment = _moduleLoader.globalEnvironment;
// First, try to parse as a top-level declaration (function, class, variable)
final declarationParseResult = parseString(
content: expression,
throwIfDiagnostics: false,
featureSet: FeatureSet.fromEnableFlags2(
sdkLanguageVersion: Version(3, 10, 0),
flags: [
'non-nullable',
'null-aware-elements',
'triple-shift',
'spread-collections',
'control-flow-collections',
'extension-methods',
'extension-types',
'digit-separators',
],
),
);
// Check if it parses as valid declaration(s)
final declErrors = declarationParseResult.errors
.where((e) => e.diagnosticCode.severity == DiagnosticSeverity.ERROR)
.toList();
if (declErrors.isEmpty &&
declarationParseResult.unit.declarations.isNotEmpty) {
// It's a declaration - process it directly in the global environment
final compilationUnit = declarationParseResult.unit;
// Declaration pass
final declarationVisitor = DeclarationVisitor(executionEnvironment);
for (final declaration in compilationUnit.declarations) {
declaration.accept<void>(declarationVisitor);
}
// Interpretation pass — RC-4: ordered by type to handle forward references
for (final declaration in compilationUnit.declarations) {
if (declaration is EnumDeclaration) {
declaration.accept<Object?>(_visitor!);
}
}
// Bug-43 / forward-class-reference FIX (mirrors above).
_visitor!.deferStaticFieldInits = true;
try {
for (final declaration in compilationUnit.declarations) {
if (declaration is ClassDeclaration ||
declaration is MixinDeclaration) {
declaration.accept<Object?>(_visitor!);
}
}
} finally {
_visitor!.deferStaticFieldInits = false;
}
_visitor!.runDeferredStaticInitializers();
for (final declaration in compilationUnit.declarations) {
if (declaration is! EnumDeclaration &&
declaration is! ClassDeclaration &&
declaration is! MixinDeclaration) {
declaration.accept<Object?>(_visitor!);
}
}
Logger.debug("[D4rt.eval] Processed declaration(s)");
return null;
}
// Check if this looks like multiple statements (contains ; followed by more code)
// This heuristic helps us choose the right wrapper: statements vs expression
final trimmedExpr = expression.trim();
final looksLikeMultiStatement = RegExp(r';\s*\S').hasMatch(trimmedExpr);
// For single expressions, try wrapping with return to get the value
if (!looksLikeMultiStatement) {
final wrappedSource =
'''
dynamic __eval__() {
return $expression;
}
''';
final parseResult = parseString(
content: wrappedSource,
throwIfDiagnostics: false,
featureSet: FeatureSet.fromEnableFlags2(
sdkLanguageVersion: Version(3, 10, 0),
flags: [
'non-nullable',
'null-aware-elements',
'triple-shift',
'spread-collections',
'control-flow-collections',
'extension-methods',
'extension-types',
'digit-separators',
],
),
);
if (parseResult.errors.isEmpty) {
// Execute as expression with return value
final compilationUnit = parseResult.unit;
final declarationVisitor = DeclarationVisitor(executionEnvironment);
for (final declaration in compilationUnit.declarations) {
declaration.accept<void>(declarationVisitor);
}
for (final declaration in compilationUnit.declarations) {
declaration.accept<Object?>(_visitor!);
}
// Call the __eval__ function
final evalFunc = executionEnvironment.get('__eval__');
Object? result;
if (evalFunc is Callable) {
try {
result = evalFunc.call(_visitor!, [], {});
} on InternalInterpreterD4rtException catch (e) {
if (e.originalThrownValue is RuntimeD4rtException) {
throw e.originalThrownValue as RuntimeD4rtException;
}
throw e.originalThrownValue ?? e;
}
}
final bridgedResult = _bridgeInterpreterValueToNative(result);
Logger.debug("[D4rt.eval] Result: $bridgedResult");
if (bridgedResult is Future) {
return bridgedResult.then(
(value) => _bridgeInterpreterValueToNative(value),
);
}
return bridgedResult;
}
}
// Try parsing as statement(s) (no return value expected)
// This is used for multi-statement code or when expression wrapper fails
final statementSource =
'''
void __eval__() {
$expression
}
''';
final statementParseResult = parseString(
content: statementSource,
throwIfDiagnostics: false,
featureSet: FeatureSet.fromEnableFlags2(
sdkLanguageVersion: Version(3, 10, 0),
flags: [
'non-nullable',
'null-aware-elements',
'triple-shift',
'spread-collections',
'control-flow-collections',
'extension-methods',
'extension-types',
'digit-separators',
],
),
);
if (statementParseResult.errors.isEmpty) {
final compilationUnit = statementParseResult.unit;
final declarationVisitor = DeclarationVisitor(executionEnvironment);
for (final declaration in compilationUnit.declarations) {
declaration.accept<void>(declarationVisitor);
}
for (final declaration in compilationUnit.declarations) {
declaration.accept<Object?>(_visitor!);
}
// Call the __eval__ function
final evalFunc = executionEnvironment.get('__eval__');
if (evalFunc is Callable) {
try {
evalFunc.call(_visitor!, [], {});
} on InternalInterpreterD4rtException catch (e) {
if (e.originalThrownValue is RuntimeD4rtException) {
throw e.originalThrownValue as RuntimeD4rtException;
}
throw e.originalThrownValue ?? e;
}
}
Logger.debug("[D4rt.eval] Executed statement");
return null;
}
// All parsing attempts failed
final errorMessages = declErrors
.map((e) {
final location = declarationParseResult.lineInfo.getLocation(
e.offset,
);
return "- ${e.message} (line ${location.lineNumber}, column ${location.columnNumber})";
})
.join("\n");
throw SourceCodeD4rtException(
'Failed to parse expression:\n$errorMessages',
expression,
);
}