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 an array of a given record property names 77 * 78 * @param rec a record 79 * 80 * @returns an array of strings representing the given record's property names 81 */ 82 public static keys(rec: Record<PropertyKey, NullishType>): string[] { 83 const keys = new string[rec.size as int] 84 85 let i = 0 86 for (const key of rec.keys()) { 87 keys[i] = key 88 i++ 89 } 90 91 return keys 92 } 93 94 /** 95 * Returns the names of the fields of an object 96 * 97 * @param o an object 98 * 99 * @returns an array of strings representing the given object's own string-keyed field keys. 100 */ 101 public static keys(o: Object): string[] { 102 // Char, Boolean and Numeric types doesn't have keys 103 if (o instanceof Char || 104 o instanceof Boolean || 105 o instanceof Byte || 106 o instanceof Short || 107 o instanceof Int || 108 o instanceof Long || 109 o instanceof Float || 110 o instanceof Double) { 111 return new string[0] 112 } 113 // "Keys" for the string type is enumeration from 0 to str.length - 1 114 if (o instanceof String) { 115 const sv = o as string 116 const len = sv.getLength() 117 if (len == 0) { 118 return new string[0] 119 } 120 let res = new string[len] 121 for (let i = 0; i < len; i++) { 122 // NOTE(shumilov-petr): need to apply more effective way for int to String conversion 123 res[i] = new Int(i).toString() 124 } 125 return res 126 } 127 const t = Type.of(o) 128 if (t instanceof ClassType) { 129 const ct = t as ClassType 130 const fnum = ct.getFieldsNum() 131 if (fnum == 0) { 132 return new string[0] 133 } 134 let n: int = 0 135 for (let i = 0; i < fnum; i++) { 136 if (!ct.getField(i).isStatic()) { 137 n++ 138 } 139 } 140 let res = new string[n] 141 let j: int = 0 142 for (let i = 0; i < fnum; i++) { 143 let f = ct.getField(i) 144 if (!f.isStatic()) { 145 res[j] = f.getName() 146 j++ 147 } 148 } 149 return res 150 } else if (t instanceof ArrayType) { 151 const av = Value.of(o) as ArrayValue 152 const len = av.getLength() 153 if (len == 0) { 154 return new string[0] 155 } 156 let res = new string[len as int] 157 for (let i = 0; i < len; i++) { 158 // NOTE(shumilov-petr): need to apply more effective way for int to String conversion 159 res[i] = new Int(i).toString() 160 } 161 return res 162 } else if (t instanceof LambdaType) { 163 return new string[0] 164 } else if (t instanceof EnumType) { 165 // NOTE(shumilov-petr): Not implemented 166 throw new Error("Not implemented") 167 } else if (t instanceof UnionType) { 168 // NOTE(shumilov-petr): Not implemented 169 throw new Error("Not implemented") 170 } else if (t instanceof TupleType) { 171 // NOTE(shumilov-petr): Not implemented 172 throw new Error("Not implemented") 173 } 174 assert(false) 175 176 throw new Error("Invalid object"); 177 } 178 179 public static values(rec: Record<PropertyKey, NullishType>): NullishType[] { 180 const vals = new NullishType[rec.size as int] 181 182 let i = 0 183 for (let val of rec.values()) { 184 vals[i] = val 185 i++ 186 } 187 188 return vals 189 } 190 191 /** 192 * Returns the values of the fields of an object 193 * 194 * @param o an object 195 * 196 * @returns an array containing the given object's own string-keyed field values 197 */ 198 public static values(o: Object): NullishType[] { 199 if (o instanceof Char || 200 o instanceof Boolean || 201 o instanceof Byte || 202 o instanceof Short || 203 o instanceof Int || 204 o instanceof Long || 205 o instanceof Float || 206 o instanceof Double) { 207 return new NullishType[0] 208 } 209 if (o instanceof String) { 210 const sv = o as string 211 const len = sv.getLength() 212 if (len == 0) { 213 return new NullishType[0] 214 } 215 let res = new NullishType[len] 216 for (let i = 0; i < len; i++) { 217 // NOTE(shumilov-petr): must be replaced by `sv.charAt(i) as string` when #15731 will be fixed 218 res[i] = new Char(sv.charAt(i)).toString() 219 } 220 return res 221 } 222 const t = Type.of(o) 223 if (t instanceof ClassType) { 224 const cv = Value.of(o) as ClassValue 225 if (cv.getFieldsNum() == 0) { 226 return new NullishType[0] 227 } 228 const keys = Object.keys(o) 229 const len = keys.length 230 let res = new NullishType[len] 231 for (let i = 0; i < len; i++) { 232 res[i] = cv.getFieldByName(keys[i]).getData() 233 } 234 return res 235 } else if (t instanceof ArrayType) { 236 const av = Value.of(o) as ArrayValue 237 const len = av.getLength() 238 if (len == 0) { 239 return new NullishType[0] 240 } 241 let res = new NullishType[len as int] 242 for (let i = 0; i < len; i++) { 243 res[i] = av.getElement(i).getData() 244 } 245 return res 246 } else if (t instanceof LambdaType) { 247 return new NullishType[0] 248 } else if (t instanceof EnumType) { 249 // NOTE(shumilov-petr): Not implemented 250 throw new Error("Not implemented") 251 } else if (t instanceof UnionType) { 252 // NOTE(shumilov-petr): Not implemented 253 throw new Error("Not implemented") 254 } else if (t instanceof TupleType) { 255 // NOTE(shumilov-petr): Not implemented 256 throw new Error("Not implemented") 257 } 258 assert(false) 259 260 throw new Error("Invalid object") 261 } 262 263 /** 264 * Returns an array of key/values of properties of a record 265 * 266 * @param rec record that contains the fields 267 * 268 * @returns array representation of key/value 269 */ 270 public static entries(rec: Record<PropertyKey, NullishType>): EntryType[] { 271 const entries = new EntryType[rec.size as int] 272 273 let i = 0 274 for (const entry of rec.entries()) { 275 entries[i] = entry 276 i++ 277 } 278 279 return entries 280 } 281 282 /** 283 * Returns an array of key/values of properties of an object 284 * 285 * @param o object that contains the fields 286 * 287 * @returns array representation of key/value 288 */ 289 public static entries(o: Object): EntryType[] { 290 if (o instanceof Char || 291 o instanceof Boolean || 292 o instanceof Byte || 293 o instanceof Short || 294 o instanceof Int || 295 o instanceof Long || 296 o instanceof Float || 297 o instanceof Double) { 298 return new EntryType[0] 299 } 300 if (o instanceof String) { 301 const sv = o as string 302 const len = sv.getLength() 303 if (len == 0) { 304 return new EntryType[0] 305 } 306 let res = new EntryType[len] 307 for (let i = 0; i < len; i++) { 308 // NOTE(shumilov-petr): must be replaced by `sv.charAt(i) as string` when #15731 will be fixed 309 res[i] = [new Int(i).toString(), new Char(sv.charAt(i)).toString()] 310 } 311 return res 312 } 313 const t = Type.of(o) 314 if (t instanceof ClassType) { 315 const cv = Value.of(o) as ClassValue 316 if (cv.getFieldsNum() == 0) { 317 return new EntryType[0] 318 } 319 const keys = Object.keys(o) 320 const len = keys.length 321 let res = new EntryType[len] 322 for (let i = 0; i < len; i++) { 323 res[i] = [keys[i], cv.getFieldByName(keys[i]).getData()] 324 } 325 return res 326 } else if (t instanceof ArrayType) { 327 const av = Value.of(o) as ArrayValue 328 const len = av.getLength() 329 if (len == 0) { 330 return new EntryType[0] 331 } 332 let res = new EntryType[len as int] 333 for (let i = 0; i < len; i++) { 334 res[i] = [new Int(i).toString(), av.getElement(i).getData()] 335 } 336 return res 337 } else if (t instanceof LambdaType) { 338 return new EntryType[0] 339 } else if (t instanceof EnumType) { 340 // NOTE(shumilov-petr): Not implemented 341 throw new Error("Not implemented") 342 } else if (t instanceof UnionType) { 343 // NOTE(shumilov-petr): Not implemented 344 throw new Error("Not implemented") 345 } else if (t instanceof TupleType) { 346 // NOTE(shumilov-petr): Not implemented 347 throw new Error("Not implemented") 348 } 349 assert(false) 350 351 throw new Error("Invalid object") 352 } 353 354 public static getOwnPropertyNames(rec: Record<PropertyKey, NullishType>): string[] { 355 return Object.keys(rec) 356 } 357 358 /** 359 * Returns the names of the own fields of an object 360 * 361 * @param o object that contains the own fields 362 * 363 * @returns array representation of names 364 */ 365 public static getOwnPropertyNames(o: Object): string[] { 366 if (o instanceof Char || 367 o instanceof Boolean || 368 o instanceof Byte || 369 o instanceof Short || 370 o instanceof Int || 371 o instanceof Long || 372 o instanceof Float || 373 o instanceof Double) { 374 return new string[0] 375 } 376 const t = Type.of(o) 377 if (t instanceof StringType || t instanceof ArrayType) { 378 const keys = Object.keys(o) 379 const len = keys.length 380 if (len == 0) { 381 return new string[0] 382 } 383 let res = new string[len + 1] 384 for (let i = 0; i < len; i++) { 385 res[i] = keys[i] 386 } 387 res[len] = ARRAY_LENGTH_MEMBER_NAME 388 return res 389 } 390 if (t instanceof ClassType) { 391 return Object.keys(o) 392 } else if (t instanceof LambdaType) { 393 return [FUNCTION_LENGTH_MEMBER_NAME, FUNCTION_NAME_MEMBER_NAME] 394 } else if (t instanceof EnumType) { 395 // NOTE(shumilov-petr): Not implemented 396 throw new Error("Not implemented") 397 } else if (t instanceof UnionType) { 398 // NOTE(shumilov-petr): Not implemented 399 throw new Error("Not implemented") 400 } else if (t instanceof TupleType) { 401 // NOTE(shumilov-petr): Not implemented 402 throw new Error("Not implemented") 403 } 404 assert(false) 405 406 throw new Error("Invalid object") 407 } 408 409 /** 410 * Determines whether an object has a field with the specified name 411 * 412 * @param key the string name of the field to test 413 * 414 * @returns true if the object has the specified field; false otherwise 415 */ 416 public hasOwnProperty(key: string): boolean { 417 const keys = Object.getOwnPropertyNames(this) 418 const len = keys.length 419 for(let i = 0; i < len; i++) { 420 if (keys[i] == key) { 421 return true 422 } 423 } 424 return false 425 } 426 427 /** 428 * Determines whether an object has a element with the specified index 429 * 430 * @param index the number index of the element to test 431 * 432 * @returns true if the object has the specified element; false otherwise 433 */ 434 public hasOwnProperty(index: number): boolean { 435 if ((this) instanceof String) { 436 const sv = this as String 437 const len = sv.getLength() 438 const idx = index as long 439 return (0 <= idx && idx < len) 440 } 441 const t = Type.of(this) 442 if (t instanceof ArrayType) { 443 const av = Value.of(this) as ArrayValue 444 const len = av.getLength() 445 const idx = index as long 446 return (0 <= idx && idx < len) 447 } else if (t instanceof EnumType) { 448 // NOTE(shumilov-petr): Not implemented 449 throw new Error("Not implemented") 450 } else if (t instanceof UnionType) { 451 // NOTE(shumilov-petr): Not implemented 452 throw new Error("Not implemented") 453 } else if (t instanceof TupleType) { 454 // NOTE(shumilov-petr): Not implemented 455 throw new Error("Not implemented") 456 } 457 return false 458 } 459 460 /** 461 * Determines whether an object has a field with the specified name 462 * 463 * @param target an object 464 * 465 * @param key the string name of the field to test 466 * 467 * @returns true if the object has the specified field; false otherwise 468 */ 469 public static hasOwn(target: Object, key: PropertyKey): boolean { 470 return target.hasOwnProperty(key) 471 } 472 473 /** 474 * Determines whether an object has a element with the specified index 475 * 476 * @param target an object 477 * 478 * @param index the number index of the element to test 479 * 480 * @returns true if the object has the specified element; false otherwise 481 */ 482 public static hasOwn(target: Object, index: number): boolean { 483 return target.hasOwnProperty(index) 484 } 485 486 /** 487 * Transforms key-value pairs into a Record. 488 * 489 * @param entries an Iterable object that contains key-value pairs 490 * 491 * @returns new Record whose properties are given by the elements of the Iterable 492 */ 493 public static fromEntries<T = NullishType>(entries: Iterable<[PropertyKey, T]>): Record<PropertyKey, T> { 494 const result = new Record<PropertyKey, T>() 495 496 const entriesIter = entries.$_iterator() 497 498 let entriesIterResult = entriesIter.next() 499 while (!entriesIterResult.done) { 500 const entry: [PropertyKey, T] | undefined = entriesIterResult.value 501 if (entry != undefined) { 502 result[entry[0]] = entry[1] 503 } 504 505 entriesIterResult = entriesIter.next() 506 } 507 508 return result 509 } 510 511 /** 512 * Copies all own properties from one or more source objects to a target Record. 513 * 514 * @param target target Record -- what to apply the sources' properties to 515 * 516 * @param source objects containing the properties you want to apply 517 * 518 * @returns the modified target Record 519 */ 520 public static assign(target: Record<PropertyKey, NullishType>, ...source: Object[]): Record<PropertyKey, NullishType> { 521 for (const s of source) { 522 const entries = Object.entries(s) 523 for (const e of entries) { 524 target.set(e[0], e[1]) 525 } 526 } 527 return target 528 } 529} 530