X Tutup
Skip to content

Vararg Update#992

Merged
Perryvw merged 7 commits intoTypeScriptToLua:masterfrom
tomblind:vararg-extension
Mar 6, 2021
Merged

Vararg Update#992
Perryvw merged 7 commits intoTypeScriptToLua:masterfrom
tomblind:vararg-extension

Conversation

@tomblind
Copy link
Collaborator

This PR:

  • Deprecates @vararg annotation
  • Adds automatic vararg optimization, removing the need for a similar language extension
  • Adds $vararg constant language extension for accessing the global ellipsis operator

Vararg optimization will occur in any case that's viable. It will NOT be optimized:

  • Inside of IIFEs generated by TSTL (assignment expressions, etc...)
  • After the vararg's identifier has been accessed directly (since the array may have been modified)
  • If the vararg's identifier is accessed between the use and definition of a function that will be hoisted (the function may modify the array)

closes #963

@tomblind tomblind requested review from Perryvw and lolleko February 28, 2021 18:45
Copy link
Member

@Perryvw Perryvw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review to be continued

}

markSymbolAsReferencedInCurrentScopes(context, symbolId, identifier);
if (!isOptimizableVarArgSpread(context, symbol, identifier)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add a comment explaining why in this case this is not needed?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm sorry but I still don't understand this. Could you explain it from the perspective of this trackSymbolReference? Why do we not track this as a reference if the identifier is a vararg? Isn't that a reference?

}
// Check for scopes which would stop optimization
const scope = findScope(context, ScopeType.Function | ScopeType.Try | ScopeType.Catch | ScopeType.File);
if (scope?.type === ScopeType.File && isExtensionValue(context, symbol, ExtensionKind.VarargConstant)) {
Copy link
Member

@lolleko lolleko Mar 1, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This if statement and the next one (L26) are really hard to read, maybe they can be refactored into an extra function? similar to line 30/37/41

export function transformCompoundAssignmentExpression(
context: TransformationContext,
expression: ts.Expression,
// TODO: Change type to ts.LeftHandSideExpression?
Copy link
Member

@lolleko lolleko Mar 1, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this a question or a todo? Either way I think we should do a separate PR where we address all the TS deprecation issues. IRRC we have a few places where we use old API.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should I remove this? It was just copied over from where the code was previously.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, in that case just keep it. Until we figure out what exactly was meant by this.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah keep it for now.

return false;
}
// Identifier must be a var arg in the local function scope
function isSpreadParameter(p: ts.ParameterDeclaration) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure what out policy is about inline functions is, but personally I prefer arrow functions for that.

}
return test("a", "b", "c");
`
.expectLuaToMatchSnapshot()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not really sure, if we need expectLuaToMatchSnapshot and expectToMatchJsResult. I think if the behaviour of JS and Lua matches, it does not matter what Lua output looks like.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could argue in this case it does matter, since the current unoptimized lua also works, but is not exactly what we want. So we should either expect it to match a snapshot (I'm fine with this), or somehow assert the resulting code indeed has the optimizations.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good Point. I think checking if the generated code does not contain unpack could be more explicit. But I am also fine with using a snapshot.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, this was my thinking - we need to test that the optimization occurs and that it functions correctly. Testing for unpack feels a bit messy. If something breaks it will be clearly visible in the snapshot.

}
return test("a", "b", "c");
`
.expectLuaToMatchSnapshot()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could argue in this case it does matter, since the current unoptimized lua also works, but is not exactly what we want. So we should either expect it to match a snapshot (I'm fine with this), or somehow assert the resulting code indeed has the optimizations.

Copy link
Member

@Perryvw Perryvw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add the following test case to your spread tests:

function foo(...args: string[]) {
    return args;
}

This should be optimized right?

Other than that looks good to me.

}

markSymbolAsReferencedInCurrentScopes(context, symbolId, identifier);
if (!isOptimizableVarArgSpread(context, symbol, identifier)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm sorry but I still don't understand this. Could you explain it from the perspective of this trackSymbolReference? Why do we not track this as a reference if the identifier is a vararg? Isn't that a reference?

@tomblind
Copy link
Collaborator Author

tomblind commented Mar 5, 2021

@Perryvw That won't be optimized. The function returns an array so the vararg needs to be wrapped. The only way to do what you're thinking there is using $multi (which there is a test for).

@Perryvw Perryvw merged commit 0d22c0d into TypeScriptToLua:master Mar 6, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Move annotations to language extensions where possible

3 participants

X Tutup