c-next 0.2.4 → 0.2.6
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 +561 -78
- package/dist/index.js.map +3 -3
- package/package.json +3 -1
- package/src/transpiler/Transpiler.ts +1 -1
- package/src/transpiler/logic/analysis/FunctionCallAnalyzer.ts +194 -8
- package/src/transpiler/logic/analysis/__tests__/FunctionCallAnalyzer.test.ts +140 -0
- package/src/transpiler/logic/symbols/c/__tests__/CResolver.integration.test.ts +41 -0
- package/src/transpiler/logic/symbols/c/collectors/FunctionCollector.ts +11 -5
- package/src/transpiler/output/codegen/CodeGenerator.ts +195 -17
- package/src/transpiler/output/codegen/TypeResolver.ts +1 -1
- package/src/transpiler/output/codegen/__tests__/CodeGenerator.coverage.test.ts +129 -0
- package/src/transpiler/output/codegen/__tests__/CodeGenerator.test.ts +30 -5
- package/src/transpiler/output/codegen/assignment/handlers/AccessPatternHandlers.ts +2 -2
- package/src/transpiler/output/codegen/assignment/handlers/BitAccessHandlers.ts +2 -2
- package/src/transpiler/output/codegen/assignment/handlers/BitmapHandlers.ts +2 -2
- package/src/transpiler/output/codegen/assignment/handlers/RegisterHandlers.ts +4 -4
- package/src/transpiler/output/codegen/assignment/handlers/__tests__/AccessPatternHandlers.test.ts +4 -4
- package/src/transpiler/output/codegen/assignment/handlers/__tests__/BitAccessHandlers.test.ts +8 -8
- package/src/transpiler/output/codegen/assignment/handlers/__tests__/BitmapHandlers.test.ts +5 -5
- package/src/transpiler/output/codegen/assignment/handlers/__tests__/RegisterHandlers.test.ts +4 -4
- package/src/transpiler/output/codegen/generators/expressions/CallExprGenerator.ts +8 -1
- package/src/transpiler/output/codegen/generators/expressions/PostfixExpressionGenerator.ts +15 -3
- package/src/transpiler/output/codegen/helpers/ArgumentGenerator.ts +5 -0
- package/src/transpiler/output/codegen/helpers/FunctionContextManager.ts +63 -10
- package/src/transpiler/output/codegen/helpers/MemberSeparatorResolver.ts +28 -6
- package/src/transpiler/output/codegen/helpers/ParameterDereferenceResolver.ts +12 -0
- package/src/transpiler/output/codegen/helpers/ParameterInputAdapter.ts +30 -2
- package/src/transpiler/output/codegen/helpers/ParameterSignatureBuilder.ts +15 -7
- package/src/transpiler/output/codegen/helpers/StringDeclHelper.ts +8 -1
- package/src/transpiler/output/codegen/helpers/StringOperationsHelper.ts +1 -1
- package/src/transpiler/output/codegen/helpers/TypedefParamParser.ts +220 -0
- package/src/transpiler/output/codegen/helpers/VariableDeclHelper.ts +11 -0
- package/src/transpiler/output/codegen/helpers/VariableModifierBuilder.ts +16 -1
- package/src/transpiler/output/codegen/helpers/__tests__/FunctionContextManager.test.ts +5 -5
- package/src/transpiler/output/codegen/helpers/__tests__/MemberSeparatorResolver.test.ts +48 -36
- package/src/transpiler/output/codegen/helpers/__tests__/ParameterInputAdapter.test.ts +37 -0
- package/src/transpiler/output/codegen/helpers/__tests__/ParameterSignatureBuilder.test.ts +63 -0
- package/src/transpiler/output/codegen/helpers/__tests__/TypedefParamParser.test.ts +209 -0
- package/src/transpiler/output/codegen/helpers/__tests__/VariableModifierBuilder.test.ts +34 -2
- package/src/transpiler/output/codegen/resolution/EnumTypeResolver.ts +1 -1
- package/src/transpiler/output/codegen/resolution/SizeofResolver.ts +1 -1
- package/src/transpiler/output/codegen/types/IParameterInput.ts +13 -0
- package/src/transpiler/output/codegen/types/ISeparatorContext.ts +7 -0
- package/src/transpiler/output/codegen/types/TParameterInfo.ts +12 -0
- package/src/transpiler/output/codegen/types/TTypeInfo.ts +1 -0
- package/src/transpiler/output/codegen/utils/CodegenParserUtils.ts +1 -1
- package/src/transpiler/state/CodeGenState.ts +21 -2
- package/src/utils/BitUtils.ts +17 -13
- package/src/{transpiler/output/codegen/utils → utils}/ExpressionUnwrapper.ts +1 -1
- package/src/utils/__tests__/BitUtils.test.ts +56 -56
- package/src/{transpiler/output/codegen/utils → utils}/__tests__/ExpressionUnwrapper.test.ts +2 -2
package/dist/index.js
CHANGED
|
@@ -10,7 +10,7 @@ import { hideBin } from "yargs/helpers";
|
|
|
10
10
|
// package.json
|
|
11
11
|
var package_default = {
|
|
12
12
|
name: "c-next",
|
|
13
|
-
version: "0.2.
|
|
13
|
+
version: "0.2.6",
|
|
14
14
|
description: "A safer C for embedded systems development. Transpiles to clean, readable C.",
|
|
15
15
|
packageManager: "npm@11.9.0",
|
|
16
16
|
type: "module",
|
|
@@ -32,6 +32,8 @@ var package_default = {
|
|
|
32
32
|
"test:cli": "node scripts/test-cli.js",
|
|
33
33
|
"test:q": "tsx scripts/test.ts -q",
|
|
34
34
|
"test:update": "tsx scripts/test.ts --update",
|
|
35
|
+
"integration:transpile": "tsx scripts/test.ts --transpile-only",
|
|
36
|
+
"integration:execute": "tsx scripts/test.ts --execute-only",
|
|
35
37
|
analyze: "./scripts/static-analysis.sh",
|
|
36
38
|
clean: "rm -rf dist src/transpiler/logic/parser/grammar src/transpiler/logic/parser/c/grammar src/transpiler/logic/parser/cpp/grammar",
|
|
37
39
|
"prettier:check": "prettier --check .",
|
|
@@ -110764,8 +110766,12 @@ var CodeGenState = class {
|
|
|
110764
110766
|
static callbackTypes = /* @__PURE__ */ new Map();
|
|
110765
110767
|
/** Callback field types: "Struct.field" -> callbackTypeName */
|
|
110766
110768
|
static callbackFieldTypes = /* @__PURE__ */ new Map();
|
|
110767
|
-
/**
|
|
110768
|
-
|
|
110769
|
+
/**
|
|
110770
|
+
* Functions that are assigned to C callback typedefs.
|
|
110771
|
+
* Maps function name -> typedef name (e.g., "my_flush" -> "flush_cb_t")
|
|
110772
|
+
* Issue #895: We need the typedef name to look up parameter types.
|
|
110773
|
+
*/
|
|
110774
|
+
static callbackCompatibleFunctions = /* @__PURE__ */ new Map();
|
|
110769
110775
|
// ===========================================================================
|
|
110770
110776
|
// PASS-BY-VALUE ANALYSIS (Issue #269)
|
|
110771
110777
|
// ===========================================================================
|
|
@@ -111133,6 +111139,20 @@ var CodeGenState = class {
|
|
|
111133
111139
|
static getCallbackType(name) {
|
|
111134
111140
|
return this.callbackTypes.get(name);
|
|
111135
111141
|
}
|
|
111142
|
+
/**
|
|
111143
|
+
* Issue #895: Get the typedef type string for a C typedef by name.
|
|
111144
|
+
* Used to look up function pointer typedef signatures for callback-compatible functions.
|
|
111145
|
+
*
|
|
111146
|
+
* @param typedefName - Name of the typedef (e.g., "flush_cb_t")
|
|
111147
|
+
* @returns The type string (e.g., "void (*)(widget_t *, const rect_t *, uint8_t *)") or undefined
|
|
111148
|
+
*/
|
|
111149
|
+
static getTypedefType(typedefName) {
|
|
111150
|
+
const symbol = this.symbolTable.getCSymbol(typedefName);
|
|
111151
|
+
if (symbol?.kind === "type") {
|
|
111152
|
+
return symbol.type;
|
|
111153
|
+
}
|
|
111154
|
+
return void 0;
|
|
111155
|
+
}
|
|
111136
111156
|
/**
|
|
111137
111157
|
* Check if a type name is a known C-Next function.
|
|
111138
111158
|
*/
|
|
@@ -112906,7 +112926,7 @@ var TYPE_RANGES = {
|
|
|
112906
112926
|
};
|
|
112907
112927
|
var TYPE_RANGES_default = TYPE_RANGES;
|
|
112908
112928
|
|
|
112909
|
-
// src/
|
|
112929
|
+
// src/utils/ExpressionUnwrapper.ts
|
|
112910
112930
|
var ExpressionUnwrapper = class {
|
|
112911
112931
|
/**
|
|
112912
112932
|
* Navigate from ExpressionContext to ShiftExpressionContext.
|
|
@@ -115145,13 +115165,17 @@ var _generateCFunctionArg = (e, targetParam, input, orchestrator) => {
|
|
|
115145
115165
|
return wrapWithCppEnumCast(argCode, e, targetParam?.baseType, orchestrator);
|
|
115146
115166
|
}
|
|
115147
115167
|
let argType = orchestrator.getExpressionType(e);
|
|
115168
|
+
let isPointerVariable = false;
|
|
115169
|
+
const typeInfo = CodeGenState.getVariableTypeInfo(argCode);
|
|
115148
115170
|
if (!argType && !argCode.startsWith("&")) {
|
|
115149
|
-
const typeInfo = CodeGenState.getVariableTypeInfo(argCode);
|
|
115150
115171
|
if (typeInfo) {
|
|
115151
115172
|
argType = typeInfo.baseType;
|
|
115152
115173
|
}
|
|
115153
115174
|
}
|
|
115154
|
-
|
|
115175
|
+
if (typeInfo?.isPointer) {
|
|
115176
|
+
isPointerVariable = true;
|
|
115177
|
+
}
|
|
115178
|
+
const needsAddressOf = argType && !argType.endsWith("*") && !argCode.startsWith("&") && !targetParam.isArray && !isPointerVariable && (orchestrator.isStructType(argType) || _parameterExpectsAddressOf(targetParam.baseType, argType, orchestrator));
|
|
115155
115179
|
if (needsAddressOf) {
|
|
115156
115180
|
argCode = `&${argCode}`;
|
|
115157
115181
|
}
|
|
@@ -115744,6 +115768,7 @@ var generatePostfixExpression = (ctx, input, state, orchestrator) => {
|
|
|
115744
115768
|
const rootIdentifier = primary.IDENTIFIER()?.getText();
|
|
115745
115769
|
const paramInfo = rootIdentifier ? state.currentParameters.get(rootIdentifier) : null;
|
|
115746
115770
|
const isStructParam = paramInfo?.isStruct ?? false;
|
|
115771
|
+
const forcePointerSemantics = paramInfo?.forcePointerSemantics ?? false;
|
|
115747
115772
|
const hasSubscriptOps = ops.some((op) => op.expression().length > 0);
|
|
115748
115773
|
const isNonArrayParamWithSubscript = paramInfo && !paramInfo.isArray && !paramInfo.isStruct && hasSubscriptOps;
|
|
115749
115774
|
let result;
|
|
@@ -115764,6 +115789,7 @@ var generatePostfixExpression = (ctx, input, state, orchestrator) => {
|
|
|
115764
115789
|
const postfixCtx = {
|
|
115765
115790
|
rootIdentifier,
|
|
115766
115791
|
isStructParam,
|
|
115792
|
+
forcePointerSemantics,
|
|
115767
115793
|
input,
|
|
115768
115794
|
state,
|
|
115769
115795
|
orchestrator,
|
|
@@ -115855,6 +115881,7 @@ var handleMemberOp = (memberName, tracking, ctx) => {
|
|
|
115855
115881
|
memberName,
|
|
115856
115882
|
rootIdentifier: ctx.rootIdentifier,
|
|
115857
115883
|
isStructParam: ctx.isStructParam,
|
|
115884
|
+
forcePointerSemantics: ctx.forcePointerSemantics,
|
|
115858
115885
|
isGlobalAccess: tracking.isGlobalAccess,
|
|
115859
115886
|
isCppAccessChain: tracking.isCppAccessChain,
|
|
115860
115887
|
currentStructType: tracking.currentStructType,
|
|
@@ -116589,7 +116616,7 @@ var tryStructParamAccess = (ctx, orchestrator) => {
|
|
|
116589
116616
|
if (!ctx.isStructParam || ctx.result !== ctx.rootIdentifier) {
|
|
116590
116617
|
return null;
|
|
116591
116618
|
}
|
|
116592
|
-
const structParamSep = memberAccessChain_default.getStructParamSeparator({
|
|
116619
|
+
const structParamSep = ctx.forcePointerSemantics ? "->" : memberAccessChain_default.getStructParamSeparator({
|
|
116593
116620
|
cppMode: orchestrator.isCppMode()
|
|
116594
116621
|
});
|
|
116595
116622
|
const output = initializeMemberOutput(ctx);
|
|
@@ -116838,15 +116865,18 @@ var VariableModifierBuilder = class {
|
|
|
116838
116865
|
*
|
|
116839
116866
|
* @param ctx - Parser context with modifier methods
|
|
116840
116867
|
* @param inFunctionBody - Whether we're inside a function body (affects extern)
|
|
116868
|
+
* @param hasInitializer - Whether the variable has an initializer (affects extern in C mode)
|
|
116869
|
+
* @param cppMode - Whether we're generating C++ code (affects extern behavior)
|
|
116841
116870
|
* @returns Modifier strings ready for use in generated code
|
|
116842
116871
|
* @throws Error if both atomic and volatile are specified
|
|
116843
116872
|
*/
|
|
116844
|
-
static build(ctx, inFunctionBody) {
|
|
116873
|
+
static build(ctx, inFunctionBody, hasInitializer = false, cppMode = false) {
|
|
116845
116874
|
const hasConst = ctx.constModifier?.() ?? false;
|
|
116846
116875
|
const constMod = hasConst ? "const " : "";
|
|
116847
116876
|
const atomicMod = ctx.atomicModifier() ? "volatile " : "";
|
|
116848
116877
|
const volatileMod = ctx.volatileModifier() ? "volatile " : "";
|
|
116849
|
-
const
|
|
116878
|
+
const needsExtern = hasConst && !inFunctionBody && (cppMode || !hasInitializer);
|
|
116879
|
+
const externMod = needsExtern ? "extern " : "";
|
|
116850
116880
|
if (ctx.atomicModifier() && ctx.volatileModifier()) {
|
|
116851
116881
|
const line = ctx.start?.line ?? 0;
|
|
116852
116882
|
throw new Error(
|
|
@@ -118034,16 +118064,17 @@ var ScopeGenerator_default = generateScope;
|
|
|
118034
118064
|
// src/utils/BitUtils.ts
|
|
118035
118065
|
var BitUtils = class _BitUtils {
|
|
118036
118066
|
/**
|
|
118037
|
-
* Convert a boolean expression to an integer (
|
|
118067
|
+
* Convert a boolean expression to an unsigned integer (0U or 1U).
|
|
118038
118068
|
* Handles literal "true"/"false" and generates ternary for expressions.
|
|
118069
|
+
* Uses unsigned literals for MISRA C:2012 Rule 10.1 compliance.
|
|
118039
118070
|
*
|
|
118040
118071
|
* @param expr - The expression to convert
|
|
118041
|
-
* @returns C code string representing the integer value
|
|
118072
|
+
* @returns C code string representing the unsigned integer value
|
|
118042
118073
|
*/
|
|
118043
118074
|
static boolToInt(expr) {
|
|
118044
|
-
if (expr === "true") return "
|
|
118045
|
-
if (expr === "false") return "
|
|
118046
|
-
return `(${expr} ?
|
|
118075
|
+
if (expr === "true") return "1U";
|
|
118076
|
+
if (expr === "false") return "0U";
|
|
118077
|
+
return `(${expr} ? 1U : 0U)`;
|
|
118047
118078
|
}
|
|
118048
118079
|
/**
|
|
118049
118080
|
* Generate a bit mask for the given width.
|
|
@@ -118100,24 +118131,26 @@ var BitUtils = class _BitUtils {
|
|
|
118100
118131
|
}
|
|
118101
118132
|
}
|
|
118102
118133
|
/**
|
|
118103
|
-
* Return the appropriate "1" literal for a given type.
|
|
118104
|
-
* Uses "1ULL" for 64-bit types
|
|
118134
|
+
* Return the appropriate unsigned "1" literal for a given type.
|
|
118135
|
+
* Uses "1ULL" for 64-bit types, "1U" for others.
|
|
118136
|
+
* MISRA C:2012 Rule 10.1 requires unsigned operands for bitwise operations.
|
|
118105
118137
|
*
|
|
118106
118138
|
* @param typeName - The C-Next type name (e.g., "u64", "i32")
|
|
118107
|
-
* @returns "1ULL" for 64-bit types, "
|
|
118139
|
+
* @returns "1ULL" for 64-bit types, "1U" otherwise
|
|
118108
118140
|
*/
|
|
118109
118141
|
static oneForType(typeName) {
|
|
118110
|
-
return typeName === "u64" || typeName === "i64" ? "1ULL" : "
|
|
118142
|
+
return typeName === "u64" || typeName === "i64" ? "1ULL" : "1U";
|
|
118111
118143
|
}
|
|
118112
118144
|
/**
|
|
118113
|
-
* Format a number as an uppercase hex string (e.g., 255 -> "
|
|
118145
|
+
* Format a number as an unsigned uppercase hex string (e.g., 255 -> "0xFFU").
|
|
118114
118146
|
* Used for generating hex mask literals in generated C code.
|
|
118147
|
+
* Includes U suffix for MISRA C:2012 Rule 10.1 compliance.
|
|
118115
118148
|
*
|
|
118116
118149
|
* @param value - The numeric value to format
|
|
118117
|
-
* @returns Hex string like "
|
|
118150
|
+
* @returns Hex string like "0xFFU" or "0x1FU"
|
|
118118
118151
|
*/
|
|
118119
118152
|
static formatHex(value) {
|
|
118120
|
-
return `0x${value.toString(16).toUpperCase()}`;
|
|
118153
|
+
return `0x${value.toString(16).toUpperCase()}U`;
|
|
118121
118154
|
}
|
|
118122
118155
|
/**
|
|
118123
118156
|
* Generate code to read a single bit from a value.
|
|
@@ -118163,7 +118196,7 @@ var BitUtils = class _BitUtils {
|
|
|
118163
118196
|
static singleBitWrite(target, offset, value, targetType) {
|
|
118164
118197
|
const intValue = _BitUtils.boolToInt(value);
|
|
118165
118198
|
const is64Bit = targetType === "u64" || targetType === "i64";
|
|
118166
|
-
const one = is64Bit ? "1ULL" : "
|
|
118199
|
+
const one = is64Bit ? "1ULL" : "1U";
|
|
118167
118200
|
const valueShift = is64Bit ? `((uint64_t)${intValue} << ${offset})` : `(${intValue} << ${offset})`;
|
|
118168
118201
|
return `${target} = (${target} & ~(${one} << ${offset})) | ${valueShift};`;
|
|
118169
118202
|
}
|
|
@@ -120847,7 +120880,7 @@ function getBitmapFieldInfo(bitmapType, fieldName, ctx) {
|
|
|
120847
120880
|
function generateBitmapWrite(target, fieldInfo, value) {
|
|
120848
120881
|
const { maskHex } = calculateMask(fieldInfo.width);
|
|
120849
120882
|
if (fieldInfo.width === 1) {
|
|
120850
|
-
return `${target} = (${target} & ~(
|
|
120883
|
+
return `${target} = (${target} & ~(1U << ${fieldInfo.offset})) | (${BitUtils_default.boolToInt(value)} << ${fieldInfo.offset});`;
|
|
120851
120884
|
} else {
|
|
120852
120885
|
return `${target} = (${target} & ~(${maskHex} << ${fieldInfo.offset})) | ((${value} & ${maskHex}) << ${fieldInfo.offset});`;
|
|
120853
120886
|
}
|
|
@@ -121100,9 +121133,9 @@ function handleRegisterBit(ctx) {
|
|
|
121100
121133
|
bitIndex,
|
|
121101
121134
|
true
|
|
121102
121135
|
);
|
|
121103
|
-
return `${fullName} = (
|
|
121136
|
+
return `${fullName} = (1U << ${bitIndex});`;
|
|
121104
121137
|
}
|
|
121105
|
-
return `${fullName} = (${fullName} & ~(
|
|
121138
|
+
return `${fullName} = (${fullName} & ~(1U << ${bitIndex})) | (${BitUtils_default.boolToInt(ctx.generatedValue)} << ${bitIndex});`;
|
|
121106
121139
|
}
|
|
121107
121140
|
function handleRegisterBitRange(ctx) {
|
|
121108
121141
|
AssignmentHandlerUtils_default.validateNoCompoundForBitAccess(
|
|
@@ -121168,9 +121201,9 @@ function handleScopedRegisterBit(ctx) {
|
|
|
121168
121201
|
bitIndex,
|
|
121169
121202
|
true
|
|
121170
121203
|
);
|
|
121171
|
-
return `${regName} = (
|
|
121204
|
+
return `${regName} = (1U << ${bitIndex});`;
|
|
121172
121205
|
}
|
|
121173
|
-
return `${regName} = (${regName} & ~(
|
|
121206
|
+
return `${regName} = (${regName} & ~(1U << ${bitIndex})) | (${BitUtils_default.boolToInt(ctx.generatedValue)} << ${bitIndex});`;
|
|
121174
121207
|
}
|
|
121175
121208
|
function handleScopedRegisterBitRange(ctx) {
|
|
121176
121209
|
AssignmentHandlerUtils_default.validateScopeContext(CodeGenState.currentScope);
|
|
@@ -121416,7 +121449,7 @@ function handleStructMemberBit(ctx) {
|
|
|
121416
121449
|
validateNotCompound2(ctx);
|
|
121417
121450
|
const target = gen6().generateAssignmentTarget(ctx.targetCtx);
|
|
121418
121451
|
const bitIndex = gen6().generateExpression(ctx.subscripts.at(-1));
|
|
121419
|
-
const one = "
|
|
121452
|
+
const one = "1U";
|
|
121420
121453
|
const intValue = BitUtils_default.boolToInt(ctx.generatedValue);
|
|
121421
121454
|
return `${target} = (${target} & ~(${one} << ${bitIndex})) | (${intValue} << ${bitIndex});`;
|
|
121422
121455
|
}
|
|
@@ -121664,9 +121697,9 @@ function handleGlobalRegisterBit(ctx) {
|
|
|
121664
121697
|
`Cannot assign false to write-only register bit ${regName}[${bitIndex}]. Use the corresponding CLEAR register to clear bits.`
|
|
121665
121698
|
);
|
|
121666
121699
|
}
|
|
121667
|
-
return `${regName} = (
|
|
121700
|
+
return `${regName} = (1U << ${bitIndex});`;
|
|
121668
121701
|
}
|
|
121669
|
-
return `${regName} = (${regName} & ~(
|
|
121702
|
+
return `${regName} = (${regName} & ~(1U << ${bitIndex})) | (${BitUtils_default.boolToInt(ctx.generatedValue)} << ${bitIndex});`;
|
|
121670
121703
|
}
|
|
121671
121704
|
function handleMemberChain(ctx) {
|
|
121672
121705
|
const bitAnalysis = gen9().analyzeMemberChainForBitAccess(ctx.targetCtx);
|
|
@@ -122816,6 +122849,9 @@ var ArgumentGenerator = class _ArgumentGenerator {
|
|
|
122816
122849
|
if (typeInfo?.isArray && !typeInfo.isString) {
|
|
122817
122850
|
return id;
|
|
122818
122851
|
}
|
|
122852
|
+
if (typeInfo?.isPointer) {
|
|
122853
|
+
return id;
|
|
122854
|
+
}
|
|
122819
122855
|
if (CodeGenState.currentScope) {
|
|
122820
122856
|
const members = CodeGenState.getScopeMembers(CodeGenState.currentScope);
|
|
122821
122857
|
if (members?.has(id)) {
|
|
@@ -124082,7 +124118,11 @@ var StringDeclHelper = class _StringDeclHelper {
|
|
|
124082
124118
|
initValue,
|
|
124083
124119
|
arrayDims
|
|
124084
124120
|
);
|
|
124085
|
-
|
|
124121
|
+
const suppression = "// cppcheck-suppress misra-c2012-9.3\n// cppcheck-suppress misra-c2012-9.4\n";
|
|
124122
|
+
return {
|
|
124123
|
+
code: `${suppression}${decl} = ${finalInitValue};`,
|
|
124124
|
+
handled: true
|
|
124125
|
+
};
|
|
124086
124126
|
}
|
|
124087
124127
|
/**
|
|
124088
124128
|
* Handle size inference for empty array dimension.
|
|
@@ -124558,15 +124598,21 @@ ${assignments}`;
|
|
|
124558
124598
|
{ generateType: callbacks.generateType }
|
|
124559
124599
|
);
|
|
124560
124600
|
}
|
|
124601
|
+
const hasInitializer = ctx.expression() !== null;
|
|
124561
124602
|
const modifiers = VariableModifierBuilder_default.build(
|
|
124562
124603
|
ctx,
|
|
124563
|
-
CodeGenState.inFunctionBody
|
|
124604
|
+
CodeGenState.inFunctionBody,
|
|
124605
|
+
hasInitializer,
|
|
124606
|
+
CodeGenState.cppMode
|
|
124564
124607
|
);
|
|
124565
124608
|
const name = ctx.IDENTIFIER().getText();
|
|
124566
124609
|
const typeCtx = ctx.type();
|
|
124567
124610
|
_VariableDeclHelper.validateArrayDeclarationSyntax(ctx, typeCtx, name);
|
|
124568
124611
|
const type = callbacks.inferVariableType(ctx, name);
|
|
124569
124612
|
callbacks.trackLocalVariable(ctx, name);
|
|
124613
|
+
if (type.endsWith("*")) {
|
|
124614
|
+
callbacks.markVariableAsPointer(name);
|
|
124615
|
+
}
|
|
124570
124616
|
const stringResult = StringDeclHelper_default.generateStringDecl(
|
|
124571
124617
|
typeCtx,
|
|
124572
124618
|
name,
|
|
@@ -124794,7 +124840,16 @@ var MemberSeparatorResolver = class _MemberSeparatorResolver {
|
|
|
124794
124840
|
/**
|
|
124795
124841
|
* Build the separator context for a member access chain
|
|
124796
124842
|
*/
|
|
124797
|
-
static buildContext(
|
|
124843
|
+
static buildContext(input, deps) {
|
|
124844
|
+
const {
|
|
124845
|
+
firstId,
|
|
124846
|
+
hasGlobal,
|
|
124847
|
+
hasThis,
|
|
124848
|
+
currentScope,
|
|
124849
|
+
isStructParam,
|
|
124850
|
+
isCppAccess,
|
|
124851
|
+
forcePointerSemantics
|
|
124852
|
+
} = input;
|
|
124798
124853
|
const isCrossScope = hasGlobal && (deps.isKnownScope(firstId) || deps.isKnownRegister(firstId));
|
|
124799
124854
|
const scopedRegName = hasThis && currentScope ? `${currentScope}_${firstId}` : null;
|
|
124800
124855
|
const isScopedRegister = scopedRegName !== null && deps.isKnownRegister(scopedRegName);
|
|
@@ -124804,7 +124859,8 @@ var MemberSeparatorResolver = class _MemberSeparatorResolver {
|
|
|
124804
124859
|
isStructParam,
|
|
124805
124860
|
isCppAccess,
|
|
124806
124861
|
scopedRegName,
|
|
124807
|
-
isScopedRegister
|
|
124862
|
+
isScopedRegister,
|
|
124863
|
+
forcePointerSemantics
|
|
124808
124864
|
};
|
|
124809
124865
|
}
|
|
124810
124866
|
/**
|
|
@@ -124815,6 +124871,9 @@ var MemberSeparatorResolver = class _MemberSeparatorResolver {
|
|
|
124815
124871
|
return "::";
|
|
124816
124872
|
}
|
|
124817
124873
|
if (ctx.isStructParam) {
|
|
124874
|
+
if (ctx.forcePointerSemantics) {
|
|
124875
|
+
return "->";
|
|
124876
|
+
}
|
|
124818
124877
|
return deps.getStructParamSeparator();
|
|
124819
124878
|
}
|
|
124820
124879
|
if (ctx.isCrossScope) {
|
|
@@ -124875,6 +124934,9 @@ var ParameterDereferenceResolver = class _ParameterDereferenceResolver {
|
|
|
124875
124934
|
* Determine if a parameter should be passed by value (no dereference needed)
|
|
124876
124935
|
*/
|
|
124877
124936
|
static isPassByValue(paramInfo, deps) {
|
|
124937
|
+
if (paramInfo.isCallbackPointerPrimitive) {
|
|
124938
|
+
return false;
|
|
124939
|
+
}
|
|
124878
124940
|
if (paramInfo.isCallback) {
|
|
124879
124941
|
return true;
|
|
124880
124942
|
}
|
|
@@ -124913,6 +124975,9 @@ var ParameterDereferenceResolver = class _ParameterDereferenceResolver {
|
|
|
124913
124975
|
if (_ParameterDereferenceResolver.isPassByValue(paramInfo, deps)) {
|
|
124914
124976
|
return id;
|
|
124915
124977
|
}
|
|
124978
|
+
if (paramInfo.forcePointerSemantics) {
|
|
124979
|
+
return `(*${id})`;
|
|
124980
|
+
}
|
|
124916
124981
|
return deps.maybeDereference(id);
|
|
124917
124982
|
}
|
|
124918
124983
|
};
|
|
@@ -125455,6 +125520,137 @@ var CastValidator = class _CastValidator {
|
|
|
125455
125520
|
};
|
|
125456
125521
|
var CastValidator_default = CastValidator;
|
|
125457
125522
|
|
|
125523
|
+
// src/transpiler/output/codegen/helpers/TypedefParamParser.ts
|
|
125524
|
+
var TypedefParamParser = class _TypedefParamParser {
|
|
125525
|
+
/**
|
|
125526
|
+
* Parse a function pointer typedef type string.
|
|
125527
|
+
*
|
|
125528
|
+
* @param typedefType - The type string, e.g., "void (*)(widget_t *, const rect_t *, uint8_t *)"
|
|
125529
|
+
* @returns Parsed result with return type and parameters, or null if parsing fails
|
|
125530
|
+
*/
|
|
125531
|
+
static parse(typedefType) {
|
|
125532
|
+
const funcPtrIndex = typedefType.indexOf("(*)");
|
|
125533
|
+
if (funcPtrIndex === -1) {
|
|
125534
|
+
return null;
|
|
125535
|
+
}
|
|
125536
|
+
const returnType = typedefType.substring(0, funcPtrIndex).trim();
|
|
125537
|
+
const afterFuncPtr = typedefType.substring(funcPtrIndex + 3).trim();
|
|
125538
|
+
if (!afterFuncPtr.startsWith("(")) {
|
|
125539
|
+
return null;
|
|
125540
|
+
}
|
|
125541
|
+
const paramsStr = _TypedefParamParser.extractParenContent(afterFuncPtr);
|
|
125542
|
+
if (paramsStr === null) {
|
|
125543
|
+
return null;
|
|
125544
|
+
}
|
|
125545
|
+
if (!paramsStr || paramsStr === "void") {
|
|
125546
|
+
return { returnType, params: [] };
|
|
125547
|
+
}
|
|
125548
|
+
const paramStrings = _TypedefParamParser.splitParams(paramsStr);
|
|
125549
|
+
const params = paramStrings.map((p) => _TypedefParamParser.parseParam(p));
|
|
125550
|
+
return { returnType, params };
|
|
125551
|
+
}
|
|
125552
|
+
/**
|
|
125553
|
+
* Extract content between matching parentheses, handling arbitrary nesting.
|
|
125554
|
+
* @param str - String starting with '('
|
|
125555
|
+
* @returns Content between outer parens, or null if no match
|
|
125556
|
+
*/
|
|
125557
|
+
static extractParenContent(str) {
|
|
125558
|
+
if (!str.startsWith("(")) {
|
|
125559
|
+
return null;
|
|
125560
|
+
}
|
|
125561
|
+
let depth = 0;
|
|
125562
|
+
for (let i = 0; i < str.length; i++) {
|
|
125563
|
+
if (str[i] === "(") {
|
|
125564
|
+
depth++;
|
|
125565
|
+
} else if (str[i] === ")") {
|
|
125566
|
+
depth--;
|
|
125567
|
+
if (depth === 0) {
|
|
125568
|
+
return str.substring(1, i);
|
|
125569
|
+
}
|
|
125570
|
+
}
|
|
125571
|
+
}
|
|
125572
|
+
return null;
|
|
125573
|
+
}
|
|
125574
|
+
/**
|
|
125575
|
+
* Split parameter string by commas, respecting nested parentheses.
|
|
125576
|
+
*/
|
|
125577
|
+
static splitParams(paramsStr) {
|
|
125578
|
+
const params = [];
|
|
125579
|
+
let current = "";
|
|
125580
|
+
let depth = 0;
|
|
125581
|
+
for (const char of paramsStr) {
|
|
125582
|
+
if (char === "(") {
|
|
125583
|
+
depth++;
|
|
125584
|
+
current += char;
|
|
125585
|
+
} else if (char === ")") {
|
|
125586
|
+
depth--;
|
|
125587
|
+
current += char;
|
|
125588
|
+
} else if (char === "," && depth === 0) {
|
|
125589
|
+
params.push(current.trim());
|
|
125590
|
+
current = "";
|
|
125591
|
+
} else {
|
|
125592
|
+
current += char;
|
|
125593
|
+
}
|
|
125594
|
+
}
|
|
125595
|
+
if (current.trim()) {
|
|
125596
|
+
params.push(current.trim());
|
|
125597
|
+
}
|
|
125598
|
+
return params;
|
|
125599
|
+
}
|
|
125600
|
+
/**
|
|
125601
|
+
* Parse a single parameter type string.
|
|
125602
|
+
*/
|
|
125603
|
+
static parseParam(paramStr) {
|
|
125604
|
+
const trimmed = paramStr.trim();
|
|
125605
|
+
const isPointer = trimmed.includes("*");
|
|
125606
|
+
const isConst = /\bconst\b/.test(trimmed) || trimmed.startsWith("const");
|
|
125607
|
+
let baseType = trimmed.replaceAll(/\bconst\b/g, "").replace(/^const/, "").replaceAll("*", "").replaceAll(/\s+/g, " ").trim();
|
|
125608
|
+
if (baseType.includes(" ")) {
|
|
125609
|
+
baseType = baseType.replace(/\s+\w+$/, "");
|
|
125610
|
+
}
|
|
125611
|
+
if (baseType.startsWith("struct ")) {
|
|
125612
|
+
baseType = baseType.substring(7);
|
|
125613
|
+
}
|
|
125614
|
+
return {
|
|
125615
|
+
type: trimmed,
|
|
125616
|
+
isPointer,
|
|
125617
|
+
isConst,
|
|
125618
|
+
baseType
|
|
125619
|
+
};
|
|
125620
|
+
}
|
|
125621
|
+
/**
|
|
125622
|
+
* Get the parameter info at a given index, or null if not found.
|
|
125623
|
+
*/
|
|
125624
|
+
static getParamAt(typedefType, paramIndex) {
|
|
125625
|
+
const parsed = _TypedefParamParser.parse(typedefType);
|
|
125626
|
+
if (!parsed || paramIndex >= parsed.params.length) {
|
|
125627
|
+
return null;
|
|
125628
|
+
}
|
|
125629
|
+
return parsed.params[paramIndex];
|
|
125630
|
+
}
|
|
125631
|
+
/**
|
|
125632
|
+
* Check if a parameter at a given index should be a pointer based on the typedef.
|
|
125633
|
+
*
|
|
125634
|
+
* @param typedefType - The typedef type string
|
|
125635
|
+
* @param paramIndex - The parameter index (0-based)
|
|
125636
|
+
* @returns true if the param should be a pointer, false for value, null if unknown
|
|
125637
|
+
*/
|
|
125638
|
+
static shouldBePointer(typedefType, paramIndex) {
|
|
125639
|
+
return _TypedefParamParser.getParamAt(typedefType, paramIndex)?.isPointer ?? null;
|
|
125640
|
+
}
|
|
125641
|
+
/**
|
|
125642
|
+
* Check if a parameter at a given index should be const based on the typedef.
|
|
125643
|
+
*
|
|
125644
|
+
* @param typedefType - The typedef type string
|
|
125645
|
+
* @param paramIndex - The parameter index (0-based)
|
|
125646
|
+
* @returns true if the param should be const, false otherwise, null if unknown
|
|
125647
|
+
*/
|
|
125648
|
+
static shouldBeConst(typedefType, paramIndex) {
|
|
125649
|
+
return _TypedefParamParser.getParamAt(typedefType, paramIndex)?.isConst ?? null;
|
|
125650
|
+
}
|
|
125651
|
+
};
|
|
125652
|
+
var TypedefParamParser_default = TypedefParamParser;
|
|
125653
|
+
|
|
125458
125654
|
// src/transpiler/output/codegen/helpers/FunctionContextManager.ts
|
|
125459
125655
|
var FunctionContextManager = class _FunctionContextManager {
|
|
125460
125656
|
/**
|
|
@@ -125515,14 +125711,15 @@ var FunctionContextManager = class _FunctionContextManager {
|
|
|
125515
125711
|
static processParameterList(params, callbacks) {
|
|
125516
125712
|
CodeGenState.currentParameters.clear();
|
|
125517
125713
|
if (!params) return;
|
|
125518
|
-
|
|
125519
|
-
|
|
125714
|
+
const paramList = params.parameter();
|
|
125715
|
+
for (let i = 0; i < paramList.length; i++) {
|
|
125716
|
+
_FunctionContextManager.processParameter(paramList[i], callbacks, i);
|
|
125520
125717
|
}
|
|
125521
125718
|
}
|
|
125522
125719
|
/**
|
|
125523
125720
|
* Process a single parameter declaration.
|
|
125524
125721
|
*/
|
|
125525
|
-
static processParameter(param, callbacks) {
|
|
125722
|
+
static processParameter(param, callbacks, paramIndex) {
|
|
125526
125723
|
const name = param.IDENTIFIER().getText();
|
|
125527
125724
|
const isArray = param.arrayDimension().length > 0 || param.type().arrayType() !== null;
|
|
125528
125725
|
const isConst = param.constModifier() !== null;
|
|
@@ -125531,17 +125728,21 @@ var FunctionContextManager = class _FunctionContextManager {
|
|
|
125531
125728
|
typeCtx,
|
|
125532
125729
|
callbacks
|
|
125533
125730
|
);
|
|
125534
|
-
const
|
|
125535
|
-
|
|
125536
|
-
|
|
125731
|
+
const callbackTypedefInfo = _FunctionContextManager.getCallbackTypedefParamInfo(paramIndex);
|
|
125732
|
+
const isCallbackPointerParam = callbackTypedefInfo?.shouldBePointer ?? false;
|
|
125733
|
+
const isStruct = callbackTypedefInfo ? isCallbackPointerParam && typeInfo.isStruct : typeInfo.isStruct;
|
|
125734
|
+
const isCallbackPointerPrimitive = isCallbackPointerParam && !typeInfo.isStruct && !isArray;
|
|
125537
125735
|
const paramInfo = {
|
|
125538
125736
|
name,
|
|
125539
125737
|
baseType: typeInfo.typeName,
|
|
125540
125738
|
isArray,
|
|
125541
|
-
isStruct
|
|
125739
|
+
isStruct,
|
|
125542
125740
|
isConst,
|
|
125543
125741
|
isCallback: typeInfo.isCallback,
|
|
125544
|
-
isString: typeInfo.isString
|
|
125742
|
+
isString: typeInfo.isString,
|
|
125743
|
+
isCallbackPointerPrimitive,
|
|
125744
|
+
// Issue #895: Callback-compatible params need pointer semantics even in C++ mode
|
|
125745
|
+
forcePointerSemantics: isCallbackPointerParam
|
|
125545
125746
|
};
|
|
125546
125747
|
CodeGenState.currentParameters.set(name, paramInfo);
|
|
125547
125748
|
_FunctionContextManager.registerParameterType(
|
|
@@ -125704,6 +125905,32 @@ var FunctionContextManager = class _FunctionContextManager {
|
|
|
125704
125905
|
}
|
|
125705
125906
|
return dimensions;
|
|
125706
125907
|
}
|
|
125908
|
+
/**
|
|
125909
|
+
* Issue #895: Get callback typedef parameter info from the C header.
|
|
125910
|
+
* Returns null if not callback-compatible or index is invalid.
|
|
125911
|
+
*/
|
|
125912
|
+
static getCallbackTypedefParamInfo(paramIndex) {
|
|
125913
|
+
if (CodeGenState.currentFunctionName === null) return null;
|
|
125914
|
+
const typedefName = CodeGenState.callbackCompatibleFunctions.get(
|
|
125915
|
+
CodeGenState.currentFunctionName
|
|
125916
|
+
);
|
|
125917
|
+
if (!typedefName) return null;
|
|
125918
|
+
const typedefType = CodeGenState.getTypedefType(typedefName);
|
|
125919
|
+
if (!typedefType) return null;
|
|
125920
|
+
const shouldBePointer = TypedefParamParser_default.shouldBePointer(
|
|
125921
|
+
typedefType,
|
|
125922
|
+
paramIndex
|
|
125923
|
+
);
|
|
125924
|
+
const shouldBeConst = TypedefParamParser_default.shouldBeConst(
|
|
125925
|
+
typedefType,
|
|
125926
|
+
paramIndex
|
|
125927
|
+
);
|
|
125928
|
+
if (shouldBePointer === null) return null;
|
|
125929
|
+
return {
|
|
125930
|
+
shouldBePointer,
|
|
125931
|
+
shouldBeConst: shouldBeConst ?? false
|
|
125932
|
+
};
|
|
125933
|
+
}
|
|
125707
125934
|
/**
|
|
125708
125935
|
* Extract string capacity from a string type context.
|
|
125709
125936
|
*/
|
|
@@ -126496,7 +126723,8 @@ var ParameterInputAdapter = class {
|
|
|
126496
126723
|
}
|
|
126497
126724
|
const isKnownStruct = deps.isKnownStruct(typeName);
|
|
126498
126725
|
const isKnownPrimitive = !!deps.typeMap[typeName];
|
|
126499
|
-
const isAutoConst = !deps.isModified && !isConst;
|
|
126726
|
+
const isAutoConst = !deps.isCallbackCompatible && !deps.isModified && !isConst;
|
|
126727
|
+
const isPassByReference = deps.forcePassByReference || isKnownStruct || isKnownPrimitive;
|
|
126500
126728
|
return {
|
|
126501
126729
|
name,
|
|
126502
126730
|
baseType: typeName,
|
|
@@ -126507,7 +126735,12 @@ var ParameterInputAdapter = class {
|
|
|
126507
126735
|
isCallback: false,
|
|
126508
126736
|
isString: false,
|
|
126509
126737
|
isPassByValue: deps.isPassByValue,
|
|
126510
|
-
isPassByReference
|
|
126738
|
+
isPassByReference,
|
|
126739
|
+
// Issue #895: Force pointer syntax in C++ mode for callback-compatible functions
|
|
126740
|
+
// because C callback typedefs expect pointers, not C++ references
|
|
126741
|
+
forcePointerSyntax: deps.forcePassByReference,
|
|
126742
|
+
// Issue #895: Preserve const from callback typedef signature
|
|
126743
|
+
forceConst: deps.forceConst
|
|
126511
126744
|
};
|
|
126512
126745
|
}
|
|
126513
126746
|
/**
|
|
@@ -126721,11 +126954,15 @@ var ParameterSignatureBuilder = class {
|
|
|
126721
126954
|
/**
|
|
126722
126955
|
* Build pass-by-reference parameter signature.
|
|
126723
126956
|
* C mode: const Point* p
|
|
126724
|
-
* C++ mode: const Point& p
|
|
126957
|
+
* C++ mode: const Point& p (unless forcePointerSyntax)
|
|
126958
|
+
*
|
|
126959
|
+
* Issue #895: When forcePointerSyntax is set, always use pointer syntax
|
|
126960
|
+
* because C callback typedefs expect pointers, not C++ references.
|
|
126725
126961
|
*/
|
|
126726
126962
|
static _buildRefParam(param, refSuffix) {
|
|
126727
126963
|
const constPrefix = this._getConstPrefix(param);
|
|
126728
|
-
|
|
126964
|
+
const actualSuffix = param.forcePointerSyntax ? "*" : refSuffix;
|
|
126965
|
+
return `${constPrefix}${param.mappedType}${actualSuffix} ${param.name}`;
|
|
126729
126966
|
}
|
|
126730
126967
|
/**
|
|
126731
126968
|
* Build unknown type parameter (pass by value, standard C semantics).
|
|
@@ -126735,13 +126972,15 @@ var ParameterSignatureBuilder = class {
|
|
|
126735
126972
|
return `${constMod}${param.mappedType} ${param.name}`;
|
|
126736
126973
|
}
|
|
126737
126974
|
/**
|
|
126738
|
-
* Get const prefix combining explicit const
|
|
126739
|
-
*
|
|
126975
|
+
* Get const prefix combining explicit const, auto-const, and forced const.
|
|
126976
|
+
* Priority: forceConst > isConst > isAutoConst
|
|
126977
|
+
* Issue #895: forceConst preserves const from callback typedef signature.
|
|
126740
126978
|
*/
|
|
126741
126979
|
static _getConstPrefix(param) {
|
|
126742
|
-
|
|
126743
|
-
|
|
126744
|
-
|
|
126980
|
+
if (param.forceConst || param.isConst || param.isAutoConst) {
|
|
126981
|
+
return "const ";
|
|
126982
|
+
}
|
|
126983
|
+
return "";
|
|
126745
126984
|
}
|
|
126746
126985
|
};
|
|
126747
126986
|
var ParameterSignatureBuilder_default = ParameterSignatureBuilder;
|
|
@@ -127717,7 +127956,7 @@ var CodeGenerator = class _CodeGenerator {
|
|
|
127717
127956
|
}
|
|
127718
127957
|
/** Generate parameter list for function signature */
|
|
127719
127958
|
generateParameterList(ctx) {
|
|
127720
|
-
return ctx.parameter().map((p) => this.generateParameter(p)).join(", ");
|
|
127959
|
+
return ctx.parameter().map((p, index) => this.generateParameter(p, index)).join(", ");
|
|
127721
127960
|
}
|
|
127722
127961
|
/** Get the raw type name without C conversion */
|
|
127723
127962
|
getTypeName(ctx) {
|
|
@@ -129463,13 +129702,17 @@ typedef ${callbackInfo.returnType} (*${callbackInfo.typedefName})(${paramList});
|
|
|
129463
129702
|
const typedef = this.generateCallbackTypedef(name);
|
|
129464
129703
|
return typedef ? functionCode + typedef : functionCode;
|
|
129465
129704
|
}
|
|
129466
|
-
generateParameter(ctx) {
|
|
129705
|
+
generateParameter(ctx, paramIndex) {
|
|
129467
129706
|
const typeName = this.getTypeName(ctx.type());
|
|
129468
129707
|
const name = ctx.IDENTIFIER().getText();
|
|
129469
129708
|
this._validateCStyleArrayParam(ctx, typeName, name);
|
|
129470
129709
|
this._validateUnboundedArrayParam(ctx);
|
|
129471
129710
|
const isModified = this._isCurrentParameterModified(name);
|
|
129472
|
-
const
|
|
129711
|
+
const callbackInfo = paramIndex === void 0 ? null : FunctionContextManager_default.getCallbackTypedefParamInfo(paramIndex);
|
|
129712
|
+
const isPassByValue = callbackInfo ? !callbackInfo.shouldBePointer : this._isPassByValueType(typeName, name);
|
|
129713
|
+
const isCallbackCompatible = callbackInfo !== null;
|
|
129714
|
+
const forcePassByReference = callbackInfo?.shouldBePointer ?? false;
|
|
129715
|
+
const forceConst = callbackInfo?.shouldBeConst ?? false;
|
|
129473
129716
|
const input = ParameterInputAdapter_default.fromAST(ctx, {
|
|
129474
129717
|
getTypeName: (t) => this.getTypeName(t),
|
|
129475
129718
|
generateType: (t) => this.generateType(t),
|
|
@@ -129478,7 +129721,10 @@ typedef ${callbackInfo.returnType} (*${callbackInfo.typedefName})(${paramList});
|
|
|
129478
129721
|
isKnownStruct: (t) => this.isKnownStruct(t),
|
|
129479
129722
|
typeMap: TYPE_MAP_default,
|
|
129480
129723
|
isModified,
|
|
129481
|
-
isPassByValue
|
|
129724
|
+
isPassByValue,
|
|
129725
|
+
isCallbackCompatible,
|
|
129726
|
+
forcePassByReference,
|
|
129727
|
+
forceConst
|
|
129482
129728
|
});
|
|
129483
129729
|
return ParameterSignatureBuilder_default.build(input, CppModeHelper_default.refOrPtr());
|
|
129484
129730
|
}
|
|
@@ -129548,6 +129794,7 @@ typedef ${callbackInfo.returnType} (*${callbackInfo.typedefName})(${paramList});
|
|
|
129548
129794
|
getExpressionType: (exprCtx) => this.getExpressionType(exprCtx),
|
|
129549
129795
|
inferVariableType: (varCtx, name) => this._inferVariableType(varCtx, name),
|
|
129550
129796
|
trackLocalVariable: (varCtx, name) => this._trackLocalVariable(varCtx, name),
|
|
129797
|
+
markVariableAsPointer: (name) => this._markVariableAsPointer(name),
|
|
129551
129798
|
getStringConcatOperands: (concatCtx) => this._getStringConcatOperands(concatCtx),
|
|
129552
129799
|
getSubstringOperands: (substrCtx) => this._getSubstringOperands(substrCtx),
|
|
129553
129800
|
getStringExprCapacity: (exprCode) => this.getStringExprCapacity(exprCode),
|
|
@@ -129556,20 +129803,116 @@ typedef ${callbackInfo.returnType} (*${callbackInfo.typedefName})(${paramList});
|
|
|
129556
129803
|
}
|
|
129557
129804
|
/**
|
|
129558
129805
|
* Issue #696: Infer variable type, handling nullable C pointer types.
|
|
129806
|
+
* Issue #895 Bug B: Infer pointer type from C function return type.
|
|
129559
129807
|
*/
|
|
129560
129808
|
_inferVariableType(ctx, name) {
|
|
129561
|
-
|
|
129562
|
-
if (!
|
|
129809
|
+
const type = this.generateType(ctx.type());
|
|
129810
|
+
if (!ctx.expression()) {
|
|
129563
129811
|
return type;
|
|
129564
129812
|
}
|
|
129565
|
-
const
|
|
129566
|
-
|
|
129567
|
-
|
|
129568
|
-
|
|
129813
|
+
const pointerType = this._inferPointerTypeFromFunctionCall(
|
|
129814
|
+
ctx.expression(),
|
|
129815
|
+
type
|
|
129816
|
+
);
|
|
129817
|
+
if (pointerType) {
|
|
129818
|
+
return pointerType;
|
|
129819
|
+
}
|
|
129820
|
+
if (name.startsWith("c_")) {
|
|
129821
|
+
const exprText = ctx.expression().getText();
|
|
129822
|
+
for (const funcName of NullCheckAnalyzer_default.getStructPointerFunctions()) {
|
|
129823
|
+
if (exprText.includes(`${funcName}(`)) {
|
|
129824
|
+
return `${type}*`;
|
|
129825
|
+
}
|
|
129569
129826
|
}
|
|
129570
129827
|
}
|
|
129571
129828
|
return type;
|
|
129572
129829
|
}
|
|
129830
|
+
/**
|
|
129831
|
+
* Issue #895 Bug B: Infer pointer type from C function return type.
|
|
129832
|
+
* If initializer is a call to a C function that returns T*, and declared
|
|
129833
|
+
* type is T, return T* instead of T.
|
|
129834
|
+
*/
|
|
129835
|
+
_inferPointerTypeFromFunctionCall(expr, declaredType) {
|
|
129836
|
+
const funcName = this._extractCFunctionName(expr);
|
|
129837
|
+
if (!funcName) {
|
|
129838
|
+
return null;
|
|
129839
|
+
}
|
|
129840
|
+
const cFunc = CodeGenState.symbolTable?.getCSymbol(funcName);
|
|
129841
|
+
if (cFunc?.kind !== "function") {
|
|
129842
|
+
return null;
|
|
129843
|
+
}
|
|
129844
|
+
const returnType = cFunc.type;
|
|
129845
|
+
if (!returnType.endsWith("*")) {
|
|
129846
|
+
return null;
|
|
129847
|
+
}
|
|
129848
|
+
const returnBaseType = returnType.replace(/\s*\*\s*$/, "").trim();
|
|
129849
|
+
if (returnBaseType === declaredType) {
|
|
129850
|
+
return `${declaredType}*`;
|
|
129851
|
+
}
|
|
129852
|
+
return null;
|
|
129853
|
+
}
|
|
129854
|
+
/**
|
|
129855
|
+
* Extract C function name from expression patterns.
|
|
129856
|
+
* Handles both:
|
|
129857
|
+
* - global.funcName(...) - explicit global access
|
|
129858
|
+
* - funcName(...) - direct call (if funcName is a known C function)
|
|
129859
|
+
* Returns null if expression doesn't match these patterns.
|
|
129860
|
+
*/
|
|
129861
|
+
_extractCFunctionName(expr) {
|
|
129862
|
+
const postfix = ExpressionUnwrapper_default.getPostfixExpression(expr);
|
|
129863
|
+
if (!postfix) {
|
|
129864
|
+
return null;
|
|
129865
|
+
}
|
|
129866
|
+
const primary = postfix.primaryExpression();
|
|
129867
|
+
const ops = postfix.postfixOp();
|
|
129868
|
+
if (primary.GLOBAL()) {
|
|
129869
|
+
return this._extractGlobalPatternFuncName(ops);
|
|
129870
|
+
}
|
|
129871
|
+
const identifier = primary.IDENTIFIER();
|
|
129872
|
+
if (identifier) {
|
|
129873
|
+
return this._extractDirectCallFuncName(identifier.getText(), ops);
|
|
129874
|
+
}
|
|
129875
|
+
return null;
|
|
129876
|
+
}
|
|
129877
|
+
/**
|
|
129878
|
+
* Extract function name from global.funcName(...) pattern.
|
|
129879
|
+
*/
|
|
129880
|
+
_extractGlobalPatternFuncName(ops) {
|
|
129881
|
+
if (ops.length < 2) {
|
|
129882
|
+
return null;
|
|
129883
|
+
}
|
|
129884
|
+
const memberOp = ops[0];
|
|
129885
|
+
if (!memberOp.IDENTIFIER()) {
|
|
129886
|
+
return null;
|
|
129887
|
+
}
|
|
129888
|
+
const callOp = ops[1];
|
|
129889
|
+
if (!this._isCallOp(callOp)) {
|
|
129890
|
+
return null;
|
|
129891
|
+
}
|
|
129892
|
+
return memberOp.IDENTIFIER().getText();
|
|
129893
|
+
}
|
|
129894
|
+
/**
|
|
129895
|
+
* Extract function name from direct funcName(...) call if it's a C function.
|
|
129896
|
+
*/
|
|
129897
|
+
_extractDirectCallFuncName(funcName, ops) {
|
|
129898
|
+
if (ops.length < 1) {
|
|
129899
|
+
return null;
|
|
129900
|
+
}
|
|
129901
|
+
if (!this._isCallOp(ops[0])) {
|
|
129902
|
+
return null;
|
|
129903
|
+
}
|
|
129904
|
+
const cFunc = CodeGenState.symbolTable?.getCSymbol(funcName);
|
|
129905
|
+
if (cFunc?.kind === "function") {
|
|
129906
|
+
return funcName;
|
|
129907
|
+
}
|
|
129908
|
+
return null;
|
|
129909
|
+
}
|
|
129910
|
+
/**
|
|
129911
|
+
* Check if a postfix op is a function call.
|
|
129912
|
+
*/
|
|
129913
|
+
_isCallOp(op) {
|
|
129914
|
+
return Boolean(op.argumentList() || op.getText().startsWith("("));
|
|
129915
|
+
}
|
|
129573
129916
|
/**
|
|
129574
129917
|
* Issue #696: Track local variable for type registry and const values.
|
|
129575
129918
|
*/
|
|
@@ -129590,6 +129933,20 @@ typedef ${callbackInfo.returnType} (*${callbackInfo.typedefName})(${paramList});
|
|
|
129590
129933
|
}
|
|
129591
129934
|
}
|
|
129592
129935
|
}
|
|
129936
|
+
/**
|
|
129937
|
+
* Issue #895 Bug B: Mark variable as a pointer in the type registry.
|
|
129938
|
+
* Called when type inference detects that a variable should be a pointer
|
|
129939
|
+
* (e.g., initialized from a C function returning T*).
|
|
129940
|
+
*/
|
|
129941
|
+
_markVariableAsPointer(name) {
|
|
129942
|
+
const typeInfo = CodeGenState.getVariableTypeInfo(name);
|
|
129943
|
+
if (typeInfo) {
|
|
129944
|
+
CodeGenState.setVariableTypeInfo(name, {
|
|
129945
|
+
...typeInfo,
|
|
129946
|
+
isPointer: true
|
|
129947
|
+
});
|
|
129948
|
+
}
|
|
129949
|
+
}
|
|
129593
129950
|
// Issue #792: Methods _handleArrayDeclaration, _getArrayTypeDimension, _parseArrayTypeDimension,
|
|
129594
129951
|
// _parseFirstArrayDimension, _validateArrayDeclarationSyntax, _extractBaseTypeName,
|
|
129595
129952
|
// _generateVariableInitializer, _validateIntegerInitializer, _finalizeCppClassAssignments,
|
|
@@ -129816,14 +130173,18 @@ typedef ${callbackInfo.returnType} (*${callbackInfo.typedefName})(${paramList});
|
|
|
129816
130173
|
const isStructParam = paramInfo?.isStruct ?? false;
|
|
129817
130174
|
const isCppAccess = hasGlobal && this.isCppScopeSymbol(firstId);
|
|
129818
130175
|
const separatorDeps = this._buildMemberSeparatorDeps();
|
|
130176
|
+
const forcePointerSemantics = paramInfo?.forcePointerSemantics ?? false;
|
|
129819
130177
|
const separatorCtx = MemberSeparatorResolver_default.buildContext(
|
|
129820
|
-
|
|
129821
|
-
|
|
129822
|
-
|
|
129823
|
-
|
|
129824
|
-
|
|
129825
|
-
|
|
129826
|
-
|
|
130178
|
+
{
|
|
130179
|
+
firstId,
|
|
130180
|
+
hasGlobal,
|
|
130181
|
+
hasThis,
|
|
130182
|
+
currentScope: CodeGenState.currentScope,
|
|
130183
|
+
isStructParam,
|
|
130184
|
+
isCppAccess,
|
|
130185
|
+
forcePointerSemantics
|
|
130186
|
+
},
|
|
130187
|
+
separatorDeps
|
|
129827
130188
|
);
|
|
129828
130189
|
return {
|
|
129829
130190
|
generateExpression: (expr) => this.generateExpression(expr),
|
|
@@ -133162,6 +133523,8 @@ var FunctionCollector2 = class _FunctionCollector {
|
|
|
133162
133523
|
const parameters = _FunctionCollector._mapParameters(
|
|
133163
133524
|
DeclaratorUtils_default.extractFunctionParameters(declarator)
|
|
133164
133525
|
);
|
|
133526
|
+
const hasPointer = declarator.pointer() !== null;
|
|
133527
|
+
const returnType = hasPointer ? `${baseType}*` : baseType;
|
|
133165
133528
|
return {
|
|
133166
133529
|
kind: "function",
|
|
133167
133530
|
name,
|
|
@@ -133169,7 +133532,7 @@ var FunctionCollector2 = class _FunctionCollector {
|
|
|
133169
133532
|
sourceLine: line,
|
|
133170
133533
|
sourceLanguage: ESourceLanguage_default.C,
|
|
133171
133534
|
isExported: !isExtern,
|
|
133172
|
-
type:
|
|
133535
|
+
type: returnType,
|
|
133173
133536
|
parameters: parameters.length > 0 ? parameters : void 0,
|
|
133174
133537
|
isDeclaration: true
|
|
133175
133538
|
};
|
|
@@ -136858,6 +137221,8 @@ var FunctionCallAnalyzer = class {
|
|
|
136858
137221
|
if (sym?.kind !== "type") return false;
|
|
136859
137222
|
return "type" in sym && typeof sym.type === "string" && sym.type.includes("(*)");
|
|
136860
137223
|
}
|
|
137224
|
+
/** Current scope name during callback assignment scanning */
|
|
137225
|
+
scanCurrentScope = null;
|
|
136861
137226
|
/**
|
|
136862
137227
|
* Detect functions assigned to C function pointer typedefs.
|
|
136863
137228
|
* When `PointCallback cb <- my_handler;` is found and PointCallback
|
|
@@ -136866,12 +137231,46 @@ var FunctionCallAnalyzer = class {
|
|
|
136866
137231
|
collectCallbackCompatibleFunctions(tree) {
|
|
136867
137232
|
for (const decl of tree.declaration()) {
|
|
136868
137233
|
const funcDecl = decl.functionDeclaration();
|
|
136869
|
-
if (
|
|
136870
|
-
|
|
136871
|
-
|
|
136872
|
-
|
|
137234
|
+
if (funcDecl) {
|
|
137235
|
+
this.scanStandaloneFunctionForCallbacks(funcDecl);
|
|
137236
|
+
continue;
|
|
137237
|
+
}
|
|
137238
|
+
const scopeDecl = decl.scopeDeclaration();
|
|
137239
|
+
if (scopeDecl) {
|
|
137240
|
+
this.scanScopeForCallbacks(scopeDecl);
|
|
137241
|
+
}
|
|
136873
137242
|
}
|
|
136874
137243
|
}
|
|
137244
|
+
/**
|
|
137245
|
+
* Scan a standalone function declaration for callback assignments.
|
|
137246
|
+
*/
|
|
137247
|
+
scanStandaloneFunctionForCallbacks(funcDecl) {
|
|
137248
|
+
const block = funcDecl.block();
|
|
137249
|
+
if (!block) return;
|
|
137250
|
+
this.scanCurrentScope = null;
|
|
137251
|
+
this.scanBlockForCallbackAssignments(block);
|
|
137252
|
+
}
|
|
137253
|
+
/**
|
|
137254
|
+
* Scan all member functions in a scope for callback assignments (Issue #895).
|
|
137255
|
+
*/
|
|
137256
|
+
scanScopeForCallbacks(scopeDecl) {
|
|
137257
|
+
const scopeName = scopeDecl.IDENTIFIER().getText();
|
|
137258
|
+
for (const member of scopeDecl.scopeMember()) {
|
|
137259
|
+
this.scanScopeMemberForCallbacks(member, scopeName);
|
|
137260
|
+
}
|
|
137261
|
+
this.scanCurrentScope = null;
|
|
137262
|
+
}
|
|
137263
|
+
/**
|
|
137264
|
+
* Scan a single scope member for callback assignments.
|
|
137265
|
+
*/
|
|
137266
|
+
scanScopeMemberForCallbacks(member, scopeName) {
|
|
137267
|
+
const memberFunc = member.functionDeclaration();
|
|
137268
|
+
if (!memberFunc) return;
|
|
137269
|
+
const block = memberFunc.block();
|
|
137270
|
+
if (!block) return;
|
|
137271
|
+
this.scanCurrentScope = scopeName;
|
|
137272
|
+
this.scanBlockForCallbackAssignments(block);
|
|
137273
|
+
}
|
|
136875
137274
|
/**
|
|
136876
137275
|
* Recursively scan all statements in a block for callback typedef assignments.
|
|
136877
137276
|
*/
|
|
@@ -136890,6 +137289,11 @@ var FunctionCallAnalyzer = class {
|
|
|
136890
137289
|
this.checkVarDeclForCallbackAssignment(varDecl);
|
|
136891
137290
|
return;
|
|
136892
137291
|
}
|
|
137292
|
+
const exprStmt = stmt.expressionStatement();
|
|
137293
|
+
if (exprStmt) {
|
|
137294
|
+
this.checkExpressionForCallbackArgs(exprStmt.expression());
|
|
137295
|
+
return;
|
|
137296
|
+
}
|
|
136893
137297
|
const ifStmt = stmt.ifStatement();
|
|
136894
137298
|
if (ifStmt) {
|
|
136895
137299
|
for (const child of ifStmt.statement()) {
|
|
@@ -136945,22 +137349,101 @@ var FunctionCallAnalyzer = class {
|
|
|
136945
137349
|
if (!funcRef) return;
|
|
136946
137350
|
const lookupName = funcRef.includes(".") ? funcRef.replace(".", "_") : funcRef;
|
|
136947
137351
|
if (this.allLocalFunctions.has(lookupName)) {
|
|
136948
|
-
CodeGenState.callbackCompatibleFunctions.
|
|
137352
|
+
CodeGenState.callbackCompatibleFunctions.set(lookupName, typeName);
|
|
136949
137353
|
}
|
|
136950
137354
|
}
|
|
136951
137355
|
/**
|
|
136952
137356
|
* Extract a function reference from an expression context.
|
|
136953
|
-
* Matches
|
|
136954
|
-
*
|
|
137357
|
+
* Matches:
|
|
137358
|
+
* - Bare identifiers: "my_handler"
|
|
137359
|
+
* - Qualified scope names: "MyScope.handler"
|
|
137360
|
+
* - Self-scope reference: "this.handler" (resolved using scanCurrentScope)
|
|
137361
|
+
* - Global scope reference: "global.ScopeName.handler"
|
|
136955
137362
|
* Returns null if the expression is not a function reference.
|
|
136956
137363
|
*/
|
|
136957
137364
|
extractFunctionReference(expr) {
|
|
136958
137365
|
const text = expr.getText();
|
|
136959
|
-
|
|
137366
|
+
const thisPattern = /^this\.(\w+)$/;
|
|
137367
|
+
const thisMatch = thisPattern.exec(text);
|
|
137368
|
+
if (thisMatch) {
|
|
137369
|
+
if (!this.scanCurrentScope) {
|
|
137370
|
+
return null;
|
|
137371
|
+
}
|
|
137372
|
+
return this.scanCurrentScope + "." + thisMatch[1];
|
|
137373
|
+
}
|
|
137374
|
+
const globalPattern = /^global\.(\w+)\.(\w+)$/;
|
|
137375
|
+
const globalMatch = globalPattern.exec(text);
|
|
137376
|
+
if (globalMatch) {
|
|
137377
|
+
return globalMatch[1] + "." + globalMatch[2];
|
|
137378
|
+
}
|
|
137379
|
+
const simplePattern = /^\w+(\.\w+)?$/;
|
|
137380
|
+
if (simplePattern.test(text)) {
|
|
136960
137381
|
return text;
|
|
136961
137382
|
}
|
|
136962
137383
|
return null;
|
|
136963
137384
|
}
|
|
137385
|
+
/**
|
|
137386
|
+
* Issue #895: Check expression for function calls that pass C-Next functions
|
|
137387
|
+
* to C function pointer parameters.
|
|
137388
|
+
*
|
|
137389
|
+
* Pattern: `global.widget_set_flush_cb(w, my_flush)`
|
|
137390
|
+
* Where widget_set_flush_cb's 2nd param is a C function pointer typedef.
|
|
137391
|
+
*/
|
|
137392
|
+
checkExpressionForCallbackArgs(expr) {
|
|
137393
|
+
const postfix = this.findPostfixExpression(expr);
|
|
137394
|
+
if (!postfix) return;
|
|
137395
|
+
const callInfo = this.extractCallInfo(postfix);
|
|
137396
|
+
if (!callInfo) return;
|
|
137397
|
+
const cFunc = this.symbolTable?.getCSymbol(callInfo.funcName);
|
|
137398
|
+
if (cFunc?.kind !== "function" || !cFunc.parameters) return;
|
|
137399
|
+
for (let i = 0; i < callInfo.args.length; i++) {
|
|
137400
|
+
const param = cFunc.parameters[i];
|
|
137401
|
+
if (!param) continue;
|
|
137402
|
+
if (!this.isCFunctionPointerTypedef(param.type)) continue;
|
|
137403
|
+
const funcRef = this.extractFunctionReference(callInfo.args[i]);
|
|
137404
|
+
if (!funcRef) continue;
|
|
137405
|
+
const lookupName = funcRef.includes(".") ? funcRef.replace(".", "_") : funcRef;
|
|
137406
|
+
if (this.allLocalFunctions.has(lookupName)) {
|
|
137407
|
+
CodeGenState.callbackCompatibleFunctions.set(lookupName, param.type);
|
|
137408
|
+
}
|
|
137409
|
+
}
|
|
137410
|
+
}
|
|
137411
|
+
/**
|
|
137412
|
+
* Find the postfix expression within an expression tree.
|
|
137413
|
+
* Uses ExpressionUnwrapper which validates that expression is "simple"
|
|
137414
|
+
* (single term at each level), returning null for complex expressions.
|
|
137415
|
+
*/
|
|
137416
|
+
findPostfixExpression(expr) {
|
|
137417
|
+
return ExpressionUnwrapper_default.getPostfixExpression(expr);
|
|
137418
|
+
}
|
|
137419
|
+
/**
|
|
137420
|
+
* Extract function name and arguments from a postfix expression.
|
|
137421
|
+
* Returns null if not a function call.
|
|
137422
|
+
*/
|
|
137423
|
+
extractCallInfo(postfix) {
|
|
137424
|
+
const primary = postfix.primaryExpression();
|
|
137425
|
+
const ops = postfix.postfixOp();
|
|
137426
|
+
const ident = primary.IDENTIFIER();
|
|
137427
|
+
const globalKw = primary.GLOBAL();
|
|
137428
|
+
if (!ident && !globalKw) {
|
|
137429
|
+
return null;
|
|
137430
|
+
}
|
|
137431
|
+
let funcName = ident ? ident.getText() : "";
|
|
137432
|
+
let argListOp = null;
|
|
137433
|
+
for (const op of ops) {
|
|
137434
|
+
if (op.IDENTIFIER()) {
|
|
137435
|
+
const member = op.IDENTIFIER().getText();
|
|
137436
|
+
funcName = funcName ? `${funcName}_${member}` : member;
|
|
137437
|
+
} else if (op.argumentList() || op.getText().startsWith("(")) {
|
|
137438
|
+
argListOp = op;
|
|
137439
|
+
break;
|
|
137440
|
+
}
|
|
137441
|
+
}
|
|
137442
|
+
if (!argListOp || !funcName) return null;
|
|
137443
|
+
const argList = argListOp.argumentList();
|
|
137444
|
+
const args = argList?.expression() ?? [];
|
|
137445
|
+
return { funcName, args };
|
|
137446
|
+
}
|
|
136964
137447
|
/**
|
|
136965
137448
|
* ADR-040: Register a variable that holds a callable (ISR or callback)
|
|
136966
137449
|
*/
|
|
@@ -138627,7 +139110,7 @@ var Transpiler = class {
|
|
|
138627
139110
|
this.state.reset();
|
|
138628
139111
|
CodeGenState.symbolTable.clear();
|
|
138629
139112
|
SymbolRegistry_default.reset();
|
|
138630
|
-
CodeGenState.callbackCompatibleFunctions = /* @__PURE__ */ new
|
|
139113
|
+
CodeGenState.callbackCompatibleFunctions = /* @__PURE__ */ new Map();
|
|
138631
139114
|
}
|
|
138632
139115
|
/**
|
|
138633
139116
|
* Ensure output directories exist
|