From 69f51513109107a012fded86511407d301fa7d86 Mon Sep 17 00:00:00 2001 From: Kayne Ruse Date: Fri, 20 Jan 2023 23:03:00 +0000 Subject: [PATCH] Finished runner library --- repl/lib_runner.c | 267 ++++++++++++++++-- source/interpreter.c | 7 +- test/scripts/lib/runner.toy | 64 ++++- test/scripts/lib/runner/sample_1.toy | 9 + test/scripts/lib/runner/sample_2.toy | 1 + .../mustfail/access-parent-directory.toy | 8 + test/test_mustfail.c | 1 + 7 files changed, 334 insertions(+), 23 deletions(-) create mode 100644 test/scripts/lib/runner/sample_1.toy create mode 100644 test/scripts/lib/runner/sample_2.toy create mode 100644 test/scripts/mustfail/access-parent-directory.toy diff --git a/repl/lib_runner.c b/repl/lib_runner.c index 9e83408..2b4395e 100644 --- a/repl/lib_runner.c +++ b/repl/lib_runner.c @@ -13,7 +13,7 @@ typedef struct Runner { unsigned char* bytecode; size_t size; - // + bool dirty; } Runner; //Toy native functions @@ -75,6 +75,15 @@ static int nativeLoadScript(Interpreter* interpreter, LiteralArray* arguments) { deleteRefString(drivePath); freeLiteral(drivePathLiteral); + //check for break-out attempts + for (int i = 0; i < realLength - 1; i++) { + if (filePath[i] == '.' && filePath[i + 1] == '.') { + interpreter->errorOutput("Parent directory access not allowed\n"); + FREE_ARRAY(char, filePath, realLength); + return -1; + } + } + //load and compile the bytecode size_t fileSize = 0; char* source = readFile(filePath, &fileSize); @@ -95,8 +104,13 @@ static int nativeLoadScript(Interpreter* interpreter, LiteralArray* arguments) { //build the runner object Runner* runner = ALLOCATE(Runner, 1); initInterpreter(&runner->interpreter); + setInterpreterPrint(&runner->interpreter, interpreter->printOutput); + setInterpreterAssert(&runner->interpreter, interpreter->assertOutput); + setInterpreterError(&runner->interpreter, interpreter->errorOutput); + runner->interpreter.hooks = interpreter->hooks; runner->bytecode = bytecode; runner->size = fileSize; + runner->dirty = false; //build the opaque object, and push it to the stack Literal runnerLiteral = TO_OPAQUE_LITERAL(runner, OPAQUE_TAG_RUNNER); @@ -109,50 +123,232 @@ static int nativeLoadScript(Interpreter* interpreter, LiteralArray* arguments) { static int nativeRunScript(Interpreter* interpreter, LiteralArray* arguments) { //no arguments - if (arguments->count != 0) { + if (arguments->count != 1) { interpreter->errorOutput("Incorrect number of arguments to _runScript\n"); return -1; } - // + //get the runner object + Literal runnerLiteral = popLiteralArray(arguments); + Literal idn = runnerLiteral; - return -1; + if (parseIdentifierToValue(interpreter, &runnerLiteral)) { + freeLiteral(idn); + } + + if (OPAQUE_TAG(runnerLiteral) != OPAQUE_TAG_RUNNER) { + interpreter->errorOutput("Unrecognized opaque literal in _runScript\n"); + return -1; + } + + Runner* runner = AS_OPAQUE(runnerLiteral); + + //run + if (runner->dirty) { + interpreter->errorOutput("Can't re-run a dirty script (try resetting it first)\n"); + freeLiteral(runnerLiteral); + return -1; + } + + unsigned char* bytecodeCopy = ALLOCATE(unsigned char, runner->size); + memcpy(bytecodeCopy, runner->bytecode, runner->size); //need a COPY of the bytecode, because the interpreter eats it + + runInterpreter(&runner->interpreter, bytecodeCopy, runner->size); + runner->dirty = true; + + //cleanup + freeLiteral(runnerLiteral); + + return 0; } static int nativeGetScriptVar(Interpreter* interpreter, LiteralArray* arguments) { //no arguments - if (arguments->count != 0) { + if (arguments->count != 2) { interpreter->errorOutput("Incorrect number of arguments to _getScriptVar\n"); return -1; } - // + //get the runner object + Literal varName = popLiteralArray(arguments); + Literal runnerLiteral = popLiteralArray(arguments); - return -1; + if (IS_IDENTIFIER(varName)) { + Literal idn = varName; + parseIdentifierToValue(interpreter, &varName); + freeLiteral(idn); + } + + if (IS_IDENTIFIER(runnerLiteral)) { + Literal idn = runnerLiteral; + parseIdentifierToValue(interpreter, &runnerLiteral); + freeLiteral(idn); + } + + if (OPAQUE_TAG(runnerLiteral) != OPAQUE_TAG_RUNNER) { + interpreter->errorOutput("Unrecognized opaque literal in _runScript\n"); + return -1; + } + + Runner* runner = AS_OPAQUE(runnerLiteral); + + //dirty check + if (!runner->dirty) { + interpreter->errorOutput("Can't access variable from a non-dirty script (try running it first)\n"); + freeLiteral(runnerLiteral); + return -1; + } + + //get the desired variable + Literal varIdn = TO_IDENTIFIER_LITERAL(copyRefString(AS_STRING(varName))); + Literal result = TO_NULL_LITERAL; + getScopeVariable(runner->interpreter.scope, varIdn, &result); + + pushLiteralArray(&interpreter->stack, result); + + //cleanup + freeLiteral(result); + freeLiteral(varIdn); + freeLiteral(varName); + freeLiteral(runnerLiteral); + + return 1; } static int nativeCallScriptFn(Interpreter* interpreter, LiteralArray* arguments) { //no arguments - if (arguments->count != 0) { + if (arguments->count < 2) { interpreter->errorOutput("Incorrect number of arguments to _callScriptFn\n"); return -1; } - // + //get the rest args + LiteralArray tmp; + initLiteralArray(&tmp); - return -1; + while (arguments->count > 2) { + Literal lit = popLiteralArray(arguments); + pushLiteralArray(&tmp, lit); + freeLiteral(lit); + } + + LiteralArray rest; + initLiteralArray(&rest); + + while (tmp.count) { //correct the order of the rest args + Literal lit = popLiteralArray(&tmp); + pushLiteralArray(&rest, lit); + freeLiteral(lit); + } + + freeLiteralArray(&tmp); + + + //get the runner object + Literal varName = popLiteralArray(arguments); + Literal runnerLiteral = popLiteralArray(arguments); + + if (IS_IDENTIFIER(varName)) { + Literal idn = varName; + parseIdentifierToValue(interpreter, &varName); + freeLiteral(idn); + } + + if (IS_IDENTIFIER(runnerLiteral)) { + Literal idn = runnerLiteral; + parseIdentifierToValue(interpreter, &runnerLiteral); + freeLiteral(idn); + } + + if (OPAQUE_TAG(runnerLiteral) != OPAQUE_TAG_RUNNER) { + interpreter->errorOutput("Unrecognized opaque literal in _runScript\n"); + return -1; + } + + Runner* runner = AS_OPAQUE(runnerLiteral); + + //dirty check + if (!runner->dirty) { + interpreter->errorOutput("Can't access fn from a non-dirty script (try running it first)\n"); + freeLiteral(runnerLiteral); + freeLiteralArray(&rest); + return -1; + } + + //get the desired variable + Literal varIdn = TO_IDENTIFIER_LITERAL(copyRefString(AS_STRING(varName))); + Literal fn = TO_NULL_LITERAL; + getScopeVariable(runner->interpreter.scope, varIdn, &fn); + + if (!IS_FUNCTION(fn)) { + interpreter->errorOutput("Can't run a non-function literal\n"); + freeLiteral(fn); + freeLiteral(varIdn); + freeLiteral(varName); + freeLiteral(runnerLiteral); + freeLiteralArray(&rest); + } + + //call + LiteralArray resultArray; + initLiteralArray(&resultArray); + + callLiteralFn(interpreter, fn, &rest, &resultArray); + + Literal result = TO_NULL_LITERAL; + if (resultArray.count > 0) { + result = popLiteralArray(&resultArray); + } + + pushLiteralArray(&interpreter->stack, result); + + //cleanup + freeLiteralArray(&resultArray); + freeLiteral(result); + freeLiteral(fn); + freeLiteral(varIdn); + freeLiteral(varName); + freeLiteral(runnerLiteral); + freeLiteralArray(&rest); + + return 1; } static int nativeResetScript(Interpreter* interpreter, LiteralArray* arguments) { //no arguments - if (arguments->count != 0) { + if (arguments->count != 1) { interpreter->errorOutput("Incorrect number of arguments to _resetScript\n"); return -1; } - // + //get the runner object + Literal runnerLiteral = popLiteralArray(arguments); - return -1; + if (IS_IDENTIFIER(runnerLiteral)) { + Literal idn = runnerLiteral; + parseIdentifierToValue(interpreter, &runnerLiteral); + freeLiteral(idn); + } + + if (OPAQUE_TAG(runnerLiteral) != OPAQUE_TAG_RUNNER) { + interpreter->errorOutput("Unrecognized opaque literal in _runScript\n"); + return -1; + } + + Runner* runner = AS_OPAQUE(runnerLiteral); + + //reset + if (!runner->dirty) { + interpreter->errorOutput("Can't reset a non-dirty script (try running it first)\n"); + freeLiteral(runnerLiteral); + return -1; + } + + resetInterpreter(&runner->interpreter); + runner->dirty = false; + freeLiteral(runnerLiteral); + + return 0; } static int nativeFreeScript(Interpreter* interpreter, LiteralArray* arguments) { @@ -179,6 +375,7 @@ static int nativeFreeScript(Interpreter* interpreter, LiteralArray* arguments) { Runner* runner = AS_OPAQUE(runnerLiteral); //clear out the runner object + runner->interpreter.hooks = NULL; freeInterpreter(&runner->interpreter); FREE_ARRAY(unsigned char, runner->bytecode, runner->size); @@ -189,6 +386,41 @@ static int nativeFreeScript(Interpreter* interpreter, LiteralArray* arguments) { return 0; } +static int nativeCheckScriptDirty(Interpreter* interpreter, LiteralArray* arguments) { + //no arguments + if (arguments->count != 1) { + interpreter->errorOutput("Incorrect number of arguments to _runScript\n"); + return -1; + } + + //get the runner object + Literal runnerLiteral = popLiteralArray(arguments); + + if (IS_IDENTIFIER(runnerLiteral)) { + Literal idn = runnerLiteral; + parseIdentifierToValue(interpreter, &runnerLiteral); + freeLiteral(idn); + } + + if (OPAQUE_TAG(runnerLiteral) != OPAQUE_TAG_RUNNER) { + interpreter->errorOutput("Unrecognized opaque literal in _runScript\n"); + return -1; + } + + Runner* runner = AS_OPAQUE(runnerLiteral); + + //run + Literal result = TO_BOOLEAN_LITERAL(runner->dirty); + + pushLiteralArray(&interpreter->stack, result); + + //cleanup + freeLiteral(result); + freeLiteral(runnerLiteral); + + return 0; +} + //call the hook typedef struct Natives { char* name; @@ -199,11 +431,12 @@ int hookRunner(Interpreter* interpreter, Literal identifier, Literal alias) { //build the natives list Natives natives[] = { {"loadScript", nativeLoadScript}, - {"x_runScript", nativeRunScript}, - {"x_getScriptVar", nativeGetScriptVar}, - {"x_callScriptFn", nativeCallScriptFn}, - {"x_resetScript", nativeResetScript}, + {"_runScript", nativeRunScript}, + {"_getScriptVar", nativeGetScriptVar}, + {"_callScriptFn", nativeCallScriptFn}, + {"_resetScript", nativeResetScript}, {"_freeScript", nativeFreeScript}, + {"_checkScriptDirty", nativeCheckScriptDirty}, {NULL, NULL} }; diff --git a/source/interpreter.c b/source/interpreter.c index 26e701c..bb2bcfc 100644 --- a/source/interpreter.c +++ b/source/interpreter.c @@ -2438,7 +2438,10 @@ void freeInterpreter(Interpreter* interpreter) { interpreter->scope = popScope(interpreter->scope); } - freeLiteralDictionary(interpreter->hooks); - FREE(LiteralDictionary, interpreter->hooks); + if (interpreter->hooks) { + freeLiteralDictionary(interpreter->hooks); + FREE(LiteralDictionary, interpreter->hooks); + } + interpreter->hooks = NULL; } diff --git a/test/scripts/lib/runner.toy b/test/scripts/lib/runner.toy index 630acf2..9da845e 100644 --- a/test/scripts/lib/runner.toy +++ b/test/scripts/lib/runner.toy @@ -7,10 +7,66 @@ import runner; s.freeScript(); } -//TODO: test running an external script -//TODO: test resetting an external script -//TODO: test retrieving a script variable -//TODO: test calling a script function +//test running an external script +{ + var s = loadScript("scripts:/runner_sample_code.toy"); + + s.runScript(); + + s.freeScript(); +} + +//test resetting an external script +{ + var s = loadScript("scripts:/runner_sample_code.toy"); + + s.runScript(); + s.resetScript(); + + assert !s.checkScriptDirty(), "checkScriptDirty failed"; + + s.runScript(); + + assert s.checkScriptDirty(), "_checkScriptDirty() failed"; + + s.resetScript(); + s.runScript(); + + s.freeScript(); +} + +//test running a nested external script +{ + var s = loadScript("scripts:/lib/runner/sample_1.toy"); + + s.runScript(); + + s.freeScript(); +} + +//test retrieving a script variable +{ + var s = loadScript("scripts:/runner_sample_code.toy"); + + s.runScript(); + + var fib = s.getScriptVar("fib"); + + assert fib(12) == 144, "_getScriptVar() failed"; + + s.freeScript(); +} + +//test calling a script function +{ + var s = loadScript("scripts:/runner_sample_code.toy"); + + s.runScript(); + + assert s.callScriptFn("fib", 12) == 144, "_callScriptFn() failed"; + + s.freeScript(); +} print "All good"; diff --git a/test/scripts/lib/runner/sample_1.toy b/test/scripts/lib/runner/sample_1.toy new file mode 100644 index 0000000..e0f7946 --- /dev/null +++ b/test/scripts/lib/runner/sample_1.toy @@ -0,0 +1,9 @@ +import runner; + +//delegate to the other sample script +var s = loadScript("scripts:/lib/runner/sample_2.toy"); + +s.runScript(); + +s.freeScript(); + diff --git a/test/scripts/lib/runner/sample_2.toy b/test/scripts/lib/runner/sample_2.toy new file mode 100644 index 0000000..51141d7 --- /dev/null +++ b/test/scripts/lib/runner/sample_2.toy @@ -0,0 +1 @@ +assert true, "Nested sample scripts worked"; diff --git a/test/scripts/mustfail/access-parent-directory.toy b/test/scripts/mustfail/access-parent-directory.toy new file mode 100644 index 0000000..661faee --- /dev/null +++ b/test/scripts/mustfail/access-parent-directory.toy @@ -0,0 +1,8 @@ +import runner; + +var s = loadScript("scripts:/lib/../runner_sample_code.toy"); + +s.runScript(); + +s.freeScript(); + diff --git a/test/test_mustfail.c b/test/test_mustfail.c index bf2850e..a1ab29f 100644 --- a/test/test_mustfail.c +++ b/test/test_mustfail.c @@ -95,6 +95,7 @@ int main() { { //run each file in tests/scripts/ char* filenames[] = { + "access-parent-directory.toy", "declare-types-array.toy", "declare-types-dictionary-key.toy", "declare-types-dictionary-value.toy",