• 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 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