X Tutup
Skip to content
48 changes: 48 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,54 @@

- TypeScript has been updated to 3.8. See [release notes](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html) for details.

- Fixed class accessors not working when base class is lacking type information (#725)

- Class extension code has been extracted to lualib

```ts
class A {}
class B extends A {}
```

```diff
A = __TS__Class()
B = __TS__Class()
-B.____super = A
-setmetatable(B, B.____super)
-setmetatable(B.prototype, B.____super.prototype)
+__TS__ClassExtends(A, B)
```

- Generated code for class accessors is more dynamic now

```ts
class A {
get a() {
return true;
}
}
```

```diff
A = __TS__Class()
-A.prototype.____getters = {}
-A.prototype.__index = __TS__Index(A.prototype)
-function A.prototype.____getters.a(self)
- return true
-end
+__TS__SetDescriptor(
+ A.prototype,
+ "a",
+ {
+ get = function(self)
+ return true
+ end
+ }
+)
```

This change simplifies our codebase and opens a path to object accessors implementation

## 0.31.0

- **Breaking:** The old annotation syntax (`/* !varArg */`) **no longer works**, the only currently supported syntax is:
Expand Down
6 changes: 2 additions & 4 deletions src/LuaLib.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,20 +26,18 @@ export enum LuaLibFeature {
ArrayFlatMap = "ArrayFlatMap",
ArraySetLength = "ArraySetLength",
Class = "Class",
ClassIndex = "ClassIndex",
ClassNewIndex = "ClassNewIndex",
ClassExtends = "ClassExtends",
Decorate = "Decorate",
Descriptors = "Descriptors",
Error = "Error",
FunctionApply = "FunctionApply",
FunctionBind = "FunctionBind",
FunctionCall = "FunctionCall",
Index = "Index",
InstanceOf = "InstanceOf",
InstanceOfObject = "InstanceOfObject",
Iterator = "Iterator",
Map = "Map",
New = "New",
NewIndex = "NewIndex",
Number = "Number",
NumberIsFinite = "NumberIsFinite",
NumberIsNaN = "NumberIsNaN",
Expand Down
4 changes: 1 addition & 3 deletions src/lualib/Class.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
function __TS__Class(): LuaClass {
const c = {} as LuaClass;
c.__index = c;
c.prototype = {};
const c: LuaClass = { prototype: {} };
c.prototype.__index = c.prototype;
c.prototype.constructor = c;
return c;
Expand Down
19 changes: 19 additions & 0 deletions src/lualib/ClassExtends.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
function __TS__ClassExtends(this: void, target: LuaClass, base: LuaClass): void {
target.____super = base;

// Set base class as a metatable, because descriptors use `getmetatable` to get extended prototype
const staticMetatable: any = setmetatable({ __index: base }, base);
setmetatable(target, staticMetatable);

const baseMetatable = getmetatable(base);
if (baseMetatable) {
// Re-add metatable events defined by descriptors
if (typeof baseMetatable.__index === "function") staticMetatable.__index = baseMetatable.__index;
if (typeof baseMetatable.__newindex === "function") staticMetatable.__newindex = baseMetatable.__newindex;
}

setmetatable(target.prototype, base.prototype);
// Re-add metatable events defined by accessors with `__TS__SetDescriptor`
if (typeof base.prototype.__index === "function") target.prototype.__index = base.prototype.__index;
if (typeof base.prototype.__newindex === "function") target.prototype.__newindex = base.prototype.__newindex;
}
21 changes: 0 additions & 21 deletions src/lualib/ClassIndex.ts

This file was deleted.

17 changes: 0 additions & 17 deletions src/lualib/ClassNewIndex.ts

This file was deleted.

75 changes: 75 additions & 0 deletions src/lualib/Descriptors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
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);
if (rawResult !== undefined) {
return rawResult;
}

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

return;
}
}

metatable = getmetatable(metatable);
}
}

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) {
if (descriptor.set) {
descriptor.set.call(this, value);
}

return;
}
}

