diff --git a/lib/builtin/builtin_name.ts b/lib/builtin/builtin_name.ts index 7b65430b..7d5575b1 100644 --- a/lib/builtin/builtin_name.ts +++ b/lib/builtin/builtin_name.ts @@ -53,6 +53,9 @@ export namespace BuiltinNames { // builtin function name export const globalInitFuncName = 'global|init|func'; + export const findPropertyFlagAndIndex = 'find_property_flag_and_index'; + export const findPropertyType = 'find_property_type'; + export const getInfcProperty = 'get_infc_property'; // builtin globals export const builtinTypeManglePrefix = 'lib/builtin/lib.type.d'; diff --git a/runtime-library/utils/type_utils.c b/runtime-library/utils/type_utils.c index 93c21552..8b692501 100644 --- a/runtime-library/utils/type_utils.c +++ b/runtime-library/utils/type_utils.c @@ -1012,6 +1012,8 @@ get_prop_index_of_struct(wasm_exec_env_t exec_env, const char *prop, uint32_t argc = 3, argv[3] = { 0 }, offset; wasm_struct_type_t struct_type; wasm_struct_type_t vtable_type; + int property_flag = -1; + int property_index = -1; module_inst = wasm_runtime_get_module_inst(exec_env); wasm_struct_obj = (wasm_struct_obj_t)(*wasm_obj); @@ -1019,37 +1021,32 @@ get_prop_index_of_struct(wasm_exec_env_t exec_env, const char *prop, wasm_struct_obj_get_field((wasm_struct_obj_t)vtable_value.gc_obj, 0, false, &meta); struct_type = (wasm_struct_type_t)wasm_obj_get_defined_type(*wasm_obj); - func = wasm_runtime_lookup_function(module_inst, "find_index", NULL); + func = wasm_runtime_lookup_function(module_inst, + "find_property_flag_and_index", NULL); bh_assert(func); argv[0] = meta.i32; offset = wasm_runtime_addr_native_to_app(module_inst, (void *)prop); argv[1] = offset; - argv[2] = 0; + argv[2] = ALL; wasm_runtime_call_wasm(exec_env, func, argc, argv); - if (argv[0] == -1) { - /* check if flag is method */ - argv[0] = meta.i32; - argv[1] = offset; - argv[2] = 1; - wasm_runtime_call_wasm(exec_env, func, argc, argv); - if (argv[0] == -1) { - return -1; - } - else { + if (argv[0] != -1) { + property_flag = argv[0] & META_FLAG_MASK; + property_index = (argv[0] & META_INDEX_MASK) >> 4; + if (property_flag == METHOD) { vtable_type = (wasm_struct_type_t)wasm_obj_get_defined_type( vtable_value.gc_obj); - *field_type = - wasm_struct_type_get_field_type(vtable_type, argv[0], &is_mut); + *field_type = wasm_struct_type_get_field_type( + vtable_type, property_index, &is_mut); + } + else if (property_flag == FIELD) { + *field_type = wasm_struct_type_get_field_type( + struct_type, property_index, &is_mut); } - } - else { - *field_type = - wasm_struct_type_get_field_type(struct_type, argv[0], &is_mut); } - return argv[0]; + return property_index; } /**********Utils for search field value of object through meta diff --git a/runtime-library/utils/type_utils.h b/runtime-library/utils/type_utils.h index 0f658278..c450f71e 100644 --- a/runtime-library/utils/type_utils.h +++ b/runtime-library/utils/type_utils.h @@ -39,6 +39,7 @@ enum field_flag { METHOD = 1, GETTER = 2, SETTER = 3, + ALL = 4, }; typedef enum ts_value_type_t { diff --git a/src/backend/binaryen/index.ts b/src/backend/binaryen/index.ts index c14c0717..b497ce95 100644 --- a/src/backend/binaryen/index.ts +++ b/src/backend/binaryen/index.ts @@ -68,7 +68,6 @@ import { ObjectDescription, } from '../../semantics/runtime.js'; import { dyntype } from './lib/dyntype/utils.js'; -import { clearWasmStringMap, getCString } from './utils.js'; import { assert } from 'console'; import ts from 'typescript'; import { VarValue } from '../../semantics/value.js'; @@ -97,9 +96,7 @@ export class WASMFunctionContext { } i32Local() { - return this.insertTmpVar( - this.binaryenCtx.wasmTypeComp.getWASMType(Primitive.Int), - ); + return this.insertTmpVar(binaryen.i32); } insert(insn: binaryen.ExpressionRef) { @@ -403,7 +400,7 @@ export class WASMGen extends Ts2wasmBackend { } private wasmGenerate() { - clearWasmStringMap(); + UtilFuncs.clearWasmStringMap(); FunctionalFuncs.resetDynContextRef(); // init wasm environment @@ -752,7 +749,7 @@ export class WASMGen extends Ts2wasmBackend { const heap = this.wasmTypeComp.getWASMHeapType(func.funcType); funcRef = binaryenCAPI._BinaryenAddFunctionWithHeapType( this.module.ptr, - getCString(func.name), + UtilFuncs.getCString(func.name), heap, arrayToPtr(allVarsTypeRefs).ptr, allVarsTypeRefs.length, @@ -1126,7 +1123,7 @@ export class WASMGen extends Ts2wasmBackend { ); const expr = binaryenCAPI._BinaryenGlobalSet( this.module.ptr, - getCString(name), + UtilFuncs.getCString(name), JSGlobalObj, ); return expr; @@ -1141,7 +1138,7 @@ export class WASMGen extends Ts2wasmBackend { binaryenCAPI._BinaryenFunctionSetLocalName( funcRef, idx, - getCString(name), + UtilFuncs.getCString(name), ); }); const isBuiltIn = debugFilePath.includes(BuiltinNames.builtinTypeName); diff --git a/src/backend/binaryen/lib/env_init.ts b/src/backend/binaryen/lib/env_init.ts index d1fd9a57..cb4369e6 100644 --- a/src/backend/binaryen/lib/env_init.ts +++ b/src/backend/binaryen/lib/env_init.ts @@ -517,8 +517,15 @@ export function addItableFunc(module: binaryen.Module) { ); const itableLib = fs.readFileSync(itableFilePath, 'utf-8'); const watModule = binaryen.parseText(itableLib); - UtilFuncs.addWatFuncs(watModule, 'find_index', module); - module.addFunctionExport('find_index', 'find_index'); - UtilFuncs.addWatFuncs(watModule, 'find_type_by_index', module); + UtilFuncs.addWatFuncs( + watModule, + BuiltinNames.findPropertyFlagAndIndex, + module, + ); + module.addFunctionExport( + BuiltinNames.findPropertyFlagAndIndex, + BuiltinNames.findPropertyFlagAndIndex, + ); + UtilFuncs.addWatFuncs(watModule, BuiltinNames.findPropertyType, module); watModule.dispose(); } diff --git a/src/backend/binaryen/lib/init_builtin_api.ts b/src/backend/binaryen/lib/init_builtin_api.ts index f2b3793a..28427315 100644 --- a/src/backend/binaryen/lib/init_builtin_api.ts +++ b/src/backend/binaryen/lib/init_builtin_api.ts @@ -20,14 +20,11 @@ import { UtilFuncs, FunctionalFuncs, FlattenLoop, - getCString, - getFieldFromMetaByOffset, MetaDataOffset, META_FLAG_MASK, ItableFlag, - MetaFieldOffset, + MetaPropertyOffset, SIZE_OF_META_FIELD, - getWASMObjectMeta, } from '../utils.js'; import { dyntype } from './dyntype/utils.js'; import { arrayToPtr } from '../glue/transform.js'; @@ -51,7 +48,7 @@ function anyrefCond(module: binaryen.Module) { const dynCtx = binaryenCAPI._BinaryenGlobalGet( module.ptr, - getCString(dyntype.dyntype_context), + UtilFuncs.getCString(dyntype.dyntype_context), dyntype.dyn_ctx_t, ); const cond = module.call( @@ -66,7 +63,7 @@ function anyrefCond(module: binaryen.Module) { ); const extRef = binaryenCAPI._BinaryenTableGet( module.ptr, - getCString(BuiltinNames.extrefTable), + UtilFuncs.getCString(BuiltinNames.extrefTable), index, binaryen.anyref, ); @@ -124,14 +121,18 @@ function getPropNameThroughMeta(module: binaryen.Module) { const statementArray: binaryen.ExpressionRef[] = []; // 1. get meta - const metaValue = getWASMObjectMeta(module, obj); + const metaValue = FunctionalFuncs.getWASMObjectMeta(module, obj); statementArray.push(module.local.set(metaIndex, metaValue)); // 2. get meta fields count statementArray.push( module.local.set( metaFieldsCountIndex, - getFieldFromMetaByOffset(module, meta, MetaDataOffset.COUNT_OFFSET), + FunctionalFuncs.getFieldFromMetaByOffset( + module, + meta, + MetaDataOffset.COUNT_OFFSET, + ), ), ); @@ -160,7 +161,7 @@ function getPropNameThroughMeta(module: binaryen.Module) { ); const loopStmtsArray: binaryen.ExpressionRef[] = []; const flagAndIndex = module.i32.load( - MetaFieldOffset.FLAG_AND_INDEX_OFFSET, + MetaPropertyOffset.FLAG_AND_INDEX_OFFSET, memoryAlignment, metaFieldsPtr, ); @@ -241,7 +242,7 @@ function getPropNameThroughMeta(module: binaryen.Module) { module.local.set( propNameIndex, module.i32.load( - MetaFieldOffset.NAME_OFFSET, + MetaPropertyOffset.NAME_OFFSET, memoryAlignment, metaFieldsPtr, ), @@ -2224,7 +2225,10 @@ function string_match_stringref(module: binaryen.Module) { module.ptr, stringArrayTypeInfoForStringRef.heapTypeRef, module.i32.const(1), - binaryenCAPI._BinaryenStringConst(module.ptr, getCString('')), + binaryenCAPI._BinaryenStringConst( + module.ptr, + UtilFuncs.getCString(''), + ), ), ), ); @@ -2503,7 +2507,10 @@ function string_charAt_stringref(module: binaryen.Module) { module.i32.ge_s(index_i32, len), ), module.return( - binaryenCAPI._BinaryenStringConst(module.ptr, getCString('')), + binaryenCAPI._BinaryenStringConst( + module.ptr, + UtilFuncs.getCString(''), + ), ), ), ); @@ -3155,7 +3162,10 @@ function string_trim_stringref(module: binaryen.Module) { module.ptr, StringRefEqOp.EQ, char, - binaryenCAPI._BinaryenStringConst(module.ptr, getCString(' ')), + binaryenCAPI._BinaryenStringConst( + module.ptr, + UtilFuncs.getCString(' '), + ), ), ); const loopStmts = module.block(null, [ @@ -3254,7 +3264,7 @@ function allocExtRefTableSlot(module: binaryen.Module) { const arrName = getBuiltInFuncName(BuiltinNames.extRefTableMaskArr); const maskArr = binaryenCAPI._BinaryenGlobalGet( module.ptr, - getCString(arrName), + UtilFuncs.getCString(arrName), charArrayTypeInfo.typeRef, ); const newArray = binaryenCAPI._BinaryenArrayNew( @@ -3262,13 +3272,13 @@ function allocExtRefTableSlot(module: binaryen.Module) { charArrayTypeInfo.heapTypeRef, binaryenCAPI._BinaryenTableSize( module.ptr, - getCString(BuiltinNames.extrefTable), + UtilFuncs.getCString(BuiltinNames.extrefTable), ), module.i32.const(0), ); const tableGrow = binaryenCAPI._BinaryenTableGrow( module.ptr, - getCString(BuiltinNames.extrefTable), + UtilFuncs.getCString(BuiltinNames.extrefTable), binaryenCAPI._BinaryenRefNull( module.ptr, binaryenCAPI._BinaryenTypeStructref(), @@ -3285,7 +3295,7 @@ function allocExtRefTableSlot(module: binaryen.Module) { tableGrow, binaryenCAPI._BinaryenGlobalSet( module.ptr, - getCString(arrName), + UtilFuncs.getCString(arrName), newArray, ), ]), @@ -3358,7 +3368,7 @@ function allocExtRefTableSlot(module: binaryen.Module) { ifStmts2.push( binaryenCAPI._BinaryenGlobalSet( module.ptr, - getCString(arrName), + UtilFuncs.getCString(arrName), module.local.get(tmpMaskArrIdx, charArrayTypeInfo.typeRef), ), ); @@ -3373,7 +3383,7 @@ function allocExtRefTableSlot(module: binaryen.Module) { ); const tableSetOp = binaryenCAPI._BinaryenTableSet( module.ptr, - getCString(BuiltinNames.extrefTable), + UtilFuncs.getCString(BuiltinNames.extrefTable), module.local.get(tableIdx, binaryen.i32), module.local.get(objIdx, binaryen.anyref), ); @@ -3410,7 +3420,7 @@ function newExtRef(module: binaryen.Module) { [ binaryenCAPI._BinaryenGlobalGet( module.ptr, - getCString(dyntype.dyntype_context), + UtilFuncs.getCString(dyntype.dyntype_context), binaryen.anyref, ), tableIdx, diff --git a/src/backend/binaryen/lib/interface/meta.c b/src/backend/binaryen/lib/interface/meta.c index 2f28f498..b607c407 100644 --- a/src/backend/binaryen/lib/interface/meta.c +++ b/src/backend/binaryen/lib/interface/meta.c @@ -9,65 +9,78 @@ /** - * MetaField record the properties information of object + * MetaProperty record the properties information of object * name: property name * flag_and_index: flag and index of the property * type: type of the property, represent by property's type id */ -typedef struct MetaField { +typedef struct MetaProperty { char *name; int flag_and_index; int type; -} MetaField; +} MetaProperty; /** * Meta record type id and the properties information of object * type_id: type id of the object - * count: number of fields - * fields: fields information + * count: number of properties + * properties: properties information */ - typedef struct Meta { int type_id; int impl_id; int count; - MetaField fields[0]; + /* property includes field, method and accessor */ + MetaProperty properties[0]; } Meta; +enum propertyFlag { + FIELD = 0, + METHOD = 1, + GETTER = 2, + SETTER = 3, + ALL = 4, +}; + #define META_FLAG_MASK 0x0000000F #define META_INDEX_MASK 0xFFFFFFF0 -/* find field index based on prop_name*/ -int find_index(Meta *meta, char *prop_name, int flag) { - MetaField f; - int f_flag; - int f_index; - int s_flag = flag & META_FLAG_MASK; +/* find property index based on prop_name*/ +int find_property_flag_and_index(Meta *meta, char *prop_name, enum propertyFlag flag) { + MetaProperty prop; + int target_flag = flag & META_FLAG_MASK; + int all_flag = ALL & META_FLAG_MASK; for (int i = 0; i < meta->count; i++) { - f = meta->fields[i]; - f_flag = f.flag_and_index & META_FLAG_MASK; - f_index = (f.flag_and_index & META_INDEX_MASK) >> 4; - - if (strcmp(f.name, prop_name) == 0 && f_flag == s_flag) { - return f_index; + prop = meta->properties[i]; + if (strcmp(prop.name, prop_name) == 0) { + if (target_flag == all_flag) { + return prop.flag_and_index; + } else if ((prop.flag_and_index & META_FLAG_MASK) == target_flag) { + return prop.flag_and_index; + } } } + return -1; } -/* find field type based on prop_name*/ -int find_type_by_index(Meta *meta, char *prop_name, int flag) { - MetaField f; - int f_flag; +/* find property type based on prop_name*/ +int find_property_type(Meta *meta, char *prop_name, enum propertyFlag flag) { + MetaProperty prop; + int target_flag = flag & META_FLAG_MASK; + int all_flag = ALL & META_FLAG_MASK; for (int i = 0; i < meta->count; i++) { - f = meta->fields[i]; - f_flag = f.flag_and_index & META_FLAG_MASK; - - if (strcmp(f.name, prop_name) == 0 && f_flag == flag) { - return f.type; + prop = meta->properties[i]; + if (strcmp(prop.name, prop_name) == 0) { + if (target_flag == all_flag) { + return prop.type; + } else if ((prop.flag_and_index & META_FLAG_MASK) == target_flag) { + return prop.type; + } } } + return -1; } diff --git a/src/backend/binaryen/lib/interface/meta.wat b/src/backend/binaryen/lib/interface/meta.wat index 8968b2a5..e23b8047 100644 --- a/src/backend/binaryen/lib/interface/meta.wat +++ b/src/backend/binaryen/lib/interface/meta.wat @@ -1,19 +1,20 @@ (module (type $i32_i32_i32_=>_i32 (func (param i32 i32 i32) (result i32))) - (type $FUNCSIG$iii (func (param i32 i32) (result i32))) - (import "env" "strcmp" (func $strcmp (type $FUNCSIG$iii) (param i32 i32) (result i32))) + (type $i32_i32_=>_i32 (func (param i32 i32) (result i32))) + (import "env" "strcmp" (func $strcmp (param i32 i32) (result i32))) (memory $0 1) (export "memory" (memory $0)) - (export "find_index" (func $find_index)) - (export "find_type_by_index" (func $find_type_by_index)) - (func $find_index (type $i32_i32_i32_=>_i32) (; has Stack IR ;) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) + (export "find_property_flag_and_index" (func $find_property_flag_and_index)) + (export "find_property_type" (func $find_property_type)) + (func $find_property_flag_and_index (param $0 i32) (param $1 i32) (param $2 i32) (result i32) (local $3 i32) (local $4 i32) (local $5 i32) - (block $label$0 + (local $6 i32) + (block $label$1 (if (i32.gt_s - (local.tee $5 + (local.tee $4 (i32.load offset=8 (local.get $0) ) @@ -27,30 +28,46 @@ (i32.const 16) ) ) - (loop $label$2 - (local.set $4 + (local.set $6 + (i32.eq + (local.tee $5 + (i32.and + (local.get $2) + (i32.const 15) + ) + ) + (i32.const 4) + ) + ) + (loop $label$3 + (local.set $2 (i32.load (local.get $0) ) ) - (br_if $label$0 + (if (i32.eqz - (i32.or - (call $strcmp - (i32.load - (i32.sub - (local.get $0) - (i32.const 4) - ) + (call $strcmp + (i32.load + (i32.sub + (local.get $0) + (i32.const 4) ) - (local.get $1) ) - (i32.and - (i32.xor + (local.get $1) + ) + ) + (block + (br_if $label$1 + (local.get $6) + ) + (br_if $label$1 + (i32.eq + (i32.and (local.get $2) - (local.get $4) + (i32.const 15) ) - (i32.const 15) + (local.get $5) ) ) ) @@ -61,7 +78,7 @@ (i32.const 12) ) ) - (br_if $label$2 + (br_if $label$3 (i32.lt_s (local.tee $3 (i32.add @@ -69,25 +86,23 @@ (i32.const 1) ) ) - (local.get $5) + (local.get $4) ) ) ) ) ) - (return + (local.set $2 (i32.const -1) ) ) - (i32.shr_u - (local.get $4) - (i32.const 4) - ) + (local.get $2) ) - (func $find_type_by_index (type $i32_i32_i32_=>_i32) (; has Stack IR ;) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) + (func $find_property_type (param $0 i32) (param $1 i32) (param $2 i32) (result i32) (local $3 i32) (local $4 i32) - (block $label$0 + (local $5 i32) + (block $label$1 (if (i32.gt_s (local.tee $4 @@ -104,7 +119,18 @@ (i32.const 20) ) ) - (loop $label$2 + (local.set $5 + (i32.eq + (local.tee $2 + (i32.and + (local.get $2) + (i32.const 15) + ) + ) + (i32.const 4) + ) + ) + (loop $label$3 (if (i32.eqz (call $strcmp @@ -117,18 +143,23 @@ (local.get $1) ) ) - (br_if $label$0 - (i32.eq - (i32.and - (i32.load - (i32.sub - (local.get $0) - (i32.const 4) + (block + (br_if $label$1 + (local.get $5) + ) + (br_if $label$1 + (i32.eq + (i32.and + (i32.load + (i32.sub + (local.get $0) + (i32.const 4) + ) ) + (i32.const 15) ) - (i32.const 15) + (local.get $2) ) - (local.get $2) ) ) ) @@ -138,7 +169,7 @@ (i32.const 12) ) ) - (br_if $label$2 + (br_if $label$3 (i32.lt_s (local.tee $3 (i32.add @@ -161,3 +192,4 @@ ) ) ) + diff --git a/src/backend/binaryen/utils.ts b/src/backend/binaryen/utils.ts index aab065ca..6d0685e6 100644 --- a/src/backend/binaryen/utils.ts +++ b/src/backend/binaryen/utils.ts @@ -84,8 +84,40 @@ export enum ItableFlag { METHOD, GETTER, SETTER, + ALL, } +export const enum StructFieldIndex { + VTABLE_INDEX = 0, +} + +export const enum VtableFieldIndex { + META_INDEX = 0, +} + +export const SIZE_OF_META_FIELD = 12; + +export const enum MetaDataOffset { + TYPE_ID_OFFSET = 0, + IMPL_ID_OFFSET = 4, + COUNT_OFFSET = 8, + FIELDS_PTR_OFFSET = 12, +} + +export const enum MetaPropertyOffset { + NAME_OFFSET = 0, + FLAG_AND_INDEX_OFFSET = 4, + TYPE_OFFSET = 8, +} + +export interface SourceMapLoc { + location: SourceLocation; + ref: binaryen.ExpressionRef; +} + +export const META_FLAG_MASK = 0x0000000f; +export const META_INDEX_MASK = 0xfffffff0; + export namespace UtilFuncs { export function getFuncName( moduleName: string, @@ -129,6 +161,53 @@ export namespace UtilFuncs { return false; } } + + export const wasmStringMap = new Map(); + + export function getCString(str: string) { + if (wasmStringMap.has(str)) { + return wasmStringMap.get(str) as number; + } + const wasmStr = binaryenCAPI._malloc(str.length + 1); + let index = wasmStr; + // consider UTF-8 only + for (let i = 0; i < str.length; i++) { + binaryenCAPI.__i32_store8(index++, str.codePointAt(i) as number); + } + binaryenCAPI.__i32_store8(index, 0); + wasmStringMap.set(str, wasmStr); + return wasmStr; + } + + export function clearWasmStringMap() { + wasmStringMap.clear(); + } + + export function utf16ToUtf8(utf16String: string): string { + let utf8String = ''; + + for (let i = 0; i < utf16String.length; i++) { + const charCode = utf16String.charCodeAt(i); + if (charCode <= 0x7f) { + utf8String += String.fromCharCode(charCode); + } else if (charCode <= 0x7ff) { + utf8String += String.fromCharCode( + 0xc0 | ((charCode >> 6) & 0x1f), + ); + utf8String += String.fromCharCode(0x80 | (charCode & 0x3f)); + } else { + utf8String += String.fromCharCode( + 0xe0 | ((charCode >> 12) & 0x0f), + ); + utf8String += String.fromCharCode( + 0x80 | ((charCode >> 6) & 0x3f), + ); + utf8String += String.fromCharCode(0x80 | (charCode & 0x3f)); + } + } + + return utf8String; + } } export namespace FunctionalFuncs { @@ -147,7 +226,7 @@ export namespace FunctionalFuncs { so we use C-API instead */ dyntypeContextRef = binaryenCAPI._BinaryenGlobalGet( module.ptr, - getCString(dyntype.dyntype_context), + UtilFuncs.getCString(dyntype.dyntype_context), dyntype.dyn_ctx_t, ); } @@ -324,6 +403,17 @@ export namespace FunctionalFuncs { ); } + export function isDynUndefined( + module: binaryen.Module, + valueRef: binaryen.ExpressionRef, + ) { + return module.call( + dyntype.dyntype_is_undefined, + [getDynContextRef(module), valueRef], + binaryen.i32, + ); + } + export function generateDynUndefined(module: binaryen.Module) { return module.call( dyntype.dyntype_new_undefined, @@ -1295,11 +1385,7 @@ export namespace FunctionalFuncs { ); // TODO: ref.null need table.get support in native API } else if (rightValueType.kind === ValueTypeKind.UNDEFINED) { - res = module.call( - dyntype.dyntype_is_undefined, - [dynCtx, leftValueRef], - binaryen.i32, - ); + res = isDynUndefined(module, leftValueRef); } else if (rightValueType.kind === ValueTypeKind.NUMBER) { res = operateF64F64ToDyn( module, @@ -1587,123 +1673,97 @@ export namespace FunctionalFuncs { false, ); } -} -export const wasmStringMap = new Map(); -export function getCString(str: string) { - if (wasmStringMap.has(str)) { - return wasmStringMap.get(str) as number; - } - const wasmStr = binaryenCAPI._malloc(str.length + 1); - let index = wasmStr; - // consider UTF-8 only - for (let i = 0; i < str.length; i++) { - binaryenCAPI.__i32_store8(index++, str.codePointAt(i) as number); + export function getFieldFromMetaByOffset( + module: binaryen.Module, + meta: binaryen.ExpressionRef, + offset: number, + ) { + return module.i32.load(offset, memoryAlignment, meta); } - binaryenCAPI.__i32_store8(index, 0); - wasmStringMap.set(str, wasmStr); - return wasmStr; -} -export function utf16ToUtf8(utf16String: string): string { - let utf8String = ''; - - for (let i = 0; i < utf16String.length; i++) { - const charCode = utf16String.charCodeAt(i); - if (charCode <= 0x7f) { - utf8String += String.fromCharCode(charCode); - } else if (charCode <= 0x7ff) { - utf8String += String.fromCharCode(0xc0 | ((charCode >> 6) & 0x1f)); - utf8String += String.fromCharCode(0x80 | (charCode & 0x3f)); - } else { - utf8String += String.fromCharCode(0xe0 | ((charCode >> 12) & 0x0f)); - utf8String += String.fromCharCode(0x80 | ((charCode >> 6) & 0x3f)); - utf8String += String.fromCharCode(0x80 | (charCode & 0x3f)); - } + export function getWasmStructFieldByIndex( + module: binaryen.Module, + ref: binaryen.ExpressionRef, + typeRef: binaryen.Type, + idx: number, + ) { + return binaryenCAPI._BinaryenStructGet( + module.ptr, + idx, + ref, + typeRef, + false, + ); } - return utf8String; -} - -export function clearWasmStringMap() { - wasmStringMap.clear(); -} - -export const enum StructFieldIndex { - VTABLE_INDEX = 0, -} - -export const enum VtableFieldIndex { - META_INDEX = 0, -} - -export const SIZE_OF_META_FIELD = 12; - -export const enum MetaDataOffset { - TYPE_ID_OFFSET = 0, - IMPL_ID_OFFSET = 4, - COUNT_OFFSET = 8, - FIELDS_PTR_OFFSET = 12, -} - -export const enum MetaFieldOffset { - NAME_OFFSET = 0, - FLAG_AND_INDEX_OFFSET = 4, - TYPE_OFFSET = 8, -} + export function getWASMObjectVtable( + module: binaryen.Module, + ref: binaryen.ExpressionRef, + ) { + return getWasmStructFieldByIndex( + module, + ref, + baseVtableType.typeRef, + StructFieldIndex.VTABLE_INDEX, + ); + } -export function getFieldFromMetaByOffset( - module: binaryen.Module, - meta: binaryen.ExpressionRef, - offset: number, -) { - return module.i32.load(offset, memoryAlignment, meta); -} + export function getWASMObjectMeta( + module: binaryen.Module, + ref: binaryen.ExpressionRef, + ) { + const vtable = getWASMObjectVtable(module, ref); + return getWasmStructFieldByIndex( + module, + vtable, + binaryen.i32, + VtableFieldIndex.META_INDEX, + ); + } -export function getWasmStructFieldByIndex( - module: binaryen.Module, - ref: binaryen.ExpressionRef, - typeRef: binaryen.Type, - idx: number, -) { - return binaryenCAPI._BinaryenStructGet( - module.ptr, - idx, - ref, - typeRef, - false, - ); -} + export function isPropertyExist( + module: binaryen.Module, + flagAndIndexRef: binaryen.ExpressionRef, + ) { + return module.i32.eq(flagAndIndexRef, module.i32.const(-1)); + } -export function getWASMObjectVtable( - module: binaryen.Module, - ref: binaryen.ExpressionRef, -) { - return getWasmStructFieldByIndex( - module, - ref, - baseVtableType.typeRef, - StructFieldIndex.VTABLE_INDEX, - ); -} + export function isFieldFlag( + module: binaryen.Module, + flagRef: binaryen.ExpressionRef, + ) { + return module.i32.eq(flagRef, module.i32.const(ItableFlag.FIELD)); + } -export function getWASMObjectMeta( - module: binaryen.Module, - ref: binaryen.ExpressionRef, -) { - const vtable = getWASMObjectVtable(module, ref); - return getWasmStructFieldByIndex( - module, - vtable, - binaryen.i32, - VtableFieldIndex.META_INDEX, - ); -} + export function isMethodFlag( + module: binaryen.Module, + flagRef: binaryen.ExpressionRef, + ) { + return module.i32.eq(flagRef, module.i32.const(ItableFlag.METHOD)); + } -export interface SourceMapLoc { - location: SourceLocation; - ref: binaryen.ExpressionRef; + export function isShapeCompatible( + module: binaryen.Module, + typeId: number, + metaRef: binaryen.ExpressionRef, + ) { + /* judge if type_id is equal or impl_id is equal */ + const infcTypeIdRef = module.i32.const(typeId); + const objTypeIdRef = getFieldFromMetaByOffset( + module, + metaRef, + MetaDataOffset.TYPE_ID_OFFSET, + ); + const objImplIdRef = getFieldFromMetaByOffset( + module, + metaRef, + MetaDataOffset.IMPL_ID_OFFSET, + ); + const ifShapeCompatibal = module.i32.or( + module.i32.eq(infcTypeIdRef, objTypeIdRef), + module.i32.eq(infcTypeIdRef, objImplIdRef), + ); + return ifShapeCompatibal; + } } - -export const META_FLAG_MASK = 0x0000000f; -export const META_INDEX_MASK = 0xfffffff0; diff --git a/src/backend/binaryen/wasm_expr_gen.ts b/src/backend/binaryen/wasm_expr_gen.ts index cf486c6b..6f26e525 100644 --- a/src/backend/binaryen/wasm_expr_gen.ts +++ b/src/backend/binaryen/wasm_expr_gen.ts @@ -19,13 +19,9 @@ import { Logger } from '../../log.js'; import { UtilFuncs, FunctionalFuncs, - getCString, ItableFlag, - utf16ToUtf8, FlattenLoop, - getFieldFromMetaByOffset, MetaDataOffset, - getWASMObjectMeta, } from './utils.js'; import { PredefinedTypeId, @@ -340,7 +336,7 @@ export class WASMExpressionGen { str = str.substring(1, str.length - 1); } const ptr = this.wasmCompiler.generateRawString(str); - const len = utf16ToUtf8(str).length; + const len = UtilFuncs.utf16ToUtf8(str).length; return FunctionalFuncs.generateStringForStringref( this.module, this.module.i32.const(ptr), @@ -975,6 +971,7 @@ export class WASMExpressionGen { closureRef: binaryen.ExpressionRef, funcType: FunctionType, args?: SemanticsValue[], + objRef?: binaryen.ExpressionRef, ) { const closureVarTypeRef = binaryen.getExpressionType(closureRef); const closureTmpVar = @@ -1002,7 +999,7 @@ export class WASMExpressionGen { false, ); this.wasmCompiler.currentFuncCtx!.insert(setClosureTmpVarRef); - return this.callFuncRef(funcType, funcRef, args, undefined, context); + return this.callFuncRef(funcType, funcRef, args, objRef, context); } private callBuiltinOrStaticMethod( @@ -1083,15 +1080,6 @@ export class WASMExpressionGen { ); } } - } else { - return this.getInfcMember( - member, - value.owner.type, - this.wasmExprGen(value.owner), - memberIdx, - true, - value.parameters, - ); } isBuiltIn = false; } @@ -1435,10 +1423,10 @@ export class WASMExpressionGen { * the original unspecialized type is stored in shape, and the specific specialized type is stored in type */ const owner = value.owner as VarValue; - const meta = owner.shape!.meta!; - const member = meta.members[value.index]; + const shapeMeta = owner.shape!.meta!; + const shapeMember = shapeMeta.members[value.index]; const args = value.parameters; - let target = meta.name; + let target = shapeMeta.name; let isBuiltin = false; /* Workaround: should use meta.isBuiltin, but currently only class defined @@ -1454,7 +1442,7 @@ export class WASMExpressionGen { if (isBuiltin) { return this.callBuiltinOrStaticMethod( - member, + shapeMember, target, value.parameters, true, @@ -1466,18 +1454,21 @@ export class WASMExpressionGen { if (owner.ref instanceof ObjectType) { return this.callClassStaticMethod( owner.ref, - member.name, + shapeMember.name, value.parameters, ); } else { const ownerType = owner.type as ObjectType; const typeMeta = ownerType.meta; + const typeMember = typeMeta.findMember( + shapeMember.name, + ) as MemberDescription; const thisRef = this.wasmExprGen(owner); - return this.getInstMember( + return this.getInstProperty( thisRef, ownerType, typeMeta, - member, + typeMember, true, args, ); @@ -1816,6 +1807,28 @@ export class WASMExpressionGen { ); } + private setObjMethod( + objRef: binaryen.ExpressionRef, + methodIdx: number, + objTypeRef: binaryen.Type, + targetValueRef: binaryen.ExpressionRef, + ) { + const vtableRef = binaryenCAPI._BinaryenStructGet( + this.module.ptr, + 0, + objRef, + objTypeRef, + false, + ); + return binaryenCAPI._BinaryenStructSet( + this.module.ptr, + /** because the first index is point to meta, so methodIdx should plus 1 */ + methodIdx + 1, + vtableRef, + targetValueRef, + ); + } + private getObjField( objRef: binaryen.ExpressionRef, fieldIdx: number, @@ -1853,15 +1866,15 @@ export class WASMExpressionGen { } private callFuncRef( - funcType: ValueType, + funcType: FunctionType, targetFunction: binaryen.ExpressionRef, args?: SemanticsValue[], objRef?: binaryen.ExpressionRef, context?: binaryen.ExpressionRef, funcDecl?: FunctionDeclareNode, ) { - const funcTypeRef = this.wasmTypeGen.getWASMValueType( - (funcType as FunctionType).returnType, + const returnTypeRef = this.wasmTypeGen.getWASMValueType( + funcType.returnType, ); if (!context) { context = binaryenCAPI._BinaryenRefNull( @@ -1870,11 +1883,17 @@ export class WASMExpressionGen { ); } const envArgs: binaryen.ExpressionRef[] = [context]; - if (objRef) { - envArgs.push(objRef); + if (funcType.envParamLen === 2) { + if (objRef) { + envArgs.push(objRef); + } else { + throw new Error( + 'class method calling must provide $this reference', + ); + } } const callArgsRefs = this.parseArguments( - funcType as FunctionType, + funcType, envArgs, args, funcDecl, @@ -1885,7 +1904,7 @@ export class WASMExpressionGen { targetFunction, arrayToPtr(callArgsRefs).ptr, callArgsRefs.length, - funcTypeRef, + returnTypeRef, false, ); } @@ -1923,315 +1942,384 @@ export class WASMExpressionGen { rightValue?: SemanticsValue, ) { const owner = value.owner as VarValue; - const meta = owner.shape!.meta; - const member = meta.members[value.index]; + const shapeMeta = owner.shape!.meta; + const shapeMember = shapeMeta.members[value.index]; const ownerType = owner.type as ObjectType; let targetValue = value.value!; if (rightValue) { targetValue = rightValue; } const typeMeta = ownerType.meta; - return this.setInstField( + const typeMember = typeMeta.findMember( + shapeMember.name, + ) as MemberDescription; + return this.setInstProperty( this.wasmExprGen(owner), - targetValue, ownerType, typeMeta, - member, + typeMember, + targetValue, ); } - private setInstField( + private setInstProperty( thisRef: binaryen.ExpressionRef, - targetValue: SemanticsValue, ownerType: ObjectType, - meta: ObjectDescription, - member: MemberDescription, + typeMeta: ObjectDescription, + typeMember: MemberDescription, + targetValue: SemanticsValue, ) { - const thisTypeRef = this.wasmTypeGen.getWASMType(ownerType); - const valueIdx = this.getTruthIdx(meta, member, member.hasSetter); - if (meta.isInterface) { - return this.setInfcFieldWithSetter( - member, - ownerType, + const isSetter = typeMember.hasSetter ? true : false; + if (isSetter) { + return this.getInstProperty( thisRef, - valueIdx, - targetValue, + ownerType, + typeMeta, + typeMember, + isSetter, + [targetValue], + isSetter, ); } else { - return this.setObjFieldWithSetter( - member, - thisRef, - thisTypeRef, - valueIdx, - targetValue, + const propertyIdx = this.getTruthIdx( + typeMeta, + typeMember, + typeMember.hasSetter, ); + if (typeMeta.isInterface) { + return this.setInfcProperty( + typeMember, + ownerType, + thisRef, + propertyIdx, + targetValue, + ); + } else { + return this.setObjProperty( + typeMember, + ownerType, + thisRef, + propertyIdx, + targetValue, + ); + } } } - private setInfcFieldWithSetter( + private setInfcProperty( member: MemberDescription, infcType: ValueType, thisRef: binaryen.ExpressionRef, - fieldIdx: number, + propertyIdx: number, targetValue: SemanticsValue, ) { - let res: binaryen.ExpressionRef; - const isSet = member.hasSetter ? false : true; - const callMethod = member.hasSetter ? true : false; - const targetValueRef = member.hasSetter - ? undefined - : this.wasmExprGen(targetValue); - const memberValueType = member.hasSetter + const metaRef = FunctionalFuncs.getWASMObjectMeta(this.module, thisRef); + const memberNameRef = this.getStringOffset(member.name); + let flag = ItableFlag.ALL; + if (member.hasSetter) { + flag = ItableFlag.SETTER; + } + const flagAndIndexRef = this.getPropFlagAndIdxFromObj( + metaRef, + memberNameRef, + flag, + ); + const propTypeIdRef = this.getPropTypeFromObj( + metaRef, + memberNameRef, + flag, + ); + const propType = member.hasSetter ? (member.setter as VarValue).type : member.valueType; - res = this.getOriObjInfoByFindIdx( + const targetValueRef = this.wasmExprGen(targetValue); + + /* TODO: workaround: quick path may fail, since cast failure */ + const infcDescTypeRef = this.wasmTypeGen.getWASMObjOriType(infcType); + const castedObjRef = binaryenCAPI._BinaryenRefCast( + this.module.ptr, thisRef, - infcType, - member, - memberValueType, - fieldIdx, - isSet, - callMethod, - targetValueRef, + infcDescTypeRef, ); - if (member.hasSetter) { - res = this.callFuncRef( - memberValueType, - res, - [targetValue], - thisRef, + const ifShapeCompatibal = FunctionalFuncs.isShapeCompatible( + this.module, + infcType.typeId, + metaRef, + ); + let ifCompatibalTrue: binaryen.ExpressionRef; + if (propType.kind === ValueTypeKind.FUNCTION) { + /* if property's value type is function, and typeid is equal, then we can set property to vtable */ + ifCompatibalTrue = this.setObjMethod( + castedObjRef, + propertyIdx, + infcDescTypeRef, + targetValueRef, ); + } else { + /* if property's value type is not function, then it must be a field */ + ifCompatibalTrue = this.setObjField( + castedObjRef, + propertyIdx, + targetValueRef, + ); + // TODO: box & unbox depend on field_type_id } - return res; + const ifCompatibalFalse = this.dynSetInfcProperty( + thisRef, + flagAndIndexRef, + propType, + member.isOptional, + propTypeIdRef, + targetValueRef, + ); + /* set property from interface */ + return this.module.if( + ifShapeCompatibal, + ifCompatibalTrue, + ifCompatibalFalse, + ); } - private setObjFieldWithSetter( + private setObjProperty( member: MemberDescription, + objType: ValueType, thisRef: binaryen.ExpressionRef, - thisTypeRef: binaryen.Type, - fieldIdx: number, + propertyIdx: number, targetValue: SemanticsValue, ) { - let res: binaryen.ExpressionRef; - let targetFuncRef: binaryen.ExpressionRef; + const thisTypeRef = this.wasmTypeGen.getWASMType(objType); const targetValueRef = this.wasmExprGen(targetValue); - if (!member.hasSetter) { - res = this.setObjField(thisRef, fieldIdx, targetValueRef); + let res: binaryen.ExpressionRef; + + if (member.type === MemberType.FIELD) { + res = this.setObjField(thisRef, propertyIdx, targetValueRef); } else { - const setterType = (member.setter as VarValue).type; - targetFuncRef = this.getObjMethod(thisRef, fieldIdx, thisTypeRef); - res = this.callFuncRef( - setterType, - targetFuncRef, - [targetValue], + res = this.setObjMethod( thisRef, + propertyIdx, + thisTypeRef, + targetValueRef, ); } return res; } - private getInstMember( + private getInstProperty( thisRef: binaryen.ExpressionRef, ownerType: ObjectType, - meta: ObjectDescription, - member: MemberDescription, + typeMeta: ObjectDescription, + typeMember: MemberDescription, isCall = false, args?: SemanticsValue[], + isSetter = false, ) { - const thisTypeRef = this.wasmTypeGen.getWASMType(ownerType); - const valueIdx = this.getTruthIdx(meta, member); - const typeMember = meta.findMember(member.name) as MemberDescription; - if (meta.isInterface) { - return this.getInfcMember( + const propertyIdx = this.getTruthIdx(typeMeta, typeMember, isSetter); + if (typeMeta.isInterface) { + return this.getInfcProperty( typeMember, ownerType, thisRef, - valueIdx, + propertyIdx, isCall, args, + isSetter, ); } else { - return this.getObjMember( + return this.getObjProperty( typeMember, + ownerType, thisRef, - thisTypeRef, - valueIdx, + propertyIdx, isCall, args, + isSetter, ); } } - /** access method, for example: obj.method, return a closure - * TODO: because of the closure lacks of `this`, so now call the closure will not work - */ - private getInstMethod( - thisRef: binaryen.ExpressionRef, - ownerType: ObjectType, - meta: ObjectDescription, - member: MemberDescription, - ) { - const thisTypeRef = this.wasmTypeGen.getWASMType(ownerType); - const valueIdx = this.getTruthIdx(meta, member); - const typeMember = meta.findMember(member.name) as MemberDescription; - const methodType = member.valueType as FunctionType; - const memberName = typeMember.name; - - let res: binaryen.ExpressionRef; - if (meta.isInterface) { - const metaRef = getWASMObjectMeta(this.module, thisRef); - const memberNameRef = this.module.i32.const( - this.wasmCompiler.generateRawString(memberName), - ); - const indexRef = this.getPropIndexOfInfc( - metaRef, - memberNameRef, - ItableFlag.METHOD, - ); - let func = this.dynGetInfcField( - thisRef, - indexRef, - typeMember.valueType, - typeMember.isOptional, - this.getPropTypeOnIndexOfInfc( - metaRef, - memberNameRef, - ItableFlag.METHOD, - ), - ); - res = func; - if (!typeMember.isOptional) { - const wasmFuncType = this.wasmTypeGen.getWASMType(methodType); - func = binaryenCAPI._BinaryenRefCast( - this.module.ptr, - func, - wasmFuncType, - ); - res = this.getClosureOfMethod(func, methodType); - } - } else { - const func = this.getObjMethod(thisRef, valueIdx, thisTypeRef); - res = this.getClosureOfMethod(func, methodType); - } - return res; - } - private getInfcMember( + private getInfcProperty( member: MemberDescription, infcType: ValueType, thisRef: binaryen.ExpressionRef, - memberIdx: number, + propertyIdx: number, isCall = false, args?: SemanticsValue[], + isSetter = false, ) { - let res: binaryen.ExpressionRef; - const memberValueType = member.hasGetter + const metaRef = FunctionalFuncs.getWASMObjectMeta(this.module, thisRef); + const memberNameRef = this.getStringOffset(member.name); + const flag = ItableFlag.ALL; + const flagAndIndexRef = this.getPropFlagAndIdxFromObj( + metaRef, + memberNameRef, + flag, + ); + const propTypeIdRef = this.getPropTypeFromObj( + metaRef, + memberNameRef, + flag, + ); + const propType = isSetter + ? (member.setter as VarValue).type + : member.hasGetter ? (member.getter as VarValue).type : member.valueType; - res = this.getOriObjInfoByFindIdx( + /* TODO: workaround: quick path may fail, since cast failure */ + const infcDescTypeRef = this.wasmTypeGen.getWASMObjOriType(infcType); + const castedObjRef = binaryenCAPI._BinaryenRefCast( + this.module.ptr, thisRef, - infcType, - member, - memberValueType, - memberIdx, - false, - member.type !== MemberType.FIELD, + infcDescTypeRef, + ); + const ifShapeCompatibal = FunctionalFuncs.isShapeCompatible( + this.module, + infcType.typeId, + metaRef, ); - /** is call method */ + let ifCompatibalTrue: binaryen.ExpressionRef; + if (propType.kind === ValueTypeKind.FUNCTION) { + /* if property's value type is function, and typeid is equal, then we can get property from vtable */ + /* methodRef get from vtable is a funcref, we need to box it to closure */ + ifCompatibalTrue = this.getClosureOfMethod( + this.getObjMethod(castedObjRef, propertyIdx, infcDescTypeRef), + propType as FunctionType, + ); + } else { + /* if property's value type is not function, then it must be a field */ + ifCompatibalTrue = this.getObjField( + castedObjRef, + propertyIdx, + infcDescTypeRef, + ); + // TODO: box & unbox depend on field_type_id + } + const ifCompatibalFalse = this.dynGetInfcProperty( + thisRef, + flagAndIndexRef, + propType, + member.isOptional, + propTypeIdRef, + ); + /* get property from interface */ + let res = this.module.if( + ifShapeCompatibal, + ifCompatibalTrue, + ifCompatibalFalse, + ); + + /* If isCall or member is an accessor, call the memberRef, and get the result */ if ( member.type === MemberType.ACCESSOR || - (member.type === MemberType.METHOD && isCall) + (propType.kind === ValueTypeKind.FUNCTION && isCall) ) { - const memberFuncType = memberValueType as FunctionType; - /** - * TODO: Not support optional method in class, that means optional method in interface - * point to undefined or closure. Because of mixed type, so here we only allow - * if (i.x) { - * i.x(); - * } - * that means if i.x is undefined, then we will not call it. - */ + /* if member is GETTER or member is a METHOD, then just callFuncRef, if member is a FIELD, need to callClosureInternal */ + /* now their envParamLen is not equal, field is 1, others is 2 */ if (member.isOptional) { - const wasmMethodType = - this.wasmTypeGen.getWASMValueType(memberFuncType); - const tableIndex = this.module.call( - dyntype.dyntype_to_extref, - [FunctionalFuncs.getDynContextRef(this.module), res], - dyntype.int, - ); - const closure = binaryenCAPI._BinaryenRefCast( - this.module.ptr, - this.module.table.get( - BuiltinNames.extrefTable, - tableIndex, - binaryen.anyref, - ), - wasmMethodType, - ); - res = binaryenCAPI._BinaryenStructGet( - this.module.ptr, - 1, - closure, - wasmMethodType, - false, + /* if member is optional, need to do unbox */ + res = FunctionalFuncs.unboxAnyToExtref( + this.module, + res, + this.wasmTypeGen.getWASMValueType(propType as FunctionType), ); } - res = this.callFuncRef(memberFuncType, res, args, thisRef); + res = this.callClosureInternal( + res, + propType as FunctionType, + args, + thisRef, + ); } return res; } - private getObjMember( + private getObjProperty( member: MemberDescription, + objType: ValueType, thisRef: binaryen.ExpressionRef, - thisTypeRef: binaryen.Type, memberIdx: number, isCall = false, args?: SemanticsValue[], + isSetter = false, ) { + const thisTypeRef = this.wasmTypeGen.getWASMType(objType); + const propType = isSetter + ? (member.setter as VarValue).type + : member.hasGetter + ? (member.getter as VarValue).type + : member.valueType; let res: binaryen.ExpressionRef; + if (member.type === MemberType.FIELD) { res = this.getObjField(thisRef, memberIdx, thisTypeRef); if (isCall) { res = this.callClosureInternal( res, - member.valueType as FunctionType, + propType as FunctionType, args, ); } } else { res = this.getObjMethod(thisRef, memberIdx, thisTypeRef); - if (member.type === MemberType.METHOD && isCall) { - const memberFuncType = member.valueType as FunctionType; - res = this.callFuncRef(memberFuncType, res, args, thisRef); - } - if (member.type === MemberType.ACCESSOR) { - const accessorFuncType = (member.getter! as VarValue).type; - res = this.callFuncRef(accessorFuncType, res, args, thisRef); + if ( + member.type === MemberType.ACCESSOR || + (member.type === MemberType.METHOD && isCall) + ) { + res = this.callFuncRef( + propType as FunctionType, + res, + args, + thisRef, + ); } } return res; } - private getPropIndexOfInfc( + private getPropFlagAndIdxFromObj( meta: binaryen.ExpressionRef, name: binaryen.ExpressionRef, flag: ItableFlag, ) { - return this.module.call( - 'find_index', + const flagAndIndexRef = this.module.call( + BuiltinNames.findPropertyFlagAndIndex, [meta, name, this.module.i32.const(flag)], binaryen.i32, ); + const flagAndIndexVar = this.wasmCompiler.currentFuncCtx!.i32Local(); + const stmts: binaryen.ExpressionRef[] = []; + stmts.push( + this.module.local.set(flagAndIndexVar.index, flagAndIndexRef), + ); + stmts.push( + this.module.local.get(flagAndIndexVar.index, flagAndIndexVar.type), + ); + return this.module.block(null, stmts, flagAndIndexVar.type); + } + + private getPropFlagFromObj(flagAndIndexRef: binaryen.ExpressionRef) { + const flagRef = this.module.i32.and( + flagAndIndexRef, + this.module.i32.const(15), + ); + return flagRef; + } + + private getPropIndexFromObj(flagAndIndexRef: binaryen.ExpressionRef) { + const indexRef = this.module.i32.shr_u( + flagAndIndexRef, + this.module.i32.const(4), + ); + return indexRef; } - private getPropTypeOnIndexOfInfc( + private getPropTypeFromObj( meta: binaryen.ExpressionRef, name: binaryen.ExpressionRef, flag: binaryen.ExpressionRef, ) { return this.module.call( - 'find_type_by_index', + BuiltinNames.findPropertyType, [meta, name, this.module.i32.const(flag)], binaryen.i32, ); @@ -2344,8 +2432,8 @@ export class WASMExpressionGen { } private infcCastToObj(ref: binaryen.ExpressionRef, toType: ObjectType) { - const meta = getWASMObjectMeta(this.module, ref); - const typeIdRef = getFieldFromMetaByOffset( + const meta = FunctionalFuncs.getWASMObjectMeta(this.module, ref); + const typeIdRef = FunctionalFuncs.getFieldFromMetaByOffset( this.module, meta, MetaDataOffset.TYPE_ID_OFFSET, @@ -2391,96 +2479,6 @@ export class WASMExpressionGen { ); } - private getOriObjInfoByFindIdx( - infcRef: binaryen.ExpressionRef, - infcType: ValueType, - member: MemberDescription, - memberValueType: ValueType, - valueIdx: number, - isSet = false, - callMethod = false, - targetValueRef?: binaryen.ExpressionRef, - ) { - const memberName = member.name; - const memberType = member.type; - const optional = member.isOptional; - - /** the type of interface description */ - const infcDescTypeRef = this.wasmTypeGen.getWASMObjOriType(infcType); - const infcTypeIdRef = this.module.i32.const(infcType.typeId); - - const metaRef = getWASMObjectMeta(this.module, infcRef); - const typeIdRef = getFieldFromMetaByOffset( - this.module, - metaRef, - MetaDataOffset.TYPE_ID_OFFSET, - ); - const implIdRef = getFieldFromMetaByOffset( - this.module, - metaRef, - MetaDataOffset.IMPL_ID_OFFSET, - ); - const castedObjRef = binaryenCAPI._BinaryenRefCast( - this.module.ptr, - infcRef, - infcDescTypeRef, - ); - let flag = ItableFlag.FIELD; - if (memberType === MemberType.METHOD) { - flag = ItableFlag.METHOD; - } else if (memberType === MemberType.ACCESSOR) { - if (isSet) { - flag = ItableFlag.SETTER; - } else { - flag = ItableFlag.GETTER; - } - } - - const memberNameRef = this.module.i32.const( - this.wasmCompiler.generateRawString(memberName), - ); - const indexRef = this.getPropIndexOfInfc(metaRef, memberNameRef, flag); - - let ifTrue: binaryen.ExpressionRef; - let ifFalse: binaryen.ExpressionRef; - - if (isSet) { - ifTrue = this.setObjField(castedObjRef, valueIdx, targetValueRef!); - ifFalse = this.dynSetInfcField( - infcRef, - indexRef, - memberValueType, - optional, - this.getPropTypeOnIndexOfInfc(metaRef, memberNameRef, flag), - targetValueRef!, - ); - } else { - ifTrue = callMethod - ? this.getObjMethod(castedObjRef, valueIdx, infcDescTypeRef) - : this.getObjField(castedObjRef, valueIdx, infcDescTypeRef); - ifFalse = this.dynGetInfcField( - infcRef, - indexRef, - memberValueType, - optional, - this.getPropTypeOnIndexOfInfc(metaRef, memberNameRef, flag), - ); - } - const res = this.createInfcAccessInfo( - this.module, - infcTypeIdRef, - typeIdRef, - implIdRef, - ifTrue, - ifFalse, - indexRef, - isSet, - optional, - optional && memberType !== MemberType.FIELD, - ); - return res; - } - private getClassStaticField( member: MemberDescription, meta: ObjectDescription, @@ -2494,7 +2492,7 @@ export class WASMExpressionGen { const name = meta.name + '|static_fields'; const staticFields = binaryenCAPI._BinaryenGlobalGet( this.module.ptr, - getCString(name), + UtilFuncs.getCString(name), staticFieldsTypeRef, ); return binaryenCAPI._BinaryenStructGet( @@ -2514,15 +2512,15 @@ export class WASMExpressionGen { ) { /* Workaround: ShapeGetValue's field index now based on its origin shape, not objectType */ const owner = value.owner; - const meta = owner.shape!.meta; - const member = meta.members[value.index]; + const shapeMeta = owner.shape!.meta; + const shapeMember = shapeMeta.members[value.index]; switch (owner.type.kind) { case ValueTypeKind.UNION: case ValueTypeKind.ANY: { /* let o: A|null = new A; o'field type is real type, not any type */ const objRef = this.wasmExprGen(owner); - const propNameRef = this.getStringOffset(member.name); - const memberType = member.valueType; + const propNameRef = this.getStringOffset(shapeMember.name); + const memberType = shapeMember.valueType; const anyObjProp = FunctionalFuncs.getDynObjProp( this.module, objRef, @@ -2538,134 +2536,176 @@ export class WASMExpressionGen { case ValueTypeKind.OBJECT: { const ownerType = owner.type as ObjectType; const typeMeta = ownerType.meta; + const typeMember = typeMeta.findMember( + shapeMember.name, + ) as MemberDescription; if ( owner instanceof VarValue && owner.ref instanceof ObjectType ) { /* static field get */ - return this.getClassStaticField(member, meta, ownerType); + return this.getClassStaticField( + typeMember, + typeMeta, + ownerType, + ); } else { /* Workaround: ownerType's meta different from shape's meta */ const objRef = this.wasmExprGen(owner); - if (member.type === MemberType.METHOD) { - return this.getInstMethod( - objRef, - ownerType, - typeMeta, - member, - ); - } - return this.getInstMember( + return this.getInstProperty( objRef, ownerType, typeMeta, - member, + typeMember, ); } } case ValueTypeKind.ARRAY: { const objRef = this.wasmExprGen(owner); - if (member.name === 'length') { + if (shapeMember.name === 'length') { return FunctionalFuncs.getArrayRefLen(this.module, objRef); } - throw Error(`unhandle Array field get: ${member.name}`); + throw Error(`unhandle Array field get: ${shapeMember.name}`); } case ValueTypeKind.STRING: { const objRef = this.wasmExprGen(owner); - if (member.name === 'length') { + if (shapeMember.name === 'length') { return FunctionalFuncs.getStringRefLen(this.module, objRef); } - throw Error(`unhandle String field get: ${member.name}`); + throw Error(`unhandle String field get: ${shapeMember.name}`); } default: throw new UnimplementError('Unimplement wasmObjFieldGet'); } } - private dynGetInfcField( - ref: binaryen.ExpressionRef, - index: binaryen.ExpressionRef, - type: ValueType, - optional: boolean, - fieldTypeRef: binaryen.ExpressionRef, + private dynGetInfcProperty( + objRef: binaryen.ExpressionRef, + flagAndIndexRef: binaryen.ExpressionRef, + valueType: ValueType, + isOptional: boolean, + propTypeIdRef: binaryen.ExpressionRef, ) { - const wasmType = this.wasmTypeGen.getWASMType(type); - const typeKind = type.kind; + const wasmType = this.wasmTypeGen.getWASMType(valueType); + const typeKind = valueType.kind; + const indexRef = this.getPropIndexFromObj(flagAndIndexRef); + const flagRef = this.getPropFlagFromObj(flagAndIndexRef); let res: binaryen.ExpressionRef | null = null; - if ( - type instanceof UnionType || - (type instanceof FunctionType && optional) - ) { - return this.dynGetInfcUnionField( - ref, - index, - type, - fieldTypeRef, - optional, + if (valueType instanceof UnionType) { + return this.dynGetInfcUnionProperty( + objRef, + flagAndIndexRef, + valueType, + propTypeIdRef, + isOptional, ); } if (typeKind === ValueTypeKind.BOOLEAN) { res = this.module.call( structdyn.StructDyn.struct_get_indirect_i32, - [ref, index], + [objRef, indexRef], binaryen.i32, ); } else if (typeKind === ValueTypeKind.NUMBER) { res = this.module.call( structdyn.StructDyn.struct_get_indirect_f64, - [ref, index], + [objRef, indexRef], binaryen.f64, ); } else if (typeKind === ValueTypeKind.FUNCTION) { - /** get vtable firstly */ - res = this.module.call( + /* the member can be a field or a method, depend on flagRef */ + /* if is field, the method will be boxed to closure, and should get method from instance directly */ + const closureRef = this.module.call( structdyn.StructDyn.struct_get_indirect_anyref, - [ref, this.module.i32.const(0)], + [objRef, indexRef], binaryen.anyref, ); - res = this.module.call( + const isFieldTrue = binaryenCAPI._BinaryenRefCast( + this.module.ptr, + closureRef, + this.wasmTypeGen.getWASMValueType(valueType), + ); + /* if is method, get vtable firstly, then get method from vtable, finally box method to closureRef */ + const vtableRef = this.module.call( + structdyn.StructDyn.struct_get_indirect_anyref, + [objRef, this.module.i32.const(0)], + binaryen.anyref, + ); + const funcRef = this.module.call( structdyn.StructDyn.struct_get_indirect_funcref, - [res, index], + [vtableRef, indexRef], binaryen.funcref, ); - res = binaryenCAPI._BinaryenRefCast(this.module.ptr, res, wasmType); + const isMethodTrue = this.getClosureOfMethod( + binaryenCAPI._BinaryenRefCast( + this.module.ptr, + funcRef, + wasmType, + ), + valueType as FunctionType, + ); + res = this.module.if( + FunctionalFuncs.isFieldFlag(this.module, flagRef), + isFieldTrue, + this.module.if( + FunctionalFuncs.isMethodFlag(this.module, flagRef), + isMethodTrue, + this.module.unreachable(), + ), + ); + if (isOptional) { + /* if function is optional, then result need to box to any */ + res = this.module.if( + FunctionalFuncs.isPropertyExist( + this.module, + flagAndIndexRef, + ), + FunctionalFuncs.generateDynUndefined(this.module), + FunctionalFuncs.boxNonLiteralToAny( + this.module, + res, + typeKind, + ), + ); + } } else if (wasmType === binaryen.i64) { res = this.module.call( structdyn.StructDyn.struct_get_indirect_i64, - [ref, index], + [objRef, indexRef], binaryen.i32, ); } else if (wasmType === binaryen.f32) { res = this.module.call( structdyn.StructDyn.struct_get_indirect_f32, - [ref, index], + [objRef, indexRef], binaryen.f32, ); } else if (wasmType === binaryen.anyref) { res = this.module.call( structdyn.StructDyn.struct_get_indirect_anyref, - [ref, index], + [objRef, indexRef], binaryen.anyref, ); } else { const obj = this.module.call( structdyn.StructDyn.struct_get_indirect_anyref, - [ref, index], + [objRef, indexRef], binaryen.anyref, ); res = binaryenCAPI._BinaryenRefCast(this.module.ptr, obj, wasmType); } if (!res) { - throw new Error(`get interface field failed, type: ${type}`); + throw new Error(`get interface field failed, type: ${valueType}`); } return res; } - private dynGetInfcUnionField( + + private dynGetInfcUnionProperty( ref: binaryen.ExpressionRef, - index: binaryen.ExpressionRef, - type: UnionType | FunctionType, - indexType: binaryen.ExpressionRef, + flagAndIndexRef: binaryen.ExpressionRef, + type: UnionType, + propertyTypeIdRef: binaryen.ExpressionRef, optional: boolean, ) { /** @@ -2679,11 +2719,11 @@ export class WASMExpressionGen { if (type instanceof UnionType) { types = Array.from(type.types); } - const ifExpr = this.dynGetInfcUnionFieldHelper( + const ifExpr = this.dynGetInfcUnionPropertyHelper( ref, - index, + flagAndIndexRef, types[0], - indexType, + propertyTypeIdRef, ); let curIfExpr = ifExpr; parsedTypes.add(types[0].kind); @@ -2691,11 +2731,11 @@ export class WASMExpressionGen { if (parsedTypes.has(types[i].kind)) { continue; } - const ifExprOfIth = this.dynGetInfcUnionFieldHelper( + const ifExprOfIth = this.dynGetInfcUnionPropertyHelper( ref, - index, + flagAndIndexRef, types[i], - indexType, + propertyTypeIdRef, ); binaryenCAPI._BinaryenIfSetIfFalse(curIfExpr, ifExprOfIth); curIfExpr = ifExprOfIth; @@ -2704,14 +2744,17 @@ export class WASMExpressionGen { if (optional) { const isUndefinedBranch = this.module.if( this.module.i32.eq( - indexType, + propertyTypeIdRef, this.module.i32.const(PredefinedTypeId.ANY), ), FunctionalFuncs.generateDynUndefined(this.module), this.module.unreachable(), ); const branchForOpt = this.module.if( - this.module.i32.eq(indexType, this.module.i32.const(-1)), + this.module.i32.eq( + propertyTypeIdRef, + this.module.i32.const(-1), + ), FunctionalFuncs.generateDynUndefined(this.module), type instanceof FunctionType ? isUndefinedBranch @@ -2728,12 +2771,13 @@ export class WASMExpressionGen { return this.module.block(null, [ifExpr], binaryen.anyref); } - private dynGetInfcUnionFieldHelper( + private dynGetInfcUnionPropertyHelper( ref: binaryen.ExpressionRef, - index: binaryen.ExpressionRef, + flagAndIndexRef: binaryen.ExpressionRef, valueType: ValueType, - type: binaryen.ExpressionRef, + typeIdRef: binaryen.ExpressionRef, ) { + const indexRef = this.getPropIndexFromObj(flagAndIndexRef); let cond: binaryen.ExpressionRef; let ifTrue: binaryen.ExpressionRef; const kind = valueType.kind; @@ -2743,12 +2787,12 @@ export class WASMExpressionGen { switch (kind) { case ValueTypeKind.BOOLEAN: { cond = this.module.i32.eq( - type, + typeIdRef, this.module.i32.const(PredefinedTypeId.BOOLEAN), ); ifTrue = this.module.call( structdyn.StructDyn.struct_get_indirect_i32, - [ref, index], + [ref, indexRef], binaryen.i32, ); ifTrue = FunctionalFuncs.boxBaseTypeToAny( @@ -2760,12 +2804,12 @@ export class WASMExpressionGen { } case ValueTypeKind.NUMBER: { cond = this.module.i32.eq( - type, + typeIdRef, this.module.i32.const(PredefinedTypeId.NUMBER), ); ifTrue = this.module.call( structdyn.StructDyn.struct_get_indirect_f64, - [ref, index], + [ref, indexRef], binaryen.f64, ); ifTrue = FunctionalFuncs.boxBaseTypeToAny( @@ -2778,19 +2822,19 @@ export class WASMExpressionGen { case ValueTypeKind.UNDEFINED: case ValueTypeKind.ANY: { cond = this.module.i32.eq( - type, + typeIdRef, this.module.i32.const(PredefinedTypeId.ANY), ); ifTrue = this.module.call( structdyn.StructDyn.struct_get_indirect_anyref, - [ref, index], + [ref, indexRef], binaryen.anyref, ); break; } case ValueTypeKind.FUNCTION: { cond = this.module.i32.eq( - type, + typeIdRef, this.module.i32.const(PredefinedTypeId.FUNCTION), ); ifTrue = this.module.call( @@ -2800,7 +2844,7 @@ export class WASMExpressionGen { ); ifTrue = this.module.call( structdyn.StructDyn.struct_get_indirect_funcref, - [ifTrue, index], + [ifTrue, indexRef], binaryen.funcref, ); ifTrue = binaryenCAPI._BinaryenRefCast( @@ -2836,10 +2880,13 @@ export class WASMExpressionGen { if (kind === ValueTypeKind.ARRAY) { typeId = PredefinedTypeId.ARRAY; } - cond = this.module.i32.eq(type, this.module.i32.const(typeId)); + cond = this.module.i32.eq( + typeIdRef, + this.module.i32.const(typeId), + ); ifTrue = this.module.call( structdyn.StructDyn.struct_get_indirect_anyref, - [ref, index], + [ref, indexRef], binaryen.anyref, ); ifTrue = FunctionalFuncs.boxNonLiteralToAny( @@ -2851,12 +2898,12 @@ export class WASMExpressionGen { } case ValueTypeKind.NULL: { cond = this.module.i32.eq( - type, + typeIdRef, this.module.i32.const(PredefinedTypeId.NULL), ); ifTrue = this.module.call( structdyn.StructDyn.struct_get_indirect_anyref, - [ref, index], + [ref, indexRef], binaryen.anyref, ); ifTrue = FunctionalFuncs.boxNonLiteralToAny( @@ -2869,19 +2916,27 @@ export class WASMExpressionGen { case ValueTypeKind.INTERFACE: case ValueTypeKind.OBJECT: { cond = this.module.i32.ge_u( - type, + typeIdRef, this.module.i32.const(PredefinedTypeId.CUSTOM_TYPE_BEGIN), ); - ifTrue = this.module.call( - structdyn.StructDyn.struct_get_indirect_anyref, - [ref, index], - binaryen.anyref, - ); - ifTrue = FunctionalFuncs.boxNonLiteralToAny( - this.module, - ifTrue, - kind, + ifTrue = this.module.if( + FunctionalFuncs.isPropertyExist( + this.module, + flagAndIndexRef, + ), + FunctionalFuncs.generateDynUndefined(this.module), + FunctionalFuncs.boxNonLiteralToAny( + this.module, + this.module.call( + structdyn.StructDyn.struct_get_indirect_anyref, + [ref, indexRef], + binaryen.anyref, + ), + kind, + ), ); + // ifTrue = ; + // ifTrue = ; break; } default: { @@ -2894,80 +2949,107 @@ export class WASMExpressionGen { return this.module.if(cond, ifTrue); } - private dynSetInfcField( - ref: binaryen.ExpressionRef, - index: binaryen.ExpressionRef, - type: ValueType, + private dynSetInfcProperty( + objRef: binaryen.ExpressionRef, + flagAndIndexRef: binaryen.ExpressionRef, + valueType: ValueType, optional: boolean, fieldTypeRef: binaryen.ExpressionRef, - value: binaryen.ExpressionRef, + valueRef: binaryen.ExpressionRef, ) { - const wasmType = this.wasmTypeGen.getWASMType(type); - const typeKind = type.kind; + const wasmType = this.wasmTypeGen.getWASMType(valueType); + const typeKind = valueType.kind; + const indexRef = this.getPropIndexFromObj(flagAndIndexRef); + const flagRef = this.getPropFlagFromObj(flagAndIndexRef); let res: binaryen.ExpressionRef | null = null; if ( - type instanceof UnionType || - (type instanceof FunctionType && optional) + valueType instanceof UnionType || + (valueType instanceof FunctionType && optional) ) { - return this.dynSetInfcUnionField( - ref, - index, - type, + return this.dynSetInfcUnionProperty( + objRef, + indexRef, + valueType, fieldTypeRef, optional, - value, + valueRef, ); } if (typeKind === ValueTypeKind.BOOLEAN) { res = this.module.call( structdyn.StructDyn.struct_set_indirect_i32, - [ref, index, value], + [objRef, indexRef, valueRef], binaryen.none, ); } else if (typeKind === ValueTypeKind.NUMBER) { res = this.module.call( structdyn.StructDyn.struct_set_indirect_f64, - [ref, index, value], + [objRef, indexRef, valueRef], binaryen.none, ); } else if (typeKind === ValueTypeKind.FUNCTION) { - res = this.module.call( + /* the member can be a field or a method, depend on flagRef */ + const ifIsField = this.module.i32.eq( + flagRef, + this.module.i32.const(ItableFlag.FIELD), + ); + const ifIsMethod = this.module.i32.eq( + flagRef, + this.module.i32.const(ItableFlag.METHOD), + ); + /* if is field, just set method to instance directly, we should ensure that the value is a closureRef */ + const isFieldTrue = this.module.call( + structdyn.StructDyn.struct_set_indirect_anyref, + [objRef, indexRef, valueRef], + binaryen.none, + ); + /* if is method, get vtable firstly, then set method to vtable, we should ensure that the value is a funcRef */ + const vtableRef = this.module.call( structdyn.StructDyn.struct_get_indirect_anyref, - [ref, this.module.i32.const(0)], + [objRef, this.module.i32.const(0)], binaryen.anyref, ); - res = this.module.call( + const isMethodTrue = this.module.call( structdyn.StructDyn.struct_set_indirect_funcref, - [res, index, value], + [vtableRef, indexRef, valueRef], binaryen.none, ); + res = this.module.if( + ifIsField, + isFieldTrue, + this.module.if( + ifIsMethod, + isMethodTrue, + this.module.unreachable(), + ), + ); } else if (wasmType === binaryen.i64) { res = this.module.call( structdyn.StructDyn.struct_set_indirect_i64, - [ref, index, value], + [objRef, indexRef, valueRef], binaryen.none, ); } else if (wasmType === binaryen.f32) { res = this.module.call( structdyn.StructDyn.struct_set_indirect_f32, - [ref, index, value], + [objRef, indexRef, valueRef], binaryen.none, ); } else { res = this.module.call( structdyn.StructDyn.struct_set_indirect_anyref, - [ref, index, value], + [objRef, indexRef, valueRef], binaryen.none, ); } if (!res) { - throw new Error(`set interface field failed, type: ${type}`); + throw new Error(`set interface field failed, type: ${valueType}`); } return res; } - private dynSetInfcUnionField( + private dynSetInfcUnionProperty( ref: binaryen.ExpressionRef, index: binaryen.ExpressionRef, type: UnionType | FunctionType, @@ -2980,7 +3062,7 @@ export class WASMExpressionGen { if (type instanceof UnionType) { types = Array.from(type.types); } - const ifExpr = this.dynSetInfcUnionFieldHelper( + const ifExpr = this.dynSetInfcUnionPropertyHelper( ref, index, types[0], @@ -2993,7 +3075,7 @@ export class WASMExpressionGen { if (parsedTypes.has(types[i].kind)) { continue; } - const ifExprOfIth = this.dynSetInfcUnionFieldHelper( + const ifExprOfIth = this.dynSetInfcUnionPropertyHelper( ref, index, types[i], @@ -3025,7 +3107,7 @@ export class WASMExpressionGen { return this.module.block(null, [ifExpr], binaryen.anyref); } - private dynSetInfcUnionFieldHelper( + private dynSetInfcUnionPropertyHelper( ref: binaryen.ExpressionRef, index: binaryen.ExpressionRef, valueType: ValueType, @@ -3356,7 +3438,7 @@ export class WASMExpressionGen { staticType.meta, propName, ); - propValueRef = this.getInstMember( + propValueRef = this.getInstProperty( ownerStaticValueRef, staticType, staticType.meta, @@ -3391,18 +3473,9 @@ export class WASMExpressionGen { case ValueTypeKind.OBJECT: { const meta = (owner.type as ObjectType).meta; const foundMember = this.getMemberByName(meta, propName); - const valueIdx = this.getTruthIdx(meta, foundMember); + const propertyIdx = this.getTruthIdx(meta, foundMember); - if (meta.isInterface) { - /* let i: I = xx; i.yy */ - const ownValueRef = this.wasmExprGen(owner); - return this.getInfcMember( - foundMember, - owner.type, - ownValueRef, - valueIdx, - ); - } else if (meta.isObjectClass) { + if (meta.isObjectClass) { /* class A; A.yy */ /* workaround: class get static field is a ShapeGetValue, this can be deleted later */ return this.getClassStaticField( @@ -3414,14 +3487,11 @@ export class WASMExpressionGen { /* let a: A = xx; a.yy */ /* let o = {xx}; o.yy */ const ownValueRef = this.wasmExprGen(owner); - const ownValueTypeRef = this.wasmTypeGen.getWASMType( - owner.type, - ); - return this.getObjMember( + return this.getObjProperty( foundMember, + owner.type, ownValueRef, - ownValueTypeRef, - valueIdx, + propertyIdx, ); } } @@ -3475,14 +3545,14 @@ export class WASMExpressionGen { } case ValueTypeKind.OBJECT: { const objType = ownVarDecl.type as ObjectType; - const meta = objType.meta; - const foundMember = this.getMemberByName(meta, value.name); - return this.setInstField( + const typeMeta = objType.meta; + const typeMember = this.getMemberByName(typeMeta, value.name); + return this.setInstProperty( ownValueRef, - oriValue, objType, - meta, - foundMember, + typeMeta, + typeMember, + oriValue, ); } default: @@ -3554,29 +3624,38 @@ export class WASMExpressionGen { const propertyOffset = this.encodeStringrefToLinearMemory(indexStrRef); /* invoke get_indirect/set_indirect to set prop value to obj */ - const metaRef = getWASMObjectMeta(this.module, ownerRef); - let flag = ItableFlag.FIELD; - if (valueType.kind === ValueTypeKind.FUNCTION) { - flag = ItableFlag.METHOD; - } - const indexRef = this.getPropIndexOfInfc(metaRef, propertyOffset, flag); + const metaRef = FunctionalFuncs.getWASMObjectMeta( + this.module, + ownerRef, + ); + const flag = ItableFlag.ALL; + const flagAndIndexRef = this.getPropFlagAndIdxFromObj( + metaRef, + propertyOffset, + flag, + ); + const fieldTypeRef = this.getPropTypeFromObj( + metaRef, + propertyOffset, + flag, + ); let elemOperation: binaryen.ExpressionRef; if (value.kind === SemanticsValueKind.OBJECT_KEY_SET) { - elemOperation = this.dynSetInfcField( + elemOperation = this.dynSetInfcProperty( ownerRef, - indexRef, + flagAndIndexRef, valueType, false, - this.getPropTypeOnIndexOfInfc(metaRef, propertyOffset, flag), + fieldTypeRef, this.wasmExprGen((value as ElementSetValue).value!), ); } else { - elemOperation = this.dynGetInfcField( + elemOperation = this.dynGetInfcProperty( ownerRef, - indexRef, + flagAndIndexRef, valueType, false, - this.getPropTypeOnIndexOfInfc(metaRef, propertyOffset, flag), + fieldTypeRef, ); } return elemOperation; diff --git a/src/backend/binaryen/wasm_type_gen.ts b/src/backend/binaryen/wasm_type_gen.ts index 139e4629..b3e012ad 100644 --- a/src/backend/binaryen/wasm_type_gen.ts +++ b/src/backend/binaryen/wasm_type_gen.ts @@ -40,7 +40,7 @@ import { MemberType, ObjectDescription, } from '../../semantics/runtime.js'; -import { FunctionalFuncs, UtilFuncs, getCString } from './utils.js'; +import { FunctionalFuncs, UtilFuncs } from './utils.js'; import { BuiltinNames } from '../../../lib/builtin/builtin_name.js'; import { VarValue } from '../../semantics/value.js'; import { needSpecialized } from '../../semantics/type_creator.js'; @@ -642,7 +642,7 @@ export class WASMTypeGen { this.infcObjHeapTypeMap.set(type, wasmClassType.heapTypeRef); } else { /* vtable instance */ - const vtableNameRef = getCString( + const vtableNameRef = UtilFuncs.getCString( `vt-inst${this.structHeapTypeCnt}`, ); const vtableInstance = this.createVtableInst( @@ -862,7 +862,7 @@ export class WASMTypeGen { this.wasmComp.globalInitFuncCtx.insert( binaryenCAPI._BinaryenGlobalSet( this.wasmComp.module.ptr, - getCString(name), + UtilFuncs.getCString(name), binaryenCAPI._BinaryenStructNew( this.wasmComp.module.ptr, arrayToPtr([]).ptr, @@ -874,7 +874,7 @@ export class WASMTypeGen { let staticFieldIdx = 0; const staticFields = binaryenCAPI._BinaryenGlobalGet( this.wasmComp.module.ptr, - getCString(name), + UtilFuncs.getCString(name), this.getWASMStaticFieldsType(type), ); for (const member of metaInfo.members) { @@ -943,7 +943,7 @@ export class WASMTypeGen { binaryenCAPI._BinaryenModuleSetTypeName( this.wasmComp.module.ptr, heapTypeRef, - getCString(name), + UtilFuncs.getCString(name), ); } @@ -1082,7 +1082,7 @@ export class WASMTypeGen { ); /** static fields */ /* vtable instance */ - const vtableNameRef = getCString( + const vtableNameRef = UtilFuncs.getCString( `vt-inst${this.structHeapTypeCnt++}`, ); const vtableTypeRef = this.getWASMVtableType(type); @@ -1304,7 +1304,7 @@ export class WASMTypeGen { /** clazz meta */ binaryenCAPI._BinaryenAddGlobal( this.wasmComp.module.ptr, - getCString(name), + UtilFuncs.getCString(name), staticStructType.typeRef, true, this.wasmComp.module.ref.null( diff --git a/src/backend/index.ts b/src/backend/index.ts index ca5c977f..fc7d16a1 100644 --- a/src/backend/index.ts +++ b/src/backend/index.ts @@ -4,7 +4,7 @@ */ import { ParserContext } from '../frontend.js'; -import { utf16ToUtf8 } from './binaryen/utils.js'; +import { UtilFuncs } from './binaryen/utils.js'; export { ParserContext } from '../frontend.js'; export abstract class Ts2wasmBackend { @@ -66,7 +66,7 @@ export class DataSegmentContext { const offset = this.currentOffset; this.stringOffsetMap.set(str, offset); - const utf8Str = utf16ToUtf8(str); + const utf8Str = UtilFuncs.utf16ToUtf8(str); this.currentOffset += utf8Str.length + 1; const buffer = new Uint8Array(utf8Str.length + 1); diff --git a/tests/samples/obj_elem_get_and_set.ts b/tests/samples/obj_elem_get_and_set.ts index 2cabbeb0..7054b3ee 100644 --- a/tests/samples/obj_elem_get_and_set.ts +++ b/tests/samples/obj_elem_get_and_set.ts @@ -44,9 +44,9 @@ export function obj_set_field() { obj['x'] = 100; console.log(obj['x']); } -/* TODO: assignment between funcref and closureref -export function infc_obj_get_method() { - const obj: I_FUNC = { + +export function obj_get_method() { + const obj = { x: () => 1, y: () => 2, }; @@ -54,21 +54,47 @@ export function infc_obj_get_method() { console.log(a()); } -export function infc_obj_set_method() { +export interface I1 { + [key: string]: any; +} +export type T1 = (params?: I1) => void; +export interface I2 { + [key: string]: T1; +} + +export function infc_obj_get_method() { + const obj: I2 = { + a: (params?: I1) => { + console.log('hi'); + }, + }; + const a = obj['a']; + a(); +} + +/* TODO: assignment between funcref and closureref + * Need to get funcref from closureref + +1. obj['hello']' will be stored in vtable, its envParamLen is 2, we can only see envParamLen = 1 in interface type +so this case will cast fail +export function infc_obj_get_method() { const obj: I_FUNC = { x: () => 1, y: () => 2, + hello() { + return 5; + } }; - obj['x'] = () => 100; - const a = obj['x']; + const a = obj['hello']; console.log(a()); } -export function obj_get_method() { - const obj = { +export function infc_obj_set_method() { + const obj: I_FUNC = { x: () => 1, y: () => 2, }; + obj['x'] = () => 100; const a = obj['x']; console.log(a()); } @@ -82,5 +108,5 @@ export function obj_set_method() { const a = obj['x']; console.log(a()); } -*/ +*/ diff --git a/tools/validate/wamr/validation.json b/tools/validate/wamr/validation.json index fb175234..494b2b9e 100644 --- a/tools/validate/wamr/validation.json +++ b/tools/validate/wamr/validation.json @@ -2926,6 +2926,16 @@ "name": "obj_set_field", "args": [], "result": "100" + }, + { + "name": "obj_get_method", + "args": [], + "result": "1" + }, + { + "name": "infc_obj_get_method", + "args": [], + "result": "hi" } ] },