c-next 0.2.2 → 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +105 -22
- package/dist/index.js.map +3 -3
- package/package.json +1 -1
- package/src/transpiler/logic/symbols/c/utils/DeclaratorUtils.ts +99 -2
- package/src/transpiler/logic/symbols/c/utils/__tests__/DeclaratorUtils.test.ts +128 -0
- package/src/transpiler/output/codegen/__tests__/CodeGenerator.test.ts +49 -36
- package/src/transpiler/output/codegen/__tests__/RequireInclude.test.ts +4 -2
- package/src/transpiler/output/codegen/generators/expressions/PostfixExpressionGenerator.ts +23 -14
- package/src/transpiler/output/codegen/generators/expressions/__tests__/PostfixExpressionGenerator.test.ts +9 -6
- package/src/transpiler/output/codegen/generators/statements/SwitchGenerator.ts +10 -1
- package/src/transpiler/output/codegen/helpers/ArrayAccessHelper.ts +6 -3
- package/src/transpiler/output/codegen/helpers/FloatBitHelper.ts +36 -22
- package/src/transpiler/output/codegen/helpers/__tests__/ArrayAccessHelper.test.ts +8 -6
- package/src/transpiler/output/codegen/helpers/__tests__/FloatBitHelper.test.ts +34 -18
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* FloatBitHelper - Generates float bit write operations using
|
|
2
|
+
* FloatBitHelper - Generates float bit write operations using union-based type punning
|
|
3
3
|
*
|
|
4
4
|
* Issue #644: Extracted from CodeGenerator to reduce file size.
|
|
5
|
+
* Issue #857: Changed from memcpy to union for MISRA C:2012 Rule 21.15 compliance.
|
|
5
6
|
*
|
|
6
|
-
* Floats don't support direct bit access in C, so we use a
|
|
7
|
-
*
|
|
8
|
-
* 1. Declare
|
|
9
|
-
* 2.
|
|
10
|
-
* 3. Modify
|
|
11
|
-
* 4.
|
|
7
|
+
* Floats don't support direct bit access in C, so we use a union for type punning.
|
|
8
|
+
* The pattern:
|
|
9
|
+
* 1. Declare union variable if needed: union { float f; uint32_t u; } __bits_name;
|
|
10
|
+
* 2. Copy float to union: __bits_name.f = floatVar;
|
|
11
|
+
* 3. Modify bits via union member: __bits_name.u = ...
|
|
12
|
+
* 4. Copy back: floatVar = __bits_name.f;
|
|
13
|
+
*
|
|
14
|
+
* This approach is MISRA-compliant because union type punning is well-defined in C99+.
|
|
12
15
|
*
|
|
13
16
|
* Migrated to use CodeGenState instead of constructor DI.
|
|
14
17
|
*/
|
|
@@ -30,16 +33,25 @@ interface IFloatBitCallbacks {
|
|
|
30
33
|
}
|
|
31
34
|
|
|
32
35
|
/**
|
|
33
|
-
*
|
|
36
|
+
* Get the C float type name for a C-Next float type.
|
|
37
|
+
*/
|
|
38
|
+
const getFloatTypeName = (baseType: string): string => {
|
|
39
|
+
return baseType === "f64" ? "double" : "float";
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Generates float bit write operations using union-based type punning.
|
|
34
44
|
*
|
|
35
45
|
* For single bit: width is null, uses bitIndex only
|
|
36
46
|
* For bit range: width is provided, uses bitIndex as start position
|
|
37
47
|
*/
|
|
38
48
|
class FloatBitHelper {
|
|
39
49
|
/**
|
|
40
|
-
* Generate float bit write using
|
|
50
|
+
* Generate float bit write using union-based type punning.
|
|
41
51
|
* Returns null if typeInfo is not a float type.
|
|
42
52
|
*
|
|
53
|
+
* Uses union { float f; uint32_t u; } for MISRA 21.15 compliance instead of memcpy.
|
|
54
|
+
*
|
|
43
55
|
* @param name - Variable name being written
|
|
44
56
|
* @param typeInfo - Type information for the variable
|
|
45
57
|
* @param bitIndex - Bit index expression (start position)
|
|
@@ -62,11 +74,11 @@ class FloatBitHelper {
|
|
|
62
74
|
return null;
|
|
63
75
|
}
|
|
64
76
|
|
|
65
|
-
callbacks.requireInclude("string"); // For memcpy
|
|
66
77
|
callbacks.requireInclude("float_static_assert"); // For size verification
|
|
67
78
|
|
|
68
79
|
const isF64 = typeInfo.baseType === "f64";
|
|
69
|
-
const
|
|
80
|
+
const floatType = getFloatTypeName(typeInfo.baseType);
|
|
81
|
+
const intType = isF64 ? "uint64_t" : "uint32_t";
|
|
70
82
|
const shadowName = `__bits_${name}`;
|
|
71
83
|
const maskSuffix = isF64 ? "ULL" : "U";
|
|
72
84
|
|
|
@@ -76,13 +88,15 @@ class FloatBitHelper {
|
|
|
76
88
|
CodeGenState.floatBitShadows.add(shadowName);
|
|
77
89
|
}
|
|
78
90
|
|
|
79
|
-
// Check if shadow already has current value (skip redundant
|
|
91
|
+
// Check if shadow already has current value (skip redundant read)
|
|
80
92
|
const shadowIsCurrent = CodeGenState.floatShadowCurrent.has(shadowName);
|
|
81
93
|
|
|
82
|
-
|
|
83
|
-
const
|
|
84
|
-
?
|
|
85
|
-
:
|
|
94
|
+
// Union declaration: union { float f; uint32_t u; } __bits_name;
|
|
95
|
+
const decl = needsDeclaration
|
|
96
|
+
? `union { ${floatType} f; ${intType} u; } ${shadowName};\n`
|
|
97
|
+
: "";
|
|
98
|
+
// Read from float into union: __bits_name.f = floatVar;
|
|
99
|
+
const readUnion = shadowIsCurrent ? "" : `${shadowName}.f = ${name};\n`;
|
|
86
100
|
|
|
87
101
|
// Mark shadow as current after this write
|
|
88
102
|
CodeGenState.floatShadowCurrent.add(shadowName);
|
|
@@ -90,17 +104,17 @@ class FloatBitHelper {
|
|
|
90
104
|
if (width === null) {
|
|
91
105
|
// Single bit assignment: floatVar[3] <- true
|
|
92
106
|
return (
|
|
93
|
-
`${decl}${
|
|
94
|
-
`${shadowName} = (${shadowName} & ~(1${maskSuffix} << ${bitIndex})) | ((${
|
|
95
|
-
|
|
107
|
+
`${decl}${readUnion}` +
|
|
108
|
+
`${shadowName}.u = (${shadowName}.u & ~(1${maskSuffix} << ${bitIndex})) | ((${intType})${callbacks.foldBooleanToInt(value)} << ${bitIndex});\n` +
|
|
109
|
+
`${name} = ${shadowName}.f;`
|
|
96
110
|
);
|
|
97
111
|
} else {
|
|
98
112
|
// Bit range assignment: floatVar[0, 8] <- b0
|
|
99
113
|
const mask = callbacks.generateBitMask(width, isF64);
|
|
100
114
|
return (
|
|
101
|
-
`${decl}${
|
|
102
|
-
`${shadowName} = (${shadowName} & ~(${mask} << ${bitIndex})) | (((${
|
|
103
|
-
|
|
115
|
+
`${decl}${readUnion}` +
|
|
116
|
+
`${shadowName}.u = (${shadowName}.u & ~(${mask} << ${bitIndex})) | (((${intType})${value} & ${mask}) << ${bitIndex});\n` +
|
|
117
|
+
`${name} = ${shadowName}.f;`
|
|
104
118
|
);
|
|
105
119
|
}
|
|
106
120
|
}
|
|
@@ -243,7 +243,7 @@ describe("ArrayAccessHelper", () => {
|
|
|
243
243
|
expect(mockDeps.generateBitMask).toHaveBeenCalledWith("8");
|
|
244
244
|
});
|
|
245
245
|
|
|
246
|
-
it("should route to float bit range for f32", () => {
|
|
246
|
+
it("should route to float bit range for f32 (no string.h needed)", () => {
|
|
247
247
|
const info: IArrayAccessInfo = {
|
|
248
248
|
rawName: "fval",
|
|
249
249
|
resolvedName: "fval",
|
|
@@ -260,8 +260,9 @@ describe("ArrayAccessHelper", () => {
|
|
|
260
260
|
};
|
|
261
261
|
|
|
262
262
|
const result = ArrayAccessHelper.generateBitRange(info, mockDeps);
|
|
263
|
-
expect(result).toContain("
|
|
264
|
-
|
|
263
|
+
expect(result).toContain("__bits_fval");
|
|
264
|
+
// No string.h - uses union-based type punning (MISRA 21.15 compliant)
|
|
265
|
+
expect(mockDeps.requireInclude).not.toHaveBeenCalledWith("string");
|
|
265
266
|
expect(mockDeps.requireInclude).toHaveBeenCalledWith(
|
|
266
267
|
"float_static_assert",
|
|
267
268
|
);
|
|
@@ -356,9 +357,10 @@ describe("ArrayAccessHelper", () => {
|
|
|
356
357
|
|
|
357
358
|
const result = ArrayAccessHelper.generateFloatBitRange(info, mockDeps);
|
|
358
359
|
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
expect(
|
|
360
|
+
// Note: BitRangeHelper.buildFloatBitReadExpr still uses memcpy internally,
|
|
361
|
+
// but ArrayAccessHelper no longer requires string.h (MISRA 21.15 compliant)
|
|
362
|
+
expect(result).toContain("__bits_fval");
|
|
363
|
+
expect(mockDeps.requireInclude).not.toHaveBeenCalledWith("string");
|
|
362
364
|
expect(mockDeps.requireInclude).toHaveBeenCalledWith(
|
|
363
365
|
"float_static_assert",
|
|
364
366
|
);
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* Unit tests for FloatBitHelper
|
|
3
3
|
*
|
|
4
4
|
* Issue #644: Tests for the extracted float bit write helper.
|
|
5
|
+
* Issue #857: Updated for union-based type punning (MISRA 21.15 compliance).
|
|
5
6
|
* Migrated to use CodeGenState instead of constructor DI.
|
|
6
7
|
*/
|
|
7
8
|
|
|
@@ -57,7 +58,7 @@ describe("FloatBitHelper", () => {
|
|
|
57
58
|
expect(callbacks.requireInclude).not.toHaveBeenCalled();
|
|
58
59
|
});
|
|
59
60
|
|
|
60
|
-
it("generates single bit write for f32", () => {
|
|
61
|
+
it("generates single bit write for f32 using union", () => {
|
|
61
62
|
const typeInfo: TTypeInfo = {
|
|
62
63
|
baseType: "f32",
|
|
63
64
|
bitWidth: 32,
|
|
@@ -74,17 +75,27 @@ describe("FloatBitHelper", () => {
|
|
|
74
75
|
callbacks,
|
|
75
76
|
);
|
|
76
77
|
|
|
77
|
-
|
|
78
|
-
expect(result).toContain(
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
78
|
+
// Union declaration
|
|
79
|
+
expect(result).toContain(
|
|
80
|
+
"union { float f; uint32_t u; } __bits_myFloat;",
|
|
81
|
+
);
|
|
82
|
+
// Read via union
|
|
83
|
+
expect(result).toContain("__bits_myFloat.f = myFloat;");
|
|
84
|
+
// Bit manipulation via .u
|
|
85
|
+
expect(result).toContain(
|
|
86
|
+
"__bits_myFloat.u = (__bits_myFloat.u & ~(1U << 3))",
|
|
87
|
+
);
|
|
88
|
+
// Write back via union
|
|
89
|
+
expect(result).toContain("myFloat = __bits_myFloat.f;");
|
|
90
|
+
// No memcpy for MISRA compliance
|
|
91
|
+
expect(result).not.toContain("memcpy");
|
|
92
|
+
expect(callbacks.requireInclude).not.toHaveBeenCalledWith("string");
|
|
82
93
|
expect(callbacks.requireInclude).toHaveBeenCalledWith(
|
|
83
94
|
"float_static_assert",
|
|
84
95
|
);
|
|
85
96
|
});
|
|
86
97
|
|
|
87
|
-
it("generates single bit write for f64", () => {
|
|
98
|
+
it("generates single bit write for f64 using union", () => {
|
|
88
99
|
const typeInfo: TTypeInfo = {
|
|
89
100
|
baseType: "f64",
|
|
90
101
|
bitWidth: 64,
|
|
@@ -101,11 +112,13 @@ describe("FloatBitHelper", () => {
|
|
|
101
112
|
callbacks,
|
|
102
113
|
);
|
|
103
114
|
|
|
104
|
-
expect(result).toContain(
|
|
115
|
+
expect(result).toContain(
|
|
116
|
+
"union { double f; uint64_t u; } __bits_myDouble;",
|
|
117
|
+
);
|
|
105
118
|
expect(result).toContain("1ULL << 5");
|
|
106
119
|
});
|
|
107
120
|
|
|
108
|
-
it("generates bit range write for f32", () => {
|
|
121
|
+
it("generates bit range write for f32 using union", () => {
|
|
109
122
|
const typeInfo: TTypeInfo = {
|
|
110
123
|
baseType: "f32",
|
|
111
124
|
bitWidth: 32,
|
|
@@ -122,11 +135,13 @@ describe("FloatBitHelper", () => {
|
|
|
122
135
|
callbacks,
|
|
123
136
|
);
|
|
124
137
|
|
|
125
|
-
expect(result).toContain(
|
|
138
|
+
expect(result).toContain(
|
|
139
|
+
"union { float f; uint32_t u; } __bits_myFloat;",
|
|
140
|
+
);
|
|
126
141
|
expect(callbacks.generateBitMask).toHaveBeenCalledWith("8", false);
|
|
127
142
|
});
|
|
128
143
|
|
|
129
|
-
it("skips declaration when shadow already exists", () => {
|
|
144
|
+
it("skips union declaration when shadow already exists", () => {
|
|
130
145
|
const typeInfo: TTypeInfo = {
|
|
131
146
|
baseType: "f32",
|
|
132
147
|
bitWidth: 32,
|
|
@@ -146,11 +161,11 @@ describe("FloatBitHelper", () => {
|
|
|
146
161
|
callbacks,
|
|
147
162
|
);
|
|
148
163
|
|
|
149
|
-
expect(result).not.toContain("uint32_t
|
|
150
|
-
expect(result).toContain("
|
|
164
|
+
expect(result).not.toContain("union { float f; uint32_t u; }");
|
|
165
|
+
expect(result).toContain("__bits_myFloat.f = myFloat;");
|
|
151
166
|
});
|
|
152
167
|
|
|
153
|
-
it("skips redundant
|
|
168
|
+
it("skips redundant union read when shadow is current", () => {
|
|
154
169
|
const typeInfo: TTypeInfo = {
|
|
155
170
|
baseType: "f32",
|
|
156
171
|
bitWidth: 32,
|
|
@@ -171,10 +186,11 @@ describe("FloatBitHelper", () => {
|
|
|
171
186
|
callbacks,
|
|
172
187
|
);
|
|
173
188
|
|
|
174
|
-
expect(result).not.toContain("uint32_t
|
|
175
|
-
// Should not have read
|
|
176
|
-
|
|
177
|
-
|
|
189
|
+
expect(result).not.toContain("union { float f; uint32_t u; }");
|
|
190
|
+
// Should not have the read assignment, only the write-back
|
|
191
|
+
expect(result).not.toContain("__bits_myFloat.f = myFloat;");
|
|
192
|
+
// Should still have the write-back
|
|
193
|
+
expect(result).toContain("myFloat = __bits_myFloat.f;");
|
|
178
194
|
});
|
|
179
195
|
|
|
180
196
|
it("marks shadow as current after write", () => {
|