From a2b0289afd613a8f1c44f20e1d69f4787d502aea Mon Sep 17 00:00:00 2001 From: pitust Date: Fri, 27 Nov 2020 15:09:31 +0000 Subject: [PATCH 1/6] Fix #74 Signed-off-by: pitust --- src/nodes/statements.ts | 18 ++++++- src/nodes/variable.ts | 76 +++++++++++++++--------------- src/template.ts | 23 ++++++--- tests/funcs/regression1.res.expect | 1 + tests/funcs/regression1.ts | 7 +++ 5 files changed, 77 insertions(+), 48 deletions(-) create mode 100644 tests/funcs/regression1.res.expect create mode 100644 tests/funcs/regression1.ts diff --git a/src/nodes/statements.ts b/src/nodes/statements.ts index 87af18a..3fd96c9 100644 --- a/src/nodes/statements.ts +++ b/src/nodes/statements.ts @@ -71,16 +71,30 @@ export class CEmptyStatement { } @CodeTemplate(` -{destructors} -return {expression}; +{ + {returnTypeAndVar} = {expression}; + {destructors} + return {returnTemp}; +} `, ts.SyntaxKind.ReturnStatement) export class CReturnStatement extends CTemplateBase { public expression: CExpression; public destructors: CVariableDestructors; public retVarName: string = null; + public returnTypeAndVar: string = null; + public returnTemp: string = null; public closureParams: { name: string, value: CExpression }[] = []; constructor(scope: IScope, node: ts.ReturnStatement) { super(); + this.returnTemp = scope.root.symbolsHelper.addTemp(node, 'returnVal'); + let returnType = scope.root.typeHelper.getCType(node.expression); + let fakeVar = new CVariable(scope, '__fake', returnType, { removeStorageSpecifier: true, arraysToPointers: true });; + let fakeVarType = fakeVar.resolve().slice(0, -6).trim(); + this.returnTypeAndVar = fakeVarType; + if (this.returnTypeAndVar.indexOf('{var}') == -1) { + this.returnTypeAndVar += ' {var}'; + } + this.returnTypeAndVar = this.returnTypeAndVar.replace('{var}', this.returnTemp); this.expression = CodeTemplateFactory.createForNode(scope, node.expression); this.destructors = new CVariableDestructors(scope, node); } diff --git a/src/nodes/variable.ts b/src/nodes/variable.ts index 72c8435..f456604 100644 --- a/src/nodes/variable.ts +++ b/src/nodes/variable.ts @@ -113,45 +113,43 @@ export class CVariableAllocation extends CTemplateBase { } @CodeTemplate(` -{#statements} - {arrayDestructors => for (gc_i = 0; gc_i < ({this} ? {this}->size : 0); gc_i++) free((void*){this}->data[gc_i]);\n} - {destructors => free({this});\n} - {#if gcArraysCVarName} - for (gc_i = 0; gc_i < {gcArraysCVarName}->size; gc_i++) { - for (gc_j = 0; gc_j < ({gcArraysCVarName}->data[gc_i] ? {gcArraysCVarName}->data[gc_i]->size : 0); gc_j++) - free((void*){gcArraysCVarName}->data[gc_i]->data[gc_j]);\n - free({gcArraysCVarName}->data[gc_i] ? {gcArraysCVarName}->data[gc_i]->data : NULL); - free({gcArraysCVarName}->data[gc_i]); - } - free({gcArraysCVarName}->data); - free({gcArraysCVarName}); - {/if} - {#if gcArraysVarName} - for (gc_i = 0; gc_i < {gcArraysVarName}->size; gc_i++) { - free({gcArraysVarName}->data[gc_i]->data); - free({gcArraysVarName}->data[gc_i]); - } - free({gcArraysVarName}->data); - free({gcArraysVarName}); - {/if} - {#if gcDictsVarName} - for (gc_i = 0; gc_i < {gcDictsVarName}->size; gc_i++) { - free({gcDictsVarName}->data[gc_i]->index->data); - free({gcDictsVarName}->data[gc_i]->index); - free({gcDictsVarName}->data[gc_i]->values->data); - free({gcDictsVarName}->data[gc_i]->values); - free({gcDictsVarName}->data[gc_i]); - } - free({gcDictsVarName}->data); - free({gcDictsVarName}); - {/if} - {#if gcVarName} - for (gc_i = 0; gc_i < {gcVarName}->size; gc_i++) - free({gcVarName}->data[gc_i]); - free({gcVarName}->data); - free({gcVarName}); - {/if} -{/statements}` +{arrayDestructors => for (gc_i = 0; gc_i < ({this} ? {this}->size : 0); gc_i++) free((void*){this}->data[gc_i]);\n} +{destructors => free({this});\n} +{#if gcArraysCVarName} + for (gc_i = 0; gc_i < {gcArraysCVarName}->size; gc_i++) { + for (gc_j = 0; gc_j < ({gcArraysCVarName}->data[gc_i] ? {gcArraysCVarName}->data[gc_i]->size : 0); gc_j++) + free((void*){gcArraysCVarName}->data[gc_i]->data[gc_j]);\n + free({gcArraysCVarName}->data[gc_i] ? {gcArraysCVarName}->data[gc_i]->data : NULL); + free({gcArraysCVarName}->data[gc_i]); + } + free({gcArraysCVarName}->data); + free({gcArraysCVarName}); +{/if} +{#if gcArraysVarName} + for (gc_i = 0; gc_i < {gcArraysVarName}->size; gc_i++) { + free({gcArraysVarName}->data[gc_i]->data); + free({gcArraysVarName}->data[gc_i]); + } + free({gcArraysVarName}->data); + free({gcArraysVarName}); +{/if} +{#if gcDictsVarName} + for (gc_i = 0; gc_i < {gcDictsVarName}->size; gc_i++) { + free({gcDictsVarName}->data[gc_i]->index->data); + free({gcDictsVarName}->data[gc_i]->index); + free({gcDictsVarName}->data[gc_i]->values->data); + free({gcDictsVarName}->data[gc_i]->values); + free({gcDictsVarName}->data[gc_i]); + } + free({gcDictsVarName}->data); + free({gcDictsVarName}); +{/if} +{#if gcVarName} + for (gc_i = 0; gc_i < {gcVarName}->size; gc_i++) + free({gcVarName}->data[gc_i]); + free({gcVarName}->data); + free({gcVarName}); +{/if}` ) export class CVariableDestructors extends CTemplateBase { public gcVarName: string = null; diff --git a/src/template.ts b/src/template.ts index a646822..d5e4c3f 100644 --- a/src/template.ts +++ b/src/template.ts @@ -1,4 +1,4 @@ -import {IScope} from './program'; +import { IScope } from './program'; interface INode { kind: number, getText(): string }; @@ -16,7 +16,7 @@ export class CodeTemplateFactory { : "/* Unsupported node: " + node.getText().replace(/[\n\s]+/g, ' ') + " */;\n"; } public static templateToString(template: string | CTemplateBase) { - return typeof(template) === "string" ? template : template.resolve(); + return typeof (template) === "string" ? template : template.resolve(); } } @@ -26,7 +26,7 @@ export function CodeTemplate(tempString: string, nodeKind?: number | number[]): let self = this; let retValue = target.apply(self, arguments); let [code, statements] = processTemplate(tempString, self); - if (statements) + if (statements && scope.statements) scope.statements.push(statements); self.resolve = function () { return code; @@ -51,6 +51,15 @@ export function CodeTemplate(tempString: string, nodeKind?: number | number[]): function processTemplate(template: string, args: string | CTemplateBase): [string, string] { let statements = ""; + //@ts-ignore + if (typeof global !== 'undefined' && ((global as any).process && (global as any).process.env.DEBUG)) { + statements = '/* ------------------------- Start [stmt] backtrace ------------------------- */\n' + + (new Error()).stack.split('\n').slice(1).map(e => '/* ' + e + ' */').join('\n') + + '\n/* ------------------------- End [stmt] backtrace ------------------------- */\n'; + template = '/* ------------------------- Start [exp] backtrace ------------------------- */\n' + + (new Error()).stack.split('\n').slice(1).map(e => '/* ' + e + ' */').join('\n') + + '\n/* ------------------------- End [exp] backtrace ------------------------- */\n' + template; + } if (template.indexOf("{#statements}") > -1) { let statementsStartPos = template.indexOf("{#statements}"); let statementsBodyStartPos = statementsStartPos + "{#statements}".length; @@ -123,15 +132,15 @@ function processTemplate(template: string, args: string | CTemplateBase): [strin let index = -1; while ((index = template.indexOf("{" + k + "}")) > -1) { let spaces = ''; - while (template.length > index && template[index-1] == ' ') { + while (template.length > index && template[index - 1] == ' ') { index--; - spaces+=' '; + spaces += ' '; } let value = args[k]; if (value && value.resolve) value = value.resolve(); if (value && typeof value === 'string') - value = value.replace(/\n/g, '\n'+spaces); + value = value.replace(/\n/g, '\n' + spaces); template = template.replace("{" + k + "}", () => value); replaced = true; } @@ -226,7 +235,7 @@ function replaceArray(data, k, array, statements) { if (elementsResolved != "") elementsResolved += separator; elementsResolved += resolvedElement; - } + } } diff --git a/tests/funcs/regression1.res.expect b/tests/funcs/regression1.res.expect new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/tests/funcs/regression1.res.expect @@ -0,0 +1 @@ +1 diff --git a/tests/funcs/regression1.ts b/tests/funcs/regression1.ts new file mode 100644 index 0000000..e6048ae --- /dev/null +++ b/tests/funcs/regression1.ts @@ -0,0 +1,7 @@ +// regression test for #74 +function return_from_obj() { + let obj = { key: 1 }; + return obj.key; +} + +console.log(return_from_obj()); From 7a140b3ae483a97167bcd59f505adda557d2df65 Mon Sep 17 00:00:00 2001 From: pitust Date: Fri, 27 Nov 2020 15:11:16 +0000 Subject: [PATCH 2/6] Move the tests to the correct directory Signed-off-by: pitust --- tests/funcs/regression1.res.expect | 1 - tests/funcs/regression1.ts | 7 ------- 2 files changed, 8 deletions(-) delete mode 100644 tests/funcs/regression1.res.expect delete mode 100644 tests/funcs/regression1.ts diff --git a/tests/funcs/regression1.res.expect b/tests/funcs/regression1.res.expect deleted file mode 100644 index d00491f..0000000 --- a/tests/funcs/regression1.res.expect +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/funcs/regression1.ts b/tests/funcs/regression1.ts deleted file mode 100644 index e6048ae..0000000 --- a/tests/funcs/regression1.ts +++ /dev/null @@ -1,7 +0,0 @@ -// regression test for #74 -function return_from_obj() { - let obj = { key: 1 }; - return obj.key; -} - -console.log(return_from_obj()); From 2378b952c969b313140837baa404f1d3c873165f Mon Sep 17 00:00:00 2001 From: pitust Date: Fri, 27 Nov 2020 15:11:39 +0000 Subject: [PATCH 3/6] Move the tests to the correct directory Signed-off-by: pitust --- tests/regression/regression1.res.expect | 1 + tests/regression/regression1.ts | 7 +++++++ 2 files changed, 8 insertions(+) create mode 100644 tests/regression/regression1.res.expect create mode 100644 tests/regression/regression1.ts diff --git a/tests/regression/regression1.res.expect b/tests/regression/regression1.res.expect new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/tests/regression/regression1.res.expect @@ -0,0 +1 @@ +1 diff --git a/tests/regression/regression1.ts b/tests/regression/regression1.ts new file mode 100644 index 0000000..e6048ae --- /dev/null +++ b/tests/regression/regression1.ts @@ -0,0 +1,7 @@ +// regression test for #74 +function return_from_obj() { + let obj = { key: 1 }; + return obj.key; +} + +console.log(return_from_obj()); From e40d46c398900325ebbe9b9df520c58a2da7a22d Mon Sep 17 00:00:00 2001 From: pitust Date: Sun, 13 Dec 2020 11:54:43 +0000 Subject: [PATCH 4/6] removed debug feature --- src/template.ts | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/src/template.ts b/src/template.ts index d5e4c3f..974974f 100644 --- a/src/template.ts +++ b/src/template.ts @@ -26,7 +26,7 @@ export function CodeTemplate(tempString: string, nodeKind?: number | number[]): let self = this; let retValue = target.apply(self, arguments); let [code, statements] = processTemplate(tempString, self); - if (statements && scope.statements) + if (statements) scope.statements.push(statements); self.resolve = function () { return code; @@ -51,15 +51,6 @@ export function CodeTemplate(tempString: string, nodeKind?: number | number[]): function processTemplate(template: string, args: string | CTemplateBase): [string, string] { let statements = ""; - //@ts-ignore - if (typeof global !== 'undefined' && ((global as any).process && (global as any).process.env.DEBUG)) { - statements = '/* ------------------------- Start [stmt] backtrace ------------------------- */\n' - + (new Error()).stack.split('\n').slice(1).map(e => '/* ' + e + ' */').join('\n') - + '\n/* ------------------------- End [stmt] backtrace ------------------------- */\n'; - template = '/* ------------------------- Start [exp] backtrace ------------------------- */\n' - + (new Error()).stack.split('\n').slice(1).map(e => '/* ' + e + ' */').join('\n') - + '\n/* ------------------------- End [exp] backtrace ------------------------- */\n' + template; - } if (template.indexOf("{#statements}") > -1) { let statementsStartPos = template.indexOf("{#statements}"); let statementsBodyStartPos = statementsStartPos + "{#statements}".length; From da64643440570979a30dbbe1841780eee98aa8b3 Mon Sep 17 00:00:00 2001 From: pitust Date: Sun, 13 Dec 2020 12:05:04 +0000 Subject: [PATCH 5/6] remove unnecessary code generation Signed-off-by: pitust --- src/nodes/statements.ts | 46 +++++++++++++++++++++++++++++++---------- 1 file changed, 35 insertions(+), 11 deletions(-) diff --git a/src/nodes/statements.ts b/src/nodes/statements.ts index 3fd96c9..c292f97 100644 --- a/src/nodes/statements.ts +++ b/src/nodes/statements.ts @@ -1,6 +1,6 @@ import * as ts from 'typescript'; import { CodeTemplate, CodeTemplateFactory, CTemplateBase } from '../template'; -import { CProgram, IScope} from '../program'; +import { CProgram, IScope } from '../program'; import { ArrayType, NumberVarType, StringVarType } from '../types/ctypes'; import { CVariable, CVariableDeclaration, CVariableDestructors } from './variable'; import { CExpression, CCondition } from './expressions'; @@ -71,11 +71,14 @@ export class CEmptyStatement { } @CodeTemplate(` -{ +{#if returnTypeAndVar} {returnTypeAndVar} = {expression}; {destructors} return {returnTemp}; -} +{#else} + {destructors} + return {expression}; +{/if} `, ts.SyntaxKind.ReturnStatement) export class CReturnStatement extends CTemplateBase { public expression: CExpression; @@ -84,18 +87,40 @@ export class CReturnStatement extends CTemplateBase { public returnTypeAndVar: string = null; public returnTemp: string = null; public closureParams: { name: string, value: CExpression }[] = []; + doCheckVarNeeded(scope: IScope, node: ts.ReturnStatement): boolean { + let s = scope.root.memoryManager.getDestructorsForScope(node); + let idents = []; + for (let e of s) { + // strings will not be GCed here, thanks to escape analysis + if (e.dict || e.array) { + idents.push(e.varName); + } + } + function testNode(n: ts.Node): boolean { + let totalResult = ts.isIdentifier(n) && idents.indexOf(n.text) != -1; + for (let i = 0; i < n.getChildCount(); i++) { + totalResult ||= testNode(n.getChildAt(i)); + } + return totalResult; + } + return testNode(node); + } constructor(scope: IScope, node: ts.ReturnStatement) { super(); this.returnTemp = scope.root.symbolsHelper.addTemp(node, 'returnVal'); let returnType = scope.root.typeHelper.getCType(node.expression); - let fakeVar = new CVariable(scope, '__fake', returnType, { removeStorageSpecifier: true, arraysToPointers: true });; - let fakeVarType = fakeVar.resolve().slice(0, -6).trim(); - this.returnTypeAndVar = fakeVarType; - if (this.returnTypeAndVar.indexOf('{var}') == -1) { - this.returnTypeAndVar += ' {var}'; + if (this.doCheckVarNeeded(scope, node)) { + // todo: make this less hackyy + let fakeVar = new CVariable(scope, '__fake', returnType, { removeStorageSpecifier: true, arraysToPointers: true });; + let fakeVarType = fakeVar.resolve().slice(0, -6).trim(); + this.returnTypeAndVar = fakeVarType; + if (this.returnTypeAndVar.indexOf('{var}') == -1) { + this.returnTypeAndVar += ' {var}'; + } + this.returnTypeAndVar = this.returnTypeAndVar.replace('{var}', this.returnTemp); } - this.returnTypeAndVar = this.returnTypeAndVar.replace('{var}', this.returnTemp); this.expression = CodeTemplateFactory.createForNode(scope, node.expression); + this.destructors = new CVariableDestructors(scope, node); } } @@ -394,8 +419,7 @@ export class CForInStatement extends CTemplateBase implements IScope { else this.init = new CElementAccess(scope, node.initializer); - if (node.statement.kind == ts.SyntaxKind.Block) - { + if (node.statement.kind == ts.SyntaxKind.Block) { let block = node.statement; for (let s of block.statements) this.statements.push(CodeTemplateFactory.createForNode(this, s)); From e32b352ecbb416e4ab4f9a8a843ce057114dbb00 Mon Sep 17 00:00:00 2001 From: pitust Date: Sun, 13 Dec 2020 12:52:25 +0000 Subject: [PATCH 6/6] Fix the warning and CReturnStatement.doCheckVarNeeded --- src/nodes/statements.ts | 24 ++++++++---------------- src/template.ts | 2 ++ 2 files changed, 10 insertions(+), 16 deletions(-) diff --git a/src/nodes/statements.ts b/src/nodes/statements.ts index c292f97..c9d16ee 100644 --- a/src/nodes/statements.ts +++ b/src/nodes/statements.ts @@ -72,9 +72,11 @@ export class CEmptyStatement { @CodeTemplate(` {#if returnTypeAndVar} - {returnTypeAndVar} = {expression}; - {destructors} - return {returnTemp}; + { + {returnTypeAndVar} = {expression}; + {destructors} + return {returnTemp}; + } {#else} {destructors} return {expression}; @@ -89,21 +91,11 @@ export class CReturnStatement extends CTemplateBase { public closureParams: { name: string, value: CExpression }[] = []; doCheckVarNeeded(scope: IScope, node: ts.ReturnStatement): boolean { let s = scope.root.memoryManager.getDestructorsForScope(node); - let idents = []; + let result = false; for (let e of s) { - // strings will not be GCed here, thanks to escape analysis - if (e.dict || e.array) { - idents.push(e.varName); - } - } - function testNode(n: ts.Node): boolean { - let totalResult = ts.isIdentifier(n) && idents.indexOf(n.text) != -1; - for (let i = 0; i < n.getChildCount(); i++) { - totalResult ||= testNode(n.getChildAt(i)); - } - return totalResult; + result = result || new RegExp('\\W' + e.varName + '\\W').test(' ' + node.getFullText() + ' '); } - return testNode(node); + return result; } constructor(scope: IScope, node: ts.ReturnStatement) { super(); diff --git a/src/template.ts b/src/template.ts index 974974f..d67e7d4 100644 --- a/src/template.ts +++ b/src/template.ts @@ -42,6 +42,8 @@ export function CodeTemplate(tempString: string, nodeKind?: number | number[]): nodeKindTemplates[nk] = newConstructor; } + newConstructor.prototype = target.prototype; + return newConstructor; } as ClassDecorator;