From c2ad08f0e165beba7e8781fe63d6de5fde034e1f Mon Sep 17 00:00:00 2001 From: Lars Melchior Date: Sun, 2 Dec 2018 22:21:24 +0100 Subject: [PATCH 1/4] check for inherited accessor methods --- src/TSHelper.ts | 50 +++++++++++++++++++++++------------ test/unit/expressions.spec.ts | 31 ++++++++++++++++++++++ 2 files changed, 64 insertions(+), 17 deletions(-) diff --git a/src/TSHelper.ts b/src/TSHelper.ts index c3eac9460..80af776cf 100644 --- a/src/TSHelper.ts +++ b/src/TSHelper.ts @@ -99,13 +99,7 @@ export class TSHelper { } public static isArrayType(type: ts.Type, checker: ts.TypeChecker): boolean { - const baseTypes = type.getBaseTypes(); - if (baseTypes) { - for (const baseType of baseTypes) { - if (this.isExplicitArrayType(baseType, checker)) { return true; } - } - } - return this.isExplicitArrayType(type, checker); + return this.forAllTypes(type, t => this.isExplicitArrayType(t, checker)); } public static isTupleReturnCall(node: ts.Node, checker: ts.TypeChecker): boolean { @@ -182,28 +176,50 @@ export class TSHelper { return null; } + public static hasExplicitGetAccessor(type: ts.Type, name: ts.__String): boolean { + if (type && type.symbol && type.symbol.members) { + const field = type.symbol.members.get(name); + return field && (field.flags & ts.SymbolFlags.GetAccessor) !== 0; + } + } + + // iterate over a type and its bases until the callback returns true. + public static forAllTypes(type: ts.Type, callback: (type: ts.Type) => boolean): boolean { + if (callback(type)) { + return true; + } + const baseTypes = type.getBaseTypes(); + if (baseTypes) { + for (const baseType of baseTypes) { + if (this.forAllTypes(baseType, callback)) { + return true; + } + } + } + return false; + } + public static hasGetAccessor(node: ts.Node, checker: ts.TypeChecker): boolean { if (ts.isPropertyAccessExpression(node)) { const name = node.name.escapedText; const type = checker.getTypeAtLocation(node.expression); - - if (type && type.symbol && type.symbol.members) { - const field = type.symbol.members.get(name); - return field && (field.flags & ts.SymbolFlags.GetAccessor) !== 0; - } + return this.forAllTypes(type, t => this.hasExplicitGetAccessor(t, name)); } return false; } + public static hasExplicitSetAccessor(type: ts.Type, name: ts.__String): boolean { + if (type && type.symbol && type.symbol.members) { + const field = type.symbol.members.get(name); + return field && (field.flags & ts.SymbolFlags.SetAccessor) !== 0; + } + } + public static hasSetAccessor(node: ts.Node, checker: ts.TypeChecker): boolean { if (ts.isPropertyAccessExpression(node)) { const name = node.name.escapedText; const type = checker.getTypeAtLocation(node.expression); - - if (type && type.symbol && type.symbol.members) { - const field = type.symbol.members.get(name); - return field && (field.flags & ts.SymbolFlags.SetAccessor) !== 0; - } + return this.forAllTypes(type, t => this.hasExplicitSetAccessor(t, name)); } return false; } diff --git a/test/unit/expressions.spec.ts b/test/unit/expressions.spec.ts index 7fbea1209..04b00f2a8 100644 --- a/test/unit/expressions.spec.ts +++ b/test/unit/expressions.spec.ts @@ -254,6 +254,37 @@ export class ExpressionTests { Expect(result).toBe(expected); } + @TestCase("inst.baseField", 10) + @TestCase("inst.field", 8) + @TestCase("inst.superField", 6) + @Test("Inherited accessors") + public inheritedAccessors(expression: string, expected: any): void { + const source = `class MyBaseClass {` + + ` public _baseField: number;` + + ` public get baseField(): number { return this._baseField + 6; }` + + ` public set baseField(v: number) { this._baseField = v; }` + + `}` + + `class MyClass extends MyBaseClass {` + + ` public _field: number;` + + ` public get field(): number { return this._field + 4; }` + + ` public set field(v: number) { this._field = v; }` + + `}` + + `class MySuperClass extends MyClass {` + + ` public _superField: number;` + + ` public get superField(): number { return this._superField + 2; }` + + ` public set superField(v: number) { this._superField = v; }` + + `}` + + `var inst = new MySuperClass();` + + `inst.baseField = 4;` + + `inst.field = 4;` + + `inst.superField = 4;` + + `return ${expression};`; + + const lua = util.transpileString(source); + const result = util.executeLua(lua); + Expect(result).toBe(expected); + } + @TestCase("i++", 10) @TestCase("i--", 10) @TestCase("++i", 11) From 4161b57b836b3396ec12e03ab923c91f1596c143 Mon Sep 17 00:00:00 2001 From: Lars Melchior Date: Sun, 2 Dec 2018 22:30:57 +0100 Subject: [PATCH 2/4] diversify test --- test/unit/expressions.spec.ts | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/test/unit/expressions.spec.ts b/test/unit/expressions.spec.ts index 04b00f2a8..17906cd63 100644 --- a/test/unit/expressions.spec.ts +++ b/test/unit/expressions.spec.ts @@ -1,10 +1,11 @@ -import { Expect, Test, TestCase } from "alsatian"; +import { Expect, Test, TestCase, FocusTests } from "alsatian"; import { TranspileError } from "../../src/Errors"; import { LuaTarget } from "../../src/Transpiler"; import * as ts from "typescript"; import * as util from "../src/util"; +@FocusTests export class ExpressionTests { @TestCase("i++", "i = (i+1);") @@ -254,9 +255,9 @@ export class ExpressionTests { Expect(result).toBe(expected); } - @TestCase("inst.baseField", 10) - @TestCase("inst.field", 8) - @TestCase("inst.superField", 6) + @TestCase("inst.baseField", 7) + @TestCase("inst.field", 6) + @TestCase("inst.superField", 5) @Test("Inherited accessors") public inheritedAccessors(expression: string, expected: any): void { const source = `class MyBaseClass {` @@ -275,9 +276,9 @@ export class ExpressionTests { + ` public set superField(v: number) { this._superField = v; }` + `}` + `var inst = new MySuperClass();` - + `inst.baseField = 4;` - + `inst.field = 4;` - + `inst.superField = 4;` + + `inst.baseField = 1;` + + `inst.field = 2;` + + `inst.superField = 3;` + `return ${expression};`; const lua = util.transpileString(source); From 61c6e76c9c60d62559a7b22327d81a8a0540c4a2 Mon Sep 17 00:00:00 2001 From: Lars Melchior Date: Sun, 2 Dec 2018 22:35:06 +0100 Subject: [PATCH 3/4] removed FocusTests and repositioned forAllTypes --- src/TSHelper.ts | 32 ++++++++++++++++---------------- test/unit/expressions.spec.ts | 3 +-- 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/src/TSHelper.ts b/src/TSHelper.ts index 80af776cf..c50c95b5d 100644 --- a/src/TSHelper.ts +++ b/src/TSHelper.ts @@ -80,6 +80,22 @@ export class TSHelper { || (ts.isBinaryExpression(node.parent) && ts.isArrayLiteralExpression(node.parent.left))); } + // iterate over a type and its bases until the callback returns true. + public static forAllTypes(type: ts.Type, callback: (type: ts.Type) => boolean): boolean { + if (callback(type)) { + return true; + } + const baseTypes = type.getBaseTypes(); + if (baseTypes) { + for (const baseType of baseTypes) { + if (this.forAllTypes(baseType, callback)) { + return true; + } + } + } + return false; + } + public static isStringType(type: ts.Type): boolean { return (type.flags & ts.TypeFlags.String) !== 0 || (type.flags & ts.TypeFlags.StringLike) !== 0 @@ -183,22 +199,6 @@ export class TSHelper { } } - // iterate over a type and its bases until the callback returns true. - public static forAllTypes(type: ts.Type, callback: (type: ts.Type) => boolean): boolean { - if (callback(type)) { - return true; - } - const baseTypes = type.getBaseTypes(); - if (baseTypes) { - for (const baseType of baseTypes) { - if (this.forAllTypes(baseType, callback)) { - return true; - } - } - } - return false; - } - public static hasGetAccessor(node: ts.Node, checker: ts.TypeChecker): boolean { if (ts.isPropertyAccessExpression(node)) { const name = node.name.escapedText; diff --git a/test/unit/expressions.spec.ts b/test/unit/expressions.spec.ts index 17906cd63..868b9bee7 100644 --- a/test/unit/expressions.spec.ts +++ b/test/unit/expressions.spec.ts @@ -1,11 +1,10 @@ -import { Expect, Test, TestCase, FocusTests } from "alsatian"; +import { Expect, Test, TestCase } from "alsatian"; import { TranspileError } from "../../src/Errors"; import { LuaTarget } from "../../src/Transpiler"; import * as ts from "typescript"; import * as util from "../src/util"; -@FocusTests export class ExpressionTests { @TestCase("i++", "i = (i+1);") From 976c96980b545cee15520458167c3510447b4d69 Mon Sep 17 00:00:00 2001 From: Lars Melchior Date: Fri, 7 Dec 2018 13:41:35 +0100 Subject: [PATCH 4/4] rename forTypeOrAnySupertype --- src/TSHelper.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/TSHelper.ts b/src/TSHelper.ts index c50c95b5d..aa87170e5 100644 --- a/src/TSHelper.ts +++ b/src/TSHelper.ts @@ -81,14 +81,14 @@ export class TSHelper { } // iterate over a type and its bases until the callback returns true. - public static forAllTypes(type: ts.Type, callback: (type: ts.Type) => boolean): boolean { + public static forTypeOrAnySupertype(type: ts.Type, callback: (type: ts.Type) => boolean): boolean { if (callback(type)) { return true; } const baseTypes = type.getBaseTypes(); if (baseTypes) { for (const baseType of baseTypes) { - if (this.forAllTypes(baseType, callback)) { + if (this.forTypeOrAnySupertype(baseType, callback)) { return true; } } @@ -115,7 +115,7 @@ export class TSHelper { } public static isArrayType(type: ts.Type, checker: ts.TypeChecker): boolean { - return this.forAllTypes(type, t => this.isExplicitArrayType(t, checker)); + return this.forTypeOrAnySupertype(type, t => this.isExplicitArrayType(t, checker)); } public static isTupleReturnCall(node: ts.Node, checker: ts.TypeChecker): boolean { @@ -203,7 +203,7 @@ export class TSHelper { if (ts.isPropertyAccessExpression(node)) { const name = node.name.escapedText; const type = checker.getTypeAtLocation(node.expression); - return this.forAllTypes(type, t => this.hasExplicitGetAccessor(t, name)); + return this.forTypeOrAnySupertype(type, t => this.hasExplicitGetAccessor(t, name)); } return false; } @@ -219,7 +219,7 @@ export class TSHelper { if (ts.isPropertyAccessExpression(node)) { const name = node.name.escapedText; const type = checker.getTypeAtLocation(node.expression); - return this.forAllTypes(type, t => this.hasExplicitSetAccessor(t, name)); + return this.forTypeOrAnySupertype(type, t => this.hasExplicitSetAccessor(t, name)); } return false; }