Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/LuaLib.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ export enum LuaLibFeature {
DecorateParam = "DecorateParam",
Delete = "Delete",
DelegatedYield = "DelegatedYield",
DescriptorGet = "DescriptorGet",
DescriptorSet = "DescriptorSet",
Error = "Error",
FunctionBind = "FunctionBind",
Generator = "Generator",
Expand Down
25 changes: 25 additions & 0 deletions src/lualib/DescriptorGet.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
const getmetatable = _G.getmetatable;
const rawget = _G.rawget;

export function __TS__DescriptorGet(this: any, metatable: any, key: string): void {
while (metatable) {
const rawResult = rawget(metatable, key as any);
if (rawResult !== undefined) {
return rawResult;
}

const descriptors = rawget(metatable, "_descriptors");
if (descriptors) {
const descriptor: PropertyDescriptor = descriptors[key];
if (descriptor !== undefined) {
if (descriptor.get) {
return descriptor.get.call(this);
}

return descriptor.value;
}
}

metatable = getmetatable(metatable);
}
}
28 changes: 28 additions & 0 deletions src/lualib/DescriptorSet.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
const getmetatable = _G.getmetatable;
const rawget = _G.rawget;
const rawset = _G.rawset;

export function __TS__DescriptorSet(this: any, metatable: any, key: string, value: any): void {
while (metatable) {
const descriptors = rawget(metatable, "_descriptors");
if (descriptors) {
const descriptor: PropertyDescriptor = descriptors[key];
if (descriptor !== undefined) {
if (descriptor.set) {
descriptor.set.call(this, value);
} else {
if (descriptor.writable === false) {
throw `Cannot assign to read only property '${key}' of object '${this}'`;
}

descriptor.value = value;
}
return;
}
}

metatable = getmetatable(metatable);
}

rawset(this, key, value);
}
55 changes: 6 additions & 49 deletions src/lualib/SetDescriptor.ts
Original file line number Diff line number Diff line change
@@ -1,58 +1,15 @@
import { __TS__CloneDescriptor } from "./CloneDescriptor";
import { __TS__DescriptorGet } from "./DescriptorGet";
import { __TS__DescriptorSet } from "./DescriptorSet";

function descriptorIndex(this: any, key: string): void {
const value = rawget(this, key);
if (value !== null) {
return value;
}

let metatable = getmetatable(this);
while (metatable) {
const rawResult = rawget(metatable, key as any);
if (rawResult !== undefined) {
return rawResult;
}
const getmetatable = _G.getmetatable;

const descriptors = rawget(metatable, "_descriptors");
if (descriptors) {
const descriptor: PropertyDescriptor = descriptors[key];
if (descriptor !== undefined) {
if (descriptor.get) {
return descriptor.get.call(this);
}

return descriptor.value;
}
}

metatable = getmetatable(metatable);
}
function descriptorIndex(this: any, key: string): void {
return __TS__DescriptorGet.call(this, getmetatable(this), key);
}

function descriptorNewIndex(this: any, key: string, value: any): void {
let metatable = getmetatable(this);
while (metatable) {
const descriptors = rawget(metatable, "_descriptors");
if (descriptors) {
const descriptor: PropertyDescriptor = descriptors[key];
if (descriptor !== undefined) {
if (descriptor.set) {
descriptor.set.call(this, value);
} else {
if (descriptor.writable === false) {
throw `Cannot assign to read only property '${key}' of object '${this}'`;
}

descriptor.value = value;
}
return;
}
}

metatable = getmetatable(metatable);
}

rawset(this, key, value);
return __TS__DescriptorSet.call(this, getmetatable(this), key, value);
}

// It's also used directly in class transform to add descriptors to the prototype
Expand Down
13 changes: 13 additions & 0 deletions src/transformation/visitors/access.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
isOptionalContinuation,
captureThisValue,
} from "./optional-chaining";
import { SyntaxKind } from "typescript";

function addOneToArrayAccessArgument(
context: TransformationContext,
Expand Down Expand Up @@ -173,6 +174,18 @@ export function transformPropertyAccessExpressionWithCapture(
thisValue,
};
}
if (node.expression.kind === SyntaxKind.SuperKeyword) {
return {
expression: transformLuaLibFunction(
context,
LuaLibFeature.DescriptorGet,
node,
lua.createIdentifier("self"),
table,
lua.createStringLiteral(property)
),
};
}
return { expression: lua.createTableIndexExpression(table, lua.createStringLiteral(property), node) };
}

Expand Down
21 changes: 21 additions & 0 deletions src/transformation/visitors/binary-expression/assignments.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as ts from "typescript";
import { SyntaxKind } from "typescript";
import * as lua from "../../../LuaAST";
import { TransformationContext } from "../../context";
import { validateAssignment } from "../../utils/assignment-validation";
Expand Down Expand Up @@ -75,6 +76,26 @@ export function transformAssignment(
return [arrayLengthAssignment];
}

if (ts.isPropertyAccessExpression(lhs) || ts.isElementAccessExpression(lhs)) {
if (lhs.expression.kind === SyntaxKind.SuperKeyword) {
return [
lua.createExpressionStatement(
transformLuaLibFunction(
context,
LuaLibFeature.DescriptorSet,
parent,
lua.createIdentifier("self"),
context.transformExpression(lhs.expression),
ts.isPropertyAccessExpression(lhs)
? lua.createStringLiteral(lhs.name.text)
: context.transformExpression(lhs.argumentExpression),
right
)
),
];
}
}

const symbol =
lhs.parent && ts.isShorthandPropertyAssignment(lhs.parent)
? context.checker.getShorthandAssignmentValueSymbol(lhs.parent)
Expand Down
41 changes: 41 additions & 0 deletions test/unit/classes/accessors.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -327,3 +327,44 @@ test("static get/set accessors in base class", () => {
return fooOriginal + Bar.foo;
`.expectToMatchJsResult();
});

// https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1437
test("super class get accessor (#1437)", () => {
util.testFunction`
class A {
get foo() {
return "A";
}
}

class B extends A {
override get foo() {
return super.foo + "B";
}
}

return new B().foo;
`.expectToMatchJsResult();
});

test("super class set accessor", () => {
util.testFunction`
let result = "unset";

class A {
set result(value: string) {
result = "foo" + value;
}
}

class B extends A {
override set result(value: string) {
super.result = "bar" + value;
}
}

new B().result = "baz";

return result;
`.expectToMatchJsResult();
});