diff --git a/doc/developer-guide/basic_concepts.md b/doc/developer-guide/basic_concepts.md index c614a728..06b1fb17 100644 --- a/doc/developer-guide/basic_concepts.md +++ b/doc/developer-guide/basic_concepts.md @@ -30,7 +30,7 @@ As shown in the graph above, the fundamental processing principles for different - `user defined interface` is widely used in TypeScript, treating it as dynamic will largely influence the performance, so we introduced `meta` to apply static compilation - `builtin objects` are objects provided by JavaScript runtime, TypeScript defines them as interface for type checking purpose. - - Implementing these built-in objects demands a significant amount of effort, so we treat them as `any` by default, this allows us to immediately use the standard library implementation already available in external environment. (see [fallback](../fallback.md)) + - Implementing these built-in objects demands a significant amount of effort, so we treat them as `any` by default, this allows us to immediately use the standard library implementation already available in external environment. (see [fallback](./fallback.md)) - Simultaneously, we are working on static compilation solutions for selected built-in objects (e.g. `String`, `Array`) to improve performance. The priority of static compilation for these objects is determined based on their actual usage frequency. diff --git a/runtime-library/libdyntype/dynamic-simple/dyn-value/class/date.c b/runtime-library/libdyntype/dynamic-simple/dyn-value/class/date.c index 3db0d53c..b95f03f2 100644 --- a/runtime-library/libdyntype/dynamic-simple/dyn-value/class/date.c +++ b/runtime-library/libdyntype/dynamic-simple/dyn-value/class/date.c @@ -6,45 +6,160 @@ #include "dyn_class.h" #include +/** + * @brief char* to time_t + * + * @param str year-month-day hour:minute:second, like + * "2023-12-23 15:58:12" + * @param t time_t addr + * @return int 0: success + */ +int +strtotime(const char *str, time_t *t) +{ + + int year, month, day, hour, minute, second; + struct tm tm_; + + if (sscanf(str, "%d-%d-%d %d:%d:%d", &year, &month, &day, &hour, &minute, + &second) + != 6) { + return -1; + } + if (hour < 0 || hour > 23) { + return -1; + } + tm_.tm_hour = hour; + + if (minute < 0 || minute > 59) { + return -1; + } + tm_.tm_min = minute; + + if (second < 0 || second > 59) { + return -1; + } + tm_.tm_sec = second; + + tm_.tm_year = year - 1900; + tm_.tm_mon = month - 1; + tm_.tm_mday = day; + tm_.tm_isdst = 0; + *t = mktime(&tm_); + + if (*t == -1) + return -1; + + return 0; +} + /* Constructor (new Date()) */ -DynValue *date_constructor(int argc, DynValue *argv[]) +DynValue * +date_constructor(int argc, DynValue *argv[]) { - DyntypeObject *dyn_obj = - (DyntypeObject *)wasm_runtime_malloc(sizeof(DyntypeObject)); + DyntypeDate *dyn_obj = + (DyntypeDate *)wasm_runtime_malloc(sizeof(DyntypeDate)); if (!dyn_obj) { return NULL; } - if (!init_dyn_object(dyn_obj, DynClassDate)) { + if (!init_dyn_object((DyntypeObject *)dyn_obj, DynClassDate)) { wasm_runtime_free(dyn_obj); return NULL; } + if (argc == 0) { + dyn_obj->time = time(NULL); + } + else if (argc == 1 && argv[0]->class_id == DynClassString) { + DyntypeString *str = (DyntypeString *)argv[0]; + if (strtotime(str->data, &dyn_obj->time) != 0) { + return NULL; + } + } return (DynValue *)dyn_obj; } -DynValue *date_get_full_year(DynValue *this_val, int argc, DynValue *argv[]) +DynValue * +date_get_full_year(DynValue *this_val, int argc, DynValue *argv[]) { - /* TODO */ - return dyn_value_new_undefined(); + DyntypeDate *dyn_obj = (DyntypeDate *)this_val; + struct tm *timeval = localtime(&(dyn_obj->time)); + + return dyn_value_new_number((double)(1900 + timeval->tm_year)); +} + +DynValue * +date_get_month(DynValue *this_val, int argc, DynValue *argv[]) +{ + DyntypeDate *dyn_obj = (DyntypeDate *)this_val; + struct tm *timeval = localtime(&(dyn_obj->time)); + return dyn_value_new_number((double)(timeval->tm_mon)); +} + +DynValue * +date_get_date(DynValue *this_val, int argc, DynValue *argv[]) +{ + DyntypeDate *dyn_obj = (DyntypeDate *)this_val; + struct tm *timeval = localtime(&(dyn_obj->time)); + return dyn_value_new_number((double)(timeval->tm_mday)); +} + +DynValue * +date_get_day(DynValue *this_val, int argc, DynValue *argv[]) +{ + DyntypeDate *dyn_obj = (DyntypeDate *)this_val; + struct tm *timeval = localtime(&(dyn_obj->time)); + return dyn_value_new_number((double)(timeval->tm_wday)); +} + +DynValue * +date_get_hours(DynValue *this_val, int argc, DynValue *argv[]) +{ + DyntypeDate *dyn_obj = (DyntypeDate *)this_val; + struct tm *timeval = localtime(&(dyn_obj->time)); + return dyn_value_new_number((double)(timeval->tm_hour)); +} + +DynValue * +date_get_minutes(DynValue *this_val, int argc, DynValue *argv[]) +{ + DyntypeDate *dyn_obj = (DyntypeDate *)this_val; + struct tm *timeval = localtime(&(dyn_obj->time)); + return dyn_value_new_number((double)(timeval->tm_min)); +} + +DynValue * +date_get_seconds(DynValue *this_val, int argc, DynValue *argv[]) +{ + DyntypeDate *dyn_obj = (DyntypeDate *)this_val; + struct tm *timeval = localtime(&(dyn_obj->time)); + return dyn_value_new_number((double)(timeval->tm_sec)); } /* Date.prototype.xxx */ -ClassMethod date_instance_methods[] = { - { "getFullYear", date_get_full_year } +ClassMethod date_instance_methods[] = { + { "getFullYear", date_get_full_year }, + { "getMonth", date_get_month }, + { "getDate", date_get_date }, + { "getDay", date_get_day }, + { "getHours", date_get_hours }, + { "getMinutes", date_get_minutes }, + { "getSeconds", date_get_seconds }, }; -DynValue *date_now(DynValue *this_val, int argc, DynValue *argv[]) +DynValue * +date_now(DynValue *this_val, int argc, DynValue *argv[]) { // get unix timestamp - time_t now = time(NULL); - return dyn_value_new_number((double)now); + struct timeval start; + gettimeofday(&start, NULL); + return dyn_value_new_number( + (double)(start.tv_sec * 1000 + start.tv_usec / 1000)); } /* Date.xxx */ -ClassMethod date_class_methods[] = { - { "now", date_now } -}; +ClassMethod date_class_methods[] = { { "now", date_now } }; ClassMeta date_class_meta = { .constructor = date_constructor, @@ -53,6 +168,7 @@ ClassMeta date_class_meta = { .inst_methods = date_instance_methods, .class_method_num = sizeof(date_class_methods) / sizeof(ClassMethod), .class_methods = date_class_methods, + .name = "Date" }; /* Date, never free this object */ diff --git a/runtime-library/libdyntype/dynamic-simple/dyn-value/class/dyn_class.c b/runtime-library/libdyntype/dynamic-simple/dyn-value/class/dyn_class.c index 4c0b6da8..0b08e913 100644 --- a/runtime-library/libdyntype/dynamic-simple/dyn-value/class/dyn_class.c +++ b/runtime-library/libdyntype/dynamic-simple/dyn-value/class/dyn_class.c @@ -101,6 +101,8 @@ find_class_constructor(const char *name) if (!meta) continue; + assert(meta->name != NULL); + if (!strcmp(meta->name, name)) return meta->constructor; } diff --git a/runtime-library/libdyntype/dynamic-simple/dyn-value/class/object.c b/runtime-library/libdyntype/dynamic-simple/dyn-value/class/object.c index 6f384a89..4a948cae 100644 --- a/runtime-library/libdyntype/dynamic-simple/dyn-value/class/object.c +++ b/runtime-library/libdyntype/dynamic-simple/dyn-value/class/object.c @@ -26,6 +26,7 @@ ClassMeta object_class_meta = { .parent_class_id = DynClassNone, .class_method_num = sizeof(object_class_methods) / sizeof(ClassMethod), .class_methods = object_class_methods, + .name = "Object" }; /* Object, never free this object */ diff --git a/runtime-library/libdyntype/dynamic-simple/dyn-value/class/string.c b/runtime-library/libdyntype/dynamic-simple/dyn-value/class/string.c index dfcec17e..d9b8530c 100644 --- a/runtime-library/libdyntype/dynamic-simple/dyn-value/class/string.c +++ b/runtime-library/libdyntype/dynamic-simple/dyn-value/class/string.c @@ -56,4 +56,5 @@ ClassMeta string_class_meta = { .parent_class_id = DynClassObject, .inst_method_num = sizeof(string_inst_methods) / sizeof(ClassMethod), .inst_methods = string_inst_methods, + .name = "String" }; diff --git a/runtime-library/libdyntype/dynamic-simple/dyn-value/dyn_value.h b/runtime-library/libdyntype/dynamic-simple/dyn-value/dyn_value.h index 56d90227..7737c48d 100644 --- a/runtime-library/libdyntype/dynamic-simple/dyn-value/dyn_value.h +++ b/runtime-library/libdyntype/dynamic-simple/dyn-value/dyn_value.h @@ -61,6 +61,11 @@ typedef struct DyntypeExtref { int32_t ref; } DyntypeExtref; +typedef struct DyntypeDate { + DyntypeObject base; + time_t time; +} DyntypeDate; + DynValue * dyn_value_new_number(double value); diff --git a/src/backend/binaryen/wasm_expr_gen.ts b/src/backend/binaryen/wasm_expr_gen.ts index 08834448..f209e72e 100644 --- a/src/backend/binaryen/wasm_expr_gen.ts +++ b/src/backend/binaryen/wasm_expr_gen.ts @@ -1950,12 +1950,12 @@ export class WASMExpressionGen { index--; } /** it occupies two slots */ - if (members[i].hasGetter && members[i].hasSetter) { + if (members[i].hasGetter || members[i].hasSetter) { index++; } } - if (isSetter && member.hasGetter) { + if (isSetter) { index++; } diff --git a/src/scope.ts b/src/scope.ts index 059f7178..bee086f5 100644 --- a/src/scope.ts +++ b/src/scope.ts @@ -535,23 +535,6 @@ export class Scope { } } - // shadow copy - copy(scope: Scope) { - scope.kind = this.kind; - scope.name = this.name; - scope.children = this.children; - scope.parent = this.parent; - scope.namedTypeMap = this.namedTypeMap; - scope.debugFilePath = this.debugFilePath; - scope.tempVarArray = this.tempVarArray; - scope.variableArray = this.variableArray; - scope.statementArray = this.statementArray; - scope.localIndex = this.localIndex; - scope.mangledName = this.mangledName; - scope.modifiers = this.modifiers; - if (this.genericOwner) scope.setGenericOwner(this.genericOwner); - } - // process generic specialization specialize(scope: Scope) { scope.kind = this.kind; @@ -587,13 +570,6 @@ export class ClosureEnvironment extends Scope { } } - copy(scope: ClosureEnvironment) { - super.copy(scope); - scope.kind = this.kind; - scope.hasFreeVar = this.hasFreeVar; - scope.contextVariable = this.contextVariable; - } - specialize(scope: ClosureEnvironment) { super.specialize(scope); scope.kind = this.kind; @@ -752,17 +728,6 @@ export class FunctionScope extends ClosureEnvironment { return this._className !== ''; } - copy(funcScope: FunctionScope) { - super.copy(funcScope); - funcScope.kind = this.kind; - funcScope.parameterArray = this.parameterArray; - funcScope.functionType = this.functionType; - funcScope._className = this._className; - funcScope.realParamCtxType = this.realParamCtxType; - funcScope.oriFuncName = this.oriFuncName; - funcScope.debugLocations = this.debugLocations; - } - specialize(funcScope: FunctionScope) { super.specialize(funcScope); funcScope.kind = this.kind; @@ -815,13 +780,6 @@ export class ClassScope extends Scope { return this._classType; } - copy(classScope: ClassScope) { - super.copy(classScope); - classScope.kind = this.kind; - classScope.name = this.name; - classScope._classType = this._classType; - } - specialize(classScope: ClassScope) { super.specialize(classScope); classScope.kind = this.kind; diff --git a/src/variable.ts b/src/variable.ts index 9b2147b8..52edd0b6 100644 --- a/src/variable.ts +++ b/src/variable.ts @@ -254,16 +254,6 @@ export class VariableScanner { if (variableType instanceof TSEnum) { variableType = variableType.memberType; } - /* Sometimes the variable's type is inferred as a TSFunction with - isDeclare == true, we need to treat it as non declare function - here to keep the same calling convention for closure */ - if ( - variableType instanceof TSFunction && - variableType.isDeclare - ) { - variableType = variableType.clone(); - (variableType as TSFunction).isDeclare = false; - } const variable = new Variable( variableName, diff --git a/tests/samples/class_accessor.ts b/tests/samples/class_accessor.ts index 587d1909..c9acf37b 100644 --- a/tests/samples/class_accessor.ts +++ b/tests/samples/class_accessor.ts @@ -311,4 +311,64 @@ export function test15() { const i3: I3 = new Z(1); i3.ref = 2; console.log(i3.ref); +} + +class OnlySetter { + a = 1; + + constructor(a: number) { + this.a = a; + this.foo(); + this.bar(); + } + + foo() { + console.log('invoke foo'); + } + + set value(a: number) { + this.a = a; + } + + bar() { + console.log('invoke bar'); + } +} + +export function testOnlySetter() { + const obj = new OnlySetter(10); + console.log(obj.a); + obj.a = 100; + console.log(obj.a); +} + +class OnlyGetter { + a = 1; + + constructor(a: number) { + this.a = a; + this.foo(); + this.bar(); + } + + foo() { + console.log('invoke foo'); + } + + + get value() : number { + return this.a; + } + + + bar() { + console.log('invoke bar'); + } +} + +export function testOnlyGetter() { + const obj = new OnlyGetter(10); + console.log(obj.value); + obj.a = 100; + console.log(obj.value); } \ No newline at end of file diff --git a/tools/validate/run_module_on_node/run_module_on_node.md b/tools/validate/run_module_on_node/run_module_on_node.md index 395b3322..93c629d9 100644 --- a/tools/validate/run_module_on_node/run_module_on_node.md +++ b/tools/validate/run_module_on_node/run_module_on_node.md @@ -2,6 +2,10 @@ This document describes how to execute WASM module on Node.js. +> Note: Wasmnizer-ts follows the latest WasmGC spec, which requires `V8 v11.9+`, but the latest nodejs (v21.5.0) is using `V8 11.8.172.17`, so currently the generated WASM module can't execute on any nodejs releases. + +> If you do want to try on nodejs, you can reset to commit `94cf9929421d47a9976fa6edf74b25ef2a00ee12` to build the compiler, which is compatible to older V8 versions. + ## Prerequisites - node.js version 20.0.0 or higher diff --git a/tools/validate/wamr/validation.json b/tools/validate/wamr/validation.json index d6cd72eb..931eaad2 100644 --- a/tools/validate/wamr/validation.json +++ b/tools/validate/wamr/validation.json @@ -1752,6 +1752,16 @@ "name": "test15", "args": [], "result": "undefined\n2\n1\n1\n2" + }, + { + "name": "testOnlySetter", + "args": [], + "result": "invoke foo\ninvoke bar\n10\n100" + }, + { + "name": "testOnlyGetter", + "args": [], + "result": "invoke foo\ninvoke bar\n10\n100" } ] },