[JS IR] Access with exported properties by its name, not accessor

[JS IR] Export properties to ts as getter/setter, not fields

[JS IR] Fix typescript tests



Merge-request: KT-MR-5074
This commit is contained in:
Ilya Goncharov
2021-11-26 09:18:12 +00:00
committed by Space
parent 353f82f9e6
commit cac286c915
21 changed files with 228 additions and 145 deletions
@@ -56,6 +56,7 @@ class ExportedProperty(
val isStatic: Boolean = false,
val isAbstract: Boolean,
val isProtected: Boolean,
val isField: Boolean,
val irGetter: IrFunction?,
val irSetter: IrFunction?,
val exportedObject: ExportedClass? = null,
@@ -144,6 +144,7 @@ class ExportModelGenerator(
isStatic = false,
isAbstract = parentClass?.isInterface == false && property.modality == Modality.ABSTRACT,
isProtected = property.visibility == DescriptorVisibilities.PROTECTED,
isField = parentClass?.isInterface == true,
irGetter = property.getter,
irSetter = property.setter
)
@@ -169,6 +170,7 @@ class ExportModelGenerator(
isProtected = false,
irGetter = null,
irSetter = null,
isField = false,
)
val nameProperty = fakeProperty(
@@ -195,7 +197,8 @@ class ExportModelGenerator(
isProtected = parentClass.visibility == DescriptorVisibilities.PROTECTED,
irGetter = context.mapping.enumEntryToGetInstanceFun[irEnumEntry]
?: error("Unable to find get instance fun for ${field.fqNameWhenAvailable}"),
irSetter = null
irSetter = null,
isField = false,
)
}
@@ -385,6 +388,7 @@ class ExportModelGenerator(
irGetter = context.mapping.objectToGetInstanceFunction[klass]!!,
irSetter = null,
exportedObject = exportedClass,
isField = false,
)
}
@@ -24,7 +24,7 @@ fun wrapTypeScript(name: String, moduleKind: ModuleKind, dts: String): String {
else -> "declare "
}
val types = """
type Nullable<T> = T | null | undefined
type Nullable<T> = T | null | undefined
${declareKeyword}const __doNotImplementIt: unique symbol
type __doNotImplementIt = typeof __doNotImplementIt
""".trimIndent().prependIndent(moduleKind.indent) + "\n"
@@ -105,7 +105,7 @@ fun ExportedDeclaration.toTypeScript(indent: String, prefix: String = ""): Strin
is ExportedProperty -> {
val visibility = if (isProtected) "protected " else ""
val keyword = when {
isMember -> (if (isAbstract) "abstract " else "") + (if (!mutable) "readonly " else "")
isMember -> (if (isAbstract) "abstract " else "")
else -> if (mutable) "let " else "const "
}
val possibleStatic = if (isMember && isStatic) "static " else ""
@@ -114,7 +114,18 @@ fun ExportedDeclaration.toTypeScript(indent: String, prefix: String = ""): Strin
isMember && containsUnresolvedChar -> "\"$name\""
else -> name
}
if (!isMember && containsUnresolvedChar) "" else "$prefix$visibility$possibleStatic$keyword$memberName: ${type.toTypeScript(indent)};"
val typeToTypeScript = type.toTypeScript(indent)
if (isMember && !isField) {
val getter = "$prefix$visibility$possibleStatic${keyword}get $memberName(): $typeToTypeScript;"
if (!mutable) getter
else getter + "\n" + "$indent$prefix$visibility$possibleStatic${keyword}set $memberName(value: $typeToTypeScript);"
} else {
if (!isMember && containsUnresolvedChar) ""
else {
val readonly = if (isMember && !mutable) "readonly " else ""
"$prefix$visibility$possibleStatic$keyword$readonly$memberName: $typeToTypeScript;"
}
}
}
is ExportedClass -> {
@@ -202,8 +213,9 @@ fun List<ExportedDeclaration>.withMagicProperty(): List<ExportedDeclaration> {
isStatic = false,
isAbstract = false,
isProtected = false,
isField = true,
irGetter = null,
irSetter = null
irSetter = null,
)
)
}
@@ -250,6 +262,7 @@ fun ExportedClass.toReadonlyProperty(): ExportedProperty {
isStatic = false,
isAbstract = false,
isProtected = false,
isField = false,
irGetter = null,
irSetter = null
)
@@ -124,7 +124,7 @@ fun translateCall(
val property = function.correspondingPropertySymbol?.owner
if (
property != null &&
(property.isEffectivelyExternal() || property.isExportedInterfaceMember())
(property.isEffectivelyExternal() || property.isExportedMember())
) {
val propertyName = context.getNameForProperty(property)
val nameRef = when (jsDispatchReceiver) {
@@ -11,6 +11,9 @@ import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.util.parentClassOrNull
fun IrDeclaration.isExportedMember() =
parentClassOrNull.let { it is IrClass && it.isJsExport() }
fun IrDeclaration?.isExportedClass() =
this is IrClass && kind.isClass && isJsExport()
+3 -3
View File
@@ -276,9 +276,9 @@
}
},
"typescript": {
"version": "3.9.5",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.5.tgz",
"integrity": "sha512-hSAifV3k+i6lEoCJ2k6R2Z/rp/H3+8sdmcn5NrS3/3kE7+RyZXm9aqvxWqjEXHAd8b0pShatpcdMTvEdvAJltQ==",
"version": "4.5.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.2.tgz",
"integrity": "sha512-5BlMof9H1yGt0P8/WF+wPNw6GfctgGjXp5hkblpyT+8rkASSmkUKMXrxR0Xg8ThVCi/JnHQiKXeBaEwCeQwMFw==",
"dev": true
},
"wrappy": {
+1 -1
View File
@@ -5,7 +5,7 @@
"devDependencies": {
"mocha": "3.2.0",
"mocha-teamcity-reporter": "1.1.1",
"typescript": "^3.5.3"
"typescript": "^4.5.2"
},
"scripts": {
"runOnTeamcity": "mocha --reporter mocha-teamcity-reporter",
@@ -5,28 +5,28 @@ declare namespace JS_TESTS {
namespace foo {
class TestInner {
constructor(a: string);
readonly a: string;
readonly Inner: {
get a(): string;
get Inner(): {
new(a: string): TestInner.Inner;
} & typeof TestInner.Inner;
}
namespace TestInner {
class Inner {
protected constructor($outer: foo.TestInner, a: string);
readonly a: string;
readonly concat: string;
get a(): string;
get concat(): string;
static fromNumber(a: number): foo.TestInner.Inner;
readonly SecondLayerInner: {
get SecondLayerInner(): {
new(a: string): TestInner.Inner.SecondLayerInner;
} & typeof TestInner.Inner.SecondLayerInner;
}
namespace Inner {
class SecondLayerInner {
protected constructor($outer: foo.TestInner.Inner, a: string);
readonly a: string;
readonly concat: string;
get a(): string;
get concat(): string;
}
}
}
}
}
}
@@ -4,26 +4,26 @@ declare namespace JS_TESTS {
type __doNotImplementIt = typeof __doNotImplementIt
class ClassWithDefaultCtor {
constructor();
readonly x: string;
get x(): string;
}
class ClassWithPrimaryCtor {
constructor(x: string);
readonly x: string;
get x(): string;
}
class ClassWithSecondaryCtor {
private constructor();
readonly x: string;
get x(): string;
static create(y: string): ClassWithSecondaryCtor;
}
class ClassWithMultipleSecondaryCtors {
private constructor();
readonly x: string;
get x(): string;
static createFromString(y: string): ClassWithMultipleSecondaryCtors;
static createFromInts(y: number, z: number): ClassWithMultipleSecondaryCtors;
}
class OpenClassWithMixedConstructors {
constructor(x: string);
readonly x: string;
get x(): string;
static createFromStrings(y: string, z: string): OpenClassWithMixedConstructors;
static createFromInts(y: number, z: number): OpenClassWithMixedConstructors;
}
@@ -34,6 +34,6 @@ declare namespace JS_TESTS {
}
class KotlinGreeter {
constructor(greeting: string);
readonly greeting: string;
get greeting(): string;
}
}
@@ -26,40 +26,43 @@ declare namespace JS_TESTS {
}
class A1 {
constructor(x: number);
readonly x: number;
get x(): number;
}
class A2 {
constructor(x: string, y: boolean);
readonly x: string;
y: boolean;
get x(): string;
get y(): boolean;
set y(value: boolean);
}
class A3 {
constructor();
readonly x: number;
get x(): number;
}
class A4 {
constructor();
readonly _valCustom: number;
readonly _valCustomWithField: number;
_varCustom: number;
_varCustomWithField: number;
get _valCustom(): number;
get _valCustomWithField(): number;
get _varCustom(): number;
set _varCustom(value: number);
get _varCustomWithField(): number;
set _varCustomWithField(value: number);
}
const O0: {
};
const O: {
readonly x: number;
get x(): number;
foo(): number;
};
function takesO(o: typeof foo.O): number;
class KT_37829 {
constructor();
static readonly Companion: {
readonly x: number;
static get Companion(): {
get x(): number;
};
}
class TestSealed {
protected constructor(name: string);
readonly name: string;
get name(): string;
}
namespace TestSealed {
class AA extends foo.TestSealed {
@@ -73,7 +76,7 @@ declare namespace JS_TESTS {
}
abstract class TestAbstract {
constructor(name: string);
readonly name: string;
get name(): string;
}
namespace TestAbstract {
class AA extends foo.TestAbstract {
@@ -87,7 +90,7 @@ declare namespace JS_TESTS {
}
class TestDataClass {
constructor(name: string);
readonly name: string;
get name(): string;
component1(): string;
copy(name: string): foo.TestDataClass;
toString(): string;
@@ -97,32 +100,32 @@ declare namespace JS_TESTS {
namespace TestDataClass {
class Nested {
constructor();
readonly prop: string;
get prop(): string;
}
}
abstract class TestEnumClass {
private constructor();
readonly constructorParameter: string;
static readonly A: foo.TestEnumClass & {
readonly name: "A";
readonly ordinal: 0;
get constructorParameter(): string;
static get A(): foo.TestEnumClass & {
get name(): "A";
get ordinal(): 0;
};
static readonly B: foo.TestEnumClass & {
readonly name: "B";
readonly ordinal: 1;
static get B(): foo.TestEnumClass & {
get name(): "B";
get ordinal(): 1;
};
readonly foo: number;
get foo(): number;
bar(value: string): string;
bay(): string;
static values(): Array<foo.TestEnumClass>;
static valueOf(value: string): foo.TestEnumClass;
readonly name: "A" | "B";
readonly ordinal: 0 | 1;
get name(): "A" | "B";
get ordinal(): 0 | 1;
}
namespace TestEnumClass {
class Nested {
constructor();
readonly prop: string;
get prop(): string;
}
}
interface TestInterface {
@@ -132,7 +135,7 @@ declare namespace JS_TESTS {
}
class TestInterfaceImpl implements foo.TestInterface {
constructor(value: string);
readonly value: string;
get value(): string;
getOwnerName(): string;
readonly __doNotUseIt: __doNotImplementIt;
}
@@ -3,19 +3,21 @@ declare namespace JS_TESTS {
const __doNotImplementIt: unique symbol
type __doNotImplementIt = typeof __doNotImplementIt
namespace foo {
function invalid_args_name_sum(first_value: number, second_value: number): number;
class A1 {
constructor(first_value: number, second_value: number);
readonly "first value": number;
"second.value": number;
get "first value"(): number;
get "second.value"(): number;
set "second.value"(value: number);
}
class A2 {
constructor();
"invalid:name": number;
get "invalid:name"(): number;
set "invalid:name"(value: number);
}
class A3 {
constructor();
@@ -24,10 +26,11 @@ declare namespace JS_TESTS {
}
class A4 {
constructor();
static readonly Companion: {
"@invalid+name@": number;
static get Companion(): {
get "@invalid+name@"(): number;
set "@invalid+name@"(value: number);
"^)run.something.weird^("(): string;
};
}
}
}
}
@@ -10,7 +10,8 @@ declare namespace JS_TESTS {
function consumer(value: any/* foo.NonExportedType */): number;
class A {
constructor(value: any/* foo.NonExportedType */);
value: any/* foo.NonExportedType */;
get value(): any/* foo.NonExportedType */;
set value(value: any/* foo.NonExportedType */);
increment<T>(t: T): any/* foo.NonExportedType */;
}
class B /* extends foo.NonExportedType */ {
@@ -31,4 +32,4 @@ declare namespace JS_TESTS {
constructor();
}
}
}
}
@@ -17,16 +17,17 @@ declare namespace JS_TESTS {
namespace foo {
abstract class AC implements foo.I2 {
constructor();
x: string;
abstract readonly y: boolean;
get x(): string;
set x(value: string);
abstract get y(): boolean;
abstract z(z: number): void;
readonly acProp: string;
abstract readonly acAbstractProp: string;
get acProp(): string;
abstract get acAbstractProp(): string;
}
class OC extends foo.AC implements foo.I<string, boolean, number> {
constructor(y: boolean, acAbstractProp: string);
readonly y: boolean;
readonly acAbstractProp: string;
get y(): boolean;
get acAbstractProp(): string;
z(z: number): void;
}
class FC extends foo.OC {
@@ -50,48 +51,53 @@ declare namespace JS_TESTS {
function getC(): foo.I3;
abstract class A2 implements foo.I3 {
constructor();
abstract readonly foo: string;
abstract bar: string;
abstract readonly baz: string;
abstract get foo(): string;
abstract get bar(): string;
abstract set bar(value: string);
abstract get baz(): string;
abstract bay(): string;
readonly __doNotUseIt: __doNotImplementIt;
}
class B2 extends foo.A2 {
constructor();
readonly foo: string;
bar: string;
readonly baz: string;
get foo(): string;
get bar(): string;
set bar(value: string);
get baz(): string;
bay(): string;
}
class C2 extends foo.B2 {
constructor();
readonly foo: string;
bar: string;
baz: string;
get foo(): string;
get bar(): string;
set bar(value: string);
get baz(): string;
set baz(value: string);
bay(): string;
}
abstract class EC implements foo.I3 {
private constructor();
static readonly EC1: foo.EC & {
readonly name: "EC1";
readonly ordinal: 0;
static get EC1(): foo.EC & {
get name(): "EC1";
get ordinal(): 0;
};
static readonly EC2: foo.EC & {
readonly name: "EC2";
readonly ordinal: 1;
static get EC2(): foo.EC & {
get name(): "EC2";
get ordinal(): 1;
};
static readonly EC3: foo.EC & {
readonly name: "EC3";
readonly ordinal: 2;
static get EC3(): foo.EC & {
get name(): "EC3";
get ordinal(): 2;
};
readonly foo: string;
bar: string;
get foo(): string;
get bar(): string;
set bar(value: string);
bay(): string;
static values(): Array<foo.EC>;
static valueOf(value: string): foo.EC;
readonly name: "EC1" | "EC2" | "EC3";
readonly ordinal: 0 | 1 | 2;
abstract readonly baz: string;
get name(): "EC1" | "EC2" | "EC3";
get ordinal(): 0 | 1 | 2;
abstract get baz(): string;
readonly __doNotUseIt: __doNotImplementIt;
}
}
@@ -3,10 +3,12 @@ var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
@@ -24,6 +26,7 @@ var getC = JS_TESTS.foo.getC;
var B2 = JS_TESTS.foo.B2;
var C2 = JS_TESTS.foo.C2;
var EC = JS_TESTS.foo.EC;
var A2 = JS_TESTS.foo.A2;
var Impl = /** @class */ (function (_super) {
__extends(Impl, _super);
function Impl() {
@@ -43,16 +46,42 @@ var Impl = /** @class */ (function (_super) {
});
return Impl;
}(AC));
// class A2Impl extends A2 {
// bar: string = "barA2"
// readonly baz: string = "bazA2"
// readonly foo: string = "fooA2"
//
// bay(): string {
// return "bayA2";
// }
//
// }
var A2Impl = /** @class */ (function (_super) {
__extends(A2Impl, _super);
function A2Impl() {
var _this = _super !== null && _super.apply(this, arguments) || this;
_this._bar = "barA2";
return _this;
}
Object.defineProperty(A2Impl.prototype, "bar", {
get: function () {
return this._bar;
},
set: function (value) {
this._bar = value;
},
enumerable: false,
configurable: true
});
Object.defineProperty(A2Impl.prototype, "baz", {
get: function () {
return "bazA2";
},
enumerable: false,
configurable: true
});
Object.defineProperty(A2Impl.prototype, "foo", {
get: function () {
return "fooA2";
},
enumerable: false,
configurable: true
});
A2Impl.prototype.bay = function () {
return "bayA2";
};
return A2Impl;
}(A2));
function box() {
var impl = new Impl();
if (impl.acProp !== "acProp")
@@ -115,11 +144,18 @@ function box() {
return "Fail 25";
if (getC().bay() != "bayC")
return "Fail 26";
// const a2Impl = new A2Impl()
// if (a2Impl.foo != "fooA2") return "Fail 27"
// if (a2Impl.bar != "barA2") return "Fail 28"
// if (a2Impl.baz != "bazA2") return "Fail 29"
// if (a2Impl.bay() != "bayA2") return "Fail 30"
var a2Impl = new A2Impl();
if (a2Impl.foo != "fooA2")
return "Fail 27";
if (a2Impl.bar != "barA2")
return "Fail 28";
a2Impl.bar = "barA2.2";
if (a2Impl.bar != "barA2.2")
return "Fail 28.2";
if (a2Impl.baz != "bazA2")
return "Fail 29";
if (a2Impl.bay() != "bayA2")
return "Fail 30";
var b2 = new B2();
if (b2.foo != "fooB2")
return "Fail 31";
@@ -10,6 +10,7 @@ import getC = JS_TESTS.foo.getC;
import B2 = JS_TESTS.foo.B2;
import C2 = JS_TESTS.foo.C2;
import EC = JS_TESTS.foo.EC;
import A2 = JS_TESTS.foo.A2;
class Impl extends AC {
z(z: number): void {
@@ -19,17 +20,27 @@ class Impl extends AC {
get y(): boolean { return true; }
}
// TODO: Uncomment with fix of export open properties
// class A2Impl extends A2 {
// bar: string = "barA2"
// readonly baz: string = "bazA2"
// readonly foo: string = "fooA2"
//
// bay(): string {
// return "bayA2";
// }
//
// }
class A2Impl extends A2 {
_bar: string = "barA2"
get bar(): string {
return this._bar
}
set bar(value: string) {
this._bar = value
}
get baz(): string {
return "bazA2"
}
get foo(): string {
return "fooA2"
}
bay(): string {
return "bayA2";
}
}
function box(): string {
const impl = new Impl();
@@ -75,11 +86,13 @@ function box(): string {
if (getC().baz != "bazC") return "Fail 25"
if (getC().bay() != "bayC") return "Fail 26"
// const a2Impl = new A2Impl()
// if (a2Impl.foo != "fooA2") return "Fail 27"
// if (a2Impl.bar != "barA2") return "Fail 28"
// if (a2Impl.baz != "bazA2") return "Fail 29"
// if (a2Impl.bay() != "bayA2") return "Fail 30"
const a2Impl = new A2Impl()
if (a2Impl.foo != "fooA2") return "Fail 27"
if (a2Impl.bar != "barA2") return "Fail 28"
a2Impl.bar = "barA2.2"
if (a2Impl.bar != "barA2.2") return "Fail 28.2"
if (a2Impl.baz != "bazA2") return "Fail 29"
if (a2Impl.bay() != "bayA2") return "Fail 30"
const b2 = new B2()
if (b2.foo != "fooB2") return "Fail 31"
@@ -5,7 +5,7 @@ export namespace foo {
const prop: number;
class C {
constructor(x: number);
readonly x: number;
get x(): number;
doubleX(): number;
}
function box(): string;
@@ -6,7 +6,7 @@ declare namespace JS_TESTS {
const prop: number;
class C {
constructor(x: number);
readonly x: number;
get x(): number;
doubleX(): number;
}
function box(): string;
@@ -5,7 +5,7 @@ export namespace foo {
const prop: number;
class C {
constructor(x: number);
readonly x: number;
get x(): number;
doubleX(): number;
}
function box(): string;
@@ -5,7 +5,7 @@ declare namespace JS_TESTS {
namespace foo.bar.baz {
class C1 {
constructor(value: string);
readonly value: string;
get value(): string;
component1(): string;
copy(value: string): foo.bar.baz.C1;
toString(): string;
@@ -17,7 +17,7 @@ declare namespace JS_TESTS {
namespace a.b {
class C2 {
constructor(value: string);
readonly value: string;
get value(): string;
component1(): string;
copy(value: string): a.b.C2;
toString(): string;
@@ -28,7 +28,7 @@ declare namespace JS_TESTS {
}
class C3 {
constructor(value: string);
readonly value: string;
get value(): string;
component1(): string;
copy(value: string): C3;
toString(): string;
@@ -15,7 +15,7 @@ declare namespace JS_TESTS {
function exportedFun(): number;
class ExportedClass {
constructor();
readonly value: number;
get value(): number;
}
}
namespace foo {
@@ -23,7 +23,7 @@ declare namespace JS_TESTS {
function fileLevelExportedFun(): number;
class FileLevelExportedClass {
constructor();
readonly value: number;
get value(): number;
}
}
}
@@ -11,14 +11,14 @@ declare namespace JS_TESTS {
}
class Class {
constructor();
protected readonly protectedVal: number;
protected get protectedVal(): number;
protected protectedFun(): number;
protected static readonly protectedNestedObject: {
protected static get protectedNestedObject(): {
};
protected static readonly Companion: {
readonly companionObjectProp: number;
protected static get Companion(): {
get companionObjectProp(): number;
};
readonly publicVal: number;
get publicVal(): number;
publicFun(): number;
}
namespace Class {
@@ -35,17 +35,17 @@ declare namespace JS_TESTS {
}
abstract class EnumClass {
private constructor();
static readonly EC1: EnumClass & {
readonly name: "EC1";
readonly ordinal: 0;
static get EC1(): EnumClass & {
get name(): "EC1";
get ordinal(): 0;
};
static readonly EC2: EnumClass & {
readonly name: "EC2";
readonly ordinal: 1;
static get EC2(): EnumClass & {
get name(): "EC2";
get ordinal(): 1;
};
static values(): Array<EnumClass>;
static valueOf(value: string): EnumClass;
readonly name: "EC1" | "EC2";
readonly ordinal: 0 | 1;
get name(): "EC1" | "EC2";
get ordinal(): 0 | 1;
}
}