diff --git a/plugins/ets/runtime/ets_libbase_runtime.yaml b/plugins/ets/runtime/ets_libbase_runtime.yaml index dd96ec4845dcf3744aa4c74a1cd6e57219c767f6..e1449c37a308eb26a8d216102abce65c81e7a9c5 100644 --- a/plugins/ets/runtime/ets_libbase_runtime.yaml +++ b/plugins/ets/runtime/ets_libbase_runtime.yaml @@ -2137,6 +2137,17 @@ intrinsics: ################### # std.core.Value # ################### + - name: ValueAPIGetFieldByName + space: ets + class_name: std.core.ETSGLOBAL + method_name: ValueAPIGetFieldByName + static: true + signature: + ret: std.core.Object + args: [ std.core.Object, std.core.String ] + impl: panda::ets::intrinsics::ValueAPIGetFieldByName + clear_flags: [ ] + - name: ValueAPIGetFieldObject space: ets class_name: std.core.ETSGLOBAL @@ -2440,6 +2451,28 @@ intrinsics: + - name: ValueAPIGetArrayLength + space: ets + class_name: std.core.ETSGLOBAL + method_name: ValueAPIGetArrayLength + static: true + signature: + ret: i64 + args: [ std.core.Object ] + impl: panda::ets::intrinsics::ValueAPIGetArrayLength + clear_flags: [ ] + + - name: ValueAPIGetArrayElement + space: ets + class_name: std.core.ETSGLOBAL + method_name: ValueAPIGetArrayElement + static: true + signature: + ret: std.core.Object + args: [ std.core.Object, i64 ] + impl: panda::ets::intrinsics::ValueAPIGetArrayElement + clear_flags: [ ] + - name: ValueAPISetElementObject space: ets class_name: std.core.ETSGLOBAL diff --git a/plugins/ets/runtime/interop_js/ets_proxy/ets_class_wrapper.cpp b/plugins/ets/runtime/interop_js/ets_proxy/ets_class_wrapper.cpp index 84b25f4a2dd82a55eac8cde3577d151304d27253..a8a33fa75156cc31847692b32418f1f47a905e3a 100644 --- a/plugins/ets/runtime/interop_js/ets_proxy/ets_class_wrapper.cpp +++ b/plugins/ets/runtime/interop_js/ets_proxy/ets_class_wrapper.cpp @@ -274,18 +274,33 @@ std::pair, std::vector> EtsClassWrapper::Calculat INTEROP_FATAL_IF(!it.second); } } - // Collect methods - for (auto &m : klass->GetMethods()) { - if (m.IsPrivate()) { - continue; - } - auto name = m.GetName().data; - if (overloads != nullptr && overloads->find(name) != overloads->end()) { - continue; - } - auto it = props.insert({m.GetName().data, &m}); - if (UNLIKELY(!it.second)) { - fatal_method_overloaded(&m); + + // If class is std.core.Object + auto klass_desc = utf::Mutf8AsCString(klass->GetDescriptor()); + if (klass_desc == panda_file_items::class_descriptors::OBJECT) { + // Ingore all methods of std.core.Object due to names intersection with JS Object + // Keep constructors only + auto obj_ctors = ets_class_->GetConstructors(); + // Assuming that ETS StdLib guarantee that Object has the only one ctor + ASSERT(obj_ctors.size() == 1); + auto ctor = obj_ctors[0]->GetPandaMethod(); + props.insert({ctor->GetName().data, ctor}); + // TODO(shumilov-petr): Think about removing methods from std.core.Object + // that are already presented in JS Object, others should be kept + } else { + // Collect methods + for (auto &m : klass->GetMethods()) { + if (m.IsPrivate()) { + continue; + } + auto name = m.GetName().data; + if (overloads != nullptr && overloads->find(name) != overloads->end()) { + continue; + } + auto it = props.insert({m.GetName().data, &m}); + if (UNLIKELY(!it.second)) { + fatal_method_overloaded(&m); + } } } diff --git a/plugins/ets/runtime/intrinsics/std_core_Type.cpp b/plugins/ets/runtime/intrinsics/std_core_Type.cpp index 5725633c7b4965bc67d449fe5fb158f3b5eaad0e..86389b1be1f44d0e67fd8d9c84604aa1b6daf796 100644 --- a/plugins/ets/runtime/intrinsics/std_core_Type.cpp +++ b/plugins/ets/runtime/intrinsics/std_core_Type.cpp @@ -298,7 +298,7 @@ EtsObject *TypeAPIGetStaticFieldValue(EtsString *owner_td, EtsString *name) using T = EtsTypeEnumToCppType; auto val = owner_type->GetRuntimeClass()->GetFieldPrimitive(*field->GetCoreType()); // SUPPRESS_CSA_NEXTLINE(alpha.core.WasteObjHeader) - return EtsBoxPrimitive::Create(EtsCoroutine::GetCurrent(), val); + return EtsBoxPrimitive::Create(coroutine, val); }); } diff --git a/plugins/ets/runtime/intrinsics/std_core_Value.cpp b/plugins/ets/runtime/intrinsics/std_core_Value.cpp index 108cdc496300ab645be42f6539e3cbedba9db3d0..5aa5611f4a240f3499fe20fed15ff56516b9f6a3 100644 --- a/plugins/ets/runtime/intrinsics/std_core_Value.cpp +++ b/plugins/ets/runtime/intrinsics/std_core_Value.cpp @@ -27,20 +27,44 @@ #include "types/ets_method.h" #include "types/ets_primitives.h" #include "types/ets_type.h" +#include "types/ets_type_comptime_traits.h" #include "types/ets_typeapi.h" #include "types/ets_typeapi_field.h" namespace panda::ets::intrinsics { +EtsObject *ValueAPIGetFieldByName(EtsObject *obj, EtsString *name) +{ + auto coroutine = EtsCoroutine::GetCurrent(); + [[maybe_unused]] HandleScope scope(coroutine); + VMHandle obj_handle(coroutine, obj->GetCoreType()); + VMHandle name_handle(coroutine, name->GetCoreType()); + + auto type_class = obj_handle.GetPtr()->GetClass(); + auto field = type_class->GetFieldByName(name_handle.GetPtr()); + + if (field->GetType()->IsPrimitive()) { + return EtsPrimitiveTypeEnumToComptimeConstant(ConvertPandaTypeToEtsType(field->GetCoreType()->GetType()), + [&](auto type) -> EtsObject * { + using T = EtsTypeEnumToCppType; + auto val = obj_handle.GetPtr()->GetFieldPrimitive(field); + // SUPPRESS_CSA_NEXTLINE(alpha.core.WasteObjHeader) + return EtsBoxPrimitive::Create(coroutine, val); + }); + } + return obj_handle.GetPtr()->GetFieldObject(field); +} + EtsVoid *ValueAPISetFieldObject(EtsObject *obj, EtsLong i, EtsObject *val) { auto coroutine = EtsCoroutine::GetCurrent(); [[maybe_unused]] HandleScope scope(coroutine); VMHandle obj_handle(coroutine, obj->GetCoreType()); + VMHandle val_handle(coroutine, val->GetCoreType()); auto type_class = obj_handle.GetPtr()->GetClass(); auto field_object = type_class->GetFieldByIndex(i); - obj_handle.GetPtr()->SetFieldObject(field_object, val); + obj_handle.GetPtr()->SetFieldObject(field_object, val_handle.GetPtr()); return EtsVoid::GetInstance(); } @@ -113,10 +137,13 @@ EtsVoid *ValueAPISetFieldByNameObject(EtsObject *obj, EtsString *name, EtsObject auto coroutine = EtsCoroutine::GetCurrent(); [[maybe_unused]] HandleScope scope(coroutine); VMHandle obj_handle(coroutine, obj->GetCoreType()); + VMHandle name_handle(coroutine, name->GetCoreType()); + VMHandle val_handle(coroutine, val->GetCoreType()); + auto type_class = obj_handle.GetPtr()->GetClass(); - auto field_object = type_class->GetFieldIDByName(name->GetMutf8().c_str()); - obj_handle.GetPtr()->SetFieldObject(field_object, val); + auto field_object = type_class->GetFieldIDByName(name_handle.GetPtr()->GetMutf8().c_str()); + obj_handle.GetPtr()->SetFieldObject(field_object, val_handle.GetPtr()); return EtsVoid::GetInstance(); } @@ -126,9 +153,10 @@ void SetFieldByNameValue(EtsObject *obj, EtsString *name, T val) auto coroutine = EtsCoroutine::GetCurrent(); [[maybe_unused]] HandleScope scope(coroutine); VMHandle obj_handle(coroutine, obj->GetCoreType()); + VMHandle name_handle(coroutine, name->GetCoreType()); auto type_class = obj_handle.GetPtr()->GetClass(); - auto field_object = type_class->GetFieldIDByName(name->GetMutf8().c_str()); + auto field_object = type_class->GetFieldIDByName(name_handle.GetPtr()->GetMutf8().c_str()); if (field_object->GetType()->IsBoxedClass()) { obj_handle.GetPtr()->SetFieldObject(field_object, EtsBoxPrimitive::Create(coroutine, val)); return; @@ -251,13 +279,44 @@ EtsDouble ValueAPIGetFieldDouble(EtsObject *obj, EtsLong i) return GetFieldValue(obj, i); } +EtsLong ValueAPIGetArrayLength(EtsObject *obj) +{ + auto coroutine = EtsCoroutine::GetCurrent(); + [[maybe_unused]] HandleScope scope(coroutine); + VMHandle arr_handle(coroutine, obj->GetCoreType()); + + return static_cast(arr_handle->GetLength()); +} + +EtsObject *ValueAPIGetArrayElement(EtsObject *obj, EtsLong i) +{ + auto coroutine = EtsCoroutine::GetCurrent(); + [[maybe_unused]] HandleScope scope(coroutine); + auto type_class = obj->GetClass(); + if (type_class->GetComponentType()->IsPrimitive()) { + return EtsPrimitiveTypeEnumToComptimeConstant( + ConvertPandaTypeToEtsType(type_class->GetComponentType()->GetRuntimeClass()->GetType()), + [&](auto type) -> EtsObject * { + using ET = EtsTypeEnumToCppType; + using AT = EtsTypeEnumToEtsArrayType; + VMHandle arr_handle(coroutine, obj->GetCoreType()); + auto val = arr_handle.GetPtr()->Get(i); + // SUPPRESS_CSA_NEXTLINE(alpha.core.WasteObjHeader) + return EtsBoxPrimitive::Create(coroutine, val); + }); + } + VMHandle arr_handle(coroutine, obj->GetCoreType()); + return arr_handle.GetPtr()->Get(i); +} + EtsVoid *ValueAPISetElementObject(EtsObject *obj, EtsLong i, EtsObject *val) { auto coroutine = EtsCoroutine::GetCurrent(); [[maybe_unused]] HandleScope scope(coroutine); VMHandle arr_handle(coroutine, obj->GetCoreType()); + VMHandle val_handle(coroutine, val->GetCoreType()); - arr_handle.GetPtr()->Set(i, val); + arr_handle.GetPtr()->Set(i, val_handle.GetPtr()); return EtsVoid::GetInstance(); } diff --git a/plugins/ets/runtime/types/ets_class.cpp b/plugins/ets/runtime/types/ets_class.cpp index 07cc66b1fe160ece59f3f2fe9113f490c6d6eb9d..74e51af3bbc6990ca2d41b3e4865b247fa784895 100644 --- a/plugins/ets/runtime/types/ets_class.cpp +++ b/plugins/ets/runtime/types/ets_class.cpp @@ -80,6 +80,27 @@ EtsField *EtsClass::GetOwnFieldByIndex(uint32_t i) return EtsField::FromRuntimeField(&GetRuntimeClass()->GetFields()[i]); } +EtsField *EtsClass::GetFieldByName(EtsString *name) +{ + auto coroutine = EtsCoroutine::GetCurrent(); + [[maybe_unused]] HandleScope scope(coroutine); + VMHandle expected_name(coroutine, name->GetCoreType()); + + EtsField *res = nullptr; + EnumerateBaseClasses([&](EtsClass *c) { + auto fields = c->GetRuntimeClass()->GetFields(); + for (auto &f : fields) { + auto ets_field = EtsField::FromRuntimeField(&f); + if (ets_field->GetNameString()->StringsAreEqual(expected_name.GetPtr()->AsObject())) { + res = ets_field; + return true; + } + } + return false; + }); + return res; +} + EtsMethod *EtsClass::GetDirectMethod(const char *name, const char *signature) { auto core_name = reinterpret_cast(name); diff --git a/plugins/ets/runtime/types/ets_class.h b/plugins/ets/runtime/types/ets_class.h index 965688ae0c344b7f993ee38b9e467f1ae09f2ef1..f4722db78d981ffbbf2ec40351b7a354b73f472a 100644 --- a/plugins/ets/runtime/types/ets_class.h +++ b/plugins/ets/runtime/types/ets_class.h @@ -91,6 +91,8 @@ public: EtsField *GetOwnFieldByIndex(uint32_t i); + EtsField *GetFieldByName(EtsString *name); + EtsField *GetFieldIDByName(const char *name, const char *sig = nullptr) { auto u8name = reinterpret_cast(name); diff --git a/plugins/ets/runtime/types/ets_type_comptime_traits.h b/plugins/ets/runtime/types/ets_type_comptime_traits.h index 865f75d7077723a7dfb8d579bb5a0091478c492c..9adaa34565a5d7250483060115aaf5ff89010bc0 100644 --- a/plugins/ets/runtime/types/ets_type_comptime_traits.h +++ b/plugins/ets/runtime/types/ets_type_comptime_traits.h @@ -17,6 +17,7 @@ #include "ets_type.h" #include "ets_primitives.h" +#include "ets_array.h" namespace panda::ets { @@ -69,11 +70,62 @@ template <> struct EtsTypeEnumToCppTypeT { using Type = EtsObject *; }; + +template +struct EtsTypeEnumToEtsArrayTypeT; + +template <> +struct EtsTypeEnumToEtsArrayTypeT { + using Type = EtsBooleanArray; +}; + +template <> +struct EtsTypeEnumToEtsArrayTypeT { + using Type = EtsByteArray; +}; + +template <> +struct EtsTypeEnumToEtsArrayTypeT { + using Type = EtsCharArray; +}; + +template <> +struct EtsTypeEnumToEtsArrayTypeT { + using Type = EtsShortArray; +}; + +template <> +struct EtsTypeEnumToEtsArrayTypeT { + using Type = EtsIntArray; +}; + +template <> +struct EtsTypeEnumToEtsArrayTypeT { + using Type = EtsLongArray; +}; + +template <> +struct EtsTypeEnumToEtsArrayTypeT { + using Type = EtsFloatArray; +}; + +template <> +struct EtsTypeEnumToEtsArrayTypeT { + using Type = EtsDoubleArray; +}; + +template <> +struct EtsTypeEnumToEtsArrayTypeT { + using Type = EtsObjectArray; +}; } // namespace detail template using EtsTypeEnumToCppType = typename detail::EtsTypeEnumToCppTypeT::Type; +template +using EtsTypeEnumToEtsArrayType = typename detail::EtsTypeEnumToEtsArrayTypeT::Type; + /* make switch on EtsType compile-time * to func is passed argument, such that */ diff --git a/plugins/ets/stdlib/escompat/Reflect.ets b/plugins/ets/stdlib/escompat/Reflect.ets new file mode 100644 index 0000000000000000000000000000000000000000..51d0f1709c73bbe131a38db1cba1a019def805c7 --- /dev/null +++ b/plugins/ets/stdlib/escompat/Reflect.ets @@ -0,0 +1,219 @@ +/* + * Copyright (c) 2021-2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package escompat + +export class Reflect { + + private constructor () {} + + /** + * Gets the field of target, equivalent to target.key + * + * @param target the target object on which to get the field + * + * @param key the string name of the field to get + * + * @returns the value of the field + */ + // TODO(shumilov-petr): Make result type `Object | undefined` when `undefined` will be available + public static get(target: Object, key: string): Object | null { + if (target instanceof Char || + target instanceof Boolean || + target instanceof Byte || + target instanceof Short || + target instanceof Int || + target instanceof Long || + target instanceof Float || + target instanceof Double || + target instanceof String) { + throw new Error("`target` argument of Reflect.get must have fields") + } + if (!Reflect.has(target, key)) { + // TODO(shumilov-petr): Replace `null` into `undefined` + // return undefined + return null + } + let t = Type.of(target) + if (t instanceof ClassType) { + return (Value.of(target) as ClassValue).getFieldByName(key).getData() + } else if (t instanceof ArrayType) { + if (key == "length") { + return new Number((Value.of(target) as ArrayValue).getLength()) + } + } else if (t instanceof LambdaType) { + if (key == "length") { + return new Number((t as FunctionType).getParametersNum()) + } else if (key == "name") { + return "" + } + } else if (t instanceof EnumType) { + // TODO(shumilov-petr): Not implemented + throw new Error("Not implemented") + } else if (t instanceof UnionType) { + // TODO(shumilov-petr): Not implemented + throw new Error("Not implemented") + } else if (t instanceof TupleType) { + // TODO(shumilov-petr): Not implemented + throw new Error("Not implemented") + } + // TODO(shumilov-petr): Replace `null` into `undefined` + // return undefined + return null + } + + /** + * Gets the element of target, equivalent to target[index] + * + * @param target the target object on which to get the element + * + * @param key the number index of the element to get + * + * @returns the value of the element + */ + // TODO(shumilov-petr): Make result type `Object | undefined` when `undefined` will be available + public static get(target: Object, index: number): Object | null { + if (target instanceof Char || + target instanceof Boolean || + target instanceof Byte || + target instanceof Short || + target instanceof Int || + target instanceof Long || + target instanceof Float || + target instanceof Double || + target instanceof String) { + throw new Error("`target` argument of Reflect.get must be indexed") + } + if (!Reflect.has(target, index)) { + // TODO(shumilov-petr): Replace `null` into `undefined` + // return undefined + return null + } + let t = Type.of(target) + if (t instanceof ArrayType) { + let av = Value.of(target) as ArrayValue + return av.getElement(index as long).getData() + } else if (t instanceof EnumType) { + // TODO(shumilov-petr): Not implemented + throw new Error("Not implemented") + } else if (t instanceof UnionType) { + // TODO(shumilov-petr): Not implemented + throw new Error("Not implemented") + } else if (t instanceof TupleType) { + // TODO(shumilov-petr): Not implemented + throw new Error("Not implemented") + } + // TODO(shumilov-petr): Replace `null` into `undefined` + // return undefined + return null + } + + /** + * Sets the field of target, equivalent to target.key = value + * + * @param target the target object on which to set the field + * + * @param key the name of the field to set + * + * @param value the value to set. + * + * @returns a Boolean indicating whether or not setting the field was successful + */ + public static set(target: Object, key: string, value: Object): Boolean { + let v = Value.of(target) + let vt = v.getType() + if (vt instanceof ClassType) { + let ct = vt as ClassType + if (!ct.hasField(key)) { + return false + } + let cv = v as ClassValue + try { + cv.setFieldByName(key, Value.of(value)) + } catch (e: Error) { + return false + } + return true + } + return false + } + + /** + * Sets the element of target, equivalent to target[index] = value + * + * @param target the target object on which to set the element + * + * @param index the index of the element to set + * + * @param value the value to set. + * + * @returns a boolean indicating whether or not setting the element was successful + */ + public static set(target: Object, index: number, value: Object): Boolean { + let v = Value.of(target) + let vt = v.getType() + if (vt instanceof ArrayType) { + if (!Reflect.has(target, index)) { + return false + } + let av = v as ArrayValue + try { + av.setElementByIndex(index as long, Value.of(value)) + } catch (e: Error) { + return false + } + return true + } + return false + } + + /** + * Returns the names of the own fields of an object + * + * @param o object that contains the own fields + * + * @returns array representation of names + */ + public static ownKeys(target: Object): string[] { + return Object.getOwnPropertyNames(target) + } + + /** + * Determines whether an object has a field with the specified name + * + * @param target an object + * + * @param key a field name + * + * @returns a boolean indicating whether or not the target has the field + */ + public static has(target: Object, key: string): boolean { + return Object.hasOwn(target, key) + } + + /** + * Determines whether an object has a field with the specified name + * + * @param target an object + * + * @param index an element index + * + * @returns a boolean indicating whether or not the target has the element + */ + public static has(target: Object, index: number): boolean { + return Object.hasOwn(target, index) + } +} + diff --git a/plugins/ets/stdlib/escompat/json.ets b/plugins/ets/stdlib/escompat/json.ets index 153de97b50585023a518bd9525ee4d716c822b9a..061a6294902e79692f9f78e2e9fec468b1a88fcc 100644 --- a/plugins/ets/stdlib/escompat/json.ets +++ b/plugins/ets/stdlib/escompat/json.ets @@ -381,7 +381,7 @@ export class JSON { let cVal = Value.of(object) as ClassValue let unboxString : (v: JSONString) => String = (v: JSONString): String => { return v.value } - let fieldArray = jVal.keys.map(unboxString) + let fieldArray = jVal.keys_.map(unboxString) for (let field_num = 0; field_num < cType.getFieldsNum(); field_num++) { let field : Field = cType.getField(field_num) let indexInJSON = fieldArray.indexOf(field.getName()) @@ -551,7 +551,7 @@ export class JSONParser { } private parseKeyValue(res: JSONObject): JSONObject { - res.keys.push(this.parse(new JSONValue()) as JSONString) + res.keys_.push(this.parse(new JSONValue()) as JSONString) this.getNextChar() if (this.curChar != c':') { throw new SyntaxError("Expected : \",\" at " + this.getCurPosDescr() + " got \"" + this.curChar + "\"") @@ -692,7 +692,7 @@ export class JSONParser { export class JSONValue extends Object {} export class JSONObject extends JSONValue { - keys: Array = new Array() + keys_: Array = new Array() values: Array = new Array() readonly static START_CHAR = c'{' readonly static END_CHAR = c'}' @@ -701,11 +701,11 @@ export class JSONObject extends JSONValue { public override toString(): String { let res = new StringBuilder([JSONObject.START_CHAR]) - for (let i = 0; i < this.keys.length() - 1; ++i) { - res.append("" + this.keys.at(i) + JSONObject.SEPARATOR + this.values.at(i) + JSONObject.DELIMETER) + for (let i = 0; i < this.keys_.length() - 1; ++i) { + res.append("" + this.keys_.at(i) + JSONObject.SEPARATOR + this.values.at(i) + JSONObject.DELIMETER) } - if (this.keys.length() > 0) { - res.append("" + this.keys.at(this.keys.length() - 1) + JSONObject.SEPARATOR + this.values.at(this.keys.length() - 1)) + if (this.keys_.length() > 0) { + res.append("" + this.keys_.at(this.keys_.length() - 1) + JSONObject.SEPARATOR + this.values.at(this.keys_.length() - 1)) } res.append(JSONObject.END_CHAR) return res.toString() diff --git a/plugins/ets/stdlib/std/core/Object.ets b/plugins/ets/stdlib/std/core/Object.ets index 4c5d6a02ae58104870b3e80ea29e046c6a610fb9..f016300bb33e27b636c80203aed60003c0df865a 100644 --- a/plugins/ets/stdlib/std/core/Object.ets +++ b/plugins/ets/stdlib/std/core/Object.ets @@ -15,6 +15,9 @@ package std.core; +// TODO(shumilov-petr): Temporary solution +type Tuple = Object[] + /** * Common ancestor amongst all other classes */ @@ -31,7 +34,7 @@ export class Object { * @returns result of the conversion */ public toString(): String { - return Type.of(this).toString(); + return Value.of(this).toString() } /** @@ -50,9 +53,332 @@ export class Object { * * @returns true if provided object and this instance have same references, false otherwise */ - public equals(to: Object|null): boolean { + public equals(to: NullishType): boolean { return runtime.equals(this, to); } + + /** + * Returns the names of the fields of an object + * + * @param o an object + * + * @returns an array of strings representing the given object's own string-keyed field keys. + */ + public static keys(o: Object): string[] { + // Char, Boolean and Numeric types doesn't have keys + if (o instanceof Char || + o instanceof Boolean || + o instanceof Byte || + o instanceof Short || + o instanceof Int || + o instanceof Long || + o instanceof Float || + o instanceof Double) { + return new string[0] + } + // "Keys" for the string type is enumeration from 0 to str.length - 1 + if (o instanceof String) { + let sv = o as string + let len = sv.length() + let res = new string[len as int] + for (let i = 0; i < len; i++) { + // TODO(shumilov-petr): need to apply more effective way for int to String conversion + res[i] = new Int(i).toString() + } + return res + } + let t = Type.of(o) + if (t instanceof ClassType) { + let ct = t as ClassType + let fnum = ct.getFieldsNum() + let n: int = 0 + for (let i = 0; i < fnum; i++) { + if (!ct.getField(i).isStatic()) { + n++ + } + } + let res = new string[n] + let j: int = 0 + for (let i = 0; i < fnum; i++) { + let f = ct.getField(i) + if (!f.isStatic()) { + res[j] = f.getName() + j++ + } + } + return res + } else if (t instanceof ArrayType) { + let av = Value.of(o) as ArrayValue + let len = av.getLength() + let res = new string[len as int] + for (let i = 0; i < len; i++) { + res[i] = new Int(i).toString() + } + return res + } else if (t instanceof LambdaType) { + return new string[0] + } else if (t instanceof EnumType) { + // TODO(shumilov-petr): Not implemented + throw new Error("Not implemented") + } else if (t instanceof UnionType) { + // TODO(shumilov-petr): Not implemented + throw new Error("Not implemented") + } else if (t instanceof TupleType) { + // TODO(shumilov-petr): Not implemented + throw new Error("Not implemented") + } + assert(false) + } + + /** + * Returns the values of the fields of an object + * + * @param o an object + * + * @returns an array containing the given object's own string-keyed field values + */ + // TODO(shumilov-petr): Need to rename into `values`. Blocked by Object's childs + public static values__(o: Object): Object[] { + if (o instanceof Char || + o instanceof Boolean || + o instanceof Byte || + o instanceof Short || + o instanceof Int || + o instanceof Long || + o instanceof Float || + o instanceof Double) { + return new Object[0] + } + if (o instanceof String) { + let sv = o as string + let len = sv.length() + let res = new Object[len] + for (let i = 0; i < len; i++) { + res[i] = new StringBuilder().append(sv.charAt(i)).toString() + } + return res + } + let t = Type.of(o) + if (t instanceof ClassType) { + let cv = Value.of(o) as ClassValue + let keys = Object.keys(o) + let len = keys.length + let res = new Object[len] + for (let i = 0; i < len; i++) { + res[i] = cv.getFieldByName(keys[i]).getData() + } + return res + } else if (t instanceof ArrayType) { + let av = Value.of(o) as ArrayValue + let len = av.getLength() + let res = new Object[len as int] + for (let i = 0; i < len; i++) { + res[i] = av.getElement(i).getData() + } + return res + } else if (t instanceof LambdaType) { + return new Object[0] + } else if (t instanceof EnumType) { + // TODO(shumilov-petr): Not implemented + throw new Error("Not implemented") + } else if (t instanceof UnionType) { + // TODO(shumilov-petr): Not implemented + throw new Error("Not implemented") + } else if (t instanceof TupleType) { + // TODO(shumilov-petr): Not implemented + throw new Error("Not implemented") + } + assert(false) + } + + /** + * Returns an array of key/values of properties of an object + * + * @param o object that contains the fields + * + * @returns array representation of key/values + */ + public static entries(o: Object): Tuple[] { + if (o instanceof Char || + o instanceof Boolean || + o instanceof Byte || + o instanceof Short || + o instanceof Int || + o instanceof Long || + o instanceof Float || + o instanceof Double) { + return new Tuple[0] + } + if (o instanceof String) { + let sv = o as string + let len = sv.length() + let res = new Tuple[len] + for (let i = 0; i < len; i++) { + res[i] = new Object[2] + res[i][0] = new Int(i).toString() + res[i][1] = new StringBuilder().append(sv.charAt(i)).toString() + } + return res + } + let t = Type.of(o) + if (t instanceof ClassType) { + let cv = Value.of(o) as ClassValue + let keys = Object.keys(o) + let len = keys.length + let res = new Tuple[len] + for (let i = 0; i < len; i++) { + res[i] = new Object[2] + res[i][0] = keys[i] + res[i][1] = cv.getFieldByName(keys[i]).getData() + } + return res + } else if (t instanceof ArrayType) { + let av = Value.of(o) as ArrayValue + let len = av.getLength() + let res = new Tuple[len as int] + for (let i = 0; i < len; i++) { + res[i] = new Object[2] + res[i][0] = new Int(i).toString() + res[i][1] = av.getElement(i).getData() + } + return res + } else if (t instanceof LambdaType) { + return new Tuple[0] + } else if (t instanceof EnumType) { + // TODO(shumilov-petr): Not implemented + throw new Error("Not implemented") + } else if (t instanceof UnionType) { + // TODO(shumilov-petr): Not implemented + throw new Error("Not implemented") + } else if (t instanceof TupleType) { + // TODO(shumilov-petr): Not implemented + throw new Error("Not implemented") + } + assert(false) + } + + /** + * Returns the names of the own fields of an object + * + * @param o object that contains the own fields + * + * @returns array representation of names + */ + public static getOwnPropertyNames(o: Object): string[] { + if (o instanceof Char || + o instanceof Boolean || + o instanceof Byte || + o instanceof Short || + o instanceof Int || + o instanceof Long || + o instanceof Float || + o instanceof Double) { + return new string[0] + } + let t = Type.of(o) + if (t instanceof StringType || t instanceof ArrayType) { + let keys = Object.keys(o) + let len = keys.length + let res = new string[len + 1] + for (let i = 0; i < len; i++) { + res[i] = keys[i] + } + res[len] = "length" + return res + } + if (t instanceof ClassType) { + return Object.keys(o) + } else if (t instanceof LambdaType) { + return ["length", "name"] + } else if (t instanceof EnumType) { + // TODO(shumilov-petr): Not implemented + throw new Error("Not implemented") + } else if (t instanceof UnionType) { + // TODO(shumilov-petr): Not implemented + throw new Error("Not implemented") + } else if (t instanceof TupleType) { + // TODO(shumilov-petr): Not implemented + throw new Error("Not implemented") + } + assert(false) + } + + /** + * Determines whether an object has a field with the specified name + * + * @param key the string name of the field to test + * + * @returns true if the object has the specified field; false otherwise + */ + public hasOwnProperty(key: string): boolean { + let keys = Object.getOwnPropertyNames(this) + let len = keys.length + for(let i = 0; i < len; i++) { + if (keys[i] == key) { + return true + } + } + return false + } + + /** + * Determines whether an object has a element with the specified index + * + * @param index the number index of the element to test + * + * @returns true if the object has the specified element; false otherwise + */ + public hasOwnProperty(index: number): boolean { + if ((this) instanceof String) { + let sv = this as String + let len = sv.length() + let idx = index as long + return (0 <= idx && idx < len) + } + let t = Type.of(this) + if (t instanceof ArrayType) { + let av = Value.of(this) as ArrayValue + let len = av.getLength() + let idx = index as long + return (0 <= idx && idx < len) + } else if (t instanceof EnumType) { + // TODO(shumilov-petr): Not implemented + throw new Error("Not implemented") + } else if (t instanceof UnionType) { + // TODO(shumilov-petr): Not implemented + throw new Error("Not implemented") + } else if (t instanceof TupleType) { + // TODO(shumilov-petr): Not implemented + throw new Error("Not implemented") + } + return false + } + + /** + * Determines whether an object has a field with the specified name + * + * @param target an object + * + * @param key the string name of the field to test + * + * @returns true if the object has the specified field; false otherwise + */ + public static hasOwn(target: Object, key: string): boolean { + return target.hasOwnProperty(key) + } + + /** + * Determines whether an object has a element with the specified index + * + * @param target an object + * + * @param index the number index of the element to test + * + * @returns true if the object has the specified element; false otherwise + */ + public static hasOwn(target: Object, index: number): boolean { + return target.hasOwnProperty(index) + } } export type NullishType = Object | null diff --git a/plugins/ets/stdlib/std/core/Type.ets b/plugins/ets/stdlib/std/core/Type.ets index a2339d2a107fbd6f592ec4d464f394333efc0159..9673d1f5742531a180da2bba54bd3aef06ae7677 100644 --- a/plugins/ets/stdlib/std/core/Type.ets +++ b/plugins/ets/stdlib/std/core/Type.ets @@ -346,8 +346,8 @@ export abstract class Type extends Object { if (this.equals(other)) { return true } else if (other.equals(ObjectType)) { - let isNullable = (this) instanceof UndefinedType || (this) instanceof NullType - return this.isReference() && !isNullable && !((this) instanceof VoidType) + let isNullish = (this) instanceof UndefinedType || (this) instanceof NullType + return this.isReference() && !isNullish && !((this) instanceof VoidType) } return false } @@ -1143,7 +1143,6 @@ export final class ClassType extends Type { } return obj } - } diff --git a/plugins/ets/stdlib/std/core/Value.ets b/plugins/ets/stdlib/std/core/Value.ets index 834d03b610a478c54b6a44ad8b84e6ea21125bb8..174f3b7284cac0f15cabe19f0d56f65646f275bd 100644 --- a/plugins/ets/stdlib/std/core/Value.ets +++ b/plugins/ets/stdlib/std/core/Value.ets @@ -16,6 +16,9 @@ package std.core; // Class Value +native function ValueAPIGetFieldByName(obj: Object, name: string): Object + + native function ValueAPIGetFieldBoolean(obj: Object, i: long): boolean native function ValueAPIGetFieldByte(obj: Object, i: long): byte @@ -73,6 +76,10 @@ native function ValueAPISetFieldByNameLong(obj: Object, name: String, val: long) native function ValueAPISetFieldByNameObject(obj: Object, name: String, val: Object): void // Array Value +native function ValueAPIGetArrayLength(obj: Object): long + +native function ValueAPIGetArrayElement(obj: Object, i: long): Object + native function ValueAPISetElementBoolean(obj: Object, i: long, val: boolean): void native function ValueAPISetElementByte(obj: Object, i: long, val: byte): void @@ -92,38 +99,160 @@ native function ValueAPISetElementLong(obj: Object, i: long, val: long): void native function ValueAPISetElementObject(obj: Object, i: long, val: Object): void export abstract class Value extends Object { + public static of(v: boolean): Value { + return new BooleanValue(BooleanType.VAL, v) + } + + public static of(v: char): Value { + return new CharValue(CharType.VAL, v) + } + + public static of(v: byte): Value { + return new ByteValue(ByteType.VAL, v) + } + + public static of(v: short): Value { + return new ShortValue(ShortType.VAL, v) + } + + public static of(v: int): Value { + return new IntValue(IntType.VAL, v) + } + + public static of(v: long): Value { + return new LongValue(LongType.VAL, v) + } + + public static of(v: float): Value { + return new FloatValue(FloatType.VAL, v) + } + + public static of(v: double): Value { + return new DoubleValue(DoubleType.VAL, v) + } + + // ----- + + public static of(v: Boolean): Value { + return new BooleanValue(BooleanType.REF, v) + } + + public static of(v: Char): Value { + return new CharValue(CharType.REF, v) + } + + public static of(v: Byte): Value { + return new ByteValue(ByteType.REF, v) + } + + public static of(v: Short): Value { + return new ShortValue(ShortType.REF, v) + } + + public static of(v: Int): Value { + return new IntValue(IntType.REF, v) + } + + public static of(v: Long): Value { + return new LongValue(LongType.REF, v) + } + + public static of(v: Float): Value { + return new FloatValue(FloatType.REF, v) + } + + public static of(v: Double): Value { + return new DoubleValue(DoubleType.REF, v) + } + + // ----- + public static of(o: NullishType): Value { + if (o instanceof Value) { + return o as Value + } else if (o instanceof Boolean) { + return new BooleanValue(BooleanType.REF, o as Boolean) + } else if (o instanceof Char) { + return new CharValue(CharType.REF, o as Char) + } else if (o instanceof Byte) { + return new ByteValue(ByteType.REF, o as Byte) + } else if (o instanceof Short) { + return new ShortValue(ShortType.REF, o as Short) + } else if (o instanceof Int) { + return new IntValue(IntType.REF, o as Int) + } else if (o instanceof Long) { + return new LongValue(LongType.REF, o as Long) + } else if (o instanceof Float) { + return new FloatValue(FloatType.REF, o as Float) + } else if (o instanceof Double) { + return new DoubleValue(DoubleType.REF, o as Double) + } + let t = Type.of(o) if (t instanceof NullType) { return NullValue.INSTANCE + } else if (t instanceof VoidType) { + return new VoidValue() + } else if (t instanceof UndefinedType) { + return new UndefinedValue() } else if (t instanceof ClassType) { return new ClassValue(t as ClassType, o!) } else if (t instanceof ArrayType) { return new ArrayValue(t as ArrayType, o!) } else if (t instanceof StringType) { - return new StringValue(t as StringType, o! as String) - } else if (t instanceof BooleanType) { - return new BooleanValue(t as BooleanType, o! as Boolean) - } else if (t instanceof ByteType) { - return new ByteValue(t as ByteType, o! as Byte) - } else if (t instanceof ShortType) { - return new ShortValue(t as ShortType, o! as Short) - } else if (t instanceof CharType) { - return new CharValue(t as CharType, o! as Char) - } else if (t instanceof IntType) { - return new IntValue(t as IntType, o! as Int) - } else if (t instanceof FloatType) { - return new FloatValue(t as FloatType, o! as Float) - } else if (t instanceof DoubleType) { - return new DoubleValue(t as DoubleType, o! as Double) - } else if (t instanceof LongType) { - return new LongValue(t as LongType, o! as Long) - } else { + return new StringValue(t as StringType, o! as string) + } else if (t instanceof LambdaType) { + return new LambdaValue(t as LambdaType, o!) + } else if (t instanceof MethodType) { + throw new Error("The MethodType cannot be instantiated") + } else if (t instanceof EnumType) { + throw new Error("Not implemented") + } else if (t instanceof UnionType) { + throw new Error("Not implemented") + } else if (t instanceof TupleType) { throw new Error("Not implemented") } + // TODO(shumilov-petr): throw `unknown type` exception + assert(false) + } + + // ----- + + public static of(v: boolean[]): Value { + return new ArrayValue(ArrayType.BOOLEAN_VAL, v) + } + + public static of(v: char[]): Value { + return new ArrayValue(ArrayType.CHAR_VAL, v) + } + + public static of(v: byte[]): Value { + return new ArrayValue(ArrayType.BYTE_VAL, v) + } + + public static of(v: short[]): Value { + return new ArrayValue(ArrayType.SHORT_VAL, v) + } + + public static of(v: int[]): Value { + return new ArrayValue(ArrayType.INT_VAL, v) + } + + public static of(v: long[]): Value { + return new ArrayValue(ArrayType.LONG_VAL, v) + } + + public static of(v: float[]): Value { + return new ArrayValue(ArrayType.FLOAT_VAL, v) + } + + public static of(v: double[]): Value { + return new ArrayValue(ArrayType.DOUBLE_VAL, v) } public abstract getType(): Type + + public abstract getData(): Object } export final class ClassValue extends Value { @@ -134,7 +263,7 @@ export final class ClassValue extends Value { return this.typ as Type } - public getData(): Object { + public override getData(): Object { return this.data } @@ -148,7 +277,11 @@ export final class ClassValue extends Value { } public getField(i: long): Value { - let ft = this.typ.getField(i).getType() + let f = this.typ.getField(i) + if (f.isStatic()) { + throw new Error("Field must be not static") + } + let ft = f.getType() if (!ft.isReference()) { if (ft instanceof BooleanType) { return new BooleanValue(ft as BooleanType, ValueAPIGetFieldBoolean(this.data, i)) @@ -171,30 +304,66 @@ export final class ClassValue extends Value { return Value.of(ValueAPIGetFieldObject(this.data, i)) } + public getFieldByName(name: string): Value { + let f = this.typ.getFieldByName(name) + if (f.isStatic()) { + throw new Error("Field must be not static") + } + const ft = f.getType() + const fv = ValueAPIGetFieldByName(this.data, name) + if (ft.isReference()) { + return Value.of(fv) + } + // Underlying value is primitive + if (fv instanceof Boolean) { + return Value.of((fv as Boolean).unboxed()) + } else if (fv instanceof Byte) { + return Value.of((fv as Byte).unboxed()) + } else if (fv instanceof Short) { + return Value.of((fv as Short).unboxed()) + } else if (fv instanceof Char) { + return Value.of((fv as Char).unboxed()) + } else if (fv instanceof Int) { + return Value.of((fv as Int).unboxed()) + } else if (fv instanceof Float) { + return Value.of((fv as Float).unboxed()) + } else if (fv instanceof Double) { + return Value.of((fv as Double).unboxed()) + } else if (fv instanceof Long) { + return Value.of((fv as Long).unboxed()) + } + // Unreachable + assert(false) + } + public setFieldByName(name: string, val: Value) { - let ft = this.typ.getFieldByName(name).getType() + let f = this.typ.getFieldByName(name) + if (f.isStatic()) { + throw new Error("Field must be not static") + } + let ft = f.getType() let vt = val.getType() if (!ft.assignableFrom(vt)) { throw new Error("Cannot assign field of type " + ft + " with value of type " + vt) } if (ft instanceof BooleanType && val instanceof BooleanValue) { - ValueAPISetFieldByNameBoolean(this.data, name, (val as BooleanValue).getData()) + ValueAPISetFieldByNameBoolean(this.data, name, (val as BooleanValue).getValueData()) } else if (ft instanceof ByteType && val instanceof ByteValue) { - ValueAPISetFieldByNameByte(this.data, name, (val as ByteValue).getData()) + ValueAPISetFieldByNameByte(this.data, name, (val as ByteValue).getValueData()) } else if (ft instanceof ShortType && val instanceof ShortValue) { - ValueAPISetFieldByNameShort(this.data, name, (val as ShortValue).getData()) + ValueAPISetFieldByNameShort(this.data, name, (val as ShortValue).getValueData()) } else if (ft instanceof CharType && val instanceof CharValue) { - ValueAPISetFieldByNameChar(this.data, name, (val as CharValue).getData()) + ValueAPISetFieldByNameChar(this.data, name, (val as CharValue).getValueData()) } else if (ft instanceof IntType && val instanceof IntValue) { - ValueAPISetFieldByNameInt(this.data, name, (val as IntValue).getData()) + ValueAPISetFieldByNameInt(this.data, name, (val as IntValue).getValueData()) } else if (ft instanceof FloatType && val instanceof FloatValue) { - ValueAPISetFieldByNameFloat(this.data, name, (val as FloatValue).getData()) + ValueAPISetFieldByNameFloat(this.data, name, (val as FloatValue).getValueData()) } else if (ft instanceof DoubleType && val instanceof DoubleValue) { - ValueAPISetFieldByNameDouble(this.data, name, (val as DoubleValue).getData()) + ValueAPISetFieldByNameDouble(this.data, name, (val as DoubleValue).getValueData()) } else if (ft instanceof LongType && val instanceof LongValue) { - ValueAPISetFieldByNameLong(this.data, name, (val as LongValue).getData()) + ValueAPISetFieldByNameLong(this.data, name, (val as LongValue).getValueData()) } else if (ft instanceof StringType && val instanceof StringValue) { - ValueAPISetFieldByNameObject(this.data, name, (val as StringValue).getData()) + ValueAPISetFieldByNameObject(this.data, name, (val as StringValue).getData() as string) } else if (ft instanceof ArrayType && val instanceof ArrayValue) { ValueAPISetFieldByNameObject(this.data, name, (val as ArrayValue).getData()) } else if (ft instanceof ClassType && val instanceof ClassValue) { @@ -205,29 +374,33 @@ export final class ClassValue extends Value { } public setField(i: long, val: Value) { - let ft = this.typ.getField(i).getType() + let f = this.typ.getField(i) + if (f.isStatic()) { + throw new Error("Field must be not static") + } + let ft = f.getType() let vt = val.getType() if (!ft.assignableFrom(vt)) { throw new Error("Cannot assign field of type " + ft + " with value of type " + vt) } if (ft instanceof BooleanType && val instanceof BooleanValue) { - ValueAPISetFieldBoolean(this.data, i, (val as BooleanValue).getData()) + ValueAPISetFieldBoolean(this.data, i, (val as BooleanValue).getValueData()) } else if (ft instanceof ByteType && val instanceof ByteValue) { - ValueAPISetFieldByte(this.data, i, (val as ByteValue).getData()) + ValueAPISetFieldByte(this.data, i, (val as ByteValue).getValueData()) } else if (ft instanceof ShortType && val instanceof ShortValue) { - ValueAPISetFieldShort(this.data, i, (val as ShortValue).getData()) + ValueAPISetFieldShort(this.data, i, (val as ShortValue).getValueData()) } else if (ft instanceof CharType && val instanceof CharValue) { - ValueAPISetFieldChar(this.data, i, (val as CharValue).getData()) + ValueAPISetFieldChar(this.data, i, (val as CharValue).getValueData()) } else if (ft instanceof IntType && val instanceof IntValue) { - ValueAPISetFieldInt(this.data, i, (val as IntValue).getData()) + ValueAPISetFieldInt(this.data, i, (val as IntValue).getValueData()) } else if (ft instanceof FloatType && val instanceof FloatValue) { - ValueAPISetFieldFloat(this.data, i, (val as FloatValue).getData()) + ValueAPISetFieldFloat(this.data, i, (val as FloatValue).getValueData()) } else if (ft instanceof DoubleType && val instanceof DoubleValue) { - ValueAPISetFieldDouble(this.data, i, (val as DoubleValue).getData()) + ValueAPISetFieldDouble(this.data, i, (val as DoubleValue).getValueData()) } else if (ft instanceof LongType && val instanceof LongValue) { - ValueAPISetFieldLong(this.data, i, (val as LongValue).getData()) + ValueAPISetFieldLong(this.data, i, (val as LongValue).getValueData()) } else if (ft instanceof StringType && val instanceof StringValue) { - ValueAPISetFieldObject(this.data, i, (val as StringValue).getData()) + ValueAPISetFieldObject(this.data, i, (val as StringValue).getData() as string) } else if (ft instanceof ArrayType && val instanceof ArrayValue) { ValueAPISetFieldObject(this.data, i, (val as ArrayValue).getData()) } else if (ft instanceof ClassType && val instanceof ClassValue) { @@ -237,6 +410,22 @@ export final class ClassValue extends Value { } } + public override toString(): string { + const fnum = this.getFieldsNum() + let res = new StringBuilder("{") + for (let i = 0; i < fnum; i++) { + const f = this.typ.getField(i) + const fv = this.getField(i) + res.append(f.getName()) + res.append(": ") + res.append(fv.toString()) + if (i != fnum - 1) { + res.append(", ") + } + } + res.append("}") + return res.toString() + } } export final class ArrayValue extends Value { @@ -247,7 +436,7 @@ export final class ArrayValue extends Value { return this.typ as Type } - public getData(): Object { + public override getData(): Object { return this.data } @@ -256,6 +445,39 @@ export final class ArrayValue extends Value { this.data = data } + public getLength(): long { + return ValueAPIGetArrayLength(this.data) + } + + public getElement(i: long): Value { + const et = this.typ.getElementType() + const ev = ValueAPIGetArrayElement(this.data, i) + if (et.isReference()) { + return Value.of(ev) + } + // Underlying value is primitive + if (ev instanceof Boolean) { + return Value.of((ev as Boolean).unboxed()) + } else if (ev instanceof Byte) { + return Value.of((ev as Byte).unboxed()) + } else if (ev instanceof Short) { + return Value.of((ev as Short).unboxed()) + } else if (ev instanceof Char) { + return Value.of((ev as Char).unboxed()) + } else if (ev instanceof Int) { + return Value.of((ev as Int).unboxed()) + } else if (ev instanceof Float) { + return Value.of((ev as Float).unboxed()) + } else if (ev instanceof Double) { + return Value.of((ev as Double).unboxed()) + } else if (ev instanceof Long) { + return Value.of((ev as Long).unboxed()) + } + // Unreachable + assert(false) + } + + // TODO(shumilov-petr): Need to rename into `setElement` public setElementByIndex(i: long, val: Value) { let et = this.typ.getElementType() let vt = val.getType() @@ -263,23 +485,23 @@ export final class ArrayValue extends Value { throw new Error("Cannot assign array of type " + et + " with value of type " + vt) } if (et instanceof BooleanType && val instanceof BooleanValue) { - ValueAPISetElementBoolean(this.data, i, (val as BooleanValue).getData()) + ValueAPISetElementBoolean(this.data, i, (val as BooleanValue).getValueData()) } else if (et instanceof ByteType && val instanceof ByteValue) { - ValueAPISetElementByte(this.data, i, (val as ByteValue).getData()) + ValueAPISetElementByte(this.data, i, (val as ByteValue).getValueData()) } else if (et instanceof ShortType && val instanceof ShortValue) { - ValueAPISetElementShort(this.data, i, (val as ShortValue).getData()) + ValueAPISetElementShort(this.data, i, (val as ShortValue).getValueData()) } else if (et instanceof CharType && val instanceof CharValue) { - ValueAPISetElementChar(this.data, i, (val as CharValue).getData()) + ValueAPISetElementChar(this.data, i, (val as CharValue).getValueData()) } else if (et instanceof IntType && val instanceof IntValue) { - ValueAPISetElementInt(this.data, i, (val as IntValue).getData()) + ValueAPISetElementInt(this.data, i, (val as IntValue).getValueData()) } else if (et instanceof FloatType && val instanceof FloatValue) { - ValueAPISetElementFloat(this.data, i, (val as FloatValue).getData()) + ValueAPISetElementFloat(this.data, i, (val as FloatValue).getValueData()) } else if (et instanceof DoubleType && val instanceof DoubleValue) { - ValueAPISetElementDouble(this.data, i, (val as DoubleValue).getData()) + ValueAPISetElementDouble(this.data, i, (val as DoubleValue).getValueData()) } else if (et instanceof LongType && val instanceof LongValue) { - ValueAPISetElementLong(this.data, i, (val as LongValue).getData()) + ValueAPISetElementLong(this.data, i, (val as LongValue).getValueData()) } else if (et instanceof StringType && val instanceof StringValue) { - ValueAPISetElementObject(this.data, i, (val as StringValue).getData()) + ValueAPISetElementObject(this.data, i, (val as StringValue).getData() as string) } else if (et instanceof ArrayType && val instanceof ArrayValue) { ValueAPISetElementObject(this.data, i, (val as ArrayValue).getData()) } else if (et instanceof ClassType && val instanceof ClassValue) { @@ -288,6 +510,46 @@ export final class ArrayValue extends Value { throw new Error("Cannot assign array of type " + et + " with value of type " + vt) } } + + public override toString(): string { + const len = this.getLength() + let res = new StringBuilder("[") + for (let i = 0; i < len; i++) { + res.append(this.getElement(i).toString()) + if (i != len - 1) { + res.append(", ") + } + } + res.append("]") + return res.toString() + } +} + +export final class LambdaValue extends Value { + private typ: LambdaType + private data: Object + + public override getType(): Type { + return this.typ as Type + } + + public override getData(): Object { + return this.data + } + + internal constructor(typ: LambdaType, data: Object) { + this.typ = typ + this.data = data + } + + public invoke(...args: Object[]): Object { + // TODO(shumilov-petr): not implemented + throw new Error("Not implemented") + } + + public override toString(): string { + return this.typ.toString() + } } export final class BooleanValue extends Value { @@ -298,7 +560,11 @@ export final class BooleanValue extends Value { return this.typ as Type } - public getData(): Boolean { + public override getData(): Object { + return new Boolean(this.data) + } + + public getValueData(): boolean { return this.data } @@ -310,6 +576,10 @@ export final class BooleanValue extends Value { internal constructor(typ: BooleanType, data: Boolean) { this(typ, data.unboxed()) } + + public override toString(): string { + return new StringBuilder().append(this.data).toString() + } } export final class ByteValue extends Value { @@ -320,7 +590,11 @@ export final class ByteValue extends Value { return this.typ as Type } - public getData(): Byte { + public override getData(): Object { + return new Byte(this.data) + } + + public getValueData(): byte { return this.data } @@ -332,6 +606,10 @@ export final class ByteValue extends Value { internal constructor(typ: ByteType, data: Byte) { this(typ, data.unboxed()) } + + public override toString(): string { + return new StringBuilder().append(this.data).toString() + } } export final class ShortValue extends Value { @@ -342,7 +620,11 @@ export final class ShortValue extends Value { return this.typ as Type } - public getData(): Short { + public override getData(): Object { + return new Short(this.data) + } + + public getValueData(): short { return this.data } @@ -354,6 +636,10 @@ export final class ShortValue extends Value { internal constructor(typ: ShortType, data: Short) { this(typ, data.unboxed()) } + + public override toString(): string { + return new StringBuilder().append(this.data).toString() + } } export final class CharValue extends Value { @@ -364,7 +650,11 @@ export final class CharValue extends Value { return this.typ as Type } - public getData(): Char { + public override getData(): Object { + return new Char(this.data) + } + + public getValueData(): char { return this.data } @@ -376,6 +666,10 @@ export final class CharValue extends Value { internal constructor(typ: CharType, data: Char) { this(typ, data.unboxed()) } + + public override toString(): string { + return new StringBuilder("\"").append(this.data).append("\"").toString() + } } export final class IntValue extends Value { @@ -386,7 +680,11 @@ export final class IntValue extends Value { return this.typ as Type } - public getData(): Int { + public override getData(): Object { + return new Int(this.data) + } + + public getValueData(): int { return this.data } @@ -398,6 +696,10 @@ export final class IntValue extends Value { internal constructor(typ: IntType, data: Int) { this(typ, data.unboxed()) } + + public override toString(): string { + return new StringBuilder().append(this.data).toString() + } } export final class FloatValue extends Value { @@ -408,7 +710,11 @@ export final class FloatValue extends Value { return this.typ as Type } - public getData(): Float { + public override getData(): Object { + return new Float(this.data) + } + + public getValueData(): float { return this.data } @@ -420,6 +726,10 @@ export final class FloatValue extends Value { internal constructor(typ: FloatType, data: Float) { this(typ, data.unboxed()) } + + public override toString(): string { + return new StringBuilder().append(this.data).toString() + } } export final class DoubleValue extends Value { @@ -430,7 +740,11 @@ export final class DoubleValue extends Value { return this.typ as Type } - public getData(): Double { + public override getData(): Object { + return this.data + } + + public getValueData(): double { return this.data } @@ -442,6 +756,10 @@ export final class DoubleValue extends Value { internal constructor(typ: DoubleType, data: Double) { this(typ, data.unboxed()) } + + public override toString(): string { + return new StringBuilder().append(this.data).toString() + } } export final class LongValue extends Value { @@ -452,7 +770,11 @@ export final class LongValue extends Value { return this.typ as Type } - public getData(): Long { + public override getData(): Object { + return new Long(this.data) + } + + public getValueData(): long { return this.data } @@ -464,17 +786,21 @@ export final class LongValue extends Value { internal constructor(typ: LongType, data: Long) { this(typ, data.unboxed()) } + + public override toString(): string { + return new StringBuilder().append(this.data).toString() + } } export final class StringValue extends Value { private typ: StringType - private data: String + private data: string public override getType(): Type { return this.typ as Type } - public getData(): String { + public override getData(): Object { return this.data } @@ -482,6 +808,10 @@ export final class StringValue extends Value { this.typ = typ this.data = data } + + public override toString(): string { + return new StringBuilder("\"").append(this.data).append("\"").toString() + } } export final class NullValue extends Value { @@ -491,15 +821,50 @@ export final class NullValue extends Value { return NullType.REF as Type } + public override getData(): Object { + return NullValue.INSTANCE + } + internal constructor() {} + + public override toString(): string { + return NullValue.INSTANCE.toString() + } } export final class UndefinedValue extends Value { + // TODO(shumilov-petr): replace `new UndefinedValue() into `undefined` when one will be available public static readonly INSTANCE = new UndefinedValue() public override getType(): Type { return UndefinedType.REF as Type } + public override getData(): Object { + return UndefinedValue.INSTANCE + } + internal constructor() {} + + public override toString(): string { + return UndefinedValue.INSTANCE.toString() + } +} + +export final class VoidValue extends Value { + public static readonly INSTANCE = Void + + public override getType(): Type { + return VoidType.REF as Type + } + + public override getData(): Object { + return VoidValue.INSTANCE + } + + internal constructor() {} + + public override toString(): string { + return VoidValue.INSTANCE.toString() + } } diff --git a/plugins/ets/tests/ets-templates/03.types/08.reference_types/01.objects/string_concatenation_0.ets b/plugins/ets/tests/ets-templates/03.types/08.reference_types/01.objects/string_concatenation_0.ets index 5b2bcd27ee1906be28d1d745bf6c20ed48f5d5f9..6f711414baaef0b32e27e677000c02e6831e578b 100644 --- a/plugins/ets/tests/ets-templates/03.types/08.reference_types/01.objects/string_concatenation_0.ets +++ b/plugins/ets/tests/ets-templates/03.types/08.reference_types/01.objects/string_concatenation_0.ets @@ -23,5 +23,5 @@ function main(): void { let a: Object = new A(); let b: String = "as"; let c: String = a + b; - assert c == "Aas"; + assert c == "{}as"; } diff --git a/plugins/ets/tests/ets_func_tests/escompat/Reflect.ets b/plugins/ets/tests/ets_func_tests/escompat/Reflect.ets new file mode 100644 index 0000000000000000000000000000000000000000..35216f24fe8064989b4c21bc4663cf93cc5ab677 --- /dev/null +++ b/plugins/ets/tests/ets_func_tests/escompat/Reflect.ets @@ -0,0 +1,380 @@ +/* + * Copyright (c) 2021-2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +function main(): int { + let failures: int = 0; + + // get + failures += test(reflectGetBadCases(), "Reflect.get on types without fields and elements"); + failures += test(reflectGetClass(), "Reflect.get on class type"); + failures += test(reflectGetArray(), "Reflect.get on array type"); + failures += test(reflectGetLambda(), "Reflect.get on function type"); + + // set + failures += test(reflectSetClass(), "Reflect.set on class type"); + failures += test(reflectSetArray(), "Reflect.set on array type"); + + // ownKeys + failures += test(reflectOwnKeys(), "Reflect.ownKeys"); + + // has + failures += test(reflectHas(), "Reflect.has"); + + return test(failures, "All tests run"); +} + +function reflectGetBadCases(): int { + let c: char = c'c' + let bo: boolean = true + let bt: byte = 10 + let sh: short = 20 + let i: int = 30 + let lo: long = 40 + let fl: float = 50.0 + let dou: double = 60.0 + let s: string = "abc" + + let result: int = 0 + try { + Reflect.get(c, "a") + } catch (e: Error) { + result += e.toString().contains("`target` argument of Reflect.get must have fields", 0) ? 0 : 1 + } + + try { + Reflect.get(bo, "a") + } catch (e: Error) { + result += e.toString().contains("`target` argument of Reflect.get must have fields", 0) ? 0 : 1 + } + + try { + Reflect.get(bt, "a") + } catch (e: Error) { + result += e.toString().contains("`target` argument of Reflect.get must have fields", 0) ? 0 : 1 + } + + try { + Reflect.get(sh, "a") + } catch (e: Error) { + result += e.toString().contains("`target` argument of Reflect.get must have fields", 0) ? 0 : 1 + } + + try { + Reflect.get(i, "a") + } catch (e: Error) { + result += e.toString().contains("`target` argument of Reflect.get must have fields", 0) ? 0 : 1 + } + + try { + Reflect.get(lo, "a") + } catch (e: Error) { + result += e.toString().contains("`target` argument of Reflect.get must have fields", 0) ? 0 : 1 + } + + try { + Reflect.get(fl, "a") + } catch (e: Error) { + result += e.toString().contains("`target` argument of Reflect.get must have fields", 0) ? 0 : 1 + } + + try { + Reflect.get(dou, "a") + } catch (e: Error) { + result += e.toString().contains("`target` argument of Reflect.get must have fields", 0) ? 0 : 1 + } + + try { + Reflect.get(s, "a") + } catch (e: Error) { + result += e.toString().contains("`target` argument of Reflect.get must have fields", 0) ? 0 : 1 + } + + // ---------- + + try { + Reflect.get(c, 1) + } catch (e: Error) { + result += e.toString().contains("`target` argument of Reflect.get must be indexed", 0) ? 0 : 1 + } + + try { + Reflect.get(bo, 1) + } catch (e: Error) { + result += e.toString().contains("`target` argument of Reflect.get must be indexed", 0) ? 0 : 1 + } + + try { + Reflect.get(bt, 1) + } catch (e: Error) { + result += e.toString().contains("`target` argument of Reflect.get must be indexed", 0) ? 0 : 1 + } + + try { + Reflect.get(sh, 1) + } catch (e: Error) { + result += e.toString().contains("`target` argument of Reflect.get must be indexed", 0) ? 0 : 1 + } + + try { + Reflect.get(i, 1) + } catch (e: Error) { + result += e.toString().contains("`target` argument of Reflect.get must be indexed", 0) ? 0 : 1 + } + + try { + Reflect.get(lo, 1) + } catch (e: Error) { + result += e.toString().contains("`target` argument of Reflect.get must be indexed", 0) ? 0 : 1 + } + + try { + Reflect.get(fl, 1) + } catch (e: Error) { + result += e.toString().contains("`target` argument of Reflect.get must be indexed", 0) ? 0 : 1 + } + + try { + Reflect.get(dou, 1) + } catch (e: Error) { + result += e.toString().contains("`target` argument of Reflect.get must be indexed", 0) ? 0 : 1 + } + + try { + Reflect.get(s, 1) + } catch (e: Error) { + result += e.toString().contains("`target` argument of Reflect.get must be indexed", 0) ? 0 : 1 + } + + return result +} + +class Point2D { + static axisnum: number = 2 + x: number + y: number + constructor (x_: number, y_: number) { + this.x = x_ + this.y = y_ + } +} + +class Point3D extends Point2D { + static axisnum: number = 3 + z: number + constructor (x_: number, y_: number, z_: number) { + super(x_, y_) + this.z = z_ + } +} + +function reflectGetClass(): int { + let result: int = 0 + + let p: Point2D = new Point3D(10, 20, 30) + + result += (Reflect.get(p, "x") as Number == 10) ? 0 : 1 + result += (Reflect.get(p, "y") as Number == 20) ? 0 : 1 + result += (Reflect.get(p, "z") as Number == 30) ? 0 : 1 + + // TODO(shumilov-petr): Replace `null` into `undefined` + result += (Reflect.get(p, "axisnum") == null) ? 0 : 1 + result += (Reflect.get(p, "qwerty") == null) ? 0 : 1 + result += (Reflect.get(p, 1) == null) ? 0 : 1 + + return result +} + +function reflectGetArray(): int { + let result: int = 0 + + let arr: number[] = [10, 20, 30] + let brr: string[] = ["p", "q", "t", "w"] + + result += (Reflect.get(arr, 0) as Number == 10) ? 0 : 1 + result += (Reflect.get(arr, 1) as Number == 20) ? 0 : 1 + result += (Reflect.get(arr, 2) as Number == 30) ? 0 : 1 + + result += (Reflect.get(brr, 0) == "p") ? 0 : 1 + result += (Reflect.get(brr, 1) == "q") ? 0 : 1 + result += (Reflect.get(brr, 2) == "t") ? 0 : 1 + + result += (Reflect.get(arr, "length") as Number == 3) ? 0 : 1 + result += (Reflect.get(brr, "length") as Number == 4) ? 0 : 1 + + // TODO(shumilov-petr): Replace `null` into `undefined` + result += (Reflect.get(arr, "qwerty") == null) ? 0 : 1 + result += (Reflect.get(arr, 10) == null) ? 0 : 1 + result += (Reflect.get(brr, 4) == null) ? 0 : 1 + + return result +} + +function reflectGetLambda(): int { + let result: int = 0 + + let lambda: (a: number) => number = (a: number): number => { + return a + 1 + } + + result += (Reflect.get(lambda, 0) == null) ? 0 : 1 + result += (Reflect.get(lambda, "length") as Number == 1) ? 0 : 1 + result += (Reflect.get(lambda, "name") == "") ? 0 : 1 + + // TODO(shumilov-petr): Replace `null` into `undefined` + result += (Reflect.get(lambda, "qwerty") == null) ? 0 : 1 + + return result +} + +function reflectSetClass(): int { + let result: int = 0 + + let p: Point2D = new Point3D(10, 20, 30) + + result += (Reflect.set(p, "x", 40 as number) == true) ? 0 : 1 + result += (Reflect.set(p, "y", 50 as number) == true) ? 0 : 1 + result += (Reflect.set(p, "z", 60 as number) == true) ? 0 : 1 + + result += (Reflect.get(p, "x") as Number == 40) ? 0 : 1 + result += (Reflect.get(p, "y") as Number == 50) ? 0 : 1 + result += (Reflect.get(p, "z") as Number == 60) ? 0 : 1 + + // TODO(shumilov-petr): Replace `null` into `undefined` + result += (Reflect.set(p, "axisnum", 10 as number) == false) ? 0 : 1 + result += (Reflect.set(p, "x", "string") == false) ? 0 : 1 + + return result +} + +function reflectSetArray(): int { + let result: int = 0 + + let arr: number[] = [10, 20, 30] + let brr: string[] = ["p", "q", "t", "w"] + + result += (Reflect.set(arr, 0, 40 as number) == true) ? 0 : 1 + result += (Reflect.set(arr, 1, 50 as number) == true) ? 0 : 1 + result += (Reflect.set(arr, 2, 60 as number) == true) ? 0 : 1 + + result += (Reflect.get(arr, 0) as Number == 40) ? 0 : 1 + result += (Reflect.get(arr, 1) as Number == 50) ? 0 : 1 + result += (Reflect.get(arr, 2) as Number == 60) ? 0 : 1 + + // TODO(shumilov-petr): Replace `null` into `undefined` + result += (Reflect.set(arr, 100, 10 as number) == false) ? 0 : 1 + result += (Reflect.set(arr, 0, "string") == false) ? 0 : 1 + + return result +} + +function arraysAreEqual(a: string[], b: string[]): boolean { + let alen = a.length + if (alen != b.length) { + return false + } + for (let i = 0; i < alen; i++) { + if (a[i] != b[i]) { + return false + } + } + return true +} + +function reflectOwnKeys(): int { + let result: int = 0 + + let c: char = c'c' + let bo: boolean = true + let bt: byte = 10 + let sh: short = 20 + let i: int = 30 + let lo: long = 40 + let fl: float = 50.0 + let dou: double = 60.0 + + let arr: number[] = [10, 20, 30] + let str: string = "abc" + + let cl: Point2D = new Point3D(10, 20, 30) + + let lambda: (a: number) => number = (a: number): number => { + return a + 1 + } + + let emptyArr: string[] = [] + + result += arraysAreEqual(Reflect.ownKeys(c), emptyArr) ? 0 : 1 + result += arraysAreEqual(Reflect.ownKeys(bo), emptyArr) ? 0 : 1 + result += arraysAreEqual(Reflect.ownKeys(bt), emptyArr) ? 0 : 1 + result += arraysAreEqual(Reflect.ownKeys(sh), emptyArr) ? 0 : 1 + result += arraysAreEqual(Reflect.ownKeys(i), emptyArr) ? 0 : 1 + result += arraysAreEqual(Reflect.ownKeys(lo), emptyArr) ? 0 : 1 + result += arraysAreEqual(Reflect.ownKeys(fl), emptyArr) ? 0 : 1 + result += arraysAreEqual(Reflect.ownKeys(dou), emptyArr) ? 0 : 1 + + result += arraysAreEqual(Reflect.ownKeys(arr), ["0", "1", "2", "length"]) ? 0 : 1 + result += arraysAreEqual(Reflect.ownKeys(str), ["0", "1", "2", "length"]) ? 0 : 1 + + result += arraysAreEqual(Reflect.ownKeys(cl), ["x", "y", "z"]) ? 0 : 1 + + result += arraysAreEqual(Reflect.ownKeys(lambda), ["length", "name"]) ? 0 : 1 + + return result +} + +function reflectHas(): int { + let result: int = 0 + + let arr: number[] = [10, 20, 30] + let str: string = "abc" + + let cl: Point2D = new Point3D(10, 20, 30) + + let lambda: (a: number) => number = (a: number): number => { + return a + 1 + } + + result += (Reflect.hasOwn(arr, 0) == true) ? 0 : 1 + result += (Reflect.hasOwn(arr, 3) == false) ? 0 : 1 + result += (Reflect.hasOwn(arr, "length") == true) ? 0 : 1 + result += (Reflect.hasOwn(arr, "qwerty") == false) ? 0 : 1 + + result += (Reflect.hasOwn(str, 0) == true) ? 0 : 1 + result += (Reflect.hasOwn(str, 3) == false) ? 0 : 1 + result += (Reflect.hasOwn(str, "length") == true) ? 0 : 1 + result += (Reflect.hasOwn(str, "qwerty") == false) ? 0 : 1 + + result += (Reflect.hasOwn(cl, "x") == true) ? 0 : 1 + result += (Reflect.hasOwn(cl, "y") == true) ? 0 : 1 + result += (Reflect.hasOwn(cl, "z") == true) ? 0 : 1 + result += (Reflect.hasOwn(cl, "axisnum") == false) ? 0 : 1 + result += (Reflect.hasOwn(cl, "asdasd") == false) ? 0 : 1 + result += (Reflect.hasOwn(cl, 0) == false) ? 0 : 1 + + result += (Reflect.hasOwn(lambda, "length") == true) ? 0 : 1 + result += (Reflect.hasOwn(lambda, "name") == true) ? 0 : 1 + result += (Reflect.hasOwn(lambda, 0) == false) ? 0 : 1 + + return result +} + +function test(result: int, message: String ): int { + if (result == 0) { + console.log('PASSED: ' + message); + return 0; + } + console.log('FAILED: ' + message); + return 1; +} diff --git a/plugins/ets/tests/ets_func_tests/std/core/Object.ets b/plugins/ets/tests/ets_func_tests/std/core/Object.ets new file mode 100644 index 0000000000000000000000000000000000000000..919521c9c82c3517ac1d0db912e456b88ea71a54 --- /dev/null +++ b/plugins/ets/tests/ets_func_tests/std/core/Object.ets @@ -0,0 +1,335 @@ +/* + * Copyright (c) 2021-2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +function main(): int { + let failures: int = 0; + + failures += test(objectKeys(), "Object.keys"); + + failures += test(objectValues(), "Object.values"); + + failures += test(objectEntries(), "Object.entries"); + + failures += test(objectGetOwnPropertyNames(), "Object.getOwnPropertyNames"); + + failures += test(objectHasOwnProperty(), "Object.hasOwnProperty"); + + return test(failures, "All tests run"); +} + +type Tuple = Object[] + +class Point2D { + static axisnum: number = 2 + x: number + y: number + constructor (x_: number, y_: number) { + this.x = x_ + this.y = y_ + } +} + +class Point3D extends Point2D { + static axisnum: number = 3 + z: number + constructor (x_: number, y_: number, z_: number) { + super(x_, y_) + this.z = z_ + } +} + +function strArraysAreEqual(a: string[], b: string[]): boolean { + let alen = a.length + if (alen != b.length) { + return false + } + for (let i = 0; i < alen; i++) { + if (a[i] != b[i]) { + return false + } + } + return true +} + +function objArraysAreEqual(a: Object[], b: Object[]): boolean { + let alen = a.length + if (alen != b.length) { + return false + } + for (let i = 0; i < alen; i++) { + if (!(a[i].equals(b[i]))) { + return false + } + } + return true +} + +function tupleArraysAreEqual(a: Tuple[], b: Tuple[]): boolean { + let alen = a.length + if (alen != b.length) { + return false + } + for (let i = 0; i < alen; i++) { + if (a[i].length != b[i].length) { + return false + } + let aalen = a[i].length + for (let j = 0; j < aalen; j++) { + if (!(a[i][j].equals(b[i][j]))) { + return false + } + } + } + return true +} + +function objectKeys(): int { + let result: int = 0 + + let c: char = c'c' + let bo: boolean = true + let bt: byte = 10 + let sh: short = 20 + let i: int = 30 + let lo: long = 40 + let fl: float = 50.0 + let dou: double = 60.0 + + let arr: number[] = [10, 20, 30] + let str: string = "abc" + + let cl: Point2D = new Point3D(10, 20, 30) + + let lambda: (a: number) => number = (a: number): number => { + return a + 1 + } + + let emptyArr: string[] = [] + + result += strArraysAreEqual(Object.keys(c), emptyArr) ? 0 : 1 + result += strArraysAreEqual(Object.keys(bo), emptyArr) ? 0 : 1 + result += strArraysAreEqual(Object.keys(bt), emptyArr) ? 0 : 1 + result += strArraysAreEqual(Object.keys(sh), emptyArr) ? 0 : 1 + result += strArraysAreEqual(Object.keys(i), emptyArr) ? 0 : 1 + result += strArraysAreEqual(Object.keys(lo), emptyArr) ? 0 : 1 + result += strArraysAreEqual(Object.keys(fl), emptyArr) ? 0 : 1 + result += strArraysAreEqual(Object.keys(dou), emptyArr) ? 0 : 1 + + result += strArraysAreEqual(Object.keys(arr), ["0", "1", "2"]) ? 0 : 1 + result += strArraysAreEqual(Object.keys(str), ["0", "1", "2"]) ? 0 : 1 + + result += strArraysAreEqual(Object.keys(cl), ["x", "y", "z"]) ? 0 : 1 + + result += strArraysAreEqual(Object.keys(lambda), emptyArr) ? 0 : 1 + + return result +} + +function objectValues(): int { + let result: int = 0 + + let c: char = c'c' + let bo: boolean = true + let bt: byte = 10 + let sh: short = 20 + let i: int = 30 + let lo: long = 40 + let fl: float = 50.0 + let dou: double = 60.0 + + let arr: number[] = [10, 20, 30] + let str: string = "abc" + + let cl: Point2D = new Point3D(10, 20, 30) + + let lambda: (a: number) => number = (a: number): number => { + return a + 1 + } + + let emptyArr: Object[] = [] + + result += objArraysAreEqual(Object.values__(c), emptyArr) ? 0 : 1 + result += objArraysAreEqual(Object.values__(bo), emptyArr) ? 0 : 1 + result += objArraysAreEqual(Object.values__(bt), emptyArr) ? 0 : 1 + result += objArraysAreEqual(Object.values__(sh), emptyArr) ? 0 : 1 + result += objArraysAreEqual(Object.values__(i), emptyArr) ? 0 : 1 + result += objArraysAreEqual(Object.values__(lo), emptyArr) ? 0 : 1 + result += objArraysAreEqual(Object.values__(fl), emptyArr) ? 0 : 1 + result += objArraysAreEqual(Object.values__(dou), emptyArr) ? 0 : 1 + + let arrVal: Object[] = [new Number(10), new Number(20), new Number(30)] + result += objArraysAreEqual(Object.values__(arr), arrVal) ? 0 : 1 + + let strVal: Object[] = ["a" as string, "b" as string, "c" as string] + result += objArraysAreEqual(Object.values__(str), strVal) ? 0 : 1 + + let clVal: Object[] = [new Number(10), new Number(20), new Number(30)] + result += objArraysAreEqual(Object.values__(cl), clVal) ? 0 : 1 + + result += objArraysAreEqual(Object.values__(lambda), emptyArr) ? 0 : 1 + + return result +} + +function objectEntries(): int { + let result: int = 0 + + let c: char = c'c' + let bo: boolean = true + let bt: byte = 10 + let sh: short = 20 + let i: int = 30 + let lo: long = 40 + let fl: float = 50.0 + let dou: double = 60.0 + + let arr: number[] = [10, 20, 30] + let str: string = "abc" + + let cl: Point2D = new Point3D(10, 20, 30) + + let lambda: (a: number) => number = (a: number): number => { + return a + 1 + } + + let emptyTuple: Tuple[] = new Tuple[0] + + result += tupleArraysAreEqual(Object.entries(c), emptyTuple) ? 0 : 1 + result += tupleArraysAreEqual(Object.entries(bo), emptyTuple) ? 0 : 1 + result += tupleArraysAreEqual(Object.entries(bt), emptyTuple) ? 0 : 1 + result += tupleArraysAreEqual(Object.entries(sh), emptyTuple) ? 0 : 1 + result += tupleArraysAreEqual(Object.entries(i), emptyTuple) ? 0 : 1 + result += tupleArraysAreEqual(Object.entries(lo), emptyTuple) ? 0 : 1 + result += tupleArraysAreEqual(Object.entries(fl), emptyTuple) ? 0 : 1 + result += tupleArraysAreEqual(Object.entries(dou), emptyTuple) ? 0 : 1 + + let arrVal: Tuple[] = new Tuple[3] + arrVal[0] = new Object[2] + arrVal[0] = ["0", new Number(10)] + arrVal[1] = new Object[2] + arrVal[1] = ["1", new Number(20)] + arrVal[2] = new Object[2] + arrVal[2] = ["2", new Number(30)] + result += tupleArraysAreEqual(Object.entries(arr), arrVal) ? 0 : 1 + + let strVal: Tuple[] = new Tuple[3] + strVal[0] = new Object[2] + strVal[0] = ["0", "a"] + strVal[1] = new Object[2] + strVal[1] = ["1", "b"] + strVal[2] = new Object[2] + strVal[2] = ["2", "c"] + result += tupleArraysAreEqual(Object.entries(str), strVal) ? 0 : 1 + + let clVal: Tuple[] = new Tuple[3] + clVal[0] = new Object[2] + clVal[0] = ["x", new Number(10)] + clVal[1] = new Object[2] + clVal[1] = ["y", new Number(20)] + clVal[2] = new Object[2] + clVal[2] = ["z", new Number(30)] + result += tupleArraysAreEqual(Object.entries(cl), clVal) ? 0 : 1 + + result += tupleArraysAreEqual(Object.entries(lambda), emptyTuple) ? 0 : 1 + + return result +} + +function objectGetOwnPropertyNames(): int { + let result: int = 0 + + let c: char = c'c' + let bo: boolean = true + let bt: byte = 10 + let sh: short = 20 + let i: int = 30 + let lo: long = 40 + let fl: float = 50.0 + let dou: double = 60.0 + + let arr: number[] = [10, 20, 30] + let str: string = "abc" + + let cl: Point2D = new Point3D(10, 20, 30) + + let lambda: (a: number) => number = (a: number): number => { + return a + 1 + } + + let emptyArr: string[] = [] + + result += strArraysAreEqual(Reflect.ownKeys(c), emptyArr) ? 0 : 1 + result += strArraysAreEqual(Reflect.ownKeys(bo), emptyArr) ? 0 : 1 + result += strArraysAreEqual(Reflect.ownKeys(bt), emptyArr) ? 0 : 1 + result += strArraysAreEqual(Reflect.ownKeys(sh), emptyArr) ? 0 : 1 + result += strArraysAreEqual(Reflect.ownKeys(i), emptyArr) ? 0 : 1 + result += strArraysAreEqual(Reflect.ownKeys(lo), emptyArr) ? 0 : 1 + result += strArraysAreEqual(Reflect.ownKeys(fl), emptyArr) ? 0 : 1 + result += strArraysAreEqual(Reflect.ownKeys(dou), emptyArr) ? 0 : 1 + + result += strArraysAreEqual(Reflect.ownKeys(arr), ["0", "1", "2", "length"]) ? 0 : 1 + result += strArraysAreEqual(Reflect.ownKeys(str), ["0", "1", "2", "length"]) ? 0 : 1 + + result += strArraysAreEqual(Reflect.ownKeys(cl), ["x", "y", "z"]) ? 0 : 1 + + result += strArraysAreEqual(Reflect.ownKeys(lambda), ["length", "name"]) ? 0 : 1 + + return result +} + +function objectHasOwnProperty(): int { + let result: int = 0 + + let arr: number[] = [10, 20, 30] + let str: string = "abc" + + let cl: Point2D = new Point3D(10, 20, 30) + + let lambda: (a: number) => number = (a: number): number => { + return a + 1 + } + + result += ((arr as Object).hasOwnProperty(0) == true) ? 0 : 1 + result += ((arr as Object).hasOwnProperty(3) == false) ? 0 : 1 + result += ((arr as Object).hasOwnProperty("length") == true) ? 0 : 1 + result += ((arr as Object).hasOwnProperty("qwerty") == false) ? 0 : 1 + + result += (str.hasOwnProperty(0) == true) ? 0 : 1 + result += (str.hasOwnProperty(3) == false) ? 0 : 1 + result += (str.hasOwnProperty("length") == true) ? 0 : 1 + result += (str.hasOwnProperty("qwerty") == false) ? 0 : 1 + + result += (cl.hasOwnProperty("x") == true) ? 0 : 1 + result += (cl.hasOwnProperty("y") == true) ? 0 : 1 + result += (cl.hasOwnProperty("z") == true) ? 0 : 1 + result += (cl.hasOwnProperty("axisnum") == false) ? 0 : 1 + result += (cl.hasOwnProperty("asdasd") == false) ? 0 : 1 + result += (cl.hasOwnProperty(0) == false) ? 0 : 1 + + result += ((lambda as Object).hasOwnProperty("length") == true) ? 0 : 1 + result += ((lambda as Object).hasOwnProperty("name") == true) ? 0 : 1 + result += ((lambda as Object).hasOwnProperty(0) == false) ? 0 : 1 + + return result +} + +function test(result: int, message: String ): int { + if (result == 0) { + console.log('PASSED: ' + message); + return 0; + } + console.log('FAILED: ' + message); + return 1; +}