1/* 2 * Copyright (c) 2021-2024 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16package std.core; 17 18export type NullishType = Object | null | undefined 19export type NullableType = Object | null 20export type Nullish<T> = T | null | undefined 21 22export type PropertyKey = string 23 24export const ARRAY_LENGTH_MEMBER_NAME = "length" 25export const FUNCTION_LENGTH_MEMBER_NAME = "length" 26export const FUNCTION_NAME_MEMBER_NAME = "name" 27 28export const OBJECT_TO_STRING_MEMBER_NAME: string = "toString" 29export const OBJECT_TO_LOCALE_STRING_MEMBER_NAME: string = "toLocaleString" 30export const OBJECT_HAS_OWN_PROPERTY_MEMBER_NAME: string = "hasOwnProperty" 31 32type EntryType = [PropertyKey, NullishType] 33 34/** 35 * `object` is an alias for type `Object` 36 */ 37export type object = Object; 38 39/** 40 * Common ancestor amongst all other classes 41 */ 42export class Object { 43 /** 44 * Constructs a new blank Object 45 */ 46 constructor () {}; 47 48 /** 49 * Converts this object to a string 50 * 51 * @returns result of the conversion 52 */ 53 public toString(): String { 54 return Value.of(this).toString() 55 } 56 57 /** 58 * Converts this object to locale-specific string representation 59 * 60 * @returns result of the conversion 61 */ 62 public toLocaleString(): String { 63 return Value.of(this).toLocaleString() 64 } 65 66 /** 67 * Returns a hash code (integer representation) for this instance 68 * 69 * @returns representation of this instance 70 */ 71 public $_hashCode(): int { 72 return runtime.getHashCode(this); 73 } 74 75 /** 76 * Returns the names of the fields of an object 77 * 78 * @param o an object 79 * 80 * @returns an array of strings representing the given object's own string-keyed field keys. 81 */ 82 public static keys(o: Object): string[] { 83 // Char, Boolean and Numeric types doesn't have keys 84 if (o instanceof Char || 85 o instanceof Boolean || 86 o instanceof Byte || 87 o instanceof Short || 88 o instanceof Int || 89 o instanceof Long || 90 o instanceof Float || 91 o instanceof Double) { 92 return new string[0] 93 } 94 // "Keys" for the string type is enumeration from 0 to str.length - 1 95 if (o instanceof String) { 96 const sv = o as string 97 const len = sv.getLength() 98 if (len == 0) { 99 return new string[0] 100 } 101 let res = new string[len] 102 for (let i = 0; i < len; i++) { 103 // NOTE(shumilov-petr): need to apply more effective way for int to String conversion 104 res[i] = new Int(i).toString() 105 } 106 return res 107 } 108 const t = Type.of(o) 109 if (t instanceof ClassType) { 110 const ct = t as ClassType 111 const fnum = ct.getFieldsNum() 112 if (fnum == 0) { 113 return new string[0] 114 } 115 let n: int = 0 116 for (let i = 0; i < fnum; i++) { 117 if (!ct.getField(i).isStatic()) { 118 n++ 119 } 120 } 121 let res = new string[n] 122 let j: int = 0 123 for (let i = 0; i < fnum; i++) { 124 let f = ct.getField(i) 125 if (!f.isStatic()) { 126 res[j] = f.getName() 127 j++ 128 } 129 } 130 return res 131 } else if (t instanceof ArrayType) { 132 const av = Value.of(o) as ArrayValue 133 const len = av.getLength() 134 if (len == 0) { 135 return new string[0] 136 } 137 let res = new string[len as int] 138 for (let i = 0; i < len; i++) { 139 // NOTE(shumilov-petr): need to apply more effective way for int to String conversion 140 res[i] = new Int(i).toString() 141 } 142 return res 143 } else if (t instanceof LambdaType) { 144 return new string[0] 145 } else if (t instanceof EnumType) { 146 // NOTE(shumilov-petr): Not implemented 147 throw new Error("Not implemented") 148 } else if (t instanceof UnionType) { 149 // NOTE(shumilov-petr): Not implemented 150 throw new Error("Not implemented") 151 } else if (t instanceof TupleType) { 152 // NOTE(shumilov-petr): Not implemented 153 throw new Error("Not implemented") 154 } 155 assert(false) 156 157 throw new Error("Invalid object"); 158 } 159 160 /** 161 * Returns the values of the fields of an object 162 * 163 * @param o an object 164 * 165 * @returns an array containing the given object's own string-keyed field values 166 */ 167 public static values(o: Object): NullishType[] { 168 if (o instanceof Char || 169 o instanceof Boolean || 170 o instanceof Byte || 171 o instanceof Short || 172 o instanceof Int || 173 o instanceof Long || 174 o instanceof Float || 175 o instanceof Double) { 176 return new NullishType[0] 177 } 178 if (o instanceof String) { 179 const sv = o as string 180 const len = sv.getLength() 181 if (len == 0) { 182 return new NullishType[0] 183 } 184 let res = new NullishType[len] 185 for (let i = 0; i < len; i++) { 186 // NOTE(shumilov-petr): must be replaced by `sv.charAt(i) as string` when #15731 will be fixed 187 res[i] = new Char(sv.charAt(i)).toString() 188 } 189 return res 190 } 191 const t = Type.of(o) 192 if (t instanceof ClassType) { 193 const cv = Value.of(o) as ClassValue 194 if (cv.getFieldsNum() == 0) { 195 return new NullishType[0] 196 } 197 const keys = Object.keys(o) 198 const len = keys.length 199 let res = new NullishType[len] 200 for (let i = 0; i < len; i++) { 201 res[i] = cv.getFieldByName(keys[i]).getData() 202 } 203 return res 204 } else if (t instanceof ArrayType) { 205 const av = Value.of(o) as ArrayValue 206 const len = av.getLength() 207 if (len == 0) { 208 return new NullishType[0] 209 } 210 let res = new NullishType[len as int] 211 for (let i = 0; i < len; i++) { 212 res[i] = av.getElement(i).getData() 213 } 214 return res 215 } else if (t instanceof LambdaType) { 216 return new NullishType[0] 217 } else if (t instanceof EnumType) { 218 // NOTE(shumilov-petr): Not implemented 219 throw new Error("Not implemented") 220 } else if (t instanceof UnionType) { 221 // NOTE(shumilov-petr): Not implemented 222 throw new Error("Not implemented") 223 } else if (t instanceof TupleType) { 224 // NOTE(shumilov-petr): Not implemented 225 throw new Error("Not implemented") 226 } 227 assert(false) 228 229 throw new Error("Invalid object") 230 } 231 232 /** 233 * Returns an array of key/values of properties of an object 234 * 235 * @param o object that contains the fields 236 * 237 * @returns array representation of key/value 238 */ 239 public static entries(o: Object): EntryType[] { 240 if (o instanceof Char || 241 o instanceof Boolean || 242 o instanceof Byte || 243 o instanceof Short || 244 o instanceof Int || 245 o instanceof Long || 246 o instanceof Float || 247 o instanceof Double) { 248 return new EntryType[0] 249 } 250 if (o instanceof String) { 251 const sv = o as string 252 const len = sv.getLength() 253 if (len == 0) { 254 return new EntryType[0] 255 } 256 let res = new EntryType[len] 257 for (let i = 0; i < len; i++) { 258 // NOTE(shumilov-petr): must be replaced by `sv.charAt(i) as string` when #15731 will be fixed 259 res[i] = [new Int(i).toString(), new Char(sv.charAt(i)).toString()] 260 } 261 return res 262 } 263 const t = Type.of(o) 264 if (t instanceof ClassType) { 265 const cv = Value.of(o) as ClassValue 266 if (cv.getFieldsNum() == 0) { 267 return new EntryType[0] 268 } 269 const keys = Object.keys(o) 270 const len = keys.length 271 let res = new EntryType[len] 272 for (let i = 0; i < len; i++) { 273 res[i] = [keys[i], cv.getFieldByName(keys[i]).getData()] 274 } 275 return res 276 } else if (t instanceof ArrayType) { 277 const av = Value.of(o) as ArrayValue 278 const len = av.getLength() 279 if (len == 0) { 280 return new EntryType[0] 281 } 282 let res = new EntryType[len as int] 283 for (let i = 0; i < len; i++) { 284 res[i] = [new Int(i).toString(), av.getElement(i).getData()] 285 } 286 return res 287 } else if (t instanceof LambdaType) { 288 return new EntryType[0] 289 } else if (t instanceof EnumType) { 290 // NOTE(shumilov-petr): Not implemented 291 throw new Error("Not implemented") 292 } else if (t instanceof UnionType) { 293 // NOTE(shumilov-petr): Not implemented 294 throw new Error("Not implemented") 295 } else if (t instanceof TupleType) { 296 // NOTE(shumilov-petr): Not implemented 297 throw new Error("Not implemented") 298 } 299 assert(false) 300 301 throw new Error("Invalid object") 302 } 303 304 /** 305 * Returns the names of the own fields of an object 306 * 307 * @param o object that contains the own fields 308 * 309 * @returns array representation of names 310 */ 311 public static getOwnPropertyNames(o: Object): string[] { 312 if (o instanceof Char || 313 o instanceof Boolean || 314 o instanceof Byte || 315 o instanceof Short || 316 o instanceof Int || 317 o instanceof Long || 318 o instanceof Float || 319 o instanceof Double) { 320 return new string[0] 321 } 322 const t = Type.of(o) 323 if (t instanceof StringType || t instanceof ArrayType) { 324 const keys = Object.keys(o) 325 const len = keys.length 326 if (len == 0) { 327 return new string[0] 328 } 329 let res = new string[len + 1] 330 for (let i = 0; i < len; i++) { 331 res[i] = keys[i] 332 } 333 res[len] = ARRAY_LENGTH_MEMBER_NAME 334 return res 335 } 336 if (t instanceof ClassType) { 337 return Object.keys(o) 338 } else if (t instanceof LambdaType) { 339 return [FUNCTION_LENGTH_MEMBER_NAME, FUNCTION_NAME_MEMBER_NAME] 340 } else if (t instanceof EnumType) { 341 // NOTE(shumilov-petr): Not implemented 342 throw new Error("Not implemented") 343 } else if (t instanceof UnionType) { 344 // NOTE(shumilov-petr): Not implemented 345 throw new Error("Not implemented") 346 } else if (t instanceof TupleType) { 347 // NOTE(shumilov-petr): Not implemented 348 throw new Error("Not implemented") 349 } 350 assert(false) 351 352 throw new Error("Invalid object") 353 } 354 355 /** 356 * Determines whether an object has a field with the specified name 357 * 358 * @param key the string name of the field to test 359 * 360 * @returns true if the object has the specified field; false otherwise 361 */ 362 public hasOwnProperty(key: string): boolean { 363 const keys = Object.getOwnPropertyNames(this) 364 const len = keys.length 365 for(let i = 0; i < len; i++) { 366 if (keys[i] == key) { 367 return true 368 } 369 } 370 return false 371 } 372 373 /** 374 * Determines whether an object has a element with the specified index 375 * 376 * @param index the number index of the element to test 377 * 378 * @returns true if the object has the specified element; false otherwise 379 */ 380 public hasOwnProperty(index: number): boolean { 381 if ((this) instanceof String) { 382 const sv = this as String 383 const len = sv.getLength() 384 const idx = index as long 385 return (0 <= idx && idx < len) 386 } 387 const t = Type.of(this) 388 if (t instanceof ArrayType) { 389 const av = Value.of(this) as ArrayValue 390 const len = av.getLength() 391 const idx = index as long 392 return (0 <= idx && idx < len) 393 } else if (t instanceof EnumType) { 394 // NOTE(shumilov-petr): Not implemented 395 throw new Error("Not implemented") 396 } else if (t instanceof UnionType) { 397 // NOTE(shumilov-petr): Not implemented 398 throw new Error("Not implemented") 399 } else if (t instanceof TupleType) { 400 // NOTE(shumilov-petr): Not implemented 401 throw new Error("Not implemented") 402 } 403 return false 404 } 405 406 /** 407 * Determines whether an object has a field with the specified name 408 * 409 * @param target an object 410 * 411 * @param key the string name of the field to test 412 * 413 * @returns true if the object has the specified field; false otherwise 414 */ 415 public static hasOwn(target: Object, key: PropertyKey): boolean { 416 return target.hasOwnProperty(key) 417 } 418 419 /** 420 * Determines whether an object has a element with the specified index 421 * 422 * @param target an object 423 * 424 * @param index the number index of the element to test 425 * 426 * @returns true if the object has the specified element; false otherwise 427 */ 428 public static hasOwn(target: Object, index: number): boolean { 429 return target.hasOwnProperty(index) 430 } 431 432 /** 433 * Transforms key-value pairs into a Record. 434 * 435 * @param entries an Iterable object that contains key-value pairs 436 * 437 * @returns new Record whose properties are given by the elements of the Iterable 438 */ 439 public static fromEntries<T = NullishType>(entries: Iterable<[PropertyKey, T]>): Record<PropertyKey, T> { 440 const result = new Record<PropertyKey, T>() 441 442 const entriesIter = entries.$_iterator() 443 444 let entriesIterResult = entriesIter.next() 445 while (!entriesIterResult.done) { 446 const entry: [PropertyKey, T] | undefined = entriesIterResult.value 447 if (entry != undefined) { 448 result[entry[0]] = entry[1] 449 } 450 451 entriesIterResult = entriesIter.next() 452 } 453 454 return result 455 } 456 457 /** 458 * Copies all own properties from one or more source objects to a target Record. 459 * 460 * @param target target Record -- what to apply the sources' properties to 461 * 462 * @param source objects containing the properties you want to apply 463 * 464 * @returns the modified target Record 465 */ 466 public static assign(target: Record<PropertyKey, NullishType>, ...source: Object[]): Record<PropertyKey, NullishType> { 467 for (const s of source) { 468 const entries = Object.entries(s) 469 for (const e of entries) { 470 target.set(e[0], e[1]) 471 } 472 } 473 return target 474 } 475} 476