Skip to content
Merged
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
7 changes: 6 additions & 1 deletion src/cs/Executor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ public enum ScriptType : uint {

public record struct ThreadInfoData(int Activator);

public class StackUnderflowException : Exception {}

public readonly ref struct ThreadHandle
{
private readonly unsafe Interop.Thread* m_ptr;
Expand Down Expand Up @@ -272,7 +274,10 @@ public bool HasActiveThread() {
}
public void Exec() {
unsafe {
Interop.Methods.Exec(m_executor);
var error = Interop.Methods.Exec(m_executor);
if (error == Interop.ExecError.StackUnderflow) {
throw new StackUnderflowException();
}
}
}
public unsafe bool SaveState(string file)
Expand Down
14 changes: 10 additions & 4 deletions src/cxx/ACSVM/ACSVM/Stack.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@

namespace ACSVM
{
class StackUnderflowException {};

//
// Stack
//
Expand All @@ -48,13 +50,17 @@ namespace ACSVM

// drop
void drop() {
if (stkPtr <= stack)
return;
(--stkPtr)->~T();
drop(1);
}
void drop(std::size_t n) {
while (n-- && stkPtr > stack)
while (n--)
{
if (stkPtr <= stack)
{
throw StackUnderflowException();
}
(--stkPtr)->~T();
}
}

// empty
Expand Down
15 changes: 10 additions & 5 deletions src/cxx/HelionACS.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
#include "ACSVM/ACSVM/Serial.hpp"
#include "ACSVM/ACSVM/SerialSTD.hpp"
#include "ACSVM/ACSVM/Error.hpp"
#include "ACSVM/ACSVM/Stack.hpp"
#include <cstddef>
#include <optional>
#include <span>
#include <string_view>
#include <fstream>
Expand Down Expand Up @@ -191,8 +191,13 @@ class Executor {
bool HasActiveThread() {
return this->env.hasActiveThread();
}
void Exec() {
this->env.exec();
ExecError Exec() {
try {
this->env.exec();
return ExecError::None;
} catch (ACSVM::StackUnderflowException) {
return ExecError::StackUnderflow;
}
}

ACSVM::Word AddCallFunc(void* funcContext, CallFunc callFunc) {
Expand Down Expand Up @@ -298,8 +303,8 @@ bool ScriptPauseNum(Executor* executor, ACSVM::Word num, ACSVM::Word hubId, ACSV
bool HasActiveThread(Executor *executor) {
return executor->HasActiveThread();
}
void Exec(Executor* executor) {
executor->Exec();
ExecError Exec(Executor* executor) {
return executor->Exec();
}
bool SaveState(Executor* executor, char* toFile) {
return executor->SaveState(toFile);
Expand Down
7 changes: 6 additions & 1 deletion src/cxx/HelionACS.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,13 @@ HELIONACS_API bool ScriptStopNum(Executor* executor, ACSVM::Word num, ACSVM::Wor
HELIONACS_API bool ScriptPauseName(Executor* executor, const char* name, ACSVM::Word hubId, ACSVM::Word mapId);
HELIONACS_API bool ScriptPauseNum(Executor* executor, ACSVM::Word num, ACSVM::Word hubId, ACSVM::Word mapId);

enum ExecError {
None,
StackUnderflow
};

HELIONACS_API bool HasActiveThread(Executor* executor);
HELIONACS_API void Exec(Executor* executor);
HELIONACS_API ExecError Exec(Executor* executor);
HELIONACS_API bool SaveState(Executor * executor, char* toFile);
HELIONACS_API bool LoadState(Executor * executor, char* fromFile);

Expand Down
49 changes: 49 additions & 0 deletions tests/Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,15 @@ public MyExecutor() {
AddCodeDataACS0(62, "W", 0, CF_TagWait);
AddCodeDataACS0(86, "", 0, CF_EndPrint);

AddCodeDataACS0(149, "", 6, CF_Spawn);
AddCodeDataACS0(150, "WSWWWWW", 0, CF_Spawn);

AddFuncDataACS0(9, CF_GetActorVelX);
}
public void UseBrokenSpawn() {
AddCodeDataACS0(149, "", 6, CF_SpawnBroken);
AddCodeDataACS0(150, "WSWWWWW", 0, CF_SpawnBroken);
}

public override byte[] LoadModule(string moduleName) {
Assert.Equal("module", moduleName);
Expand Down Expand Up @@ -68,6 +75,26 @@ public bool CF_GetActorVelX(HelionACS.ThreadHandle thread, uint[] args) {
}
return false;
}
public bool CF_Spawn(HelionACS.ThreadHandle thread, uint[] args) {
Assert.Equal("something", thread.GetString(args[0]));
Assert.Equal(1u, args[1]);
Assert.Equal(2u, args[2]);
Assert.Equal(3u, args[3]);
Assert.Equal(0u, args[4]);
Assert.Equal(0u, args[5]);
thread.PushStack(1);
return false;
}
public bool CF_SpawnBroken(HelionACS.ThreadHandle thread, uint[] args) {
Assert.Equal("something", thread.GetString(args[0]));
Assert.Equal(1u, args[1]);
Assert.Equal(2u, args[2]);
Assert.Equal(3u, args[3]);
Assert.Equal(0u, args[4]);
Assert.Equal(0u, args[5]);
// lack of stack push
return true;
}
}

public class ExecutorTests
Expand Down Expand Up @@ -193,4 +220,26 @@ public void TestAddFunc()

Assert.False(executor.HasActiveThread());
}

[Fact]
public void TestSpawn()
{
Assert.True(executor.ScriptStart("UsesSpawn", 0, 0, [], DefaultThreadInfo));
Assert.True(executor.HasActiveThread()); executor.Exec();
Assert.Equal(["1"], executor.printBufferOutput);

Assert.False(executor.HasActiveThread());
}

[Fact]
public void TestStackUnderflow()
{
var executor = new MyExecutor();
executor.UseBrokenSpawn();
executor.LoadHubMap(0, 0, ["module"]);

Assert.True(executor.ScriptStart("UsesSpawn", 0, 0, [], DefaultThreadInfo));
Assert.True(executor.HasActiveThread());
Assert.Throws<HelionACS.StackUnderflowException>(() => executor.Exec());
}
}
3 changes: 3 additions & 0 deletions tests/test.acs
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,6 @@ script "UsesGetActorVelX" (void) {
Print(f:GetActorVelX(5));
Print(f:GetActorVelX(4));
}
script "UsesSpawn" (void) {
Print(i:Spawn("something", 1, 2, 3));
}
Binary file modified tests/test.acs.o
Binary file not shown.
Loading