From 5e24aaa1a9c4959b943e32139dea4cb992b83f10 Mon Sep 17 00:00:00 2001 From: Congcong Cai Date: Wed, 24 Sep 2025 00:13:21 +0800 Subject: [PATCH 1/3] wip --- src/common.ts | 2 ++ src/compiler.ts | 63 ++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 64 insertions(+), 1 deletion(-) diff --git a/src/common.ts b/src/common.ts index f5d6675edd..3cc25a0845 100644 --- a/src/common.ts +++ b/src/common.ts @@ -194,6 +194,8 @@ export namespace CommonNames { export const ASC_VERSION_MAJOR = "ASC_VERSION_MAJOR"; export const ASC_VERSION_MINOR = "ASC_VERSION_MINOR"; export const ASC_VERSION_PATCH = "ASC_VERSION_PATCH"; + // enums + export const EnumToString = "__enum_to_string"; // classes export const I8 = "I8"; export const I16 = "I16"; diff --git a/src/compiler.ts b/src/compiler.ts index 04306fba01..accbf6e64f 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -1522,6 +1522,58 @@ export class Compiler extends DiagnosticEmitter { return true; } + private ensureEnumToString(enumElement: Enum): string | null { + if (!this.compileEnum(enumElement)) return null; + let members = enumElement.members; + if (!members) return null; // TODO + + let module = this.module; + const functionName = `${enumElement.internalName}#${CommonNames.EnumToString}`; + const isInline = enumElement.is(CommonFlags.Const) || enumElement.hasDecorator(DecoratorFlags.Inline); + let _keys = Map_keys(members), _values = Map_values(members); + if (isInline) { + let valueToNames: Map = new Map(); + for (let i = 0, k = _keys.length; i < k; ++i) { + let name = unchecked(_keys[i]); + let member = unchecked(_values[i]); + if (member.kind != ElementKind.EnumValue) continue; + let enumValue = member; + valueToNames.set(i64_low(enumValue.constantIntegerValue), name); + } + let exprs = new Array(); + for (let [value, names] of valueToNames) { + let expr = module.if( + module.binary(BinaryOp.EqI32, module.i32(value), module.local_get(0, TypeRef.I32)), + module.return(this.ensureStaticString(names)) + ); + exprs.push(expr); + } + exprs.push(module.unreachable()); + module.addFunction(functionName, TypeRef.I32, TypeRef.I32, null, module.block(null, exprs, TypeRef.I32)); + return functionName; + } else { + let internalNameToNames: Map = new Map(); + for (let i = 0, k = _keys.length; i < k; ++i) { + let name = unchecked(_keys[i]); + let member = unchecked(_values[i]); + if (member.kind != ElementKind.EnumValue) continue; + let enumValue = member; + internalNameToNames.set(enumValue.internalName, name); + } + let exprs = new Array(); + for (let [internalName, names] of internalNameToNames) { + let expr = module.if( + module.binary(BinaryOp.EqI32, module.global_get(internalName, TypeRef.I32), module.local_get(0, TypeRef.I32)), + module.return(this.ensureStaticString(names)) + ); + exprs.push(expr); + } + exprs.push(module.unreachable()); + module.addFunction(functionName, TypeRef.I32, TypeRef.I32, null, module.block(null, exprs, TypeRef.I32)); + return functionName; + } + } + // === Functions ================================================================================ /** Compiles a priorly resolved function. */ @@ -7092,7 +7144,16 @@ export class Compiler extends DiagnosticEmitter { ): ExpressionRef { let module = this.module; let targetExpression = expression.expression; - let targetType = this.resolver.resolveExpression(targetExpression, this.currentFlow); // reports + let resolver = this.resolver; + let targetElement = resolver.lookupExpression(targetExpression, this.currentFlow, Type.auto, ReportMode.Swallow); + if (targetElement && targetElement.kind == ElementKind.Enum) { + const toStringFunctionName = this.ensureEnumToString(targetElement); + if (toStringFunctionName == null) return module.unreachable(); + const elementExpr = this.compileExpression(expression.elementExpression, Type.i32, Constraints.ConvImplicit); + return module.call(toStringFunctionName, [ elementExpr ], TypeRef.I32); + } + + let targetType = resolver.resolveExpression(targetExpression, this.currentFlow); if (targetType) { let classReference = targetType.getClassOrWrapper(this.program); if (classReference) { From ba4ed53078490fab8815fbf6eec9968785a8d4eb Mon Sep 17 00:00:00 2001 From: Congcong Cai Date: Thu, 25 Sep 2025 13:44:17 +0800 Subject: [PATCH 2/3] feat: support get name of enum value --- src/compiler.ts | 80 ++- src/diagnosticMessages.json | 1 + src/program.ts | 2 +- tests/compiler/enum-to-string-error.json | 7 + tests/compiler/enum-to-string-error.ts | 9 + tests/compiler/enum-to-string.debug.wat | 613 ++++++++++++++++++++++ tests/compiler/enum-to-string.json | 3 + tests/compiler/enum-to-string.release.wat | 408 ++++++++++++++ tests/compiler/enum-to-string.ts | 43 ++ 9 files changed, 1118 insertions(+), 48 deletions(-) create mode 100644 tests/compiler/enum-to-string-error.json create mode 100644 tests/compiler/enum-to-string-error.ts create mode 100644 tests/compiler/enum-to-string.debug.wat create mode 100644 tests/compiler/enum-to-string.json create mode 100644 tests/compiler/enum-to-string.release.wat create mode 100644 tests/compiler/enum-to-string.ts diff --git a/src/compiler.ts b/src/compiler.ts index accbf6e64f..b4aae6ecd3 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -1522,56 +1522,41 @@ export class Compiler extends DiagnosticEmitter { return true; } - private ensureEnumToString(enumElement: Enum): string | null { + private ensureEnumToString(enumElement: Enum, reportNode: Node): string | null { if (!this.compileEnum(enumElement)) return null; + if (enumElement.is(CommonFlags.Const)) { + this.errorRelated( + DiagnosticCode.A_const_enum_member_can_only_be_accessed_using_a_string_literal, + reportNode.range, enumElement.identifierNode.range + ); + return null; + } let members = enumElement.members; if (!members) return null; // TODO - - let module = this.module; + if (enumElement.toStringFunctionName) return enumElement.toStringFunctionName; const functionName = `${enumElement.internalName}#${CommonNames.EnumToString}`; - const isInline = enumElement.is(CommonFlags.Const) || enumElement.hasDecorator(DecoratorFlags.Inline); - let _keys = Map_keys(members), _values = Map_values(members); - if (isInline) { - let valueToNames: Map = new Map(); - for (let i = 0, k = _keys.length; i < k; ++i) { - let name = unchecked(_keys[i]); - let member = unchecked(_values[i]); - if (member.kind != ElementKind.EnumValue) continue; - let enumValue = member; - valueToNames.set(i64_low(enumValue.constantIntegerValue), name); - } - let exprs = new Array(); - for (let [value, names] of valueToNames) { - let expr = module.if( - module.binary(BinaryOp.EqI32, module.i32(value), module.local_get(0, TypeRef.I32)), - module.return(this.ensureStaticString(names)) - ); - exprs.push(expr); - } - exprs.push(module.unreachable()); - module.addFunction(functionName, TypeRef.I32, TypeRef.I32, null, module.block(null, exprs, TypeRef.I32)); - return functionName; - } else { - let internalNameToNames: Map = new Map(); - for (let i = 0, k = _keys.length; i < k; ++i) { - let name = unchecked(_keys[i]); - let member = unchecked(_values[i]); - if (member.kind != ElementKind.EnumValue) continue; - let enumValue = member; - internalNameToNames.set(enumValue.internalName, name); - } - let exprs = new Array(); - for (let [internalName, names] of internalNameToNames) { - let expr = module.if( - module.binary(BinaryOp.EqI32, module.global_get(internalName, TypeRef.I32), module.local_get(0, TypeRef.I32)), - module.return(this.ensureStaticString(names)) - ); - exprs.push(expr); - } - exprs.push(module.unreachable()); - module.addFunction(functionName, TypeRef.I32, TypeRef.I32, null, module.block(null, exprs, TypeRef.I32)); - return functionName; + enumElement.toStringFunctionName = functionName; + const isInline = enumElement.hasDecorator(DecoratorFlags.Inline); + let module = this.module; + let exprs = new Array(); + // when the values are the same, TS returns the last enum value name that appears + for (let _keys = Map_keys(members), _values = Map_values(members), i = 1, k = _keys.length; i <= k; ++i) { + let enumValueName = unchecked(_keys[k - i]); + let member = unchecked(_values[k - i]); + if (member.kind != ElementKind.EnumValue) continue; + let enumValue = member; + const enumValueExpr = isInline + ? module.i32(i64_low(enumValue.constantIntegerValue)) + : module.global_get(enumValue.internalName, TypeRef.I32); + let expr = module.if( + module.binary(BinaryOp.EqI32, enumValueExpr, module.local_get(0, TypeRef.I32)), + module.return(this.ensureStaticString(enumValueName)) + ); + exprs.push(expr); } + exprs.push(module.unreachable()); + module.addFunction(functionName, TypeRef.I32, TypeRef.I32, null, module.block(null, exprs, TypeRef.I32)); + return functionName; } // === Functions ================================================================================ @@ -7147,9 +7132,10 @@ export class Compiler extends DiagnosticEmitter { let resolver = this.resolver; let targetElement = resolver.lookupExpression(targetExpression, this.currentFlow, Type.auto, ReportMode.Swallow); if (targetElement && targetElement.kind == ElementKind.Enum) { - const toStringFunctionName = this.ensureEnumToString(targetElement); - if (toStringFunctionName == null) return module.unreachable(); const elementExpr = this.compileExpression(expression.elementExpression, Type.i32, Constraints.ConvImplicit); + const toStringFunctionName = this.ensureEnumToString(targetElement, expression); + this.currentType = this.program.stringInstance.type; + if (toStringFunctionName == null) return module.unreachable(); return module.call(toStringFunctionName, [ elementExpr ], TypeRef.I32); } diff --git a/src/diagnosticMessages.json b/src/diagnosticMessages.json index 5b0249f9b2..bd12f1f7e3 100644 --- a/src/diagnosticMessages.json +++ b/src/diagnosticMessages.json @@ -174,6 +174,7 @@ "Type '{0}' has no property '{1}'.": 2460, "The '{0}' operator cannot be applied to type '{1}'.": 2469, "In 'const' enum declarations member initializer must be constant expression.": 2474, + "A const enum member can only be accessed using a string literal.": 2476, "Export declaration conflicts with exported declaration of '{0}'.": 2484, "'{0}' is referenced directly or indirectly in its own base expression.": 2506, "Cannot create an instance of an abstract class.": 2511, diff --git a/src/program.ts b/src/program.ts index bf8dcc433c..8bcf1bfa19 100644 --- a/src/program.ts +++ b/src/program.ts @@ -3449,7 +3449,7 @@ export class Namespace extends DeclaredElement { /** An enum. */ export class Enum extends TypedElement { - + toStringFunctionName: string | null = null; /** Constructs a new enum. */ constructor( /** Simple name. */ diff --git a/tests/compiler/enum-to-string-error.json b/tests/compiler/enum-to-string-error.json new file mode 100644 index 0000000000..1165029fc1 --- /dev/null +++ b/tests/compiler/enum-to-string-error.json @@ -0,0 +1,7 @@ +{ + "asc_flags": [], + "stderr": [ + "TS2476: A const enum member can only be accessed using a string literal.", + "EOF" + ] +} diff --git a/tests/compiler/enum-to-string-error.ts b/tests/compiler/enum-to-string-error.ts new file mode 100644 index 0000000000..cbb58deb38 --- /dev/null +++ b/tests/compiler/enum-to-string-error.ts @@ -0,0 +1,9 @@ +const enum CE { + CE0, + CE1, + CE2, +} + +assert(CE[CE.CE0] === "CE0"); + +ERROR("EOF"); \ No newline at end of file diff --git a/tests/compiler/enum-to-string.debug.wat b/tests/compiler/enum-to-string.debug.wat new file mode 100644 index 0000000000..0f60906aa8 --- /dev/null +++ b/tests/compiler/enum-to-string.debug.wat @@ -0,0 +1,613 @@ +(module + (type $0 (func (param i32) (result i32))) + (type $1 (func)) + (type $2 (func (param i32 i32 i32 i32 i32) (result i32))) + (type $3 (func (param i32 i32 i32 i32))) + (type $4 (func (param i32 i32) (result i32))) + (import "env" "abort" (func $~lib/builtins/abort (param i32 i32 i32 i32))) + (global $enum-to-string/Enum.E0 i32 (i32.const 0)) + (global $enum-to-string/Enum.E1 i32 (i32.const 1)) + (global $enum-to-string/Enum.E2 i32 (i32.const 2)) + (global $~lib/shared/runtime/Runtime.Stub i32 (i32.const 0)) + (global $~lib/shared/runtime/Runtime.Minimal i32 (i32.const 1)) + (global $~lib/shared/runtime/Runtime.Incremental i32 (i32.const 2)) + (global $~lib/native/ASC_SHRINK_LEVEL i32 (i32.const 0)) + (global $enum-to-string/v (mut i32) (i32.const 0)) + (global $enum-to-string/EnumWithInit.E0 i32 (i32.const 1)) + (global $enum-to-string/EnumWithInit.E1 i32 (i32.const 2)) + (global $enum-to-string/EnumWithInit.E2 i32 (i32.const 4)) + (global $enum-to-string/EnumWithDup.E0 i32 (i32.const 1)) + (global $enum-to-string/EnumWithDup.E1 i32 (i32.const 2)) + (global $enum-to-string/EnumWithDup.E2 i32 (i32.const 1)) + (global $~lib/memory/__data_end i32 (i32.const 268)) + (global $~lib/memory/__stack_pointer (mut i32) (i32.const 33036)) + (global $~lib/memory/__heap_base i32 (i32.const 33036)) + (memory $0 1) + (data $0 (i32.const 12) "\1c\00\00\00\00\00\00\00\00\00\00\00\02\00\00\00\04\00\00\00E\002\00\00\00\00\00\00\00\00\00") + (data $1 (i32.const 44) "\1c\00\00\00\00\00\00\00\00\00\00\00\02\00\00\00\04\00\00\00E\001\00\00\00\00\00\00\00\00\00") + (data $2 (i32.const 76) "\1c\00\00\00\00\00\00\00\00\00\00\00\02\00\00\00\04\00\00\00E\000\00\00\00\00\00\00\00\00\00") + (data $3 (i32.const 108) "<\00\00\00\00\00\00\00\00\00\00\00\02\00\00\00\"\00\00\00e\00n\00u\00m\00-\00t\00o\00-\00s\00t\00r\00i\00n\00g\00.\00t\00s\00\00\00\00\00\00\00\00\00\00\00") + (data $4 (i32.const 172) "\1c\00\00\00\00\00\00\00\00\00\00\00\02\00\00\00\06\00\00\00C\00E\002\00\00\00\00\00\00\00") + (data $5 (i32.const 204) "\1c\00\00\00\00\00\00\00\00\00\00\00\02\00\00\00\06\00\00\00C\00E\001\00\00\00\00\00\00\00") + (data $6 (i32.const 236) "\1c\00\00\00\00\00\00\00\00\00\00\00\02\00\00\00\06\00\00\00C\00E\000\00\00\00\00\00\00\00") + (table $0 1 1 funcref) + (elem $0 (i32.const 1)) + (export "memory" (memory $0)) + (start $~start) + (func $enum-to-string/Enum#__enum_to_string (param $0 i32) (result i32) + global.get $enum-to-string/Enum.E2 + local.get $0 + i32.eq + if + i32.const 32 + return + end + global.get $enum-to-string/Enum.E1 + local.get $0 + i32.eq + if + i32.const 64 + return + end + global.get $enum-to-string/Enum.E0 + local.get $0 + i32.eq + if + i32.const 96 + return + end + unreachable + ) + (func $~lib/rt/common/OBJECT#get:rtSize (param $this i32) (result i32) + local.get $this + i32.load offset=16 + ) + (func $~lib/string/String#get:length (param $this i32) (result i32) + local.get $this + i32.const 20 + i32.sub + call $~lib/rt/common/OBJECT#get:rtSize + i32.const 1 + i32.shr_u + return + ) + (func $~lib/util/string/compareImpl (param $str1 i32) (param $index1 i32) (param $str2 i32) (param $index2 i32) (param $len i32) (result i32) + (local $ptr1 i32) + (local $ptr2 i32) + (local $7 i32) + (local $a i32) + (local $b i32) + local.get $str1 + local.get $index1 + i32.const 1 + i32.shl + i32.add + local.set $ptr1 + local.get $str2 + local.get $index2 + i32.const 1 + i32.shl + i32.add + local.set $ptr2 + i32.const 0 + i32.const 2 + i32.lt_s + drop + local.get $len + i32.const 4 + i32.ge_u + if (result i32) + local.get $ptr1 + i32.const 7 + i32.and + local.get $ptr2 + i32.const 7 + i32.and + i32.or + i32.eqz + else + i32.const 0 + end + if + block $do-break|0 + loop $do-loop|0 + local.get $ptr1 + i64.load + local.get $ptr2 + i64.load + i64.ne + if + br $do-break|0 + end + local.get $ptr1 + i32.const 8 + i32.add + local.set $ptr1 + local.get $ptr2 + i32.const 8 + i32.add + local.set $ptr2 + local.get $len + i32.const 4 + i32.sub + local.set $len + local.get $len + i32.const 4 + i32.ge_u + br_if $do-loop|0 + end + end + end + loop $while-continue|1 + local.get $len + local.tee $7 + i32.const 1 + i32.sub + local.set $len + local.get $7 + if + local.get $ptr1 + i32.load16_u + local.set $a + local.get $ptr2 + i32.load16_u + local.set $b + local.get $a + local.get $b + i32.ne + if + local.get $a + local.get $b + i32.sub + return + end + local.get $ptr1 + i32.const 2 + i32.add + local.set $ptr1 + local.get $ptr2 + i32.const 2 + i32.add + local.set $ptr2 + br $while-continue|1 + end + end + i32.const 0 + return + ) + (func $enum-to-string/InlineEnum#__enum_to_string (param $0 i32) (result i32) + i32.const 2 + local.get $0 + i32.eq + if + i32.const 192 + return + end + i32.const 1 + local.get $0 + i32.eq + if + i32.const 224 + return + end + i32.const 0 + local.get $0 + i32.eq + if + i32.const 256 + return + end + unreachable + ) + (func $enum-to-string/EnumWithInit#__enum_to_string (param $0 i32) (result i32) + global.get $enum-to-string/EnumWithInit.E2 + local.get $0 + i32.eq + if + i32.const 32 + return + end + global.get $enum-to-string/EnumWithInit.E1 + local.get $0 + i32.eq + if + i32.const 64 + return + end + global.get $enum-to-string/EnumWithInit.E0 + local.get $0 + i32.eq + if + i32.const 96 + return + end + unreachable + ) + (func $enum-to-string/EnumWithDup#__enum_to_string (param $0 i32) (result i32) + global.get $enum-to-string/EnumWithDup.E2 + local.get $0 + i32.eq + if + i32.const 32 + return + end + global.get $enum-to-string/EnumWithDup.E1 + local.get $0 + i32.eq + if + i32.const 64 + return + end + global.get $enum-to-string/EnumWithDup.E0 + local.get $0 + i32.eq + if + i32.const 96 + return + end + unreachable + ) + (func $~start + call $start:enum-to-string + ) + (func $~stack_check + global.get $~lib/memory/__stack_pointer + global.get $~lib/memory/__data_end + i32.lt_s + if + i32.const 33056 + i32.const 33104 + i32.const 1 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + ) + (func $~lib/string/String.__eq (param $left i32) (param $right i32) (result i32) + (local $leftLength i32) + (local $3 i32) + global.get $~lib/memory/__stack_pointer + i32.const 8 + i32.sub + global.set $~lib/memory/__stack_pointer + call $~stack_check + global.get $~lib/memory/__stack_pointer + i64.const 0 + i64.store + local.get $left + local.get $right + i32.eq + if + i32.const 1 + local.set $3 + global.get $~lib/memory/__stack_pointer + i32.const 8 + i32.add + global.set $~lib/memory/__stack_pointer + local.get $3 + return + end + local.get $left + i32.const 0 + i32.eq + if (result i32) + i32.const 1 + else + local.get $right + i32.const 0 + i32.eq + end + if + i32.const 0 + local.set $3 + global.get $~lib/memory/__stack_pointer + i32.const 8 + i32.add + global.set $~lib/memory/__stack_pointer + local.get $3 + return + end + local.get $left + local.set $3 + global.get $~lib/memory/__stack_pointer + local.get $3 + i32.store + local.get $3 + call $~lib/string/String#get:length + local.set $leftLength + local.get $leftLength + local.get $right + local.set $3 + global.get $~lib/memory/__stack_pointer + local.get $3 + i32.store + local.get $3 + call $~lib/string/String#get:length + i32.ne + if + i32.const 0 + local.set $3 + global.get $~lib/memory/__stack_pointer + i32.const 8 + i32.add + global.set $~lib/memory/__stack_pointer + local.get $3 + return + end + local.get $left + local.set $3 + global.get $~lib/memory/__stack_pointer + local.get $3 + i32.store + local.get $3 + i32.const 0 + local.get $right + local.set $3 + global.get $~lib/memory/__stack_pointer + local.get $3 + i32.store offset=4 + local.get $3 + i32.const 0 + local.get $leftLength + call $~lib/util/string/compareImpl + i32.eqz + local.set $3 + global.get $~lib/memory/__stack_pointer + i32.const 8 + i32.add + global.set $~lib/memory/__stack_pointer + local.get $3 + return + ) + (func $start:enum-to-string + (local $0 i32) + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.sub + global.set $~lib/memory/__stack_pointer + call $~stack_check + global.get $~lib/memory/__stack_pointer + i32.const 0 + i32.store + global.get $enum-to-string/Enum.E0 + call $enum-to-string/Enum#__enum_to_string + local.set $0 + global.get $~lib/memory/__stack_pointer + local.get $0 + i32.store + local.get $0 + i32.const 96 + call $~lib/string/String.__eq + i32.eqz + if + i32.const 0 + i32.const 128 + i32.const 7 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + global.get $enum-to-string/Enum.E1 + call $enum-to-string/Enum#__enum_to_string + local.set $0 + global.get $~lib/memory/__stack_pointer + local.get $0 + i32.store + local.get $0 + i32.const 64 + call $~lib/string/String.__eq + i32.eqz + if + i32.const 0 + i32.const 128 + i32.const 8 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + global.get $enum-to-string/Enum.E2 + call $enum-to-string/Enum#__enum_to_string + local.set $0 + global.get $~lib/memory/__stack_pointer + local.get $0 + i32.store + local.get $0 + i32.const 32 + call $~lib/string/String.__eq + i32.eqz + if + i32.const 0 + i32.const 128 + i32.const 9 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + global.get $enum-to-string/Enum.E2 + global.set $enum-to-string/v + global.get $enum-to-string/v + call $enum-to-string/Enum#__enum_to_string + local.set $0 + global.get $~lib/memory/__stack_pointer + local.get $0 + i32.store + local.get $0 + i32.const 32 + call $~lib/string/String.__eq + i32.eqz + if + i32.const 0 + i32.const 128 + i32.const 12 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + i32.const 0 + call $enum-to-string/InlineEnum#__enum_to_string + local.set $0 + global.get $~lib/memory/__stack_pointer + local.get $0 + i32.store + local.get $0 + i32.const 256 + call $~lib/string/String.__eq + i32.eqz + if + i32.const 0 + i32.const 128 + i32.const 21 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + i32.const 1 + call $enum-to-string/InlineEnum#__enum_to_string + local.set $0 + global.get $~lib/memory/__stack_pointer + local.get $0 + i32.store + local.get $0 + i32.const 224 + call $~lib/string/String.__eq + i32.eqz + if + i32.const 0 + i32.const 128 + i32.const 22 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + i32.const 2 + call $enum-to-string/InlineEnum#__enum_to_string + local.set $0 + global.get $~lib/memory/__stack_pointer + local.get $0 + i32.store + local.get $0 + i32.const 192 + call $~lib/string/String.__eq + i32.eqz + if + i32.const 0 + i32.const 128 + i32.const 23 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + global.get $enum-to-string/EnumWithInit.E0 + call $enum-to-string/EnumWithInit#__enum_to_string + local.set $0 + global.get $~lib/memory/__stack_pointer + local.get $0 + i32.store + local.get $0 + i32.const 96 + call $~lib/string/String.__eq + i32.eqz + if + i32.const 0 + i32.const 128 + i32.const 31 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + global.get $enum-to-string/EnumWithInit.E1 + call $enum-to-string/EnumWithInit#__enum_to_string + local.set $0 + global.get $~lib/memory/__stack_pointer + local.get $0 + i32.store + local.get $0 + i32.const 64 + call $~lib/string/String.__eq + i32.eqz + if + i32.const 0 + i32.const 128 + i32.const 32 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + global.get $enum-to-string/EnumWithInit.E2 + call $enum-to-string/EnumWithInit#__enum_to_string + local.set $0 + global.get $~lib/memory/__stack_pointer + local.get $0 + i32.store + local.get $0 + i32.const 32 + call $~lib/string/String.__eq + i32.eqz + if + i32.const 0 + i32.const 128 + i32.const 33 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + global.get $enum-to-string/EnumWithDup.E0 + call $enum-to-string/EnumWithDup#__enum_to_string + local.set $0 + global.get $~lib/memory/__stack_pointer + local.get $0 + i32.store + local.get $0 + i32.const 32 + call $~lib/string/String.__eq + i32.eqz + if + i32.const 0 + i32.const 128 + i32.const 41 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + global.get $enum-to-string/EnumWithDup.E1 + call $enum-to-string/EnumWithDup#__enum_to_string + local.set $0 + global.get $~lib/memory/__stack_pointer + local.get $0 + i32.store + local.get $0 + i32.const 64 + call $~lib/string/String.__eq + i32.eqz + if + i32.const 0 + i32.const 128 + i32.const 42 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + global.get $enum-to-string/EnumWithDup.E2 + call $enum-to-string/EnumWithDup#__enum_to_string + local.set $0 + global.get $~lib/memory/__stack_pointer + local.get $0 + i32.store + local.get $0 + i32.const 32 + call $~lib/string/String.__eq + i32.eqz + if + i32.const 0 + i32.const 128 + i32.const 43 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.add + global.set $~lib/memory/__stack_pointer + ) +) diff --git a/tests/compiler/enum-to-string.json b/tests/compiler/enum-to-string.json new file mode 100644 index 0000000000..ea57a955e1 --- /dev/null +++ b/tests/compiler/enum-to-string.json @@ -0,0 +1,3 @@ +{ + "asc_flags": [] +} diff --git a/tests/compiler/enum-to-string.release.wat b/tests/compiler/enum-to-string.release.wat new file mode 100644 index 0000000000..91a870b03a --- /dev/null +++ b/tests/compiler/enum-to-string.release.wat @@ -0,0 +1,408 @@ +(module + (type $0 (func)) + (type $1 (func (param i32 i32 i32 i32))) + (type $2 (func (param i32 i32) (result i32))) + (import "env" "abort" (func $~lib/builtins/abort (param i32 i32 i32 i32))) + (global $~lib/memory/__stack_pointer (mut i32) (i32.const 34060)) + (memory $0 1) + (data $0 (i32.const 1036) "\1c") + (data $0.1 (i32.const 1048) "\02\00\00\00\04\00\00\00E\002") + (data $1 (i32.const 1068) "\1c") + (data $1.1 (i32.const 1080) "\02\00\00\00\04\00\00\00E\001") + (data $2 (i32.const 1100) "\1c") + (data $2.1 (i32.const 1112) "\02\00\00\00\04\00\00\00E\000") + (data $3 (i32.const 1132) "<") + (data $3.1 (i32.const 1144) "\02\00\00\00\"\00\00\00e\00n\00u\00m\00-\00t\00o\00-\00s\00t\00r\00i\00n\00g\00.\00t\00s") + (data $4 (i32.const 1196) "\1c") + (data $4.1 (i32.const 1208) "\02\00\00\00\06\00\00\00C\00E\002") + (data $5 (i32.const 1228) "\1c") + (data $5.1 (i32.const 1240) "\02\00\00\00\06\00\00\00C\00E\001") + (data $6 (i32.const 1260) "\1c") + (data $6.1 (i32.const 1272) "\02\00\00\00\06\00\00\00C\00E\000") + (export "memory" (memory $0)) + (start $~start) + (func $~start + call $start:enum-to-string + ) + (func $~lib/string/String.__eq (param $0 i32) (param $1 i32) (result i32) + (local $2 i32) + (local $3 i32) + (local $4 i32) + (local $5 i32) + global.get $~lib/memory/__stack_pointer + i32.const 8 + i32.sub + global.set $~lib/memory/__stack_pointer + global.get $~lib/memory/__stack_pointer + i32.const 1292 + i32.lt_s + if + i32.const 34080 + i32.const 34128 + i32.const 1 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + global.get $~lib/memory/__stack_pointer + i64.const 0 + i64.store + local.get $0 + local.get $1 + i32.eq + if + global.get $~lib/memory/__stack_pointer + i32.const 8 + i32.add + global.set $~lib/memory/__stack_pointer + i32.const 1 + return + end + block $folding-inner0 + local.get $1 + i32.eqz + local.get $0 + i32.eqz + i32.or + br_if $folding-inner0 + global.get $~lib/memory/__stack_pointer + local.get $0 + i32.store + local.get $0 + i32.const 20 + i32.sub + i32.load offset=16 + i32.const 1 + i32.shr_u + local.set $3 + global.get $~lib/memory/__stack_pointer + local.get $1 + i32.store + local.get $3 + local.get $1 + i32.const 20 + i32.sub + i32.load offset=16 + i32.const 1 + i32.shr_u + i32.ne + br_if $folding-inner0 + global.get $~lib/memory/__stack_pointer + local.get $0 + i32.store + local.get $0 + local.set $2 + global.get $~lib/memory/__stack_pointer + local.get $1 + i32.store offset=4 + local.get $3 + local.tee $0 + i32.const 4 + i32.ge_u + if (result i32) + local.get $2 + i32.const 7 + i32.and + local.get $1 + i32.const 7 + i32.and + i32.or + else + i32.const 1 + end + i32.eqz + if + loop $do-loop|0 + local.get $2 + i64.load + local.get $1 + i64.load + i64.eq + if + local.get $2 + i32.const 8 + i32.add + local.set $2 + local.get $1 + i32.const 8 + i32.add + local.set $1 + local.get $0 + i32.const 4 + i32.sub + local.tee $0 + i32.const 4 + i32.ge_u + br_if $do-loop|0 + end + end + end + block $__inlined_func$~lib/util/string/compareImpl$2 + loop $while-continue|1 + local.get $0 + local.tee $3 + i32.const 1 + i32.sub + local.set $0 + local.get $3 + if + local.get $2 + i32.load16_u + local.tee $5 + local.get $1 + i32.load16_u + local.tee $4 + i32.sub + local.set $3 + local.get $4 + local.get $5 + i32.ne + br_if $__inlined_func$~lib/util/string/compareImpl$2 + local.get $2 + i32.const 2 + i32.add + local.set $2 + local.get $1 + i32.const 2 + i32.add + local.set $1 + br $while-continue|1 + end + end + i32.const 0 + local.set $3 + end + global.get $~lib/memory/__stack_pointer + i32.const 8 + i32.add + global.set $~lib/memory/__stack_pointer + local.get $3 + i32.eqz + return + end + global.get $~lib/memory/__stack_pointer + i32.const 8 + i32.add + global.set $~lib/memory/__stack_pointer + i32.const 0 + ) + (func $start:enum-to-string + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.sub + global.set $~lib/memory/__stack_pointer + global.get $~lib/memory/__stack_pointer + i32.const 1292 + i32.lt_s + if + i32.const 34080 + i32.const 34128 + i32.const 1 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + global.get $~lib/memory/__stack_pointer + i32.const 0 + i32.store + global.get $~lib/memory/__stack_pointer + i32.const 1120 + i32.store + i32.const 1120 + i32.const 1120 + call $~lib/string/String.__eq + i32.eqz + if + i32.const 0 + i32.const 1152 + i32.const 7 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + global.get $~lib/memory/__stack_pointer + i32.const 1088 + i32.store + i32.const 1088 + i32.const 1088 + call $~lib/string/String.__eq + i32.eqz + if + i32.const 0 + i32.const 1152 + i32.const 8 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + global.get $~lib/memory/__stack_pointer + i32.const 1056 + i32.store + i32.const 1056 + i32.const 1056 + call $~lib/string/String.__eq + i32.eqz + if + i32.const 0 + i32.const 1152 + i32.const 9 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + global.get $~lib/memory/__stack_pointer + i32.const 1056 + i32.store + i32.const 1056 + i32.const 1056 + call $~lib/string/String.__eq + i32.eqz + if + i32.const 0 + i32.const 1152 + i32.const 12 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + global.get $~lib/memory/__stack_pointer + i32.const 1280 + i32.store + i32.const 1280 + i32.const 1280 + call $~lib/string/String.__eq + i32.eqz + if + i32.const 0 + i32.const 1152 + i32.const 21 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + global.get $~lib/memory/__stack_pointer + i32.const 1248 + i32.store + i32.const 1248 + i32.const 1248 + call $~lib/string/String.__eq + i32.eqz + if + i32.const 0 + i32.const 1152 + i32.const 22 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + global.get $~lib/memory/__stack_pointer + i32.const 1216 + i32.store + i32.const 1216 + i32.const 1216 + call $~lib/string/String.__eq + i32.eqz + if + i32.const 0 + i32.const 1152 + i32.const 23 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + global.get $~lib/memory/__stack_pointer + i32.const 1120 + i32.store + i32.const 1120 + i32.const 1120 + call $~lib/string/String.__eq + i32.eqz + if + i32.const 0 + i32.const 1152 + i32.const 31 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + global.get $~lib/memory/__stack_pointer + i32.const 1088 + i32.store + i32.const 1088 + i32.const 1088 + call $~lib/string/String.__eq + i32.eqz + if + i32.const 0 + i32.const 1152 + i32.const 32 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + global.get $~lib/memory/__stack_pointer + i32.const 1056 + i32.store + i32.const 1056 + i32.const 1056 + call $~lib/string/String.__eq + i32.eqz + if + i32.const 0 + i32.const 1152 + i32.const 33 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + global.get $~lib/memory/__stack_pointer + i32.const 1056 + i32.store + i32.const 1056 + i32.const 1056 + call $~lib/string/String.__eq + i32.eqz + if + i32.const 0 + i32.const 1152 + i32.const 41 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + global.get $~lib/memory/__stack_pointer + i32.const 1088 + i32.store + i32.const 1088 + i32.const 1088 + call $~lib/string/String.__eq + i32.eqz + if + i32.const 0 + i32.const 1152 + i32.const 42 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + global.get $~lib/memory/__stack_pointer + i32.const 1056 + i32.store + i32.const 1056 + i32.const 1056 + call $~lib/string/String.__eq + i32.eqz + if + i32.const 0 + i32.const 1152 + i32.const 43 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.add + global.set $~lib/memory/__stack_pointer + ) +) diff --git a/tests/compiler/enum-to-string.ts b/tests/compiler/enum-to-string.ts new file mode 100644 index 0000000000..2fc056323d --- /dev/null +++ b/tests/compiler/enum-to-string.ts @@ -0,0 +1,43 @@ +enum Enum { + E0, + E1, + E2, +} + +assert(Enum[Enum.E0] === "E0"); +assert(Enum[Enum.E1] === "E1"); +assert(Enum[Enum.E2] === "E2"); + +let v = Enum.E2; +assert(Enum[v] === "E2"); + +@inline +enum InlineEnum { + CE0, + CE1, + CE2, +} + +assert(InlineEnum[InlineEnum.CE0] === "CE0"); +assert(InlineEnum[InlineEnum.CE1] === "CE1"); +assert(InlineEnum[InlineEnum.CE2] === "CE2"); + +enum EnumWithInit { + E0 = 1, + E1 = 2, + E2 = 4, +} + +assert(EnumWithInit[EnumWithInit.E0] === "E0"); +assert(EnumWithInit[EnumWithInit.E1] === "E1"); +assert(EnumWithInit[EnumWithInit.E2] === "E2"); + +enum EnumWithDup { + E0 = 1, + E1 = 2, + E2 = 1, +} + +assert(EnumWithDup[EnumWithDup.E0] === "E2"); +assert(EnumWithDup[EnumWithDup.E1] === "E1"); +assert(EnumWithDup[EnumWithDup.E2] === "E2"); From 24909f640aff3ec1c2660a87e132591ae56d1b53 Mon Sep 17 00:00:00 2001 From: Congcong Cai Date: Thu, 25 Sep 2025 17:03:33 +0800 Subject: [PATCH 3/3] clean --- src/compiler.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler.ts b/src/compiler.ts index b4aae6ecd3..0cef47918e 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -1532,7 +1532,7 @@ export class Compiler extends DiagnosticEmitter { return null; } let members = enumElement.members; - if (!members) return null; // TODO + if (!members) return null; if (enumElement.toStringFunctionName) return enumElement.toStringFunctionName; const functionName = `${enumElement.internalName}#${CommonNames.EnumToString}`; enumElement.toStringFunctionName = functionName;