ecere/sys/JSON: Fixed crash on unknown member
[sdk] / ecere / src / sys / JSON.ec
1 namespace sys;
2
3 import "System"
4 import "Array"
5
6 default:
7 __attribute__((unused)) static void UnusedFunction()
8 {
9    int a;
10    a.OnGetDataFromString(null);
11    a.OnGetString(null, 0, 0);
12    a.OnFree();
13 }
14 extern int __ecereVMethodID_class_OnGetDataFromString;
15 extern int __ecereVMethodID_class_OnGetString;
16 extern int __ecereVMethodID_class_OnFree;
17 private:
18
19 public enum JSONResult { syntaxError, success, typeMismatch, noItem };
20
21 public enum SetBool : uint
22 {
23    unset, false, true;
24
25    /*public property bool     // NOT WORKING!
26    {
27       set { return value ? true : false; }
28       get { return (this == true); }
29    }*/
30 };
31
32
33 public class JSONParser
34 {
35 public:
36    File f;
37    char ch;
38
39    void SkipEmpty()
40    {
41       while(!f.Eof() && (!ch || ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t'))
42       {
43          f.Getc(&ch);
44       }
45    }
46
47    JSONResult GetValue(Class type, DataValue value)
48    {
49       JSONResult result = syntaxError;
50       ch = 0;
51       SkipEmpty();
52       if(ch == '\"')
53       {
54          String string;
55          result = GetString(&string);
56          if(result)
57          {
58             Property prop;
59             if(type && (!strcmp(type.name, "String") || !strcmp(type.dataTypeString, "char *")))
60             {
61                value.p = string;
62             }
63             else if(type && (type.type == enumClass || type.type == unitClass))
64             {
65                if(((bool (*)(void *, void *, const char *))(void *)type._vTbl[__ecereVMethodID_class_OnGetDataFromString])(type, &value.i, string))
66                   result = success;
67                else
68                   result = typeMismatch;
69                delete string;
70             }
71             else if(type && (prop = eClass_FindProperty(type, "String", type.module)))
72             {
73                // TOFIX: Add more conversion property support... Expecting void * compatible here
74                value.p = ((void *(*)())(void *)prop.Set)(string);
75                result = success;
76                delete string;
77             }
78             else if(type && (type.type == structClass))
79             {
80                if(((bool (*)(void *, void *, const char *))(void *)type._vTbl[__ecereVMethodID_class_OnGetDataFromString])(type, value.p, string))
81                   result = success;
82                else
83                   result = typeMismatch;
84                delete string;
85             }
86             else
87             {
88                delete string;
89                result = typeMismatch;
90             }
91          }
92       }
93       else if(ch == '[')
94       {
95          Container array;
96          if(type && eClass_IsDerived(type, class(Map)))
97          {
98             result = GetMap(type, (Map *)&array);
99          }
100          else
101             result = GetArray(type, &array);
102
103          if(result == success && type && eClass_IsDerived(type, class(Container)))
104          {
105             value.p = array;
106          }
107          else
108          {
109             if(array)
110                array.Free();
111             delete array;
112             if(result != success)
113                result = typeMismatch;
114          }
115       }
116       else if(ch == '-' || isdigit(ch))
117       {
118          result = GetNumber(type, value);
119       }
120       else if(ch == '{')
121       {
122          void * object = value.p;
123          result = GetObject(type, &object);
124          if(result)
125          {
126             if(type && type.type == structClass);
127             else if(type && (type.type == normalClass || type.type == noHeadClass || type.type == bitClass))
128             {
129                value.p = object;
130             }
131             else
132             {
133                result = typeMismatch;
134                if(type)
135                   ((void (*)(void *, void *))(void *)type._vTbl[__ecereVMethodID_class_OnFree])(type, object);
136             }
137          }
138       }
139       else if(isalpha(ch))
140       {
141          char buffer[256];
142          int c = 0;
143          while(c < sizeof(buffer)-1 && isalpha(ch))
144          {
145             buffer[c++] = ch;
146             if(!f.Getc(&ch)) break;
147          }
148          buffer[c] = 0;
149          result = success;
150
151          if(type)
152          {
153             if(!strcmp(type.name, "bool"))
154             {
155                if(!strcmpi(buffer, "false")) value.i = 0;
156                else if(!strcmpi(buffer, "true")) value.i = 1;
157                else
158                   result = typeMismatch;
159             }
160             else if(!strcmp(type.name, "SetBool"))
161             {
162                if(!strcmpi(buffer, "false")) value.i = SetBool::false;
163                else if(!strcmpi(buffer, "true")) value.i = SetBool::true;
164                else
165                   result = typeMismatch;
166             }
167             else if(!strcmpi(buffer, "null"))
168             {
169                value.p = 0;
170             }
171             else
172                result = typeMismatch;
173          }
174          else
175             result = typeMismatch;
176       }
177       else if(ch == '}' || ch == ']')
178          result = noItem;
179       return result;
180    }
181
182    JSONResult GetArray(Class type, Container * array)
183    {
184       JSONResult result = syntaxError;
185       SkipEmpty();
186       *array = null;
187       if(ch == '[')
188       {
189          *array = eInstance_New(type);
190          result = success;
191          while(result)
192          {
193             DataValue value { };
194             Class arrayType = null;
195             JSONResult itemResult;
196
197             if(eClass_IsDerived(type, class(Container)))
198             {
199                arrayType = type.templateArgs[0].dataTypeClass;
200             }
201
202             if(arrayType && arrayType.type == structClass)
203                value.p = new0 byte[arrayType.structSize];
204             itemResult = GetValue(arrayType, value);
205             if(itemResult == success)
206             {
207                // TODO: Verify the matching between template type and uint64
208                uint64 t;
209                if(arrayType.type == structClass)
210                {
211                   t = (uint64)(uintptr)value.p;
212                }
213                else if(arrayType == class(double) || !strcmp(arrayType.dataTypeString, "double"))
214                {
215                   t = value.ui64; //*(uint64 *)&value.d;
216                }
217                else if(arrayType == class(float) || !strcmp(arrayType.dataTypeString, "float"))
218                {
219                   t = value.ui; //f*(uint *)&value.f;
220                }
221                else if(arrayType.typeSize == sizeof(int64) || !strcmp(arrayType.dataTypeString, "int64") ||
222                   !strcmp(arrayType.dataTypeString, "unsigned int64") || !strcmp(arrayType.dataTypeString, "uint64"))
223                {
224                   t = value.ui64;
225                }
226                else if(arrayType.typeSize == sizeof(int) || !strcmp(arrayType.dataTypeString, "int") ||
227                   !strcmp(arrayType.dataTypeString, "unsigned int") || !strcmp(arrayType.dataTypeString, "uint"))
228                {
229                   t = value.i;
230                }
231                else if(arrayType.typeSize == sizeof(short int) || !strcmp(arrayType.dataTypeString, "short") ||
232                   !strcmp(arrayType.dataTypeString, "unsigned short") || !strcmp(arrayType.dataTypeString, "uint16") ||
233                   !strcmp(arrayType.dataTypeString, "int16"))
234                {
235                   t = value.s;
236                }
237                else if(arrayType.typeSize == sizeof(byte) || !strcmp(arrayType.dataTypeString, "char") ||
238                   !strcmp(arrayType.dataTypeString, "unsigned char") || !strcmp(arrayType.dataTypeString, "byte"))
239                {
240                   t = value.c;
241                }
242                else
243                {
244                   t = (uint64)(uintptr)value.p;
245                }
246                ((void *(*)(void *, uint64))(void *)array->Add)(*array, t);
247
248                if(arrayType && arrayType.type == structClass)
249                   delete value.p;
250             }
251             else
252             {
253                if(itemResult == typeMismatch)
254                {
255                   if(arrayType)
256                      PrintLn("Warning: Incompatible value for array value, expected ", (String)arrayType.name);
257                }
258                else if(itemResult == noItem)
259                   result = success;
260                else
261                   result = itemResult;
262             }
263
264             if(result != syntaxError)
265             {
266                if(ch != ']' && ch != ',')
267                {
268                   ch = 0;
269                   SkipEmpty();
270                }
271                if(ch == ']')
272                {
273                   break;
274                }
275                else if(ch != ',')
276                   result = syntaxError;
277             }
278          }
279       }
280       ch = 0;
281       return result;
282    }
283
284    JSONResult GetMap(Class type, Map * map)
285    {
286       JSONResult result = syntaxError;
287       SkipEmpty();
288       *map = null;
289       if(ch == '[')
290       {
291          Class mapNodeType = type.templateArgs[0].dataTypeClass;
292          Class keyType = mapNodeType.templateArgs[0].dataTypeClass;
293          Property keyProp = null;
294          if(keyType && !strcmp(keyType.dataTypeString, "char *"))
295             keyProp = eClass_FindProperty(mapNodeType, "key", mapNodeType.module);
296
297          *map = eInstance_New(type);
298          result = success;
299
300          while(result)
301          {
302             DataValue value { };
303
304             JSONResult itemResult;
305
306             itemResult = GetValue(mapNodeType, value);
307             if(itemResult == success)
308             {
309                String s = keyProp ? ((void * (*)(void *))(void *)keyProp.Get)(value.p) : null;
310                ((void *(*)(void *, uint64))(void *)map->Add)(*map, (uint64)(uintptr)value.p);
311                // Must free String keys here
312                delete s;
313             }
314             else
315             {
316                if(itemResult == typeMismatch)
317                {
318                   if(mapNodeType)
319                      PrintLn("Warning: Incompatible value for array value, expected ", (String)mapNodeType.name);
320                }
321                else if(itemResult == noItem)
322                   result = success;
323                else
324                   result = itemResult;
325             }
326
327             if(result != syntaxError)
328             {
329                if(ch != ']' && ch != ',')
330                {
331                   ch = 0;
332                   SkipEmpty();
333                }
334                if(ch == ']')
335                {
336                   break;
337                }
338                else if(ch != ',')
339                   result = syntaxError;
340             }
341          }
342       }
343       ch = 0;
344       return result;
345    }
346
347    JSONResult GetString(String * string)
348    {
349       JSONResult result = syntaxError;
350       Array<char> buffer { minAllocSize = 256 };
351       bool escaped = false;
352
353       *string = null;
354       SkipEmpty();
355       if(ch == '\"')
356       {
357          while(f.Getc(&ch))
358          {
359             if(ch == '\\' && !escaped)
360                escaped = true;
361             else
362             {
363                if(escaped)
364                {
365                   if(ch == 'b') ch = '\b';
366                   else if(ch == 'f') ch = '\f';
367                   else if(ch == 'n') ch = '\n';
368                   else if(ch == 'r') ch = '\r';
369                   else if(ch == 't') ch = '\t';
370                   else if(ch == 'u')
371                   {
372                      // SKIP FOR NOW...
373                      char unicode[4];
374                      f.Getc(&unicode[0]);
375                      f.Getc(&unicode[1]);
376                      f.Getc(&unicode[2]);
377                      f.Getc(&unicode[3]);
378                   }
379                   escaped = false;
380                }
381                else if(ch == '\"')
382                {
383                   break;
384                }
385                buffer.Add(ch);
386                if(buffer.minAllocSize < buffer.count)
387                   buffer.minAllocSize *= 2;
388             }
389          }
390          buffer.Add(0);
391          *string = CopyString(buffer.array);
392          result = success;
393       }
394       delete buffer;
395       if(ch != ',' && ch != '}')
396          ch = 0;
397       return result;
398    }
399
400    public JSONResult GetObject(Class objectType, void ** object)
401    {
402       JSONResult result = syntaxError;
403       if(!objectType || objectType.type != structClass)
404          *object = null;
405       SkipEmpty();
406       if(ch == '{')
407       {
408          Class mapKeyClass = null, mapDataClass = null;
409
410          if(objectType && objectType.templateClass && eClass_IsDerived(objectType.templateClass, class(MapNode)))
411          {
412             mapKeyClass = objectType.templateArgs[0].dataTypeClass;
413             mapDataClass = objectType.templateArgs[2].dataTypeClass;
414          }
415
416          result = success;
417          if(objectType && (objectType.type == noHeadClass || objectType.type == normalClass))
418          {
419             *object = eInstance_New(objectType);
420          }
421          else if(objectType && objectType.type != structClass)
422          {
423             *object = eSystem_New(objectType.typeSize);
424          }
425
426          while(result)
427          {
428             String string;
429             ch = 0;
430             if(GetString(&string))
431             {
432                DataMember member = null;
433                Property prop = null;
434                Class type = null;
435                bool isKey = false;
436                uint offset = 0;
437
438                if(objectType)
439                {
440                   string[0] = (char)tolower(string[0]);
441                   if(mapKeyClass && !strcmp(string, "key"))
442                   {
443                      prop = eClass_FindProperty(objectType, "key", objectType.module);
444                      type = mapKeyClass;
445                      isKey = true;
446                   }
447                   else if(mapDataClass && !strcmp(string, "value"))
448                   {
449                      prop = eClass_FindProperty(objectType, "value", objectType.module);
450                      type = mapDataClass;
451                   }
452                   else
453                   {
454                      member = eClass_FindDataMember(objectType, string, objectType.module, null, null);
455                      if(member)
456                      {
457                         type = eSystem_FindClass(__thisModule, member.dataTypeString);
458                         if(!type)
459                            type = eSystem_FindClass(__thisModule.application, member.dataTypeString);
460
461                         offset = member._class.offset + member.offset;
462                      }
463                      else if(!member)
464                      {
465                         prop = eClass_FindProperty(objectType, string, objectType.module);
466                         if(prop)
467                         {
468                            type = eSystem_FindClass(__thisModule, prop.dataTypeString);
469                            if(!type)
470                               type = eSystem_FindClass(__thisModule.application, prop.dataTypeString);
471                         }
472                         else
473                            PrintLn("Warning: member ", string, " not found in class ", (String)objectType.name);
474                      }
475                   }
476                }
477                // Find Member in Object Class
478                {
479                   DataValue value { };
480
481                   if(type && type.type == structClass)
482                   {
483                      if(member)
484                      {
485                         value.p = (byte *)*object + offset;
486                         memset(value.p, 0, type.structSize);
487                      }
488                      else if(prop)
489                      {
490                         value.p = new0 byte[type.structSize];
491                      }
492                   }
493                   ch = 0;
494                   SkipEmpty();
495                   if(ch == ':')
496                   {
497                      JSONResult itemResult = GetValue(type, value);
498                      if(itemResult != syntaxError)
499                      {
500                         if(prop || member)
501                         {
502                            if(!type)
503                            {
504                               PrintLn("warning: Unresolved data type ", member ? (String)member.dataTypeString : (String)prop.dataTypeString);
505                            }
506                            else if(itemResult == success)
507                            {
508                               // Set value
509                               if(member)
510                               {
511                                  // TOFIX: How to swiftly handle classes with base data type?
512                                  if(type.type == structClass)
513                                     ;
514                                  else if(type.type == normalClass || type.type == noHeadClass)
515                                  {
516                                     void ** ptr = (void**)((byte *)*object + offset);
517                                     if(eClass_IsDerived(type, class(Container)) && *ptr)
518                                     {
519                                        Container container = (Container)*ptr;
520                                        container.Free();
521                                        delete container;
522                                     }
523                                     *ptr = value.p;
524                                  }
525                                  else if(type == class(double) || !strcmp(type.dataTypeString, "double"))
526                                  {
527                                     *(double *)((byte *)*object + offset) = value.d;
528                                  }
529                                  else if(type == class(float) || !strcmp(type.dataTypeString, "float"))
530                                  {
531                                     *(float *)((byte *)*object + offset) = value.f;
532                                  }
533                                  else if(type.typeSize == sizeof(int64) || !strcmp(type.dataTypeString, "int64") ||
534                                     !strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64"))
535                                  {
536                                     *(uint64 *)((byte *)*object + offset) = value.ui64;
537                                  }
538                                  else if(type.typeSize == sizeof(int) || !strcmp(type.dataTypeString, "int") ||
539                                     !strcmp(type.dataTypeString, "unsigned int") || !strcmp(type.dataTypeString, "uint"))
540                                  {
541                                     *(int *)((byte *)*object + offset) = value.i;
542                                  }
543                                  else if(type.typeSize == sizeof(short int) || !strcmp(type.dataTypeString, "short") ||
544                                     !strcmp(type.dataTypeString, "unsigned short") || !strcmp(type.dataTypeString, "uint16") ||
545                                     !strcmp(type.dataTypeString, "int16"))
546                                  {
547                                     *(short *)((byte *)*object + offset) = value.s;
548                                  }
549                                  else if(type.typeSize == sizeof(byte) || !strcmp(type.dataTypeString, "char") ||
550                                     !strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte"))
551                                  {
552                                     *(char *)((byte *)*object + offset) = value.c;
553                                  }
554                                  else
555                                  {
556                                     *(void **)((byte *)*object + offset) = value.p;
557                                  }
558                               }
559                               else if(prop && prop.Set)
560                               {
561                                  if(!strcmp(type.dataTypeString, "char *"))
562                                  {
563                                     ((void (*)(void *, void *))(void *)prop.Set)(*object, value.p);
564                                     if(!isKey)
565                                        delete value.p;
566                                  }
567                                  // TOFIX: How to swiftly handle classes with base data type?
568                                  else if(type == class(double) || !strcmp(type.dataTypeString, "double"))
569                                  {
570                                     ((void (*)(void *, double))(void *)prop.Set)(*object, value.d);
571                                  }
572                                  else if(type == class(float) || !strcmp(type.dataTypeString, "float"))
573                                  {
574                                     ((void (*)(void *, float))(void *)prop.Set)(*object, value.f);
575                                  }
576                                  else if(type.typeSize == sizeof(int64) || !strcmp(type.dataTypeString, "int64") ||
577                                     !strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64"))
578                                  {
579                                     ((void (*)(void *, uint64))(void *)prop.Set)(*object, value.ui64);
580                                  }
581                                  else if(type.typeSize == sizeof(int) || !strcmp(type.dataTypeString, "int") ||
582                                     !strcmp(type.dataTypeString, "unsigned int") || !strcmp(type.dataTypeString, "uint"))
583                                  {
584                                     ((void (*)(void *, int))(void *)prop.Set)(*object, value.i);
585                                  }
586                                  else if(type.typeSize == sizeof(short int) || !strcmp(type.dataTypeString, "short") ||
587                                     !strcmp(type.dataTypeString, "unsigned short") || !strcmp(type.dataTypeString, "uint16") ||
588                                     !strcmp(type.dataTypeString, "int16"))
589                                  {
590                                     ((void (*)(void *, short))(void *)prop.Set)(*object, value.s);
591                                  }
592                                  else if(type.typeSize == sizeof(byte) || !strcmp(type.dataTypeString, "char") ||
593                                     !strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte"))
594                                  {
595                                     ((void (*)(void *, char))(void *)prop.Set)(*object, value.c);
596                                  }
597                                  else
598                                  {
599                                     ((void (*)(void *, void *))(void *)prop.Set)(*object, value.p);
600                                  }
601                               }
602                            }
603                            else
604                            {
605                               PrintLn("Warning: Incompatible value for ", member ? (String)member.name : (String)prop.name,
606                                  ", expected ", member ? (String)member.dataTypeString : (String)prop.dataTypeString);
607                            }
608                         }
609                      }
610                   }
611                   else
612                      result = syntaxError;
613
614                   if(prop && type.type == structClass)
615                   {
616                      delete value.p;
617                   }
618                }
619             }
620             else if(ch && ch != '}' && ch != ',')
621                result = syntaxError;
622             delete string;
623
624             if(result)
625             {
626                SkipEmpty();
627                if(ch == '}')
628                {
629                   break;
630                }
631                else if(ch != ',')
632                   result = syntaxError;
633             }
634          }
635       }
636       ch = 0;
637       return result;
638    }
639
640    JSONResult GetNumber(Class type, DataValue value)
641    {
642       JSONResult result = syntaxError;
643       char buffer[256];
644       int c = 0;
645       while(c < sizeof(buffer)-1 && (ch == '-' || ch == '.' || tolower(ch) == 'e' || ch == '+' || isdigit(ch)))
646       {
647          buffer[c++] = ch;
648          if(!f.Getc(&ch)) break;
649       }
650       buffer[c] = 0;
651       //if(strchr(buffer, '.'))
652       if(!type) return success;
653
654       // TOFIX: How to swiftly handle classes with base data type?
655       if(type == class(double) || !strcmp(type.dataTypeString, "double"))
656       {
657          value.d = strtod(buffer, null);
658          result = success;
659       }
660       else if(type == class(float) || !strcmp(type.dataTypeString, "float"))
661       {
662          value.f = (float)strtod(buffer, null);
663          result = success;
664       }
665       // TOFIX: int64 looks for class long long?
666       //else if(type == class(int64) || !strcmp(type.dataTypeString, "int64"))
667       else if(!strcmp(type.dataTypeString, "int64"))
668       {
669          value.i64 = strtol(buffer, null, 10);  // TOFIX: 64 bit support
670          result = success;
671       }
672       else if(type == class(uint64) || !strcmp(type.dataTypeString, "uint64"))
673       {
674          value.ui64 = strtoul(buffer, null, 10);  // TOFIX: 64 bit support
675          result = success;
676       }
677       else if(type == class(uint) || !strcmp(type.dataTypeString, "unsigned int"))
678       {
679          value.ui = (uint)strtoul(buffer, null, 10);  // TOFIX: 64 bit support
680          result = success;
681       }
682       else
683       {
684          value.i = (int)strtol(buffer, null, 10);
685          result = success;
686       }
687
688       if(result == success && type.type == unitClass)
689       {
690          // Convert to reference unit
691          Property prop;
692          for(prop = type.conversions.first; prop; prop = prop.next)
693          {
694             bool refProp = false;
695             Class c;
696             if(!strcmp(prop.name, type.base.fullName))
697                refProp = true;
698             else if( (c = eSystem_FindClass(type.module, prop.name) ) )
699             {
700                Property p;
701                for(p = c.conversions.first; p; p = p.next)
702                {
703                   if(!strcmp(p.name, type.base.fullName) && !p.Set && !p.Get)
704                   {
705                      refProp = true;
706                      break;
707                   }
708                }
709             }
710             if(refProp)
711             {
712                if(prop.Set && prop.Get)
713                {
714                   const String dts = type.base.dataTypeString;
715                   if(!strcmp(dts, "double"))
716                      value.d = ((double(*)(double))(void *)prop.Get)(value.d);
717                   else if(!strcmp(dts, "float"))
718                      value.f = ((float(*)(float))(void *)prop.Get)(value.f);
719                   else if(!strcmp(dts, "int"))
720                      value.i = ((int(*)(int))(void *)prop.Get)(value.i);
721                   else if(!strcmp(dts, "int64"))
722                      value.i64 = ((int64(*)(int64))(void *)prop.Get)(value.i64);
723                   else if(!strcmp(dts, "unsigned int"))
724                      value.ui = ((uint(*)(uint))(void *)prop.Get)(value.ui);
725                   else if(!strcmp(dts, "uint64"))
726                      value.ui64 = ((uint64(*)(uint64))(void *)prop.Get)(value.ui64);
727                }
728                else
729                   break;
730             }
731          }
732       }
733       return result;
734    }
735 }
736
737 bool WriteMap(File f, Class type, Map map, int indent)
738 {
739    if(map)
740    {
741       int i;
742       bool isFirst = true;
743       MapIterator it { map = map };
744       Class mapNodeClass = map._class.templateArgs[0].dataTypeClass;
745       f.Puts("[\n");
746       indent++;
747
748       while(it.Next())
749       {
750          MapNode n = (MapNode)it.pointer;
751          if(!isFirst)
752             f.Puts(",\n");
753          else
754             isFirst = false;
755          for(i = 0; i<indent; i++) f.Puts("   ");
756          _WriteJSONObject(f, mapNodeClass, n, indent);
757       }
758       f.Puts("\n");
759       indent--;
760       for(i = 0; i<indent; i++) f.Puts("   ");
761       f.Puts("]");
762    }
763    else
764       f.Puts("null");
765    return true;
766 }
767
768 bool WriteArray(File f, Class type, Container array, int indent)
769 {
770    if(array)
771    {
772       int i;
773       bool isFirst = true;
774       Iterator it { array };
775       Class arrayType = type.templateArgs[0].dataTypeClass;
776       f.Puts("[\n");
777       indent++;
778
779       while(it.Next())
780       {
781          DataValue value { };
782          uint64 t = ((uint64(*)(void *, void *))(void *)array.GetData)(array, it.pointer);
783          if(!isFirst)
784             f.Puts(",\n");
785          else
786             isFirst = false;
787
788          // Add value
789          // TODO: Verify the matching between template type and uint64
790          if(arrayType.type == structClass)
791          {
792             value.p = (void *)(uintptr)t;
793          }
794          else if(arrayType == class(double) || !strcmp(arrayType.dataTypeString, "double"))
795          {
796             value.ui64 = t;
797             //value.d = *(double *)&t;
798          }
799          else if(arrayType == class(float) || !strcmp(arrayType.dataTypeString, "float"))
800          {
801             value.ui = (uint)t;
802             //value.f = *(float *)&t;
803          }
804          else if(arrayType.typeSize == sizeof(int64) || !strcmp(arrayType.dataTypeString, "int64") ||
805             !strcmp(arrayType.dataTypeString, "unsigned int64") || !strcmp(arrayType.dataTypeString, "uint64"))
806          {
807             value.ui64 = t;
808          }
809          else if(arrayType.typeSize == sizeof(int) || !strcmp(arrayType.dataTypeString, "int") ||
810             !strcmp(arrayType.dataTypeString, "unsigned int") || !strcmp(arrayType.dataTypeString, "uint"))
811          {
812             value.i = (int)t;
813          }
814          else if(arrayType.typeSize == sizeof(short int) || !strcmp(arrayType.dataTypeString, "short") ||
815             !strcmp(arrayType.dataTypeString, "unsigned short") || !strcmp(arrayType.dataTypeString, "uint16") ||
816             !strcmp(arrayType.dataTypeString, "int16"))
817          {
818             value.s = (short)t;
819          }
820          else if(arrayType.typeSize == sizeof(byte) || !strcmp(arrayType.dataTypeString, "char") ||
821             !strcmp(arrayType.dataTypeString, "unsigned char") || !strcmp(arrayType.dataTypeString, "byte"))
822          {
823             value.c = (char)t;
824          }
825          else
826          {
827             value.p = (void *)(uintptr)t;
828          }
829          for(i = 0; i<indent; i++) f.Puts("   ");
830          WriteValue(f, arrayType, value, indent);
831       }
832       f.Puts("\n");
833       indent--;
834       for(i = 0; i<indent; i++) f.Puts("   ");
835       f.Puts("]");
836    }
837    else
838       f.Puts("null");
839    return true;
840 }
841
842 bool WriteNumber(File f, Class type, DataValue value, int indent)
843 {
844    char buffer[1024];
845    bool needClass = false;
846    bool quote;
847    buffer[0] = 0;
848    if(type == class(double) || !strcmp(type.dataTypeString, "double"))
849       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.d, buffer, 0, &needClass);
850    else if(type == class(float) || !strcmp(type.dataTypeString, "float"))
851       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.f, buffer, null, &needClass);
852    else if(!strcmp(type.dataTypeString, "int64"))
853       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.i64, buffer, null, &needClass);
854    else if(!strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64") || type.typeSize == sizeof(int64))
855       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.ui64, buffer, null, &needClass);
856    else if(!strcmp(type.dataTypeString, "int"))
857       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.i, buffer, null, &needClass);
858    else if(!strcmp(type.dataTypeString, "unsigned int") || !strcmp(type.dataTypeString, "uint") || type.typeSize == sizeof(int))
859       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.ui, buffer, null, &needClass);
860    else if(!strcmp(type.dataTypeString, "short") || !strcmp(type.dataTypeString, "int16"))
861       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.s, buffer, null, &needClass);
862    else if(!strcmp(type.dataTypeString, "unsigned short") || !strcmp(type.dataTypeString, "uint16") || type.typeSize == sizeof(short int))
863       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.us, buffer, null, &needClass);
864    else if(!strcmp(type.dataTypeString, "char"))
865       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.c, buffer, null, &needClass);
866    else if(!strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte") || type.typeSize == sizeof(byte))
867       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.uc, buffer, null, &needClass);
868
869    quote = (type.type == unitClass && ((buffer[0] != '.' && !isdigit(buffer[0])) || strchr(buffer, ' ')));
870    if(quote) f.Puts("\"");
871    f.Puts(buffer);
872    if(quote) f.Puts("\"");
873    return true;
874 }
875
876 bool WriteValue(File f, Class type, DataValue value, int indent)
877 {
878    if(!strcmp(type.name, "String") || !strcmp(type.dataTypeString, "char *"))
879    {
880       if(!value.p)
881          f.Puts("null");
882       else
883       {
884          f.Puts("\"");
885          //if(strchr(value.p, '\"') || strchr(value.p, '\\'))
886          {
887             int c = 0;
888             int b = 0;
889             char buffer[1024];
890             char * string = value.p;
891             char ch;
892             while(true)
893             {
894                ch = string[c++];
895                if(ch == '\"')
896                {
897                   buffer[b] = 0;
898                   f.Puts(buffer);
899                   f.Puts("\\\"");
900                   b = 0;
901                }
902                else if(ch == '\\')
903                {
904                   buffer[b] = 0;
905                   f.Puts(buffer);
906                   f.Puts("\\\\");
907                   b = 0;
908                }
909                else if(b == sizeof(buffer)-2 || !ch)
910                {
911                   buffer[b++] = ch;
912                   if(ch) buffer[b] = 0;
913                   f.Puts(buffer);
914                   b = 0;
915                   if(!ch) break;
916                }
917                else
918                   buffer[b++] = ch;
919             }
920          }
921          /*else
922             f.Puts(value.p);*/
923          f.Puts("\"");
924       }
925    }
926    else if(!strcmp(type.name, "bool"))
927    {
928       if(value.i)
929          f.Puts("true");
930       else
931          f.Puts("false");
932    }
933    else if(!strcmp(type.name, "SetBool"))
934    {
935       if(value.i == SetBool::true)
936          f.Puts("true");
937       else if(value.i == SetBool::false)
938          f.Puts("false");
939       else
940          f.Puts("unset");
941    }
942    else if(type.type == enumClass)
943    {
944       f.Puts("\"");
945       WriteNumber(f, type, value, indent);
946       f.Puts("\"");
947    }
948    else if(eClass_IsDerived(type, class(Map)))
949    {
950       WriteMap(f, type, value.p, indent);
951    }
952    else if(eClass_IsDerived(type, class(Container)))
953    {
954       WriteArray(f, type, value.p, indent);
955    }
956    else if(type.type == normalClass || type.type == noHeadClass || type.type == structClass)
957    {
958       _WriteJSONObject(f, type, value.p, indent);
959    }
960    else if(type.type == bitClass)
961    {
962       Class dataType;
963       dataType = eSystem_FindClass(__thisModule, type.dataTypeString);
964       WriteNumber(f, dataType, value, indent);
965    }
966    else if(type.type == systemClass || type.type == unitClass)
967    {
968       WriteNumber(f, type, value, indent);
969    }
970    return true;
971 }
972
973 public bool WriteJSONObject(File f, Class objectType, void * object, int indent)
974 {
975    bool result = false;
976    if(object)
977    {
978       result = _WriteJSONObject(f, objectType, object, indent);
979       f.Puts("\n");
980    }
981    return result;
982 }
983
984 static bool _WriteJSONObject(File f, Class objectType, void * object, int indent)
985 {
986    if(object)
987    {
988       const char * string = null;
989
990       if(objectType._vTbl[__ecereVMethodID_class_OnGetString] != objectType.base._vTbl[__ecereVMethodID_class_OnGetString])
991       {
992          char buffer[1024];
993          buffer[0] = 0;
994          string = ((const char *(*)())(void *)objectType._vTbl[__ecereVMethodID_class_OnGetString])(objectType, object, buffer, null, null);
995       }
996       if(string)
997       {
998          // TOCHECK: ProjectNode.ec why do we add quotes in OnGetString there?
999          if(string[0] == '\"')
1000             f.Puts(string);
1001          else
1002          {
1003             f.Puts("\"");
1004             f.Puts(string);
1005             f.Puts("\"");
1006          }
1007       }
1008       else
1009       {
1010          Property prop;
1011          int c;
1012          bool isFirst = true;
1013          Class mapKeyClass = null, mapDataClass = null;
1014          Class baseClass;
1015          List<Class> bases { };
1016
1017          if(objectType.templateClass && eClass_IsDerived(objectType.templateClass, class(MapNode)))
1018          {
1019             mapKeyClass = objectType.templateArgs[0].dataTypeClass;
1020             mapDataClass = objectType.templateArgs[2].dataTypeClass;
1021          }
1022
1023          f.Puts("{\n");
1024          indent++;
1025
1026          for(baseClass = objectType; baseClass; baseClass = baseClass.base)
1027          {
1028             if(baseClass.isInstanceClass || !baseClass.base)
1029                break;
1030             bases.Insert(null, baseClass);
1031          }
1032
1033          for(baseClass : bases)
1034          {
1035             for(prop = baseClass.membersAndProperties.first; prop; prop = prop.next)
1036             {
1037                if(prop.memberAccess != publicAccess || (prop.isProperty && (!prop.Set || !prop.Get))) continue;
1038                if(prop.isProperty)
1039                {
1040                   if(!prop.conversion && (!prop.IsSet || prop.IsSet(object)))
1041                   {
1042                      DataValue value { };
1043                      Class type;
1044
1045                      if(mapKeyClass && !strcmp(prop.name, "key"))
1046                         type = mapKeyClass;
1047                      else if(mapDataClass && !strcmp(prop.name, "value"))
1048                         type = mapDataClass;
1049                      else
1050                         type = eSystem_FindClass(__thisModule, prop.dataTypeString);
1051                      if(!type)
1052                         type = eSystem_FindClass(__thisModule.application, prop.dataTypeString);
1053                      if(!type)
1054                         PrintLn("warning: Unresolved data type ", (String)prop.dataTypeString);
1055                      else
1056                      {
1057                         // TOFIX: How to swiftly handle classes with base data type?
1058                         if(type == class(double) || !strcmp(type.dataTypeString, "double"))
1059                         {
1060                            value.d = ((double (*)(void *))(void *)prop.Get)(object);
1061                         }
1062                         else if(type == class(float) || !strcmp(type.dataTypeString, "float"))
1063                         {
1064                            value.f = ((float (*)(void *))(void *)prop.Get)(object);
1065                         }
1066                         else if(type.typeSize == sizeof(int64) || !strcmp(type.dataTypeString, "int64") ||
1067                            !strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64"))
1068                         {
1069                            value.ui64 = ((uint64 (*)(void *))(void *)prop.Get)(object);
1070                         }
1071                         else if(type.typeSize == sizeof(int) || !strcmp(type.dataTypeString, "int") ||
1072                            !strcmp(type.dataTypeString, "unsigned int") || !strcmp(type.dataTypeString, "uint"))
1073                         {
1074                            value.i = ((int (*)(void *))(void *)prop.Get)(object);
1075                         }
1076                         else if(type.typeSize == sizeof(short int) || !strcmp(type.dataTypeString, "short") ||
1077                            !strcmp(type.dataTypeString, "unsigned short") || !strcmp(type.dataTypeString, "uint16") ||
1078                            !strcmp(type.dataTypeString, "int16"))
1079                         {
1080                            value.s = ((short (*)(void *))(void *)prop.Get)(object);
1081                         }
1082                         else if(type.typeSize == sizeof(byte) || !strcmp(type.dataTypeString, "char") ||
1083                            !strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte"))
1084                         {
1085                            value.c = ((char (*)(void *))(void *)prop.Get)(object);
1086                         }
1087                         else if(type.type == structClass)
1088                         {
1089                            value.p = new0 byte[type.structSize];
1090                            ((void (*)(void *, void *))(void *)prop.Get)(object, value.p);
1091                         }
1092                         else
1093                         {
1094                            value.p = ((void *(*)(void *))(void *)prop.Get)(object);
1095                         }
1096
1097                         if(!isFirst) f.Puts(",\n");
1098                         for(c = 0; c<indent; c++) f.Puts("   ");
1099
1100                         f.Puts("\"");
1101                         f.Putc((char)toupper(prop.name[0]));
1102                         f.Puts(prop.name+1);
1103                         f.Puts("\" : ");
1104                         WriteValue(f, type, value, indent);
1105                         isFirst = false;
1106                         if(type.type == structClass)
1107                            delete value.p;
1108                      }
1109                   }
1110                }
1111                else
1112                {
1113                   DataMember member = (DataMember)prop;
1114                   DataValue value { };
1115                   uint offset;
1116                   Class type = eSystem_FindClass(__thisModule, member.dataTypeString);
1117                   if(!type)
1118                      type = eSystem_FindClass(__thisModule.application, member.dataTypeString);
1119
1120                   offset = member._class.offset + member.offset;
1121
1122                   if(type)
1123                   {
1124                      if(type.type == normalClass || type.type == noHeadClass || type.type == structClass || !strcmp(type.name, "String"))
1125                      {
1126                         if(type.type == structClass)
1127                            value.p = (void *)((byte *)object + offset);
1128                         else
1129                            value.p = *(void **)((byte *)object + offset);
1130                         if(!value.p)
1131                            continue;
1132                      }
1133                      else if(type == class(double) || !strcmp(type.dataTypeString, "double"))
1134                      {
1135                         value.d = *(double *)((byte *)object + offset);
1136                      }
1137                      else if(type == class(float) || !strcmp(type.dataTypeString, "float"))
1138                      {
1139                         value.f = *(float *)((byte *)object + offset);
1140                      }
1141                      else if(type.typeSize == sizeof(int64) || !strcmp(type.dataTypeString, "int64") ||
1142                         !strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64"))
1143                      {
1144                         value.ui64 = *(uint64 *)((byte *)object + offset);
1145                      }
1146                      else if(type.typeSize == sizeof(int) || !strcmp(type.dataTypeString, "int") ||
1147                         !strcmp(type.dataTypeString, "unsigned int") || !strcmp(type.dataTypeString, "uint"))
1148                      {
1149                         value.i = *(int *)((byte *)object + offset);
1150                         if(!strcmp(type.name, "bool") || type.type == enumClass)
1151                            if(!value.i)
1152                               continue;
1153                      }
1154                      else if(type.typeSize == sizeof(short int) || !strcmp(type.dataTypeString, "short") ||
1155                         !strcmp(type.dataTypeString, "unsigned short") || !strcmp(type.dataTypeString, "uint16") ||
1156                         !strcmp(type.dataTypeString, "int16"))
1157                      {
1158                         value.s = *(short *)((byte *)object + offset);
1159                      }
1160                      else if(type.typeSize == sizeof(byte) || !strcmp(type.dataTypeString, "char") ||
1161                         !strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte"))
1162                      {
1163                         value.c = *(char *)((byte *)object + offset);
1164                      }
1165                      else
1166                      {
1167                         value.i = *(int *)((byte *)object + offset);
1168                      }
1169
1170                      if(!isFirst) f.Puts(",\n");
1171                      for(c = 0; c<indent; c++) f.Puts("   ");
1172
1173                      f.Puts("\"");
1174                      f.Putc((char)toupper(member.name[0]));
1175                      f.Puts(member.name+1);
1176                      f.Puts("\" : ");
1177                      WriteValue(f, type, value, indent);
1178                      isFirst = false;
1179                   }
1180                }
1181             }
1182          }
1183
1184          delete bases;
1185
1186          indent--;
1187          f.Puts("\n");
1188          for(c = 0; c<indent; c++) f.Puts("   "); f.Puts("}");
1189       }
1190    }
1191    else
1192       f.Puts("null");
1193    return true;
1194 }