• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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