Recent

Author Topic: mormot2 JSON + If + and = wtf?  (Read 1125 times)

commanderz

  • New Member
  • *
  • Posts: 10
mormot2 JSON + If + and = wtf?
« on: February 04, 2023, 08:53:21 pm »
Hello ,
I'm messing around with mormot2 JSON here and noticed a very strange thing (which works fine in delphi)
In the following code, I try to parse JSON using the rather convenient "variant way".
And I have a question why the lines are marked as //ok!!!! and //error!!! that's how they behave.
I can argue that both methods will work in delphi, but for some reason only one in lazarus (fpc).
 More details:
the problem in my opinion is that lazarus (fpc ) tries to execute the next "and" operator even though the previous one returned false,
which is enough for the whole expression to be false .  :o
  Maybe there is some optimizing parameter for the compiler that I omitted?  :-[
Code: Pascal  [Select][+][-]
  1.  //for those who are too lazy to read the code, we have the following values:
  2.   J._Count=2
  3.   J._(2)=nil
  4.  //now you see why we check these conditions before calling non-existent values in 71, 74 lines
  5.  
Next is our program, a simple form with two memos and a button
Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}
  4. {$H+}
  5.  
  6. interface
  7.  
  8. uses
  9.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls
  10.   ,mormot.core.base
  11.   ,mormot.core.text
  12.   ,mormot.core.json
  13.   ,mormot.core.variants
  14.   ;
  15.  
  16.  
  17. type
  18.  
  19.   { TForm1 }
  20.  
  21.   TForm1 = class(TForm)
  22.     Button1: TButton;
  23.     Memo1: TMemo;
  24.     Memo2: TMemo;
  25.     procedure Button1Click(Sender: TObject);
  26.   private
  27.  
  28.   public
  29.  
  30.   end;
  31.  
  32. var
  33.   Form1: TForm1;
  34.  
  35. implementation
  36.  
  37. {$R *.lfm}
  38.  
  39. { TForm1 }
  40.  
  41. procedure TForm1.Button1Click(Sender: TObject);
  42. Var S: RawUtf8;
  43.     J: Variant;
  44.     i:integer;
  45.     R:  TTestArray;
  46. begin
  47.  memo1.text='[{"name":"ABC"
  48.  ,"Record01":{"ax":"2022-05-11T23:59:56.971655858Z","bx":"abc","cx":123.45,"dx":123,"ex":["abc","def"],"fx":12345,"gx":"ABC"}
  49.  ,"Record02":{"az":"2022-05-11T23:59:56.971655858Z","bz":"def","cz":678.90,"as":456,"ez":"ABC","fz":123.57,"gz":2,"hz":["HIJ"]}
  50.  ,"Record03":{"ad":"2022-05-11T23:59:00Z","bd":123.45,"cd":456.78,"dd":101.01,"ed":234.56,"fd":1234}
  51.  ,"Record04":{"ad":"2022-05-11T04:00:00Z","bd":789.01,"cd":345.56,"dd":901.23,"ed":146.53,"fd":4567}
  52.  ,"Record05":{"ad":"2022-05-10T04:00:00Z","bd":890.12,"cd":345.67,"dd":890.13,"ed":456.87,"fd":90123}
  53. }
  54. ,{"name":"DEF"
  55.  ,"Record01":{"ax":"2022-05-11T23:59:52.111529728Z","bx":"def","cx":678.9,"dx":456,"ex":["ghi","jkl"],"fx":67890,"gx":"DEF"}
  56.  ,"Record02":{"az":"2022-05-11T23:59:59.061193216Z","bz":"ghi","cz":123.45,"as":789,"ez":"DEF","fz":456.89,"gz":1,"hz":["KLM"]}
  57.  ,"Record03":{"ad":"2022-05-11T23:59:00Z","bd":678.90,"cd":901.23,"dd":202.02,"ed":789.01,"fd":5678}
  58.  ,"Record04":{"ad":"2022-05-11T04:00:00Z","bd":234.56,"cd":789.01,"dd":456.78,"ed":456.78,"fd":8901}
  59.  ,"Record05":{"ad":"2022-05-10T04:00:00Z","bd":345.67,"cd":890.12,"dd":456.78,"ed":867.91,"fd":45678}
  60. }
  61. ]
  62. ';
  63.  s := memo1.text;
  64.  J := TDocVariant.NewJson(S);
  65.  memo2.lines.add('Variant way=================');
  66.  memo2.lines.add('J kind='+intToStr(J._Kind)+', J count='+intToStr(J._Count));
  67.  if (J.Exists('none')) then memo2.lines.add('J.none='+J.none);
  68.  //V1._Kind  V1._Count
  69.  
  70.  //ok!!!:
  71.  if ((J._Kind=2)and(J._Count>2)and( J._(2)<>nil) ) then if (J._(2).Exists('name')) then memo2.lines.add('J._(1).name='+J._(1).name);
  72.  
  73.  //error!!!:
  74.  if ((J._Kind=2)and(J._Count>2)and( J._(2)<>nil)and(J._(2).Exists('name')) ) then memo2.lines.add('J._(1).name='+J._(1).name);
  75.  
  76. //ok:
  77.  if ((J._Kind=2)and(J._Count>2) ) then if (J._(1).Exists('name')) then memo2.lines.add('J._(1).name='+J._(1).name);
  78.  
  79. //ok:
  80.  if (J._(0).Exists('name')) then memo2.lines.add('J._(0).name='+J._(0).name);
  81.  if (J._(1).exists('name')) then memo2.lines.add('J._(1).name='+J._(1).name);
  82.  if (    (J._(1).exists('Record01'))
  83.      and (J._(1).Record01._Count>0)
  84.     )
  85.  then memo2.lines.add('J._(1).Record01.ex._(0)='+J._(1).Record01.ex._(0));
  86.  
  87. end;
  88.  
  89. end.
  90.  
  91.  




