1 ////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (C) 1996-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 <iostream>
31 
32 #include "Array.h"
33 
34 #include "defun.h"
35 #include "error.h"
36 #include "interpreter.h"
37 #include "interpreter-private.h"
38 #include "ops.h"
39 #include "ov-typeinfo.h"
40 #include "ov.h"
41 
42 namespace octave
43 {
44   // FIXME: we should also store all class names and provide a
45   // way to list them (calling class with nargin == 0?).
46 
as_nd_array(const Array<int> & x)47   static NDArray as_nd_array (const Array<int>& x)
48   {
49     NDArray retval (x.dims ());
50 
51     for (int i = 0; i < x.numel (); i++)
52       retval.xelem(i) = x(i);
53 
54     return retval;
55   }
56 
as_bool_nd_array(const Array<void * > & x)57   static boolNDArray as_bool_nd_array (const Array<void *>& x)
58   {
59     boolNDArray retval (x.dims ());
60 
61     for (octave_idx_type i = 0; i < x.numel (); i++)
62       retval.xelem (i) = x(i);
63 
64     return retval;
65   }
66 
type_info(int init_tab_sz)67   type_info::type_info (int init_tab_sz)
68     : num_types (0), types (dim_vector (init_tab_sz, 1), ""),
69       vals (dim_vector (init_tab_sz, 1)),
70       unary_class_ops (dim_vector (octave_value::num_unary_ops, 1), nullptr),
71       unary_ops (dim_vector (octave_value::num_unary_ops, init_tab_sz), nullptr),
72       non_const_unary_ops (dim_vector (octave_value::num_unary_ops, init_tab_sz), nullptr),
73       binary_class_ops (dim_vector (octave_value::num_binary_ops, 1), nullptr),
74       binary_ops (dim_vector (octave_value::num_binary_ops, init_tab_sz, init_tab_sz), nullptr),
75       compound_binary_class_ops (dim_vector (octave_value::num_compound_binary_ops, 1), nullptr),
76       compound_binary_ops (dim_vector (octave_value::num_compound_binary_ops, init_tab_sz, init_tab_sz), nullptr),
77       cat_ops (dim_vector (init_tab_sz, init_tab_sz), nullptr),
78       assign_ops (dim_vector (octave_value::num_assign_ops, init_tab_sz, init_tab_sz), nullptr),
79       assignany_ops (dim_vector (octave_value::num_assign_ops, init_tab_sz), nullptr),
80       pref_assign_conv (dim_vector (init_tab_sz, init_tab_sz), -1),
81       widening_ops (dim_vector (init_tab_sz, init_tab_sz), nullptr)
82   {
83     install_types (*this);
84 
85     install_ops (*this);
86   }
87 
register_type(const std::string & t_name,const std::string &,const octave_value & val,bool abort_on_duplicate)88   int type_info::register_type (const std::string& t_name,
89                                 const std::string& /* c_name */,
90                                 const octave_value& val,
91                                 bool abort_on_duplicate)
92   {
93     int i = 0;
94 
95     for (i = 0; i < num_types; i++)
96       {
97         if (t_name == types (i))
98           {
99             if (abort_on_duplicate)
100               {
101                 std::cerr << "duplicate type " << t_name << std::endl;
102                 abort ();
103               }
104 
105             warning ("duplicate type %s\n", t_name.c_str ());
106 
107             return i;
108           }
109       }
110 
111     int len = types.numel ();
112 
113     if (i == len)
114       {
115         len *= 2;
116 
117         types.resize (dim_vector (len, 1), "");
118 
119         vals.resize (dim_vector (len, 1), nullptr);
120 
121         unary_ops.resize
122           (dim_vector (octave_value::num_unary_ops, len), nullptr);
123 
124         non_const_unary_ops.resize
125           (dim_vector (octave_value::num_unary_ops, len), nullptr);
126 
127         binary_ops.resize
128           (dim_vector (octave_value::num_binary_ops, len, len), nullptr);
129 
130         compound_binary_ops.resize
131           (dim_vector (octave_value::num_compound_binary_ops, len, len),
132            nullptr);
133 
134         cat_ops.resize (dim_vector (len, len), nullptr);
135 
136         assign_ops.resize
137           (dim_vector (octave_value::num_assign_ops, len, len), nullptr);
138 
139         assignany_ops.resize
140           (dim_vector (octave_value::num_assign_ops, len), nullptr);
141 
142         pref_assign_conv.resize (dim_vector (len, len), -1);
143 
144         widening_ops.resize (dim_vector (len, len), nullptr);
145       }
146 
147     types (i) = t_name;
148 
149     // Yes, this object is intentionally not deleted in the destructor
150     // so that we avoid a crash on exit for user-defined data types.
151     // See bug #53156.  If that problem is properly fixed, then this
152     // could be stored as an object instead of a pointer to an object
153     // allocated with new.
154 
155     vals(i) = new octave_value (val);
156 
157     num_types++;
158 
159     return i;
160   }
161 
register_unary_class_op(octave_value::unary_op op,type_info::unary_class_op_fcn f,bool abort_on_duplicate)162   bool type_info::register_unary_class_op (octave_value::unary_op op,
163                                            type_info::unary_class_op_fcn f,
164                                            bool abort_on_duplicate)
165   {
166     if (lookup_unary_class_op (op))
167       {
168         std::string op_name = octave_value::unary_op_as_string (op);
169 
170         if (abort_on_duplicate)
171           {
172             std::cerr << "duplicate unary operator '" << op_name
173                       << "' for class dispatch" << std::endl;
174             abort ();
175           }
176 
177         warning ("duplicate unary operator '%s' for class dispatch",
178                  op_name.c_str ());
179       }
180 
181     unary_class_ops.checkelem (static_cast<int> (op))
182       = reinterpret_cast<void *> (f);
183 
184     return false;
185   }
186 
register_unary_op(octave_value::unary_op op,int t,unary_op_fcn f,bool abort_on_duplicate)187   bool type_info::register_unary_op (octave_value::unary_op op, int t,
188                                      unary_op_fcn f, bool abort_on_duplicate)
189   {
190     if (lookup_unary_op (op, t))
191       {
192         std::string op_name = octave_value::unary_op_as_string (op);
193         std::string type_name = types(t);
194 
195         if (abort_on_duplicate)
196           {
197             std::cerr << "duplicate unary operator '" << op_name
198                       << "' for type '" << type_name << "'" << std::endl;
199             abort ();
200           }
201 
202         warning ("duplicate unary operator '%s' for type '%s'",
203                  op_name.c_str (), type_name.c_str ());
204       }
205 
206     unary_ops.checkelem (static_cast<int> (op), t) = reinterpret_cast<void *> (f);
207 
208     return false;
209   }
210 
211   bool
register_non_const_unary_op(octave_value::unary_op op,int t,type_info::non_const_unary_op_fcn f,bool abort_on_duplicate)212   type_info::register_non_const_unary_op (octave_value::unary_op op, int t,
213                                           type_info::non_const_unary_op_fcn f,
214                                           bool abort_on_duplicate)
215   {
216     if (lookup_non_const_unary_op (op, t))
217       {
218         std::string op_name = octave_value::unary_op_as_string (op);
219         std::string type_name = types(t);
220 
221         if (abort_on_duplicate)
222           {
223             std::cerr << "duplicate unary operator '" << op_name
224                       << "' for type '" << type_name << "'" << std::endl;
225             abort ();
226           }
227 
228         warning ("duplicate unary operator '%s' for type '%s'",
229                  op_name.c_str (), type_name.c_str ());
230       }
231 
232     non_const_unary_ops.checkelem (static_cast<int> (op), t)
233       = reinterpret_cast<void *> (f);
234 
235     return false;
236   }
237 
238   bool
register_binary_class_op(octave_value::binary_op op,type_info::binary_class_op_fcn f,bool abort_on_duplicate)239   type_info::register_binary_class_op (octave_value::binary_op op,
240                                        type_info::binary_class_op_fcn f,
241                                        bool abort_on_duplicate)
242   {
243     if (lookup_binary_class_op (op))
244       {
245         std::string op_name = octave_value::binary_op_as_string (op);
246 
247         if (abort_on_duplicate)
248           {
249 
250             std::cerr << "duplicate binary operator '" << op_name
251                       << "' for class dispatch" << std::endl;
252             abort ();
253           }
254 
255         warning ("duplicate binary operator '%s' for class dispatch",
256                  op_name.c_str ());
257       }
258 
259     binary_class_ops.checkelem (static_cast<int> (op))
260       = reinterpret_cast<void *> (f);
261 
262     return false;
263   }
264 
register_binary_op(octave_value::binary_op op,int t1,int t2,type_info::binary_op_fcn f,bool abort_on_duplicate)265   bool type_info::register_binary_op (octave_value::binary_op op,
266                                       int t1, int t2,
267                                       type_info::binary_op_fcn f,
268                                       bool abort_on_duplicate)
269   {
270     if (lookup_binary_op (op, t1, t2))
271       {
272         std::string op_name = octave_value::binary_op_as_string (op);
273         std::string t1_name = types(t1);
274         std::string t2_name = types(t2);
275 
276         if (abort_on_duplicate)
277           {
278             std::cerr << "duplicate binary operator '" << op_name
279                       << "' for types '" << t1_name << "' and '"
280                       << t2_name << "'" << std::endl;
281             abort ();
282           }
283 
284         warning ("duplicate binary operator '%s' for types '%s' and '%s'",
285                  op_name.c_str (), t1_name.c_str (), t1_name.c_str ());
286       }
287 
288     binary_ops.checkelem (static_cast<int> (op), t1, t2)
289       = reinterpret_cast<void *> (f);
290 
291     return false;
292   }
293 
294   bool
register_binary_class_op(octave_value::compound_binary_op op,type_info::binary_class_op_fcn f,bool abort_on_duplicate)295   type_info::register_binary_class_op (octave_value::compound_binary_op op,
296                                        type_info::binary_class_op_fcn f,
297                                        bool abort_on_duplicate)
298   {
299     if (lookup_binary_class_op (op))
300       {
301         std::string op_name = octave_value::binary_op_fcn_name (op);
302 
303         if (abort_on_duplicate)
304           {
305             std::cerr << "duplicate compound binary operator '"
306                       << op_name << "' for class dispatch" << std::endl;
307             abort ();
308           }
309 
310         warning ("duplicate compound binary operator '%s' for class dispatch",
311                  op_name.c_str ());
312       }
313 
314     compound_binary_class_ops.checkelem (static_cast<int> (op))
315       = reinterpret_cast<void *> (f);
316 
317     return false;
318   }
319 
register_binary_op(octave_value::compound_binary_op op,int t1,int t2,type_info::binary_op_fcn f,bool abort_on_duplicate)320   bool type_info::register_binary_op (octave_value::compound_binary_op op,
321                                       int t1, int t2,
322                                       type_info::binary_op_fcn f,
323                                       bool abort_on_duplicate)
324   {
325     if (lookup_binary_op (op, t1, t2))
326       {
327         std::string op_name = octave_value::binary_op_fcn_name (op);
328         std::string t1_name = types(t1);
329         std::string t2_name = types(t2);
330 
331         if (abort_on_duplicate)
332           {
333             std::cerr << "duplicate compound binary operator '"
334                       << op_name << "' for types '" << t1_name
335                       << "' and '" << t2_name << "'" << std::endl;
336             abort ();
337           }
338 
339         warning ("duplicate compound binary operator '%s' for types '%s' and '%s'",
340                  op_name.c_str (), t1_name.c_str (), t1_name.c_str ());
341       }
342 
343     compound_binary_ops.checkelem (static_cast<int> (op), t1, t2)
344       = reinterpret_cast<void *> (f);
345 
346     return false;
347   }
348 
register_cat_op(int t1,int t2,type_info::cat_op_fcn f,bool abort_on_duplicate)349   bool type_info::register_cat_op (int t1, int t2, type_info::cat_op_fcn f,
350                                    bool abort_on_duplicate)
351   {
352     if (lookup_cat_op (t1, t2))
353       {
354         std::string t1_name = types(t1);
355         std::string t2_name = types(t2);
356 
357         if (abort_on_duplicate)
358           {
359             std::cerr << "duplicate concatenation operator for types '"
360                       << t1_name << "' and '" << t2_name << "'" << std::endl;
361             abort ();
362           }
363 
364         warning ("duplicate concatenation operator for types '%s' and '%s'",
365                  t1_name.c_str (), t1_name.c_str ());
366       }
367 
368     cat_ops.checkelem (t1, t2) = reinterpret_cast<void *> (f);
369 
370     return false;
371   }
372 
register_assign_op(octave_value::assign_op op,int t_lhs,int t_rhs,type_info::assign_op_fcn f,bool abort_on_duplicate)373   bool type_info::register_assign_op (octave_value::assign_op op,
374                                       int t_lhs, int t_rhs,
375                                       type_info::assign_op_fcn f,
376                                       bool abort_on_duplicate)
377   {
378     if (lookup_assign_op (op, t_lhs, t_rhs))
379       {
380         std::string op_name = octave_value::assign_op_as_string (op);
381         std::string t_lhs_name = types(t_lhs);
382         std::string t_rhs_name = types(t_rhs);
383 
384         if (abort_on_duplicate)
385           {
386             std::cerr << "duplicate assignment operator '"
387                       << op_name << "' for types '" << t_lhs_name
388                       << "' and '" << t_rhs_name << "'" << std::endl;
389             abort ();
390           }
391 
392         warning ("duplicate assignment operator '%s' for types '%s' and '%s'",
393                  op_name.c_str (), t_lhs_name.c_str (), t_rhs_name.c_str ());
394       }
395 
396     assign_ops.checkelem (static_cast<int> (op), t_lhs, t_rhs)
397       = reinterpret_cast<void *> (f);
398 
399     return false;
400   }
401 
register_assignany_op(octave_value::assign_op op,int t_lhs,type_info::assignany_op_fcn f,bool abort_on_duplicate)402   bool type_info::register_assignany_op (octave_value::assign_op op, int t_lhs,
403                                          type_info::assignany_op_fcn f,
404                                          bool abort_on_duplicate)
405   {
406     if (lookup_assignany_op (op, t_lhs))
407       {
408         std::string op_name = octave_value::assign_op_as_string (op);
409         std::string t_lhs_name = types(t_lhs);
410 
411         if (abort_on_duplicate)
412           {
413             std::cerr << "duplicate assignment operator '" << op_name
414                       << "' for types '" << t_lhs_name << "'" << std::endl;
415             abort ();
416           }
417 
418         warning ("duplicate assignment operator '%s' for types '%s'",
419                  op_name.c_str (), t_lhs_name.c_str ());
420       }
421 
422     assignany_ops.checkelem (static_cast<int> (op), t_lhs)
423       = reinterpret_cast<void *> (f);
424 
425     return false;
426   }
427 
register_pref_assign_conv(int t_lhs,int t_rhs,int t_result,bool abort_on_duplicate)428   bool type_info::register_pref_assign_conv (int t_lhs, int t_rhs,
429                                              int t_result,
430                                              bool abort_on_duplicate)
431   {
432     if (lookup_pref_assign_conv (t_lhs, t_rhs) >= 0)
433       {
434         std::string t_lhs_name = types(t_lhs);
435         std::string t_rhs_name = types(t_rhs);
436 
437         if (abort_on_duplicate)
438           {
439             std::cerr << "overriding assignment conversion for types '"
440                       << t_lhs_name << "' and '" << t_rhs_name << "'"
441                       << std::endl;
442             abort ();
443           }
444 
445         warning ("overriding assignment conversion for types '%s' and '%s'",
446                  t_lhs_name.c_str (), t_rhs_name.c_str ());
447       }
448 
449     pref_assign_conv.checkelem (t_lhs, t_rhs) = t_result;
450 
451     return false;
452   }
453 
register_widening_op(int t,int t_result,octave_base_value::type_conv_fcn f,bool abort_on_duplicate)454   bool type_info::register_widening_op (int t, int t_result,
455                                         octave_base_value::type_conv_fcn f,
456                                         bool abort_on_duplicate)
457   {
458     if (lookup_widening_op (t, t_result))
459       {
460         std::string t_name = types(t);
461         std::string t_result_name = types(t_result);
462 
463         if (abort_on_duplicate)
464           {
465             std::cerr << "overriding widening op for '" << t_name
466                       << "' to '" << t_result_name << "'" << std::endl;
467             abort ();
468           }
469 
470         warning ("overriding widening op for '%s' to '%s'",
471                  t_name.c_str (), t_result_name.c_str ());
472       }
473 
474     widening_ops.checkelem (t, t_result) = reinterpret_cast<void *> (f);
475 
476     return false;
477   }
478 
lookup_type(const std::string & nm)479   octave_value type_info::lookup_type (const std::string& nm)
480   {
481     octave_value retval;
482 
483     for (int i = 0; i < num_types; i++)
484       {
485         if (nm == types(i))
486           {
487             retval = *vals(i);
488             retval.make_unique ();
489             break;
490           }
491       }
492 
493     return retval;
494   }
495 
496   type_info::unary_class_op_fcn
lookup_unary_class_op(octave_value::unary_op op)497   type_info::lookup_unary_class_op (octave_value::unary_op op)
498   {
499     void *f = unary_class_ops.checkelem (static_cast<int> (op));
500     return reinterpret_cast<type_info::unary_class_op_fcn> (f);
501   }
502 
503   type_info::unary_op_fcn
lookup_unary_op(octave_value::unary_op op,int t)504   type_info::lookup_unary_op (octave_value::unary_op op, int t)
505   {
506     void *f = unary_ops.checkelem (static_cast<int> (op), t);
507     return reinterpret_cast<type_info::unary_op_fcn> (f);
508   }
509 
510   type_info::non_const_unary_op_fcn
lookup_non_const_unary_op(octave_value::unary_op op,int t)511   type_info::lookup_non_const_unary_op (octave_value::unary_op op, int t)
512   {
513     void *f = non_const_unary_ops.checkelem (static_cast<int> (op), t);
514     return reinterpret_cast<type_info::non_const_unary_op_fcn> (f);
515   }
516 
517   type_info::binary_class_op_fcn
lookup_binary_class_op(octave_value::binary_op op)518   type_info::lookup_binary_class_op (octave_value::binary_op op)
519   {
520     void *f = binary_class_ops.checkelem (static_cast<int> (op));
521     return reinterpret_cast<type_info::binary_class_op_fcn> (f);
522   }
523 
524   type_info::binary_op_fcn
lookup_binary_op(octave_value::binary_op op,int t1,int t2)525   type_info::lookup_binary_op (octave_value::binary_op op, int t1, int t2)
526   {
527     void *f = binary_ops.checkelem (static_cast<int> (op), t1, t2);
528     return reinterpret_cast<type_info::binary_op_fcn> (f);
529   }
530 
531   type_info::binary_class_op_fcn
lookup_binary_class_op(octave_value::compound_binary_op op)532   type_info::lookup_binary_class_op (octave_value::compound_binary_op op)
533   {
534     void *f = compound_binary_class_ops.checkelem (static_cast<int> (op));
535     return reinterpret_cast<type_info::binary_class_op_fcn> (f);
536   }
537 
538   type_info::binary_op_fcn
lookup_binary_op(octave_value::compound_binary_op op,int t1,int t2)539   type_info::lookup_binary_op (octave_value::compound_binary_op op,
540                                int t1, int t2)
541   {
542     void *f = compound_binary_ops.checkelem (static_cast<int> (op), t1, t2);
543     return reinterpret_cast<type_info::binary_op_fcn> (f);
544   }
545 
546   type_info::cat_op_fcn
lookup_cat_op(int t1,int t2)547   type_info::lookup_cat_op (int t1, int t2)
548   {
549     void *f = cat_ops.checkelem (t1, t2);
550     return reinterpret_cast<type_info::cat_op_fcn> (f);
551   }
552 
553   type_info::assign_op_fcn
lookup_assign_op(octave_value::assign_op op,int t_lhs,int t_rhs)554   type_info::lookup_assign_op (octave_value::assign_op op,
555                                int t_lhs, int t_rhs)
556   {
557     void *f = assign_ops.checkelem (static_cast<int> (op), t_lhs, t_rhs);
558     return reinterpret_cast<type_info::assign_op_fcn> (f);
559   }
560 
561   type_info::assignany_op_fcn
lookup_assignany_op(octave_value::assign_op op,int t_lhs)562   type_info::lookup_assignany_op (octave_value::assign_op op, int t_lhs)
563   {
564     void *f = assignany_ops.checkelem (static_cast<int> (op), t_lhs);
565     return reinterpret_cast<type_info::assignany_op_fcn> (f);
566   }
567 
568   int
lookup_pref_assign_conv(int t_lhs,int t_rhs)569   type_info::lookup_pref_assign_conv (int t_lhs, int t_rhs)
570   {
571     return pref_assign_conv.checkelem (t_lhs, t_rhs);
572   }
573 
574   octave_base_value::type_conv_fcn
lookup_widening_op(int t,int t_result)575   type_info::lookup_widening_op (int t, int t_result)
576   {
577     void *f = widening_ops.checkelem (t, t_result);
578     return reinterpret_cast<octave_base_value::type_conv_fcn> (f);
579   }
580 
581   string_vector
installed_type_names(void) const582   type_info::installed_type_names (void) const
583   {
584     string_vector retval (num_types);
585 
586     for (int i = 0; i < num_types; i++)
587       retval(i) = types(i);
588 
589     return retval;
590   }
591 
592   octave_scalar_map
unary_ops_map(void) const593   type_info::unary_ops_map (void) const
594   {
595     octave_scalar_map retval;
596 
597     int len = std::min (static_cast<int> (non_const_unary_ops.columns ()),
598                         num_types);
599 
600     dim_vector tab_dims (1, len);
601 
602     for (int j = 0; j < octave_value::num_unary_ops; j++)
603       {
604         boolNDArray tab (tab_dims);
605 
606         for (int i = 0; i < len; i++)
607           tab.xelem (i) = (unary_ops(j,i) != nullptr);
608 
609         octave_value::unary_op op_id = static_cast<octave_value::unary_op> (j);
610 
611         retval.setfield (octave_value::unary_op_as_string (op_id), tab);
612       }
613 
614     return retval;
615   }
616 
617   octave_scalar_map
non_const_unary_ops_map(void) const618   type_info::non_const_unary_ops_map (void) const
619   {
620     octave_scalar_map retval;
621 
622     int len = std::min (static_cast<int> (non_const_unary_ops.columns ()),
623                         num_types);
624 
625     dim_vector tab_dims (1, len);
626 
627     for (int j = 0; j < octave_value::num_unary_ops; j++)
628       {
629         boolNDArray tab (tab_dims);
630 
631         for (int i = 0; i < len; i++)
632           tab.xelem (i) = (non_const_unary_ops(j,i) != nullptr);
633 
634         octave_value::unary_op op_id = static_cast<octave_value::unary_op> (j);
635 
636         retval.setfield (octave_value::unary_op_as_string (op_id), tab);
637       }
638 
639     return retval;
640   }
641 
642   octave_scalar_map
binary_ops_map(void) const643   type_info::binary_ops_map (void) const
644   {
645     octave_scalar_map retval;
646 
647     int len = std::min (static_cast<int> (binary_ops.columns ()), num_types);
648 
649     dim_vector tab_dims (len, len);
650 
651     for (int k = 0; k < octave_value::num_binary_ops; k++)
652       {
653         boolNDArray tab (tab_dims);
654 
655         for (int j = 0; j < len; j++)
656           for (int i = 0; i < len; i++)
657             tab.xelem (j,i) = (binary_ops(k,j,i) != nullptr);
658 
659         octave_value::binary_op op_id = static_cast<octave_value::binary_op> (k);
660 
661         retval.setfield (octave_value::binary_op_as_string (op_id), tab);
662       }
663 
664     return retval;
665   }
666 
667   octave_scalar_map
compound_binary_ops_map(void) const668   type_info::compound_binary_ops_map (void) const
669   {
670     octave_scalar_map retval;
671 
672     int len = std::min (static_cast<int> (compound_binary_ops.columns ()),
673                         num_types);
674 
675     dim_vector tab_dims (len, len);
676 
677     for (int k = 0; k < octave_value::num_compound_binary_ops; k++)
678       {
679         boolNDArray tab (tab_dims);
680 
681         for (int j = 0; j < len; j++)
682           for (int i = 0; i < len; i++)
683             tab.xelem (j,i) = (compound_binary_ops(k,j,i) != nullptr);
684 
685         octave_value::compound_binary_op op_id
686           = static_cast<octave_value::compound_binary_op> (k);
687 
688         retval.setfield (octave_value::binary_op_fcn_name (op_id), tab);
689       }
690 
691     return retval;
692   }
693 
694   octave_scalar_map
assign_ops_map(void) const695   type_info::assign_ops_map (void) const
696   {
697     octave_scalar_map retval;
698 
699     int len = std::min (static_cast<int> (assign_ops.columns ()), num_types);
700 
701     dim_vector tab_dims (len, len);
702 
703     for (int k = 0; k < octave_value::num_assign_ops; k++)
704       {
705         boolNDArray tab (tab_dims);
706 
707         for (int j = 0; j < len; j++)
708           for (int i = 0; i < len; i++)
709             tab.xelem (j,i) = (assign_ops(k,j,i) != nullptr);
710 
711         octave_value::assign_op op_id = static_cast<octave_value::assign_op> (k);
712 
713         retval.setfield (octave_value::assign_op_as_string (op_id), tab);
714       }
715 
716     return retval;
717   }
718 
719   octave_scalar_map
assignany_ops_map(void) const720   type_info::assignany_ops_map (void) const
721   {
722     octave_scalar_map retval;
723 
724     int len = std::min (static_cast<int> (assignany_ops.columns ()), num_types);
725 
726     dim_vector tab_dims (1, len);
727 
728     for (int j = 0; j < octave_value::num_assign_ops; j++)
729       {
730         boolNDArray tab (tab_dims);
731 
732         for (int i = 0; i < len; i++)
733           tab.xelem (i) = (assignany_ops(j,i) != nullptr);
734 
735         octave_value::assign_op op_id = static_cast<octave_value::assign_op> (j);
736 
737         retval.setfield (octave_value::assign_op_as_string (op_id), tab);
738       }
739 
740     return retval;
741   }
742 
743   octave_scalar_map
installed_type_info(void) const744   type_info::installed_type_info (void) const
745   {
746     octave_scalar_map retval;
747 
748     retval.setfield ("types", octave_value (Cell (installed_type_names ())));
749     retval.setfield ("unary_ops", unary_ops_map ());
750     retval.setfield ("non_const_unary_ops", non_const_unary_ops_map ());
751     retval.setfield ("binary_ops", binary_ops_map ());
752     retval.setfield ("compound_binary_ops", compound_binary_ops_map ());
753     retval.setfield ("cat_ops", as_bool_nd_array (cat_ops));
754     retval.setfield ("assign_ops", assign_ops_map ());
755     retval.setfield ("assignany_ops", assignany_ops_map ());
756     retval.setfield ("pref_assign_conv", as_nd_array (pref_assign_conv));
757     retval.setfield ("widening_ops", as_bool_nd_array (widening_ops));
758 
759     return retval;
760   }
761 }
762 
763 namespace octave_value_typeinfo
764 {
register_type(const std::string & t_name,const std::string & c_name,const octave_value & val)765   int register_type (const std::string& t_name, const std::string& c_name,
766                      const octave_value& val)
767   {
768     octave::type_info& type_info
769       = octave::__get_type_info__ ("register_type");
770 
771     return type_info.register_type (t_name, c_name, val);
772   }
773 
lookup_type(const std::string & nm)774   octave_value lookup_type (const std::string& nm)
775   {
776     octave::type_info& type_info
777       = octave::__get_type_info__ ("lookup_type");
778 
779     return type_info.lookup_type (nm);
780   }
781 
lookup_unary_class_op(octave_value::unary_op op)782   unary_class_op_fcn lookup_unary_class_op (octave_value::unary_op op)
783   {
784     octave::type_info& type_info
785       = octave::__get_type_info__ ("lookup_unary_class_op");
786 
787     return type_info.lookup_unary_class_op (op);
788   }
789 
lookup_unary_op(octave_value::unary_op op,int t)790   unary_op_fcn lookup_unary_op (octave_value::unary_op op, int t)
791   {
792     octave::type_info& type_info
793       = octave::__get_type_info__ ("lookup_unary_op");
794 
795     return type_info.lookup_unary_op (op, t);
796   }
797 
798   non_const_unary_op_fcn
lookup_non_const_unary_op(octave_value::unary_op op,int t)799   lookup_non_const_unary_op (octave_value::unary_op op, int t)
800   {
801     octave::type_info& type_info
802       = octave::__get_type_info__ ("lookup_non_const_unary_op");
803 
804     return type_info.lookup_non_const_unary_op (op, t);
805   }
806 
807   binary_class_op_fcn
lookup_binary_class_op(octave_value::binary_op op)808   lookup_binary_class_op (octave_value::binary_op op)
809   {
810     octave::type_info& type_info
811       = octave::__get_type_info__ ("lookup_binary_class_op");
812 
813     return type_info.lookup_binary_class_op (op);
814   }
815 
816   binary_op_fcn
lookup_binary_op(octave_value::binary_op op,int t1,int t2)817   lookup_binary_op (octave_value::binary_op op, int t1, int t2)
818   {
819     octave::type_info& type_info
820       = octave::__get_type_info__ ("lookup_binary_op");
821 
822     return type_info.lookup_binary_op (op, t1, t2);
823   }
824 
825   binary_class_op_fcn
lookup_binary_class_op(octave_value::compound_binary_op op)826   lookup_binary_class_op (octave_value::compound_binary_op op)
827   {
828     octave::type_info& type_info
829       = octave::__get_type_info__ ("lookup_binary_class_op");
830 
831     return type_info.lookup_binary_class_op (op);
832   }
833 
834   binary_op_fcn
lookup_binary_op(octave_value::compound_binary_op op,int t1,int t2)835   lookup_binary_op (octave_value::compound_binary_op op, int t1, int t2)
836   {
837     octave::type_info& type_info
838       = octave::__get_type_info__ ("lookup_binary_op");
839 
840     return type_info.lookup_binary_op (op, t1, t2);
841   }
842 
lookup_cat_op(int t1,int t2)843   cat_op_fcn lookup_cat_op (int t1, int t2)
844   {
845     octave::type_info& type_info
846       = octave::__get_type_info__ ("lookup_cat_op");
847 
848     return type_info.lookup_cat_op (t1, t2);
849   }
850 
851   assign_op_fcn
lookup_assign_op(octave_value::assign_op op,int t_lhs,int t_rhs)852   lookup_assign_op (octave_value::assign_op op, int t_lhs, int t_rhs)
853   {
854     octave::type_info& type_info
855       = octave::__get_type_info__ ("lookup_assign_op");
856 
857     return type_info.lookup_assign_op (op, t_lhs, t_rhs);
858   }
859 
860   assignany_op_fcn
lookup_assignany_op(octave_value::assign_op op,int t_lhs)861   lookup_assignany_op (octave_value::assign_op op, int t_lhs)
862   {
863     octave::type_info& type_info
864       = octave::__get_type_info__ ("lookup_assignany_op");
865 
866     return type_info.lookup_assignany_op (op, t_lhs);
867   }
868 
lookup_pref_assign_conv(int t_lhs,int t_rhs)869   int lookup_pref_assign_conv (int t_lhs, int t_rhs)
870   {
871     octave::type_info& type_info
872       = octave::__get_type_info__ ("lookup_pref_assign_conv");
873 
874     return type_info.lookup_pref_assign_conv (t_lhs, t_rhs);
875   }
876 
877   octave_base_value::type_conv_fcn
lookup_widening_op(int t,int t_result)878   lookup_widening_op (int t, int t_result)
879   {
880     octave::type_info& type_info
881       = octave::__get_type_info__ ("lookup_widening_op");
882 
883     return type_info.lookup_widening_op (t, t_result);
884   }
885 
installed_type_names(void)886   string_vector installed_type_names (void)
887   {
888     octave::type_info& type_info
889       = octave::__get_type_info__ ("installed_type_names");
890 
891     return type_info.installed_type_names ();
892   }
893 
installed_type_info(void)894   octave_scalar_map installed_type_info (void)
895   {
896     octave::type_info& type_info
897       = octave::__get_type_info__ ("installed_type_info");
898 
899     return type_info.installed_type_info ();
900   }
901 }
902 
903 DEFMETHOD (typeinfo, interp, args, ,
904            doc: /* -*- texinfo -*-
905 @deftypefn  {} {} typeinfo ()
906 @deftypefnx {} {} typeinfo (@var{expr})
907 
908 Return the type of the expression @var{expr}, as a string.
909 
910 If @var{expr} is omitted, return a cell array of strings containing all the
911 currently installed data types.
912 @seealso{class, isa}
913 @end deftypefn */)
914 {
915   int nargin = args.length ();
916 
917   if (nargin > 1)
918     print_usage ();
919 
920   if (nargin == 0)
921     {
922       octave::type_info& type_info = interp.get_type_info ();
923 
924       return ovl (Cell (type_info.installed_type_names ()));
925     }
926   else
927     return ovl (args(0).type_name ());
928 }
929 
930 /*
931 %!assert (iscellstr (typeinfo ()))
932 
933 %!assert (typeinfo ({"cell"}), "cell")
934 
935 %!assert (typeinfo (1), "scalar")
936 %!assert (typeinfo (double (1)), "scalar")
937 %!assert (typeinfo (i), "complex scalar")
938 
939 %!assert (typeinfo ([1, 2]), "matrix")
940 %!assert (typeinfo (double ([1, 2])), "matrix")
941 %!assert (typeinfo (diag ([1, 2])), "diagonal matrix")
942 %!assert (typeinfo ([i, 2]), "complex matrix")
943 %!assert (typeinfo (diag ([i, 2])), "complex diagonal matrix")
944 
945 %!assert (typeinfo (1:2), "range")
946 
947 %!assert (typeinfo (false), "bool")
948 %!assert (typeinfo ([true, false]), "bool matrix")
949 
950 %!assert (typeinfo ("string"), "string")
951 %!assert (typeinfo ('string'), "sq_string")
952 
953 %!assert (typeinfo (int8 (1)), "int8 scalar")
954 %!assert (typeinfo (int16 (1)), "int16 scalar")
955 %!assert (typeinfo (int32 (1)), "int32 scalar")
956 %!assert (typeinfo (int64 (1)), "int64 scalar")
957 %!assert (typeinfo (uint8 (1)), "uint8 scalar")
958 %!assert (typeinfo (uint16 (1)), "uint16 scalar")
959 %!assert (typeinfo (uint32 (1)), "uint32 scalar")
960 %!assert (typeinfo (uint64 (1)), "uint64 scalar")
961 
962 %!assert (typeinfo (int8 ([1,2])), "int8 matrix")
963 %!assert (typeinfo (int16 ([1,2])), "int16 matrix")
964 %!assert (typeinfo (int32 ([1,2])), "int32 matrix")
965 %!assert (typeinfo (int64 ([1,2])), "int64 matrix")
966 %!assert (typeinfo (uint8 ([1,2])), "uint8 matrix")
967 %!assert (typeinfo (uint16 ([1,2])), "uint16 matrix")
968 %!assert (typeinfo (uint32 ([1,2])), "uint32 matrix")
969 %!assert (typeinfo (uint64 ([1,2])), "uint64 matrix")
970 
971 %!assert (typeinfo (sparse ([true, false])), "sparse bool matrix")
972 %!assert (typeinfo (logical (sparse (i * eye (10)))), "sparse bool matrix")
973 %!assert (typeinfo (sparse ([1,2])), "sparse matrix")
974 %!assert (typeinfo (sparse (eye (10))), "sparse matrix")
975 %!assert (typeinfo (sparse ([i,2])), "sparse complex matrix")
976 %!assert (typeinfo (sparse (i * eye (10))), "sparse complex matrix")
977 
978 %!test
979 %! s(2).a = 1;
980 %! assert (typeinfo (s), "struct");
981 
982 %!test
983 %! s.a = 1;
984 %! assert (typeinfo (s), "scalar struct");
985 
986 ## FIXME: This doesn't work as a test for comma-separated list
987 %!#test
988 %! clist = {1, 2, 3};
989 %! assert (typeinfo (clist{:}), "cs-list");
990 
991 %!assert (typeinfo (@sin), "function handle")
992 %!assert (typeinfo (@(x) x), "function handle")
993 
994 %!assert (typeinfo (single (1)), "float scalar")
995 %!assert (typeinfo (single (i)), "float complex scalar")
996 %!assert (typeinfo (single ([1, 2])), "float matrix")
997 
998 %!assert (typeinfo (single (diag ([1, 2]))), "float diagonal matrix")
999 %!assert (typeinfo (diag (single ([1, 2]))), "float diagonal matrix")
1000 %!assert (typeinfo (single (diag ([i, 2]))), "float complex diagonal matrix")
1001 %!assert (typeinfo (diag (single ([i, 2]))), "float complex diagonal matrix")
1002 
1003 %!assert (typeinfo (eye(3)(:,[1 3 2])), "permutation matrix")
1004 %!test
1005 %! [l, u, p] = lu (rand (3));
1006 %! assert (typeinfo (p), "permutation matrix");
1007 
1008 %!assert (typeinfo ([]), "null_matrix")
1009 %!assert (typeinfo (""), "null_string")
1010 %!assert (typeinfo (''), "null_sq_string")
1011 
1012 %!test
1013 %! cvar = onCleanup (@() "");
1014 %! assert (typeinfo (cvar), "onCleanup");
1015 
1016 %!testif HAVE_JAVA; usejava ("jvm")
1017 %! x = javaObject ("java.lang.StringBuffer");
1018 %! assert (typeinfo (x), "octave_java");
1019 
1020 ## Test input validation
1021 %!error typeinfo ("foo", 1)
1022 */
1023 
1024 DEFMETHOD (__dump_typeinfo__, interp, args, ,
1025            doc: /* -*- texinfo -*-
1026 @deftypefn {} {} __dump_typeinfo__ ()
1027 Undocumented internal function.
1028 @end deftypefn */)
1029 {
1030   if (args.length () > 0)
1031     print_usage ();
1032 
1033   octave::type_info& type_info = interp.get_type_info ();
1034 
1035   return ovl (type_info.installed_type_info ());
1036 }
1037