Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/OneScript.StandardLibrary/Binary/FileStreamContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ namespace OneScript.StandardLibrary.Binary
/// Следует учитывать, что помимо буферизации существует кэширование чтения и записи файлов в операционной системе, на которое невозможно повлиять программно.
/// </summary>
[ContextClass("ФайловыйПоток", "FileStream")]
public class FileStreamContext : AutoContext<FileStreamContext>, IStreamWrapper, IDisposable
public class FileStreamContext : AutoContext<FileStreamContext>, IDisposable, IStreamWrapper
{

private readonly FileStream _underlyingStream;
Expand Down
92 changes: 38 additions & 54 deletions src/OneScript.StandardLibrary/Json/JSONReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,16 @@ This Source Code Form is subject to the terms of the
at http://mozilla.org/MPL/2.0/.
----------------------------------------------------------*/

using System;
using System.IO;
using Newtonsoft.Json;
using OneScript.Commons;
using OneScript.Contexts;
using OneScript.Exceptions;
using OneScript.StandardLibrary.Binary;
using OneScript.StandardLibrary.Text;
using ScriptEngine.Machine;
using ScriptEngine.Machine.Contexts;
using System;
using System.IO;

namespace OneScript.StandardLibrary.Json
{
Expand All @@ -40,7 +41,6 @@ public override bool Read()
}

/// <summary>
///
/// Предназначен для последовательного чтения JSON-данных из файла или строки.
/// </summary>
[ContextClass("ЧтениеJSON", "JSONReader")]
Expand All @@ -50,7 +50,6 @@ public class JSONReader : AutoContext<JSONReader>
private JsonReaderInternal _reader;

/// <summary>
///
/// Возвращает true если для объекта чтения json был задан текст для парсинга.
/// </summary>
private bool IsOpen() => _reader != null;
Expand All @@ -71,49 +70,25 @@ public static JSONReader Constructor()
}

/// <summary>
///
/// Указывает на позицию, находящуюся сразу после прочитанного значения.
/// При ошибке чтение остается на позиции последнего успешно считанного символа.
/// </summary>
/// <value>Число (Number), Неопределено (Undefined)</value>
[ContextProperty("ТекущаяПозиция", "CurrentPosition")]
public IValue CurrentPosition
{
get
{
if (IsOpen())
{
return ValueFactory.Create(_reader.LinePosition);
}

return ValueFactory.Create(); // Неопределено
}
}
=> IsOpen() ? ValueFactory.Create(_reader.LinePosition) : ValueFactory.Create();

/// <summary>
///
/// Указывает на позицию сразу после прочитанного значения.
/// Например, перед чтением первого элемента - 0, после чтения первого элемента -1 .
/// </summary>
/// <value>Число (Number), Неопределено (Undefined)</value>
[ContextProperty("ТекущаяСтрока", "CurrentLine")]
public IValue CurrentLine
{
get
{
if (IsOpen())
{
return ValueFactory.Create(_reader.LineNumber);
}

return ValueFactory.Create(); // Неопределено
}
}
=> IsOpen() ? ValueFactory.Create(_reader.LineNumber) : ValueFactory.Create();

/// <summary>
///
/// Содержит текущее значение:
///
/// - Число - если ТипТекущегоЗначения имеет значение Число;
/// - Строка - если ТипТекущегоЗначения имеет одно из следующих значений:
/// - Комментарий,
Expand Down Expand Up @@ -160,15 +135,13 @@ public IValue CurrentValue

default:
throw JSONReaderException.CannotGetValue();
;
}
}
}

public object ReaderValue => _reader.Value;

/// <summary>
///
/// Тип текущего значения в документе JSON во внутреннем формате.
/// null - если чтение еще не началось или достигнут конец файла.
/// </summary>
Expand All @@ -183,7 +156,6 @@ public JsonToken CurrentJsonTokenType
}

/// <summary>
///
/// Тип текущего значения в документе JSON.
/// Неопределено - если чтение еще не началось или достигнут конец файла.
/// </summary>
Expand Down Expand Up @@ -217,10 +189,8 @@ public JSONValueTypeEnum CurrentValueType
}

/// <summary>
///
/// Завершает чтение текста JSON из файла или строки.
/// </summary>
///
[ContextMethod("Закрыть", "Close")]
public void Close()
{
Expand All @@ -232,28 +202,25 @@ public void Close()
}

/// <summary>
///
/// Открывает JSON-файл для чтения данным объектом. Если перед вызовом данного метода уже производилось чтение JSON из другого файла или строки, то чтение прекращается и объект инициализируется для чтения из указанного файла.
/// Открывает JSON-файл для чтения данным объектом.
/// Если перед вызовом данного метода уже производилось чтение JSON из другого файла или строки,
/// то чтение прекращается и объект инициализируется для чтения из указанного файла.
/// </summary>
///
/// <param name="JSONFileName">
/// Имя файла, содержащего текст JSON. </param>
/// <param name="encoding">
/// Позволяет задать кодировку входного файла.</param>
[ContextMethod("ОткрытьФайл", "OpenFile")]
public void OpenFile(string JSONFileName, IValue encoding = null)
{
if (IsOpen())
Close();
Close();
Comment on lines 214 to +216
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add explicit null guards for concrete string inputs.

OpenFile and SetString accept string parameters but don’t explicitly handle null. Direct C# calls can still pass null, and failures then happen deeper with less clear diagnostics.

Proposed fix
 public void OpenFile(string JSONFileName, IValue encoding = null)
 {
+    if (JSONFileName == null)
+        throw new ArgumentNullException(nameof(JSONFileName));
+
     Close();

     StreamReader _fileReader;
 public void SetString(string JSONString)
 {
+    if (JSONString == null)
+        throw new ArgumentNullException(nameof(JSONString));
+
     Close();

     _reader = new JsonReaderInternal(new StringReader(JSONString))

Based on learnings: when script-side calls omit concrete string arguments they may become "", while direct calls may pass null, so public methods should explicitly handle null.

Also applies to: 294-297

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/OneScript.StandardLibrary/Json/JSONReader.cs` around lines 214 - 216,
OpenFile and SetString (and the other public methods around lines 294-297 that
accept concrete string parameters) currently assume non-null inputs; add
explicit null checks at the start of each such public method to guard against
direct C# callers passing null by either throwing
ArgumentNullException(nameof(param)) or otherwise handling null
deterministically (e.g., treat as empty string) — locate the methods by name
(OpenFile, SetString and the similarly patterned public string-accepting
methods) and add a clear null-guard for each string parameter with the parameter
name in the exception to provide immediate, unambiguous diagnostics.


StreamReader _fileReader;

try
{
if (encoding != null)
_fileReader = FileOpener.OpenReader(JSONFileName, TextEncodingEnum.GetEncoding(encoding));
else
_fileReader = FileOpener.OpenReader(JSONFileName, System.Text.Encoding.UTF8);
{
var enc = encoding != null ? TextEncodingEnum.GetEncoding(encoding) : System.Text.Encoding.UTF8;
_fileReader = FileOpener.OpenReader(JSONFileName, enc);
}
catch (Exception e)
{
Expand All @@ -266,11 +233,35 @@ public void OpenFile(string JSONFileName, IValue encoding = null)
};
}

/// <summary>
/// Устанавливает поток для чтения JSON данным объектом.
/// Если перед вызовом данного метода уже производилось чтение JSON из другого файла, строки или потока,
/// то чтение прекращается и объект инициализируется для чтения из указанного потока.
/// </summary>
/// <param name="streamContext">
/// Поток для чтения текста JSON.</param>
/// <param name="encoding">
/// Позволяет задать кодировку входного потока.</param>
[ContextMethod("ОткрытьПоток", "OpenStream")]
public void OpenStream(IStreamWrapper streamContext, IValue encoding = null)
{
Close();

var stream = streamContext?.GetUnderlyingStream()
?? throw new ArgumentNullException(nameof(streamContext));

var enc = encoding != null ? TextEncodingEnum.GetEncoding(encoding) : System.Text.Encoding.UTF8;

_reader = new JsonReaderInternal(new StreamReader(stream, enc, leaveOpen:true))
{
SupportMultipleContent = true
};
Comment on lines +253 to +258
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Align OpenStream open/init exception behavior with OpenFile.

OpenFile wraps initialization failures into RuntimeException, while OpenStream currently leaks raw .NET exceptions from encoding resolution/reader creation. Keeping this consistent improves script-facing behavior.

Proposed fix
-            var enc = encoding != null ? TextEncodingEnum.GetEncoding(encoding) : System.Text.Encoding.UTF8;
-
-            _reader = new JsonReaderInternal(new StreamReader(stream, enc, leaveOpen:true))
-            {
-                SupportMultipleContent = true
-            };
+            try
+            {
+                var enc = encoding != null ? TextEncodingEnum.GetEncoding(encoding) : System.Text.Encoding.UTF8;
+
+                _reader = new JsonReaderInternal(new StreamReader(stream, enc, leaveOpen: true))
+                {
+                    SupportMultipleContent = true
+                };
+            }
+            catch (Exception e)
+            {
+                throw new RuntimeException(e.Message, e);
+            }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
var enc = encoding != null ? TextEncodingEnum.GetEncoding(encoding) : System.Text.Encoding.UTF8;
_reader = new JsonReaderInternal(new StreamReader(stream, enc, leaveOpen:true))
{
SupportMultipleContent = true
};
try
{
var enc = encoding != null ? TextEncodingEnum.GetEncoding(encoding) : System.Text.Encoding.UTF8;
_reader = new JsonReaderInternal(new StreamReader(stream, enc, leaveOpen: true))
{
SupportMultipleContent = true
};
}
catch (Exception e)
{
throw new RuntimeException(e.Message, e);
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/OneScript.StandardLibrary/Json/JSONReader.cs` around lines 253 - 258,
OpenStream currently allows .NET exceptions from TextEncodingEnum.GetEncoding
and new JsonReaderInternal to leak; mirror OpenFile by wrapping the encoding
resolution and _reader initialization in a try/catch and throw a
RuntimeException instead. Specifically, surround the calls to
TextEncodingEnum.GetEncoding(encoding) and the JsonReaderInternal/StreamReader
creation that assigns _reader with a try block, catch Exception ex, and rethrow
a new RuntimeException (include a clear message and set ex as the inner
exception) so OpenStream behavior matches OpenFile.

}

/// <summary>
/// Если текущее значение – начало массива или объекта, то пропускает его содержимое и конец.
/// Для остальных типов значений работает аналогично методу Прочитать().
/// </summary>
///
[ContextMethod("Пропустить", "Skip")]
public bool Skip()
{
Expand All @@ -287,29 +278,22 @@ public bool Skip()
/// <summary>
/// Выполняет чтение значения JSON.
/// </summary>
///
[ContextMethod("Прочитать", "Read")]
public bool Read()
{
CheckIfOpen();
return _reader.Read();

}

/// <summary>
///
/// Устанавливает строку, содержащую текст JSON для чтения данным объектом. Если перед вызовом данного метода уже производилось чтение JSON из другого файла или строки, то чтение прекращается и объект инициализируется для чтения из указанной строки.
/// </summary>
///
/// <param name="JSONString">
/// Строка, содержащая текст в формате JSON. </param>
///
///
[ContextMethod("УстановитьСтроку", "SetString")]
public void SetString(string JSONString)
{
if (IsOpen())
Close();
Close();

_reader = new JsonReaderInternal(new StringReader(JSONString))
{
Expand Down
37 changes: 37 additions & 0 deletions tests/json/test-json_reader.os
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@
СписокТестов.Добавить("Тест_Должен_ПроверитьПропускНезавершенногоМассива");
СписокТестов.Добавить("Тест_Должен_ПроверитьПропускМассиваСВложенными");
СписокТестов.Добавить("Тест_Должен_ПроверитьПропускОбъектаСВложениями");

СписокТестов.Добавить("Тест_Должен_ПроверитьОткрытиеПотока");
СписокТестов.Добавить("Тест_Должен_ПроверитьПовторноеОткрытиеПотока");
СписокТестов.Добавить("Тест_Должен_ПроверитьСохранениеПотокаПослеЗакрытияЧтения");
Возврат СписокТестов;

КонецФункции
Expand Down Expand Up @@ -219,3 +223,36 @@
юТест.ПроверитьРавенство(ТипЗначенияJSON.Число, Чтение.ТипТекущегоЗначения);
юТест.ПроверитьРавенство(5, Чтение.ТекущееЗначение);
КонецПроцедуры

Процедура Тест_Должен_ПроверитьОткрытиеПотока() Экспорт
БДД = ПолучитьБуферДвоичныхДанныхИзСтроки("{""ответ"":42}");
Поток = Новый ПотокВПамяти(БДД);
Чтение = Новый ЧтениеJSON;
Чтение.ОткрытьПоток(Поток);
Json = ПрочитатьJSON(Чтение);

юТест.ПроверитьРавенство(42, Json.Ответ);
КонецПроцедуры

Процедура Тест_Должен_ПроверитьПовторноеОткрытиеПотока() Экспорт
БДД = ПолучитьБуферДвоичныхДанныхИзСтроки("{""ответ"":42}");
Поток = Новый ПотокВПамяти(БДД);
Чтение = Новый ЧтениеJSON;
Чтение.ОткрытьПоток(Поток);
Чтение.ОткрытьПоток(Поток); // повторно
Json = ПрочитатьJSON(Чтение);

юТест.ПроверитьРавенство(42, Json.Ответ);
КонецПроцедуры

Процедура Тест_Должен_ПроверитьСохранениеПотокаПослеЗакрытияЧтения() Экспорт
БДД = ПолучитьБуферДвоичныхДанныхИзСтроки("{""ответ"":42}");
Поток = Новый ПотокВПамяти(БДД);
Чтение = Новый ЧтениеJSON;
Чтение.ОткрытьПоток(Поток);
Чтение.Закрыть();
Чтение.ОткрытьПоток(Поток); // Поток должен остаться открытым
Json = ПрочитатьJSON(Чтение);

юТест.ПроверитьРавенство(42, Json.Ответ);
КонецПроцедуры
Comment thread
coderabbitai[bot] marked this conversation as resolved.