p.s Using in this code mormot2 JSON synapse help link:
https://blog.synopse.info/?post/2014/02/25/TDocVariant-custom-variant-type

« Last Edit: February 04, 2023, 09:01:46 pm by commanderz »

PascalDragon

  • Hero Member
  • *****
  • Posts: 4968
  • Compiler Developer
Re: mormot2 JSON + If + and = wtf?
« Reply #1 on: February 06, 2023, 11:26:49 pm »
It could be that you've encountered a bug for which we're looking for a reproducible test case (see here). Though it would be nicer if we could reproduce it without a dependency on mORMot...

Joanna

  • Sr. Member
  • ****
  • Posts: 297
Re: mormot2 JSON + If + and = wtf?
« Reply #2 on: February 07, 2023, 02:57:29 pm »
I vaguely remember that the default behavior of pascal is to stop evaluating Boolean expressions when the answer becomes known but there was some setting somewhere to make it evaluate entire expression anyway ?
 I’m not sure why anyone would want to do this but somewhere in some compiler of pascal it was an option. I wish I could remember more about it.
Come chat on IRC .. https://libera.chat/guides/
IRC.LIBERA.CHAT  Ports [6667 plaintext ] or [6697 secure] channels  #fpc #lazarus #pascal 
Private Message me if you want to be added to invite list, otherwise type /msg nickserv help register to get started

TRon

  • Hero Member
  • *****
  • Posts: 834
Re: mormot2 JSON + If + and = wtf?
« Reply #3 on: February 07, 2023, 03:06:24 pm »
I vaguely remember that the default behavior of pascal is to stop evaluating Boolean expressions when the answer becomes known but there was some setting somewhere to make it evaluate entire expression anyway ?
 I’m not sure why anyone would want to do this but somewhere in some compiler of pascal it was an option. I wish I could remember more about it.
You didn't even bother to click the link did you ?

Everything relevant is already provided by PascalDragon including the refreshment for your memory so that you do not have to bother reading the manual concerning compiler directives.

440bx

  • Hero Member
  • *****
  • Posts: 3325
Re: mormot2 JSON + If + and = wtf?
« Reply #4 on: February 07, 2023, 03:07:36 pm »
I wish I could remember more about it.
You're thinking about $B directive.  An explanation for it is at https://www.freepascal.org/docs-html/current/prog/progsu4.html

FPC v3.0.4 and Lazarus 1.8.2 on Windows 7 SP1 64bit.

Joanna

  • Sr. Member
  • ****
  • Posts: 297
