diff --git a/CMakeLists.txt b/CMakeLists.txt index 2e2db2c..867a68b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -53,7 +53,7 @@ clangformat_targets() message(STATUS "Adding tests") -FILE(GLOB ALL_FILES +FILE(GLOB_RECURSE ALL_FILES CONFIGURE_DEPENDS tests/* ) @@ -67,3 +67,6 @@ endforeach() set_property(TEST three_regions.vpy PROPERTY WILL_FAIL true) set_property(TEST leak_with_global.vpy PROPERTY WILL_FAIL true) +set_property(TEST invalid_read.vpy PROPERTY WILL_FAIL true) +set_property(TEST invalid_shared_region.vpy PROPERTY WILL_FAIL true) +set_property(TEST invalid_write.vpy PROPERTY WILL_FAIL true) diff --git a/src/lang/bytecode.h b/src/lang/bytecode.h index 14c4a63..7f0425c 100644 --- a/src/lang/bytecode.h +++ b/src/lang/bytecode.h @@ -7,6 +7,7 @@ /// The value name is stored in the location of the node inline const trieste::TokenDef LoadFrame{"load_frame", trieste::flag::print}; inline const trieste::TokenDef StoreFrame{"store_frame", trieste::flag::print}; +inline const trieste::TokenDef SwapFrame{"swap_frame", trieste::flag::print}; /// Loads a value with a given name from the current scope or the global /// namespace. This should be used for function resolution. /// @@ -14,8 +15,8 @@ inline const trieste::TokenDef StoreFrame{"store_frame", trieste::flag::print}; inline const trieste::TokenDef LoadGlobal{"load_global", trieste::flag::print}; inline const trieste::TokenDef LoadField{"load_field"}; inline const trieste::TokenDef StoreField{"store_field"}; +inline const trieste::TokenDef SwapField{"swap_field"}; inline const trieste::TokenDef CreateObject{"create_object"}; -inline const trieste::TokenDef Proto{"prototype"}; inline const trieste::TokenDef Dictionary{"dictionary"}; inline const trieste::TokenDef String{"string", trieste::flag::print}; inline const trieste::TokenDef KeyIter{"key_iter"}; @@ -33,8 +34,6 @@ inline const trieste::TokenDef ReturnValue{"return_value"}; /// Stack: `[]::` -> `[]::::` inline const trieste::TokenDef Dup{"dup", trieste::flag::print}; -inline const trieste::TokenDef CreateRegion{"create_region"}; -inline const trieste::TokenDef FreezeObject{"freeze_object"}; inline const trieste::TokenDef Null{"null"}; inline const trieste::TokenDef Label{"label"}; inline const trieste::TokenDef Eq{"=="}; diff --git a/src/lang/interpreter.cc b/src/lang/interpreter.cc index 037ae7a..d721f05 100644 --- a/src/lang/interpreter.cc +++ b/src/lang/interpreter.cc @@ -165,12 +165,6 @@ namespace verona::interpreter obj = rt::make_iter(v); rt::remove_reference(frame(), v); } - else if (payload == Proto) - { - obj = rt::make_object(); - // RC transferred - rt::set_prototype(obj, pop("prototype source")); - } else if (payload == Func) { assert( @@ -244,6 +238,7 @@ namespace verona::interpreter if (node == StoreFrame) { + assert(stack().size() >= 1 && "the stack is too small"); auto v = pop("value to store"); std::string field{node->location().view()}; auto v2 = rt::set(frame(), field, v); @@ -251,6 +246,18 @@ namespace verona::interpreter return ExecNext{}; } + if (node == SwapFrame) + { + assert(stack().size() >= 1 && "the stack is too small"); + auto new_var = pop("swap value"); + std::string field{node->location().view()}; + + auto old_var = rt::set(frame(), field, new_var); + stack().push_back(old_var); + + return ExecNext{}; + } + if (node == LoadField) { assert(stack().size() >= 2 && "the stack is too small"); @@ -275,6 +282,7 @@ namespace verona::interpreter if (node == StoreField) { + assert(stack().size() >= 3 && "the stack is too small"); auto v = pop("value to store"); auto k = pop("lookup-key"); auto v2 = pop("lookup-value"); @@ -286,19 +294,20 @@ namespace verona::interpreter return ExecNext{}; } - if (node == CreateRegion) + if (node == SwapField) { - auto v = pop("region source"); - rt::create_region(v); - rt::remove_reference(frame(), v); - return ExecNext{}; - } + assert(stack().size() >= 3 && "the stack is too small"); + auto new_var = pop("swap value"); + auto key = pop("lookup-key"); + auto obj = pop("lookup-value"); + auto old_var = rt::set(obj, key, new_var); + stack().push_back(old_var); + + rt::move_reference(frame(), obj, new_var); + rt::move_reference(obj, frame(), old_var); + rt::remove_reference(frame(), obj); + rt::remove_reference(frame(), key); - if (node == FreezeObject) - { - auto v = pop("object to freeze"); - rt::freeze(v); - rt::remove_reference(frame(), v); return ExecNext{}; } @@ -401,7 +410,6 @@ namespace verona::interpreter if (result) { stack().push_back(result.value()); - rt::add_reference(frame(), result.value()); } rt::remove_reference(frame(), func); return ExecNext{}; diff --git a/src/lang/lang.h b/src/lang/lang.h index a685c5c..d6ad4d8 100644 --- a/src/lang/lang.h +++ b/src/lang/lang.h @@ -9,15 +9,13 @@ using namespace trieste; inline const TokenDef Ident{"ident", trieste::flag::print}; inline const TokenDef Assign{"assign"}; -inline const TokenDef Create{"create"}; inline const TokenDef For{"for"}; inline const TokenDef If{"if"}; inline const TokenDef Else{"else"}; inline const TokenDef Block{"block"}; inline const TokenDef Empty{"empty"}; inline const TokenDef Drop{"drop"}; -inline const TokenDef Freeze{"freeze"}; -inline const TokenDef Region{"region"}; +inline const TokenDef Take{"take"}; inline const TokenDef Lookup{"lookup"}; inline const TokenDef Parens{"parens"}; inline const TokenDef Method{"method"}; @@ -32,7 +30,7 @@ inline const TokenDef Compile{"compile"}; namespace verona::wf { inline const auto lv = Ident | Lookup; - inline const auto rv = lv | Empty | Null | String | Create | Call | Method; + inline const auto rv = lv | Empty | Null | String | Call | Method | Take; inline const auto cmp_values = Ident | Lookup | Null; inline const auto key = Ident | Lookup | String; inline const auto operand = Lookup | Call | Method | Ident; @@ -41,11 +39,9 @@ namespace verona::wf inline const auto grouping = (Top <<= File) | (File <<= Body) | (Body <<= Block) | (Block <<= - (Freeze | Region | Assign | If | For | Func | Return | ReturnValue | Call | - Method)++) | - (Assign <<= (Lhs >>= lv) * (Rhs >>= rv)) | - (Lookup <<= (Op >>= operand) * (Rhs >>= key)) | (Region <<= Ident) | - (Freeze <<= Ident) | (Create <<= Ident) | + (Assign | If | For | Func | Return | ReturnValue | Call | Method)++) | + (Assign <<= (Lhs >>= lv) * (Rhs >>= rv)) | (Take <<= (Lhs >>= lv)) | + (Lookup <<= (Op >>= operand) * (Rhs >>= key)) | (If <<= (Op >>= Cond) * Block * Block) | (For <<= (Key >>= Ident) * (Value >>= Ident) * (Op >>= lv) * Block) | (Eq <<= (Lhs >>= cmp_values) * (Rhs >>= cmp_values)) | @@ -56,17 +52,17 @@ namespace verona::wf inline const trieste::wf::Wellformed bytecode = (Top <<= Body) | (Body <<= - (LoadFrame | LoadGlobal | StoreFrame | LoadField | StoreField | Drop | - Null | CreateObject | CreateRegion | FreezeObject | IterNext | Print | - Eq | Neq | Jump | JumpFalse | Label | Call | Return | ReturnValue | - ClearStack | Dup)++) | - (CreateObject <<= (Dictionary | String | KeyIter | Proto | Func)) | + (LoadFrame | LoadGlobal | StoreFrame | SwapFrame | LoadField | StoreField | + SwapField | Drop | Null | CreateObject | IterNext | Print | Eq | Neq | + Jump | JumpFalse | Label | Call | Return | ReturnValue | ClearStack | + Dup)++) | + (CreateObject <<= (Dictionary | String | KeyIter | Func)) | (Func <<= Body) | (Label <<= Ident)[Ident]; } inline const auto LV = T(Ident, Lookup); inline const auto RV = - T(Empty, Ident, Lookup, Null, String, Create, Call, Method); + T(Empty, Ident, Lookup, Null, String, Call, Method, Take); inline const auto CMP_V = T(Ident, Lookup, Null); inline const auto KEY = T(Ident, Lookup, String); inline const auto OPERAND = T(Lookup, Call, Method, Ident); diff --git a/src/lang/passes/bytecode.cc b/src/lang/passes/bytecode.cc index ba54fee..b487e83 100644 --- a/src/lang/passes/bytecode.cc +++ b/src/lang/passes/bytecode.cc @@ -51,6 +51,15 @@ PassDef bytecode() << create_from(LoadField, _(Lookup)); }, + T(Compile) << (T(Take) << T(Ident)[Ident]) >> + [](auto& _) { return Seq << Null << create_from(SwapFrame, _(Ident)); }, + T(Compile) + << (T(Take) << (T(Lookup)[Lookup] << (Any[Op] * Any[Key] * End))) >> + [](auto& _) { + return Seq << (Compile << _[Op]) << (Compile << _[Key]) << Null + << SwapField; + }, + T(Compile) << (T(Assign)[Op] << (T(Ident)[Ident] * Any[Rhs])) >> [](auto& _) { return Seq << (Compile << _[Rhs]) << create_from(StoreFrame, _(Ident)) @@ -68,23 +77,6 @@ PassDef bytecode() << create_print(_(Assign)); }, - T(Compile) << (T(Freeze)[Op] << T(Ident)[Ident]) >> - [](auto& _) { - return Seq << (Compile << _[Ident]) << FreezeObject - << create_print(_(Op)); - }, - - T(Compile) << (T(Create)[Op] << T(Ident)[Ident]) >> - [](auto& _) { - return Seq << (Compile << _[Ident]) << (CreateObject << Proto); - }, - - T(Compile) << (T(Region)[Op] << T(Ident)[Ident]) >> - [](auto& _) { - return Seq << (Compile << _[Ident]) << CreateRegion - << create_print(_(Op)); - }, - T(Compile) << (T(Method)[Method] << ((T(Lookup)[Lookup] << (OPERAND[Op] * T(String)[Key])) * diff --git a/src/lang/passes/call_stmts.cc b/src/lang/passes/call_stmts.cc index 6eb6ce4..84bd886 100644 --- a/src/lang/passes/call_stmts.cc +++ b/src/lang/passes/call_stmts.cc @@ -6,8 +6,8 @@ namespace verona::wf inline const auto call_stmts = grouping | (Block <<= - (Freeze | Region | Assign | If | For | Func | Return | ReturnValue | Call | - Method | ClearStack | Print)++); + (Assign | If | For | Func | Return | ReturnValue | Call | Method | + ClearStack | Print)++); } PassDef call_stmts() diff --git a/src/lang/passes/flatten.cc b/src/lang/passes/flatten.cc index 50210e1..d0d9b20 100644 --- a/src/lang/passes/flatten.cc +++ b/src/lang/passes/flatten.cc @@ -6,16 +6,14 @@ namespace verona::wf inline const trieste::wf::Wellformed flatten = (Top <<= File) | (File <<= Body) | (Body <<= - (Freeze | Region | Assign | Eq | Neq | Label | Jump | JumpFalse | Print | - StoreFrame | LoadFrame | CreateObject | Ident | IterNext | Create | - StoreField | Lookup | String | Call | Method | Return | ReturnValue | - ClearStack)++) | + (Assign | Take | Eq | Neq | Label | Jump | JumpFalse | Print | StoreFrame | + LoadFrame | CreateObject | Ident | IterNext | StoreField | Lookup | + String | Call | Method | Return | ReturnValue | ClearStack)++) | (CreateObject <<= (KeyIter | String | Dictionary | Func)) | - (Func <<= Compile) | (Compile <<= Body) | (Create <<= Ident) | - (Assign <<= (Lhs >>= lv) * (Rhs >>= rv)) | - (Lookup <<= (Op >>= operand) * (Rhs >>= key)) | (Region <<= Ident) | - (Freeze <<= Ident) | (Call <<= Ident * List) | (Method <<= Lookup * List) | - (List <<= rv++) | (Params <<= Ident++) | + (Func <<= Compile) | (Compile <<= Body) | + (Assign <<= (Lhs >>= lv) * (Rhs >>= rv)) | (Take <<= lv) | + (Lookup <<= (Op >>= operand) * (Rhs >>= key)) | (Call <<= Ident * List) | + (Method <<= Lookup * List) | (List <<= rv++) | (Params <<= Ident++) | (Eq <<= (Lhs >>= cmp_values) * (Rhs >>= cmp_values)) | (Neq <<= (Lhs >>= cmp_values) * (Rhs >>= cmp_values)) | (Label <<= Ident)[Ident]; diff --git a/src/lang/passes/grouping.cc b/src/lang/passes/grouping.cc index 4226669..e827d8e 100644 --- a/src/lang/passes/grouping.cc +++ b/src/lang/passes/grouping.cc @@ -16,20 +16,11 @@ PassDef grouping() In(Group) * OPERAND[Op] * (T(Lookup)[Lookup] << (T(Group) << KEY[Rhs])) >> [](auto& _) { return Lookup << _(Op) << _(Rhs); }, - T(Group) << ((T(Region)[Region] << End) * T(Ident)[Ident] * End) >> - [](auto& _) { - _(Region)->extend(_(Ident)->location()); - return _(Region) << _(Ident); - }, - - T(Group) << ((T(Freeze)[Freeze] << End) * T(Ident)[Ident] * End) >> - [](auto& _) { - _(Freeze)->extend(_(Ident)->location()); - return _(Freeze) << _(Ident); - }, - T(Group) << ((T(Drop)[Drop] << End) * LV[Lhs] * End) >> [](auto& _) { return Assign << _(Lhs) << Null; }, + T(Group) << ((T(Take)[Take] << End) * LV[Lhs] * End) >> + [](auto& _) { return create_from(Take, _(Take)) << _(Lhs); }, + // function(arg, arg) --In(Func) * (T(Group)[Group] << (T(Ident)[Ident]) * @@ -66,12 +57,6 @@ PassDef grouping() return create_from(Method, _(Group)) << _(Lookup) << list; }, - T(Group) << ((T(Create)[Create] << End) * T(Ident)[Ident] * End) >> - [](auto& _) { - _(Create)->extend(_(Ident)->location()); - return Group << (_(Create) << _(Ident)); - }, - T(Assign) << ((T(Group) << LV[Lhs] * End) * ((T(Group) << (RV[Rhs] * End)) / (RV[Rhs] * End)) * End) >> @@ -123,10 +108,10 @@ PassDef grouping() << (Body << _(Block)); }, // Normalize parenthesis with a single node to also have a list token - T(Parens)[Parens] << (T(Group) << (Any[Ident] * End)) >> + T(Parens)[Parens] << ((T(Group) << (RV[Rhs] * End)) / (RV[Rhs] * End)) >> [](auto& _) { return create_from(Parens, _(Parens)) - << (create_from(List, _(Parens)) << _(Ident)); + << (create_from(List, _(Parens)) << _(Rhs)); }, T(Return)[Return] << ((T(Group) << End) * End) >> diff --git a/src/lang/passes/parse.cc b/src/lang/passes/parse.cc index df3cb1f..2930608 100644 --- a/src/lang/passes/parse.cc +++ b/src/lang/passes/parse.cc @@ -4,8 +4,8 @@ namespace verona::wf { using namespace trieste::wf; - inline const auto parse_tokens = Region | Ident | Lookup | Empty | Freeze | - Drop | Null | String | Create | Parens; + inline const auto parse_tokens = + Ident | Lookup | Empty | Drop | Take | Null | String | Parens; inline const auto parse_groups = Group | Assign | If | Else | Block | For | Func | List | Return; @@ -157,9 +157,7 @@ trieste::Parse parser() m.push(Block); }, "drop\\b" >> [](auto& m) { m.add(Drop); }, - "create\\b" >> [](auto& m) { m.add(Create); }, - "freeze\\b" >> [](auto& m) { m.add(Freeze); }, - "region\\b" >> [](auto& m) { m.add(Region); }, + "take\\b" >> [](auto& m) { m.add(Take); }, "None\\b" >> [](auto& m) { m.add(Null); }, "[0-9A-Za-z_]+" >> [](auto& m) { m.add(Ident); }, "\\[" >> [](auto& m) { m.push(Lookup); }, diff --git a/src/rt/core.h b/src/rt/core.h index 0f801ac..9fb0499 100644 --- a/src/rt/core.h +++ b/src/rt/core.h @@ -197,6 +197,59 @@ namespace rt::core } }; + // The prototype object for cown + inline PrototypeObject* cownPrototypeObject() + { + static PrototypeObject* proto = new PrototypeObject("Cown"); + return proto; + } + + class CownObject : public objects::DynObject + { + public: + CownObject(objects::DynObject* region) + : objects::DynObject(cownPrototypeObject()) + { + // FIXME: Add once regions are reified + // assert( + // region->get_prototype() == regionPrototype() && + // "Cowns can only store regions"); + // + // FIXME: Also check that the region has a LRC == 1, with 1 + // being the reference passed into this constructor + + if (region->change_rc(0) != 1) + { + ui::error("regions used for cown creation need to have an rc of 1"); + } + + // this->set would fail, since this is a cown + this->fields["region"] = region; + } + + std::string get_name() + { + return ""; + } + + objects::DynObject* is_primitive() + { + return this; + } + + bool is_opaque() override + { + // For now there is no mechanism to aquire the cown, it'll therefore + // always be opaque. + return true; + } + + bool is_cown() override + { + return true; + } + }; + inline std::set* globals() { static std::set* globals = @@ -207,6 +260,7 @@ namespace rt::core builtinFuncPrototypeObject(), stringPrototypeObject(), keyIterPrototypeObject(), + cownPrototypeObject(), trueObject(), falseObject(), }; diff --git a/src/rt/core/builtin.cc b/src/rt/core/builtin.cc index 12caa44..4810b02 100644 --- a/src/rt/core/builtin.cc +++ b/src/rt/core/builtin.cc @@ -3,6 +3,15 @@ namespace rt::core { + rt::objects::DynObject* + pop(std::vector* stack, char const* data_info) + { + auto v = stack->back(); + stack->pop_back(); + std::cout << "pop " << v << " (" << data_info << ")" << std::endl; + return v; + } + void mermaid_builtins(ui::UI* ui) { if (!ui->is_mermaid()) @@ -50,8 +59,56 @@ namespace rt::core }); } + void ctor_builtins() + { + add_builtin("cown", [](auto frame, auto stack, auto args) { + assert(args == 1); + + auto region = pop(stack, "region for cown creation"); + auto cown = make_cown(region); + move_reference(frame, cown, region); + + return cown; + }); + + add_builtin("create", [](auto frame, auto stack, auto args) { + assert(args == 1); + + auto obj = make_object(); + // RC transferred + rt::set_prototype(obj, pop(stack, "prototype source")); + + return obj; + }); + } + + void action_builtins() + { + add_builtin("freeze", [](auto frame, auto stack, auto args) { + assert(args == 1); + + auto value = pop(stack, "object to freeze"); + freeze(value); + remove_reference(frame, value); + + return std::nullopt; + }); + + add_builtin("region", [](auto frame, auto stack, auto args) { + assert(args == 1); + + auto value = pop(stack, "region source"); + create_region(value); + remove_reference(frame, value); + + return std::nullopt; + }); + } + void init_builtins(ui::UI* ui) { mermaid_builtins(ui); + ctor_builtins(); + action_builtins(); } } diff --git a/src/rt/objects/dyn_object.h b/src/rt/objects/dyn_object.h index 45e5009..0a6cde8 100644 --- a/src/rt/objects/dyn_object.h +++ b/src/rt/objects/dyn_object.h @@ -16,6 +16,11 @@ #include #include +namespace rt::core +{ + class CownObject; +} + namespace rt::objects { constexpr uintptr_t ImmutableTag{1}; @@ -29,6 +34,7 @@ namespace rt::objects friend objects::DynObject* rt::make_iter(objects::DynObject* obj); friend class ui::MermaidUI; friend class ui::MermaidDiagram; + friend class core::CownObject; friend void destruct(DynObject* obj); friend void dealloc(DynObject* obj); template @@ -77,8 +83,11 @@ namespace rt::objects return; } - assert(target->parent == src); - Region::dec_prc(target); + if (src) + { + assert(target->parent == src); + Region::dec_prc(target); + } return; } @@ -110,7 +119,7 @@ namespace rt::objects { std::cout << "Change RC: " << get_name() << " " << rc << " + " << delta << std::endl; - if (!is_immutable()) + if (!(is_immutable() || is_cown())) { assert(delta == 0 || rc != 0); rc += delta; @@ -230,12 +239,22 @@ namespace rt::objects return nullptr; } + virtual bool is_opaque() + { + return false; + } + + virtual bool is_cown() + { + return false; + } + void freeze() { // TODO SCC algorithm visit(this, [](Edge e) { auto obj = e.target; - if (obj->is_immutable()) + if (!obj || obj->is_immutable()) return false; auto r = get_region(obj); @@ -244,7 +263,8 @@ namespace rt::objects get_region(obj)->objects.erase(obj); } obj->region.set_tag(ImmutableTag); - return true; + + return !obj->is_cown(); }); } @@ -271,12 +291,29 @@ namespace rt::objects return nullptr; } - [[nodiscard]] DynObject* set(std::string name, DynObject* value) + void assert_modifiable() { if (is_immutable()) { ui::error("Cannot mutate immutable object"); } + + if (is_opaque()) + { + if (is_cown()) + { + ui::error("Cannot mutate a cown that is not aquired"); + } + else + { + ui::error("Cannot mutate opaque object"); + } + } + } + + [[nodiscard]] DynObject* set(std::string name, DynObject* value) + { + assert_modifiable(); DynObject* old = fields[name]; fields[name] = value; return old; @@ -285,10 +322,7 @@ namespace rt::objects // The caller must provide an rc for value. [[nodiscard]] DynObject* set_prototype(DynObject* value) { - if (is_immutable()) - { - ui::error("Cannot mutate immutable object"); - } + assert_modifiable(); DynObject* old = prototype; prototype = value; return old; diff --git a/src/rt/rt.cc b/src/rt/rt.cc index 6125ffb..1a73f0b 100644 --- a/src/rt/rt.cc +++ b/src/rt/rt.cc @@ -45,17 +45,22 @@ namespace rt { return new objects::DynObject(); } - objects::DynObject* make_frame(objects::DynObject* parent) { return new core::FrameObject(parent); } + objects::DynObject* make_cown(objects::DynObject* region) + { + return new core::CownObject(region); + } thread_local objects::RegionPointer objects::DynObject::local_region = new Region(); void freeze(objects::DynObject* obj) { + // Cown specific handling of the freeze operation is handled by the + // `freeze()` implementation of the object obj->freeze(); } @@ -66,6 +71,17 @@ namespace rt objects::DynObject* get(objects::DynObject* obj, std::string key) { + if (obj->is_opaque()) + { + if (obj->is_cown()) + { + ui::error("Cannot access data on a cown that is not aquired"); + } + else + { + ui::error("Cannot access data on an opaque type"); + } + } return obj->get(key); } diff --git a/src/rt/rt.h b/src/rt/rt.h index f9b4634..bb5a993 100644 --- a/src/rt/rt.h +++ b/src/rt/rt.h @@ -22,6 +22,7 @@ namespace rt objects::DynObject* make_str(std::string str_value); objects::DynObject* make_object(); objects::DynObject* make_frame(objects::DynObject* parent); + objects::DynObject* make_cown(objects::DynObject* region); void freeze(objects::DynObject* obj); void create_region(objects::DynObject* objects); diff --git a/tests/cowns/invalid_read.vpy b/tests/cowns/invalid_read.vpy new file mode 100644 index 0000000..636b8bc --- /dev/null +++ b/tests/cowns/invalid_read.vpy @@ -0,0 +1,7 @@ +# A simple cown +a = {} +region(a) +co = cown(take a) + +# Reading the cown is forbidden +dummy = co.region diff --git a/tests/cowns/invalid_shared_region.vpy b/tests/cowns/invalid_shared_region.vpy new file mode 100644 index 0000000..50ac365 --- /dev/null +++ b/tests/cowns/invalid_shared_region.vpy @@ -0,0 +1,9 @@ +# Creating a region +a = {} +region(a) + +# LRC = 2 +r01 = a + +# Error due to invalid RC +co = cown(take a) diff --git a/tests/cowns/invalid_write.vpy b/tests/cowns/invalid_write.vpy new file mode 100644 index 0000000..f7a73fb --- /dev/null +++ b/tests/cowns/invalid_write.vpy @@ -0,0 +1,7 @@ +# A simple cown +a = {} +region(a) +co = cown(take a) + +# Modifying the cown is forbidden +co.other = {} diff --git a/tests/cowns/valid_01.vpy b/tests/cowns/valid_01.vpy new file mode 100644 index 0000000..5ed36f9 --- /dev/null +++ b/tests/cowns/valid_01.vpy @@ -0,0 +1,17 @@ +global = {} + +# A simple cown +a = {} +a.b = {} +region(a) + + +c01 = cown(take a) + +# Store the cown in a global +global.cown = take c01 + +# Freeze global with a cown +freeze(global) + +drop global diff --git a/tests/exprs/take.vpy b/tests/exprs/take.vpy new file mode 100644 index 0000000..289af4e --- /dev/null +++ b/tests/exprs/take.vpy @@ -0,0 +1,7 @@ +a = {} +a.var = "hey" + +temp = take a.var +a.var = take temp + +a.var = take a.var diff --git a/tests/prototype.vpy b/tests/prototype.vpy index e6d374e..cb3f5e8 100644 --- a/tests/prototype.vpy +++ b/tests/prototype.vpy @@ -2,11 +2,9 @@ a = {} b = {} c = {} -b["f"] = a -drop a +b["f"] = take a -c = create b -drop b +c = create(take b) c["g"] = c["f"] -drop c \ No newline at end of file +drop c diff --git a/tests/recursive_list.vpy b/tests/recursive_list.vpy index becc02d..1179807 100644 --- a/tests/recursive_list.vpy +++ b/tests/recursive_list.vpy @@ -32,13 +32,13 @@ def new_list(): list = new_list() # Required to not leak memory -region list +region(list) value = "x" # Dyrona doesn't freeze shared objects automatically (yet) proto = value["__proto__"] -freeze proto +freeze(proto) drop proto insert(list.head, {}) diff --git a/tests/three_regions.vpy b/tests/three_regions.vpy index cbb7426..d37fc86 100644 --- a/tests/three_regions.vpy +++ b/tests/three_regions.vpy @@ -3,37 +3,34 @@ a = {} a["self"] = a b = {} a["b"] = b -region a +region(a) + c = {} c["self"] = c -d = {} -c["d"] = d -region c +c["d"] = {} +region(c) e = {} e["self"] = e -f = {} -e["f"] = f -region e +e["f"] = {} +region(e) # connect first region to second in two ways -a["c"] = c -b["d"] = d +a.c = c +a.b.d = c.d # Connect first region to third with single entry point -b["e"] = f +a.b.e = e.f # Now freeze part of the first region, and the reachable parts of the # second and third regions -freeze b +freeze(b) # Drop all the references drop a -drop d -drop f -drop b +drop b drop c drop e diff --git a/tests/tobias3.vpy b/tests/tobias3.vpy index b0e06b4..4fd8d94 100644 --- a/tests/tobias3.vpy +++ b/tests/tobias3.vpy @@ -2,7 +2,7 @@ a = {} b = {} a["f"] = b c = a.f -region a +region(a) drop b drop c