Coverage Report

Created: 2026-02-23 20:32

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/Users/alexjokela/projects/lattice/src/env.c
Line
Count
Source
1
#include "env.h"
2
#include "memory.h"
3
#include <stdlib.h>
4
#include <string.h>
5
6
2.57k
#define INITIAL_SCOPE_CAP 8
7
8
14.0k
static Scope scope_new(void) {
9
14.0k
    return lat_map_new(sizeof(LatValue));
10
14.0k
}
11
12
294k
static void scope_free_values(const char *key, void *value, void *ctx) {
13
294k
    (void)key; (void)ctx;
14
294k
    LatValue *v = (LatValue *)value;
15
294k
    value_free(v);
16
294k
}
17
18
14.0k
static void scope_free(Scope *s) {
19
14.0k
    lat_map_iter(s, scope_free_values, NULL);
20
14.0k
    lat_map_free(s);
21
14.0k
}
22
23
2.57k
Env *env_new(void) {
24
2.57k
    Env *env = malloc(sizeof(Env));
25
2.57k
    env->cap = INITIAL_SCOPE_CAP;
26
2.57k
    env->count = 1;
27
2.57k
    env->refcount = 1;
28
2.57k
    env->arena_backed = false;
29
2.57k
    env->scopes = malloc(env->cap * sizeof(Scope));
30
2.57k
    env->scopes[0] = scope_new();
31
2.57k
    return env;
32
2.57k
}
33
34
7.52k
void env_retain(Env *env) {
35
7.52k
    if (env) env->refcount++;
36
7.52k
}
37
38
9.42k
void env_release(Env *env) {
39
9.42k
    if (!env) return;
40
9.42k
    if (--env->refcount == 0)
41
1.89k
        env_free(env);
42
9.42k
}
43
44
4.47k
void env_free(Env *env) {
45
4.47k
    if (!env) return;
46
4.47k
    if (env->arena_backed) return;
47
10.9k
    for (size_t i = 0; i < env->count; i++) {
48
6.44k
        scope_free(&env->scopes[i]);
49
6.44k
    }
50
4.47k
    free(env->scopes);
51
4.47k
    free(env);
52
4.47k
}
53
54
7.64k
void env_push_scope(Env *env) {
55
7.64k
    if (env->count >= env->cap) {
56
22
        env->cap *= 2;
57
22
        env->scopes = realloc(env->scopes, env->cap * sizeof(Scope));
58
22
    }
59
7.64k
    env->scopes[env->count++] = scope_new();
60
7.64k
}
61
62
7.64k
void env_pop_scope(Env *env) {
63
7.64k
    if (env->count <= 1) return;
64
7.64k
    env->count--;
65
7.64k
    scope_free(&env->scopes[env->count]);
66
7.64k
}
67
68
290k
void env_define(Env *env, const char *name, LatValue value) {
69
290k
    if (env->count == 0) return;
70
290k
    env_define_at(env, env->count - 1, name, value);
71
290k
}
72
73
294k
void env_define_at(Env *env, size_t scope_idx, const char *name, LatValue value) {
74
294k
    if (scope_idx >= env->count) return;
75
294k
    Scope *scope = &env->scopes[scope_idx];
76
294k
    LatValue *existing = lat_map_get(scope, name);
77
294k
    if (existing) {
78
156
        value_free(existing);
79
156
    }
80
294k
    lat_map_set(scope, name, &value);
81
294k
}
82
83
26.9k
bool env_get(const Env *env, const char *name, LatValue *out) {
84
26.9k
    if (env->count == 1) {
85
3.39k
        LatValue *v = lat_map_get(&env->scopes[0], name);
86
3.39k
        if (v) { *out = value_deep_clone(v); return true; }
87
103
        return false;
88
3.39k
    }
89
40.5k
    for (size_t i = env->count; i > 0; i--) {
90
40.5k
        LatValue *v = lat_map_get(&env->scopes[i - 1], name);
91
40.5k
        if (v) {
92
23.5k
            *out = value_deep_clone(v);
93
23.5k
            return true;
94
23.5k
        }
95
40.5k
    }
96
6
    return false;
97
23.5k
}
98
99
813
LatValue *env_get_ref(const Env *env, const char *name) {
100
813
    if (env->count == 1)
101
809
        return lat_map_get(&env->scopes[0], name);
102
4
    for (size_t i = env->count; i > 0; i--) {
103
4
        LatValue *v = lat_map_get(&env->scopes[i - 1], name);
104
4
        if (v) return v;
105
4
    }
106
0
    return NULL;
107
4
}
108
109
3.40k
LatValue *env_get_ref_prehashed(const Env *env, const char *name, size_t hash) {
110
3.40k
    if (env->count == 1)
111
3.38k
        return lat_map_get_prehashed(&env->scopes[0], name, hash);
112
21
    for (size_t i = env->count; i > 0; i--) {
113
21
        LatValue *v = lat_map_get_prehashed(&env->scopes[i - 1], name, hash);
114
21
        if (v) return v;
115
21
    }
116
0
    return NULL;
117
16
}
118
119
15.8k
bool env_set(Env *env, const char *name, LatValue value) {
120
15.8k
    if (env->count == 1) {
121
10.0k
        LatValue *existing = lat_map_get(&env->scopes[0], name);
122
10.0k
        if (existing) {
123
8.64k
            value_free(existing);
124
8.64k
            lat_map_set(&env->scopes[0], name, &value);
125
8.64k
            return true;
126
8.64k
        }
127
1.38k
        return false;
128
10.0k
    }
129
11.4k
    for (size_t i = env->count; i > 0; i--) {
130
11.4k
        LatValue *existing = lat_map_get(&env->scopes[i - 1], name);
131
11.4k
        if (existing) {
132
5.78k
            value_free(existing);
133
5.78k
            lat_map_set(&env->scopes[i - 1], name, &value);
134
5.78k
            return true;
135
5.78k
        }
136
11.4k
    }
137
0
    return false;
138
5.78k
}
139
140
3
bool env_remove(Env *env, const char *name, LatValue *out) {
141
3
    for (size_t i = env->count; i > 0; i--) {
142
3
        LatValue *existing = lat_map_get(&env->scopes[i - 1], name);
143
3
        if (existing) {
144
3
            if (out) *out = *existing;
145
            /* Remove without freeing the value (caller takes ownership) */
146
            /* We need to copy the value out before removing */
147
3
            LatValue copy = *existing;
148
            /* Set to unit so map_remove doesn't free our data */
149
3
            memset(existing, 0, sizeof(LatValue));
150
3
            existing->type = VAL_UNIT;
151
3
            lat_map_remove(&env->scopes[i - 1], name);
152
3
            if (out) *out = copy;
153
0
            else value_free(&copy);
154
3
            return true;
155
3
        }
156
3
    }
157
0
    return false;
158
3
}
159
160
/* Deep clone helper — normal (non-arena) path */
161
typedef struct {
162
    Env *dest;
163
    size_t scope_idx;
164
} CloneCtx;
165
166
734
static void clone_entry(const char *key, void *value, void *ctx) {
167
734
    CloneCtx *cc = (CloneCtx *)ctx;
168
734
    LatValue *v = (LatValue *)value;
169
734
    LatValue cloned = value_deep_clone(v);
170
734
    lat_map_set(&cc->dest->scopes[cc->scope_idx], key, &cloned);
171
734
}
172
173
/* Arena-routed clone: build scope map internals directly via arena alloc */
174
13
static Env *env_clone_arena(const Env *env) {
175
13
    Env *new_env = lat_alloc_routed(sizeof(Env));
176
13
    new_env->cap = env->cap;
177
13
    new_env->count = env->count;
178
13
    new_env->refcount = 1;
179
13
    new_env->scopes = lat_alloc_routed(new_env->cap * sizeof(Scope));
180
181
13
    new_env->arena_backed = true;
182
51
    for (size_t i = 0; i < env->count; i++) {
183
38
        const LatMap *src = &env->scopes[i];
184
38
        LatMap *dst = &new_env->scopes[i];
185
38
        dst->value_size = src->value_size;
186
38
        dst->cap = src->cap;
187
38
        dst->count = src->count;  /* preserve tombstone count for probe chains */
188
38
        dst->live = src->live;
189
38
        dst->entries = lat_calloc_routed(src->cap, sizeof(LatMapEntry));
190
646
        for (size_t j = 0; j < src->cap; j++) {
191
            /* Point value to inline buffer for all entries */
192
608
            dst->entries[j].value = dst->entries[j]._ibuf;
193
608
            if (src->entries[j].state == MAP_OCCUPIED) {
194
19
                dst->entries[j].state = MAP_OCCUPIED;
195
19
                dst->entries[j].key = lat_strdup_routed(src->entries[j].key);
196
19
                LatValue *sv = (LatValue *)src->entries[j].value;
197
19
                LatValue cloned = value_deep_clone(sv);
198
19
                *(LatValue *)dst->entries[j].value = cloned;
199
589
            } else if (src->entries[j].state == MAP_TOMBSTONE) {
200
0
                dst->entries[j].state = MAP_TOMBSTONE;
201
0
            }
202
608
        }
203
38
    }
204
13
    return new_env;
205
13
}
206
207
1.91k
Env *env_clone(const Env *env) {
208
1.91k
    if (value_get_arena()) return env_clone_arena(env);
209
210
1.90k
    Env *new_env = malloc(sizeof(Env));
211
1.90k
    new_env->cap = env->cap;
212
1.90k
    new_env->count = env->count;
213
1.90k
    new_env->refcount = 1;
214
1.90k
    new_env->arena_backed = false;
215
1.90k
    new_env->scopes = malloc(new_env->cap * sizeof(Scope));
216
217
1.90k
    CloneCtx ctx;
218
1.90k
    ctx.dest = new_env;
219
5.77k
    for (size_t i = 0; i < env->count; i++) {
220
3.87k
        new_env->scopes[i] = scope_new();
221
3.87k
        ctx.scope_idx = i;
222
3.87k
        lat_map_iter(&env->scopes[i], clone_entry, &ctx);
223
3.87k
    }
224
1.90k
    return new_env;
225
1.91k
}
226
227
60.7k
static void iter_scope_values(const char *key, void *value, void *ctx) {
228
60.7k
    (void)key;
229
60.7k
    void **args = (void **)ctx;
230
60.7k
    EnvIterFn fn = (EnvIterFn)args[0];
231
60.7k
    void *user_ctx = args[1];
232
60.7k
    fn((LatValue *)value, user_ctx);
233
60.7k
}
234
235
15.0k
void env_iter_values(Env *env, EnvIterFn fn, void *ctx) {
236
15.0k
    void *args[2] = { (void *)fn, ctx };
237
72.2k
    for (size_t i = 0; i < env->count; i++) {
238
57.1k
        lat_map_iter(&env->scopes[i], iter_scope_values, args);
239
57.1k
    }
240
15.0k
}