Skip to content

Nested Ternary with Bridged String Variable Causes TypeError: null is not a subtype of int #251

@tedfire

Description

@tedfire

Amazing work on this library and thank you, its a really powerful feature and super useful in my projects. My bug report follows:

Bug: Nested Ternary with Bridged String Variable Causes TypeError: null is not a subtype of int

Library Versions:
• dart_eval: 0.8.2
• flutter_eval: 0.8.1
• Flutter/Dart Environment:
[√] Flutter (Channel stable, 3.32.0, on Microsoft Windows [Version 10.0.26100.6584], locale en-GB) [1,375ms]
• Flutter version 3.32.0 on channel stable at ...\MY_Code\flutter
• Upstream repository https://github.com/flutter/flutter.git
• Framework revision be698c48a6 (4 months ago), 2025-05-19 12:59:14 -0700
• Engine revision 1881800949
• Dart version 3.8.0
• DevTools version 2.45.1

Problem Description:
When evaluating an expression that contains a nested ternary operator, a TypeError is thrown if the condition involves comparing a bridged string variable (a variable passed into the runtime from the host application) with a string literal.
The stack trace indicates the == operator within the sandbox returns null instead of a boolean equivalent (0 or 1). This null value is then passed to a BoxInt operation, which fails with the TypeError: null is not a subtype of int.
A single-level ternary operator with the same bridged variable works correctly. The issue is specific to the nesting of the conditional logic.
Steps to Reproduce:
The following minimal, self-contained code snippet reproduces the error reliably. It uses the required bridging mechanism to pass a Map<String, dynamic> into the runtime.

import 'package:dart_eval/dart_eval.dart';
import 'package:dart_eval/stdlib/core.dart';

// The service class used to evaluate the expression
class DartEvalService {
  final _compiler = Compiler();

  dynamic evaluate(String userExpression, Map<String, dynamic> dataContext) {
    final sourceCode = '''
      dynamic main(Map<String, dynamic> data) {
        // Unpack the map into named variables
        ${dataContext.keys.map((key) => "final $key = data['$key'];").join('\n')}
        return $userExpression;
      }
    ''';

    try {
      final program = _compiler.compile({'my_package': {'main.dart': sourceCode}});
      final runtime = Runtime.ofProgram(program);

      final wrappedContext = $Map.wrap({
        for (var entry in dataContext.entries)
          $String(entry.key): switch (entry.value) {
            String v => $String(v),
            int v    => $int(v),
            double v => $double(v),
            bool v   => $bool(v),
            null     => $null(),
            _        => $null(),
          }
      });

      var result = runtime.executeLib('package:my_package/main.dart', 'main', [wrappedContext]);

      return result?.$value;
    } catch (e) {
      print('--- DART EVAL RUNTIME ERROR ---');
      print('Expression: $userExpression');
      print('Error: $e');
      print('-------------------------------');
      rethrow;
    }
  }
}

// Main function to demonstrate the bug
void main() {
  final service = DartEvalService();

  // The data context passed from the "host" application
  final data = {
    'StageStatus': 'Current',
  };

  // This single-level ternary works perfectly
  print('Testing single ternary (EXPECTED: SUCCESS)');
  final singleTernary = 'StageStatus == "Outstanding" ? 1 : 0';
  service.evaluate(singleTernary, data);
  print('SUCCESS\n');
  
  // This nested ternary fails
  print('Testing nested ternary (EXPECTED: FAILURE)');
  final nestedTernary = 'StageStatus == "Outstanding" ? 1 : (StageStatus == "Current" ? 2 : 3)';
  service.evaluate(nestedTernary, data);
}

Expected Behavior:

The code should execute without any errors. The evaluate call with the nestedTernary expression should successfully return the integer 2.

Actual Behavior:

The code throws a dart_eval runtime exception. The following error and stack trace are produced:

--- DART EVAL DEBUG ---
INCOMING EXPRESSION: "StageStatus=="Outstanding" ? 0xFFFF0000 : (StageStatus=="Current" ? 0xFFFFFF00 : 0xFF00FF00)"
FULL DATA CONTEXT: {salst_name: Tender Identified & Qualified, salst_sort: 10, StageStatus: Current, DateCompleted: 2025-09-16T05:25:38.943Z}
VALUE OF "StageStatus": "Current"
RUNTIME TYPE of "StageStatus": String

--- DART EVAL RUNTIME ERROR ---
Expression: StageStatus=="Outstanding" ? 0xFFFF0000 : (StageStatus=="Current" ? 0xFFFFFF00 : 0xFF00FF00)
Error: dart_eval runtime exception: TypeError: null: type 'Null' is not a subtype of type 'int'
http://localhost:64171/dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/errors.dart 266:3 throw_
http://localhost:64171/dart-sdk/lib/_internal/js_dev_runtime/private/profile.dart 117:39 _asInt
package:dart_eval/src/eval/runtime/ops/primitives.dart 181:50 run
primitives.dart:181
at main()

RUNTIME STATE

Program offset: 127
Stack sample: [L3: $"salst_sort", L4: $10, L5: $"StageStatus", L6: $"Current", L7: $"DateCompleted", L8: $"2025-09-16T05:25:38.943Z", L9: null, *L10: $"Outstanding", L11: false, L12: 4294967040]
Args sample: []
Call stack: [0, -1]
TRACE:
121: CopyValue (L12 <-- L15)
122: Pop (1)
123: Pop (2)
124: CopyValue (L12 <-- L12)
125: Pop (1)
126: Pop (2)
127: BoxInt (L9) <<< EXCEPTION
128: Return (L9)

Thanks again !
Ted

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions