diff --git a/tests/compiler/closure-common-js-patterns.json b/tests/compiler/closure-common-js-patterns.json new file mode 100644 index 0000000000..e756e83135 --- /dev/null +++ b/tests/compiler/closure-common-js-patterns.json @@ -0,0 +1,10 @@ +{ + "asc_flags": [ + "--runtime full", + "--use ASC_RTRACE=1" + ], + "stderr": [ + "AS100: Not implemented: Closures", + "AS100: Not implemented: Closures" + ] +} diff --git a/tests/compiler/closure-common-js-patterns.ts b/tests/compiler/closure-common-js-patterns.ts new file mode 100644 index 0000000000..be3c830941 --- /dev/null +++ b/tests/compiler/closure-common-js-patterns.ts @@ -0,0 +1,183 @@ +// NOTE torch2424 6/15/20: This test has a lot of errors skipped. Closures is currently a WIP + +// Common use cases / concepts for closures, as covered in articles like: +// https://medium.com/@dis_is_patrick/practical-uses-for-closures-c65640ae7304 +// https://stackoverflow.com/questions/2728278/what-is-a-practical-use-for-a-closure-in-javascript +// https://softwareengineering.stackexchange.com/questions/285941/why-would-a-program-use-a-closure +// https://medium.com/@prashantramnyc/what-is-an-iife-in-javascript-24baf0febf08 + +// Currently, IIFEs and simple Function Factories work +// But my advanced Function Factory Pub Sub, and weird function namespacing does not +// Due to runtime and/or compilation errors :) + +// IIFE (Immediately Invoked function expressions) +// Used for encapsulating data usually + +// Simple IIFE +let myData = ((): boolean => { + return true; +})(); + +assert(myData == true); + +// Constructor IIFE? +// Don't know why someone wouldn't just use their class, but yeah + +class IIFEReturn { + myBool: boolean + myFunc: (x: i32) => i32 +} + +let myInstanceThing = ((): IIFEReturn => { + return { + myBool: true, + myFunc: (x: i32) => { + return x + 1; + } + }; +})(); + +assert(myInstanceThing.myBool == true); +assert(myInstanceThing.myFunc(24) == 25); + +// Function Factories +// Closures that create specific functions + +// Simple function that will change depending on input +type generatedFunc = () => i32; +let myFactory = (x: i32): generatedFunc => { + let myFunc = (): i32 => { + return 24 + x; + }; + + return myFunc; +}; + +let generatedPlusOne: generatedFunc = myFactory(1); +let generatedPlusTwo: generatedFunc = myFactory(2); + +// For some reason, couldn't do +// Cannot invoke an expression whose type lacks a call signature. Type 'closure-common-js-patterns/myFactory' has no compatible call signatures. +// assert(myFactory(1)() == 25); +assert(generatedPlusOne() == 25); +assert(generatedPlusTwo() == 26); + +// I often will use this for like Pub/Sub stuff + +type SubFunc = () => void; +type UnSubFunc = () => void; +let subscriptions = new Array(); +let globalSubVar: i32 = 0; + +function subscribe(funcToCallOnPub: SubFunc): UnSubFunc { + subscriptions.push(funcToCallOnPub); + return (): void => { + let funcIndex = subscriptions.indexOf(funcToCallOnPub); + subscriptions.splice(funcIndex, 1); + }; +} + +function publish(): void { + for(let i = 0; i < subscriptions.length; i++) { + // Can't call directly? Get a Type error + // ERROR TS2757: Type '() => void' has no call signatures. + // Noticed some other weird type errors if I don't declare the function type + // But I also am meh at typescripte signatures haha! + // subscriptions[i](); + + let subFunc = subscriptions[i]; + subFunc(); + } +} + +let plusOne = (): void => { + globalSubVar += 1; +}; + +let plusTwo = (): void => { + globalSubVar += 1; +}; + + +let unsubPlusOne: () => void = subscribe(plusOne); +let unsubPlusTwo: () => void = subscribe(plusTwo); + +assert(globalSubVar == 0); +assert(subscriptions.length == 2); + +publish(); + +assert(globalSubVar == 3); +assert(subscriptions.length == 2); + +unsubPlusOne(); + +assert(globalSubVar == 3); +assert(subscriptions.length == 1); + +publish(); + +assert(globalSubVar == 5); +assert(subscriptions.length == 1); + +unsubPlusTwo(); + +assert(globalSubVar == 5); +assert(subscriptions.length == 0); + +publish(); + +assert(globalSubVar == 5); +assert(subscriptions.length == 0); + + +// TODO (torch2424 6/15/20): Uncomment this test once closures is fully implemented +/* + +// Namespacing private functions +// Again, kind of weird, they should probably just make a class, but it's another interesting test + +class Chunk { + totalSize: i32; + usedSize: i32; + write: (size: i32) => void; +} + +let getChunk = (): Chunk => { + let chunk: Chunk = { + totalSize: 1024, + usedSize: 0, + write: (x: i32): void => {} + }; + + let growChunk = (): void => { + chunk.totalSize += 1024; + } + + let allocateForChunk = (amount: i32): void => { + if (chunk.usedSize + amount <= chunk.totalSize) { + chunk.usedSize += amount; + } else { + // growChunk(chunk); + // allocateForChunk(chunk, amount); + } + } + + chunk.write = (x: i32) => { + allocateForChunk(x); + } + + return chunk; + +} + +let myChunk: Chunk = getChunk(); + +assert(myChunk.totalSize == 1024); +assert(myChunk.usedSize == 0); + +myChunk.write(1025); + +assert(myChunk.totalSize == 2048); +assert(myChunk.usedSize == 1025); +*/ diff --git a/tests/compiler/closure-common-js-patterns.untouched.wat b/tests/compiler/closure-common-js-patterns.untouched.wat new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/compiler/closure-limitations-runtime.json b/tests/compiler/closure-limitations-runtime.json new file mode 100644 index 0000000000..f4b71f4f4b --- /dev/null +++ b/tests/compiler/closure-limitations-runtime.json @@ -0,0 +1,9 @@ +{ + "asc_flags": [ + "--runtime full" + ], + "stderr": [ + "AS100: Not implemented: Closures" + ], + "skipInstantiate": true +} diff --git a/tests/compiler/closure-limitations-runtime.ts b/tests/compiler/closure-limitations-runtime.ts new file mode 100644 index 0000000000..be1560fff7 --- /dev/null +++ b/tests/compiler/closure-limitations-runtime.ts @@ -0,0 +1,7 @@ +export function exportedClosureReturns(): (value: i32) => i32 { + var $local0 = 0; + return function inner(value: i32): i32 { + return $local0; + }; +} +exportedClosureReturns(); diff --git a/tests/compiler/closure-limitations-runtime.untouched.wat b/tests/compiler/closure-limitations-runtime.untouched.wat new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/compiler/closure-limitations.json b/tests/compiler/closure-limitations.json new file mode 100644 index 0000000000..86235e0716 --- /dev/null +++ b/tests/compiler/closure-limitations.json @@ -0,0 +1,11 @@ +{ + "asc_flags": [ + "--runtime none" + ], + "stderr": [ + "AS100: Not implemented: Closures", + "AS100: Not implemented: Closures", + "AS100: Not implemented: Closures", + "EOF" + ] +} diff --git a/tests/compiler/closure-limitations.ts b/tests/compiler/closure-limitations.ts new file mode 100644 index 0000000000..07cfc46f2b --- /dev/null +++ b/tests/compiler/closure-limitations.ts @@ -0,0 +1,22 @@ +function closureWrites(): (value: i32) => i32 { + var $local0 = 0; + return function inner(value: i32) { + $local0 = $local0 + 1; + return $local0; + }; +} +closureWrites(); + +function inScopeNestedCalls(): (value: i32) => i32 { + var x = 0; + var f = (): i32 => { + return x; + }; + var p = (value: i32): i32 => { + return f(); + }; + return p; +} +inScopeNestedCalls(); + +ERROR("EOF"); diff --git a/tests/compiler/closure.json b/tests/compiler/closure.json index 1a61979c47..796dfc8d8d 100644 --- a/tests/compiler/closure.json +++ b/tests/compiler/closure.json @@ -1,11 +1,15 @@ { "asc_flags": [ - "--runtime none" + "--runtime full", + "--use ASC_RTRACE=1" ], "stderr": [ "AS100: Not implemented: Closures", "AS100: Not implemented: Closures", - "Cannot find name '$local0'.", - "EOF" + "TS2304: Cannot find name '$local0'.", + "AS100: Not implemented: Closures", + "AS100: Not implemented: Closures", + "AS100: Not implemented: Closures", + "AS100: Not implemented: Closures" ] -} \ No newline at end of file +} diff --git a/tests/compiler/closure.ts b/tests/compiler/closure.ts index cd1bca4d89..f9ee87bc01 100644 --- a/tests/compiler/closure.ts +++ b/tests/compiler/closure.ts @@ -1,3 +1,5 @@ +// NOTE torch2424 6/15/20: This test has a lot of errors skipped. Closures is currently a WIP + function testParam($local0: i32, $local1: i32): (value: i32) => i32 { return function inner(value: i32) { return $local1; // closure @@ -16,9 +18,68 @@ testVar(); function testLet(): (value: i32) => i32 { let $local0 = 0; return function inner(value: i32) { - return $local0; // closure + return $local0; }; } testLet(); -ERROR("EOF"); +function passItAround(arg: i32): usize { + return runClosure(createClosure(arg)); +} +passItAround(1); + +function runInline(arg: i32, foo: i32, bar: i32): i32 { + return ((): i32 => { return arg + foo + bar; } )(); +} +runInline(1,1,1); + +function fallOutOfScope(arg: i32): i32 { + var releaseMe = createClosure(arg); + return 10; +} +fallOutOfScope(1); + +function createClosure(arg: i32): (x3: i32) => i32 { + var closure = (x3: i32): i32 => { return arg + x3; }; + return closure; +} + +function complexCreateClosure(arg: i32): (x3: i32) => i32 { + var foo = 2; + var bar = 3; + var baz = 4; + var f = (x1: i32): i32 => { return foo + bar - baz; }; + var g = (x2: i32): i32 => { return (bar - baz) + foo; }; + foo = 7; + bar = 11; + return g; +} + +function runClosure(closureToRun: (x3: i32) => i32): i32 { + return closureToRun(1); +} + +// Ensure that non-closures do not abort upon returning +export function returnOverBoundary(): () => i32 { + return function(): i32 { return 6; }; +} +returnOverBoundary(); + +// KNOWN BUGS (torch2424: 6/15/20 - As of the original Closures PR) + +// Causes a memory leak, copyFunction is properly released +const func = (i: i32): i32 => i; +let copyFunction: (i: i32) => i32 = func; + +// Also causes a memory leak +function nestedExecutionTest(arg: i32): i32 { + var x = 7; + var f = complexCreateClosure(arg); + var g = (fn: (x3: i32) => i32): i32 => { + var first = fn(arg); + return x; + }; + return g(f); +} +nestedExecutionTest(1); + diff --git a/tests/compiler/closure.untouched.wat b/tests/compiler/closure.untouched.wat new file mode 100644 index 0000000000..e69de29bb2