Skip to content

Commit 60c50dc

Browse files
committed
static const evalution
1 parent 9d538ae commit 60c50dc

File tree

7 files changed

+480
-115
lines changed

7 files changed

+480
-115
lines changed

pkgs/ffigen/lib/src/header_parser/sub_parsers/macro_parser.dart

Lines changed: 2 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
import 'dart:ffi';
66
import 'dart:io';
7-
import 'dart:typed_data';
87

98
import 'package:ffi/ffi.dart';
109
import 'package:logging/logging.dart';
@@ -130,13 +129,13 @@ void _macroVariablevisitor(
130129
originalName: context.savedMacros[macroName]!.originalName,
131130
name: macroName,
132131
rawType: 'double',
133-
rawValue: _writeDoubleAsString(
132+
rawValue: writeDoubleAsString(
134133
clang.clang_EvalResult_getAsDouble(e),
135134
),
136135
);
137136
break;
138137
case clang_types.CXEvalResultKind.CXEval_StrLiteral:
139-
final rawValue = _getWrittenRepresentation(
138+
final rawValue = getWrittenStringRepresentation(
140139
macroName,
141140
clang.clang_EvalResult_getAsStr(e),
142141
context,
@@ -243,108 +242,3 @@ class MacroVariableString {
243242
return s.substring(nameStart, nameStart + len);
244243
}
245244
}
246-
247-
/// Gets a written representation string of a C string.
248-
///
249-
/// E.g- For a string "Hello\nWorld", The new line character is converted to \n.
250-
/// Note: The string is considered to be Utf8, but is treated as Extended ASCII,
251-
/// if the conversion fails.
252-
String _getWrittenRepresentation(
253-
String macroName,
254-
Pointer<Char> strPtr,
255-
Context context,
256-
) {
257-
final sb = StringBuffer();
258-
try {
259-
// Consider string to be Utf8 encoded by default.
260-
sb.clear();
261-
// This throws a Format Exception if string isn't Utf8 so that we handle it
262-
// in the catch block.
263-
final result = strPtr.cast<Utf8>().toDartString();
264-
for (final s in result.runes) {
265-
sb.write(_getWritableChar(s));
266-
}
267-
} catch (e) {
268-
// Handle string if it isn't Utf8. String is considered to be
269-
// Extended ASCII in this case.
270-
context.logger.warning(
271-
"Couldn't decode Macro string '$macroName' as Utf8, using "
272-
'ASCII instead.',
273-
);
274-
sb.clear();
275-
final length = strPtr.cast<Utf8>().length;
276-
final charList = Uint8List.view(
277-
strPtr.cast<Uint8>().asTypedList(length).buffer,
278-
0,
279-
length,
280-
);
281-
282-
for (final char in charList) {
283-
sb.write(_getWritableChar(char, utf8: false));
284-
}
285-
}
286-
287-
return sb.toString();
288-
}
289-
290-
/// Creates a writable char from [char] code.
291-
///
292-
/// E.g- `\` is converted to `\\`.
293-
String _getWritableChar(int char, {bool utf8 = true}) {
294-
/// Handle control characters.
295-
if (char >= 0 && char < 32 || char == 127) {
296-
/// Handle these - `\b \t \n \v \f \r` as special cases.
297-
switch (char) {
298-
case 8: // \b
299-
return r'\b';
300-
case 9: // \t
301-
return r'\t';
302-
case 10: // \n
303-
return r'\n';
304-
case 11: // \v
305-
return r'\v';
306-
case 12: // \f
307-
return r'\f';
308-
case 13: // \r
309-
return r'\r';
310-
default:
311-
final h = char.toRadixString(16).toUpperCase().padLeft(2, '0');
312-
return '\\x$h';
313-
}
314-
}
315-
316-
/// Handle characters - `$ ' \` these need to be escaped when writing to file.
317-
switch (char) {
318-
case 36: // $
319-
return r'\$';
320-
case 39: // '
321-
return r"\'";
322-
case 92: // \
323-
return r'\\';
324-
}
325-
326-
/// In case encoding is not Utf8, we know all characters will fall in [0..255]
327-
/// Print range [128..255] as `\xHH`.
328-
if (!utf8) {
329-
final h = char.toRadixString(16).toUpperCase().padLeft(2, '0');
330-
return '\\x$h';
331-
}
332-
333-
/// In all other cases, simply convert to string.
334-
return String.fromCharCode(char);
335-
}
336-
337-
/// Converts a double to a string, handling cases like Infinity and NaN.
338-
String _writeDoubleAsString(double d) {
339-
if (d.isFinite) {
340-
return d.toString();
341-
} else {
342-
// The only Non-Finite numbers are Infinity, NegativeInfinity and NaN.
343-
if (d.isInfinite) {
344-
return d.isNegative
345-
? strings.doubleNegativeInfinity
346-
: strings.doubleInfinity;
347-
}
348-
return strings.doubleNaN;
349-
}
350-
}

pkgs/ffigen/lib/src/header_parser/sub_parsers/var_parser.dart

Lines changed: 60 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,3 @@
1-
// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
2-
// for details. All rights reserved. Use of this source code is governed by a
3-
// BSD-style license that can be found in the LICENSE file.
4-
51
import '../../code_generator.dart';
62
import '../../config_provider/config.dart';
73
import '../../config_provider/config_types.dart';
@@ -10,21 +6,78 @@ import '../clang_bindings/clang_bindings.dart' as clang_types;
106
import '../utils.dart';
117

128
/// Parses a global variable
13-
Global? parseVarDeclaration(Context context, clang_types.CXCursor cursor) {
9+
Binding? parseVarDeclaration(Context context, clang_types.CXCursor cursor) {
1410
final logger = context.logger;
1511
final config = context.config;
1612
final nativeOutputStyle = config.output.style is NativeExternalBindings;
1713
final bindingsIndex = context.bindingsIndex;
1814
final name = cursor.spelling();
1915
final usr = cursor.usr();
16+
2017
if (bindingsIndex.isSeenGlobalVar(usr)) {
2118
return bindingsIndex.getSeenGlobalVar(usr);
2219
}
20+
if (bindingsIndex.isSeenVariableConstant(usr)) {
21+
return bindingsIndex.getSeenVariableConstant(usr);
22+
}
23+
2324
final decl = Declaration(usr: usr, originalName: name);
25+
final cType = cursor.type();
2426

25-
logger.fine('++++ Adding Global: ${cursor.completeStringRepr()}');
27+
// Try to evaluate as a constant first.
28+
if (cType.isConstQualified) {
29+
final evalResult = clang.clang_Cursor_Evaluate(cursor);
30+
final evalKind = clang.clang_EvalResult_getKind(evalResult);
31+
Constant? constant;
2632

27-
final cType = cursor.type();
33+
switch (evalKind) {
34+
case clang_types.CXEvalResultKind.CXEval_Int:
35+
final value = clang.clang_EvalResult_getAsLongLong(evalResult);
36+
constant = Constant(
37+
usr: usr,
38+
originalName: name,
39+
name: config.globals.rename(decl),
40+
dartDoc: getCursorDocComment(context, cursor),
41+
rawType: 'int',
42+
rawValue: value.toString(),
43+
);
44+
break;
45+
case clang_types.CXEvalResultKind.CXEval_Float:
46+
final value = clang.clang_EvalResult_getAsDouble(evalResult);
47+
constant = Constant(
48+
usr: usr,
49+
originalName: name,
50+
name: config.globals.rename(decl),
51+
dartDoc: getCursorDocComment(context, cursor),
52+
rawType: 'double',
53+
rawValue: writeDoubleAsString(value),
54+
);
55+
break;
56+
case clang_types.CXEvalResultKind.CXEval_StrLiteral:
57+
final value = clang.clang_EvalResult_getAsStr(evalResult);
58+
final rawValue = getWrittenStringRepresentation(name, value, context);
59+
constant = Constant(
60+
usr: usr,
61+
originalName: name,
62+
name: config.globals.rename(decl),
63+
dartDoc: getCursorDocComment(context, cursor),
64+
rawType: 'String',
65+
rawValue: "'$rawValue'",
66+
);
67+
break;
68+
}
69+
clang.clang_EvalResult_dispose(evalResult);
70+
71+
if (constant != null) {
72+
logger.fine(
73+
'++++ Adding Constant from Global: ${cursor.completeStringRepr()}',
74+
);
75+
bindingsIndex.addVariableConstantToSeen(usr, constant);
76+
return constant;
77+
}
78+
}
79+
80+
logger.fine('++++ Adding Global: ${cursor.completeStringRepr()}');
2881

2982
final type = cType.toCodeGenType(
3083
context,

pkgs/ffigen/lib/src/header_parser/utils.dart

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// BSD-style license that can be found in the LICENSE file.
44

55
import 'dart:ffi';
6+
import 'dart:typed_data';
67

78
import 'package:ffi/ffi.dart';
89
import 'package:logging/logging.dart';
@@ -11,6 +12,7 @@ import '../code_generator.dart';
1112
import '../config_provider/config_types.dart';
1213
import '../context.dart';
1314
import '../strings.dart';
15+
import '../strings.dart' as strings;
1416
import 'clang_bindings/clang_bindings.dart' as clang_types;
1517
import 'type_extractor/extractor.dart';
1618

@@ -492,6 +494,7 @@ class BindingsIndex {
492494
final Map<String, Constant> _unnamedEnumConstants = {};
493495
final Map<String, String> _macros = {};
494496
final Map<String, Global> _globals = {};
497+
final Map<String, Constant> _variableConstants = {};
495498
final Map<String, ObjCBlock> _objcBlocks = {};
496499
final Map<String, ObjCProtocol> _objcProtocols = {};
497500
final Map<String, ObjCCategory> _objcCategories = {};
@@ -512,6 +515,11 @@ class BindingsIndex {
512515
bool isSeenGlobalVar(String usr) => _globals.containsKey(usr);
513516
void addGlobalVarToSeen(String usr, Global global) => _globals[usr] = global;
514517
Global? getSeenGlobalVar(String usr) => _globals[usr];
518+
bool isSeenVariableConstant(String usr) =>
519+
_variableConstants.containsKey(usr);
520+
void addVariableConstantToSeen(String usr, Constant constant) =>
521+
_variableConstants[usr] = constant;
522+
Constant? getSeenVariableConstant(String usr) => _variableConstants[usr];
515523
bool isSeenMacro(String usr) => _macros.containsKey(usr);
516524
void addMacroToSeen(String usr, String macro) => _macros[usr] = macro;
517525
bool isSeenUnsupportedTypealias(String usr) =>
@@ -576,3 +584,108 @@ class CursorIndex {
576584
}
577585
}
578586
}
587+
588+
/// Converts a double to a string, handling cases like Infinity and NaN.
589+
String writeDoubleAsString(double d) {
590+
if (d.isFinite) {
591+
return d.toString();
592+
} else {
593+
// The only Non-Finite numbers are Infinity, NegativeInfinity and NaN.
594+
if (d.isInfinite) {
595+
return d.isNegative
596+
? strings.doubleNegativeInfinity
597+
: strings.doubleInfinity;
598+
}
599+
return strings.doubleNaN;
600+
}
601+
}
602+
603+
/// Gets a written representation string of a C string.
604+
///
605+
/// E.g- For a string "Hello\nWorld", The new line character is converted to \n.
606+
/// Note: The string is considered to be Utf8, but is treated as Extended ASCII,
607+
/// if the conversion fails.
608+
String getWrittenStringRepresentation(
609+
String varName,
610+
Pointer<Char> strPtr,
611+
Context context,
612+
) {
613+
final sb = StringBuffer();
614+
try {
615+
// Consider string to be Utf8 encoded by default.
616+
sb.clear();
617+
// This throws a Format Exception if string isn't Utf8 so that we handle it
618+
// in the catch block.
619+
final result = strPtr.cast<Utf8>().toDartString();
620+
for (final s in result.runes) {
621+
sb.write(_getWritableChar(s));
622+
}
623+
} catch (e) {
624+
// Handle string if it isn't Utf8. String is considered to be
625+
// Extended ASCII in this case.
626+
context.logger.warning(
627+
"Couldn't decode string value for '$varName' as Utf8, using "
628+
'ASCII instead.',
629+
);
630+
sb.clear();
631+
final length = strPtr.cast<Utf8>().length;
632+
final charList = Uint8List.view(
633+
strPtr.cast<Uint8>().asTypedList(length).buffer,
634+
0,
635+
length,
636+
);
637+
638+
for (final char in charList) {
639+
sb.write(_getWritableChar(char, utf8: false));
640+
}
641+
}
642+
643+
return sb.toString();
644+
}
645+
646+
/// Creates a writable char from [char] code.
647+
///
648+
/// E.g- `\` is converted to `\\`.
649+
String _getWritableChar(int char, {bool utf8 = true}) {
650+
/// Handle control characters.
651+
if (char >= 0 && char < 32 || char == 127) {
652+
/// Handle these - `\b \t \n \v \f \r` as special cases.
653+
switch (char) {
654+
case 8: // \b
655+
return r'\b';
656+
case 9: // \t
657+
return r'\t';
658+
case 10: // \n
659+
return r'\n';
660+
case 11: // \v
661+
return r'\v';
662+
case 12: // \f
663+
return r'\f';
664+
case 13: // \r
665+
return r'\r';
666+
default:
667+
final h = char.toRadixString(16).toUpperCase().padLeft(2, '0');
668+
return '\\x$h';
669+
}
670+
}
671+
672+
/// Handle characters - `$ ' \` these need to be escaped when writing to file.
673+
switch (char) {
674+
case 36: // $
675+
return r'\$';
676+
case 39: // '
677+
return r"\'";
678+
case 92: // \
679+
return r'\\';
680+
}
681+
682+
/// In case encoding is not Utf8, we know all characters will fall in [0..255]
683+
/// Print range [128..255] as `\xHH`.
684+
if (!utf8) {
685+
final h = char.toRadixString(16).toUpperCase().padLeft(2, '0');
686+
return '\\x$h';
687+
}
688+
689+
/// In all other cases, simply convert to string.
690+
return String.fromCharCode(char);
691+
}

pkgs/ffigen/lib/src/visitor/apply_config_filters.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,13 @@ class ApplyConfigFiltersVisitation extends Visitation {
9191
@override
9292
void visitGlobal(Global node) => _visitImpl(node, config.globals);
9393

94+
@override
95+
void visitConstant(Constant node) {
96+
// MacroConstant and UnnamedEnumConstant have their own overrides, so this
97+
// only applies to base Constants (e.g. from static const variables).
98+
_visitImpl(node, config.globals);
99+
}
100+
94101
@override
95102
void visitTypealias(Typealias node) => _visitImpl(node, config.typedefs);
96103
}

0 commit comments

Comments
 (0)