Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Moving global objects into the local region (at the start) #26

Merged
merged 5 commits into from
Oct 19, 2024
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
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,4 @@ foreach(FILE ${ALL_FILES})
endforeach()

set_property(TEST three_regions.vpy PROPERTY WILL_FAIL true)
set_property(TEST leak_with_global.vpy PROPERTY WILL_FAIL true)
9 changes: 5 additions & 4 deletions src/lang/interpreter.cc
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,8 @@ namespace verona::interpreter
std::cout << node->location().view() << std::endl << std::endl;

// Mermaid output
std::vector<rt::objects::Edge> edges{{nullptr, "?", frame()}};
ui->output(edges, std::string(node->location().view()));
std::vector<rt::objects::DynObject*> roots{frame()};
ui->output(roots, std::string(node->location().view()));

// Continue
return ExecNext{};
Expand Down Expand Up @@ -464,12 +464,13 @@ namespace verona::interpreter
out.open(path);
}

void output(std::vector<rt::objects::Edge>& edges, std::string message)
void
output(std::vector<rt::objects::DynObject*>& roots, std::string message)
{
out << "```" << std::endl;
out << message << std::endl;
out << "```" << std::endl;
rt::ui::mermaid(edges, out);
rt::ui::mermaid(roots, out);
if (interactive)
{
out.close();
Expand Down
75 changes: 58 additions & 17 deletions src/rt/core.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,13 @@

namespace rt::core
{

class PrototypeObject : public objects::DynObject
{
std::string name;

public:
PrototypeObject(std::string name_, objects::DynObject* prototype = nullptr)
: objects::DynObject(prototype, true), name(name_)
: objects::DynObject(prototype), name(name_)
{}

std::string get_name()
Expand All @@ -21,15 +20,19 @@ namespace rt::core
}
};

PrototypeObject framePrototypeObject{"Frame"};
inline PrototypeObject* framePrototypeObject()
{
static PrototypeObject* proto = new PrototypeObject("Frame");
return proto;
}

class FrameObject : public objects::DynObject
{
FrameObject() : objects::DynObject(&framePrototypeObject, true) {}
FrameObject() : objects::DynObject(framePrototypeObject(), true) {}

public:
FrameObject(objects::DynObject* parent_frame)
: objects::DynObject(&framePrototypeObject)
: objects::DynObject(framePrototypeObject())
{
if (parent_frame)
{
Expand All @@ -45,9 +48,17 @@ namespace rt::core
}
};

PrototypeObject funcPrototypeObject{"Function"};
PrototypeObject bytecodeFuncPrototypeObject{
"BytecodeFunction", &funcPrototypeObject};
inline PrototypeObject* funcPrototypeObject()
{
static PrototypeObject* proto = new PrototypeObject("Function");
return proto;
}
inline PrototypeObject* bytecodeFuncPrototypeObject()
{
static PrototypeObject* proto =
new PrototypeObject("BytecodeFunction", funcPrototypeObject());
return proto;
}

class FuncObject : public objects::DynObject
{
Expand All @@ -63,7 +74,7 @@ namespace rt::core

public:
BytecodeFuncObject(verona::interpreter::Bytecode* body_)
: FuncObject(&bytecodeFuncPrototypeObject), body(body_)
: FuncObject(bytecodeFuncPrototypeObject()), body(body_)
{}
~BytecodeFuncObject()
{
Expand All @@ -77,15 +88,19 @@ namespace rt::core
}
};

PrototypeObject stringPrototypeObject{"String"};
inline PrototypeObject* stringPrototypeObject()
{
static PrototypeObject* proto = new PrototypeObject("String");
return proto;
}

class StringObject : public objects::DynObject
{
std::string value;

public:
StringObject(std::string value_, bool global = false)
: objects::DynObject(&stringPrototypeObject, global), value(value_)
StringObject(std::string value_)
: objects::DynObject(stringPrototypeObject()), value(value_)
{}

std::string get_name()
Expand All @@ -106,12 +121,23 @@ namespace rt::core
}
};

StringObject TrueObject{"True", true};
StringObject FalseObject{"False", true};
inline StringObject* trueObject()
{
static StringObject* val = new StringObject("True");
return val;
}
inline StringObject* falseObject()
{
static StringObject* val = new StringObject("False");
return val;
}

// The prototype object for iterators
// TODO put some stuff in here?
PrototypeObject keyIterPrototypeObject{"KeyIterator"};
inline PrototypeObject* keyIterPrototypeObject()
{
static PrototypeObject* proto = new PrototypeObject("KeyIterator");
return proto;
}

class KeyIterObject : public objects::DynObject
{
Expand All @@ -120,7 +146,7 @@ namespace rt::core

public:
KeyIterObject(std::map<std::string, objects::DynObject*>& fields)
: objects::DynObject(&keyIterPrototypeObject),
: objects::DynObject(keyIterPrototypeObject()),
iter(fields.begin()),
iter_end(fields.end())
{}
Expand All @@ -147,4 +173,19 @@ namespace rt::core
return this;
}
};

inline std::set<objects::DynObject*>* globals()
{
static std::set<objects::DynObject*>* globals =
new std::set<objects::DynObject*>{
framePrototypeObject(),
funcPrototypeObject(),
bytecodeFuncPrototypeObject(),
stringPrototypeObject(),
keyIterPrototypeObject(),
trueObject(),
falseObject(),
};
return globals;
}
} // namespace rt::core
14 changes: 9 additions & 5 deletions src/rt/objects/dyn_object.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ namespace rt::objects
friend class Reference;
friend objects::DynObject* rt::make_iter(objects::DynObject* obj);
friend void
rt::ui::mermaid(std::vector<objects::Edge>& roots, std::ostream& out);
rt::ui::mermaid(std::vector<objects::DynObject*>& roots, std::ostream& out);
friend void destruct(DynObject* obj);
friend void dealloc(DynObject* obj);
template<typename Pre, typename Post>
Expand Down Expand Up @@ -170,20 +170,21 @@ namespace rt::objects

