mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-02-06 15:24:55 +08:00
Temporary docs/ to keep track of refactoring work
This commit is contained in:
parent
cd24e558e2
commit
b9424e0745
2
.gitignore
vendored
2
.gitignore
vendored
@ -236,10 +236,10 @@ spine-libgdx/spine-skeletonviewer/.settings/org.eclipse.jdt.apt.core.prefs
|
||||
spine-cpp/.cache
|
||||
.build
|
||||
spine-libgdx/.settings
|
||||
docs/
|
||||
out.json
|
||||
spine-cpp/extraction.log
|
||||
extraction.log
|
||||
spine-c-new/codegen/dist
|
||||
spine-c-new/codegen/all-spine-types.json
|
||||
spine-c-new/codegen/spine-cpp-types.json
|
||||
docs/spine-runtimes-types.md
|
||||
|
||||
127
docs/generate-type-hierarchy.js
Executable file
127
docs/generate-type-hierarchy.js
Executable file
@ -0,0 +1,127 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { execSync } = require('child_process');
|
||||
|
||||
// Generate LSP symbols if not already present
|
||||
const symbolsFile = path.join(__dirname, 'spine-libgdx-symbols.json');
|
||||
if (!fs.existsSync(symbolsFile)) {
|
||||
console.log('Generating LSP symbols for spine-libgdx...');
|
||||
execSync(`npx lsp-cli ../spine-libgdx/spine-libgdx/src java ${symbolsFile}`, {
|
||||
stdio: 'inherit',
|
||||
cwd: __dirname
|
||||
});
|
||||
}
|
||||
|
||||
// Read and parse symbols
|
||||
const data = JSON.parse(fs.readFileSync(symbolsFile, 'utf8'));
|
||||
const allTypes = [];
|
||||
|
||||
// Recursively collect all types (including inner types)
|
||||
function collectTypes(symbols, parentName = '') {
|
||||
symbols.forEach(symbol => {
|
||||
if (['class', 'interface', 'enum'].includes(symbol.kind)) {
|
||||
// Skip utils package
|
||||
if (symbol.file && symbol.file.includes('/utils/')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip SkeletonRenderer classes
|
||||
if (symbol.name === 'SkeletonRenderer' || symbol.name === 'SkeletonRendererDebug') {
|
||||
return;
|
||||
}
|
||||
|
||||
const fullName = parentName ? `${parentName}.${symbol.name}` : symbol.name;
|
||||
allTypes.push({
|
||||
name: fullName,
|
||||
kind: symbol.kind,
|
||||
supertypes: symbol.supertypes || [],
|
||||
documentation: symbol.documentation || null
|
||||
});
|
||||
|
||||
// Recursively process children
|
||||
if (symbol.children) {
|
||||
collectTypes(symbol.children, fullName);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Collect all types
|
||||
collectTypes(data.symbols);
|
||||
|
||||
// Sort types alphabetically
|
||||
allTypes.sort((a, b) => a.name.localeCompare(b.name));
|
||||
|
||||
// Generate markdown
|
||||
let markdown = `# Spine Runtime Types
|
||||
|
||||
Generated from spine-libgdx (reference implementation)
|
||||
Total types: ${allTypes.length}
|
||||
|
||||
## Type Hierarchy
|
||||
|
||||
`;
|
||||
|
||||
// Build hierarchy map
|
||||
const childrenMap = new Map();
|
||||
allTypes.forEach(type => {
|
||||
type.supertypes.forEach(supertype => {
|
||||
if (!childrenMap.has(supertype)) {
|
||||
childrenMap.set(supertype, []);
|
||||
}
|
||||
childrenMap.get(supertype).push(type.name);
|
||||
});
|
||||
});
|
||||
|
||||
// Helper to print hierarchy
|
||||
function printHierarchy(typeName, indent = '') {
|
||||
const children = childrenMap.get(typeName);
|
||||
if (children && children.length > 0) {
|
||||
children.sort().forEach(child => {
|
||||
markdown += `${indent}- ${child}\n`;
|
||||
printHierarchy(child, indent + ' ');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Only show types that have subclasses
|
||||
const typesWithChildren = [];
|
||||
allTypes.forEach(type => {
|
||||
if (childrenMap.has(type.name)) {
|
||||
typesWithChildren.push(type.name);
|
||||
}
|
||||
});
|
||||
|
||||
// Sort and print hierarchy for types with children
|
||||
typesWithChildren.sort().forEach(typeName => {
|
||||
markdown += `\n**${typeName}**\n`;
|
||||
printHierarchy(typeName, '');
|
||||
});
|
||||
|
||||
// Add complete type list
|
||||
markdown += `\n## All Types\n\n`;
|
||||
|
||||
allTypes.forEach(type => {
|
||||
markdown += `- **${type.name}** (${type.kind})`;
|
||||
|
||||
if (type.supertypes.length > 0) {
|
||||
markdown += ` extends ${type.supertypes.join(', ')}`;
|
||||
}
|
||||
|
||||
if (type.documentation) {
|
||||
const firstLine = type.documentation.split('\n')[0].trim();
|
||||
if (firstLine) {
|
||||
markdown += ` - ${firstLine}`;
|
||||
}
|
||||
}
|
||||
|
||||
markdown += '\n';
|
||||
});
|
||||
|
||||
// Write output
|
||||
const outputFile = path.join(__dirname, 'spine-runtimes-types.md');
|
||||
fs.writeFileSync(outputFile, markdown);
|
||||
console.log(`Generated: ${outputFile}`);
|
||||
console.log(`Total types: ${allTypes.length}`);
|
||||
137
docs/project-description.md
Normal file
137
docs/project-description.md
Normal file
@ -0,0 +1,137 @@
|
||||
# Project: Spine Runtimes
|
||||
|
||||
Multi-language runtime libraries for Spine 2D skeletal animations. The project provides core runtimes for different programming languages and integrations for various game engines and frameworks.
|
||||
|
||||
## Core Runtimes (Language-specific implementations)
|
||||
|
||||
### spine-libgdx (Java) - Reference Implementation
|
||||
- **Build**: `./gradlew build`
|
||||
- **Publish**: `./gradlew publishToMavenLocal`
|
||||
- All other runtimes are ports of this reference implementation
|
||||
|
||||
### spine-cpp (C++)
|
||||
- **Build**: `cmake -B build && cmake --build build`
|
||||
- Port of spine-libgdx
|
||||
- Uses custom RTTI (no C++ exceptions/RTTI)
|
||||
|
||||
### spine-csharp (C#)
|
||||
- **Build**: `msbuild spine-csharp.csproj` or `dotnet build`
|
||||
- Port of spine-libgdx
|
||||
- Targets .NET Framework 4.8
|
||||
|
||||
### spine-ts (TypeScript/JavaScript)
|
||||
- **Build**: `npm install && npm run build`
|
||||
- **Dev**: `npm run dev`
|
||||
- Port of spine-libgdx
|
||||
- Core module at spine-ts/spine-core/
|
||||
|
||||
### spine-haxe (Haxe)
|
||||
- **Build**: `./build.sh`
|
||||
- Port of spine-libgdx
|
||||
- Uses Haxelib package manager
|
||||
|
||||
### spine-c (C)
|
||||
- **Build**: `cmake -B build && cmake --build build`
|
||||
- Actively maintained C implementation
|
||||
- Different structure from other runtimes due to C language constraints
|
||||
|
||||
## Integration Runtimes (Engine/Framework-specific)
|
||||
|
||||
### Using spine-cpp:
|
||||
- **spine-cocos2dx** - Cocos2d-x engine
|
||||
- **spine-flutter** - Flutter (via spine-cpp-lite C wrapper + Dart FFI)
|
||||
- **spine-glfw** - GLFW/OpenGL example
|
||||
- **spine-godot** - Godot Engine
|
||||
- **spine-ios** - iOS native (via spine-cpp-lite C wrapper + Swift codegen)
|
||||
- **spine-sdl** - SDL (optional, can use spine-c)
|
||||
- **spine-sfml/cpp** - SFML C++ version
|
||||
- **spine-ue** - Unreal Engine 4.27-5.4
|
||||
|
||||
### Using spine-csharp:
|
||||
- **spine-unity** - Unity game engine
|
||||
- **spine-monogame** - MonoGame framework
|
||||
- **spine-xna** - XNA Framework (legacy)
|
||||
|
||||
### Using spine-libgdx:
|
||||
- **spine-android** - Android native
|
||||
|
||||
### Using spine-c:
|
||||
- **spine-sdl** - SDL (optional, can use spine-cpp)
|
||||
- **spine-sfml/c** - SFML C version
|
||||
|
||||
### Using spine-core (TypeScript):
|
||||
- **spine-canvas** - HTML5 Canvas
|
||||
- **spine-webgl** - Direct WebGL
|
||||
- **spine-phaser** - Phaser.js (v3 & v4)
|
||||
- **spine-pixi** - PixiJS (v7 & v8)
|
||||
- **spine-threejs** - Three.js
|
||||
- **spine-player** - Embeddable web player
|
||||
- **spine-canvaskit** - Skia CanvasKit
|
||||
- **spine-webcomponents** - Web Components
|
||||
|
||||
## Special Cases
|
||||
|
||||
### spine-cpp-lite
|
||||
- C wrapper around spine-cpp for FFI-friendly bindings
|
||||
- Located in spine-cpp/spine-cpp-lite/
|
||||
- Provides simplified C API consumed by:
|
||||
- spine-flutter (via Dart FFI)
|
||||
- spine-ios (via Swift code generation)
|
||||
|
||||
## Commands Summary
|
||||
- **Check spine-cpp**: `cd spine-cpp && cmake -B build && cmake --build build`
|
||||
- **Check spine-ts**: `cd spine-ts && npm install && npm run build`
|
||||
- **Check spine-libgdx**: `cd spine-libgdx && ./gradlew build`
|
||||
- **Check spine-csharp**: `cd spine-csharp && msbuild spine-csharp.csproj`
|
||||
- **Check spine-c**: `cd spine-c && cmake -B build && cmake --build build`
|
||||
|
||||
## Development Workflow
|
||||
- All changes start in spine-libgdx (reference implementation)
|
||||
- Port changes to other core runtimes using git diffs
|
||||
- Integration runtimes automatically inherit changes from their core runtime
|
||||
|
||||
## Type System Documentation
|
||||
For a complete reference of all types in the Spine runtime, see [spine-runtimes-types.md](spine-runtimes-types.md).
|
||||
To regenerate this documentation, run: `node docs/generate-type-hierarchy.js`
|
||||
|
||||
## Spine Type System Overview (from spine-libgdx)
|
||||
|
||||
### Core Types
|
||||
- **Skeleton** - Stores the current pose for a skeleton
|
||||
- **SkeletonData** - Stores setup pose and stateless data
|
||||
- **Bone/BoneData** - Current pose and setup data for bones
|
||||
- **Slot/SlotData** - Manages attachments and drawing order
|
||||
- **Skin** - Collection of attachments keyed by slot and name
|
||||
|
||||
### Animation System
|
||||
- **Animation** - List of timelines to animate skeleton pose
|
||||
- **AnimationState** - Applies animations, handles mixing and layering
|
||||
- **AnimationStateData** - Stores mix (crossfade) durations
|
||||
|
||||
### Attachment Types (Visual Elements)
|
||||
- **Attachment** - Base class for all attachments
|
||||
- **RegionAttachment** - Textured quadrilateral
|
||||
- **MeshAttachment** - Textured mesh with vertices
|
||||
- **BoundingBoxAttachment** - Polygon for hit detection
|
||||
- **ClippingAttachment** - Polygon for clipping rendering
|
||||
- **PathAttachment** - Composite Bezier curve
|
||||
- **PointAttachment** - Single point with rotation
|
||||
- **SkeletonAttachment** - Nested skeleton
|
||||
|
||||
### Constraint Types
|
||||
- **IkConstraint** - Inverse kinematics (1-2 bones)
|
||||
- **TransformConstraint** - Copy/mix transform from target
|
||||
- **PathConstraint** - Follow path attachment
|
||||
- **PhysicsConstraint** - Apply physics simulation
|
||||
|
||||
### Pose System (new in 4.2+)
|
||||
- **Pose** - Interface for pose data
|
||||
- **BoneLocal/BonePose** - Local and world bone transforms
|
||||
- **Update** - Interface for updateable elements
|
||||
- **PosedActive/PosedData** - Base classes for runtime/setup data
|
||||
|
||||
### Other Core Types
|
||||
- **Event/EventData** - Animation events system
|
||||
- **Sequence** - Frame-by-frame texture sequences
|
||||
- **SkeletonBounds** - Hit detection utilities
|
||||
- **SkeletonLoader** - Base for JSON/Binary loaders
|
||||
@ -1,3 +1,6 @@
|
||||
- clean up logging, use chalk to do colored warnings/errors and make logging look very nice and informative (no emojis)
|
||||
- it's weird that method generation comes before array specialization. i'd assume method generation should get the specialization and use those to output the correct array type for return types and parameters and error if an array type is not specialized?
|
||||
- type extractor should also report typedefs like typedef long long PropertyId; so primitive type to some name, and we need to handle that in the codegen
|
||||
- Generate language bindings from spine-cpp-lite
|
||||
- Create a TypeScript-based generator that takes spine-cpp-lite.h and generates:
|
||||
- Swift bindings (matching current spine-ios output from spine-cpp-lite-codegen.py)
|
||||
|
||||
621
docs/todos/done/2025-07-08-02-50-52-unfuck-spine-cpp-lite.md
Normal file
621
docs/todos/done/2025-07-08-02-50-52-unfuck-spine-cpp-lite.md
Normal file
@ -0,0 +1,621 @@
|
||||
# Create spine-c-new - automated C wrapper generator for spine-cpp
|
||||
|
||||
**Status:** In Progress
|
||||
**Created:** 2025-01-08T02:50:52
|
||||
**Agent PID:** 11931
|
||||
|
||||
## Description
|
||||
Create a new project `spine-c-new` with a TypeScript-based code generator that parses spine-cpp headers and generates a C wrapper API. This will replace spine-cpp-lite but we'll keep the current spine-cpp-lite as reference. The new project will live in the spine-runtimes root folder and contain both the generator and generated code. It references spine-cpp in its CMake build.
|
||||
|
||||
### Key Paths
|
||||
- **spine-cpp headers**: `/spine-cpp/spine-cpp/include/spine/*.h`
|
||||
- **spine-cpp-lite reference**: `/spine-cpp/spine-cpp-lite/`
|
||||
- **New project location**: `/spine-c-new/` (create in spine-runtimes root)
|
||||
- **Type hierarchy reference**: `/docs/generate-type-hierarchy.js`
|
||||
|
||||
## Design
|
||||
|
||||
### Scope
|
||||
- **Include**: Every concrete type in spine-cpp (classes, enums, inner classes)
|
||||
- **Exclude**: Types and methods defined in exclusions.txt
|
||||
- **Custom**: Additional C-specific functionality in custom.h/.cpp
|
||||
|
||||
### File Organization
|
||||
Project structure:
|
||||
```
|
||||
spine-c-new/
|
||||
├── codegen/ # TypeScript generator
|
||||
│ ├── src/
|
||||
│ ├── exclusions.txt
|
||||
│ ├── rtti-bases.txt
|
||||
│ └── package.json
|
||||
├── src/
|
||||
│ ├── custom.h/.cpp # Custom C-specific code
|
||||
│ └── generated/ # Generated C wrapper code
|
||||
│ ├── bone.h/.c
|
||||
│ ├── animation.h/.c
|
||||
│ ├── animation-timeline.h/.c
|
||||
│ ├── animation-alpha-timeline.h/.c
|
||||
│ └── ... (one file pair per type)
|
||||
├── include/
|
||||
│ └── spine-c.h # Main header that includes all
|
||||
└── CMakeLists.txt
|
||||
|
||||
### Code Generation Rules
|
||||
|
||||
#### Opaque Types
|
||||
- All C++ classes exposed as opaque pointers using SPINE_OPAQUE_TYPE macro
|
||||
- Pattern: `typedef struct spine_<type>_wrapper {} spine_<type>_wrapper; typedef spine_<type>_wrapper* spine_<type>;`
|
||||
|
||||
#### Constructor Mapping
|
||||
- Generate multiple create functions for types with multiple constructors
|
||||
- Naming: `spine_<type>_create()`, `spine_<type>_create_with_<param_names>()`
|
||||
- Example: `spine_bone_create()`, `spine_bone_create_with_data(spine_bone_data data)`
|
||||
- Types without public constructors: No create functions generated
|
||||
- All types get `spine_<type>_dispose()` that cleans up wrapper and child objects
|
||||
|
||||
#### Method Mapping
|
||||
- Getters: `spine_<type>_get_<property>(spine_<type> obj)`
|
||||
- Setters: `spine_<type>_set_<property>(spine_<type> obj, <type> value)`
|
||||
- Methods: `spine_<type>_<method_name>(spine_<type> obj, params...)`
|
||||
- Include inherited methods (generate for each subclass)
|
||||
- Ignore operator overloads
|
||||
|
||||
#### Collection Handling
|
||||
Types may return Vector collections, e.g. `Vector<IkConstraint*>& SkeletonData::getIkConstraints()`. For these we generate:
|
||||
- `spine_skeleton_data_get_num_ik_constraints(spine_skeleton_data obj)` - Returns count
|
||||
- `spine_skeleton_data_get_ik_constraints(spine_skeleton_data obj)` - Returns array pointer
|
||||
- `spine_skeleton_data_ik_constraints_set(spine_skeleton_data obj, index, item)`
|
||||
- `spine_skeleton_data_ik_constraints_add(spine_skeleton_data obj, item)`
|
||||
- `spine_skeleton_data_ik_constraints_remove(spine_skeleton_data obj, index)`
|
||||
- `spine_skeleton_data_ik_constraints_clear(spine_skeleton_data obj)`
|
||||
|
||||
No memory management in add/remove - user handles via create/dispose
|
||||
|
||||
#### String Handling
|
||||
- All string returns use `const utf8*` (typedef for `const char*`)
|
||||
- String parameters accept `const utf8*`
|
||||
- All methods return `const String&` from C++ as `const utf8*`
|
||||
|
||||
#### RTTI Emulation
|
||||
Generate type enums and cast functions for base types specified in `rtti-bases.txt`.
|
||||
|
||||
**rtti-bases.txt format:**
|
||||
```
|
||||
# Base types that need RTTI emulation
|
||||
Attachment
|
||||
Constraint
|
||||
ConstraintData
|
||||
Pose
|
||||
Posed
|
||||
PosedData
|
||||
Timeline
|
||||
```
|
||||
|
||||
**Example for Attachment hierarchy:**
|
||||
```c
|
||||
// Generated enum (leaf types only, no VertexAttachment)
|
||||
typedef enum spine_attachment_type {
|
||||
SPINE_TYPE_ATTACHMENT_REGION = 0,
|
||||
SPINE_TYPE_ATTACHMENT_MESH,
|
||||
SPINE_TYPE_ATTACHMENT_BOUNDING_BOX,
|
||||
SPINE_TYPE_ATTACHMENT_CLIPPING,
|
||||
SPINE_TYPE_ATTACHMENT_PATH,
|
||||
SPINE_TYPE_ATTACHMENT_POINT,
|
||||
SPINE_TYPE_ATTACHMENT_SKELETON
|
||||
} spine_attachment_type;
|
||||
|
||||
// Generated is_type method with switch for RTTI checks
|
||||
spine_bool spine_attachment_is_type(spine_attachment attachment, spine_attachment_type type) {
|
||||
if (!attachment) return 0;
|
||||
Attachment* _attachment = (Attachment*)attachment;
|
||||
|
||||
switch (type) {
|
||||
case SPINE_TYPE_ATTACHMENT_REGION:
|
||||
return _attachment->getRTTI().instanceOf(RegionAttachment::rtti);
|
||||
case SPINE_TYPE_ATTACHMENT_MESH:
|
||||
return _attachment->getRTTI().instanceOf(MeshAttachment::rtti);
|
||||
case SPINE_TYPE_ATTACHMENT_BOUNDING_BOX:
|
||||
return _attachment->getRTTI().instanceOf(BoundingBoxAttachment::rtti);
|
||||
// ... etc for all types
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Generated cast methods (return NULL if not the correct type)
|
||||
spine_region_attachment spine_attachment_as_region_attachment(spine_attachment attachment) {
|
||||
if (!attachment) return NULL;
|
||||
Attachment* _attachment = (Attachment*)attachment;
|
||||
if (!_attachment->getRTTI().instanceOf(RegionAttachment::rtti)) return NULL;
|
||||
return (spine_region_attachment)attachment;
|
||||
}
|
||||
```
|
||||
|
||||
Apply same pattern to:
|
||||
- **Attachment**: RegionAttachment, MeshAttachment, BoundingBoxAttachment, ClippingAttachment, PathAttachment, PointAttachment, SkeletonAttachment
|
||||
- **Constraint**: IkConstraint, PathConstraint, PhysicsConstraint, Slider, TransformConstraint
|
||||
- **ConstraintData**: IkConstraintData, PathConstraintData, PhysicsConstraintData, SliderData, TransformConstraintData
|
||||
- **Pose**: BoneLocal, BonePose, IkConstraintPose, PathConstraintPose, PhysicsConstraintPose, SliderPose, SlotPose, TransformConstraintPose
|
||||
- **Posed**: Bone, Slot (skip PosedActive - intermediate)
|
||||
- **PosedData**: BoneData, SlotData
|
||||
- **Timeline**: Timeline base class plus all leaf types (AlphaTimeline, AttachmentTimeline, RotateTimeline, etc. - skip intermediates like BoneTimeline, CurveTimeline)
|
||||
|
||||
#### Exclusions File Format
|
||||
`exclusions.txt` defines both excluded types and methods:
|
||||
|
||||
```
|
||||
# Excluded types (whole type is skipped)
|
||||
type: SkeletonClipping
|
||||
type: Triangulator
|
||||
type: SpineObject
|
||||
|
||||
# Excluded methods (specific methods on otherwise included types)
|
||||
method: AnimationState::setListener
|
||||
method: AnimationState::addListener
|
||||
method: AnimationState::removeListener
|
||||
method: AnimationState::clearListeners
|
||||
method: Skeleton::updateCache
|
||||
```
|
||||
|
||||
Rules:
|
||||
- One exclusion per line
|
||||
- Comments start with #
|
||||
- Type exclusions: `type: <ClassName>`
|
||||
- Method exclusions: `method: <ClassName>::<methodName>`
|
||||
- If a type is excluded, all its methods are automatically excluded
|
||||
- Method names can be overloaded (excludes all overloads)
|
||||
|
||||
### Custom Code (from spine-cpp-lite)
|
||||
These components will be ported to spine-c-new/src/custom.h/.cpp from these locations:
|
||||
- Atlas loading with callback-based texture loading (`/spine-cpp/spine-cpp-lite/spine-cpp-lite.cpp:56-74`)
|
||||
- SkeletonDrawable implementation (`/spine-cpp/spine-cpp-lite/spine-cpp-lite.cpp:146-219`)
|
||||
- Result/error wrapper structs (`/spine-cpp/spine-cpp-lite/spine-cpp-lite.cpp:77-87`)
|
||||
- Extension management (`/spine-cpp/spine-cpp-lite/spine-cpp-lite.cpp:88-109`)
|
||||
- Helper types: bounds, vector, skin entries (`/spine-cpp/spine-cpp-lite/spine-cpp-lite.cpp:110-115`)
|
||||
- RenderCommand C API (`/spine-cpp/spine-cpp-lite/spine-cpp-lite.h:318-331`)
|
||||
- Skin manipulation special cases (`/spine-cpp/spine-cpp-lite/spine-cpp-lite.cpp:1509-1585`)
|
||||
- SPINE_OPAQUE_TYPE macro and other macros (`/spine-cpp/spine-cpp-lite/spine-cpp-lite.h:39-60`)
|
||||
|
||||
### Validation Strategy
|
||||
- Ensure all concrete types in spine-cpp are wrapped (except those in exclusions.txt)
|
||||
- Generate coverage report showing:
|
||||
- Which types were included
|
||||
- Which types were excluded and why
|
||||
- Which methods were excluded
|
||||
- Verify generated code compiles with spine-cpp
|
||||
- Test that spine-flutter and spine-ios can use the generated API
|
||||
|
||||
## Type Extraction with extract-spine-cpp-types.js
|
||||
|
||||
The `/spine-cpp/extract-spine-cpp-types.js` script provides ALL the information needed to generate C wrappers. Key features:
|
||||
|
||||
### Complete Type Information
|
||||
Each type in the output JSON contains:
|
||||
- **name**: Type name (e.g., "Bone", "Animation")
|
||||
- **kind**: "class", "struct", or "enum"
|
||||
- **superTypes**: Array of direct base classes (e.g., ["Posed", "PosedActive"])
|
||||
- **members**: ALL public members including:
|
||||
- Direct members defined in this type
|
||||
- ALL inherited members from the entire parent chain recursively
|
||||
- Template methods with proper parameter substitution
|
||||
- **isAbstract**: true if class has pure virtual methods
|
||||
- **isTemplate**: true for template classes
|
||||
- **templateParams**: Template parameter names (e.g., ["T", "U"])
|
||||
|
||||
### Inherited Member Resolution
|
||||
The script performs two passes:
|
||||
1. **Pass 1**: Extract all type definitions from all headers
|
||||
2. **Pass 2**: Add inherited methods with these features:
|
||||
- Methods from all ancestors are included (grandparent → parent → child)
|
||||
- Template inheritance is resolved (e.g., `Vector<Bone*>` → concrete types)
|
||||
- Each inherited method has `fromSupertype` field showing immediate source
|
||||
- SpineObject methods are excluded as noise
|
||||
- No need to manually track inheritance - it's all resolved!
|
||||
|
||||
### Example Type Entry
|
||||
```json
|
||||
{
|
||||
"name": "Bone",
|
||||
"kind": "class",
|
||||
"superTypes": ["Posed", "PosedActive"],
|
||||
"members": [
|
||||
// Direct members
|
||||
{
|
||||
"kind": "field",
|
||||
"name": "x",
|
||||
"type": "float"
|
||||
},
|
||||
{
|
||||
"kind": "method",
|
||||
"name": "setX",
|
||||
"returnType": "void",
|
||||
"parameters": [{"name": "x", "type": "float"}]
|
||||
},
|
||||
// Inherited from Posed
|
||||
{
|
||||
"kind": "method",
|
||||
"name": "getPosedData",
|
||||
"returnType": "PosedData *",
|
||||
"parameters": [],
|
||||
"fromSupertype": "Posed" // Shows this came from Posed
|
||||
},
|
||||
// Inherited from template supertype with substitution
|
||||
{
|
||||
"kind": "method",
|
||||
"name": "add",
|
||||
"returnType": "void",
|
||||
"parameters": [{"name": "item", "type": "Bone *"}], // T substituted with Bone*
|
||||
"fromSupertype": "Vector<Bone *>"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Usage for Wrapper Generation
|
||||
```bash
|
||||
# Generate complete type information for all spine-cpp headers
|
||||
cd /spine-cpp
|
||||
./extract-spine-cpp-types.js > all-spine-types.json
|
||||
```
|
||||
|
||||
The output JSON has structure:
|
||||
```json
|
||||
{
|
||||
"spine/Bone.h": [...types in Bone.h...],
|
||||
"spine/Animation.h": [...types in Animation.h...],
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
## Implementation Plan
|
||||
- [x] Create spine-c-new project structure:
|
||||
- [x] Create spine-c-new/ directory in spine-runtimes root
|
||||
- [x] Create subdirectories: codegen/, src/, src/generated/, include/
|
||||
- [x] Set up CMakeLists.txt for spine-c-new
|
||||
- [x] Port custom code from spine-cpp-lite:
|
||||
- [x] Copy and adapt custom code to spine-c-new/src/custom.h/.cpp
|
||||
- [x] Test compilation of custom code
|
||||
|
||||
## Notes
|
||||
- Fixed spine-cpp build error with TEXTURE_FILTER_ENUM by using correct enum name
|
||||
- Excluded spine-cpp-lite from spine-cpp CMakeLists.txt as it's incompatible with 4.3-beta API
|
||||
- Fixed spine-c-new custom code to be compatible with spine-cpp 4.3-beta:
|
||||
- Changed isPma() to pma (field access)
|
||||
- Updated readSkeletonData calls (no error parameter in 4.3)
|
||||
- Fixed Skeleton and AnimationStateData constructors (reference vs pointer)
|
||||
- Made EventListener inherit from both AnimationStateListenerObject and SpineObject for memory tracking
|
||||
- Code generator is working and created 150+ header/source file pairs
|
||||
- Generated code has some issues that need refinement:
|
||||
- Type forward declarations needed
|
||||
- Better handling of Vector<> types in parameters
|
||||
- Duplicate method removal
|
||||
- RTTI method type conversion fixes
|
||||
- **spine-cpp namespace removed**: All spine:: namespace prefixes have been removed from spine-cpp headers
|
||||
- Generator needs cleanup to remove all special handling for spine:: namespace
|
||||
- This simplifies type conversion significantly
|
||||
- [x] Set up TypeScript generator in spine-c-new/codegen/:
|
||||
- [x] Initialize npm project with TypeScript
|
||||
- [x] Create exclusions.txt for excluded types and methods
|
||||
- [x] Create rtti-bases.txt with base types needing RTTI emulation
|
||||
- [x] Implement type extraction:
|
||||
- [x] Run `/spine-cpp/extract-spine-cpp-types.js > all-spine-types.json`
|
||||
- [x] Load and parse the JSON (all inheritance already resolved!)
|
||||
- [x] Filter types based on exclusions.txt
|
||||
- [x] Identify which types need RTTI based on rtti-bases.txt
|
||||
- [x] Implement code generators:
|
||||
- [x] Opaque type declarations generator
|
||||
- [x] Create/dispose functions generator (handle multiple constructors)
|
||||
- [x] Getter/setter generator (generate for all fields)
|
||||
- [x] Method wrapper generator (all methods including inherited are in members array)
|
||||
- [x] Collection access generator (detect Vector<> return types)
|
||||
- [x] RTTI emulation generator (check superTypes against rtti-bases.txt)
|
||||
- [x] Enum wrapper generator (use values array)
|
||||
- [x] Implement file writer:
|
||||
- [x] Generate one .h/.c pair per type in spine-c-new/src/generated/
|
||||
- [x] Generate main spine-c.h in spine-c-new/include/
|
||||
- [x] Configure CMakeLists.txt glob patterns for src/ and src/generated/
|
||||
- [x] Validate coverage:
|
||||
- [x] Generate coverage report of all types in spine-cpp
|
||||
- [x] Verify all concrete types are included (except exclusions)
|
||||
- [x] Review excluded methods to ensure nothing important is missed
|
||||
- [ ] Add any missing exclusions to exclusions.txt
|
||||
- [ ] Integration testing:
|
||||
- [ ] Automated test: Compile spine-c-new
|
||||
- [ ] User test: Run full generation on current spine-cpp
|
||||
- [ ] User test: Verify generated code is according to specs
|
||||
|
||||
### Key Implementation Notes
|
||||
1. **No manual inheritance tracking needed** - extract-spine-cpp-types.js provides complete member lists
|
||||
2. **Template inheritance is pre-resolved** - Vector<Bone*> methods already have Bone* substituted
|
||||
3. **Abstract classes are marked** - Check `isAbstract` field
|
||||
4. **All constructors are listed** - Filter members where `kind === "constructor"`
|
||||
5. **Method overrides are handled** - Script ensures no duplicates in inheritance
|
||||
6. **Collection detection** - Check return types and parameters for `Vector<` patterns
|
||||
7. **RTTI hierarchy** - Use superTypes array to build complete inheritance tree
|
||||
|
||||
## Current Problem: Header Dependencies and Duplicate Methods
|
||||
|
||||
### Issues
|
||||
1. Generated headers fail to compile because they use types without proper declarations/includes
|
||||
2. Duplicate method declarations (C++ overloaded methods causing conflicts)
|
||||
3. Type name conflicts between generated types and custom types
|
||||
4. Missing forward declarations
|
||||
|
||||
### Solution: Single types.h File
|
||||
Create a single `types.h` file that:
|
||||
1. Forward declares all non-enum types using `SPINE_OPAQUE_TYPE` macro
|
||||
2. Includes all enum header files (enums can't be forward declared)
|
||||
3. Gets included by all generated .h and .cpp files
|
||||
|
||||
This eliminates the need for complex dependency analysis and ensures all types are available everywhere.
|
||||
|
||||
### Implementation Steps
|
||||
- [x] Remove DependencyAnalyzer - no longer needed
|
||||
- [x] Create types.h generator:
|
||||
- [x] Collect all parsed types from all headers
|
||||
- [x] Generate forward declarations for all class/struct types
|
||||
- [x] Generate includes for all enum header files
|
||||
- [x] Place in src/generated/types.h
|
||||
- [x] Update FileWriter:
|
||||
- [x] Include types.h in all generated headers
|
||||
- [x] Include types.h in all generated source files
|
||||
- [x] Remove any other type includes
|
||||
- [x] Fix duplicate method issues:
|
||||
- [x] When generating Vector getters, skip the raw getter
|
||||
- [x] Track method signatures to prevent duplicates
|
||||
- [x] Update spine-c.h:
|
||||
- [x] Include types.h before other headers
|
||||
|
||||
## Revised Approach: Remove Custom RTTI System
|
||||
|
||||
### Reasoning
|
||||
Originally, we were generating custom enum-based RTTI emulation for polymorphic types. However, since we're now exposing spine::RTTI as an opaque type (spine_rtti), we can directly wrap the C++ RTTI system instead of creating a parallel one. This simplifies the implementation significantly.
|
||||
|
||||
### Benefits
|
||||
1. **No duplicate RTTI system** - Use spine-cpp's existing RTTI directly
|
||||
2. **Simpler code generation** - No need for RttiGenerator or enum generation
|
||||
3. **Better compatibility** - Direct pass-through to C++ RTTI
|
||||
4. **Cleaner API** - RTTI methods become static (no object parameter needed)
|
||||
|
||||
### Changes Required
|
||||
1. **Remove RttiGenerator completely** - No longer needed
|
||||
2. **Remove spine_size_t typedef** - Unnecessary abstraction, use size_t directly
|
||||
3. **Fix RTTI getter methods**:
|
||||
- Change from: `spine_rtti spine_scale_timeline_get_rtti(spine_scale_timeline obj)`
|
||||
- To: `spine_rtti spine_scale_timeline_get_rtti()` (static, returns ScaleTimeline::rtti)
|
||||
4. **Exclude spine_rtti create/dispose** - RTTI objects are static singletons
|
||||
5. **Remove type enums** - No longer needed with direct RTTI access
|
||||
|
||||
### Implementation
|
||||
- [x] Remove RttiGenerator from codegen
|
||||
- [x] Remove spine_size_t from custom.h
|
||||
- [x] Update method generator to make get_rtti() methods static
|
||||
- [x] Add exclusion for spine_rtti create/dispose
|
||||
- [x] Remove RTTI enum generation from all types
|
||||
|
||||
## Header Organization Problem
|
||||
|
||||
### Issue
|
||||
The custom.h file contains both:
|
||||
1. Basic type definitions and macros that don't depend on generated types
|
||||
2. Extension functions that use generated types (spine_atlas, spine_color, etc.)
|
||||
|
||||
This creates a circular dependency problem when types.h tries to include custom.h.
|
||||
|
||||
### Solution
|
||||
Split custom.h into two files:
|
||||
1. **base.h** - Contains only:
|
||||
- Export macros (SPINE_C_EXPORT)
|
||||
- SPINE_OPAQUE_TYPE macro
|
||||
- Basic typedefs (utf8, spine_bool)
|
||||
- Non-generated custom types (spine_skeleton_data_result, etc.)
|
||||
|
||||
2. **extensions.h** - Contains all extension functions that depend on generated types:
|
||||
- Color functions
|
||||
- Bounds functions
|
||||
- Atlas functions
|
||||
- Skeleton data functions
|
||||
- Render command functions
|
||||
- Skin functions
|
||||
|
||||
### Include Order
|
||||
The main spine-c.h header will include in this order:
|
||||
1. base.h (basic definitions)
|
||||
2. types.h (all forward declarations and enum includes)
|
||||
3. extensions.h (extension functions that use the types)
|
||||
4. All generated type headers
|
||||
|
||||
This ensures all types are declared before being used.
|
||||
|
||||
### Implementation Steps
|
||||
- [x] Create base.h with content from custom.h up to spine_bool typedef
|
||||
- [x] Create extensions.h with remaining content from custom.h
|
||||
- [x] Rename custom.cpp to extensions.cpp
|
||||
- [x] Update types.h to include base.h instead of custom.h
|
||||
- [x] Update FileWriter to generate includes for base.h instead of custom.h
|
||||
- [x] Update spine-c.h to include files in correct order: base.h, types.h, extensions.h
|
||||
- [x] Delete custom.h
|
||||
- [x] Regenerate code and test build
|
||||
|
||||
## Current Issues: Type System and Code Generation
|
||||
|
||||
### Problems
|
||||
1. **Unstructured type conversion** - toCTypeName has too many special cases
|
||||
2. **Unclear inclusion/exclusion rules** - We're patching issues as they arise
|
||||
3. **Abstract class detection** - Need automatic detection of classes with unimplemented pure virtuals
|
||||
4. **Template class handling** - Should be automatically excluded
|
||||
5. **Array (formerly Vector) handling** - Need proper C API exposure
|
||||
6. **Const/non-const method conflicts** - C++ patterns incompatible with C
|
||||
|
||||
### Root Cause
|
||||
We've been adding patches without a systematic approach:
|
||||
- Manual exclusions for template classes (should be auto-detected)
|
||||
- Manual exclusions for abstract classes that inherit pure virtuals
|
||||
- Special cases in type conversion without clear categories
|
||||
- Hidden Array/Vector types behind void* instead of proper wrappers
|
||||
|
||||
## Proposed Solution: Systematic Type Handling
|
||||
|
||||
### 1. Type Inclusion/Exclusion Rules
|
||||
|
||||
**Auto-exclude:**
|
||||
- Template classes (detected by isTemplate field)
|
||||
- Internal utility classes (Array, String, HashMap, etc.)
|
||||
|
||||
**Auto-include with limitations:**
|
||||
- Abstract classes (has pure virtuals or inherits unimplemented ones)
|
||||
- Include type definition (users need to work with pointers)
|
||||
- No create methods
|
||||
- Include dispose method (if applicable)
|
||||
- Concrete classes - full API
|
||||
|
||||
### 2. Array Type Specializations
|
||||
|
||||
spine-cpp changed Vector to Array. Generate concrete wrapper types for each Array specialization found in spine-cpp:
|
||||
|
||||
**Implementation approach:**
|
||||
1. Scan all spine-cpp types and their members to enumerate Array<T> usage
|
||||
2. Generate specialized wrappers in `src/generated/arrays.h` and `arrays.cpp`
|
||||
3. Include `arrays.h` at the bottom of `types.h`
|
||||
4. Generated methods can then use these specialized array types
|
||||
|
||||
```c
|
||||
// In generated/arrays.h
|
||||
|
||||
// Primitive arrays
|
||||
typedef struct spine_array_float spine_array_float;
|
||||
typedef struct spine_array_int32_t spine_array_int32_t;
|
||||
typedef struct spine_array_uint8_t spine_array_uint8_t;
|
||||
|
||||
// Object arrays
|
||||
typedef struct spine_array_attachment spine_array_attachment;
|
||||
typedef struct spine_array_bone spine_array_bone;
|
||||
typedef struct spine_array_track_entry spine_array_track_entry;
|
||||
// ... etc for all Array<T*> found
|
||||
|
||||
// Full API for each specialization
|
||||
spine_array_float spine_array_float_create();
|
||||
spine_array_float spine_array_float_create_with_capacity(int32_t capacity);
|
||||
void spine_array_float_dispose(spine_array_float arr);
|
||||
float spine_array_float_get(spine_array_float arr, int32_t index);
|
||||
void spine_array_float_set(spine_array_float arr, int32_t index, float value);
|
||||
float* spine_array_float_buffer(spine_array_float arr);
|
||||
int32_t spine_array_float_size(spine_array_float arr);
|
||||
int32_t spine_array_float_capacity(spine_array_float arr);
|
||||
void spine_array_float_add(spine_array_float arr, float value);
|
||||
void spine_array_float_clear(spine_array_float arr);
|
||||
void spine_array_float_ensure_capacity(spine_array_float arr, int32_t capacity);
|
||||
// etc...
|
||||
```
|
||||
|
||||
### 3. Systematic Type Conversion
|
||||
|
||||
Instead of special cases, classify types first:
|
||||
|
||||
**Categories:**
|
||||
1. **Primitives**: int, float, double, bool, char, void, size_t
|
||||
2. **Pointers**: Based on what they point to
|
||||
3. **References**: Based on const-ness and type
|
||||
4. **Arrays**: Array<T> specializations
|
||||
5. **Enums**: Known spine enums
|
||||
6. **Classes**: Spine types
|
||||
|
||||
**Conversion rules by category:**
|
||||
- Primitives: Direct mapping (bool → spine_bool)
|
||||
- Primitive pointer: Direct (float* → float*)
|
||||
- Class pointer: ClassName* → spine_class_name
|
||||
- Const class ref: const T& → spine_t
|
||||
- Non-const primitive ref: T& → T* (output param)
|
||||
- Array<T>: → spine_array_t
|
||||
- Enum: EnumName → spine_enum_name
|
||||
|
||||
### 4. Const/Non-Const Method Handling
|
||||
|
||||
Implemented support for excluding specific const/non-const versions of methods:
|
||||
- Exclusion format: `method: Type::method const` (excludes only const version)
|
||||
- Generator detects const methods by return type pattern (e.g., `const T&`)
|
||||
- Automatically reports all const/non-const conflicts before failing
|
||||
- Currently excluding const versions of getSetupPose() methods
|
||||
|
||||
### 5. Automatic Type Extraction
|
||||
|
||||
The generator should automatically run `extract-spine-cpp-types.js` to ensure we have the latest type information:
|
||||
|
||||
1. **Rename output file**: `all-spine-types.json` → `spine-cpp-types.json`
|
||||
2. **Auto-generate on demand**:
|
||||
- Check if `spine-cpp-types.json` exists
|
||||
- If not, run `extract-spine-cpp-types.js`
|
||||
- If it exists, check timestamps:
|
||||
- Get newest modification time of all `/spine-cpp/spine-cpp/include/spine/*.h` files
|
||||
- If any header is newer than `spine-cpp-types.json`, regenerate
|
||||
3. **Separate extraction step**:
|
||||
- Create a dedicated function/module for type extraction
|
||||
- Run at the start of code generation
|
||||
- Log when regenerating vs using cached data
|
||||
|
||||
### Implementation Progress
|
||||
|
||||
#### Completed Tasks (2025-01-08):
|
||||
1. **Systematic Type Handling**: Completely refactored `toCTypeName` with proper type classification instead of ad-hoc special cases
|
||||
2. **Namespace Removal**: Removed all spine:: namespace handling from the code generator
|
||||
3. **Array Generation**: Implemented automatic generation of Array specializations with proper type handling
|
||||
4. **Bug Fixes**: Fixed class/struct prefix handling in array element types that was causing incorrect type names
|
||||
|
||||
The code generator now runs successfully without namespace handling and generates proper array specializations for all Array<T> types found in spine-cpp.
|
||||
|
||||
### Implementation Steps
|
||||
|
||||
1. **Implement automatic type extraction**:
|
||||
- [x] Create type extraction module (type-extractor.ts)
|
||||
- [x] Check file timestamps for regeneration
|
||||
- [x] Run extract-spine-cpp-types.js when needed
|
||||
- [x] Rename output to spine-cpp-types.json
|
||||
- [x] Integrated into main generator - runs automatically
|
||||
|
||||
2. **Update type extractor** (if needed):
|
||||
- [x] Already extracts isTemplate and isAbstract
|
||||
- [x] Run updated extractor with Array instead of Vector
|
||||
- [ ] Ensure it detects Array usage patterns
|
||||
|
||||
3. **Implement abstract class detection**:
|
||||
- [x] Auto-detect classes with unimplemented pure virtuals
|
||||
- [x] Remove manual exclusions for these (auto-detected now)
|
||||
|
||||
4. **Implement Array specialization generator**:
|
||||
- [x] Enumerate all Array<T> usage in spine-cpp types
|
||||
- Found 36 Array specializations:
|
||||
- Primitive: float, int, unsigned short
|
||||
- Enums: PropertyId
|
||||
- Special: String (skip with warning), nested arrays (skip with warning)
|
||||
- Pointers: 27 object pointer types
|
||||
- [x] Create array generator that:
|
||||
- Only processes Arrays from non-excluded types (filters template placeholders)
|
||||
- Generates specializations for: primitives, enums, pointer types
|
||||
- Emits warnings for: String arrays, nested arrays
|
||||
- Automatically generates methods from Array type definition
|
||||
- Adds hardcoded get/set methods for element access
|
||||
- [x] Generate arrays.h/arrays.cpp with all specializations
|
||||
- [x] Include arrays.h at bottom of types.h
|
||||
- [x] Update toCTypeName to handle Array types
|
||||
- [x] Fix class/struct prefix handling in array element types
|
||||
|
||||
5. **Refactor toCTypeName**:
|
||||
- [x] Implement type classification
|
||||
- [x] Apply systematic rules per category
|
||||
- [x] Remove special cases
|
||||
|
||||
6. **Update method generation**:
|
||||
- [x] Handle const/non-const method conflicts systematically
|
||||
- [ ] Generate proper Array method signatures
|
||||
|
||||
7. **Clean up exclusions.txt**:
|
||||
- [x] Remove manually excluded template/abstract types (now auto-detected)
|
||||
- [x] Keep only API design exclusions and const method exclusions
|
||||
|
||||
8. **Remove spine:: namespace handling**:
|
||||
- [x] Remove all spine:: prefix handling in toCTypeName
|
||||
- [x] Remove spine:: handling in method generator
|
||||
- [x] Remove spine:: handling in enum detection
|
||||
- [x] Update array-specializations.ts to remove EventData special case
|
||||
- [x] Clean up any other namespace-related special cases
|
||||
- [x] Test that code generation works without namespace handling
|
||||
39
docs/todos/done/2025-07-08-22-03-22-spine-bool-stdbool.md
Normal file
39
docs/todos/done/2025-07-08-22-03-22-spine-bool-stdbool.md
Normal file
@ -0,0 +1,39 @@
|
||||
# spine-c-new codegen should not special case spine_bool but use stdbool instead
|
||||
|
||||
**Status:** Refining
|
||||
**Created:** 2025-07-08T22:03:22
|
||||
**Agent PID:** 23353
|
||||
|
||||
## Original Todo
|
||||
- spine-c-new codegen should not special case spine_bool but use stdbool instead.
|
||||
|
||||
## Description
|
||||
The spine-c-new codegen currently defines `spine_bool` as `int32_t` for C compatibility. This task involves updating the codegen to use the standard C99 `bool` type from `<stdbool.h>` instead of the custom `spine_bool` typedef. This will make the C API more modern and standard-compliant.
|
||||
|
||||
Currently:
|
||||
- `spine_bool` is typedef'd as `int32_t` in base.h
|
||||
- The codegen maps C++ `bool` to `spine_bool` throughout
|
||||
- All generated code uses `spine_bool` instead of standard `bool`
|
||||
|
||||
The change will:
|
||||
- Use standard C99 `bool` from `<stdbool.h>`
|
||||
- Remove the special-casing in the codegen
|
||||
- Make the API more standards-compliant
|
||||
|
||||
## Implementation Plan
|
||||
- [ ] Update typedef in base.h to use stdbool.h instead of int32_t (spine-c-new/src/base.h:65)
|
||||
- [ ] Update type mapping in types.ts from 'spine_bool' to 'bool' (spine-c-new/codegen/src/types.ts:70)
|
||||
- [ ] Update pointer type handling for bool* in types.ts (spine-c-new/codegen/src/types.ts:148-149)
|
||||
- [ ] Update reference type handling for bool& in types.ts (spine-c-new/codegen/src/types.ts:176)
|
||||
- [ ] Update array element mapping in array-scanner.ts (spine-c-new/codegen/src/array-scanner.ts:103)
|
||||
- [ ] Update return type check in array-generator.ts (spine-c-new/codegen/src/generators/array-generator.ts:148)
|
||||
- [ ] Update default values for bool in array-generator.ts (spine-c-new/codegen/src/generators/array-generator.ts:209,216)
|
||||
- [ ] Update default return value check in method-generator.ts (spine-c-new/codegen/src/generators/method-generator.ts:346)
|
||||
- [ ] Run codegen to regenerate all C wrapper code
|
||||
- [ ] Update test file to ensure bool types work correctly (spine-c-new/test/test.c)
|
||||
- [ ] Automated test: Run codegen and verify it completes without errors
|
||||
- [ ] Automated test: Verify generated code compiles with C compiler
|
||||
- [ ] User test: Build spine-c-new with cmake and verify compilation succeeds
|
||||
- [ ] User test: Run test.c to verify bool values work correctly (true/false instead of 1/0)
|
||||
|
||||
## Notes
|
||||
@ -0,0 +1,184 @@
|
||||
# Review spine-c-new codegen
|
||||
|
||||
**Status:** In Progress
|
||||
**Created:** 2025-07-08T22:31:47
|
||||
**Agent PID:** 40566
|
||||
|
||||
## Context for Resumption
|
||||
**Read these files in full into your context**
|
||||
1. `/Users/badlogic/workspaces/spine-runtimes/spine-c-new/codegen/README.md` - Description of codegen
|
||||
|
||||
## Description
|
||||
Interactive review of the spine-c-new code generator with user confirmation at each step. Going through each file together to understand the code and identify issues or improvements.
|
||||
|
||||
## Loop
|
||||
- For each file in codegen:
|
||||
- Pick next file (check for incomplete sections, or ask user)
|
||||
- Add ## filename section in task.md if non-existant
|
||||
- Open file in VS Code via vs-claude
|
||||
- STOP and ask user to review file and provide feedback
|
||||
- Add checkbox for each issue reported by user in file's section
|
||||
- When user says implement, work through the checkboxes in the file's section
|
||||
- Mark section (complete) when done
|
||||
- Continue to next file
|
||||
|
||||
## index.ts (complete)
|
||||
- [x] Fix extract-spine-cpp-types.js - `isAbstract` should ALWAYS be true/false, never null (currently 134/149 classes have null)
|
||||
- [x] Fix extract-spine-cpp-types.js to check inherited pure methods after inheritance pass
|
||||
- [x] After fixing extraction, simplify index.ts to just check `type.isAbstract === true`
|
||||
- [x] Remove `isAbstractType`, `getPureVirtualMethods` and `implementsMethod` functions from index.ts
|
||||
- [x] Fix type filtering logic:
|
||||
- Only exclude template classes (check `type.isTemplate === true`)
|
||||
- Include abstract classes (check `type.isAbstract === true` but don't generate create() for them)
|
||||
- Include everything else
|
||||
- Apply exclusions filter after this base filtering
|
||||
- [x] Remove unused `opaqueTypeGen` variable (line 135)
|
||||
- [x] Extract const/non-const conflict checking (lines 141-198) into its own function `checkConstNonConstConflicts` with proper documentation
|
||||
|
||||
## exclusions.ts (complete)
|
||||
- [x] Change Exclusion type to be a discriminated union: `{ kind: 'type', typeName: string } | { kind: 'method', typeName: string, methodName: string, isConst?: boolean }`
|
||||
- [x] Document in the Exclusion type definition (in types.ts) that `isConst` refers to method const (i.e., `void meh() const`), not return type const
|
||||
- [x] Remove unnecessary logging of `isConst` in parseExclusion
|
||||
- [x] Add documentation to loadExclusions function describing its purpose and the exclusions.txt file format
|
||||
- [x] Fix isMethodExcluded to check method.isConst instead of looking at return type for const - the current logic is wrong
|
||||
|
||||
## types.ts (complete)
|
||||
|
||||
### Clean up type interfaces
|
||||
- [x] Update Member to discriminated union: `{kind: 'field', name, type, ...} | {kind: 'method', name, returnType, parameters, ...} | ...`
|
||||
- [x] Remove unused Parameter.defaultValue field
|
||||
- [x] Audit all interface fields against actual JSON output
|
||||
|
||||
### Rewrite type extraction in TypeScript (complete)
|
||||
- [x] Port extract-spine-cpp-types.js to TypeScript using the cleaned-up interfaces
|
||||
- [x] Verify identical output with original script
|
||||
|
||||
### Simplify toCTypeName logic
|
||||
- [x] Remove all special type mappings: primitives pass through unchanged, no utf8, no spine_void
|
||||
- [x] Implement isPrimitive(): tokenize by whitespace, check if ALL tokens start with lowercase
|
||||
- [x] Make toCTypeName strict: error if type isn't primitive or in filtered types list
|
||||
- [x] Remove redundant enum check (same output as class handling)
|
||||
- [x] Investigate function pointer special cases: TextureLoader is a class (should be handled normally), DisposeRendererObject is a function pointer typedef (needs proper handling)
|
||||
|
||||
### Documentation
|
||||
- [x] Document all conversion functions with examples
|
||||
|
||||
### Other files
|
||||
- [x] Include stdint.h in base.h (already included)
|
||||
- [x] Remove utf8 typedef from base.h
|
||||
|
||||
## array-scanner.ts (complete)
|
||||
- [x] Fix cTypeName for multi-word primitive types like "unsigned short" - should be "spine_array_unsigned_short" not "spine_array_unsigned short"
|
||||
- [x] Add proper typing for exclusions parameter (use Exclusion[] type)
|
||||
- [x] Move ArraySpecialization interface to types.ts
|
||||
- [x] Move extractArrayTypes function to top-level scope (not inside scanArraySpecializations)
|
||||
- [x] Actually pass includedTypes to scanArraySpecializations in index.ts since we skip excluded types anyway - remove the redundant exclusion check
|
||||
- [x] Remove enumTypes parameter - use includedTypes to get all enums instead
|
||||
- [x] Add reference to source Member in ArraySpecialization interface (for debugging)
|
||||
- [x] Make template parameter check more robust - if parent type is template, use its templateParams to check if element type is a template param
|
||||
- [x] Use isPrimitive from types.ts instead of local implementation
|
||||
- [x] Remove "Map primitive types" logic - just use primitive type name as-is and replace whitespace with _ for cTypeName
|
||||
- [x] cElementType.replace(/_t$/, '') - already removed, wasn't needed
|
||||
- [x] Move nested arrays and string arrays handling to else branch "unknown type - skip"
|
||||
- [x] Better document why we can't handle nested arrays and string arrays (minimal inline comments)
|
||||
- [x] Warning should show full path to problematic array type (cppType + member path)
|
||||
- [x] Update extractArrayTypes to directly modify arrayTypes Map with proper typing: Map<string, {type: Type, member: Member}[]>
|
||||
- [x] Fix handling of primitive pointer types like Array<float*> - should keep pointer in cElementType and generate unique cTypeName
|
||||
- [x] Skip multi-level pointers (e.g., Array<float**>) as unsupported
|
||||
- [x] Log found specializations after warnings for debugging
|
||||
- [x] Sort specializations: primitives first, enums next, then pointers
|
||||
- [x] Add check for const element types (e.g., Array<const Event*>) and error - arrays should never have const element types
|
||||
- [x] Warn about Array<T> where T is a template parameter - show which template classes use generic arrays
|
||||
- [x] Extract warnings logic from array-scanner into warnings.ts for reuse by other generators
|
||||
|
||||
## index.ts (continued)
|
||||
- [x] Add function to check all method return and parameter types for multi-level pointers (e.g., **) and error out early
|
||||
|
||||
## array-generator.ts
|
||||
- [ ] Remove unused typesJson field - it's passed to constructor but never used as a field
|
||||
- [ ] Remove spine/Array.h include since spine/spine.h already includes it
|
||||
- [ ] Remove Array/~Array constructor/destructor check - Member now has explicit constructor/destructor types
|
||||
- [ ] Fix template parameter replacement - Array methods are templated, need to replace T with actual element type
|
||||
- [ ] **MAJOR REFACTOR**: Extract common method/constructor generation logic to avoid duplication between generators
|
||||
|
||||
## Architecture Refactor - Intermediate Representation
|
||||
|
||||
### Core Types (add to c-types.ts):
|
||||
```typescript
|
||||
interface CParameter {
|
||||
name: string; // Parameter name in C
|
||||
cType: string; // C type (e.g., "float*", "spine_bone")
|
||||
cppType: string; // Original C++ type (e.g., "float&", "Bone*")
|
||||
isOutput: boolean; // true for non-const references that become output params
|
||||
}
|
||||
|
||||
interface CMethod {
|
||||
name: string; // Full C function name (e.g., "spine_bone_get_x")
|
||||
returnType: string; // C return type
|
||||
parameters: CParameter[];
|
||||
body: string; // The actual implementation code (e.g., "return ((Bone*)self)->getX();")
|
||||
}
|
||||
|
||||
interface CClassOrStruct {
|
||||
name: string; // C type name (e.g., "spine_bone")
|
||||
cppType: ClassOrStruct; // Original C++ type info
|
||||
constructors: CMethod[]; // All constructors (including default)
|
||||
destructor: CMethod; // Always present (calls delete)
|
||||
methods: CMethod[]; // All methods
|
||||
}
|
||||
|
||||
interface CEnumValue {
|
||||
name: string; // C enum value name (e.g., "SPINE_BLEND_MODE_NORMAL")
|
||||
value?: string; // Optional explicit value
|
||||
}
|
||||
|
||||
interface CEnum {
|
||||
name: string; // C type name (e.g., "spine_blend_mode")
|
||||
cppType: Enum; // Original C++ enum info
|
||||
values: CEnumValue[]; // Converted enum values
|
||||
}
|
||||
```
|
||||
|
||||
### Generators produce C types:
|
||||
- Each generator analyzes its Type and produces CClassOrStruct/CEnum with all methods/values
|
||||
- No more direct string generation in generators
|
||||
- Clear separation between "what to generate" and "how to format it"
|
||||
|
||||
### CWriter (new file: c-writer.ts):
|
||||
```typescript
|
||||
class CWriter {
|
||||
writeClassHeader(type: CClassOrStruct): string {
|
||||
// Generate all function declarations
|
||||
// Handle SPINE_C_API macros
|
||||
// Format with proper spacing
|
||||
}
|
||||
|
||||
writeClassSource(type: CClassOrStruct): string {
|
||||
// Generate all function implementations
|
||||
// Just output the body from each CMethod
|
||||
}
|
||||
|
||||
writeEnumHeader(enumType: CEnum): string {
|
||||
// Generate enum declaration
|
||||
}
|
||||
|
||||
private writeMethodDeclaration(method: CMethod): string
|
||||
private writeMethodImplementation(method: CMethod): string
|
||||
```
|
||||
|
||||
### Refactoring Steps:
|
||||
- [ ] Create c-types.ts with IR types
|
||||
- [ ] Create c-writer.ts with CWriter class
|
||||
- [ ] Create ir-generator.ts with functions to convert Type → C types:
|
||||
- [ ] `generateConstructors(type: ClassOrStruct): CMethod[]`
|
||||
- [ ] `generateDestructor(type: ClassOrStruct): CMethod`
|
||||
- [ ] `generateMethods(type: ClassOrStruct): CMethod[]`
|
||||
- [ ] `generateArrayMethods(elementType: string, cTypeName: string, arrayType: ClassOrStruct): CMethod[]`
|
||||
- [ ] `generateEnum(enumType: Enum): CEnum`
|
||||
- [ ] Update index.ts to use new architecture:
|
||||
- [ ] Generate CClassOrStruct for each class/struct type
|
||||
- [ ] Generate CEnum for each enum type
|
||||
- [ ] Pass to CWriter to generate code
|
||||
- [ ] For arrays, generate specialized CClassOrStruct with array methods
|
||||
- [ ] Delete old generators after verification
|
||||
- [ ] Verify output is byte-for-byte identical to current output
|
||||
Loading…
x
Reference in New Issue
Block a user