Skip to content

Commit 8257b1c

Browse files
authored
feat: support element access of enum (#2950)
1 parent 6087191 commit 8257b1c

File tree

10 files changed

+1141
-2
lines changed

10 files changed

+1141
-2
lines changed

‎src/common.ts‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,8 @@ export namespace CommonNames{
194194
exportconstASC_VERSION_MAJOR="ASC_VERSION_MAJOR";
195195
exportconstASC_VERSION_MINOR="ASC_VERSION_MINOR";
196196
exportconstASC_VERSION_PATCH="ASC_VERSION_PATCH";
197+
// enums
198+
exportconstEnumToString="__enum_to_string";
197199
// classes
198200
exportconstI8="I8";
199201
exportconstI16="I16";

‎src/compiler.ts‎

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1522,6 +1522,49 @@ export class Compiler extends DiagnosticEmitter{
15221522
returntrue;
15231523
}
15241524

1525+
privateensureEnumToString(enumElement: Enum,reportNode: Node): string|null{
1526+
if(enumElement.toStringFunctionName)returnenumElement.toStringFunctionName;
1527+
1528+
if(!this.compileEnum(enumElement))returnnull;
1529+
if(enumElement.is(CommonFlags.Const)){
1530+
this.errorRelated(
1531+
DiagnosticCode.A_const_enum_member_can_only_be_accessed_using_a_string_literal,
1532+
reportNode.range,enumElement.identifierNode.range
1533+
);
1534+
returnnull;
1535+
}
1536+
1537+
letmembers=enumElement.members;
1538+
if(!members)returnnull;
1539+
1540+
letmodule=this.module;
1541+
constisInline=enumElement.hasDecorator(DecoratorFlags.Inline);
1542+
1543+
constfunctionName=`${enumElement.internalName}#${CommonNames.EnumToString}`;
1544+
enumElement.toStringFunctionName=functionName;
1545+
1546+
letexprs=newArray<ExpressionRef>();
1547+
// when the values are the same, TS returns the last enum value name that appears
1548+
for(let_keys=Map_keys(members),_values=Map_values(members),i=1,k=_keys.length;i<=k;++i){
1549+
letenumValueName=unchecked(_keys[k-i]);
1550+
letmember=unchecked(_values[k-i]);
1551+
if(member.kind!=ElementKind.EnumValue)continue;
1552+
letenumValue=<EnumValue>member;
1553+
constenumValueExpr=isInline
1554+
? module.i32(i64_low(enumValue.constantIntegerValue))
1555+
: module.global_get(enumValue.internalName,TypeRef.I32);
1556+
letexpr=module.if(
1557+
module.binary(BinaryOp.EqI32,enumValueExpr,module.local_get(0,TypeRef.I32)),
1558+
module.return(this.ensureStaticString(enumValueName))
1559+
);
1560+
exprs.push(expr);
1561+
}
1562+
exprs.push(module.unreachable());
1563+
module.addFunction(functionName,TypeRef.I32,TypeRef.I32,null,module.block(null,exprs,TypeRef.I32));
1564+
1565+
returnfunctionName;
1566+
}
1567+
15251568
// === Functions ================================================================================
15261569

15271570
/** Compiles a priorly resolved function. */
@@ -7092,7 +7135,17 @@ export class Compiler extends DiagnosticEmitter{
70927135
): ExpressionRef{
70937136
letmodule=this.module;
70947137
lettargetExpression=expression.expression;
7095-
lettargetType=this.resolver.resolveExpression(targetExpression,this.currentFlow);// reports
7138+
letresolver=this.resolver;
7139+
lettargetElement=resolver.lookupExpression(targetExpression,this.currentFlow,Type.auto,ReportMode.Swallow);
7140+
if(targetElement&&targetElement.kind==ElementKind.Enum){
7141+
constelementExpr=this.compileExpression(expression.elementExpression,Type.i32,Constraints.ConvImplicit);
7142+
consttoStringFunctionName=this.ensureEnumToString(<Enum>targetElement,expression);
7143+
this.currentType=this.program.stringInstance.type;
7144+
if(toStringFunctionName==null)returnmodule.unreachable();
7145+
returnmodule.call(toStringFunctionName,[elementExpr],TypeRef.I32);
7146+
}
7147+
7148+
lettargetType=resolver.resolveExpression(targetExpression,this.currentFlow);
70967149
if(targetType){
70977150
letclassReference=targetType.getClassOrWrapper(this.program);
70987151
if(classReference){

‎src/diagnosticMessages.json‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,7 @@
174174
"Type '{0}' has no property '{1}'.": 2460,
175175
"The '{0}' operator cannot be applied to type '{1}'.": 2469,
176176
"In 'const' enum declarations member initializer must be constant expression.": 2474,
177+
"A const enum member can only be accessed using a string literal.": 2476,
177178
"Export declaration conflicts with exported declaration of '{0}'.": 2484,
178179
"'{0}' is referenced directly or indirectly in its own base expression.": 2506,
179180
"Cannot create an instance of an abstract class.": 2511,

‎src/program.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3449,7 +3449,7 @@ export class Namespace extends DeclaredElement{
34493449

34503450
/** An enum. */
34513451
exportclassEnumextendsTypedElement{
3452-
3452+
toStringFunctionName: string|null=null;
34533453
/** Constructs a new enum. */
34543454
constructor(
34553455
/** Simple name. */
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"asc_flags": [],
3+
"stderr": [
4+
"TS2476: A const enum member can only be accessed using a string literal.",
5+
"EOF"
6+
]
7+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
constenumCE{
2+
CE0,
3+
CE1,
4+
CE2,
5+
}
6+
7+
assert(CE[CE.CE0]==="CE0");
8+
9+
ERROR("EOF");

0 commit comments

Comments
(0)