1 //////////////////////////////////////////////////////////////////////// 2 // 3 // Copyright (C) 2012-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 "cdef-class.h" 31 #include "cdef-manager.h" 32 #include "cdef-method.h" 33 #include "cdef-package.h" 34 #include "cdef-property.h" 35 #include "cdef-utils.h" 36 #include "interpreter-private.h" 37 #include "ov-classdef.h" 38 #include "ov-usr-fcn.h" 39 #include "pt-eval.h" 40 41 namespace octave 42 { 43 std::string get_base_name(const std::string & nm)44 get_base_name (const std::string& nm) 45 { 46 std::string::size_type pos = nm.find_last_of ('.'); 47 48 if (pos != std::string::npos) 49 return nm.substr (pos + 1); 50 51 return nm; 52 } 53 54 void make_function_of_class(const std::string & class_name,const octave_value & fcn)55 make_function_of_class (const std::string& class_name, 56 const octave_value& fcn) 57 { 58 octave_function *of = fcn.function_value (); 59 60 of->stash_dispatch_class (class_name); 61 62 octave_user_function *uf = of->user_function_value (true); 63 64 if (uf) 65 { 66 if (get_base_name (class_name) == uf->name ()) 67 uf->mark_as_classdef_constructor (); 68 else 69 uf->mark_as_classdef_method (); 70 } 71 } 72 73 void make_function_of_class(const cdef_class & cls,const octave_value & fcn)74 make_function_of_class (const cdef_class& cls, const octave_value& fcn) 75 { 76 make_function_of_class (cls.get_name (), fcn); 77 } 78 79 cdef_class lookup_class(const std::string & name,bool error_if_not_found,bool load_if_not_found)80 lookup_class (const std::string& name, bool error_if_not_found, 81 bool load_if_not_found) 82 { 83 cdef_manager& cdm = __get_cdef_manager__ ("lookup_class"); 84 85 return cdm.find_class (name, error_if_not_found, load_if_not_found); 86 } 87 88 cdef_class lookup_class(const cdef_class & cls)89 lookup_class (const cdef_class& cls) 90 { 91 // FIXME: placeholder for the time being, the purpose 92 // is to centralized any class update activity here. 93 94 return cls; 95 } 96 97 cdef_class lookup_class(const octave_value & ov)98 lookup_class (const octave_value& ov) 99 { 100 if (ov.is_string()) 101 return lookup_class (ov.string_value ()); 102 else 103 { 104 cdef_class cls (to_cdef (ov)); 105 106 return lookup_class (cls); 107 } 108 109 return cdef_class (); 110 } 111 112 std::list<cdef_class> lookup_classes(const Cell & cls_list)113 lookup_classes (const Cell& cls_list) 114 { 115 std::list<cdef_class> retval; 116 117 for (int i = 0; i < cls_list.numel (); i++) 118 { 119 cdef_class c = lookup_class (cls_list(i)); 120 121 retval.push_back (c); 122 } 123 124 return retval; 125 } 126 127 octave_value to_ov(const cdef_object & obj)128 to_ov (const cdef_object& obj) 129 { 130 if (obj.ok ()) 131 return octave_value (new octave_classdef (obj)); 132 else 133 return octave_value (Matrix ()); 134 } 135 136 octave_value to_ov(const octave_value & ov)137 to_ov (const octave_value& ov) 138 { 139 return ov; 140 } 141 142 cdef_object to_cdef(const octave_value & val)143 to_cdef (const octave_value& val) 144 { 145 if (val.type_name () != "object") 146 error ("cannot convert '%s' into 'object'", val.type_name().c_str ()); 147 148 return dynamic_cast<octave_classdef *> (val.internal_rep ())->get_object (); 149 } 150 151 cdef_object& to_cdef_ref(const octave_value & val)152 to_cdef_ref (const octave_value& val) 153 { 154 if (val.type_name () != "object") 155 error ("cannot convert '%s' into 'object'", val.type_name().c_str ()); 156 157 return dynamic_cast<octave_classdef *> (val.internal_rep ())->get_object_ref (); 158 } 159 160 cdef_object to_cdef(const cdef_object & obj)161 to_cdef (const cdef_object& obj) 162 { 163 return obj; 164 } 165 166 octave_value to_ov(const std::list<cdef_class> & class_list)167 to_ov (const std::list<cdef_class>& class_list) 168 { 169 Cell cls (class_list.size (), 1); 170 int i = 0; 171 172 for (const auto& cdef_cls : class_list) 173 cls(i++) = to_ov (cdef_cls); 174 175 return octave_value (cls); 176 } 177 178 bool is_dummy_method(const octave_value & fcn)179 is_dummy_method (const octave_value& fcn) 180 { 181 bool retval = false; 182 183 if (fcn.is_defined ()) 184 { 185 if (fcn.is_user_function ()) 186 { 187 octave_user_function *uf = fcn.user_function_value (true); 188 189 if (! uf || ! uf->body ()) 190 retval = true; 191 } 192 } 193 else 194 retval = true; 195 196 return retval; 197 } 198 199 bool is_superclass(const cdef_class & clsa,const cdef_class & clsb,bool allow_equal,int max_depth)200 is_superclass (const cdef_class& clsa, const cdef_class& clsb, 201 bool allow_equal, int max_depth) 202 { 203 bool retval = false; 204 205 if (allow_equal && clsa == clsb) 206 retval = true; 207 else if (max_depth != 0) 208 { 209 Cell c = clsb.get ("SuperClasses").cell_value (); 210 211 for (int i = 0; ! retval && i < c.numel (); i++) 212 { 213 octave_classdef *metacls = c(i).classdef_object_value (); 214 std::string clsname = metacls->get_property (0, "Name").string_value (); 215 cdef_class cls = lookup_class (clsname); 216 217 retval = is_superclass (clsa, cls, true, 218 max_depth < 0 ? max_depth : max_depth-1); 219 } 220 } 221 222 return retval; 223 } 224 225 bool is_strict_superclass(const cdef_class & clsa,const cdef_class & clsb)226 is_strict_superclass (const cdef_class& clsa, const cdef_class& clsb) 227 { 228 return is_superclass (clsa, clsb, false); 229 } 230 231 bool is_direct_superclass(const cdef_class & clsa,const cdef_class & clsb)232 is_direct_superclass (const cdef_class& clsa, const cdef_class& clsb) 233 { 234 return is_superclass (clsa, clsb, false, 1); 235 } 236 237 cdef_package lookup_package(const std::string & name,bool error_if_not_found,bool load_if_not_found)238 lookup_package (const std::string& name, bool error_if_not_found, 239 bool load_if_not_found) 240 { 241 cdef_manager& cdm = __get_cdef_manager__ ("lookup_package"); 242 243 return cdm.find_package (name, error_if_not_found, load_if_not_found); 244 } 245 246 cdef_class get_class_context(std::string & name,bool & in_constructor)247 get_class_context (std::string& name, bool& in_constructor) 248 { 249 name = ""; 250 in_constructor = false; 251 252 cdef_class cls; 253 254 // If the dispatch class is set in the current stack frame it 255 // overrides whatever dispatch class there is for the currently 256 // executing function so that function handles returned from class 257 // methods will use the dispatch class of the class in which they 258 // are defined instead of the class in which they are executing. 259 260 tree_evaluator& tw = __get_evaluator__ ("get_class_context"); 261 262 std::string dispatch_class = tw.get_dispatch_class (); 263 264 if (! dispatch_class.empty ()) 265 return lookup_class (dispatch_class); 266 267 octave_function *fcn = tw.current_function (); 268 269 if (fcn && (fcn->is_class_method () 270 || fcn->is_classdef_constructor () 271 || fcn->is_anonymous_function_of_class () 272 || (fcn->is_private_function () 273 && ! fcn->dispatch_class ().empty ()))) 274 { 275 cls = lookup_class (fcn->dispatch_class ()); 276 277 name = fcn->name (); 278 in_constructor = fcn->is_classdef_constructor (); 279 } 280 281 return cls; 282 } 283 284 cdef_class get_class_context(void)285 get_class_context (void) 286 { 287 std::string dummy_string; 288 bool dummy_bool; 289 290 return get_class_context (dummy_string, dummy_bool); 291 } 292 293 bool check_access(const cdef_class & cls,const octave_value & acc,const std::string & meth_name,const std::string & prop_name,bool is_prop_set)294 check_access (const cdef_class& cls, const octave_value& acc, 295 const std::string& meth_name, const std::string& prop_name, 296 bool is_prop_set) 297 { 298 if (acc.is_string ()) 299 { 300 std::string acc_s = acc.string_value (); 301 302 if (acc_s == "public") 303 return true; 304 305 cdef_class ctx = get_class_context (); 306 307 // The access is private or protected, this requires a 308 // valid class context. 309 310 if (ctx.ok ()) 311 { 312 if (acc_s == "private") 313 return (ctx == cls); 314 else if (acc_s == "protected") 315 { 316 if (is_superclass (cls, ctx)) 317 // Calling a protected method in a superclass. 318 return true; 319 else if (is_strict_superclass (ctx, cls)) 320 { 321 // Calling a protected method or property in a derived class. 322 // This is only allowed if the context class knows about it 323 // and has access to it. 324 325 if (! meth_name.empty ()) 326 { 327 cdef_method m = ctx.find_method (meth_name); 328 329 if (m.ok ()) 330 return check_access (ctx, m.get ("Access"), meth_name); 331 332 return false; 333 } 334 else if (! prop_name.empty ()) 335 { 336 cdef_property p = ctx.find_property (prop_name); 337 338 if (p.ok ()) 339 { 340 octave_value p_access = p.get (is_prop_set ? 341 "SetAccess" : 342 "GetAccess"); 343 344 return check_access (ctx, p_access, meth_name, 345 prop_name, is_prop_set); 346 } 347 348 return false; 349 } 350 else 351 panic_impossible (); 352 } 353 354 return false; 355 } 356 else 357 panic_impossible (); 358 } 359 } 360 else if (acc.isobject ()) 361 { 362 cdef_class ctx = get_class_context (); 363 364 // At this point, a class context is always required. 365 if (ctx.ok ()) 366 { 367 if (ctx == cls) 368 return true; 369 370 cdef_class acc_cls (to_cdef (acc)); 371 372 if (is_superclass (acc_cls, ctx)) 373 return true; 374 } 375 } 376 else if (acc.iscell ()) 377 { 378 Cell acc_c = acc.cell_value (); 379 380 cdef_class ctx = get_class_context (); 381 382 // At this point, a class context is always required. 383 384 if (ctx.ok ()) 385 { 386 if (ctx == cls) 387 return true; 388 389 for (int i = 0; i < acc.numel (); i++) 390 { 391 cdef_class acc_cls (to_cdef (acc_c(i))); 392 393 if (is_superclass (acc_cls, ctx)) 394 return true; 395 } 396 } 397 } 398 else 399 error ("invalid property/method access in class '%s'", 400 cls.get_name ().c_str ()); 401 402 return false; 403 } 404 } 405