From 60c50dc2260ccdb4bac96d983b26d4eb85a99896 Mon Sep 17 00:00:00 2001 From: jacksonrl <203005141+jacksonrl@users.noreply.github.com> Date: Mon, 8 Dec 2025 22:31:57 -0500 Subject: [PATCH 1/7] static const evalution --- .../sub_parsers/macro_parser.dart | 110 +------- .../header_parser/sub_parsers/var_parser.dart | 67 ++++- pkgs/ffigen/lib/src/header_parser/utils.dart | 113 ++++++++ .../lib/src/visitor/apply_config_filters.dart | 7 + .../test/header_parser_tests/static_const.h | 34 +++ .../static_const_config.yaml | 12 + .../static_const_test.dart | 252 ++++++++++++++++++ 7 files changed, 480 insertions(+), 115 deletions(-) create mode 100644 pkgs/ffigen/test/header_parser_tests/static_const.h create mode 100644 pkgs/ffigen/test/header_parser_tests/static_const_config.yaml create mode 100644 pkgs/ffigen/test/header_parser_tests/static_const_test.dart diff --git a/pkgs/ffigen/lib/src/header_parser/sub_parsers/macro_parser.dart b/pkgs/ffigen/lib/src/header_parser/sub_parsers/macro_parser.dart index 5898b73ce..5a681f523 100644 --- a/pkgs/ffigen/lib/src/header_parser/sub_parsers/macro_parser.dart +++ b/pkgs/ffigen/lib/src/header_parser/sub_parsers/macro_parser.dart @@ -4,7 +4,6 @@ import 'dart:ffi'; import 'dart:io'; -import 'dart:typed_data'; import 'package:ffi/ffi.dart'; import 'package:logging/logging.dart'; @@ -130,13 +129,13 @@ void _macroVariablevisitor( originalName: context.savedMacros[macroName]!.originalName, name: macroName, rawType: 'double', - rawValue: _writeDoubleAsString( + rawValue: writeDoubleAsString( clang.clang_EvalResult_getAsDouble(e), ), ); break; case clang_types.CXEvalResultKind.CXEval_StrLiteral: - final rawValue = _getWrittenRepresentation( + final rawValue = getWrittenStringRepresentation( macroName, clang.clang_EvalResult_getAsStr(e), context, @@ -243,108 +242,3 @@ class MacroVariableString { return s.substring(nameStart, nameStart + len); } } - -/// Gets a written representation string of a C string. -/// -/// E.g- For a string "Hello\nWorld", The new line character is converted to \n. -/// Note: The string is considered to be Utf8, but is treated as Extended ASCII, -/// if the conversion fails. -String _getWrittenRepresentation( - String macroName, - Pointer strPtr, - Context context, -) { - final sb = StringBuffer(); - try { - // Consider string to be Utf8 encoded by default. - sb.clear(); - // This throws a Format Exception if string isn't Utf8 so that we handle it - // in the catch block. - final result = strPtr.cast().toDartString(); - for (final s in result.runes) { - sb.write(_getWritableChar(s)); - } - } catch (e) { - // Handle string if it isn't Utf8. String is considered to be - // Extended ASCII in this case. - context.logger.warning( - "Couldn't decode Macro string '$macroName' as Utf8, using " - 'ASCII instead.', - ); - sb.clear(); - final length = strPtr.cast().length; - final charList = Uint8List.view( - strPtr.cast().asTypedList(length).buffer, - 0, - length, - ); - - for (final char in charList) { - sb.write(_getWritableChar(char, utf8: false)); - } - } - - return sb.toString(); -} - -/// Creates a writable char from [char] code. -/// -/// E.g- `\` is converted to `\\`. -String _getWritableChar(int char, {bool utf8 = true}) { - /// Handle control characters. - if (char >= 0 && char < 32 || char == 127) { - /// Handle these - `\b \t \n \v \f \r` as special cases. - switch (char) { - case 8: // \b - return r'\b'; - case 9: // \t - return r'\t'; - case 10: // \n - return r'\n'; - case 11: // \v - return r'\v'; - case 12: // \f - return r'\f'; - case 13: // \r - return r'\r'; - default: - final h = char.toRadixString(16).toUpperCase().padLeft(2, '0'); - return '\\x$h'; - } - } - - /// Handle characters - `$ ' \` these need to be escaped when writing to file. - switch (char) { - case 36: // $ - return r'\$'; - case 39: // ' - return r"\'"; - case 92: // \ - return r'\\'; - } - - /// In case encoding is not Utf8, we know all characters will fall in [0..255] - /// Print range [128..255] as `\xHH`. - if (!utf8) { - final h = char.toRadixString(16).toUpperCase().padLeft(2, '0'); - return '\\x$h'; - } - - /// In all other cases, simply convert to string. - return String.fromCharCode(char); -} - -/// Converts a double to a string, handling cases like Infinity and NaN. -String _writeDoubleAsString(double d) { - if (d.isFinite) { - return d.toString(); - } else { - // The only Non-Finite numbers are Infinity, NegativeInfinity and NaN. - if (d.isInfinite) { - return d.isNegative - ? strings.doubleNegativeInfinity - : strings.doubleInfinity; - } - return strings.doubleNaN; - } -} diff --git a/pkgs/ffigen/lib/src/header_parser/sub_parsers/var_parser.dart b/pkgs/ffigen/lib/src/header_parser/sub_parsers/var_parser.dart index b9f6d4b53..93f8c03ab 100644 --- a/pkgs/ffigen/lib/src/header_parser/sub_parsers/var_parser.dart +++ b/pkgs/ffigen/lib/src/header_parser/sub_parsers/var_parser.dart @@ -1,7 +1,3 @@ -// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - import '../../code_generator.dart'; import '../../config_provider/config.dart'; import '../../config_provider/config_types.dart'; @@ -10,21 +6,78 @@ import '../clang_bindings/clang_bindings.dart' as clang_types; import '../utils.dart'; /// Parses a global variable -Global? parseVarDeclaration(Context context, clang_types.CXCursor cursor) { +Binding? parseVarDeclaration(Context context, clang_types.CXCursor cursor) { final logger = context.logger; final config = context.config; final nativeOutputStyle = config.output.style is NativeExternalBindings; final bindingsIndex = context.bindingsIndex; final name = cursor.spelling(); final usr = cursor.usr(); + if (bindingsIndex.isSeenGlobalVar(usr)) { return bindingsIndex.getSeenGlobalVar(usr); } + if (bindingsIndex.isSeenVariableConstant(usr)) { + return bindingsIndex.getSeenVariableConstant(usr); + } + final decl = Declaration(usr: usr, originalName: name); + final cType = cursor.type(); - logger.fine('++++ Adding Global: ${cursor.completeStringRepr()}'); + // Try to evaluate as a constant first. + if (cType.isConstQualified) { + final evalResult = clang.clang_Cursor_Evaluate(cursor); + final evalKind = clang.clang_EvalResult_getKind(evalResult); + Constant? constant; - final cType = cursor.type(); + switch (evalKind) { + case clang_types.CXEvalResultKind.CXEval_Int: + final value = clang.clang_EvalResult_getAsLongLong(evalResult); + constant = Constant( + usr: usr, + originalName: name, + name: config.globals.rename(decl), + dartDoc: getCursorDocComment(context, cursor), + rawType: 'int', + rawValue: value.toString(), + ); + break; + case clang_types.CXEvalResultKind.CXEval_Float: + final value = clang.clang_EvalResult_getAsDouble(evalResult); + constant = Constant( + usr: usr, + originalName: name, + name: config.globals.rename(decl), + dartDoc: getCursorDocComment(context, cursor), + rawType: 'double', + rawValue: writeDoubleAsString(value), + ); + break; + case clang_types.CXEvalResultKind.CXEval_StrLiteral: + final value = clang.clang_EvalResult_getAsStr(evalResult); + final rawValue = getWrittenStringRepresentation(name, value, context); + constant = Constant( + usr: usr, + originalName: name, + name: config.globals.rename(decl), + dartDoc: getCursorDocComment(context, cursor), + rawType: 'String', + rawValue: "'$rawValue'", + ); + break; + } + clang.clang_EvalResult_dispose(evalResult); + + if (constant != null) { + logger.fine( + '++++ Adding Constant from Global: ${cursor.completeStringRepr()}', + ); + bindingsIndex.addVariableConstantToSeen(usr, constant); + return constant; + } + } + + logger.fine('++++ Adding Global: ${cursor.completeStringRepr()}'); final type = cType.toCodeGenType( context, diff --git a/pkgs/ffigen/lib/src/header_parser/utils.dart b/pkgs/ffigen/lib/src/header_parser/utils.dart index 95a022bde..284aeb088 100644 --- a/pkgs/ffigen/lib/src/header_parser/utils.dart +++ b/pkgs/ffigen/lib/src/header_parser/utils.dart @@ -3,6 +3,7 @@ // BSD-style license that can be found in the LICENSE file. import 'dart:ffi'; +import 'dart:typed_data'; import 'package:ffi/ffi.dart'; import 'package:logging/logging.dart'; @@ -11,6 +12,7 @@ import '../code_generator.dart'; import '../config_provider/config_types.dart'; import '../context.dart'; import '../strings.dart'; +import '../strings.dart' as strings; import 'clang_bindings/clang_bindings.dart' as clang_types; import 'type_extractor/extractor.dart'; @@ -492,6 +494,7 @@ class BindingsIndex { final Map _unnamedEnumConstants = {}; final Map _macros = {}; final Map _globals = {}; + final Map _variableConstants = {}; final Map _objcBlocks = {}; final Map _objcProtocols = {}; final Map _objcCategories = {}; @@ -512,6 +515,11 @@ class BindingsIndex { bool isSeenGlobalVar(String usr) => _globals.containsKey(usr); void addGlobalVarToSeen(String usr, Global global) => _globals[usr] = global; Global? getSeenGlobalVar(String usr) => _globals[usr]; + bool isSeenVariableConstant(String usr) => + _variableConstants.containsKey(usr); + void addVariableConstantToSeen(String usr, Constant constant) => + _variableConstants[usr] = constant; + Constant? getSeenVariableConstant(String usr) => _variableConstants[usr]; bool isSeenMacro(String usr) => _macros.containsKey(usr); void addMacroToSeen(String usr, String macro) => _macros[usr] = macro; bool isSeenUnsupportedTypealias(String usr) => @@ -576,3 +584,108 @@ class CursorIndex { } } } + +/// Converts a double to a string, handling cases like Infinity and NaN. +String writeDoubleAsString(double d) { + if (d.isFinite) { + return d.toString(); + } else { + // The only Non-Finite numbers are Infinity, NegativeInfinity and NaN. + if (d.isInfinite) { + return d.isNegative + ? strings.doubleNegativeInfinity + : strings.doubleInfinity; + } + return strings.doubleNaN; + } +} + +/// Gets a written representation string of a C string. +/// +/// E.g- For a string "Hello\nWorld", The new line character is converted to \n. +/// Note: The string is considered to be Utf8, but is treated as Extended ASCII, +/// if the conversion fails. +String getWrittenStringRepresentation( + String varName, + Pointer strPtr, + Context context, +) { + final sb = StringBuffer(); + try { + // Consider string to be Utf8 encoded by default. + sb.clear(); + // This throws a Format Exception if string isn't Utf8 so that we handle it + // in the catch block. + final result = strPtr.cast().toDartString(); + for (final s in result.runes) { + sb.write(_getWritableChar(s)); + } + } catch (e) { + // Handle string if it isn't Utf8. String is considered to be + // Extended ASCII in this case. + context.logger.warning( + "Couldn't decode string value for '$varName' as Utf8, using " + 'ASCII instead.', + ); + sb.clear(); + final length = strPtr.cast().length; + final charList = Uint8List.view( + strPtr.cast().asTypedList(length).buffer, + 0, + length, + ); + + for (final char in charList) { + sb.write(_getWritableChar(char, utf8: false)); + } + } + + return sb.toString(); +} + +/// Creates a writable char from [char] code. +/// +/// E.g- `\` is converted to `\\`. +String _getWritableChar(int char, {bool utf8 = true}) { + /// Handle control characters. + if (char >= 0 && char < 32 || char == 127) { + /// Handle these - `\b \t \n \v \f \r` as special cases. + switch (char) { + case 8: // \b + return r'\b'; + case 9: // \t + return r'\t'; + case 10: // \n + return r'\n'; + case 11: // \v + return r'\v'; + case 12: // \f + return r'\f'; + case 13: // \r + return r'\r'; + default: + final h = char.toRadixString(16).toUpperCase().padLeft(2, '0'); + return '\\x$h'; + } + } + + /// Handle characters - `$ ' \` these need to be escaped when writing to file. + switch (char) { + case 36: // $ + return r'\$'; + case 39: // ' + return r"\'"; + case 92: // \ + return r'\\'; + } + + /// In case encoding is not Utf8, we know all characters will fall in [0..255] + /// Print range [128..255] as `\xHH`. + if (!utf8) { + final h = char.toRadixString(16).toUpperCase().padLeft(2, '0'); + return '\\x$h'; + } + + /// In all other cases, simply convert to string. + return String.fromCharCode(char); +} diff --git a/pkgs/ffigen/lib/src/visitor/apply_config_filters.dart b/pkgs/ffigen/lib/src/visitor/apply_config_filters.dart index 3fe2bfbfe..9801f1f9c 100644 --- a/pkgs/ffigen/lib/src/visitor/apply_config_filters.dart +++ b/pkgs/ffigen/lib/src/visitor/apply_config_filters.dart @@ -91,6 +91,13 @@ class ApplyConfigFiltersVisitation extends Visitation { @override void visitGlobal(Global node) => _visitImpl(node, config.globals); + @override + void visitConstant(Constant node) { + // MacroConstant and UnnamedEnumConstant have their own overrides, so this + // only applies to base Constants (e.g. from static const variables). + _visitImpl(node, config.globals); + } + @override void visitTypealias(Typealias node) => _visitImpl(node, config.typedefs); } diff --git a/pkgs/ffigen/test/header_parser_tests/static_const.h b/pkgs/ffigen/test/header_parser_tests/static_const.h new file mode 100644 index 000000000..e4f0753ae --- /dev/null +++ b/pkgs/ffigen/test/header_parser_tests/static_const.h @@ -0,0 +1,34 @@ +// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include +#include + +static const int TEST_INT = 10; +static const int TEST_NEGATIVE_INT = -10; +static const double TEST_DOUBLE = 3.14; +static const double TEST_NEGATIVE_DOUBLE = -3.14; + +static const int TEST_EXPRESSION = 5 + 5; +static const int TEST_HEX = 0xFF; +static const int TEST_NEGATIVE_HEX = -0xFF; + +static const double TEST_INF = INFINITY; +static const double TEST_NEGATIVE_INF = -INFINITY; +static const double TEST_NAN = NAN; + +static const char* const TEST_STRING = "test"; +static const char* const TEST_STRING_SPECIAL = "$dollar"; +static const char* const TEST_STRING_QUOTES = "test's"; +static const char* const TEST_STRING_BACKSLASH = "test\\"; +static const char* const TEST_STRING_CONTROLS = "hello\n\t\r\v\b"; + +typedef uint64_t MyFlags; +typedef MyFlags MyBufferUsage; +static const MyBufferUsage MyBufferUsage_None = 0x0000000000000000; +static const MyBufferUsage MyBufferUsage_MapRead = 0x0000000000000001; + +// Should be generated as Globals. +static const char TEST_STRING_ARRAY[] = "test_array"; +int test_global; \ No newline at end of file diff --git a/pkgs/ffigen/test/header_parser_tests/static_const_config.yaml b/pkgs/ffigen/test/header_parser_tests/static_const_config.yaml new file mode 100644 index 000000000..bb806f718 --- /dev/null +++ b/pkgs/ffigen/test/header_parser_tests/static_const_config.yaml @@ -0,0 +1,12 @@ +# Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file +# for details. All rights reserved. Use of this source code is governed by a +# BSD-style license that can be found in the LICENSE file. + +name: 'NativeLibrary' +description: 'Static Const Test' +output: 'unused' +headers: + entry-points: + - static_const.h + include-directives: + - '**static_const.h' \ No newline at end of file diff --git a/pkgs/ffigen/test/header_parser_tests/static_const_test.dart b/pkgs/ffigen/test/header_parser_tests/static_const_test.dart new file mode 100644 index 000000000..a8fb0e4b8 --- /dev/null +++ b/pkgs/ffigen/test/header_parser_tests/static_const_test.dart @@ -0,0 +1,252 @@ +// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:ffigen/src/code_generator.dart'; +import 'package:ffigen/src/header_parser.dart' as parser; +import 'package:ffigen/src/strings.dart' as strings; +import 'package:path/path.dart' as path; +import 'package:test/test.dart'; + +import '../test_utils.dart'; + +late Library actual, expected; + +void main() { + group('static_const_test', () { + setUpAll(() { + expected = expectedLibrary(); + actual = parser.parse( + testContext( + testConfigFromPath( + configPath( + path.join(packagePathForTests, 'test', 'header_parser_tests'), + 'static_const_config.yaml', + ), + ), + ), + ); + }); + + test('Total bindings count', () { + expect(actual.bindings.length, expected.bindings.length); + }); + + test('TEST_INT', () { + expect( + actual.getBindingAsString('TEST_INT'), + expected.getBindingAsString('TEST_INT'), + ); + }); + + test('TEST_NEGATIVE_INT', () { + expect( + actual.getBindingAsString('TEST_NEGATIVE_INT'), + expected.getBindingAsString('TEST_NEGATIVE_INT'), + ); + }); + + test('TEST_DOUBLE', () { + expect( + actual.getBindingAsString('TEST_DOUBLE'), + expected.getBindingAsString('TEST_DOUBLE'), + ); + }); + + test('TEST_NEGATIVE_DOUBLE', () { + expect( + actual.getBindingAsString('TEST_NEGATIVE_DOUBLE'), + expected.getBindingAsString('TEST_NEGATIVE_DOUBLE'), + ); + }); + + test('TEST_EXPRESSION', () { + expect( + actual.getBindingAsString('TEST_EXPRESSION'), + expected.getBindingAsString('TEST_EXPRESSION'), + ); + }); + + test('TEST_HEX', () { + expect( + actual.getBindingAsString('TEST_HEX'), + expected.getBindingAsString('TEST_HEX'), + ); + }); + + test('TEST_NEGATIVE_HEX', () { + expect( + actual.getBindingAsString('TEST_NEGATIVE_HEX'), + expected.getBindingAsString('TEST_NEGATIVE_HEX'), + ); + }); + + test('TEST_STRING', () { + expect( + actual.getBindingAsString('TEST_STRING'), + expected.getBindingAsString('TEST_STRING'), + ); + }); + + test('TEST_STRING_SPECIAL', () { + expect( + actual.getBindingAsString('TEST_STRING_SPECIAL'), + expected.getBindingAsString('TEST_STRING_SPECIAL'), + ); + }); + + test('TEST_STRING_QUOTES', () { + expect( + actual.getBindingAsString('TEST_STRING_QUOTES'), + expected.getBindingAsString('TEST_STRING_QUOTES'), + ); + }); + + test('TEST_STRING_BACKSLASH', () { + expect( + actual.getBindingAsString('TEST_STRING_BACKSLASH'), + expected.getBindingAsString('TEST_STRING_BACKSLASH'), + ); + }); + + test('TEST_STRING_CONTROLS', () { + expect( + actual.getBindingAsString('TEST_STRING_CONTROLS'), + expected.getBindingAsString('TEST_STRING_CONTROLS'), + ); + }); + + test('TEST_INF', () { + expect( + actual.getBindingAsString('TEST_INF'), + expected.getBindingAsString('TEST_INF'), + ); + }); + + test('TEST_NEGATIVE_INF', () { + expect( + actual.getBindingAsString('TEST_NEGATIVE_INF'), + expected.getBindingAsString('TEST_NEGATIVE_INF'), + ); + }); + + test('TEST_NAN', () { + expect( + actual.getBindingAsString('TEST_NAN'), + expected.getBindingAsString('TEST_NAN'), + ); + }); + + test('MyFlags typedef', () { + expect( + actual.getBindingAsString('MyFlags'), + expected.getBindingAsString('MyFlags'), + ); + }); + + test('MyBufferUsage typedef', () { + expect( + actual.getBindingAsString('MyBufferUsage'), + expected.getBindingAsString('MyBufferUsage'), + ); + }); + + test('MyBufferUsage_None', () { + expect( + actual.getBindingAsString('MyBufferUsage_None'), + expected.getBindingAsString('MyBufferUsage_None'), + ); + }); + + test('MyBufferUsage_MapRead', () { + expect( + actual.getBindingAsString('MyBufferUsage_MapRead'), + expected.getBindingAsString('MyBufferUsage_MapRead'), + ); + }); + + test('TEST_STRING_ARRAY', () { + expect( + actual.getBindingAsString('TEST_STRING_ARRAY'), + expected.getBindingAsString('TEST_STRING_ARRAY'), + ); + }); + + test('test_global', () { + expect( + actual.getBindingAsString('test_global'), + expected.getBindingAsString('test_global'), + ); + }); + }); +} + +Library expectedLibrary() { + final myFlags = Typealias( + name: 'MyFlags', + type: NativeType(SupportedNativeType.uint64), + ); + final myBufferUsage = Typealias(name: 'MyBufferUsage', type: myFlags); + + return Library( + context: testContext(), + bindings: [ + Constant(name: 'TEST_INT', rawType: 'int', rawValue: '10'), + Constant(name: 'TEST_NEGATIVE_INT', rawType: 'int', rawValue: '-10'), + Constant(name: 'TEST_DOUBLE', rawType: 'double', rawValue: '3.14'), + Constant( + name: 'TEST_NEGATIVE_DOUBLE', + rawType: 'double', + rawValue: '-3.14', + ), + Constant(name: 'TEST_EXPRESSION', rawType: 'int', rawValue: '10'), + Constant(name: 'TEST_HEX', rawType: 'int', rawValue: '255'), + Constant(name: 'TEST_NEGATIVE_HEX', rawType: 'int', rawValue: '-255'), + Constant(name: 'TEST_STRING', rawType: 'String', rawValue: "'test'"), + Constant( + name: 'TEST_STRING_SPECIAL', + rawType: 'String', + rawValue: r"'\$dollar'", + ), + Constant( + name: 'TEST_STRING_QUOTES', + rawType: 'String', + rawValue: r"'test\'s'", + ), + Constant( + name: 'TEST_STRING_BACKSLASH', + rawType: 'String', + rawValue: r"'test\\'", + ), + Constant( + name: 'TEST_STRING_CONTROLS', + rawType: 'String', + rawValue: r"'hello\n\t\r\v\b'", + ), + Constant( + name: 'TEST_INF', + rawType: 'double', + rawValue: strings.doubleInfinity, + ), + Constant( + name: 'TEST_NEGATIVE_INF', + rawType: 'double', + rawValue: strings.doubleNegativeInfinity, + ), + Constant( + name: 'TEST_NAN', + rawType: 'double', + rawValue: strings.doubleNaN, + ), + myFlags, + myBufferUsage, + Constant(name: 'MyBufferUsage_None', rawType: 'int', rawValue: '0'), + Constant(name: 'MyBufferUsage_MapRead', rawType: 'int', rawValue: '1'), + Global( + name: 'TEST_STRING_ARRAY', + type: ConstantArray(11, charType, useArrayType: false), + ), + Global(name: 'test_global', type: intType), + ], + )..forceFillNamesForTesting(); +} From ed3fc8215dd22f418b59d1fdf9bb4bba9b894e8f Mon Sep 17 00:00:00 2001 From: jacksonrl <203005141+jacksonrl@users.noreply.github.com> Date: Mon, 8 Dec 2025 22:35:58 -0500 Subject: [PATCH 2/7] fix header --- pkgs/ffigen/lib/src/header_parser/sub_parsers/var_parser.dart | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkgs/ffigen/lib/src/header_parser/sub_parsers/var_parser.dart b/pkgs/ffigen/lib/src/header_parser/sub_parsers/var_parser.dart index 93f8c03ab..0127a7f3c 100644 --- a/pkgs/ffigen/lib/src/header_parser/sub_parsers/var_parser.dart +++ b/pkgs/ffigen/lib/src/header_parser/sub_parsers/var_parser.dart @@ -1,3 +1,7 @@ +// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + import '../../code_generator.dart'; import '../../config_provider/config.dart'; import '../../config_provider/config_types.dart'; From c80fbe4012f8e910b09033fc41e3aee385ba144c Mon Sep 17 00:00:00 2001 From: jacksonrl <203005141+jacksonrl@users.noreply.github.com> Date: Tue, 9 Dec 2025 11:26:25 -0500 Subject: [PATCH 3/7] do not proxy variables where the config asks for the variable's address --- .../ffigen/lib/src/header_parser/sub_parsers/var_parser.dart | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pkgs/ffigen/lib/src/header_parser/sub_parsers/var_parser.dart b/pkgs/ffigen/lib/src/header_parser/sub_parsers/var_parser.dart index 0127a7f3c..6bd83d10b 100644 --- a/pkgs/ffigen/lib/src/header_parser/sub_parsers/var_parser.dart +++ b/pkgs/ffigen/lib/src/header_parser/sub_parsers/var_parser.dart @@ -28,8 +28,9 @@ Binding? parseVarDeclaration(Context context, clang_types.CXCursor cursor) { final decl = Declaration(usr: usr, originalName: name); final cType = cursor.type(); - // Try to evaluate as a constant first. - if (cType.isConstQualified) { + // Try to evaluate as a constant first, + // unless the config asks for the variable's address. + if (cType.isConstQualified && !config.globals.includeSymbolAddress(decl)) { final evalResult = clang.clang_Cursor_Evaluate(cursor); final evalKind = clang.clang_EvalResult_getKind(evalResult); Constant? constant; From 8c4f8a3caedbd0e77bea2e36a923e6858ab95912 Mon Sep 17 00:00:00 2001 From: jacksonrl <203005141+jacksonrl@users.noreply.github.com> Date: Tue, 9 Dec 2025 21:13:33 -0500 Subject: [PATCH 4/7] Update CHANGELOG.md --- pkgs/ffigen/CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkgs/ffigen/CHANGELOG.md b/pkgs/ffigen/CHANGELOG.md index 661143c13..c0b649599 100644 --- a/pkgs/ffigen/CHANGELOG.md +++ b/pkgs/ffigen/CHANGELOG.md @@ -9,6 +9,8 @@ - __Breaking change__: Certain synthetic USRs have been modified to ensure they cannot collide with real USRs. It's very unlikely that any user facing USRs are affected. +- __Breaking change__: Dart const values will be generated for global variables marked const in C (e.g. static const int) instead of symbol lookups. This supports integers, doubles, and string literals. Including the variable name in the globals -> symbol-address configuration will still generate symbol lookups. + ## 20.1.1 From a3a7cb1f2cd9f9e2a7ec9c5ad314b72b6775e7f0 Mon Sep 17 00:00:00 2001 From: jacksonrl <203005141+jacksonrl@users.noreply.github.com> Date: Thu, 11 Dec 2025 00:48:24 -0500 Subject: [PATCH 5/7] apply feedback --- pkgs/ffigen/CHANGELOG.md | 6 +++++- pkgs/ffigen/test/header_parser_tests/static_const.h | 2 +- .../test/header_parser_tests/static_const_config.yaml | 3 ++- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/pkgs/ffigen/CHANGELOG.md b/pkgs/ffigen/CHANGELOG.md index c0b649599..da30a97c6 100644 --- a/pkgs/ffigen/CHANGELOG.md +++ b/pkgs/ffigen/CHANGELOG.md @@ -9,7 +9,11 @@ - __Breaking change__: Certain synthetic USRs have been modified to ensure they cannot collide with real USRs. It's very unlikely that any user facing USRs are affected. -- __Breaking change__: Dart const values will be generated for global variables marked const in C (e.g. static const int) instead of symbol lookups. This supports integers, doubles, and string literals. Including the variable name in the globals -> symbol-address configuration will still generate symbol lookups. +- __Breaking change__: Dart const values will be generated for global variables + marked const in C (e.g. static const int) instead of symbol lookups. This + supports integers, doubles, and string literals. Including the variable name + in the globals -> symbol-address configuration will still generate symbol + lookups. ## 20.1.1 diff --git a/pkgs/ffigen/test/header_parser_tests/static_const.h b/pkgs/ffigen/test/header_parser_tests/static_const.h index e4f0753ae..78ffaf5ef 100644 --- a/pkgs/ffigen/test/header_parser_tests/static_const.h +++ b/pkgs/ffigen/test/header_parser_tests/static_const.h @@ -31,4 +31,4 @@ static const MyBufferUsage MyBufferUsage_MapRead = 0x0000000000000001; // Should be generated as Globals. static const char TEST_STRING_ARRAY[] = "test_array"; -int test_global; \ No newline at end of file +int test_global; diff --git a/pkgs/ffigen/test/header_parser_tests/static_const_config.yaml b/pkgs/ffigen/test/header_parser_tests/static_const_config.yaml index bb806f718..ee0fab82c 100644 --- a/pkgs/ffigen/test/header_parser_tests/static_const_config.yaml +++ b/pkgs/ffigen/test/header_parser_tests/static_const_config.yaml @@ -9,4 +9,5 @@ headers: entry-points: - static_const.h include-directives: - - '**static_const.h' \ No newline at end of file + - '**static_const.h' + \ No newline at end of file From f76074c14901c505205ba002c1c7fc3fd35718fc Mon Sep 17 00:00:00 2001 From: jacksonrl <203005141+jacksonrl@users.noreply.github.com> Date: Thu, 11 Dec 2025 00:54:08 -0500 Subject: [PATCH 6/7] try again --- pkgs/ffigen/test/header_parser_tests/static_const_config.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/pkgs/ffigen/test/header_parser_tests/static_const_config.yaml b/pkgs/ffigen/test/header_parser_tests/static_const_config.yaml index ee0fab82c..48431be35 100644 --- a/pkgs/ffigen/test/header_parser_tests/static_const_config.yaml +++ b/pkgs/ffigen/test/header_parser_tests/static_const_config.yaml @@ -10,4 +10,3 @@ headers: - static_const.h include-directives: - '**static_const.h' - \ No newline at end of file From 861475802eff9dc1053753920621a8d7bcf3c627 Mon Sep 17 00:00:00 2001 From: jacksonrl <203005141+jacksonrl@users.noreply.github.com> Date: Thu, 11 Dec 2025 00:55:42 -0500 Subject: [PATCH 7/7] ++ --- pkgs/ffigen/test/header_parser_tests/static_const_config.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/pkgs/ffigen/test/header_parser_tests/static_const_config.yaml b/pkgs/ffigen/test/header_parser_tests/static_const_config.yaml index 48431be35..c48f79a47 100644 --- a/pkgs/ffigen/test/header_parser_tests/static_const_config.yaml +++ b/pkgs/ffigen/test/header_parser_tests/static_const_config.yaml @@ -10,3 +10,4 @@ headers: - static_const.h include-directives: - '**static_const.h' +