# HG changeset patch # User John W. Eaton # Date 1523413357 14400 # Node ID c80323fe49389dd54d4948923bedbf257a66c9d1 # Parent b3ee0179d7b005a29bea15bc9451e0e0ab85d39c improve handling of global symbols (bug #53599) * load-save.cc (install_loaded_variable): For global values, check global status and link to global symbol if needed. * symrec.h (symbol_record::symbol_record_rep::unbind_global_rep): New function. (symbol_record::symbol_record_rep::clear): Use unbind_global_rep to clearing global symbols. (symbol_record::symbol_record_rep::is_marked_global): New function. (symbol_record::symbol_record_rep::is_global): Use it. (symbol_record::symbol_record_rep::bind_fwd_rep): Don't forward again if symbol is already global. (symbol_record::unbind_fwd_rep): Eliminate argument. Change all uses. (symbol_record::symbol_record_rep::unbind_fwd_rep): Eliminate argument. Simplify. * test/bug-53599.tst: New file. * test/module.mk: Update. diff -r b3ee0179d7b0 -r c80323fe4938 libinterp/corefcn/load-save.cc --- a/libinterp/corefcn/load-save.cc Tue Apr 10 08:39:24 2018 +0200 +++ b/libinterp/corefcn/load-save.cc Tue Apr 10 22:22:37 2018 -0400 @@ -153,12 +153,18 @@ if (global) { - scope.clear_variable (name); - scope.mark_global (name); - symtab.assign (name, val); + octave::symbol_record sym = scope.find_symbol (name); + + if (! sym.is_global ()) + { + octave::symbol_scope global_scope = symtab.global_scope (); + octave::symbol_record global_sym = global_scope.find_symbol (name); + + sym.bind_fwd_rep (global_scope.get_rep (), global_sym); + } } - else - scope.assign (name, val); + + scope.assign (name, val); } // Return TRUE if NAME matches one of the given globbing PATTERNS. diff -r b3ee0179d7b0 -r c80323fe4938 libinterp/corefcn/symrec.h --- a/libinterp/corefcn/symrec.h Tue Apr 10 08:39:24 2018 +0200 +++ b/libinterp/corefcn/symrec.h Tue Apr 10 22:22:37 2018 -0400 @@ -203,7 +203,7 @@ // clear the local value. if (is_global ()) - unbind_fwd_rep (); + unbind_global_rep (); if (! (is_hidden () || is_inherited ())) { @@ -280,7 +280,7 @@ if (auto t_fwd_rep = m_fwd_rep.lock ()) return t_fwd_rep->is_global (); - return m_storage_class & global; + return is_marked_global (); } bool is_persistent (void) const @@ -363,6 +363,11 @@ m_storage_class |= global; } + bool is_marked_global (void) const + { + return m_storage_class & global; + } + void mark_persistent (void) { if (auto t_fwd_rep = m_fwd_rep.lock ()) @@ -482,51 +487,51 @@ if (auto t_fwd_rep = m_fwd_rep.lock ()) { + // If this is the symbol in the global scope, then don't + // forward again! + + if (t_fwd_rep->is_marked_global ()) + return; + t_fwd_rep->bind_fwd_rep (fwd_scope, fwd_rep); - return; } m_fwd_scope = fwd_scope; m_fwd_rep = fwd_rep; } - void unbind_fwd_rep (bool recurse = true) + void unbind_fwd_rep (void) { - // When unbinding variables in a script scope, recurse will be - // false and we will only break the immediate link. By doing - // that, we ensure that any variables that are made global in - // a script remain linked as globals in the enclosing scope. - - // When unbinding variables in a (possibly nested) function - // scope, recurse will be true and we will break the link to - // the global scope, not just the immediate link to the parent - // scope of the nested function. - - if (recurse) - { - if (auto t_fwd_rep = m_fwd_rep.lock ()) - { - // Currently, it is only possible to have at most two - // levels of indirection here, either - // - // nested function -> parent function -> global scope - // - // or - // - // script -> top level scope -> global scope - // - // so we can break the recursion here by setting the - // argument to unbind_fwd_rep to false. - - t_fwd_rep->unbind_fwd_rep (false); - return; - } - } + // When unbinding variables in a script scope, we only break + // the immediate link. By doing that, we ensure that any + // variables that are made global in a script remain linked as + // globals in the enclosing scope. m_fwd_scope = std::weak_ptr (); m_fwd_rep.reset (); } + void unbind_global_rep (void) + { + // Break the link to the global symbol_record_rep. These must + // forwarded, so we don't do anything unless the forward rep + // points to something. + + if (auto t_fwd_rep = m_fwd_rep.lock ()) + { + if (t_fwd_rep->is_marked_global ()) + { + // The rep this object points to is in the global + // scope, so delete the link to it. + + m_fwd_scope = std::weak_ptr (); + m_fwd_rep.reset (); + } + else + t_fwd_rep->unbind_fwd_rep (); + } + } + std::shared_ptr dup (const std::shared_ptr& new_scope) const; @@ -657,7 +662,6 @@ void mark_formal (void) { m_rep->mark_formal (); } void mark_hidden (void) { m_rep->mark_hidden (); } void mark_inherited (void) { m_rep->mark_inherited (); } - void mark_global (void) { m_rep->mark_global (); } void mark_persistent (void) { m_rep->mark_persistent (); } void mark_added_static (void) { m_rep->mark_added_static (); } @@ -669,6 +673,9 @@ void unmark_persistent (void) { m_rep->unmark_persistent (); } void unmark_added_static (void) { m_rep->unmark_added_static (); } + bool is_marked_global (void) const { return m_rep->is_marked_global (); } + void mark_global (void) { m_rep->mark_global (); } + void init_persistent (void) { m_rep->init_persistent (); } unsigned int storage_class (void) const { return m_rep->storage_class (); } @@ -679,7 +686,7 @@ m_rep->bind_fwd_rep (fwd_scope, sr.m_rep); } - void unbind_fwd_rep (bool recurse = true) { m_rep->unbind_fwd_rep (recurse); } + void unbind_fwd_rep (void) { m_rep->unbind_fwd_rep (); } octave_value dump (context_id context) const { diff -r b3ee0179d7b0 -r c80323fe4938 libinterp/corefcn/symscope.cc --- a/libinterp/corefcn/symscope.cc Tue Apr 10 08:39:24 2018 +0200 +++ b/libinterp/corefcn/symscope.cc Tue Apr 10 22:22:37 2018 -0400 @@ -237,6 +237,6 @@ symbol_scope_rep::unbind_script_symbols (void) { for (auto& nm_sr : m_symbols) - nm_sr.second.unbind_fwd_rep (false); + nm_sr.second.unbind_fwd_rep (); } } diff -r b3ee0179d7b0 -r c80323fe4938 libinterp/corefcn/symscope.h --- a/libinterp/corefcn/symscope.h Tue Apr 10 08:39:24 2018 +0200 +++ b/libinterp/corefcn/symscope.h Tue Apr 10 22:22:37 2018 -0400 @@ -792,6 +792,10 @@ m_rep->mark_hidden (name); } + // This function should only be called for the global + // symbol_scope, and that should only happen when it is added to + // the global symbol_scope. + void mark_global (const std::string& name) { if (m_rep) diff -r b3ee0179d7b0 -r c80323fe4938 test/bug-53599.tst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/bug-53599.tst Tue Apr 10 22:22:37 2018 -0400 @@ -0,0 +1,23 @@ +%!function rval = sggval (val) +%! global gval +%! if (nargin == 1) +%! gval = val; +%! else +%! rval = gval; +%! endif +%!endfunction + +%!test +%! global gval +%! assert (isempty (gval)) +%! sggval (13); +%! assert (sggval (), 13); +%! assert (gval, 13); +%! clear global gval +%! assert (sggval (), []) +%! gval = 42; +%! assert (sggval (), []); +%! clear gval +%! global gval +%! gval = 42; +%! assert (sggval (), 42); diff -r b3ee0179d7b0 -r c80323fe4938 test/module.mk --- a/test/module.mk Tue Apr 10 08:39:24 2018 +0200 +++ b/test/module.mk Tue Apr 10 22:22:37 2018 -0400 @@ -12,6 +12,7 @@ %reldir%/bug-38576.tst \ %reldir%/bug-46330.tst \ %reldir%/bug-49904.tst \ + %reldir%/bug-53599.tst \ %reldir%/colormaps.tst \ %reldir%/command.tst \ %reldir%/complex.tst \