public:
// prototype is borrowed, the caller does not need to provide an RC.
DynObject(DynObject* prototype_ = nullptr, bool global = false)
DynObject(DynObject* prototype_ = nullptr, bool first_frame = false)
: prototype(prototype_)
{
if (!global)
if (!first_frame)
{
count++;
all_objects.insert(this);
auto local_region = get_local_region();
region = local_region;
local_region->objects.insert(this);
}

if (prototype != nullptr)
prototype->change_rc(1);
std::cout << "Allocate: " << get_name() << std::endl;
std::cout << "Allocate: " << this << std::endl;
}

// TODO This should use prototype lookup for the destructor.
Expand All @@ -198,7 +199,10 @@ namespace rt::objects
// RC is zero.
if (change_rc(0) != 0 && matched != 0)
{
ui::error("Object still has references");
std::stringstream stream;
stream << this;
stream << " still has references";
ui::error(stream.str());
}

auto r = get_region(this);
Expand Down
45 changes: 30 additions & 15 deletions src/rt/rt.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
#include "core.h"
#include "objects/dyn_object.h"

#include <algorithm>
#include <iostream>
#include <set>
#include <string>
#include <vector>

Expand Down Expand Up @@ -54,7 +56,7 @@ namespace rt
{
// TODO Add some checking. This is need to lookup the correct function in
// the prototype chain.
if (key->get_prototype() != &core::stringPrototypeObject)
if (key->get_prototype() != core::stringPrototypeObject())
{
ui::error("Key must be a string.");
}
Expand Down Expand Up @@ -96,11 +98,11 @@ namespace rt

objects::DynObject* get_true()
{
return &core::TrueObject;
return core::trueObject();
}
objects::DynObject* get_false()
{
return &core::FalseObject;
return core::falseObject();
}

void add_reference(objects::DynObject* src, objects::DynObject* target)
Expand All @@ -123,6 +125,9 @@ namespace rt

size_t pre_run()
{
std::cout << "Initilizing global objects" << std::endl;
core::globals();

std::cout << "Running test..." << std::endl;
return objects::DynObject::get_count();
}
Expand All @@ -131,17 +136,27 @@ namespace rt
{
std::cout << "Test complete - checking for cycles in local region..."
<< std::endl;
auto globals = core::globals();
if (objects::DynObject::get_count() != initial_count)
{
std::cout << "Cycles detected in local region." << std::endl;
auto objs = objects::DynObject::get_local_region()->get_objects();
std::vector<objects::Edge> edges;
for (auto obj : objs)
{
edges.push_back({nullptr, "?", obj});
}
ui.output(edges, "Cycles detected in local region.");
auto roots = objects::DynObject::get_local_region()->get_objects();
roots.erase(
std::remove_if(
roots.begin(),
roots.end(),
[&globals](auto x) { return globals->contains(x); }),
roots.end());
ui.output(roots, "Cycles detected in local region.");
}

// Freeze global objects, to low the termination of the local region
std::cout << "Freezing global objects" << std::endl;
for (auto obj : *globals)
{
obj->freeze();
}

objects::DynObject::get_local_region()->terminate_region();
if (objects::DynObject::get_count() != initial_count)
{
Expand All @@ -150,12 +165,12 @@ namespace rt
std::cout << "Final count: " << objects::DynObject::get_count()
<< std::endl;

std::vector<objects::Edge> edges;
std::vector<objects::DynObject*> roots;
for (auto obj : objects::DynObject::get_objects())
{
edges.push_back({nullptr, "?", obj});
roots.push_back(obj);
}
ui.output(edges, "Memory leak detected!");
ui.output(roots, "Memory leak detected!");

std::exit(1);
}
Expand All @@ -168,7 +183,7 @@ namespace rt
objects::DynObject* iter_next(objects::DynObject* iter)
{
assert(!iter->is_immutable());
if (iter->get_prototype() != &core::keyIterPrototypeObject)
if (iter->get_prototype() != core::keyIterPrototypeObject())
{
ui::error("Object is not an iterator.");
}
Expand All @@ -178,7 +193,7 @@ namespace rt

verona::interpreter::Bytecode* get_bytecode(objects::DynObject* func)
{
if (func->get_prototype() == &core::bytecodeFuncPrototypeObject)
if (func->get_prototype() == core::bytecodeFuncPrototypeObject())
{
return reinterpret_cast<core::BytecodeFuncObject*>(func)->get_bytecode();
}
Expand Down
4 changes: 2 additions & 2 deletions src/rt/ui.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ namespace rt::ui
class UI
{
public:
virtual void output(std::vector<objects::Edge>&, std::string) {}
virtual void output(std::vector<objects::DynObject*>&, std::string) {}
};

void mermaid(std::vector<objects::Edge>& roots, std::ostream& out);
void mermaid(std::vector<objects::DynObject*>& roots, std::ostream& out);

[[noreturn]] inline void error(const std::string& msg)
{
Expand Down
11 changes: 8 additions & 3 deletions src/rt/ui/mermaid.cc
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#include "../core.h"
#include "../objects/dyn_object.h"
#include "../ui.h"

Expand Down Expand Up @@ -30,7 +31,7 @@ namespace rt::ui
return text;
}

void mermaid(std::vector<objects::Edge>& roots, std::ostream& out)
void mermaid(std::vector<objects::DynObject*>& roots, std::ostream& out)
{
// Give a nice id to each object.
std::map<objects::DynObject*, std::size_t> visited;
Expand All @@ -56,6 +57,10 @@ namespace rt::ui
objects::DynObject* dst = e.target;
std::string key = e.key;
objects::DynObject* src = e.src;
if (unreachable && core::globals()->contains(dst))
{
return false;
}
if (src != nullptr)
{
out << " id" << visited[src] << " -->|" << escape(key) << "| ";
Expand Down Expand Up @@ -85,10 +90,10 @@ namespace rt::ui
}
return true;
};
// Output all reachable edges
// Output all reachable nodes
for (auto& root : roots)
{
objects::visit({root.src, root.key, root.target}, explore);
objects::visit(root, explore);
}

// Output the unreachable parts of the graph
Expand Down
3 changes: 3 additions & 0 deletions tests/leak_with_global.vpy
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
str = "example"

str["__proto__"].dummy = {}