metatable = getmetatable(metatable);
}

rawset(this, key, value);
}

// It's also used directly in class transform to add descriptors to the prototype
function __TS__SetDescriptor(this: void, metatable: Metatable, prop: string, descriptor: PropertyDescriptor): void {
if (!metatable._descriptors) metatable._descriptors = {};
metatable._descriptors[prop] = descriptor;

if (descriptor.get) metatable.__index = ____descriptorIndex;
if (descriptor.set) metatable.__newindex = ____descriptorNewindex;
}

// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty
function __TS__ObjectDefineProperty<T extends object>(
this: void,
object: T,
prop: string,
descriptor: PropertyDescriptor
): T {
let metatable = getmetatable(object);
if (!metatable) {
metatable = {};
setmetatable(object, metatable);
}

__TS__SetDescriptor(metatable, prop, descriptor);
return object;
}
26 changes: 0 additions & 26 deletions src/lualib/Index.ts

This file was deleted.

2 changes: 1 addition & 1 deletion src/lualib/InstanceOf.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
function __TS__InstanceOf(this: void, obj: LuaObject, classTbl: LuaClass): boolean {
function __TS__InstanceOf(this: void, obj: LuaClassInstance, classTbl: LuaClass): boolean {
if (typeof classTbl !== "object") {
// tslint:disable-next-line: no-string-throw
throw "Right-hand side of 'instanceof' is not an object";
Expand Down
24 changes: 0 additions & 24 deletions src/lualib/NewIndex.ts

This file was deleted.

19 changes: 10 additions & 9 deletions src/lualib/declarations/tstl.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,18 @@ interface Vararg<T> extends Array<T> {}
/** @forRange */
declare function forRange(start: number, limit: number, step?: number): number[];

interface LuaClass {
prototype: LuaObject;
____super?: LuaClass;
____getters?: { [key: string]: (self: LuaClass) => any };
____setters?: { [key: string]: (self: LuaClass, val: any) => void };
interface Metatable {
_descriptors?: Record<string, PropertyDescriptor>;
__index?: any;
__newindex?: any;
}

interface LuaObject {
interface LuaClass extends Metatable {
prototype: LuaClassInstance;
[Symbol.hasInstance]?(instance: LuaClassInstance): any;
____super?: LuaClass;
}

interface LuaClassInstance extends Metatable {
constructor: LuaClass;
____getters?: { [key: string]: (self: LuaObject) => any };
____setters?: { [key: string]: (self: LuaObject, val: any) => void };
__index?: any;
}
8 changes: 8 additions & 0 deletions src/transformation/context/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,19 @@ import * as lua from "../../LuaAST";
import { unwrapVisitorResult } from "../utils/lua-ast";
import { ExpressionLikeNode, ObjectVisitor, StatementLikeNode, VisitorMap } from "./visitors";

export interface AllAccessorDeclarations {
firstAccessor: ts.AccessorDeclaration;
secondAccessor: ts.AccessorDeclaration | undefined;
getAccessor: ts.GetAccessorDeclaration | undefined;
setAccessor: ts.SetAccessorDeclaration | undefined;
}

export interface EmitResolver {
isValueAliasDeclaration(node: ts.Node): boolean;
isReferencedAliasDeclaration(node: ts.Node, checkChildren?: boolean): boolean;
isTopLevelValueImportEqualsWithEntityName(node: ts.ImportEqualsDeclaration): boolean;
moduleExportsSomeValue(moduleReferenceExpression: ts.Expression): boolean;
getAllAccessorDeclarations(declaration: ts.AccessorDeclaration): AllAccessorDeclarations;
}

export interface DiagnosticsProducingTypeChecker extends ts.TypeChecker {
Expand Down
1 change: 0 additions & 1 deletion src/transformation/utils/safe-names.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ export const luaBuiltins: ReadonlySet<string> = new Set([
"pcall",
"print",
"rawget",
"rawset",
"repeat",
"require",
"self",
Expand Down
Loading
X Tutup