Skip to content
Merged
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
293 changes: 176 additions & 117 deletions backend/runtime/Namespace.c

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion backend/runtime/Namespace.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ void Namespace_promoteToShared(Namespace *self, uword_t count);

RTValue Namespace_getMappings(Namespace *self);
RTValue Namespace_getAliases(Namespace *self);
bool Namespace_checkReplacement(Namespace *self, Symbol *sym, RTValue old, RTValue neu);
bool Namespace_isInternedMapping(Namespace *self, Symbol *sym, RTValue o);
bool Namespace_checkReplacement(Namespace *self, Symbol *sym, RTValue oldVal, RTValue neuVal);
Var *Namespace_intern(Namespace *self, Symbol *sym);
RTValue Namespace_reference(Namespace *self, Symbol *sym, RTValue val);
RTValue Namespace_referenceClass(Namespace *self, Symbol *sym, Class *val);
Expand All @@ -42,8 +42,10 @@ Var *Namespace_refer(Namespace *self, Symbol *sym, Var *var);
RTValue Namespace_getMapping(Namespace *self, Symbol *name);
RTValue Namespace_findInternedVar(Namespace *self, Symbol *symbol);
RTValue Namespace_lookupAlias(Namespace *self, Symbol *alias);
struct ExecutionContext;
void Namespace_addAlias(Namespace *self, Symbol *alias, Namespace *ns);
void Namespace_removeAlias(Namespace *self, Symbol *alias);
RTValue RT_inNs(__attribute__((swift_context)) struct ExecutionContext *ctx, Symbol *nsName) __attribute__((swiftcall));

#ifdef __cplusplus
}
Expand Down
1 change: 1 addition & 0 deletions backend/runtime/PersistentArrayMap.c
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ bool PersistentArrayMap_equals(PersistentArrayMap *self,
release(otherVal);
return false;
}
release(otherVal);
}
return true;
}
Expand Down
5 changes: 3 additions & 2 deletions backend/runtime/Var.c
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ RTValue Var_getMeta(Var *self) {
return val;
}

