Delphi JSONArray SetJSONValue access violation

I have a JSONArray being passed as a parameter to my server. I have a method generated by the "generate datasnap Client Classes" procedure and when it gets to the first "SetJsonValue" line it gives me an access violation, this error comes and goes and has no aparent reason. I'm using a Rest Server in LAN (also use it with http but haven't tested it out with it yet). Sometimes it gets solved by restarting my computer/the compiler but other times it lasts longer.

the method looks as follows:

if FExecutaInsertCommand = nil then
begin
  FExecutaInsertCommand := FConnection.CreateCommand;
  FExecutaInsertCommand.RequestType := 'POST';
  FExecutaInsertCommand.Text := 'TServerMethods."ExecutaInsert"';
  FExecutaInsertCommand.Prepare(TServerMethods_ExecutaInsert);
end;
FExecutaInsertCommand.Parameters[0].Value.SetWideString(database);
FExecutaInsertCommand.Parameters[1].Value.SetWideString(Tabela);
FExecutaInsertCommand.Parameters[2].Value.SetJSONValue(aValores,   FInstanceOwner);
FExecutaInsertCommand.Parameters[3].Value.SetJSONValue(aTipos,   FInstanceOwner);
FExecutaInsertCommand.Parameters[4].Value.SetInt32(usuario);
FExecutaInsertCommand.Execute(ARequestFilter);
Result := FExecutaInsertCommand.Parameters[5].Value.GetInt32;

the method is Called from here:

result := DMClient.ServerMethodsClient.ExecutaInsert(banco, Tabela, GeraJSONArray(Registro), GeraJSONArray(GetStrings(Registro.DataType)), CodigoUsuarioLogado);

and the JSONArrays are generated like this:

function GeraJSONArray(Valores: TVariantArray) : TJSONArray;
var
  i : Integer;
begin
  result := TJSONArray.Create;
  for i := 0 to Length(Valores.Values) - 1 do
    result.Adiciona(Valores.Values[i], Valores.DataType[i]);
end;

function GeraJSONArray(Valores: TArray<String>) : TJSONArray;
var
  i : Integer;
begin
  result := TJSONArray.Create;
  for i := 0 to Length(Valores) - 1 do
    result.AddElement(TJSONString.Create(Valores[i]));
end;

JsonArray.Adiciona:

procedure TJSONArrayHelper.Adiciona(valor: variant; DataType: TFieldType);
var
  JSONValue : TJSONValue;
begin
  if not (VarIsEmpty(valor)) and not (VarIsNull(valor)) then
  begin
    case DataType of 
      TFieldType.ftString,
      TFieldType.ftMemo,
      TFieldType.ftFmtMemo,
      TFieldType.ftFixedChar,
      TFieldType.ftWideString : JSONValue := TJSONString.Create(valor);
      //-----------------------------------------------//
      TFieldType.ftSmallint,
      TFieldType.ftInteger,
      TFieldType.ftWord,
      TFieldType.ftFloat,
      TFieldType.ftCurrency,
      TFieldType.ftAutoInc,
      TFieldType.ftLargeint,
      TFieldType.ftSingle,
      TFieldType.ftBCD : JSONValue := TJSONNumber.Create(valor);
      //-----------------------------------------------//
      TFieldType.ftDateTime : JSONValue :=     TJSONString.Create(DateTimeParaString(valor));
    end;
  end
  else
  begin
    JSONValue := TJSONNull.Create;
  end;
  if JSONValue <> nil then
    AddElement(JSONValue);
end;

Parameter 2 generates the error but parameter 3 doesnt, I think there's aproblem with the Adiciona Procedure

Upvotes: 3

Views: 1073

Answers (1)

Rob Kennedy
Rob Kennedy

Reputation: 163287

In your Adiciona method, if DataType is not any of the values listed in your case statement, then your local JSONValue variable remains uninitialized. In particular, note that the default value for the variable is not nil — there is no default value that a local object-reference variable will have — so your check at the end of the method for JSONValue <> nil is a nonsense comparison.

The documentation lists 52 possible TFieldType values; your code handles only 15. You can use an else clause to account for what remains:

case DataType of
  ...
  else raise Exception.CreateFmt('Unexpected field type (%d)', [Ord(DataType)]);

The compiler should have warned you that the variable might not be initialized, although it's impossible for the analysis required for that warning to be perfect, so it's possible the compiler was unable to detect that case. If the compiler warned you and you ignored the warning, then let this be a lesson for you.

The behavior we might expect from adding an uninitialized object reference to the array is consistent with the access-violation exception you've observed. The destructor will get the uninitialized value from the array and try to call GetOwned on it. Calling methods on things that aren't object references can lead to any number of problems; the best thing you can hope for is what you're seeing now, which is an exception immediately telling you something is wrong. (Just think how bad this could have been if the program had silently continued running. Who knows what strange problems you would see if the destructor's Member.Free call had run and actually destroyed something else in your program?)

Upvotes: 1

Related Questions