Skip to content

Commit

Permalink
jule: refactor and improve closure implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
mertcandav committed Aug 31, 2024
1 parent e48e48a commit 8a6f2e7
Show file tree
Hide file tree
Showing 7 changed files with 131 additions and 34 deletions.
93 changes: 92 additions & 1 deletion api/fn.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,89 @@

namespace jule
{
// std::function wrapper of JuleC.
// Anonymous function / closure wrapper of JuleC.
template <typename Ret, typename... Args>
struct Fn2
{
public:
Ret (*f)(jule::Ptr<jule::Uintptr>, Args...);
jule::Ptr<jule::Uintptr> ctx; // Closure ctx.
void (*ctxHandler)(jule::Ptr<jule::Uintptr> &alloc) = nullptr;

Fn2(void) = default;
Fn2(const Fn2<Ret, Args...> &) = default;
Fn2(std::nullptr_t) noexcept : Fn2() {}

Fn2(Ret (*f)(jule::Ptr<jule::Uintptr>, Args...)) noexcept
{
this->f = f;
}

~Fn2(void) noexcept
{
this->f = nullptr;
if (this->ctxHandler)
{
this->ctxHandler(this->ctx);
this->ctxHandler = nullptr;
this->ctx.ref = nullptr; // Disable GC for allocation.
this->ctx = nullptr; // Assign to nullptr safely.
}
}

template <typename... Arguments>
Ret call(
#ifndef __JULE_ENABLE__PRODUCTION
const char *file,
#endif
Args... args)
{
#ifndef __JULE_DISABLE__SAFETY
if (this->f == nullptr)
#ifndef __JULE_ENABLE__PRODUCTION
jule::panic((std::string(__JULE_ERROR__INVALID_MEMORY) + "\nfile: ") + file);
#else
jule::panic(__JULE_ERROR__INVALID_MEMORY);
#endif // PRODUCTION
#endif // SAFETY
return this->f(this->ctx, args...);
}

inline auto operator()(Args... args)
{
#ifndef __JULE_ENABLE__PRODUCTION
return this->call<Args...>("/api/fn.hpp", args...);
#else
return this->call<Args...>(args...);
#endif
}

inline Fn2<Ret, Args...> &operator=(std::nullptr_t) noexcept
{
this->f = nullptr;
return *this;
}

constexpr jule::Bool operator==(std::nullptr_t) const noexcept
{
return this->f == nullptr;
}

constexpr jule::Bool operator!=(std::nullptr_t) const noexcept
{
return !this->operator==(nullptr);
}

friend std::ostream &operator<<(std::ostream &stream,
const Fn2<Ret, Args...> &f) noexcept
{
if (f == nullptr)
return (stream << "<nil>");
return (stream << (void *)f.f);
}
};

// Anonymous function / closure wrapper of JuleC.
template <typename Ret, typename... Args>
struct Fn
{
Expand Down Expand Up @@ -117,6 +199,15 @@ namespace jule
}
};

template <typename Ret, typename... Args>
jule::Fn2<Ret, Args...> __new_closure2(void *fn, jule::Ptr<jule::Uintptr> ctx, void (*ctxHandler)(jule::Ptr<jule::Uintptr> &)) noexcept
{
jule::Fn2<Ret, Args...> fn2((Ret(*)(jule::Ptr<jule::Uintptr>, Args...))fn);
fn2.ctx = std::move(ctx);
fn2.ctxHandler = ctxHandler;
return fn2;
}

static jule::Uint __page_size = __JULE_ASSUMED_PAGE_SIZE;