Re: mormot2 JSON + If + and = wtf?
« Reply #5 on: February 07, 2023, 03:33:07 pm »
Yes that’s the one. Thanks for link.
I wonder what Sorts of things that would be used for.
Come chat on IRC .. https://libera.chat/guides/
IRC.LIBERA.CHAT  Ports [6667 plaintext ] or [6697 secure] channels  #fpc #lazarus #pascal 
Private Message me if you want to be added to invite list, otherwise type /msg nickserv help register to get started

PascalDragon

  • Hero Member
  • *****
  • Posts: 4968
  • Compiler Developer
Re: mormot2 JSON + If + and = wtf?
« Reply #6 on: February 07, 2023, 09:10:34 pm »
Yes that’s the one. Thanks for link.
I wonder what Sorts of things that would be used for.

If your expression contains function calls then with short boolean evaluation those on the right might not be evaluated depending on the values of those on the left side. Additionally with full boolean evaluation the compiler can reorder the expressions to generate better code. So depending on your usecase there are uses for it.

Joanna

  • Sr. Member
  • ****
  • Posts: 297
Re: mormot2 JSON + If + and = wtf?
« Reply #7 on: February 08, 2023, 12:08:56 am »
I’ve become rather dependent upon the Boolean expression exiting after result is known because I write things like
If (x <> nil) And (x.field1 > 20) Then ...
Come chat on IRC .. https://libera.chat/guides/
IRC.LIBERA.CHAT  Ports [6667 plaintext ] or [6697 secure] channels  #fpc #lazarus #pascal 
Private Message me if you want to be added to invite list, otherwise type /msg nickserv help register to get started

PascalDragon

  • Hero Member
  • *****
  • Posts: 4968
  • Compiler Developer
Re: mormot2 JSON + If + and = wtf?
« Reply #8 on: February 08, 2023, 09:24:56 pm »
I’ve become rather dependent upon the Boolean expression exiting after result is known because I write things like
If (x <> nil) And (x.field1 > 20) Then ...

Well, short boolean evaluation is enabled by default after all...

abouchez

  • Jr. Member
  • **
  • Posts: 79
    • Synopse
Re: mormot2 JSON + If + and = wtf?
« Reply #9 on: February 13, 2023, 09:41:47 am »
You could have rather used the mORMot 2 forum directly.
https://synopse.info/forum
You would have used your answer sooner.

Your code seems not correct, because I guess you make some wrong assumptions.
Code: Pascal  [Select][+][-]
  1. J._(2)<>nil
will always be true I guess. Or at least it is weird/confusing to write such an expression.

J is a variant.
J._() is a variant pseudo-method, also called "late binding", which is evaluated at runtime, and returns a variant value.
I guess you made a confusion between nil (which is a pointer) and null (which is a variant).
So it is never equal to nil at least in FPC - perhaps Delphi makes another assumption but it is rather a very low-level undocumented behavior to compare a variant with a pointer. Perhaps Delphi compare nil into a (void) string, whereas FPC does not. But this is not the point, because it actually compare oranges and apples. ;)

For such complex code, don't use variant and late-binding.
But directly use a TDocVariant local variable. As documented.

This is cleaner, safer, and also faster.
The compiler will check all the types for you - and won't allow any confusion between nil and null for instance.
And you will have much more methods than only the "pseudo methods".

I have observed (at least some years ago) that FPC late-binding support was not as good as Delphi's, and triggered some random GPF.
https://synopse.info/files/html/Synopse%20mORMot%20Framework%20SAD%201.18.html#TITLE_40
(note that the proper documentation about TDocVariant is this URI and not the initial blog article).
So I would not advice use variant late-binding on FPC, but use explicit TDocVariantData values. Then typecast them to a variant if needed.

Casting a mORMot into a variant is mostly useful if you want to store value, and transmit and store them as variants, e.g. from one method to another, mainly for SOA calls.
Rather use TDocVariant whenever you can, if you want to work with JSON.
Using such custom variants in expressions, or casted into other variables (as strings) can be very confusing if you don't know the RTL intrinsics of custom variants types.
« Last Edit: February 13, 2023, 10:37:57 am by abouchez »

 

TinyPortal © 2005-2018