mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-03-26 22:49:01 +08:00
346 lines
14 KiB
Markdown
346 lines
14 KiB
Markdown
# Analysis for Haxe Serializer Generator Implementation
|
|
|
|
## Agent 1: Haxe Plan Analysis
|
|
|
|
I've read the complete plan for adding Haxe serializer generator support to the Spine runtime testing infrastructure. Here's a comprehensive summary of the plan:
|
|
|
|
## Overview
|
|
|
|
The plan outlines implementing Haxe support for the Spine runtime cross-compatibility testing system. The goal is to generate a Haxe serializer that produces identical JSON output to existing Java and C++ serializers, enabling comprehensive cross-runtime testing.
|
|
|
|
## Current System Architecture
|
|
|
|
The existing system has 4 layers:
|
|
|
|
1. **SerializerIR Generation** - Analyzes Java API and creates intermediate representation
|
|
2. **Language-Specific Generators** - Currently Java and C++, missing Haxe
|
|
3. **HeadlessTest Applications** - Console apps for each runtime, missing Haxe version
|
|
4. **Test Runner** - Orchestrates builds and compares outputs, needs Haxe support
|
|
|
|
## Key Components to Implement
|
|
|
|
### 1. Haxe Serializer Generator (`tests/src/generate-haxe-serializer.ts`)
|
|
- Transforms Java types to Haxe equivalents (e.g., `String` → `String`, `int` → `Int`, `boolean` → `Bool`)
|
|
- Maps Java getter methods to Haxe field access (e.g., `getName()` → `obj.name`)
|
|
- Handles abstract types using `Std.isOfType()` (Haxe's instanceof equivalent)
|
|
- Generates cycle detection and reference tracking
|
|
- Supports all property types: primitives, objects, enums, arrays, nested arrays
|
|
|
|
### 2. JsonWriter Helper Class (`spine-haxe/spine-haxe/spine/utils/JsonWriter.hx`)
|
|
- Provides structured JSON output with proper formatting
|
|
- Handles object/array nesting with context tracking
|
|
- Implements string escaping for JSON compliance
|
|
- Ensures consistent float formatting across runtimes
|
|
|
|
### 3. Haxe HeadlessTest Application (`spine-haxe/tests/HeadlessTest.hx`)
|
|
- Console application that loads skeleton data and atlas files
|
|
- Uses mock texture loader for headless operation
|
|
- Supports both JSON and binary skeleton formats
|
|
- Can optionally apply animations before serialization
|
|
- Outputs structured JSON for skeleton data, skeleton state, and animation state
|
|
|
|
### 4. Build Integration
|
|
- Build script (`spine-haxe/build-headless-test.sh`) compiles to C++ for performance
|
|
- Test runner integration adds Haxe as supported language
|
|
- Automatic build detection and incremental compilation
|
|
- Generator script updates to include Haxe serializer generation
|
|
|
|
## Technical Considerations
|
|
|
|
### Type Mapping
|
|
- Java primitives map to Haxe equivalents
|
|
- Java arrays (`T[]`) become Haxe `Array<T>`
|
|
- Object types strip package prefixes
|
|
- Enum handling uses `Type.enumConstructor()` for string representation
|
|
|
|
### Property Access
|
|
- Java getters (`getName()`) map to Haxe fields (`obj.name`)
|
|
- Boolean methods (`isActive()`) map to boolean fields (`obj.active`)
|
|
- Direct field access when appropriate
|
|
|
|
### Cross-Runtime Consistency
|
|
- Identical JSON structure across all runtimes
|
|
- Consistent float formatting (C locale style)
|
|
- Same reference tracking and cycle detection
|
|
- Unified enum value representation
|
|
|
|
## File Structure
|
|
|
|
```
|
|
spine-haxe/
|
|
├── spine-haxe/spine/utils/
|
|
│ ├── SkeletonSerializer.hx (generated from IR)
|
|
│ └── JsonWriter.hx (helper class)
|
|
├── tests/
|
|
│ └── HeadlessTest.hx (console application)
|
|
├── build-headless-test.sh (build script)
|
|
└── build/headless-test/ (compiled executable)
|
|
|
|
tests/src/
|
|
├── generate-haxe-serializer.ts (new generator)
|
|
└── headless-test-runner.ts (updated with Haxe support)
|
|
```
|
|
|
|
## Testing and Validation
|
|
|
|
The implementation includes:
|
|
- Automatic compilation validation during generation
|
|
- Cross-runtime JSON output comparison
|
|
- Support for existing skeleton test files
|
|
- Integration with CI pipeline for continuous validation
|
|
|
|
## Expected Benefits
|
|
|
|
1. **Cross-Runtime Testing** - Verify Haxe runtime matches Java/C++ behavior
|
|
2. **Debugging Support** - Unified JSON format for inspection
|
|
3. **API Consistency** - Ensure Haxe changes don't break compatibility
|
|
4. **Quality Assurance** - Automated serialization correctness verification
|
|
5. **Development Velocity** - Fast detection of runtime-specific issues
|
|
|
|
The plan is comprehensive and follows established patterns while adapting to Haxe's specific language features. It provides a complete roadmap for implementing Haxe support in the Spine runtime testing infrastructure.
|
|
|
|
## Agent 2: Existing Serializer Patterns Analysis
|
|
|
|
Perfect! Now I have a comprehensive understanding of the existing serializer generator patterns and structure. Let me provide a detailed analysis of the current codebase:
|
|
|
|
## Analysis: Existing Serializer Generator Patterns and Structure
|
|
|
|
Based on my exploration of the Spine runtime codebase, I've identified a well-established pattern for generating serializers across multiple languages. Here's a comprehensive breakdown:
|
|
|
|
### **Core Architecture**
|
|
|
|
The serializer generation system follows a **3-phase pipeline**:
|
|
|
|
1. **Analysis Phase** (`analyze-java-api.ts`)
|
|
- Analyzes Java API using LSP to extract class information
|
|
- Outputs `/tests/output/analysis-result.json`
|
|
|
|
2. **IR Generation Phase** (`generate-serializer-ir.ts`)
|
|
- Transforms analysis data into language-agnostic Intermediate Representation (IR)
|
|
- Outputs `/tests/output/serializer-ir.json`
|
|
|
|
3. **Code Generation Phase** (language-specific generators)
|
|
- Java: `generate-java-serializer.ts` → `SkeletonSerializer.java`
|
|
- C++: `generate-cpp-serializer.ts` → `SkeletonSerializer.h`
|
|
|
|
### **Key Files and Structure**
|
|
|
|
**Main Entry Point:**
|
|
- `/Users/badlogic/workspaces/spine-runtimes/tests/generate-serializers.sh` - Orchestrates the entire pipeline
|
|
|
|
**Core Generator Files:**
|
|
- `/Users/badlogic/workspaces/spine-runtimes/tests/src/generate-serializer-ir.ts` - IR generation
|
|
- `/Users/badlogic/workspaces/spine-runtimes/tests/src/generate-java-serializer.ts` - Java serializer
|
|
- `/Users/badlogic/workspaces/spine-runtimes/tests/src/generate-cpp-serializer.ts` - C++ serializer
|
|
- `/Users/badlogic/workspaces/spine-runtimes/tests/src/types.ts` - Shared type definitions
|
|
|
|
**Generated Output:**
|
|
- `/Users/badlogic/workspaces/spine-runtimes/tests/output/serializer-ir.json` - IR data
|
|
- `/Users/badlogic/workspaces/spine-runtimes/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/utils/SkeletonSerializer.java`
|
|
- `/Users/badlogic/workspaces/spine-runtimes/spine-cpp/tests/SkeletonSerializer.h`
|
|
|
|
### **IR (Intermediate Representation) Structure**
|
|
|
|
The IR follows a well-defined schema:
|
|
|
|
```typescript
|
|
interface SerializerIR {
|
|
publicMethods: PublicMethod[]; // Main entry points (serialize*)
|
|
writeMethods: WriteMethod[]; // Internal write methods per type
|
|
enumMappings: EnumMappings; // Language-specific enum conversions
|
|
}
|
|
|
|
interface WriteMethod {
|
|
name: string; // e.g., "writeAnimation"
|
|
paramType: string; // e.g., "Animation"
|
|
properties: Property[]; // Object properties to serialize
|
|
isAbstractType: boolean; // Handles inheritance
|
|
subtypeChecks?: SubtypeCheck[]; // For abstract types
|
|
}
|
|
|
|
type Property = Primitive | Object | Enum | Array | NestedArray;
|
|
```
|
|
|
|
### **Code Generation Patterns**
|
|
|
|
**Common Features Across Languages:**
|
|
1. **Cycle Detection** - Uses `visitedObjects` map with reference strings
|
|
2. **Reference Strings** - Format: `<TypeName-identifier>` for navigation
|
|
3. **Type Metadata** - Each object includes `refString` and `type` fields
|
|
4. **Abstract Type Handling** - Uses instanceof/RTTI for polymorphism
|
|
5. **Special Type Support** - Custom handlers for `Color`, `TextureRegion`, etc.
|
|
|
|
**Language-Specific Adaptations:**
|
|
|
|
**Java Implementation:**
|
|
- Uses `JsonWriter` for output
|
|
- HashMap for cycle detection
|
|
- instanceof chains for abstract types
|
|
- Special handling for LibGDX types (`IntArray`, `FloatArray`)
|
|
|
|
**C++ Implementation:**
|
|
- Uses custom `JsonWriter` and `HashMap`
|
|
- RTTI system for type checking (`obj->getRTTI().instanceOf()`)
|
|
- Pointer-based object access
|
|
- Field naming convention with `_` prefix
|
|
- Reference and pointer method overloads
|
|
|
|
### **Related Code Generation Systems**
|
|
|
|
The codebase has additional generators following similar patterns:
|
|
|
|
**Flutter/Dart Generator:**
|
|
- `/Users/badlogic/workspaces/spine-runtimes/spine-flutter/codegen/src/dart-writer.ts`
|
|
- Generates complete runtime bindings from C++ API
|
|
- Uses similar IR → target language transformation
|
|
|
|
**C Generator:**
|
|
- `/Users/badlogic/workspaces/spine-runtimes/spine-c/codegen/src/c-writer.ts`
|
|
- Generates C bindings from C++ classes
|
|
- Header/source file generation patterns
|
|
|
|
### **Infrastructure & Tooling**
|
|
|
|
**Build System:**
|
|
- TypeScript-based generators using `tsx`
|
|
- Shell script orchestration
|
|
- Automatic code formatting integration
|
|
- LSP-based API analysis
|
|
|
|
**Type System:**
|
|
- Shared TypeScript interfaces for consistency
|
|
- Exclusion file support (`java-exclusions.txt`)
|
|
- Property filtering and inheritance handling
|
|
|
|
This architecture provides an excellent foundation for adding new language generators. The IR-based approach ensures consistency while allowing language-specific optimizations and idioms.
|
|
|
|
## Agent 3: Haxe Runtime Structure Analysis
|
|
|
|
Based on my examination of the spine-haxe directory and the existing test infrastructure, I can provide you with a comprehensive understanding of the current Haxe runtime structure, existing serialization code, and Haxe-specific patterns.
|
|
|
|
## Current Haxe Runtime Structure
|
|
|
|
### **Package Organization**
|
|
The Haxe runtime follows a well-organized package structure under `/Users/badlogic/workspaces/spine-runtimes/spine-haxe/spine-haxe/spine/`:
|
|
|
|
- **Core Classes**: `Skeleton.hx`, `SkeletonData.hx`, `Bone.hx`, `BoneData.hx`, `Slot.hx`, `SlotData.hx`
|
|
- **Animation System**: `animation/` directory with `Animation.hx`, `AnimationState.hx`, etc.
|
|
- **Attachments**: `attachments/` directory with various attachment types
|
|
- **Atlas System**: `atlas/` directory for texture atlas handling
|
|
- **Framework Integrations**: `flixel/` and `starling/` subdirectories
|
|
|
|
### **Existing Serialization Infrastructure**
|
|
|
|
**1. Binary Serialization (`SkeletonBinary.hx`)**
|
|
- Comprehensive binary format reader using `BinaryInput.hx`
|
|
- Handles all Spine data types including bones, slots, constraints, animations
|
|
- Uses extensive type-specific parsing with inline constants for different timeline types
|
|
- Scale factor handling throughout parsing
|
|
|
|
**2. JSON Serialization (`SkeletonJson.hx`)**
|
|
- JSON format reader using Haxe's built-in `Json.parse()`
|
|
- Uses `Reflect` extensively for dynamic property access
|
|
- Handles type conversions and null checking
|
|
- Complex animation timeline parsing with curve interpolation
|
|
|
|
**3. Binary Input Helper (`BinaryInput.hx`)**
|
|
- Low-level binary data reading utilities
|
|
- String reference management with `strings` array
|
|
- Endianness handling for cross-platform compatibility
|
|
- Variable-length integer encoding support
|
|
|
|
### **Haxe-Specific Patterns**
|
|
|
|
**1. Type System Patterns**
|
|
```haxe
|
|
// Type-safe collections
|
|
public final bones = new Array<BoneData>();
|
|
public final slots = new Array<Slot>();
|
|
|
|
// Nullable types with explicit null checking
|
|
if (attachment == null) return null;
|
|
|
|
// Type checking and casting
|
|
if (Std.isOfType(constraint, PhysicsConstraint))
|
|
physics.push(cast(constraint, PhysicsConstraint));
|
|
```
|
|
|
|
**2. Property Access Patterns**
|
|
```haxe
|
|
// Direct field access (no getters/setters unless needed)
|
|
data.bones.push(boneData);
|
|
slot.data.name == slotName
|
|
|
|
// Property with custom getter/setter
|
|
public var scaleY(get, default):Float = 1;
|
|
function get_scaleY() {
|
|
return scaleY * Bone.yDir;
|
|
}
|
|
```
|
|
|
|
**3. Dynamic Property Access**
|
|
```haxe
|
|
// Using Reflect for JSON parsing
|
|
var boneName:String = Reflect.getProperty(boneMap, "name");
|
|
if (Reflect.hasField(map, "color"))
|
|
data.color.setFromString(Reflect.getProperty(map, "color"));
|
|
```
|
|
|
|
**4. Error Handling**
|
|
```haxe
|
|
// Custom exception type
|
|
throw new SpineException("boneName cannot be null.");
|
|
|
|
// Null validation patterns
|
|
if (data == null)
|
|
throw new SpineException("data cannot be null.");
|
|
```
|
|
|
|
**5. Array Utility Patterns**
|
|
```haxe
|
|
// ArrayUtils helper for resizing with default values
|
|
ArrayUtils.resize(deform, deformLength, 0);
|
|
|
|
// Array iteration
|
|
for (bone in bones)
|
|
if (bone.data.name == boneName) return bone;
|
|
```
|
|
|
|
### **Key Architectural Insights**
|
|
|
|
**1. Data vs Instance Separation**
|
|
- Clear separation between `*Data` classes (immutable setup) and instance classes
|
|
- `SkeletonData` contains setup pose, `Skeleton` contains current state
|
|
- `BoneData` vs `Bone`, `SlotData` vs `Slot` pattern throughout
|
|
|
|
**2. Framework Integration Strategy**
|
|
- Modular design with separate framework-specific renderers (`flixel/`, `starling/`)
|
|
- Core animation logic independent of rendering framework
|
|
- Texture loading abstracted through `TextureLoader` interface
|
|
|
|
**3. Build and Distribution**
|
|
- Uses standard Haxe toolchain with `haxelib.json` configuration
|
|
- OpenFL integration for cross-platform deployment
|
|
- Examples demonstrate usage with different frameworks
|
|
|
|
### **Serialization-Related Gaps Identified**
|
|
|
|
Based on the `/Users/badlogic/workspaces/spine-runtimes/tests/plan-haxe.md` document, the missing components for cross-runtime testing are:
|
|
|
|
1. **Missing Generator**: No `generate-haxe-serializer.ts` exists yet
|
|
2. **Missing HeadlessTest**: No Haxe equivalent of the Java/C++ headless test applications
|
|
3. **Missing JSON Writer**: No helper class for generating consistent JSON output
|
|
4. **Missing Test Runner Integration**: Haxe not supported in the test orchestration system
|
|
|
|
### **Existing Code Quality and Patterns**
|
|
|
|
The Haxe codebase demonstrates excellent patterns:
|
|
- **Type Safety**: Extensive use of typed arrays and null checking
|
|
- **Performance**: Direct field access where possible, minimal reflection
|
|
- **Cross-Platform**: Careful handling of platform differences (like `Bone.yDir`)
|
|
- **Maintainability**: Clear separation of concerns and consistent naming
|
|
- **Documentation**: Comprehensive JSDoc-style comments throughout
|
|
|
|
The existing serialization code in `SkeletonBinary.hx` and `SkeletonJson.hx` shows sophisticated handling of the Spine format, with proper scale factor management, timeline parsing, and constraint handling. The code is well-structured for extending with additional serialization capabilities.
|
|
|
|
This analysis provides the foundation needed to implement the missing serialization components following established Haxe patterns while maintaining compatibility with the existing cross-runtime testing infrastructure. |