Skip to content

Commit 01e5f81

Browse files
committed
Merge branch 'master' into ts-6b
2 parents bebb3c7 + 843544d commit 01e5f81

24 files changed

Lines changed: 850 additions & 101 deletions

src/lualib/ObjectAssign.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22
export function __TS__ObjectAssign<T extends object>(this: void, target: T, ...sources: T[]): T {
33
for (const i of $range(1, sources.length)) {
44
const source = sources[i - 1];
5-
for (const key in source) {
6-
target[key] = source[key];
5+
if (type(source) === "table") {
6+
for (const key in source) {
7+
target[key] = source[key];
8+
}
79
}
810
}
911

src/transformation/builtins/math.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,14 +42,20 @@ export function transformMathCall(
4242

4343
const expressionName = calledMethod.name.text;
4444
switch (expressionName) {
45-
// Lua 5.3: math.atan(y, x)
45+
// Lua 5.3+: math.atan(y, x)
4646
// Otherwise: math.atan2(y, x)
4747
case "atan2": {
4848
if (context.luaTarget === LuaTarget.Universal) {
4949
return transformLuaLibFunction(context, LuaLibFeature.MathAtan2, node, ...params);
5050
}
5151

52-
const method = lua.createStringLiteral(context.luaTarget === LuaTarget.Lua53 ? "atan" : "atan2");
52+
const useAtan2 =
53+
context.luaTarget === LuaTarget.Lua50 ||
54+
context.luaTarget === LuaTarget.Lua51 ||
55+
context.luaTarget === LuaTarget.Lua52 ||
56+
context.luaTarget === LuaTarget.LuaJIT ||
57+
context.luaTarget === LuaTarget.Luau;
58+
const method = lua.createStringLiteral(useAtan2 ? "atan2" : "atan");
5359
return lua.createCallExpression(lua.createTableIndexExpression(math, method), params, node);
5460
}
5561

src/transformation/builtins/number.ts

Lines changed: 43 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -77,31 +77,62 @@ export function transformNumberProperty(
7777
node
7878
);
7979
case "MIN_VALUE":
80+
// 2 ^ -1074 = 5e-324 (smallest positive double)
8081
return lua.createBinaryExpression(
81-
lua.createNumericLiteral(-2),
82-
lua.createNumericLiteral(1074),
82+
lua.createNumericLiteral(2),
83+
lua.createNumericLiteral(-1074),
8384
lua.SyntaxKind.PowerOperator,
8485
node
8586
);
8687
case "MIN_SAFE_INTEGER":
87-
return lua.createBinaryExpression(
88-
lua.createNumericLiteral(-2),
89-
lua.createNumericLiteral(1074),
90-
lua.SyntaxKind.PowerOperator,
88+
// -(2 ^ 53 - 1) = -9007199254740991
89+
return lua.createUnaryExpression(
90+
lua.createParenthesizedExpression(
91+
lua.createBinaryExpression(
92+
lua.createBinaryExpression(
93+
lua.createNumericLiteral(2),
94+
lua.createNumericLiteral(53),
95+
lua.SyntaxKind.PowerOperator
96+
),
97+
lua.createNumericLiteral(1),
98+
lua.SyntaxKind.SubtractionOperator
99+
)
100+
),
101+
lua.SyntaxKind.NegationOperator,
91102
node
92103
);
93104
case "MAX_SAFE_INTEGER":
105+
// 2 ^ 53 - 1 = 9007199254740991
94106
return lua.createBinaryExpression(
95-
lua.createNumericLiteral(2),
96-
lua.createNumericLiteral(1024),
97-
lua.SyntaxKind.PowerOperator,
107+
lua.createBinaryExpression(
108+
lua.createNumericLiteral(2),
109+
lua.createNumericLiteral(53),
110+
lua.SyntaxKind.PowerOperator
111+
),
112+
lua.createNumericLiteral(1),
113+
lua.SyntaxKind.SubtractionOperator,
98114
node
99115
);
100116
case "MAX_VALUE":
117+
// (2 - 2 ^ -52) * 2 ^ 1023 = 1.7976931348623157e+308
101118
return lua.createBinaryExpression(
102-
lua.createNumericLiteral(2),
103-
lua.createNumericLiteral(1024),
104-
lua.SyntaxKind.PowerOperator,
119+
lua.createParenthesizedExpression(
120+
lua.createBinaryExpression(
121+
lua.createNumericLiteral(2),
122+
lua.createBinaryExpression(
123+
lua.createNumericLiteral(2),
124+
lua.createNumericLiteral(-52),
125+
lua.SyntaxKind.PowerOperator
126+
),
127+
lua.SyntaxKind.SubtractionOperator
128+
)
129+
),
130+
lua.createBinaryExpression(
131+
lua.createNumericLiteral(2),
132+
lua.createNumericLiteral(1023),
133+
lua.SyntaxKind.PowerOperator
134+
),
135+
lua.SyntaxKind.MultiplicationOperator,
105136
node
106137
);
107138

src/transformation/utils/scope.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ export interface Scope {
3838
importStatements?: lua.Statement[];
3939
loopContinued?: LoopContinued;
4040
functionReturned?: boolean;
41+
asyncTryHasReturn?: boolean;
42+
asyncTryHasBreak?: boolean;
43+
asyncTryHasContinue?: LoopContinued;
4144
}
4245

4346
export interface HoistingResult {
@@ -84,6 +87,23 @@ export function findScope(context: TransformationContext, scopeTypes: ScopeType)
8487
}
8588
}
8689

90+
export function findAsyncTryScopeInStack(context: TransformationContext): Scope | undefined {
91+
for (const scope of walkScopesUp(context)) {
92+
if (scope.type === ScopeType.Function) return undefined;
93+
if (scope.type === ScopeType.Try || scope.type === ScopeType.Catch) return scope;
94+
}
95+
return undefined;
96+
}
97+
98+
/** Like findAsyncTryScopeInStack, but also stops at Loop boundaries. */
99+
export function findAsyncTryScopeBeforeLoop(context: TransformationContext): Scope | undefined {
100+
for (const scope of walkScopesUp(context)) {
101+
if (scope.type === ScopeType.Function || scope.type === ScopeType.Loop) return undefined;
102+
if (scope.type === ScopeType.Try || scope.type === ScopeType.Catch) return scope;
103+
}
104+
return undefined;
105+
}
106+
87107
export function addScopeVariableDeclaration(scope: Scope, declaration: lua.VariableDeclarationStatement) {
88108
scope.variableDeclarations ??= [];
89109

src/transformation/utils/typescript/index.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,14 @@ export function hasExportEquals(sourceFile: ts.SourceFile): boolean {
1414
* Search up until finding a node satisfying the callback
1515
*/
1616
export function findFirstNodeAbove<T extends ts.Node>(node: ts.Node, callback: (n: ts.Node) => n is T): T | undefined {
17-
let current = node;
17+
// Synthetic nodes (created by pre-transformers like usingTransformer) may have an unset .parent.
18+
// Fall back to ts.getOriginalNode so we can still walk the source-parsed parent chain.
19+
let current = ts.getOriginalNode(node);
1820
while (current.parent) {
1921
if (callback(current.parent)) {
2022
return current.parent;
2123
} else {
22-
current = current.parent;
24+
current = ts.getOriginalNode(current.parent);
2325
}
2426
}
2527
}

src/transformation/visitors/binary-expression/bit.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,22 @@ export function transformBinaryBitOperation(
7575
case LuaTarget.Lua52:
7676
return transformBinaryBitLibOperation(node, left, right, operator, "bit32");
7777
default:
78+
// Lua 5.3+ `>>` is arithmetic (sign-extending), but TS `>>>` is logical (zero-fill).
79+
// Emit `(left & 0xFFFFFFFF) >> right` to convert to unsigned 32-bit first.
80+
if (operator === ts.SyntaxKind.GreaterThanGreaterThanGreaterThanToken) {
81+
const mask = lua.createBinaryExpression(
82+
left,
83+
lua.createNumericLiteral(0xffffffff, node),
84+
lua.SyntaxKind.BitwiseAndOperator,
85+
node
86+
);
87+
return lua.createBinaryExpression(
88+
lua.createParenthesizedExpression(mask, node),
89+
right,
90+
lua.SyntaxKind.BitwiseRightShiftOperator,
91+
node
92+
);
93+
}
7894
const luaOperator = transformBitOperatorToLuaOperator(context, node, operator);
7995
return lua.createBinaryExpression(left, right, luaOperator, node);
8096
}

src/transformation/visitors/break-continue.ts

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,22 @@ import * as ts from "typescript";
22
import { LuaTarget } from "../../CompilerOptions";
33
import * as lua from "../../LuaAST";
44
import { FunctionVisitor } from "../context";
5-
import { findScope, LoopContinued, ScopeType } from "../utils/scope";
5+
import { findAsyncTryScopeBeforeLoop, findScope, LoopContinued, ScopeType } from "../utils/scope";
6+
import { isInAsyncFunction } from "../utils/typescript";
67

78
export const transformBreakStatement: FunctionVisitor<ts.BreakStatement> = (breakStatement, context) => {
8-
void context;
9+
const tryScope = isInAsyncFunction(breakStatement) ? findAsyncTryScopeBeforeLoop(context) : undefined;
10+
if (tryScope) {
11+
tryScope.asyncTryHasBreak = true;
12+
return [
13+
lua.createAssignmentStatement(
14+
lua.createIdentifier("____hasBroken"),
15+
lua.createBooleanLiteral(true),
16+
breakStatement
17+
),
18+
lua.createReturnStatement([], breakStatement),
19+
];
20+
}
921
return lua.createBreakStatement(breakStatement);
1022
};
1123

@@ -28,6 +40,19 @@ export const transformContinueStatement: FunctionVisitor<ts.ContinueStatement> =
2840
scope.loopContinued = continuedWith;
2941
}
3042

43+
const tryScope = isInAsyncFunction(statement) ? findAsyncTryScopeBeforeLoop(context) : undefined;
44+
if (tryScope) {
45+
tryScope.asyncTryHasContinue = continuedWith;
46+
return [
47+
lua.createAssignmentStatement(
48+
lua.createIdentifier("____hasContinued"),
49+
lua.createBooleanLiteral(true),
50+
statement
51+
),
52+
lua.createReturnStatement([], statement),
53+
];
54+
}
55+
3156
const label = `__continue${scope?.id ?? ""}`;
3257

3358
switch (continuedWith) {

0 commit comments

Comments
 (0)