1 ////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (C) 1993-2021 The Octave Project Developers
4 //
5 // See the file COPYRIGHT.md in the top-level directory of this
6 // distribution or <https://octave.org/copyright/>.
7 //
8 // This file is part of Octave.
9 //
10 // Octave is free software: you can redistribute it and/or modify it
11 // under the terms of the GNU General Public License as published by
12 // the Free Software Foundation, either version 3 of the License, or
13 // (at your option) any later version.
14 //
15 // Octave is distributed in the hope that it will be useful, but
16 // WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 // GNU General Public License for more details.
19 //
20 // You should have received a copy of the GNU General Public License
21 // along with Octave; see the file COPYING.  If not, see
22 // <https://www.gnu.org/licenses/>.
23 //
24 ////////////////////////////////////////////////////////////////////////
25 
26 #if defined (HAVE_CONFIG_H)
27 #  include "config.h"
28 #endif
29 
30 #include <sstream>
31 
32 #include "file-ops.h"
33 
34 #include "fcn-info.h"
35 #include "interpreter-private.h"
36 #include "interpreter.h"
37 #include "ov-fcn.h"
38 #include "ov-usr-fcn.h"
39 #include "symrec.h"
40 #include "symscope.h"
41 #include "utils.h"
42 
43 namespace octave
44 {
insert_local(const std::string & name)45   symbol_record symbol_scope_rep::insert_local (const std::string& name)
46   {
47     symbol_record sym (name);
48 
49     insert_symbol_record (sym);
50 
51     return sym;
52   }
53 
insert_symbol_record(symbol_record & sr)54   void symbol_scope_rep::insert_symbol_record (symbol_record& sr)
55   {
56     std::size_t data_offset = num_symbols ();
57     std::string name = sr.name ();
58 
59     sr.set_data_offset (data_offset);
60 
61     m_symbols[name] = sr;
62   }
63 
insert(const std::string & name)64   symbol_record symbol_scope_rep::insert (const std::string& name)
65   {
66     table_iterator p = m_symbols.find (name);
67 
68     if (p == m_symbols.end ())
69       {
70         symbol_record ret (name);
71 
72         std::size_t data_offset = num_symbols ();
73 
74         ret.set_data_offset (data_offset);
75 
76         auto t_parent = m_parent.lock ();
77 
78         std::size_t offset = 0;
79 
80         if (is_nested () && t_parent
81             && t_parent->look_nonlocal (name, offset, ret))
82           return m_symbols[name] = ret;
83         else
84           {
85             if (m_is_static)
86               ret.mark_added_static ();
87 
88             return m_symbols[name] = ret;
89           }
90       }
91     else
92       return p->second;
93   }
94 
localfunctions(void) const95   std::list<octave_value> symbol_scope_rep::localfunctions (void) const
96   {
97     std::list<octave_value> retval;
98 
99     // Find the subfunctions of this function (which should be the
100     // primary parent function for this scope).
101 
102     // 1) m_subfunction_names contains only valid subfunctions
103     // 2) m_subfunctions contains both nested functions and subfunctions
104 
105     // loop over them.
106 
107     for (const auto& nm : m_subfunction_names)
108       {
109         auto nm_fcn_iter = m_subfunctions.find (nm);
110 
111         if (nm_fcn_iter != m_subfunctions.end ())
112           {
113             octave_value ov_fcn = nm_fcn_iter->second;
114             octave_user_code *fcn = ov_fcn.user_code_value ();
115 
116             if (! fcn)
117               continue;
118 
119             octave::symbol_scope scope = fcn->scope ();
120 
121             std::list<std::string> plst = scope.parent_fcn_names ();
122 
123             octave_fcn_handle *fh = new octave_fcn_handle (ov_fcn, nm, plst);
124 
125             retval.push_back (octave_value (fh));
126           }
127       }
128 
129     return retval;
130   }
131 
132   octave_value
dump(void) const133   symbol_scope_rep::dump (void) const
134   {
135     std::map<std::string, octave_value> m
136       = {{ "name", m_name },
137          { "nesting_depth", m_nesting_depth },
138          { "is_static", m_is_static },
139          { "symbols", dump_symbols_map () },
140          { "subfunction_names", string_vector (m_subfunction_names) },
141          { "subfunctions", dump_function_map (m_subfunctions) }};
142 
143     return octave_value (m);
144   }
145 
146   octave_value
dump_symbols_map(void) const147   symbol_scope_rep::dump_symbols_map (void) const
148   {
149     std::map<std::string, octave_value> info_map;
150 
151     for (const auto& nm_sr : m_symbols)
152       {
153         std::string nm = nm_sr.first;
154         symbol_record sr = nm_sr.second;
155         info_map[nm] = sr.dump ();
156       }
157 
158     return octave_value (info_map);
159   }
160 
symbol_list(void) const161   std::list<symbol_record> symbol_scope_rep::symbol_list (void) const
162   {
163     std::list<symbol_record> retval;
164 
165     for (const auto& nm_sr : m_symbols)
166       retval.push_back (nm_sr.second);
167 
168     return retval;
169   }
170 
171   octave_value
find_subfunction(const std::string & name) const172   symbol_scope_rep::find_subfunction (const std::string& name) const
173   {
174     subfunctions_const_iterator p = m_subfunctions.find (name);
175 
176     if (p != m_subfunctions.end ())
177       return p->second;
178 
179     auto t_parent = m_parent.lock ();
180 
181     if (t_parent)
182       return t_parent->find_subfunction (name);
183 
184     return octave_value ();
185   }
186 
187   void
mark_subfunctions_in_scope_as_private(const std::string & class_name)188   symbol_scope_rep::mark_subfunctions_in_scope_as_private (const std::string& class_name)
189   {
190     for (auto& nm_sf : m_subfunctions)
191       {
192         octave_function *fcn = nm_sf.second.function_value ();
193 
194         if (fcn)
195           fcn->mark_as_private_function (class_name);
196       }
197   }
198 
199   void
cache_parent_fcn_names(const std::list<std::string> & names)200   symbol_scope_rep::cache_parent_fcn_names (const std::list<std::string>& names)
201   {
202     m_parent_fcn_names = names;
203 
204     if (m_code && m_code->is_user_function ())
205       {
206         octave_user_function *fcn
207           = dynamic_cast<octave_user_function *> (m_code);
208 
209         fcn->stash_parent_fcn_name (names.front ());
210       }
211   }
212 
213   void
set_parent(const std::shared_ptr<symbol_scope_rep> & parent)214   symbol_scope_rep::set_parent (const std::shared_ptr<symbol_scope_rep>& parent)
215   {
216     m_parent = std::weak_ptr<symbol_scope_rep> (parent);
217   }
218 
219   void
set_primary_parent(const std::shared_ptr<symbol_scope_rep> & parent)220   symbol_scope_rep::set_primary_parent (const std::shared_ptr<symbol_scope_rep>& parent)
221   {
222     m_primary_parent = std::weak_ptr<symbol_scope_rep> (parent);
223   }
224 
225   void
cache_dir_name(const std::string & name)226   symbol_scope_rep::cache_dir_name (const std::string& name)
227   {
228     m_dir_name = octave::sys::canonicalize_file_name (name);
229   }
230 
231   bool
is_relative(const std::shared_ptr<symbol_scope_rep> & scope) const232   symbol_scope_rep::is_relative (const std::shared_ptr<symbol_scope_rep>& scope) const
233   {
234     if (is_nested ())
235       {
236         // Since is_nested is true, the following should always return a
237         // valid scope.
238 
239         auto t_parent = m_parent.lock ();
240 
241         if (t_parent)
242           {
243             // SCOPE is the parent of this scope: this scope is a child
244             // of SCOPE.
245 
246             if (t_parent == scope)
247               return true;
248           }
249 
250         auto t_primary_parent = m_primary_parent.lock ();
251 
252         if (t_primary_parent)
253           {
254             // SCOPE is the primary parent of this scope: this scope is a
255             // child (or grandchild) of SCOPE.
256             if (t_primary_parent == scope)
257               return true;
258 
259             // SCOPE and this scope share the same primary parent: they are
260             // siblings (or cousins)
261             auto scope_primary_parent = scope->primary_parent_scope_rep ();
262             if (t_primary_parent == scope_primary_parent)
263               return true;
264           }
265       }
266 
267     return false;
268   }
269 
update_nest(void)270   void symbol_scope_rep::update_nest (void)
271   {
272     auto t_parent = m_parent.lock ();
273 
274     if (t_parent)
275       {
276         // fix bad symbol_records
277         for (auto& nm_sr : m_symbols)
278           {
279             symbol_record& ours = nm_sr.second;
280 
281             std::size_t offset = 0;
282 
283             if (! ours.is_formal () && is_nested ())
284               t_parent->look_nonlocal (nm_sr.first, offset, ours);
285           }
286 
287         // The scopes of nested functions are static.
288         if (is_nested ())
289           m_is_static = true;
290       }
291     else if (m_children.size ())
292       {
293         // Parents of nested functions have static scopes.
294         m_is_static = true;
295       }
296 
297     std::list<std::string> plst = parent_fcn_names ();
298     plst.push_front (m_fcn_name);
299 
300     for (auto& scope_obj : m_children)
301       {
302         scope_obj.cache_parent_fcn_names (plst);
303         scope_obj.update_nest ();
304       }
305   }
306 
look_nonlocal(const std::string & name,std::size_t offset,symbol_record & result)307   bool symbol_scope_rep::look_nonlocal (const std::string& name,
308                                         std::size_t offset, symbol_record& result)
309   {
310     offset++;
311 
312     table_iterator p = m_symbols.find (name);
313 
314     if (p == m_symbols.end ())
315       {
316         auto t_parent = m_parent.lock ();
317 
318         if (is_nested () && t_parent)
319           return t_parent->look_nonlocal (name, offset, result);
320       }
321     else
322       {
323         // Add scope offsets because the one we found may be used in
324         // this scope but initially from another parent scope beyond
325         // that.  The parent offset will already point to the first
326         // occurrence because we do the overall nesting update from the
327         // parent function down through the lists of all children.
328 
329         std::size_t t_frame_offset = offset + p->second.frame_offset ();
330         std::size_t t_data_offset = p->second.data_offset ();
331 
332         result.set_frame_offset (t_frame_offset);
333         result.set_data_offset (t_data_offset);
334 
335         return true;
336       }
337 
338     return false;
339   }
340 
localfunctions(void) const341   std::list<octave_value> symbol_scope::localfunctions (void) const
342   {
343     if (! m_rep)
344       return std::list<octave_value> ();
345 
346     if (is_primary_fcn_scope ())
347       return m_rep->localfunctions ();
348 
349     std::shared_ptr<symbol_scope_rep> ppsr
350       = m_rep->primary_parent_scope_rep ();
351 
352     if (! ppsr)
353       return std::list<octave_value> ();
354 
355     return ppsr->localfunctions ();
356   }
357 }
358