Skip to content

Commit ba4ed53

Browse files
committed
feat: support get name of enum value
1 parent 5e24aaa commit ba4ed53

9 files changed

+1118
-48
lines changed

src/compiler.ts

Lines changed: 33 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1522,56 +1522,41 @@ export class Compiler extends DiagnosticEmitter {
15221522
return true;
15231523
}
15241524

1525-
private ensureEnumToString(enumElement: Enum): string | null {
1525+
private ensureEnumToString(enumElement: Enum, reportNode: Node): string | null {
15261526
if (!this.compileEnum(enumElement)) return null;
1527+
if (enumElement.is(CommonFlags.Const)) {
1528+
this.errorRelated(
1529+
DiagnosticCode.A_const_enum_member_can_only_be_accessed_using_a_string_literal,
1530+
reportNode.range, enumElement.identifierNode.range
1531+
);
1532+
return null;
1533+
}
15271534
let members = enumElement.members;
15281535
if (!members) return null; // TODO
1529-
1530-
let module = this.module;
1536+
if (enumElement.toStringFunctionName) return enumElement.toStringFunctionName;
15311537
const functionName = `${enumElement.internalName}#${CommonNames.EnumToString}`;
1532-
const isInline = enumElement.is(CommonFlags.Const) || enumElement.hasDecorator(DecoratorFlags.Inline);
1533-
let _keys = Map_keys(members), _values = Map_values(members);
1534-
if (isInline) {
1535-
let valueToNames: Map<i32, string> = new Map();
1536-
for (let i = 0, k = _keys.length; i < k; ++i) {
1537-
let name = unchecked(_keys[i]);
1538-
let member = unchecked(_values[i]);
1539-
if (member.kind != ElementKind.EnumValue) continue;
1540-
let enumValue = <EnumValue>member;
1541-
valueToNames.set(i64_low(enumValue.constantIntegerValue), name);
1542-
}
1543-
let exprs = new Array<ExpressionRef>();
1544-
for (let [value, names] of valueToNames) {
1545-
let expr = module.if(
1546-
module.binary(BinaryOp.EqI32, module.i32(value), module.local_get(0, TypeRef.I32)),
1547-
module.return(this.ensureStaticString(names))
1548-
);
1549-
exprs.push(expr);
1550-
}
1551-
exprs.push(module.unreachable());
1552-
module.addFunction(functionName, TypeRef.I32, TypeRef.I32, null, module.block(null, exprs, TypeRef.I32));
1553-
return functionName;
1554-
} else {
1555-
let internalNameToNames: Map<string, string> = new Map();
1556-
for (let i = 0, k = _keys.length; i < k; ++i) {
1557-
let name = unchecked(_keys[i]);
1558-
let member = unchecked(_values[i]);
1559-
if (member.kind != ElementKind.EnumValue) continue;
1560-
let enumValue = <EnumValue>member;
1561-
internalNameToNames.set(enumValue.internalName, name);
1562-
}
1563-
let exprs = new Array<ExpressionRef>();
1564-
for (let [internalName, names] of internalNameToNames) {
1565-
let expr = module.if(
1566-
module.binary(BinaryOp.EqI32, module.global_get(internalName, TypeRef.I32), module.local_get(0, TypeRef.I32)),
1567-
module.return(this.ensureStaticString(names))
1568-
);
1569-
exprs.push(expr);
1570-
}
1571-
exprs.push(module.unreachable());
1572-
module.addFunction(functionName, TypeRef.I32, TypeRef.I32, null, module.block(null, exprs, TypeRef.I32));
1573-
return functionName;
1538+
enumElement.toStringFunctionName = functionName;
1539+
const isInline = enumElement.hasDecorator(DecoratorFlags.Inline);
1540+
let module = this.module;
1541+
let exprs = new Array<ExpressionRef>();
1542+
// when the values are the same, TS returns the last enum value name that appears
1543+
for (let _keys = Map_keys(members), _values = Map_values(members), i = 1, k = _keys.length; i <= k; ++i) {
1544+
let enumValueName = unchecked(_keys[k - i]);
1545+
let member = unchecked(_values[k - i]);
1546+
if (member.kind != ElementKind.EnumValue) continue;
1547+
let enumValue = <EnumValue>member;
1548+
const enumValueExpr = isInline
1549+
? module.i32(i64_low(enumValue.constantIntegerValue))
1550+
: module.global_get(enumValue.internalName, TypeRef.I32);
1551+
let expr = module.if(
1552+
module.binary(BinaryOp.EqI32, enumValueExpr, module.local_get(0, TypeRef.I32)),
1553+
module.return(this.ensureStaticString(enumValueName))
1554+
);
1555+
exprs.push(expr);
15741556
}
1557+
exprs.push(module.unreachable());
1558+
module.addFunction(functionName, TypeRef.I32, TypeRef.I32, null, module.block(null, exprs, TypeRef.I32));
1559+
return functionName;
15751560
}
15761561

15771562
// === Functions ================================================================================
@@ -7147,9 +7132,10 @@ export class Compiler extends DiagnosticEmitter {
71477132
let resolver = this.resolver;
71487133
let targetElement = resolver.lookupExpression(targetExpression, this.currentFlow, Type.auto, ReportMode.Swallow);
71497134
if (targetElement && targetElement.kind == ElementKind.Enum) {
7150-
const toStringFunctionName = this.ensureEnumToString(<Enum>targetElement);
7151-
if (toStringFunctionName == null) return module.unreachable();
71527135
const elementExpr = this.compileExpression(expression.elementExpression, Type.i32, Constraints.ConvImplicit);
7136+
const toStringFunctionName = this.ensureEnumToString(<Enum>targetElement, expression);
7137+
this.currentType = this.program.stringInstance.type;
7138+
if (toStringFunctionName == null) return module.unreachable();
71537139
return module.call(toStringFunctionName, [ elementExpr ], TypeRef.I32);
71547140
}
71557141

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
export class Enum extends TypedElement {
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+
const enum CE {
2+
CE0,
3+
CE1,
4+
CE2,
5+
}
6+
7+
assert(CE[CE.CE0] === "CE0");
8+
9+
ERROR("EOF");

0 commit comments

Comments
 (0)