Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ config.toml

**/.claude/settings.local.json
CLAUDE.md
.worktrees/
43 changes: 10 additions & 33 deletions engines/extism/evaluator/evaluator.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,29 +43,18 @@ func (be *Evaluator) String() string {
return "extism.Evaluator"
}

// getDataProvider returns the data provider from the executable unit, or nil if unavailable.
func (be *Evaluator) getDataProvider() data.Provider {
if be.execUnit == nil {
return nil
}
return be.execUnit.GetDataProvider()
}

// loadInputData retrieves input data using the data provider in the executable unit.
// Returns a map that will be used as input for the WASM module.
func (be *Evaluator) loadInputData(ctx context.Context) (map[string]any, error) {
logger := be.logger.WithGroup("loadInputData")

// If no executable unit or data provider, return empty map
if be.execUnit == nil || be.execUnit.GetDataProvider() == nil {
logger.WarnContext(ctx, "no data provider available, using empty data")
return make(map[string]any), nil
}

// Get input data from provider
inputData, err := be.execUnit.GetDataProvider().GetData(ctx)
if err != nil {
logger.ErrorContext(ctx, "failed to get input data from provider", "error", err)
return nil, err
}

if len(inputData) == 0 {
logger.WarnContext(ctx, "empty input data returned from provider")
}
logger.DebugContext(ctx, "input data loaded from provider", "inputData", inputData)
return inputData, nil
return data.LoadInputData(ctx, be.logger.WithGroup("loadInputData"), be.getDataProvider())
}

// execHelper is a utility function to handle common execution logic
Expand Down Expand Up @@ -214,17 +203,5 @@ func (be *Evaluator) AddDataToContext(
ctx context.Context,
d ...map[string]any,
) (context.Context, error) {
logger := be.logger.WithGroup("AddDataToContext")

// Use the shared helper function for context preparation
if be.execUnit == nil || be.execUnit.GetDataProvider() == nil {
return ctx, fmt.Errorf("no data provider available")
}

return data.AddDataToContextHelper(
ctx,
logger,
be.execUnit.GetDataProvider(),
d...,
)
return data.AddDataToContextFromProvider(ctx, be.logger.WithGroup("AddDataToContext"), be.getDataProvider(), d...)
}
49 changes: 20 additions & 29 deletions engines/extism/internal/jsonHelpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,48 +2,39 @@ package internal

import (
"encoding/json"
"strings"
)

// FixJSONNumberTypes converts json.Number values to appropriate Go types based on semantic rules
// convertJSONNumber converts a json.Number to an int (if it fits) or float64.
func convertJSONNumber(num json.Number) any {
if n, err := num.Int64(); err == nil {
return int(n)
}
if n, err := num.Float64(); err == nil {
return n
}
return num
Comment on lines +7 to +15
Copy link

Copilot AI Apr 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

convertJSONNumber converts json.Number -> int via int(n) after Int64(), which is architecture-dependent and can overflow/truncate on 32-bit builds (and will misrepresent large int64 values). Prefer returning int64 (or only returning int when the value fits into the platform int size, otherwise fall back to int64/float64/json.Number).

Copilot uses AI. Check for mistakes.
}

// FixJSONNumberTypes converts json.Number values to appropriate Go types.
// Integer-representable numbers become int; all others become float64.
func FixJSONNumberTypes(data any) any {
switch v := data.(type) {
case map[string]any:
// Process each key in the map
for k, val := range v {
// Handle nested structures recursively
if nestedMap, ok := val.(map[string]any); ok {
v[k] = FixJSONNumberTypes(nestedMap)
continue
}

if nestedSlice, ok := val.([]any); ok {
v[k] = FixJSONNumberTypes(nestedSlice)
continue
}

// Convert json.Number to appropriate type
if num, ok := val.(json.Number); ok {
// Fields that should be integers
if strings.HasSuffix(k, "_count") || k == "count" ||
strings.HasSuffix(k, "_id") || strings.HasSuffix(k, "Id") {
if n, err := num.Int64(); err == nil {
v[k] = int(n)
}
continue
}

// Default to float64 for other numeric fields
if n, err := num.Float64(); err == nil {
v[k] = n
}
v[k] = convertJSONNumber(num)
continue
}
v[k] = FixJSONNumberTypes(val)
}
return v

case []any:
// Process each item in the slice
for i, item := range v {
if num, ok := item.(json.Number); ok {
v[i] = convertJSONNumber(num)
continue
}
v[i] = FixJSONNumberTypes(item)
}
return v
Expand Down
Loading
Loading