#if defined(ARCH_AMD64)
Expand Down
12 changes: 11 additions & 1 deletion src/julec/obj/cxx/expr.jule
Original file line number Diff line number Diff line change
Expand Up @@ -642,6 +642,16 @@ impl exprCoder {
}
}
self.pushCallInf(m)
if m.Func.AsAnon && !m.Func.Anon {
// Function used as anonymous.
// So pass nullptr to ctx parameter.
// Do not pass nullptr if function is anonymous,
// beacause it stores ctx data internally and it will pass.
self.oc.write("nullptr")
if len(m.Args) > 0 {
self.oc.write(", ")
}
}
mut locinfo := false
if !m.Func.IsBuiltin() && len(m.Func.Decl.Params) > 0 && m.Func.Decl.Params[0].IsSelf() {
match type m.Expr {
Expand Down Expand Up @@ -928,7 +938,7 @@ impl exprCoder {

fn anonFunc(mut &self, mut m: &AnonFnExprModel) {
ident := self.oc.pushAnonFn(m)
self.oc.write("jule::__new_closure<")
self.oc.write("jule::__new_closure2<")
self.oc.tc.anonFunc(self.oc.Buf, m.Func)
self.oc.write(">((void*)")
self.oc.write(ident)
Expand Down
41 changes: 15 additions & 26 deletions src/julec/obj/cxx/object.jule
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ use std::time::{Time}
// Data offset of empty trait.
const emptyTraitOffset = 0x0

const ctxParamIdent = "__f_ctx"
const anonFnCtxSuffix = "_ctx" // Anon fn identifier suffix for ctx struct identifier.
const anonFnCtxHandlerSuffix = anonFnCtxSuffix + "_handler" // Anon fn identifier suffix for ctx allocation handler.
const anyTypeIdent = "__jule_any_type"
Expand Down Expand Up @@ -849,23 +850,6 @@ impl ObjectCoder {
}
}

fn paramsDecls(mut &self, mut &params: []&ParamIns) {
if len(params) == 0 {
self.write("(void)")
ret
}

self.write("(")
for (i, mut p) in params {
self.tc.paramIns(self.Buf, p)
if len(params)-i > 1 {
self.write(", ")
}
}

self.write(")")
}

// The ident parameter means this function is anon, mostly.
// But this parameter not only for anonymous functions.
// It also useable as custom identifiers for functions.
Expand All @@ -891,7 +875,7 @@ impl ObjectCoder {
fn funcDeclIns(mut &self, mut &f: &FnIns, ptr: bool) {
self.indent()
self.funcHead(self.Buf, f, ptr, "")
self.paramsDecls(f.Params)
self.paramsIns(self.Buf, f)
self.write(";\n")
}

Expand Down Expand Up @@ -988,15 +972,21 @@ impl ObjectCoder {
identCoder.param(buf, p.Decl)
}

fn paramsIns(mut &self, mut &buf: StrBuilder, mut &params: []&ParamIns) {
if len(params) == 0 {
fn paramsIns(mut &self, mut &buf: StrBuilder, mut &f: &FnIns) {
if !f.AsAnon && len(f.Params) == 0 {
buf.WriteStr("(void)")
ret
}
buf.WriteByte('(')
for (i, mut p) in params {
if f.AsAnon {
buf.WriteStr(ctxParamType + " " + ctxParamIdent)
if len(f.Params) > 0 {
buf.WriteStr(", ")
}
}
for (i, mut p) in f.Params {
self.paramIns(buf, p)
if len(params)-i > 1 {
if len(f.Params)-i > 1 {
buf.WriteStr(", ")
}
}
Expand Down Expand Up @@ -1054,13 +1044,13 @@ impl ObjectCoder {

fn anonFuncInsDecl(mut &self, mut &m: &AnonFnExprModel, ident: str) {
self.funcHead(self.anonObj, m.Func, false, ident)
self.paramsIns(self.anonObj, m.Func.Params)
self.paramsIns(self.anonObj, m.Func)
self.anonObj.WriteByte(';')
}

fn anonFuncIns(mut &self, mut &m: &AnonFnExprModel, ident: str) {
self.funcHead(self.Buf, m.Func, false, ident)
self.paramsIns(self.Buf, m.Func.Params)
self.paramsIns(self.Buf, m.Func)
self.write(" ")
self.sc.anonFuncScope(m, ident)
if m.Func.Scope != nil {
Expand All @@ -1070,7 +1060,7 @@ impl ObjectCoder {

fn funcIns(mut &self, mut &f: &FnIns) {
self.funcHead(self.Buf, f, false, "")
self.paramsIns(self.Buf, f.Params)
self.paramsIns(self.Buf, f)
self.write(" ")
self.sc.funcScope(f)
if f.Scope != nil {
Expand Down Expand Up @@ -1384,7 +1374,6 @@ impl ObjectCoder {

fn end(mut &self) {
self.write(`int main(int argc, char *argv[], char *envp[]) {
jule::__closure_init();
jule::setup_argv(argc, argv);
jule::setup_envp(envp);

Expand Down
8 changes: 5 additions & 3 deletions src/julec/obj/cxx/scope.jule
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ use std::jule::sema::{
}
use std::strings::{StrBuilder}

const closureCtxIdent = "__jule_closure_ctx"
const matchExpr = "_match_expr"
const resultName = "__jule_func_result"
const assignResultName = "__jule_assign_result"
Expand Down Expand Up @@ -1007,10 +1008,11 @@ impl scopeCoder {

// Get ctx.
self.oc.indent()
self.oc.write(typeCoder.Ptr + "<")
self.oc.write(ident)
self.oc.write(anonFnCtxSuffix + " *__jule_closure_ctx = (")
self.oc.write(anonFnCtxSuffix + "> " + closureCtxIdent + " = " + ctxParamIdent + ".as<")
self.oc.write(ident)
self.oc.write(anonFnCtxSuffix + "*)jule::__closure_get_ctx();\n")
self.oc.write(anonFnCtxSuffix + ">();\n")

self.commonFuncScope(m.Func)

Expand Down Expand Up @@ -1055,7 +1057,7 @@ fn isIterCopyOptimizable(&expr: &Data, &v: &Var): bool {
fn captureVarHandling(mut &oc: &ObjectCoder, mut &m: &AnonFnExprModel, mut &v: &Var): bool {
for _, cv in m.Captured {
if cv == v {
oc.write("__jule_closure_ctx->")
oc.write(closureCtxIdent + ".alloc->")
identCoder.var(oc.Buf, v)
ret true
}
Expand Down
4 changes: 3 additions & 1 deletion src/julec/obj/cxx/type.jule
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ use std::jule::sema::{
use types for std::jule::types
use std::strings::{StrBuilder}

const ctxParamType = typeCoder.Ptr + "<" + typeCoder.Uintptr + ">"

struct customType {
kind: str
}
Expand Down Expand Up @@ -57,7 +59,7 @@ impl typeCoder {
const Slice = "jule::Slice"
const Trait = "jule::Trait"
const Array = "jule::Array"
const Fn = "jule::Fn"
const Fn = "jule::Fn2"
const Bool = "jule::Bool"
const Int = "jule::Int"
const Uintptr = "jule::Uintptr"
Expand Down
6 changes: 4 additions & 2 deletions std/jule/sema/eval.jule
Original file line number Diff line number Diff line change
Expand Up @@ -2914,6 +2914,7 @@ impl Eval {
if ins == nil {
ret nil
}
ins.AsAnon = true
mut captured := make([]&Var, 0)
match type self.lookup {
| &scopeChecker:
Expand Down Expand Up @@ -3019,9 +3020,10 @@ impl Eval {
}
if len(f.Generics) != len(f.Decl.Generics) {
self.s.pushErr(expr.Token, LogMsg.HasGenerics)
}
if !f.Decl.Statically && f.Decl.IsMethod() {
} else if !f.Decl.Statically && f.Decl.IsMethod() {
self.s.pushErr(expr.Token, LogMsg.MethodNotInvoked)
} else {
f.AsAnon = true
}
}

Expand Down
1 change: 1 addition & 0 deletions std/jule/sema/fn.jule
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ struct FnIns {
Scope: &Scope
Refers: &ReferenceStack
Anon: bool
AsAnon: bool // Whether this function instance used as anonymous function.

caller: builtinCaller
reloaded: bool
Expand Down

0 comments on commit 8a6f2e7

Please sign in to comment.