Skip to content

[WIP] native: для загрузки библиотек и запуска тестов#1694

Open
Mr-Rm wants to merge 8 commits into
EvilBeaver:developfrom
Mr-Rm:v2/native-testrunner
Open

[WIP] native: для загрузки библиотек и запуска тестов#1694
Mr-Rm wants to merge 8 commits into
EvilBeaver:developfrom
Mr-Rm:v2/native-testrunner

Conversation

@Mr-Rm
Copy link
Copy Markdown
Collaborator

@Mr-Rm Mr-Rm commented Jun 1, 2026

Исправлены ошибки в native, не позволявшие скомпилировать и запустить test/testrunner.os

Summary by CodeRabbit

  • Refactor
    • Exception info now uses a more general value type for broader compatibility across runtime components.
    • Instance-context handling and runtime method compilation unified to use a direct attachable context, simplifying invocation paths.
    • Call argument preparation updated to preserve undefined for missing module-scope arguments instead of erroring.
    • Removed an obsolete internal wrapper to streamline the runtime type hierarchy and context access.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jun 1, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Removes NativeClassInstanceWrapper indirection by using IAttachableContext directly for instance targets, centralizes object member-call generation in the compiler, adds passUndef call-argument behavior, and generalizes exception-info APIs to return BslValue instead of BslObjectValue.

Changes

Wrapper Removal and Context Refactoring

Layer / File(s) Summary
Exception info contract generalization
src/OneScript.Core/Exceptions/IExceptionInfoFactory.cs, src/ScriptEngine/Machine/ExceptionInfoFactory.cs, src/OneScript.Native/Runtime/DynamicOperations.cs
Interface and implementations of exception-to-info conversion now return BslValue instead of BslObjectValue; DynamicOperations helper signature updated accordingly.
Runtime callable method context refactoring
src/OneScript.Native/Runtime/CallableMethod.cs
NativeCallable delegate signature, GetCallableWrapper logic, and delegate compilation updated to accept IAttachableContext directly as the instance target; instance wrapping via NativeClassInstanceWrapper removed.
Compiler instance and method-call refactoring
src/OneScript.Native/Compiler/MethodCompiler.cs, src/OneScript.Native/Compiler/ExpressionHelpers.cs
_this parameter type and AccessModuleVariable now use IAttachableContext. Object member-call compilation centralized into CreateObjectMethodCall; method lookup wrapped into TryFindMethodOfType. PrepareCallArguments gains passUndef behavior; CLR IValue results are cast to BslValue. Dynamic constructor arg handling and type-name conversion adjusted.

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant MethodCompiler as MethodCompiler (runtime path)
  participant DynamicOperations as DynamicOperations.GetExceptionInfo
  participant Factory as IExceptionInfoFactory.GetExceptionInfo
  Caller->>MethodCompiler: runtime exception occurs
  MethodCompiler->>DynamicOperations: GetExceptionInfo(factory, exception)
  DynamicOperations->>Factory: GetExceptionInfo(Exception)
  Factory-->>DynamicOperations: BslValue (exception info)
  DynamicOperations-->>MethodCompiler: BslValue
  MethodCompiler-->>Caller: exception description / handling
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐇 I nudged the wrapper off the way,
The context speaks without delay.
Exceptions now wear softer skin,
Values flow where once wrapped in.

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 3.85% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The PR title is in Russian and uses non-descriptive/vague language ('[WIP]' prefix, 'для загрузки библиотек и запуска тестов' = 'for loading libraries and running tests'), which does not clearly convey what the main technical changes are. It does not summarize the primary changes (refactoring from NativeClassInstanceWrapper to IAttachableContext, changing exception info return types, etc.). Revise the title to be more specific and descriptive in English, such as: 'Refactor native runtime to use IAttachableContext and fix exception info return types' or similar, clearly indicating the main technical changes made.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

Review ran into problems

🔥 Problems

Git: Failed to clone repository. Please run the @coderabbitai full review command to re-trigger a full review. If the issue persists, set path_filters to include or exclude specific files.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@Mr-Rm
Copy link
Copy Markdown
Collaborator Author

Mr-Rm commented Jun 1, 2026

Для запуска надо в oscript.cfg указать
runtime.default = native
Сами тесты запускать можно, но в них тоже есть препятствия для компиляции в native. WIP