/* Outside fercount system */
Var *Var_setDynamic(Var *self, bool dynamic) { // modifies and returns self
self->dynamic = dynamic;
return self;
Expand Down Expand Up @@ -219,7 +220,7 @@ RTValue Var_set(__attribute__((swift_context)) struct ExecutionContext *ctx,

// Check if the var has a thread-local binding (Clojure behavior)
PersistentArrayMap *m = RT_unboxPtr(ctx->bindingsMap);
Ptr_retain(m); // indexOf consumes map
Ptr_retain(m); // indexOf consumes map
Ptr_retain(self); // indexOf consumes key
if (PersistentArrayMap_indexOf(m, RT_boxPtr(self)) == -1) {
release(value);
Expand All @@ -230,7 +231,7 @@ RTValue Var_set(__attribute__((swift_context)) struct ExecutionContext *ctx,
retain(value);
RTValue oldMap = ctx->bindingsMap;
Ptr_retain(self); // assoc consumes key
Ptr_retain(m); // assoc consumes map
Ptr_retain(m); // assoc consumes map
ctx->bindingsMap =
RT_boxPtr(PersistentArrayMap_assoc(m, RT_boxPtr(self), value));
release(oldMap);
Expand Down
101 changes: 86 additions & 15 deletions backend/runtime/tests/Namespace_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "../PersistentArrayMap.h"
#include "../RuntimeInterface.h"
#include "../ConcurrentHashMap.h"
#include "../ExecutionContext.h"

extern ConcurrentHashMap *namespaces;

Expand Down Expand Up @@ -234,12 +235,14 @@ static void test_namespace_redefine_referred_var_should_throw(void **state) {
Var *ref = Namespace_refer(ns, sym, otherVar);
Ptr_release(ref);

// Now try to intern "foo" in ns. This should throw IllegalStateException!
// Now try to intern "foo" in ns. Replacement is allowed because the old value was a referred Var (non-interned),
// so it should succeed and return the newly created interned Var.
Ptr_retain(ns); Ptr_retain(sym);
ASSERT_THROWS("IllegalStateException", {
Var *v = Namespace_intern(ns, sym);
Ptr_release(v);
});
Var *v = Namespace_intern(ns, sym);
assert_non_null(v);
assert_ptr_equal(v->ns, ns);
assert_ptr_equal(v->sym, sym);
Ptr_release(v);

Ptr_release(otherVar);
Ptr_release(otherNs);
Expand All @@ -264,12 +267,14 @@ static void test_namespace_redefine_class_should_throw(void **state) {
RTValue ref = Namespace_reference(ns, sym, RT_boxPtr((Object *)strVal));
release(ref);

// Now try to intern "foo" in ns. This should throw IllegalStateException!
// Now try to intern "foo" in ns. Replacement is allowed because the old value was a String (non-Var),
// so it should succeed and return the newly created interned Var.
Ptr_retain(ns); Ptr_retain(sym);
ASSERT_THROWS("IllegalStateException", {
Var *v = Namespace_intern(ns, sym);
Ptr_release(v);
});
Var *v = Namespace_intern(ns, sym);
assert_non_null(v);
assert_ptr_equal(v->ns, ns);
assert_ptr_equal(v->sym, sym);
Ptr_release(v);

Ptr_release(ns);
Ptr_release(sym);
Expand Down Expand Up @@ -303,12 +308,12 @@ static void test_namespace_redefine_referred_var_with_other_reference_should_thr
Var *ref1 = Namespace_refer(ns, sym, otherVar);
Ptr_release(ref1);

// Now try to refer thirdVar in ns under "foo". This should throw IllegalStateException!
// Now try to refer thirdVar in ns under "foo". Replacement is allowed (with a warning),
// so it should succeed and return thirdVar.
Ptr_retain(ns); Ptr_retain(sym); Ptr_retain(thirdVar);
ASSERT_THROWS("IllegalStateException", {
Var *ref2 = Namespace_refer(ns, sym, thirdVar);
Ptr_release(ref2);
});
Var *ref2 = Namespace_refer(ns, sym, thirdVar);
assert_ptr_equal(ref2, thirdVar);
Ptr_release(ref2);

Ptr_release(otherVar);
Ptr_release(thirdVar);
Expand All @@ -320,6 +325,71 @@ static void test_namespace_redefine_referred_var_with_other_reference_should_thr
});
}

static void test_rt_in_ns(void **state) {
(void)state;
MemoryState before, after;
captureMemoryState(&before);
{
// Setup execution context with empty bindings map
ExecutionContext *ctx = ExecutionContext_create(RT_boxPtr(PersistentArrayMap_empty()));

// Setup clojure.core namespace and *ns* Var (like initializeDefaultNamespaces does)
Symbol *coreSym = Symbol_create(String_create("clojure.core"));
Namespace *coreNs = Namespace_findOrCreate(coreSym);

Symbol *nsSym = Symbol_create(String_create("*ns*"));
Var *nsVar = Namespace_intern(coreNs, nsSym);
Var_setDynamic(nsVar, true);

// Set initial bindings map: map *ns* to user namespace
Symbol *userSym = Symbol_create(String_create("user"));
Namespace *userNs = Namespace_findOrCreate(userSym);
Ptr_retain(nsVar);
Var_bindRoot(nsVar, RT_boxPtr((Object *)userNs));

RTValue oldBindings = ctx->bindingsMap;
Ptr_retain(RT_unboxPtr(oldBindings));
Ptr_retain(nsVar);
Ptr_retain(userNs);
ctx->bindingsMap = RT_boxPtr(PersistentArrayMap_assoc(
(PersistentArrayMap *)RT_unboxPtr(oldBindings),
RT_boxPtr(nsVar),
RT_boxPtr((Object *)userNs)));
release(oldBindings);

// Now call RT_inNs to switch to "my-new-ns"
Symbol *newNsSym = Symbol_create(String_create("my-new-ns"));
Ptr_retain(newNsSym);
RTValue res = RT_inNs(ctx, newNsSym);
assert_false(RT_isNil(res));
Namespace *newNs = (Namespace *)RT_unboxPtr(res);
assert_string_equal(String_c_str(newNs->name->name), "my-new-ns");

// Check that the bindings map has indeed been updated to "my-new-ns"
Ptr_retain(RT_unboxPtr(ctx->bindingsMap));
Ptr_retain(nsVar);
RTValue currentNsVal = PersistentArrayMap_get(
(PersistentArrayMap *)RT_unboxPtr(ctx->bindingsMap),
RT_boxPtr(nsVar));
assert_ptr_equal(RT_unboxPtr(currentNsVal), newNs);
release(currentNsVal);

// Clean up
release(res);
Ptr_release(ctx);
Ptr_release(nsVar);

// We created "clojure.core", "user" and "my-new-ns". Let's dissoc them from the global map to prevent leaks:
ConcurrentHashMap_dissoc_preservesSelf(namespaces, RT_boxSymbol((Object *)Symbol_create(String_create("clojure.core"))));
ConcurrentHashMap_dissoc_preservesSelf(namespaces, RT_boxSymbol((Object *)Symbol_create(String_create("user"))));
ConcurrentHashMap_dissoc_preservesSelf(namespaces, RT_boxSymbol((Object *)Symbol_create(String_create("my-new-ns"))));
Ptr_release(newNsSym);
Ebr_force_reclaim();
}
captureMemoryState(&after);
assertMemoryBalanceExcept(&before, &after, (int[]){stringType, persistentVectorType, persistentVectorNodeType, symbolType}, 4);
}

int main(void) {
RuntimeInterface_initialise();
const struct CMUnitTest tests[] = {
Expand All @@ -332,6 +402,7 @@ int main(void) {
cmocka_unit_test(test_namespace_redefine_referred_var_should_throw),
cmocka_unit_test(test_namespace_redefine_class_should_throw),
cmocka_unit_test(test_namespace_redefine_referred_var_with_other_reference_should_throw),
cmocka_unit_test(test_rt_in_ns),
};
int res = cmocka_run_group_tests(tests, NULL, NULL);
RuntimeInterface_cleanup();
Expand Down
78 changes: 56 additions & 22 deletions backend/state/ThreadsafeCompilerState.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "../runtime/Namespace.h"
#include "../runtime/Symbol.h"
#include "../runtime/Var.h"
#include "../runtime/Function.h"
#include "../tools/EdnParser.h"
#include "../tools/RTValueWrapper.h"
namespace rt {
Expand All @@ -17,23 +18,20 @@ ThreadsafeCompilerState::ThreadsafeCompilerState()
void ThreadsafeCompilerState::initializeDefaultNamespaces() {
// Setup default Clojure namespaces and *ns* Var

// 1. Create "clojure.core" namespace
Symbol *coreSym = Symbol_create(String_create("clojure.core"));
Namespace *coreNs = Namespace_findOrCreate(coreSym); // coreNs has +1 refcount
Namespace *coreNs = Namespace_findOrCreate(
Symbol_create(String_create("clojure.core"))); // coreNs has +1 refcount

// 2. Intern "*ns*" Var in "clojure.core"
Symbol *nsSym = Symbol_create(String_create("*ns*"));
Var *nsVar = Namespace_intern(coreNs, nsSym);
Var *nsVar = Namespace_intern(coreNs, Symbol_create(String_create("*ns*")));
Var_setDynamic(nsVar, true);

// 3. Create "user" namespace
Symbol *userSym = Symbol_create(String_create("user"));
Namespace *userNs = Namespace_findOrCreate(userSym); // userNs has +1 refcount
RTValue userNsVal = RT_boxPtr(userNs);
Namespace *userNs = Namespace_findOrCreate(
Symbol_create(String_create("user"))); // userNs has +1 refcount

// 4. Bind clojure.core/*ns* Var root to the user namespace object
Ptr_retain(nsVar); // Retain nsVar because Var_bindRoot consumes self
Var_bindRoot(nsVar, userNsVal);
Var_bindRoot(nsVar, RT_boxPtr(userNs));

// 6. Register in compiler state so JIT finds this exact Var object
registerVar("clojure.core/*ns*", nsVar);
Expand Down Expand Up @@ -441,6 +439,7 @@ void ThreadsafeCompilerState::validateProtocolImplementations(
}

void ThreadsafeCompilerState::extendInternalClasses(RTValue from) {
retain(from);
auto descriptions = buildClasses(from);

for (auto &desc : descriptions) {
Expand Down Expand Up @@ -470,6 +469,41 @@ void ThreadsafeCompilerState::extendInternalClasses(RTValue from) {
}
}
}

// Phase 5: Intern Clojure Var functions defined in class extensions
PersistentArrayMap *map = (PersistentArrayMap *)RT_unboxPtr(from);
for (uword_t i = 0; i < map->count; i++) {
RTValue key = map->keys[i];
RTValue value = map->values[i];

if (getType(key) == symbolType && getType(value) == functionType) {
Symbol *sym = (Symbol *)RT_unboxPtr(key);
if (sym->ns != nullptr) {
std::string nsName = String_c_str(sym->ns);
std::string symName = String_c_str(sym->name);

// Find or create the namespace
Ptr_retain(sym->ns);
Symbol *nsSym = Symbol_create(sym->ns);
Namespace *ns = Namespace_findOrCreate(nsSym);

// Intern the symbol in that namespace to get the Var
Ptr_retain(sym->name);
Symbol *varSym = Symbol_create(sym->name);
Var *var = Namespace_intern(ns, varSym);

// Bind root of the Var to the JIT-compiled ClojureFunction object
Ptr_retain(var);
retain(value);
Var_bindRoot(var, value);

// Register in compiler state so it can be resolved by JIT
std::string fullVarName = nsName + "/" + symName;
registerVar(fullVarName.c_str(), var);
}
}
}
release(from);
}

Var *ThreadsafeCompilerState::getOrCreateVar(const char *name) {
Expand All @@ -485,14 +519,15 @@ Var *ThreadsafeCompilerState::getOrCreateVar(const char *name) {

// 1. Create Symbol for Namespace
Symbol *nsSym = Symbol_create(String_createDynamicStr(nsName.c_str()));

// 2. Find or create the Namespace (consumes nsSym, returns +1 ref)
Namespace *ns = Namespace_findOrCreate(nsSym);

// 3. Create Symbol for Var
Symbol *varSym = Symbol_create(String_createDynamicStr(symName.c_str()));

// 4. Intern symbol inside Namespace (consumes ns and varSym, returns retained Var +1)
// 4. Intern symbol inside Namespace (consumes ns and varSym, returns retained
// Var +1)
Var *var = Namespace_intern(ns, varSym);
return var;
}
Expand All @@ -513,19 +548,21 @@ Var *ThreadsafeCompilerState::getCurrentVar(const char *name) {

if (RT_isNil(nsVal)) {
release(nsVal);
// Jeśli brak namespace, a nazwa była niekwalifikowana, szukamy w clojure.core
// Jeśli brak namespace, a nazwa była niekwalifikowana, szukamy w
// clojure.core
if (slashPos == std::string::npos && nsName != "clojure.core") {
Symbol *coreSym = Symbol_create(String_create("clojure.core"));
RTValue coreNsVal = Namespace_find(coreSym);

if (!RT_isNil(coreNsVal)) {
Namespace *coreNs = (Namespace *)RT_unboxPtr(coreNsVal);
Symbol *coreVarSym = Symbol_create(String_createDynamicStr(symName.c_str()));
Symbol *coreVarSym =
Symbol_create(String_createDynamicStr(symName.c_str()));
Ptr_retain(coreNs);
RTValue coreMapping = Namespace_getMapping(coreNs, coreVarSym);
Ptr_release(coreNs);
release(coreNsVal);

if (!RT_isNil(coreMapping) && getType(coreMapping) == varType) {
Var *var = (Var *)RT_unboxPtr(coreMapping);
return var;
Expand Down Expand Up @@ -553,15 +590,16 @@ Var *ThreadsafeCompilerState::getCurrentVar(const char *name) {
if (slashPos == std::string::npos && nsName != "clojure.core") {
Symbol *coreSym = Symbol_create(String_create("clojure.core"));
RTValue coreNsVal = Namespace_find(coreSym);

if (!RT_isNil(coreNsVal)) {
Namespace *coreNs = (Namespace *)RT_unboxPtr(coreNsVal);
Symbol *coreVarSym = Symbol_create(String_createDynamicStr(symName.c_str()));
Symbol *coreVarSym =
Symbol_create(String_createDynamicStr(symName.c_str()));
Ptr_retain(coreNs);
RTValue coreMapping = Namespace_getMapping(coreNs, coreVarSym);
Ptr_release(coreNs);
release(coreNsVal);

if (!RT_isNil(coreMapping) && getType(coreMapping) == varType) {
Var *var = (Var *)RT_unboxPtr(coreMapping);
return var;
Expand Down Expand Up @@ -596,14 +634,11 @@ void ThreadsafeCompilerState::registerVar(const char *name, Var *var) {
symName = varName.substr(slashPos + 1);
}

// 1. Znajdź lub utwórz Namespace
Symbol *nsSym = Symbol_create(String_createDynamicStr(nsName.c_str()));
Namespace *ns = Namespace_findOrCreate(nsSym);

// 2. Utwórz Symbol dla Var
Symbol *varSym = Symbol_create(String_createDynamicStr(symName.c_str()));

// 3. Uzupełnij pola ns i sym w strukturze Var, jeśli są puste
if (var->ns == nullptr) {
var->ns = ns;
}
Expand All @@ -612,7 +647,6 @@ void ThreadsafeCompilerState::registerVar(const char *name, Var *var) {
Ptr_retain(varSym);
}

// 4. Przypisz Var w Namespace za pomocą referencji (consumes var)
Var *referred = Namespace_refer(ns, varSym, var);
Ptr_release(referred);
}
Expand Down
Loading
Loading