@Mr-Rm
Copy link
Copy Markdown
Collaborator Author

Mr-Rm commented Jun 3, 2026

к 10b97bc : компилятор сейчас не может разделить UserScriptContextInstance с динамическими методами и AutoContext<T> со статическими. Сделано, как уже сейчас есть у свойств: найдено - статика, нет - поиск переносится в рантайм

@Mr-Rm Mr-Rm changed the title native: запускается testrunner.os [WIP] native: для загрузки библиотек и запуска тестов Jun 3, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/OneScript.Native/Compiler/MethodCompiler.cs (2)

1477-1491: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Handle trailing omitted arguments when passUndef is enabled.

passUndef only covers empty placeholders that already exist in argList. A module-scope call with fewer actual arguments still falls into this tail loop and raises “Недостаточно фактических параметров” instead of filling the remaining parameters with Неопределено, so the new behavior only works for explicit holes, not for omitted trailing args.

Suggested fix
             if (parametersToProcess > 0)
             {
                 foreach (var declaredParam in declaredParameters.Skip(parameters.Length + declStart))
                 {
                     if (declaredParam.HasDefaultValue)
                     {
                         factArguments.Add(Expression.Constant(declaredParam.DefaultValue, declaredParam.ParameterType));
                     }
+                    else if (passUndef)
+                    {
+                        factArguments.Add(Expression.Constant(BslUndefinedValue.Instance, declaredParam.ParameterType));
+                    }
                     else
                     {
                         var errText = new BilingualString("Недостаточно фактических параметров",
                             "Not enough actual parameters");
                         AddError(errText, argList.Location);
🤖 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.Native/Compiler/MethodCompiler.cs` around lines 1477 - 1491,
The tail loop currently treats missing trailing actuals as errors (AddError) by
checking declaredParam.HasDefaultValue; update the loop in MethodCompiler (the
block using parametersToProcess, declaredParameters, parameters, declStart) to
respect the passUndef flag: if passUndef is true and a declaredParam does not
have a default, append the engine's Undefined value to factArguments instead of
calling AddError, otherwise keep the existing behaviour (use default when
HasDefaultValue or call AddError). Ensure the Undefined sentinel you add matches
the runtime representation used elsewhere (the same value/type used for explicit
holes) and preserve argList.Location when producing errors.

1166-1180: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Fix off-by-one in CSharpArgumentInfo count for the InvokeMember dynamic-call fallback.

Expression.Dynamic(..., args) includes the receiver as the first element of args, but csharpArgs is seeded with an extra CSharpArgumentInfo and then adds one more per args element, so the binder receives one descriptor too many; Binder.InvokeMember requires the CSharpArgumentInfo[] count to exactly match the number of argument expressions passed to Expression.Dynamic (including the receiver). This mismatch can cause the fallback path to fail at runtime.

Suggested fix
-                var csharpArgs = new List<CSharpArgumentInfo>
-                {
-                    CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, default)
-                };
-                csharpArgs.AddRange(args.Select(x => CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, default)));
+                var csharpArgs = args
+                    .Select(_ => CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, default))
+                    .ToList();
🤖 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.Native/Compiler/MethodCompiler.cs` around lines 1166 - 1180,
The binder argument count is off-by-one: currently csharpArgs is initialized
with an extra CSharpArgumentInfo then adds one per element of args, so its
length is one greater than args and breaks Binder.InvokeMember; fix
MethodCompiler by making csharpArgs match args exactly (since args already
includes the receiver) — e.g. build csharpArgs from args.Select(...).ToList()
(or seed one and add args.Skip(1) if you prefer), then pass that array to
Microsoft.CSharp.RuntimeBinder.Binder.InvokeMember and use
Expression.Dynamic(binder, typeof(object), args) as before.
🤖 Prompt for all review comments with 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.

Outside diff comments:
In `@src/OneScript.Native/Compiler/MethodCompiler.cs`:
- Around line 1477-1491: The tail loop currently treats missing trailing actuals
as errors (AddError) by checking declaredParam.HasDefaultValue; update the loop
in MethodCompiler (the block using parametersToProcess, declaredParameters,
parameters, declStart) to respect the passUndef flag: if passUndef is true and a
declaredParam does not have a default, append the engine's Undefined value to
factArguments instead of calling AddError, otherwise keep the existing behaviour
(use default when HasDefaultValue or call AddError). Ensure the Undefined
sentinel you add matches the runtime representation used elsewhere (the same
value/type used for explicit holes) and preserve argList.Location when producing
errors.
- Around line 1166-1180: The binder argument count is off-by-one: currently
csharpArgs is initialized with an extra CSharpArgumentInfo then adds one per
element of args, so its length is one greater than args and breaks
Binder.InvokeMember; fix MethodCompiler by making csharpArgs match args exactly
(since args already includes the receiver) — e.g. build csharpArgs from
args.Select(...).ToList() (or seed one and add args.Skip(1) if you prefer), then
pass that array to Microsoft.CSharp.RuntimeBinder.Binder.InvokeMember and use
Expression.Dynamic(binder, typeof(object), args) as before.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 93a47660-f7ba-4cf9-89d0-15d3add376d5

📥 Commits

Reviewing files that changed from the base of the PR and between 10b97bc and 66be352.

📒 Files selected for processing (1)
  • src/OneScript.Native/Compiler/MethodCompiler.cs

@Mr-Rm
Copy link
Copy Markdown
Collaborator Author

Mr-Rm commented Jun 3, 2026

@coderabbitai

"Handle trailing omitted arguments when passUndef is enabled"

Недостающие параметры, в отличие от пропущенных, не заполняются Неопределено, а вызывают ошибку.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jun 3, 2026

Tip

For best results, initiate chat on the files or code changes.

@Mr-Rm, спасибо за уточнение! Вы правы — я неточно сформулировал: пропущенные параметры (явно опущенные в вызове, как foo(,val,)) заполняются Неопределено, тогда как недостающие параметры (хвостовые, не переданные вовсе) вызывают ошибку. Запомню это различие.


✏️ Learnings added
Learnt from: Mr-Rm
URL: https://github.com/EvilBeaver/OneScript/pull/1694

Timestamp: 2026-06-03T11:16:31.480Z
Learning: В OneScript (репозиторий EvilBeaver/OneScript) существует важное различие между двумя типами отсутствующих параметров при вызове методов:
- "Пропущенные параметры" (явно опущенные слоты, например `foo(,val,)`) — заполняются значением `Неопределено` (Undefined).
- "Недостающие параметры" (хвостовые параметры, не переданные вовсе) — вызывают ошибку и НЕ заполняются `Неопределено`.
Это поведение реализовано через флаг `passUndef` в `PrepareCallArguments` внутри `src/OneScript.Native/Compiler/MethodCompiler.cs`.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: nixel2007
Repo: EvilBeaver/OneScript PR: 0
File: :0-0
Timestamp: 2024-08-28T16:51:21.322Z
Learning: В репозитории EvilBeaver/OneScript всегда нужно говорить по-русски.

Learnt from: Mr-Rm
Repo: EvilBeaver/OneScript PR: 1636
File: src/OneScript.StandardLibrary/StringOperations.cs:155-157
Timestamp: 2025-12-18T16:13:16.235Z
Learning: In OneScript, when a method parameter has a concrete type like `string` (rather than `IValue`), omitted parameters in script calls are passed as empty strings (`""`) by the runtime, not as `null`. Direct (non-script) calls to the method may still pass `null`, so defensive null checks can be added for such cases.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/OneScript.Native/Compiler/MethodCompiler.cs (2)

1134-1145: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Cast direct object-method IValue returns to BslValue.

This branch now returns Expression.Call(...) as-is. If the CLR method is declared to return IValue, downstream code in this compiler still expects BslValue in several places; CreateMethodCall(...) already handles that case. Leaving it uncast can break expression-tree generation when the result flows into a BslValue-typed return or assignment path.

Proposed fix
-                var args = PrepareCallArguments(call.ArgumentList, methodInfo.GetParameters(), InjectedProcessNeeded(methodInfo));
-                return Expression.Call(target, methodInfo, args);
+                var args = PrepareCallArguments(call.ArgumentList, methodInfo.GetParameters(), InjectedProcessNeeded(methodInfo));
+                var callExpr = Expression.Call(target, methodInfo, args);
+                return callExpr.Type == typeof(IValue)
+                    ? Expression.TypeAs(callExpr, typeof(BslValue))
+                    : callExpr;
🤖 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.Native/Compiler/MethodCompiler.cs` around lines 1134 - 1145,
The branch that handles calling CLR methods found via TryFindMethodOfType
returns Expression.Call(...) directly but doesn't cast methods whose ReturnType
is IValue to BslValue, which downstream compiler code expects; update the branch
in MethodCompiler.cs (where TryFindMethodOfType, PrepareCallArguments are used)
to convert the call result to BslValue: after building the args, if
methodInfo.ReturnType == typeof(IValue) wrap/replace the raw Expression.Call
with the same conversion path used by CreateMethodCall (i.e. use
CreateMethodCall or otherwise cast the call result to BslValue) so callers get a
BslValue-typed expression.

1457-1464: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Convert omitted arguments through the normal parameter pipeline.

Line 1463 materializes Undefined as Expression.Constant(BslUndefinedValue.Instance, declaredParam.ParameterType). That only works when declaredParam.ParameterType is assignable from BslUndefinedValue; for concrete types like string it will fail while building the tree, and it also skips the existing conversion semantics for omitted script arguments.

Proposed fix
-                    else if (passUndef)
-                    {
-                        factArguments.Add(Expression.Constant(BslUndefinedValue.Instance, declaredParam.ParameterType));
-                    }
+                    else if (passUndef)
+                    {
+                        var undef = Expression.Constant(BslUndefinedValue.Instance, typeof(BslValue));
+                        factArguments.Add(PassSingleParameter(undef, declaredParam.ParameterType, argList.Location));
+                    }

Based on learnings: omitted slots are filled with Undefined, missing trailing parameters must still error, and concrete script-call parameters such as string need the normal conversion path rather than a raw typed Undefined constant.

🤖 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.Native/Compiler/MethodCompiler.cs` around lines 1457 - 1464,
The code currently emits Expression.Constant(BslUndefinedValue.Instance,
declaredParam.ParameterType) when passUndef is true which fails for concrete
parameter types and bypasses the normal conversion semantics; instead, create an
expression that represents the Undefined value (BslUndefinedValue.Instance) and
pass it through the existing parameter conversion pipeline used for actual call
arguments (the same conversion helper used elsewhere in MethodCompiler for
declaredParam/parameter conversion) before adding to factArguments so concrete
types (e.g., string) are produced correctly and existing conversion/error
behavior for missing trailing parameters is preserved.
🤖 Prompt for all review comments with 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.

Outside diff comments:
In `@src/OneScript.Native/Compiler/MethodCompiler.cs`:
- Around line 1134-1145: The branch that handles calling CLR methods found via
TryFindMethodOfType returns Expression.Call(...) directly but doesn't cast
methods whose ReturnType is IValue to BslValue, which downstream compiler code
expects; update the branch in MethodCompiler.cs (where TryFindMethodOfType,
PrepareCallArguments are used) to convert the call result to BslValue: after
building the args, if methodInfo.ReturnType == typeof(IValue) wrap/replace the
raw Expression.Call with the same conversion path used by CreateMethodCall (i.e.
use CreateMethodCall or otherwise cast the call result to BslValue) so callers
get a BslValue-typed expression.
- Around line 1457-1464: The code currently emits
Expression.Constant(BslUndefinedValue.Instance, declaredParam.ParameterType)
when passUndef is true which fails for concrete parameter types and bypasses the
normal conversion semantics; instead, create an expression that represents the
Undefined value (BslUndefinedValue.Instance) and pass it through the existing
parameter conversion pipeline used for actual call arguments (the same
conversion helper used elsewhere in MethodCompiler for declaredParam/parameter
conversion) before adding to factArguments so concrete types (e.g., string) are
produced correctly and existing conversion/error behavior for missing trailing
parameters is preserved.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a4421347-3d2c-4580-b933-46cfab0d0d75

📥 Commits

Reviewing files that changed from the base of the PR and between 66be352 and 32310b3.

📒 Files selected for processing (1)
  • src/OneScript.Native/Compiler/MethodCompiler.cs

@dmpas dmpas added the native Компиляция в CLR в движке 2.0 label Jun 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

native Компиляция в CLR в движке 2.0

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants