From a0749ada2fc5025170d95d1faf0c602629f0f004 Mon Sep 17 00:00:00 2001 From: Sergey Malenkov Date: Thu, 11 Sep 2025 11:43:31 +0300 Subject: [PATCH] Sync changes with RRI runtime Signed-off-by: Sergey Malenkov \ --- .../common/bridges/ohos/src/chai/index.ts | 213 ------- .../common/bridges/ohos/src/index.ts | 19 - .../common/bridges/ohos/src/mocha/index.ts | 56 -- .../bridges/ohos/src/mocha/types/index.d.ts | 37 -- .../common/bridges/ohos/tsconfig.json | 13 - .../incremental/common/src/Finalization.ts | 20 +- .../incremental/common/src/index.ts | 54 +- .../incremental/common/src/koalaKey.ts | 10 +- .../incremental/common/src/math.ts | 40 +- .../incremental/common/src/sha1.ts | 393 ++++++------ .../incremental/common/src/stringUtils.ts | 12 +- .../incremental/common/src/uniqueId.ts | 38 +- .../incremental/compat/src/arkts/array.ts | 33 +- .../incremental/compat/src/arkts/atomic.ts | 10 +- .../compat/src/arkts/finalization.ts | 11 +- .../incremental/compat/src/arkts/index.ts | 24 +- .../compat/src/arkts/observable.ts | 332 ++++++---- .../compat/src/arkts/performance.ts | 4 +- .../incremental/compat/src/arkts/primitive.ts | 59 +- .../compat/src/arkts/prop-deep-copy.ts | 10 +- .../compat/src/arkts/reflection.ts | 5 +- .../incremental/compat/src/arkts/strings.ts | 214 ++++--- .../compat/src/arkts/ts-reflection.ts | 12 +- .../incremental/compat/src/arkts/types.ts | 20 +- .../incremental/compat/src/arkts/utils.ts | 35 +- .../incremental/compat/src/index.ts | 30 +- .../incremental/compat/src/ohos/index.ts | 35 +- .../compat/src/ohos/performance.ts | 4 +- .../compat/src/typescript/Types.d.ts | 8 +- .../compat/src/typescript/array.ts | 12 +- .../compat/src/typescript/atomic.ts | 10 +- .../compat/src/typescript/finalization.ts | 14 +- .../compat/src/typescript/index.ts | 24 +- .../compat/src/typescript/observable.ts | 600 ++++++++++-------- .../compat/src/typescript/performance.ts | 4 +- .../compat/src/typescript/primitive.ts | 57 +- .../compat/src/typescript/prop-deep-copy.ts | 79 ++- .../compat/src/typescript/reflection.ts | 5 +- .../compat/src/typescript/strings.ts | 201 +++--- .../compat/src/typescript/ts-reflection.ts | 12 +- .../compat/src/typescript/types.ts | 53 +- .../compat/src/typescript/utils.ts | 73 ++- .../runtime/src/animation/AnimatedState.ts | 42 +- .../runtime/src/animation/Easing.ts | 4 +- .../runtime/src/animation/EasingSupport.ts | 12 +- .../runtime/src/animation/TimeAnimation.ts | 14 +- .../incremental/runtime/src/animation/memo.ts | 2 +- .../runtime/src/common/MarkableQueue.ts | 12 +- .../runtime/src/common/RuntimeProfiler.ts | 20 +- .../incremental/runtime/src/common/Unique.ts | 10 +- .../incremental/runtime/src/index.ts | 16 +- .../incremental/runtime/src/memo/bind.ts | 8 +- .../runtime/src/memo/changeListener.ts | 4 +- .../incremental/runtime/src/memo/entry.ts | 7 +- .../incremental/runtime/src/memo/node.ts | 9 +- .../incremental/runtime/src/memo/remember.ts | 3 +- .../incremental/runtime/src/memo/repeat.ts | 8 +- .../incremental/runtime/src/memo/testing.ts | 15 +- .../runtime/src/states/GlobalStateManager.ts | 34 +- .../incremental/runtime/src/states/State.ts | 341 +++++++--- .../runtime/src/tree/IncrementalNode.ts | 31 +- .../runtime/src/tree/ReadonlyTreeNode.ts | 53 +- .../incremental/runtime/src/tree/TreeNode.ts | 66 +- 63 files changed, 1920 insertions(+), 1686 deletions(-) delete mode 100644 frameworks/bridge/arkts_frontend/koala_projects/incremental/common/bridges/ohos/src/chai/index.ts delete mode 100644 frameworks/bridge/arkts_frontend/koala_projects/incremental/common/bridges/ohos/src/index.ts delete mode 100644 frameworks/bridge/arkts_frontend/koala_projects/incremental/common/bridges/ohos/src/mocha/index.ts delete mode 100644 frameworks/bridge/arkts_frontend/koala_projects/incremental/common/bridges/ohos/src/mocha/types/index.d.ts delete mode 100644 frameworks/bridge/arkts_frontend/koala_projects/incremental/common/bridges/ohos/tsconfig.json diff --git a/frameworks/bridge/arkts_frontend/koala_projects/incremental/common/bridges/ohos/src/chai/index.ts b/frameworks/bridge/arkts_frontend/koala_projects/incremental/common/bridges/ohos/src/chai/index.ts deleted file mode 100644 index 9f9075825e9..00000000000 --- a/frameworks/bridge/arkts_frontend/koala_projects/incremental/common/bridges/ohos/src/chai/index.ts +++ /dev/null @@ -1,213 +0,0 @@ -/* - * Copyright (c) 2022-2024 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. - */ - -import { expect } from "@ohos/hypium" - -export interface Assert { - (expression: any, message?: string): asserts expression - - /** - * Asserts non-strict equality (==) of actual and expected. - */ - equal(actual: T, expected: T, message?: string): void - - /** - * Asserts non-strict inequality (!=) of actual and expected. - */ - notEqual(actual: T, expected: T, message?: string): void - - /** - * Asserts strict equality (===) of actual and expected. - */ - strictEqual(actual: T, expected: T, message?: string): void - - /** - * Asserts strict inequality (!==) of actual and expected. - */ - notStrictEqual(actual: T, expected: T, message?: string): void - - deepEqual(actual: any, expected: any, message?: string): void - - notDeepEqual(actual: any, expected: any, message?: string): void - - isTrue(value: any, message?: string): void - - isFalse(value: any, message?: string): void - - closeTo(actual: number, expected: number, delta: number, message?: string): void - - fail(message?: string): void - - isNull(value: any, message?: string): void - - isNotNull(value: any, message?: string): void - - instanceOf(value: any, constructor: Function, message?: string): void - - isAtLeast(valueToCheck: number, valueToBeAtLeast: number, message?: string): void - - exists(value: any, message?: string): void - - throw(fn: () => void, message?: string): void - - throws(fn: () => void, message?: string): void - - isAbove(valueToCheck: number, valueToBeAbove: number, message?: string): void - - isBelow(valueToCheck: number, valueToBeBelow: number, message?: string): void - - match(value: string, regexp: RegExp, message?: string): void - - isDefined(value: any, message?: string): void - - isUndefined(value: any, message?: string): void - - isEmpty(object: any, message?: string): void - - isNotEmpty(object: any, message?: string): void -} - -// Improve: the 'message' arg is ignored - -export var assert: Assert = ((expression: any, message?: string): void => { - expect(Boolean(expression)).assertTrue() -}) as Assert - -assert.equal = (actual: T, expected: T, message?: string): void => { - expect(actual).assertEqual(expected) -} - -assert.notEqual = (actual: T, expected: T, message?: string): void => { - // Improve: not accurate impl, because compared values are not printed - expect(actual != expected).assertTrue() -} - -assert.strictEqual = (actual: T, expected: T, message?: string): void => { - // Improve: not accurate impl, because compared values are not printed - expect(actual === expected).assertTrue() -} - -assert.notStrictEqual = (actual: T, expected: T, message?: string): void => { - // Improve: not accurate impl, because compared values are not printed - expect(actual !== expected).assertTrue() -} - -assert.deepEqual = (actual: any, expected: any, message?: string): void => { - // Improve: implement - expect(actual).assertEqual(actual/*expected*/) -} - -assert.notDeepEqual = (actual: any, expected: any, message?: string): void => { - // Improve: implement - expect(actual).assertEqual(actual/*expected*/) -} - -assert.isTrue = (value: any, message?: string): void => { - expect(value).assertTrue() -} - -assert.isFalse = (value: any, message?: string): void => { - expect(value).assertFalse() -} - -assert.closeTo = (actual: number, expected: number, delta: number, message?: string): void => { - // implementation of 'assertClose' does not fit: - // expect(actual).assertClose(expected, delta) - - const diff = Math.abs(actual - expected) - if (diff == delta) - expect(diff).assertEqual(delta) - else - expect(diff).assertLess(delta) -} - -assert.fail = (message?: string): void => { - expect().assertFail() -} - -assert.isNull = (value: any, message?: string): void => { - expect(value).assertNull() -} - -assert.isNotNull = (value: any, message?: string): void => { - expect(value ? null : value).assertNull() -} - -assert.instanceOf = (value: any, constructor: Function, message?: string): void => { - // Improve: not accurate impl - // expect(value).assertInstanceOf(constructor.name) - expect(value instanceof constructor).assertTrue() -} - -assert.isAtLeast = (valueToCheck: number, valueToBeAtLeast: number, message?: string): void => { - if (valueToCheck == valueToBeAtLeast) - expect(valueToCheck).assertEqual(valueToBeAtLeast) - else - expect(valueToCheck).assertLarger(valueToBeAtLeast) -} - -assert.exists = (value: any, message?: string): void => { - // Improve: not accurate impl - expect(value == null).assertFalse() -} - -assert.throw = (fn: () => void, message?: string): void => { - let fnWrapper = () => { - try { - fn() - } catch (e) { - throw new Error("fn thrown exception") - } - } - expect(fnWrapper).assertThrowError("fn thrown exception") -} - -assert.throws = (fn: () => void, message?: string): void => { - assert.throw(fn, message) -} - -assert.isAbove = (valueToCheck: number, valueToBeAbove: number, message?: string): void => { - expect(valueToCheck).assertLarger(valueToBeAbove) -} - -assert.isBelow = (valueToCheck: number, valueToBeBelow: number, message?: string): void => { - expect(valueToCheck).assertLess(valueToBeBelow) -} - -assert.match = (value: string, regexp: RegExp, message?: string): void => { - // Improve: not accurate impl - expect(regexp.test(value)).assertTrue() -} - -assert.isDefined = (value: any, message?: string): void => { - // Improve: not accurate impl - expect(value === undefined).assertFalse() -} - -assert.isUndefined = (value: any, message?: string): void => { - expect(value).assertUndefined() -} - -assert.isEmpty = (object: any, message?: string): void => { - // Improve: implement - expect(object !== undefined).assertTrue() -} - -assert.isNotEmpty = (object: any, message?: string): void => { - // Improve: implement - expect(object !== undefined).assertTrue() -} - - diff --git a/frameworks/bridge/arkts_frontend/koala_projects/incremental/common/bridges/ohos/src/index.ts b/frameworks/bridge/arkts_frontend/koala_projects/incremental/common/bridges/ohos/src/index.ts deleted file mode 100644 index 17641d36d4c..00000000000 --- a/frameworks/bridge/arkts_frontend/koala_projects/incremental/common/bridges/ohos/src/index.ts +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (c) 2022-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. - */ - -export {startTests} from "./mocha" -export * from "./chai" - -import "./mocha" // globals diff --git a/frameworks/bridge/arkts_frontend/koala_projects/incremental/common/bridges/ohos/src/mocha/index.ts b/frameworks/bridge/arkts_frontend/koala_projects/incremental/common/bridges/ohos/src/mocha/index.ts deleted file mode 100644 index 2d2cf81c527..00000000000 --- a/frameworks/bridge/arkts_frontend/koala_projects/incremental/common/bridges/ohos/src/mocha/index.ts +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (c) 2022-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. - */ - -import {describe, beforeEach, it, Size} from "@ohos/hypium" - -declare namespace globalThis { - let __OpenHarmony: boolean - let __generateGolden: boolean -} - -globalThis.__OpenHarmony = true - -const suiteMap = new Map() - -suite = (title: string, fn: Fn): void => { - suiteMap.set(title, fn) -} - -suiteSetup = (title: string, fn: Fn): void => { - beforeEach(fn) -} - -test = ((title: string, fn?: Fn): void => { - it(fn ? title : `[SKIP] ${title}`, Size.MEDIUMTEST, fn ? fn : () => {}) -}) as TestFn - -test.skip = (title: string, fn?: Fn): void => { - it(`[SKIP] ${title}`, Size.MEDIUMTEST, () => {}) -} - -(performance as TimeFn) = { - now: (): number => { - return Date.now() - } -} - -export function startTests(generateGolden: boolean = false) { - globalThis.__generateGolden = generateGolden - suiteMap.forEach((fn: Fn, title: string) => { - describe(title, function () { - fn() - }) - }) -} diff --git a/frameworks/bridge/arkts_frontend/koala_projects/incremental/common/bridges/ohos/src/mocha/types/index.d.ts b/frameworks/bridge/arkts_frontend/koala_projects/incremental/common/bridges/ohos/src/mocha/types/index.d.ts deleted file mode 100644 index 61d2fc50093..00000000000 --- a/frameworks/bridge/arkts_frontend/koala_projects/incremental/common/bridges/ohos/src/mocha/types/index.d.ts +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2022-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. - */ - -declare global { - type Fn = () => void - - type SuiteFn = (title: string, fn: Fn) => void - - interface TestFn { - (title: string, fn?: Fn): void - skip(title: string, fn?: Fn): void - } - - let suite: SuiteFn - let suiteSetup: SuiteFn - let test: TestFn - - interface TimeFn { - now: () => number - } - - let performance: TimeFn -} - -export {} diff --git a/frameworks/bridge/arkts_frontend/koala_projects/incremental/common/bridges/ohos/tsconfig.json b/frameworks/bridge/arkts_frontend/koala_projects/incremental/common/bridges/ohos/tsconfig.json deleted file mode 100644 index 632f9f62e6c..00000000000 --- a/frameworks/bridge/arkts_frontend/koala_projects/incremental/common/bridges/ohos/tsconfig.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "extends": "@koalaui/build-common/tsconfig.json", - "compilerOptions": { - "outDir": "../../build/bridges/ohos", - "rootDir": "src", - "baseUrl": ".", - "module": "CommonJS", - "paths": { - "chai": ["./src/chai"] - }, - "typeRoots": ["./src/mocha/types"] - } -} diff --git a/frameworks/bridge/arkts_frontend/koala_projects/incremental/common/src/Finalization.ts b/frameworks/bridge/arkts_frontend/koala_projects/incremental/common/src/Finalization.ts index 2ba480ca2d5..c2056c2571f 100644 --- a/frameworks/bridge/arkts_frontend/koala_projects/incremental/common/src/Finalization.ts +++ b/frameworks/bridge/arkts_frontend/koala_projects/incremental/common/src/Finalization.ts @@ -13,28 +13,32 @@ * limitations under the License. */ -import { finalizerRegister as finalizerRegisterCompat, finalizerUnregister as finalizerUnregisterCompat, Thunk } from "@koalaui/compat" +import { + finalizerRegister as finalizerRegisterCompat, + finalizerUnregister as finalizerUnregisterCompat, + Thunk, +} from '@koalaui/compat'; -export { Thunk } from "@koalaui/compat" +export { Thunk } from '@koalaui/compat'; export function finalizerRegister(target: object, thunk: Thunk) { - finalizerRegisterCompat(target, thunk) + finalizerRegisterCompat(target, thunk); } export function finalizerRegisterWithCleaner(target: object, cleaner: () => void) { - finalizerRegisterCompat(target, new CleanerThunk(cleaner)) + finalizerRegisterCompat(target, new CleanerThunk(cleaner)); } export function finalizerUnregister(target: object) { - finalizerUnregisterCompat(target) + finalizerUnregisterCompat(target); } class CleanerThunk implements Thunk { - private cleaner: () => void + private cleaner: () => void; constructor(cleaner: () => void) { - this.cleaner = cleaner + this.cleaner = cleaner; } clean() { - this.cleaner() + this.cleaner(); } } diff --git a/frameworks/bridge/arkts_frontend/koala_projects/incremental/common/src/index.ts b/frameworks/bridge/arkts_frontend/koala_projects/incremental/common/src/index.ts index 7f9b0141ea3..8a5de09250f 100644 --- a/frameworks/bridge/arkts_frontend/koala_projects/incremental/common/src/index.ts +++ b/frameworks/bridge/arkts_frontend/koala_projects/incremental/common/src/index.ts @@ -14,42 +14,58 @@ */ export { - int8, uint8, - int32, uint32, - int64, uint64, - float32, float64, + int8, + uint8, + int32, + int32toFloat32, + int32toFloat64, + int32to64, + uint32, + int64, + int64toFloat32, + int64toFloat64, + int64to32, + uint64, + float32, + float32to64, + float32toInt32, + float32toInt64, + float64, + float64to32, + float64toInt32, + float64toInt64, asArray, asFloat64, - charToInt, - float32FromBits, - float64ToInt, - float64ToLong, - int32BitsFromFloat, Array_from_set, AtomicRef, CustomTextDecoder, CustomTextEncoder, - className, lcClassName, + className, + lcClassName, functionOverValue, Observed, Observable, + ObservableClass, + ClassMetadata, ObservableHandler, observableProxy, observableProxyArray, + TrackableProperties, + trackableProperties, isFunction, propDeepCopy, refEqual, int8Array, errorAsString, unsafeCast, - CoroutineLocalValue, + WorkerLocalValue, scheduleCoroutine, memoryStats, - launchJob -} from "@koalaui/compat" -export { clamp, lerp, modulo, parseNumber, isFiniteNumber, getDistancePx } from "./math" -export { hashCodeFromString } from "./stringUtils" -export * from "./Finalization" -export { SHA1Hash, createSha1 } from "./sha1" -export { UniqueId } from "./uniqueId" -export * from "./koalaKey" + launchJob, +} from '@koalaui/compat'; +export { clamp, lerp, modulo, parseNumber, isFiniteNumber, getDistancePx } from './math'; +export { hashCodeFromString } from './stringUtils'; +export * from './Finalization'; +export { SHA1Hash, createSha1 } from './sha1'; +export { UniqueId } from './uniqueId'; +export * from './koalaKey'; diff --git a/frameworks/bridge/arkts_frontend/koala_projects/incremental/common/src/koalaKey.ts b/frameworks/bridge/arkts_frontend/koala_projects/incremental/common/src/koalaKey.ts index cf79a2d19f8..a529f81bdbb 100644 --- a/frameworks/bridge/arkts_frontend/koala_projects/incremental/common/src/koalaKey.ts +++ b/frameworks/bridge/arkts_frontend/koala_projects/incremental/common/src/koalaKey.ts @@ -13,18 +13,18 @@ * limitations under the License. */ -import { int32 } from "@koalaui/compat" +import { int32 } from '@koalaui/compat'; -export type KoalaCallsiteKey = int32 +export type KoalaCallsiteKey = int32; export class KoalaCallsiteKeys { - static readonly empty: KoalaCallsiteKey = 0 + static readonly empty: KoalaCallsiteKey = 0; static combine(key1: KoalaCallsiteKey, key2: KoalaCallsiteKey): KoalaCallsiteKey { - return key1 + key2 + return key1 + key2; } static asString(key: KoalaCallsiteKey): string { - return new Number(key).toString(16) + return key.toString(16); } } diff --git a/frameworks/bridge/arkts_frontend/koala_projects/incremental/common/src/math.ts b/frameworks/bridge/arkts_frontend/koala_projects/incremental/common/src/math.ts index 8827d8d7b5e..52c39a60b09 100644 --- a/frameworks/bridge/arkts_frontend/koala_projects/incremental/common/src/math.ts +++ b/frameworks/bridge/arkts_frontend/koala_projects/incremental/common/src/math.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2024 Huawei Device Co., Ltd. + * Copyright (c) 2022-2025 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 @@ -13,7 +13,7 @@ * limitations under the License. */ -import { asFloat64, asString, float64 } from "@koalaui/compat" +import { asFloat64, float64 } from '@koalaui/compat'; /** * Computes the linear interpolation between `source` and `target` based on `weight`. @@ -24,7 +24,7 @@ import { asFloat64, asString, float64 } from "@koalaui/compat" * @returns interpolated value */ export function lerp(weight: float64, source: float64, target: float64): float64 { - return source * (1.0 - weight) + target * weight + return source * (1.0 - weight) + target * weight; } /** @@ -38,7 +38,7 @@ export function lerp(weight: float64, source: float64, target: float64): float64 * `value` otherwise */ export function clamp(value: float64, min: float64, max: float64): float64 { - return value <= min ? min : value >= max ? max : value + return value <= min ? min : value >= max ? max : value; } /** @@ -51,8 +51,8 @@ export function clamp(value: float64, min: float64, max: float64): float64 { */ export function modulo(value: float64): float64 { // The casts below are needed since floor returns double in ArkTS - const modulo: float64 = value - Math.floor(value) - return (modulo < 1.0) ? modulo : 0.0 + const modulo: float64 = value - Math.floor(value); + return modulo < 1.0 ? modulo : 0.0; } /** @@ -62,21 +62,21 @@ export function modulo(value: float64): float64 { * @returns a floating-point number * @throws Error if `str` cannot be parsed */ -export function parseNumber(str: string, name: string = "number", verify: boolean = false): float64 { - if (str != "") { // do not parse empty string to 0 +export function parseNumber(str: string, name: string = 'number', verify: boolean = false): float64 { + if (str != '') { + // do not parse empty string to 0 // ArkTS does not support NaN, isNaN, parseFloat - const value = asFloat64(str) + const value = asFloat64(str); if (verify) { - const reverseStr = asString(value) + const reverseStr = value.toString(); if (reverseStr !== undefined && reverseStr?.length == str.length && reverseStr == str) { - return value + return value; } - } - else { - return value + } else { + return value; } } - throw new Error(`cannot parse ${name}: "${str}"`) + throw new Error(`cannot parse ${name}: "${str}"`); } /** @@ -87,11 +87,11 @@ export function isFiniteNumber(number: float64): boolean { // isFiniteNumber(Number.NEGATIVE_INFINITY) == false // isFiniteNumber(Number.POSITIVE_INFINITY) == false // isFiniteNumber(NaN) == false - return number >= Number.MIN_SAFE_INTEGER && number <= Number.MAX_SAFE_INTEGER + return number >= Number.MIN_SAFE_INTEGER && number <= Number.MAX_SAFE_INTEGER; } export function getDistancePx(startX: float64, startY: float64, endX: float64, endY: float64): float64 { - const cathetA = Math.abs(endX - startX) - const cathetB = Math.abs(endY - startY) - return Math.sqrt(cathetA * cathetA + cathetB * cathetB) as float64 -} \ No newline at end of file + const cathetA = Math.abs(endX - startX); + const cathetB = Math.abs(endY - startY); + return Math.sqrt(cathetA * cathetA + cathetB * cathetB) as float64; +} diff --git a/frameworks/bridge/arkts_frontend/koala_projects/incremental/common/src/sha1.ts b/frameworks/bridge/arkts_frontend/koala_projects/incremental/common/src/sha1.ts index dceacb86dac..569d0831450 100644 --- a/frameworks/bridge/arkts_frontend/koala_projects/incremental/common/src/sha1.ts +++ b/frameworks/bridge/arkts_frontend/koala_projects/incremental/common/src/sha1.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2023 Huawei Device Co., Ltd. + * Copyright (c) 2022-2025 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 @@ -13,342 +13,337 @@ * limitations under the License. */ -import { CustomTextDecoder } from "@koalaui/compat" -import { int32 } from "@koalaui/compat" - -const K = [ - (0x5a827999 | 0) as int32, - (0x6ed9eba1 | 0) as int32, - (0x8f1bbcdc | 0) as int32, - (0xca62c1d6 | 0) as int32, -] - -const inputBytes = 64 -const inputWords = inputBytes / 4 -const highIndex = inputWords - 2 -const lowIndex = inputWords - 1 -const workWords = 80 -const allocBytes = 80 -const allocWords = allocBytes / 4 -const allocTotal = allocBytes * 100 +import { CustomTextDecoder, float64toInt32, int64to32 } from '@koalaui/compat'; +import { int32 } from '@koalaui/compat'; + +const K = [0x5a827999 | 0, 0x6ed9eba1 | 0, 0x8f1bbcdc | 0, 0xca62c1d6 | 0]; + +const inputBytes = 64; +const inputWords = inputBytes / 4; +const highIndex = inputWords - 2; +const lowIndex = inputWords - 1; +const workWords = 80; +const allocBytes = 80; +const allocWords = allocBytes / 4; +const allocTotal = allocBytes * 100; export function createSha1(): SHA1Hash { - return new SHA1Hash() + return new SHA1Hash(); } export class SHA1Hash { - private A = (0x67452301 | 0) as int32 - private B = (0xefcdab89 | 0) as int32 - private C = (0x98badcfe | 0) as int32 - private D = (0x10325476 | 0) as int32 - private E = (0xc3d2e1f0 | 0) as int32 - private readonly _byte: Uint8Array - private readonly _word: Int32Array - private _size = 0 - private _sp = 0 // surrogate pair + private A = 0x67452301 | 0; + private B = 0xefcdab89 | 0; + private C = 0x98badcfe | 0; + private D = 0x10325476 | 0; + private E = 0xc3d2e1f0 | 0; + private readonly _byte: Uint8Array; + private readonly _word: Int32Array; + private _size = 0; + private _sp = 0; // surrogate pair constructor() { if (!sharedBuffer || sharedOffset >= allocTotal) { - sharedBuffer = new ArrayBuffer(allocTotal) - sharedOffset = 0 + sharedBuffer = new ArrayBuffer(allocTotal); + sharedOffset = 0; } - this._byte = new Uint8Array(sharedBuffer, sharedOffset, allocBytes) - this._word = new Int32Array(sharedBuffer, sharedOffset, allocWords) - sharedOffset += allocBytes + this._byte = new Uint8Array(sharedBuffer, sharedOffset, allocBytes); + this._word = new Int32Array(sharedBuffer, sharedOffset, allocWords); + sharedOffset += allocBytes; } updateString(data: string, encoding?: string): SHA1Hash { - return this._utf8(data) + return this._utf8(data); } updateInt32(data: int32): SHA1Hash { - const buffer = new Int32Array(1) - buffer[0] = data - return this.update(buffer) + const buffer = new Int32Array(1); + buffer[0] = data; + return this.update(buffer); } update(data: Int32Array | Float32Array | Uint32Array | Uint8Array): SHA1Hash { if (data == null) { - throw new TypeError("SHA1Hash expected non-null data: ") + throw new TypeError('SHA1Hash expected non-null data: '); } - let byteOffset: int32 = 0 - let length: int32 = 0 - let buffer: ArrayBufferLike | undefined = undefined + let byteOffset: int32 = 0; + let length: int32 = 0; + let buffer: ArrayBufferLike | undefined = undefined; // Improve: an attempt to wrie this in a generic form causes // es2panda to segfault. - let BYTES_PER_ELEMENT = 4 + let BYTES_PER_ELEMENT = 4; if (data instanceof Int32Array) { - byteOffset = data.byteOffset as int32 - length = data.byteLength as int32 - buffer = data.buffer + byteOffset = float64toInt32(data.byteOffset); + length = float64toInt32(data.byteLength); + buffer = data.buffer; } else if (data instanceof Uint32Array) { - byteOffset = data.byteOffset as int32 - length = data.byteLength as int32 - buffer = data.buffer + byteOffset = float64toInt32(data.byteOffset); + length = float64toInt32(data.byteLength); + buffer = data.buffer; } else if (data instanceof Float32Array) { - byteOffset = data.byteOffset as int32 - length = data.byteLength as int32 - buffer = data.buffer + byteOffset = float64toInt32(data.byteOffset); + length = float64toInt32(data.byteLength); + buffer = data.buffer; } else if (data instanceof Uint8Array) { - byteOffset = data.byteOffset as int32 - length = data.byteLength as int32 - buffer = data.buffer - BYTES_PER_ELEMENT = 1 + byteOffset = float64toInt32(data.byteOffset); + length = float64toInt32(data.byteLength); + buffer = data.buffer; + BYTES_PER_ELEMENT = 1; } - let blocks: int32 = ((length / inputBytes) | 0) as int32 - let offset: int32 = 0 + let blocks: int32 = (length / inputBytes) | 0; + let offset: int32 = 0; // longer than 1 block - if ((blocks != 0) && !(byteOffset & 3) && !(this._size % inputBytes)) { - const block = new Int32Array(buffer!, byteOffset, blocks * inputWords) + if (blocks != 0 && !(byteOffset & 3) && !(this._size % inputBytes)) { + const block = new Int32Array(buffer!, byteOffset, blocks * inputWords); while (blocks--) { - this._int32(block, offset >> 2) - offset += inputBytes + this._int32(block, offset >> 2); + offset += inputBytes; } - this._size += offset + this._size += offset; } // data: TypedArray | DataView - if ((BYTES_PER_ELEMENT != 1) && buffer != undefined) { - const rest = new Uint8Array(buffer, byteOffset + offset, length - offset) - return this._uint8(rest) + if (BYTES_PER_ELEMENT != 1 && buffer != undefined) { + const rest = new Uint8Array(buffer, byteOffset + offset, length - offset); + return this._uint8(rest); } // no more bytes - if (offset == length) return this + if (offset == length) return this; - return this._uint8(new Uint8Array(buffer!), offset) + return this._uint8(new Uint8Array(buffer!), offset); } private _uint8(data: Uint8Array, offset?: int32): SHA1Hash { - const _byte = this._byte - const _word = this._word - const length = data.length - offset = ((offset ?? 0) | 0) as int32 + const _byte = this._byte; + const _word = this._word; + const length = data.length; + offset = (offset ?? 0) | 0; while (offset < length) { - const start = this._size % inputBytes - let index = start + const start = this._size % inputBytes; + let index = start; while (offset < length && index < inputBytes) { - _byte[index++] = data[offset++] + _byte[index++] = data[offset++]; } if (index >= inputBytes) { - this._int32(_word) + this._int32(_word); } - this._size += index - start + this._size += index - start; } - return this + return this; } private _utf8(text: string): SHA1Hash { - const _byte = this._byte - const _word = this._word - const length = text.length - let surrogate = this._sp + const _byte = this._byte; + const _word = this._word; + const length = text.length; + let surrogate = this._sp; for (let offset = 0; offset < length; ) { - const start = this._size % inputBytes - let index = start + const start = this._size % inputBytes; + let index = start; while (offset < length && index < inputBytes) { - let code = text.charCodeAt(offset++).toInt() | 0 + let code = float64toInt32(text.charCodeAt(offset++)) | 0; if (code < 0x80) { // ASCII characters - _byte[index++] = code + _byte[index++] = code; } else if (code < 0x800) { // 2 bytes - _byte[index++] = 0xC0 | (code >>> 6) - _byte[index++] = 0x80 | (code & 0x3F) - } else if (code < 0xD800 || code > 0xDFFF) { + _byte[index++] = 0xc0 | (code >>> 6); + _byte[index++] = 0x80 | (code & 0x3f); + } else if (code < 0xd800 || code > 0xdfff) { // 3 bytes - _byte[index++] = 0xE0 | (code >>> 12) - _byte[index++] = 0x80 | ((code >>> 6) & 0x3F) - _byte[index++] = 0x80 | (code & 0x3F) + _byte[index++] = 0xe0 | (code >>> 12); + _byte[index++] = 0x80 | ((code >>> 6) & 0x3f); + _byte[index++] = 0x80 | (code & 0x3f); } else if (surrogate) { // 4 bytes - surrogate pair - code = ((surrogate & 0x3FF) << 10) + (code & 0x3FF) + 0x10000 - _byte[index++] = 0xF0 | (code >>> 18) - _byte[index++] = 0x80 | ((code >>> 12) & 0x3F) - _byte[index++] = 0x80 | ((code >>> 6) & 0x3F) - _byte[index++] = 0x80 | (code & 0x3F) - surrogate = 0 + code = ((surrogate & 0x3ff) << 10) + (code & 0x3ff) + 0x10000; + _byte[index++] = 0xf0 | (code >>> 18); + _byte[index++] = 0x80 | ((code >>> 12) & 0x3f); + _byte[index++] = 0x80 | ((code >>> 6) & 0x3f); + _byte[index++] = 0x80 | (code & 0x3f); + surrogate = 0; } else { - surrogate = code + surrogate = int64to32(code); } } if (index >= inputBytes) { - this._int32(_word) - _word[0] = _word[inputWords] + this._int32(_word); + _word[0] = _word[inputWords]; } - this._size += index - start + this._size += index - start; } - this._sp = surrogate - return this + this._sp = surrogate; + return this; } private _int32(data: Int32Array, offset?: int32): void { - let A = this.A - let B = this.B - let C = this.C - let D = this.D - let E = this.E - let i = 0 - offset = ((offset ?? 0) | 0) as int32 + let A = this.A; + let B = this.B; + let C = this.C; + let D = this.D; + let E = this.E; + let i = 0; + offset = (offset ?? 0) | 0; while (i < inputWords) { - W[i++] = swap32(data[offset!++] as int32) + W[i++] = swap32(float64toInt32(data[offset!++])); } for (i = inputWords; i < workWords; i++) { - W[i] = rotate1((W[i - 3] as int32) ^ (W[i - 8] as int32) ^ (W[i - 14] as int32) ^ (W[i - 16] as int32)) + W[i] = rotate1(float64toInt32(W[i - 3] ^ W[i - 8] ^ W[i - 14] ^ W[i - 16])); } for (i = 0; i < workWords; i++) { - const S = (i / 20) | 0 - const T = ((rotate5(A) + ft(S, B, C, D) + E + W[i] + K[S]) as int32) | 0 - E = D - D = C - C = rotate30(B) - B = A - A = T + const S = (i / 20) | 0; + const T = float64toInt32((rotate5(A) + ft(S, B, C, D) + E + W[i] + K[S]) | 0); + E = D; + D = C; + C = rotate30(B); + B = A; + A = T; } - this.A = (A + this.A) | 0 - this.B = (B + this.B) | 0 - this.C = (C + this.C) | 0 - this.D = (D + this.D) | 0 - this.E = (E + this.E) | 0 + this.A = (A + this.A) | 0; + this.B = (B + this.B) | 0; + this.C = (C + this.C) | 0; + this.D = (D + this.D) | 0; + this.E = (E + this.E) | 0; } // digest(): Uint8Array // digest(encoding: string): string digest(encoding?: string): Uint8Array | string { - const _byte = this._byte - const _word = this._word - let i = (this._size % inputBytes) | 0 - _byte[i++] = 0x80 + const _byte = this._byte; + const _word = this._word; + let i = this._size % inputBytes | 0; + _byte[i++] = 0x80; // pad 0 for current word while (i & 3) { - _byte[i++] = 0 + _byte[i++] = 0; } - i >>= 2 + i >>= 2; if (i > highIndex) { while (i < inputWords) { - _word[i++] = 0 + _word[i++] = 0; } - i = 0 - this._int32(_word) + i = 0; + this._int32(_word); } // pad 0 for rest words while (i < inputWords) { - _word[i++] = 0 + _word[i++] = 0; } // input size - const bits64: int32 = this._size * 8 - const low32: int32 = ((bits64 & 0xffffffff) as int32 >>> 0) as int32 - const high32: int32 = ((bits64 - low32) as int32 / 0x100000000) as int32 - if (high32) _word[highIndex] = swap32(high32) as int32 - if (low32) _word[lowIndex] = swap32(low32) as int32 + const bits64: int32 = this._size * 8; + const low32: int32 = float64toInt32((bits64 & 0xffffffff) >>> 0); + const high32: int32 = float64toInt32((bits64 - low32) / 0x100000000); + if (high32) _word[highIndex] = swap32(high32); + if (low32) _word[lowIndex] = swap32(low32); - this._int32(_word) + this._int32(_word); - return (encoding === "hex") ? this._hex() : this._bin() + return encoding === 'hex' ? this._hex() : this._bin(); } private _hex(): string { - let A = this.A - let B = this.B - let C = this.C - let D = this.D - let E = this.E + let A = this.A; + let B = this.B; + let C = this.C; + let D = this.D; + let E = this.E; - return hex32Str(A, B, C, D, E) + return hex32Str(A, B, C, D, E); } private _bin(): Uint8Array { - let A = this.A - let B = this.B - let C = this.C - let D = this.D - let E = this.E - const _byte = this._byte - const _word = this._word - - _word[0] = swap32(A) - _word[1] = swap32(B) - _word[2] = swap32(C) - _word[3] = swap32(D) - _word[4] = swap32(E) - - return _byte.slice(0, 20) + let A = this.A; + let B = this.B; + let C = this.C; + let D = this.D; + let E = this.E; + const _byte = this._byte; + const _word = this._word; + + _word[0] = swap32(A); + _word[1] = swap32(B); + _word[2] = swap32(C); + _word[3] = swap32(D); + _word[4] = swap32(E); + + return _byte.slice(0, 20); } } -type NS = (num: int32) => string -type NN = (num: int32) => int32 +type NS = (num: int32) => string; +type NN = (num: int32) => int32; -const W = new Int32Array(workWords) +const W = new Int32Array(workWords); -let sharedBuffer: ArrayBuffer -let sharedOffset: int32 = 0 +let sharedBuffer: ArrayBuffer; +let sharedOffset: int32 = 0; -const swapLE: NN = ((c:int32):int32 => (((c << 24) & 0xff000000) | ((c << 8) & 0xff0000) | ((c >> 8) & 0xff00) | ((c >> 24) & 0xff))) -const swapBE: NN = ((c:int32):int32 => c) -const swap32: NN = isBE() ? swapBE : swapLE -const rotate1: NN = (num: int32): int32 => (num << 1) | (num >>> 31) -const rotate5: NN = (num: int32): int32 => (num << 5) | (num >>> 27) -const rotate30: NN = (num: int32): int32 => (num << 30) | (num >>> 2) +const swapLE: NN = (c: int32): int32 => + ((c << 24) & 0xff000000) | ((c << 8) & 0xff0000) | ((c >> 8) & 0xff00) | ((c >> 24) & 0xff); +const swapBE: NN = (c: int32): int32 => c; +const swap32: NN = isBE() ? swapBE : swapLE; +const rotate1: NN = (num: int32): int32 => (num << 1) | (num >>> 31); +const rotate5: NN = (num: int32): int32 => (num << 5) | (num >>> 27); +const rotate30: NN = (num: int32): int32 => (num << 30) | (num >>> 2); function isBE(): boolean { - let a16 = new Uint16Array(1) - a16[0] = 0xFEFF - let a8 = new Uint8Array(a16.buffer) - return a8[0] == 0xFE // BOM + let a16 = new Uint16Array(1); + a16[0] = 0xfeff; + let a8 = new Uint8Array(a16.buffer); + return a8[0] == 0xfe; // BOM } - function ft(s: int32, b: int32, c: int32, d: int32) { - if (s == 0) return (b & c) | ((~b) & d) - if (s == 2) return (b & c) | (b & d) | (c & d) - return b ^ c ^ d + if (s == 0) return (b & c) | (~b & d); + if (s == 2) return (b & c) | (b & d) | (c & d); + return b ^ c ^ d; } -const hex32Decoder = new CustomTextDecoder() -const hex32DecodeBuffer = new Uint8Array(40) +const hex32Decoder = new CustomTextDecoder(); +const hex32DecodeBuffer = new Uint8Array(40); function hex32Str(A: int32, B: int32, C: int32, D: int32, E: int32): string { - writeIntAsHexUTF8(A, hex32DecodeBuffer, 0) - writeIntAsHexUTF8(B, hex32DecodeBuffer, 8) - writeIntAsHexUTF8(C, hex32DecodeBuffer, 16) - writeIntAsHexUTF8(D, hex32DecodeBuffer, 24) - writeIntAsHexUTF8(E, hex32DecodeBuffer, 32) - return hex32Decoder.decode(hex32DecodeBuffer) + writeIntAsHexUTF8(A, hex32DecodeBuffer, 0); + writeIntAsHexUTF8(B, hex32DecodeBuffer, 8); + writeIntAsHexUTF8(C, hex32DecodeBuffer, 16); + writeIntAsHexUTF8(D, hex32DecodeBuffer, 24); + writeIntAsHexUTF8(E, hex32DecodeBuffer, 32); + return hex32Decoder.decode(hex32DecodeBuffer); } function writeIntAsHexUTF8(value: int32, buffer: Uint8Array, byteOffset: int32) { - buffer[byteOffset++] = nibbleToHexCode((value >> 28) & 0xF) - buffer[byteOffset++] = nibbleToHexCode((value >> 24) & 0xF) - buffer[byteOffset++] = nibbleToHexCode((value >> 20) & 0xF) - buffer[byteOffset++] = nibbleToHexCode((value >> 16) & 0xF) - buffer[byteOffset++] = nibbleToHexCode((value >> 12) & 0xF) - buffer[byteOffset++] = nibbleToHexCode((value >> 8 ) & 0xF) - buffer[byteOffset++] = nibbleToHexCode((value >> 4 ) & 0xF) - buffer[byteOffset++] = nibbleToHexCode((value >> 0 ) & 0xF) + buffer[byteOffset++] = nibbleToHexCode((value >> 28) & 0xf); + buffer[byteOffset++] = nibbleToHexCode((value >> 24) & 0xf); + buffer[byteOffset++] = nibbleToHexCode((value >> 20) & 0xf); + buffer[byteOffset++] = nibbleToHexCode((value >> 16) & 0xf); + buffer[byteOffset++] = nibbleToHexCode((value >> 12) & 0xf); + buffer[byteOffset++] = nibbleToHexCode((value >> 8) & 0xf); + buffer[byteOffset++] = nibbleToHexCode((value >> 4) & 0xf); + buffer[byteOffset++] = nibbleToHexCode((value >> 0) & 0xf); } function nibbleToHexCode(nibble: int32) { - return nibble > 9 ? nibble + 87 : nibble + 48 + return nibble > 9 ? nibble + 87 : nibble + 48; } diff --git a/frameworks/bridge/arkts_frontend/koala_projects/incremental/common/src/stringUtils.ts b/frameworks/bridge/arkts_frontend/koala_projects/incremental/common/src/stringUtils.ts index b69d747b83a..e4a5853f5c5 100644 --- a/frameworks/bridge/arkts_frontend/koala_projects/incremental/common/src/stringUtils.ts +++ b/frameworks/bridge/arkts_frontend/koala_projects/incremental/common/src/stringUtils.ts @@ -13,17 +13,15 @@ * limitations under the License. */ -import { int32 } from "@koalaui/compat" - +import { float64toInt32, int32, int64to32 } from '@koalaui/compat'; /** * Computes a hash code from the string {@link value}. */ export function hashCodeFromString(value: string): int32 { - let hash = 5381 - for(let i = 0; i < value.length; i++) { - hash = (hash * 33) ^ value.charCodeAt(i).toInt() - hash |= 0 + let hash = 5381; + for (let i = 0; i < value.length; i++) { + hash = int64to32((hash * 33) ^ float64toInt32(value.charCodeAt(i))); } - return hash + return hash; } diff --git a/frameworks/bridge/arkts_frontend/koala_projects/incremental/common/src/uniqueId.ts b/frameworks/bridge/arkts_frontend/koala_projects/incremental/common/src/uniqueId.ts index a0e324dbe3d..e5511e65881 100644 --- a/frameworks/bridge/arkts_frontend/koala_projects/incremental/common/src/uniqueId.ts +++ b/frameworks/bridge/arkts_frontend/koala_projects/incremental/common/src/uniqueId.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2023 Huawei Device Co., Ltd. + * Copyright (c) 2022-2025 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 @@ -13,50 +13,50 @@ * limitations under the License. */ -import { int32 } from "@koalaui/compat" -import { createSha1 } from "./sha1"; +import { float64toInt32, int32 } from '@koalaui/compat'; +import { createSha1 } from './sha1'; export class UniqueId { - private sha = createSha1() + private sha = createSha1(); public addString(data: string): UniqueId { - this.sha.updateString(data) - return this + this.sha.updateString(data); + return this; } public addI32(data: int32): UniqueId { - this.sha.updateInt32(data) - return this + this.sha.updateInt32(data); + return this; } public addF32Array(data: Float32Array): UniqueId { - this.sha.update(data) - return this + this.sha.update(data); + return this; } public addI32Array(data: Int32Array): UniqueId { - this.sha.update(data) - return this + this.sha.update(data); + return this; } public addU32Array(data: Uint32Array): UniqueId { - this.sha.update(data) - return this + this.sha.update(data); + return this; } public addU8Array(data: Uint8Array): UniqueId { - this.sha.update(data) - return this + this.sha.update(data); + return this; } public addPtr(data: Uint32Array | number): UniqueId { if (data instanceof Uint32Array) { - return this.addU32Array(data) + return this.addU32Array(data); } - return this.addI32(data as int32) + return this.addI32(float64toInt32(data)); } public compute(): string { - return this.sha.digest("hex") as string + return this.sha.digest('hex') as string; } } diff --git a/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/arkts/array.ts b/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/arkts/array.ts index e6d3eaf2b1d..843ad018de4 100644 --- a/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/arkts/array.ts +++ b/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/arkts/array.ts @@ -13,47 +13,48 @@ * limitations under the License. */ -import { float64, int32, int8 } from "./types" +import { float64, int32, int8 } from './types'; // Improve: this can be a performance disaster // just wait for the library to provide the proper functionality. export function asArray(value: T[]): Array { - return Array.of(...value) + return Array.of(...value); } // Improve: this can be a performance disaster // just wait for the library to provide the proper functionality. export function Array_from_set(set: Set): Array { - const array = new Array() // to avoid creation of undefined content - const values = set.values() - for (let it = values.next(); it.done != true; it = values.next()) { - array.push(it.value as T) + const array = new Array(); // to avoid creation of undefined content + const values = set.values(); + for (let it = values.next(); it.done != true; it = values.next()) { + array.push(it.value as T); } - return array + return array; } // Improve: this can be a performance disaster // just wait for the library to provide the proper functionality. export function Array_from_int32(data: Int32Array): number[] { - const result: number[] = [] + const result: number[] = []; for (let i: int32 = 0; i < data.length; i++) { - result[i] = data.at(i) as number + result[i] = data.at(i) as number; } - return result + return result; } // Improve: this can be a performance disaster // just wait for the library to provide the proper functionality. export function Array_from_number(data: float64[]): Array { - const result = new Array(data.length) + const result = new Array(data.length); for (let i: int32 = 0; i < data.length; i++) { - result[i] = data[i] + result[i] = data[i]; } - return result + return result; } export function int8Array(size: int32): FixedArray { - const array: FixedArray = new int8[size] - return array + // Adding () breaks the array creation + // prettier-ignore + const array: FixedArray = new int8[size]; + return array; } - diff --git a/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/arkts/atomic.ts b/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/arkts/atomic.ts index bbe71d19e2e..a3418ecbb06 100644 --- a/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/arkts/atomic.ts +++ b/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/arkts/atomic.ts @@ -17,14 +17,14 @@ * A reference that may be updated atomically. */ export class AtomicRef { - value: Value + value: Value; /** * Creates a new reference object with the given initial value. * @param value - the new value */ constructor(value: Value) { - this.value = value + this.value = value; } /** @@ -34,8 +34,8 @@ export class AtomicRef { */ getAndSet(value: Value): Value { // Improve: replace with the implementation from ArkTS language when it is ready - const result = this.value - this.value = value - return result + const result = this.value; + this.value = value; + return result; } } diff --git a/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/arkts/finalization.ts b/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/arkts/finalization.ts index 021bc1d43f1..7648b12d394 100644 --- a/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/arkts/finalization.ts +++ b/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/arkts/finalization.ts @@ -1,4 +1,3 @@ - /* * Copyright (c) 2024 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,17 +14,17 @@ */ export interface Thunk { - clean(): void + clean(): void; } const registry = new FinalizationRegistry((thunk: Thunk) => { - thunk.clean() -}) + thunk.clean(); +}); export function finalizerRegister(target: Object, thunk: Object) { - registry.register(target, thunk as Thunk) + registry.register(target, thunk as Thunk); } export function finalizerUnregister(target: Object) { - registry.unregister(target) + registry.unregister(target); } diff --git a/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/arkts/index.ts b/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/arkts/index.ts index 610fafc3ffb..f12c977dd56 100644 --- a/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/arkts/index.ts +++ b/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/arkts/index.ts @@ -13,15 +13,15 @@ * limitations under the License. */ -export * from "./array" -export * from "./atomic" -export * from "./primitive" -export * from "./finalization" -export * from "./performance" -export * from "./prop-deep-copy" -export * from "./observable" -export * from "./reflection" -export * from "./strings" -export * from "./ts-reflection" -export * from "./types" -export * from "./utils" +export * from './array'; +export * from './atomic'; +export * from './primitive'; +export * from './finalization'; +export * from './performance'; +export * from './prop-deep-copy'; +export * from './observable'; +export * from './reflection'; +export * from './strings'; +export * from './ts-reflection'; +export * from './types'; +export * from './utils'; diff --git a/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/arkts/observable.ts b/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/arkts/observable.ts index 1cb0d0ea53a..f28ece65d70 100644 --- a/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/arkts/observable.ts +++ b/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/arkts/observable.ts @@ -13,15 +13,18 @@ * limitations under the License. */ -import { int32 } from "./types" - -const OBSERVABLE_TARGET = "target" - -export function getObservableTarget(proxy: Object): Object { +export function getObservableTarget(proxy0: Object): Object { try { - return (((reflect.Value.of(proxy) as ClassValue).getFieldByName(OBSERVABLE_TARGET).getData()) ?? proxy) as Object + // do not use proxy for own observables + if (proxy0 instanceof ObservableArray + || proxy0 instanceof ObservableDate + || proxy0 instanceof ObservableMap + || proxy0 instanceof ObservableSet ) { + return proxy0 + } + return (proxy.Proxy.tryGetTarget(proxy0) as Object|undefined|null) ?? proxy0 } catch (error) { - return proxy + return proxy0 } } @@ -229,51 +232,52 @@ export function observableProxy(value: Value, parent?: ObservableHandler, const valueType = Type.of(value) if (valueType instanceof ClassType && !(value instanceof BaseEnum)) { - const meta = extractObservableMetadata(value) - if (meta == undefined) { + const isObservable = isObservedV1Class(value as Object) + if (!hasTrackableProperties(value as Object) && !isObservable) { return value as Value } if (valueType.hasEmptyConstructor()) { - const result = proxy.Proxy.create(value as Object, new CustomProxyHandler(meta)) as Value + const result = proxy.Proxy.create(value as Object, new CustomProxyHandler(isObservable)) as Value ObservableHandler.installOn(result as Object, new ObservableHandler(parent)) return result } else { throw new Error(`Class '${valueType.getName()}' must contain a default constructor`) } } - return value as Value } class CustomProxyHandler extends proxy.DefaultProxyHandler { - private readonly metadataClass: MetadataClass + private readonly isObservable: boolean - constructor(metadataClass: MetadataClass) { + constructor(isObservable: boolean) { super(); - this.metadataClass = metadataClass + this.isObservable = isObservable } - override get(target: T, name: string): NullishType { + override get(target: T, name: string): Any { const value = super.get(target, name) const targetHandler = ObservableHandler.find(target) - if (targetHandler && this.metadataClass.isObservedClass) { + if (targetHandler && this.isObservable) { const valueHandler = ObservableHandler.find(value as Object) if (valueHandler && !targetHandler.hasChild(valueHandler)) { valueHandler.addParent(targetHandler) } } - targetHandler?.onAccess(this.metadataClass.trackedProperties?.has(name) ? name : undefined) + targetHandler?.onAccess(name) return value } - override set(target: T, name: string, value: NullishType): boolean { + override set(target: T, name: string, value: Any): boolean { const observable = ObservableHandler.find(target) if (observable) { - observable.onModify(this.metadataClass.trackedProperties?.has(name) ? name : undefined) + observable.onModify(name) observable.removeChild(super.get(target, name)) value = observableProxy(value, observable, ObservableHandler.contains(observable)) } - return super.set(target, name, value) + const result = super.set(target, name, value) + observable?.onModify(name) + return result } } @@ -309,8 +313,8 @@ class ObservableArray extends Array { } override set length(length: int) { - this.handler?.onModify() super.length = length + this.handler?.onModify() } override at(index: int): T | undefined { @@ -326,84 +330,98 @@ class ObservableArray extends Array { override $_set(index: int, value: T): void { const handler = this.handler if (handler) { - handler.onModify() handler.removeChild(super.$_get(index)) value = observableProxy(value, handler) } super.$_set(index, value) + handler?.onModify() } override copyWithin(target: int, start: int, end: int): this { - this.handler?.onModify() super.copyWithin(target, start, end) + this.handler?.onModify() return this } override fill(value: T, start: int, end: int): this { const handler = this.handler if (handler) { - handler.onModify() value = observableProxy(value, handler) } super.fill(value, start, end) + handler?.onModify() return this } override pop(): T | undefined { const handler = this.handler - handler?.onModify() const result = super.pop() - if (result) handler?.removeChild(result) + handler?.onModify() + if (result) { + handler?.removeChild(result) + } return result } override pushArray(...items: T[]): number { const handler = this.handler if (handler) { - handler.onModify() proxyChildrenOnly(items, handler) } - return super.pushArray(...items) + const result = super.pushArray(...items) + handler?.onModify() + return result + } + + override pushOne(value: T): number { + const handler = this.handler + if (handler) { + value = observableProxy(value, handler) + } + const result = super.pushOne(value) + handler?.onModify() + return result } override pushECMA(...items: T[]): number { const handler = this.handler if (handler) { - handler.onModify() proxyChildrenOnly(items, handler) } - return super.pushECMA(...items) + const result = super.pushECMA(...items) + handler?.onModify() + return result } override reverse(): this { - this.handler?.onModify() super.reverse() + this.handler?.onModify() return this } override shift(): T | undefined { const handler = this.handler - handler?.onModify() const result = super.shift() if (result) handler?.removeChild(result) + handler?.onModify() return result } override sort(comparator?: (a: T, b: T) => number): this { - this.handler?.onModify() super.sort(comparator) + this.handler?.onModify() return this } override splice(index: int, count: int, ...items: T[]): Array { const handler = this.handler if (handler) { - handler.onModify() proxyChildrenOnly(items, handler) const result = super.splice(index, count, ...items) for (let i = 0; i < result.length; i++) { handler.removeChild(result[i]) } + handler.onModify() return result } return super.splice(index, count, ...items) @@ -412,10 +430,11 @@ class ObservableArray extends Array { override unshift(...items: T[]): number { const handler = this.handler if (handler) { - handler.onModify() proxyChildrenOnly(items, handler) } - return super.unshift(...items) + const result = super.unshift(...items) + handler?.onModify() + return result } override keys(): IterableIterator { @@ -592,7 +611,7 @@ class ObservableMap extends Map { return ObservableHandler.find(this) } - override get size(): Int { + override get size(): int { this.handler?.onAccess() return super.size } @@ -610,34 +629,35 @@ class ObservableMap extends Map { override set(key: T, value: V): this { const handler = this.handler if (handler) { - handler.onModify() const prev = super.get(key) if (prev) handler.removeChild(prev) value = observableProxy(value, handler) } super.set(key, value) + handler?.onModify() return this } override delete(key: T): boolean { const handler = this.handler if (handler) { - handler.onModify() const value = super.get(key) if (value) handler.removeChild(value) } - return super.delete(key) + const result = super.delete(key) + handler?.onModify() + return result } override clear() { const handler = this.handler if (handler) { - handler.onModify() for (let value of super.values()) { handler!.removeChild(value) } } super.clear() + handler?.onModify() } override keys(): IterableIterator { @@ -696,7 +716,7 @@ class ObservableSet extends Set { return new Set(this.elements.keys()).toString() } - override get size(): Int { + override get size(): int { this.handler?.onAccess() return this.elements.size } @@ -709,35 +729,37 @@ class ObservableSet extends Set { override add(value: T): this { const handler = this.handler let observable = value + let modified = !this.elements.has(value) if (handler) { - if (!this.elements.has(value)) handler.onModify() const prev = this.elements.get(value) if (prev) handler.removeChild(prev) observable = observableProxy(value) } this.elements.set(value, observable) + if (modified) handler?.onModify() return this } override delete(value: T): boolean { const handler = this.handler if (handler) { - handler.onModify() const prev = this.elements.get(value) if (prev) handler.removeChild(prev) } - return this.elements.delete(value) + const result = this.elements.delete(value) + handler?.onModify() + return result } override clear() { const handler = this.handler if (handler) { - handler.onModify() for (let value of this.elements.values()) { handler!.removeChild(value) } } this.elements.clear() + handler?.onModify() } override keys(): IterableIterator { @@ -860,13 +882,14 @@ class ObservableDate extends Date { } override setDate(value: byte) { - this.handler?.onModify() super.setDate(value) + this.handler?.onModify() } override setDate(value: number): number { + const result = super.setDate(value) this.handler?.onModify() - return super.setDate(value) + return result } override getUTCDate(): number { @@ -875,13 +898,14 @@ class ObservableDate extends Date { } override setUTCDate(value: byte) { - this.handler?.onModify() super.setUTCDate(value) + this.handler?.onModify() } override setUTCDate(value: number): number { + const result = super.setUTCDate(value) this.handler?.onModify() - return super.setUTCDate(value) + return result } override getDay(): number { @@ -890,8 +914,8 @@ class ObservableDate extends Date { } override setDay(value: byte) { - this.handler?.onModify() super.setDay(value) + this.handler?.onModify() } override getUTCDay(): number { @@ -900,13 +924,14 @@ class ObservableDate extends Date { } override setUTCDay(value: byte) { - this.handler?.onModify() super.setUTCDay(value) + this.handler?.onModify() } override setUTCDay(value: number): number { + const result = super.setUTCDay(value) this.handler?.onModify() - return super.setUTCDay(value) + return result } override getMonth(): number { @@ -915,13 +940,14 @@ class ObservableDate extends Date { } override setMonth(value: int) { - this.handler?.onModify() super.setMonth(value) + this.handler?.onModify() } override setMonth(value: number, date?: number): number { + const result = super.setMonth(value, date) this.handler?.onModify() - return super.setMonth(value, date) + return result } override getUTCMonth(): number { @@ -930,13 +956,14 @@ class ObservableDate extends Date { } override setUTCMonth(value: int) { - this.handler?.onModify() super.setUTCMonth(value) + this.handler?.onModify() } override setUTCMonth(value: number, date?: number): number { + const result = super.setUTCMonth(value, date) this.handler?.onModify() - return super.setUTCMonth(value, date) + return result } override getYear(): int { @@ -945,13 +972,13 @@ class ObservableDate extends Date { } override setYear(value: int) { - this.handler?.onModify() super.setYear(value) + this.handler?.onModify() } override setYear(value: number) { - this.handler?.onModify() super.setYear(value) + this.handler?.onModify() } override getFullYear(): number { @@ -960,13 +987,14 @@ class ObservableDate extends Date { } override setFullYear(value: number, month?: number, date?: number): number { + const result = super.setFullYear(value, month, date) this.handler?.onModify() - return super.setFullYear(value, month, date) + return result } override setFullYear(value: int) { - this.handler?.onModify() super.setFullYear(value) + this.handler?.onModify() } override getUTCFullYear(): number { @@ -975,13 +1003,14 @@ class ObservableDate extends Date { } override setUTCFullYear(value: number, month?: number, date?: number): number { + const result = super.setUTCFullYear(value, month, date) this.handler?.onModify() - return super.setUTCFullYear(value, month, date) + return result } override setUTCFullYear(value: int) { - this.handler?.onModify() super.setUTCFullYear(value) + this.handler?.onModify() } override getTime(): number { @@ -990,13 +1019,14 @@ class ObservableDate extends Date { } override setTime(value: long) { - this.handler?.onModify() super.setTime(value) + this.handler?.onModify() } override setTime(value: number): number { + const result = super.setTime(value) this.handler?.onModify() - return super.setTime(value) + return result } override getHours(): number { @@ -1005,13 +1035,14 @@ class ObservableDate extends Date { } override setHours(value: number, min?: number, sec?: number, ms?: number): number { + const result = super.setHours(value, min, sec, ms) this.handler?.onModify() - return super.setHours(value, min, sec, ms) + return result } override setHours(value: byte) { - this.handler?.onModify() super.setHours(value) + this.handler?.onModify() } override getUTCHours(): number { @@ -1020,13 +1051,14 @@ class ObservableDate extends Date { } override setUTCHours(value: number, min?: number, sec?: number, ms?: number): number { + const result = super.setUTCHours(value, min, sec, ms) this.handler?.onModify() - return super.setUTCHours(value, min, sec, ms) + return result } override setUTCHours(value: byte) { - this.handler?.onModify() super.setUTCHours(value) + this.handler?.onModify() } override getMilliseconds(): number { @@ -1035,13 +1067,14 @@ class ObservableDate extends Date { } override setMilliseconds(value: short) { - this.handler?.onModify() super.setMilliseconds(value) + this.handler?.onModify() } override setMilliseconds(value: number): number { + const result = super.setMilliseconds(value) this.handler?.onModify() - return super.setMilliseconds(value) + return result } override getUTCMilliseconds(): number { @@ -1050,13 +1083,14 @@ class ObservableDate extends Date { } override setUTCMilliseconds(value: short) { - this.handler?.onModify() super.setUTCMilliseconds(value) + this.handler?.onModify() } override setUTCMilliseconds(value: number): number { + const result = super.setUTCMilliseconds(value) this.handler?.onModify() - return super.setUTCMilliseconds(value) + return result } override getSeconds(): number { @@ -1065,13 +1099,14 @@ class ObservableDate extends Date { } override setSeconds(value: byte) { - this.handler?.onModify() super.setSeconds(value) + this.handler?.onModify() } override setSeconds(value: number, ms?: number): number { + const result = super.setSeconds(value, ms) this.handler?.onModify() - return super.setSeconds(value, ms) + return result } override getUTCSeconds(): number { @@ -1080,13 +1115,14 @@ class ObservableDate extends Date { } override setUTCSeconds(value: byte) { - this.handler?.onModify() super.setUTCSeconds(value) + this.handler?.onModify() } override setUTCSeconds(value: number, ms?: number): number { + const result = super.setUTCSeconds(value, ms) this.handler?.onModify() - return super.setUTCSeconds(value, ms) + return result } override getMinutes(): number { @@ -1095,13 +1131,14 @@ class ObservableDate extends Date { } override setMinutes(value: byte) { - this.handler?.onModify() super.setMinutes(value) + this.handler?.onModify() } override setMinutes(value: number, sec?: Number, ms?: number): number { + const result = super.setMinutes(value, sec, ms) this.handler?.onModify() - return super.setMinutes(value, sec, ms) + return result } override getUTCMinutes(): number { @@ -1110,52 +1147,135 @@ class ObservableDate extends Date { } override setUTCMinutes(value: byte) { - this.handler?.onModify() super.setUTCMinutes(value) + this.handler?.onModify() } override setUTCMinutes(value: number, sec?: Number, ms?: number): number { + const result = super.setUTCMinutes(value, sec, ms) this.handler?.onModify() - return super.setUTCMinutes(value, sec, ms) + return result } } -class MetadataClass { - readonly isObservedClass: boolean - readonly trackedProperties: ReadonlySet | undefined +function getClassMetadata(value: T): ClassMetadata | undefined { + return value instanceof ObservableClass ? value.getClassMetadata() : undefined +} - constructor(isObservedClass: boolean, - trackedProperties: ReadonlySet | undefined) { - this.isObservedClass = isObservedClass - this.trackedProperties = trackedProperties - } +function isObservedV1Class(value: Object): boolean { + return getClassMetadata(value)?.isObservedV1(value) ?? false } -function extractObservableMetadata(value: T): MetadataClass | undefined { - const isObservedClass = value instanceof ObservableClass ? value.isObserved() : false - const trackedProperties = value instanceof TrackableProps ? value.trackedProperties() : undefined - if (isObservedClass || trackedProperties) { - return new MetadataClass(isObservedClass, trackedProperties) - } - return undefined +function hasTrackableProperties(value: Object): boolean { + return getClassMetadata(value)?.hasTrackableProperties() ?? false } /** - * Interface for getting the observed properties of a class + * Interface for getting the observability status of a class */ -export interface TrackableProps { - /** - * Retrieves the set of property names that are being tracked for changes using `@Track` decorator - */ - trackedProperties(): ReadonlySet | undefined +export interface ObservableClass { + getClassMetadata(): ClassMetadata | undefined } /** - * Interface for getting the observability status of a class + * Interface for checking the observed properties of a class */ -export interface ObservableClass { +export interface TrackableProperties { + isTrackable(propertyName: string): boolean +} + +/** + * If value is a class, then returns a list of trackable properties + * @param value + */ +export function trackableProperties(value: T): TrackableProperties | undefined { + return getClassMetadata(value) +} + +export class ClassMetadata implements TrackableProperties { + private readonly parent: ClassMetadata | undefined + private readonly markAsObservedV1: boolean + private readonly markAsObservedV2: boolean + private readonly targetClass: Class + private static readonly metadataPropName = "__classMetadata" + /** - * Indicates whether the class is decorated with `@Observed`. + * Class property names marked with the @Track or @Trace decorator + * @private */ - isObserved(): boolean + private readonly trackableProperties: ReadonlySet | undefined + + /** + * Contains fields marked with the @Type decorator. + * The key of the map is the property name and the value is the typename of the corresponding field. + * @private + */ + private readonly typedProperties: ReadonlyMap | undefined + + constructor(parent: ClassMetadata | undefined, + markAsObservedV1: boolean, + markAsObservedV2: boolean, + trackable: string[] | undefined, + typed: [string, string][] | undefined) { + const target = Class.ofCaller() + if (target == undefined) { + throw new Error("ClassMetadata must be created in the class context") + } + this.targetClass = target! + this.parent = parent + this.markAsObservedV1 = markAsObservedV1 + this.markAsObservedV2 = markAsObservedV2 + if (trackable) { + this.trackableProperties = new Set(trackable) + } + if (typed) { + this.typedProperties = new Map(typed) + } + } + + isObservedV1(value: Object): boolean { + return this.markAsObservedV1 && Class.of(value) == this.targetClass + } + + isObservedV2(value: Object): boolean { + return this.markAsObservedV2 && Class.of(value) == this.targetClass + } + + isTrackable(propertyName: string): boolean { + return (this.trackableProperties?.has(propertyName) || this.parent?.isTrackable(propertyName)) ?? false + } + + hasTrackableProperties(): boolean { + if (this.trackableProperties) { + return this.trackableProperties!.size > 0 + } + return this.parent?.hasTrackableProperties() ?? false + } + + getTypenameTypeDecorator(propertyName: string): string | undefined { + if (this.typedProperties) { + return this.typedProperties?.get(propertyName) + } + if (this.parent) { + return this.parent!.getTypenameTypeDecorator(propertyName) + } + return undefined + } + + static findClassMetadata(type: Type): ClassMetadata | undefined { + if (type instanceof ClassType) { + const fieldsNum = type.getFieldsNum() + for (let i = 0; i < fieldsNum; i++) { + const field = type.getField(i) + if (field.isStatic() && field.getName() == ClassMetadata.metadataPropName) { + const meta = field.getStaticValue() + if (meta != undefined && meta instanceof ClassMetadata) { + return meta + } + break + } + } + } + return undefined + } } diff --git a/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/arkts/performance.ts b/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/arkts/performance.ts index 8926ff8519d..2b7c1c4dbe1 100644 --- a/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/arkts/performance.ts +++ b/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/arkts/performance.ts @@ -18,7 +18,7 @@ * January 1, 1970 Universal Coordinated Time (UTC). */ export function timeNow(): number { - return Date.now() + return Date.now(); } /** @@ -26,5 +26,5 @@ export function timeNow(): number { * @returns a string representing a number in fixed-point notation */ export function numberToFixed(value: number, fractionDigits: number): string { - return new Number(value).toFixed(fractionDigits) + return new Number(value).toFixed(fractionDigits); } diff --git a/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/arkts/primitive.ts b/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/arkts/primitive.ts index 99d14992b46..f2555e3d3c1 100644 --- a/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/arkts/primitive.ts +++ b/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/arkts/primitive.ts @@ -1,6 +1,5 @@ - /* - * Copyright (c) 2024 Huawei Device Co., Ltd. + * Copyright (c) 2024-2025 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 @@ -13,42 +12,48 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { float64, int32, float32, int64 } from "./types" +import { float32, float64, int32, int64 } from './types'; -export function float32To64(value: float32): float64 { - return Float.toDouble(value) +export function float32to64(value: float32): float64 { + return Float.toDouble(value); } - -export function float64To32(value: float64): float32 { - return Double.toFloat(value) +export function float32toInt32(value: float32): int32 { + return Float.toInt(value); } - -export function asFloat64(value: string): float64 { - return (new Number(value)).valueOf() +export function float32toInt64(value: float32): int64 { + return Float.toLong(value); } -export function asString(value: float64 | undefined): string | undefined { - if (value === undefined) return undefined - return (new Number(value)).toString() +export function float64to32(value: float64): float32 { + return Double.toFloat(value); } - -export function float32FromBits(value: int32): float32 { - return Float.bitCastFromInt(value) +export function float64toInt32(value: float64): int32 { + return Double.toInt(value); } - -export function int32BitsFromFloat(value: float32): int32 { - return Float.bitCastToInt(value) +export function float64toInt64(value: float64): int64 { + return Double.toLong(value); } -export function float64ToInt(value: float64): int32 { - return value.toInt() +export function int32toFloat32(value: int32): float32 { + return Int.toFloat(value); } - -export function float64ToLong(value: float64): int64 { - return value.toLong() +export function int32toFloat64(value: int32): float64 { + return Int.toDouble(value); +} +export function int32to64(value: int32): int64 { + return Int.toLong(value); } -export function charToInt(value: char): int32 { - return value.toInt() +export function int64toFloat32(value: int64): float32 { + return Long.toFloat(value); +} +export function int64toFloat64(value: int64): float64 { + return Long.toDouble(value); +} +export function int64to32(value: int64): int32 { + return Long.toInt(value); } +export function asFloat64(value: string): float64 { + return new Number(value).valueOf(); +} diff --git a/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/arkts/prop-deep-copy.ts b/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/arkts/prop-deep-copy.ts index 1e13dfc129d..53aa5e2d0d1 100644 --- a/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/arkts/prop-deep-copy.ts +++ b/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/arkts/prop-deep-copy.ts @@ -18,7 +18,15 @@ @Prop makes a deep copy, during which all types, except primitive types, Map, Set, Date, and Array, will be lost. */ +import { getObservableTarget } from './observable.ts'; export function propDeepCopy(sourceObject: T): T { - return deepcopy(sourceObject) as T + // at the moment of intergation deepcopy from the stdlib requires a default constructor + // but default constructor is not available for ObservableDate, so we + // add a special case for Date (a parent for ObservableDate) + if (sourceObject instanceof Date) { + const copy: Date = new Date(sourceObject.valueOf()); + return copy as T; + } + return deepcopy(getObservableTarget(sourceObject as Object) as T) as T; } diff --git a/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/arkts/reflection.ts b/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/arkts/reflection.ts index 7961ab53e33..9768e144ff1 100644 --- a/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/arkts/reflection.ts +++ b/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/arkts/reflection.ts @@ -1,4 +1,3 @@ - /* * Copyright (c) 2022-2023 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); @@ -13,8 +12,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { className } from "./ts-reflection" +import { className } from './ts-reflection'; export function lcClassName(object: Object) { - return className(object).toLowerCase() + return className(object).toLowerCase(); } diff --git a/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/arkts/strings.ts b/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/arkts/strings.ts index 66442a51c03..2d1d21f5fa6 100644 --- a/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/arkts/strings.ts +++ b/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/arkts/strings.ts @@ -13,9 +13,8 @@ * limitations under the License. */ -import { int32, uint8 } from "./types" -import { Array_from_int32 } from "./array" - +import { int32, uint8 } from './types'; +import { Array_from_int32 } from './array'; interface SystemTextEncoder { encode(input?: string): Uint8Array; @@ -27,188 +26,193 @@ interface WithStreamOption { } interface SystemTextDecoder { - decode( - input: ArrayBuffer | null | undefined | Uint8Array, - options: WithStreamOption | undefined - ): string; + decode(input: ArrayBuffer | null | undefined | Uint8Array, options: WithStreamOption | undefined): string; } export class CustomTextEncoder { - static readonly HeaderLen: int32 = Int32Array.BYTES_PER_ELEMENT + static readonly HeaderLen: int32 = Int32Array.BYTES_PER_ELEMENT; - constructor(encoder:SystemTextEncoder|undefined = undefined) { - this.encoder = encoder + constructor(encoder: SystemTextEncoder | undefined = undefined) { + this.encoder = encoder; } - private readonly encoder: SystemTextEncoder|undefined + private readonly encoder: SystemTextEncoder | undefined; public static stringLength(input: string): int32 { - let length = 0 + let length = 0; for (let i = 0; i < input.length; i++) { - length++ - let cp = input.codePointAt(i)! + length++; + let cp = input.codePointAt(i)!; if (cp >= 0x10000) { - i++ + i++; } } - return length + return length; } encodedLength(input: string): int32 { - let length = 0 + let length = 0; for (let i = 0; i < input.length; i++) { - let cp = input.codePointAt(i)! + let cp = input.codePointAt(i)!; if (cp < 0x80) { - length += 1 + length += 1; } else if (cp < 0x800) { - length += 2 + length += 2; } else if (cp < 0x10000) { - length += 3 + length += 3; } else { - length += 4 - i++ + length += 4; + i++; } } - return length + return length; } private addLength(array: Uint8Array, offset: int32, length: int32 | number): void { - const len = length.toInt() - array.set(offset, len & 0xff) - array.set(offset + 1, (len >> 8) & 0xff) - array.set(offset + 2, (len >> 16) & 0xff) - array.set(offset + 3, (len >> 24) & 0xff) + const len = length.toInt(); + array.set(offset, len & 0xff); + array.set(offset + 1, (len >> 8) & 0xff); + array.set(offset + 2, (len >> 16) & 0xff); + array.set(offset + 3, (len >> 24) & 0xff); } static getHeaderLength(array: Uint8Array, offset: int32 = 0): int32 { return ( - (array.at(offset)!.toInt()) | - (array.at(((offset + 1) << 8))!.toInt()) | - (array.at((offset + 2) << 16)!.toInt()) | - (array.at((offset + 3) << 24))!.toInt()) + array.at(offset)!.toInt() | + array.at((offset + 1) << 8)!.toInt() | + array.at((offset + 2) << 16)!.toInt() | + array.at((offset + 3) << 24)!.toInt() + ); } // Produces array of bytes with encoded string headed by 4 bytes (little endian) size information: // [s0][s1][s2][s3] [c_0] ... [c_size-1] encode(input: string | undefined, addLength: boolean = true): Uint8Array { - let headerLen = addLength ? CustomTextEncoder.HeaderLen : 0 - let result: Uint8Array + let headerLen = addLength ? CustomTextEncoder.HeaderLen : 0; + let result: Uint8Array; if (!input) { - result = new Uint8Array(headerLen) + result = new Uint8Array(headerLen); } else if (this.encoder !== undefined) { - result = this.encoder!.encode('s'.repeat(headerLen) + input) + result = this.encoder!.encode('s'.repeat(headerLen) + input); } else { - let length = this.encodedLength(input) - result = new Uint8Array(length + headerLen) - this.encodeInto(input, result, headerLen) + let length = this.encodedLength(input); + result = new Uint8Array(length + headerLen); + this.encodeInto(input, result, headerLen); } if (addLength) { - this.addLength(result, 0, (result.length - headerLen).toInt()) + this.addLength(result, 0, (result.length - headerLen).toInt()); } - return result + return result; } // Produces encoded array of strings with size information. encodeArray(strings: Array): Uint8Array { - let totalBytes = CustomTextEncoder.HeaderLen - let lengths = new Int32Array(strings.length) + let totalBytes = CustomTextEncoder.HeaderLen; + let lengths = new Int32Array(strings.length); for (let i = 0; i < lengths.length; i++) { - let len = this.encodedLength(strings[i]) - lengths[i] = len - totalBytes += len + CustomTextEncoder.HeaderLen + let len = this.encodedLength(strings[i]); + lengths[i] = len; + totalBytes += len + CustomTextEncoder.HeaderLen; } - let array = new Uint8Array(totalBytes) - let position = 0 - this.addLength(array, position, lengths.length.toInt()) - position += CustomTextEncoder.HeaderLen + let array = new Uint8Array(totalBytes); + let position = 0; + this.addLength(array, position, lengths.length.toInt()); + position += CustomTextEncoder.HeaderLen; for (let i = 0; i < lengths.length; i++) { - this.addLength(array, position, lengths[i].toInt()) - position += CustomTextEncoder.HeaderLen - this.encodeInto(strings[i], array, position) - position += lengths[i] + this.addLength(array, position, lengths[i].toInt()); + position += CustomTextEncoder.HeaderLen; + this.encodeInto(strings[i], array, position); + position += lengths[i]; } - return array + return array; } encodeInto(input: string, result: Uint8Array, position: int32): Uint8Array { if (this.encoder !== undefined) { - this.encoder!.encodeInto(input, result.subarray(position, result.length)) - return result + this.encoder!.encodeInto(input, result.subarray(position, result.length)); + return result; } - let index = position + let index = position; for (let stringPosition = 0; stringPosition < input.length; stringPosition++) { - let cp = input.codePointAt(stringPosition)! + let cp = input.codePointAt(stringPosition)!; if (cp < 0x80) { - result[index++] = (cp | 0) + result[index++] = cp | 0; } else if (cp < 0x800) { - result[index++] = ((cp >> 6) | 0xc0) - result[index++] = ((cp & 0x3f) | 0x80) + result[index++] = (cp >> 6) | 0xc0; + result[index++] = (cp & 0x3f) | 0x80; } else if (cp < 0x10000) { - result[index++] = ((cp >> 12) | 0xe0) - result[index++] = (((cp >> 6) & 0x3f) | 0x80) - result[index++] = ((cp & 0x3f) | 0x80) + result[index++] = (cp >> 12) | 0xe0; + result[index++] = ((cp >> 6) & 0x3f) | 0x80; + result[index++] = (cp & 0x3f) | 0x80; } else { - result[index++] = ((cp >> 18) | 0xf0) - result[index++] = (((cp >> 12) & 0x3f) | 0x80) - result[index++] = (((cp >> 6) & 0x3f) | 0x80) - result[index++] = ((cp & 0x3f) | 0x80) - stringPosition++ + result[index++] = (cp >> 18) | 0xf0; + result[index++] = ((cp >> 12) & 0x3f) | 0x80; + result[index++] = ((cp >> 6) & 0x3f) | 0x80; + result[index++] = (cp & 0x3f) | 0x80; + stringPosition++; } } - result[index] = 0 - return result + result[index] = 0; + return result; } } export class CustomTextDecoder { - static cpArrayMaxSize = 128 - constructor(decoder: SystemTextDecoder|undefined = undefined) { - this.decoder = decoder + static cpArrayMaxSize = 128; + constructor(decoder: SystemTextDecoder | undefined = undefined) { + this.decoder = decoder; } - private readonly decoder: SystemTextDecoder|undefined + private readonly decoder: SystemTextDecoder | undefined; decode(input: Uint8Array): string { if (this.decoder !== undefined) { - return this.decoder!.decode(input, undefined) + return this.decoder!.decode(input, undefined); } - const cpSize = Math.min(CustomTextDecoder.cpArrayMaxSize, input.length) - let codePoints = new Int32Array(cpSize) + const cpSize = Math.min(CustomTextDecoder.cpArrayMaxSize, input.length); + let codePoints = new Int32Array(cpSize); let cpIndex = 0; - let index = 0 - let result = "" + let index = 0; + let result = ''; while (index < input.length) { - let elem = input[index].toByte() - let lead = elem & 0xff - let count = 0 - let value = 0 + let elem = input[index].toByte(); + let lead = elem & 0xff; + let count = 0; + let value = 0; if (lead < 0x80) { - count = 1 - value = elem - } else if ((lead >> 5) == 0x6) { - value = (((elem << 6) & 0x7ff) + (input[index + 1] & 0x3f)).toInt() - count = 2 - } else if ((lead >> 4) == 0xe) { - value = (((elem << 12) & 0xffff) + ((input[index + 1] << 6) & 0xfff) + - (input[index + 2] & 0x3f)).toInt() - count = 3 - } else if ((lead >> 3) == 0x1e) { - value = (((elem << 18) & 0x1fffff) + ((input[index + 1] << 12) & 0x3ffff) + - ((input[index + 2] << 6) & 0xfff) + (input[index + 3] & 0x3f)).toInt() - count = 4 + count = 1; + value = elem; + } else if (lead >> 5 == 0x6) { + value = (((elem << 6) & 0x7ff) + (input[index + 1] & 0x3f)).toInt(); + count = 2; + } else if (lead >> 4 == 0xe) { + value = ( + ((elem << 12) & 0xffff) + + ((input[index + 1] << 6) & 0xfff) + + (input[index + 2] & 0x3f) + ).toInt(); + count = 3; + } else if (lead >> 3 == 0x1e) { + value = ( + ((elem << 18) & 0x1fffff) + + ((input[index + 1] << 12) & 0x3ffff) + + ((input[index + 2] << 6) & 0xfff) + + (input[index + 3] & 0x3f) + ).toInt(); + count = 4; } - codePoints[cpIndex++] = value + codePoints[cpIndex++] = value; if (cpIndex == cpSize) { - cpIndex = 0 + cpIndex = 0; //result += String.fromCodePoint(...codePoints) - result += String.fromCodePoint(...Array_from_int32(codePoints)) + result += String.fromCodePoint(...Array_from_int32(codePoints)); } - index += count + index += count; } if (cpIndex > 0) { - result += String.fromCodePoint(...Array_from_int32(codePoints.slice(0, cpIndex))) + result += String.fromCodePoint(...Array_from_int32(codePoints.slice(0, cpIndex))); } - return result + return result; } } diff --git a/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/arkts/ts-reflection.ts b/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/arkts/ts-reflection.ts index f9f80694482..90744e2732f 100644 --- a/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/arkts/ts-reflection.ts +++ b/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/arkts/ts-reflection.ts @@ -14,25 +14,25 @@ */ export function className(object?: Object): string { - return object ? (Type.of(object) as ClassType).getName() : "null" + return object ? (Type.of(object) as ClassType).getName() : 'null'; } export function isFunction(object?: Object): boolean { - return Type.of(object) instanceof FunctionType + return Type.of(object) instanceof FunctionType; } // Improve: This is an very ad hoc function, // but I could not find in ArkTS stdlib enough functionality // for a more generic way. -export function functionOverValue(value: Value|(()=>Value)): boolean { - return Type.of(value) instanceof FunctionType +export function functionOverValue(value: Value | (() => Value)): boolean { + return Type.of(value) instanceof FunctionType; } // Somehow es2panda only allows === on reference types. export function refEqual(a: Value, b: Value): boolean { - return a == b + return a == b; } export function isNotPrimitive(value: Object): boolean { - return !Type.of(value).isPrimitive() + return !Type.of(value).isPrimitive(); } diff --git a/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/arkts/types.ts b/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/arkts/types.ts index b0edbd27a4e..8bb86313d5a 100644 --- a/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/arkts/types.ts +++ b/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/arkts/types.ts @@ -13,13 +13,13 @@ * limitations under the License. */ -export type uint8 = byte -export type int8 = byte -export type unt16 = short -export type int16 = short -export type int32 = int -export type uint32 = int -export type int64 = long -export type uint64 = long -export type float32 = float -export type float64 = double \ No newline at end of file +export type uint8 = byte; +export type int8 = byte; +export type unt16 = short; +export type int16 = short; +export type int32 = int; +export type uint32 = int; +export type int64 = long; +export type uint64 = long; +export type float32 = float; +export type float64 = double; diff --git a/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/arkts/utils.ts b/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/arkts/utils.ts index 75c8e744f68..0dd2550bbbe 100644 --- a/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/arkts/utils.ts +++ b/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/arkts/utils.ts @@ -13,39 +13,28 @@ * limitations under the License. */ -export function errorAsString(error: Error): string { - const stack = error.stack - return stack - ? error.toString() + '\n' + stack - : error.toString() +export function errorAsString(error: Any): string { + if (error instanceof Error) { + const stack = error.stack; + return stack ? error.toString() + '\n' + stack : error.toString(); + } + return JSON.stringify(error); } export function unsafeCast(value: Object): T { - return value as T + return value as T; } export function scheduleCoroutine(): void { - Coroutine.Schedule() + Coroutine.Schedule(); } export function memoryStats(): string { - return `used ${GC.getUsedHeapSize()} free ${GC.getFreeHeapSize()}` + return `used ${GC.getUsedHeapSize()} free ${GC.getFreeHeapSize()}`; } -export function launchJob(job: () => void): Promise { - throw new Error("unsupported yet: return launch job()") +export function launchJob(task: () => void): Promise { + return taskpool.execute(task); } -export class CoroutineLocalValue { - private map = new containers.ConcurrentHashMap - get(): T | undefined { - return this.map.get(CoroutineExtras.getWorkerId()) - } - set(value: T | undefined) { - if (value) { - this.map.set(CoroutineExtras.getWorkerId(), value) - } else { - this.map.delete(CoroutineExtras.getWorkerId()) - } - } -} +export type WorkerLocalValue = WorkerLocal; diff --git a/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/index.ts b/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/index.ts index e4a0a9c4ddf..83ad0450ee4 100644 --- a/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/index.ts +++ b/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/index.ts @@ -20,14 +20,6 @@ export { Array_from_number, AtomicRef, asFloat64, - asString, - float64To32, - float64ToInt, - float64ToLong, - float32To64, - float32FromBits, - int32BitsFromFloat, - charToInt, Thunk, finalizerRegister, finalizerUnregister, @@ -36,8 +28,10 @@ export { Observed, Observable, ObservableHandler, - TrackableProps, ObservableClass, + TrackableProperties, + trackableProperties, + ClassMetadata, observableProxy, observableProxyArray, propDeepCopy, @@ -53,16 +47,28 @@ export { int8, int16, int32, + int32toFloat32, + int32toFloat64, + int32to64, uint32, int64, + int64toFloat32, + int64toFloat64, + int64to32, uint64, float32, + float32to64, + float32toInt32, + float32toInt64, float64, + float64to32, + float64toInt32, + float64toInt64, int8Array, errorAsString, unsafeCast, - CoroutineLocalValue, + WorkerLocalValue, scheduleCoroutine, memoryStats, - launchJob -} from "#platform" + launchJob, +} from '#platform'; diff --git a/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/ohos/index.ts b/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/ohos/index.ts index 3edbe8fe57e..4bf6e7fc8d9 100644 --- a/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/ohos/index.ts +++ b/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/ohos/index.ts @@ -20,14 +20,6 @@ export { Array_from_number, AtomicRef, asFloat64, - asString, - float64To32, - float64ToInt, - float64ToLong, - float32To64, - float32FromBits, - int32BitsFromFloat, - charToInt, Thunk, finalizerRegister, finalizerUnregister, @@ -35,8 +27,10 @@ export { Observable, ObservableHandler, observableProxy, - TrackableProps, ObservableClass, + TrackableProperties, + trackableProperties, + ClassMetadata, observableProxyArray, propDeepCopy, lcClassName, @@ -51,21 +45,30 @@ export { int8, int16, int32, + int32toFloat32, + int32toFloat64, + int32to64, uint32, int64, + int64toFloat32, + int64toFloat64, + int64to32, uint64, float32, + float32to64, + float32toInt32, + float32toInt64, float64, + float64to32, + float64toInt32, + float64toInt64, int8Array, errorAsString, unsafeCast, - CoroutineLocalValue, + WorkerLocalValue, scheduleCoroutine, memoryStats, - launchJob -} from "../typescript" + launchJob, +} from '../typescript'; -export { - timeNow, - numberToFixed, -} from "./performance" +export { timeNow, numberToFixed } from './performance'; diff --git a/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/ohos/performance.ts b/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/ohos/performance.ts index 8926ff8519d..2b7c1c4dbe1 100644 --- a/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/ohos/performance.ts +++ b/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/ohos/performance.ts @@ -18,7 +18,7 @@ * January 1, 1970 Universal Coordinated Time (UTC). */ export function timeNow(): number { - return Date.now() + return Date.now(); } /** @@ -26,5 +26,5 @@ export function timeNow(): number { * @returns a string representing a number in fixed-point notation */ export function numberToFixed(value: number, fractionDigits: number): string { - return new Number(value).toFixed(fractionDigits) + return new Number(value).toFixed(fractionDigits); } diff --git a/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/typescript/Types.d.ts b/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/typescript/Types.d.ts index 4d42fbbf5de..a4b3ef111fc 100644 --- a/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/typescript/Types.d.ts +++ b/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/typescript/Types.d.ts @@ -13,7 +13,7 @@ * limitations under the License. */ -type int = number -type long = number -type float = number -type double = number +type int = number; +type long = number; +type float = number; +type double = number; diff --git a/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/typescript/array.ts b/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/typescript/array.ts index 156ea36f2e3..1d87e05b092 100644 --- a/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/typescript/array.ts +++ b/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/typescript/array.ts @@ -13,24 +13,24 @@ * limitations under the License. */ -import { float64, int32, int8 } from "./types" +import { float64, int32, int8 } from './types'; export function asArray(value: T[]): Array { - return value + return value; } export function Array_from_set(set: Set): Array { - return Array.from(set) + return Array.from(set); } export function Array_from_int32(data: Int32Array): int32[] { - return Array.from(data) + return Array.from(data); } export function Array_from_number(data: float64[]): Array { - return data + return data; } export function int8Array(size: int32): int8[] { - return [] + return []; } diff --git a/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/typescript/atomic.ts b/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/typescript/atomic.ts index b3b12b85fa0..6164e189ce7 100644 --- a/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/typescript/atomic.ts +++ b/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/typescript/atomic.ts @@ -17,14 +17,14 @@ * A reference that may be updated atomically. */ export class AtomicRef { - value: Value + value: Value; /** * Creates a new reference object with the given initial value. * @param value - the new value */ constructor(value: Value) { - this.value = value + this.value = value; } /** @@ -33,8 +33,8 @@ export class AtomicRef { * @returns the previous value */ getAndSet(value: Value): Value { - const result = this.value - this.value = value - return result + const result = this.value; + this.value = value; + return result; } } diff --git a/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/typescript/finalization.ts b/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/typescript/finalization.ts index c7d5e05e846..34d7e8a00ff 100644 --- a/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/typescript/finalization.ts +++ b/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/typescript/finalization.ts @@ -14,7 +14,7 @@ */ export interface Thunk { - clean(): void + clean(): void; } interface FinalizationRegistry { @@ -24,19 +24,19 @@ interface FinalizationRegistry { interface FinalizationRegistryConstructor { readonly prototype: FinalizationRegistry; - new(callback: (value: any) => void): FinalizationRegistry; + new (callback: (value: any) => void): FinalizationRegistry; } -declare const FinalizationRegistry: FinalizationRegistryConstructor +declare const FinalizationRegistry: FinalizationRegistryConstructor; const registry = new FinalizationRegistry((thunk: Thunk) => { - thunk.clean() -}) + thunk.clean(); +}); export function finalizerRegister(target: object, thunk: object) { - registry.register(target, thunk) + registry.register(target, thunk); } export function finalizerUnregister(target: object) { - registry.unregister(target) + registry.unregister(target); } diff --git a/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/typescript/index.ts b/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/typescript/index.ts index e5ca0c3be7c..71ecbdad7c9 100644 --- a/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/typescript/index.ts +++ b/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/typescript/index.ts @@ -13,15 +13,15 @@ * limitations under the License. */ -export * from "./array" -export * from "./atomic" -export * from "./primitive" -export * from "./finalization" -export * from "./observable" -export * from "./performance" -export * from "./prop-deep-copy" -export * from "./reflection" -export * from "./strings" -export * from "./ts-reflection" -export * from "./types" -export * from "./utils" +export * from './array'; +export * from './atomic'; +export * from './primitive'; +export * from './finalization'; +export * from './observable'; +export * from './performance'; +export * from './prop-deep-copy'; +export * from './reflection'; +export * from './strings'; +export * from './ts-reflection'; +export * from './types'; +export * from './utils'; diff --git a/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/typescript/observable.ts b/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/typescript/observable.ts index 8a3fd4d9166..affbf61f10f 100644 --- a/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/typescript/observable.ts +++ b/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/typescript/observable.ts @@ -13,103 +13,105 @@ * limitations under the License. */ -const OBSERVABLE_TARGET = "__proxy_observable_target__" +const OBSERVABLE_TARGET = '__proxy_observable_target__'; export function getObservableTarget(proxy: Object): Object { - return getPropertyValue(OBSERVABLE_TARGET, proxy) ?? proxy + return getPropertyValue(OBSERVABLE_TARGET, proxy) ?? proxy; } function getPropertyValue(name: string, object: any): any { - return object[name] + return object[name]; } /** * Data class decorator that makes all child fields trackable. */ export function Observed(constructorFunction: Function) { - constructorFunction.prototype[OBSERVED] = true + constructorFunction.prototype[OBSERVED] = true; } -const OBSERVED = "__ObservedByArkUI__" +const OBSERVED = '__ObservedByArkUI__'; function isObserved(value: any): boolean { - return value[OBSERVED] === true + return value[OBSERVED] === true; } /** @internal */ export interface Observable { /** It is called when the observable value is accessed. */ - onAccess(): void + onAccess(): void; /** It is called when the observable value is modified. */ - onModify(): void + onModify(): void; } /** @internal */ export class ObservableHandler implements Observable { - private static handlers: WeakMap | undefined = undefined + private static handlers: WeakMap | undefined = undefined; - private parents = new Set() - private children = new Map() + private parents = new Set(); + private children = new Map(); - private readonly observables = new Set() - private _modified = false + private readonly observables = new Set(); + private _modified = false; - readonly observed: boolean + readonly observed: boolean; constructor(parent?: ObservableHandler, observed: boolean = false) { - this.observed = observed - if (parent) this.addParent(parent) + this.observed = observed; + if (parent) this.addParent(parent); } onAccess(): void { if (this.observables.size > 0) { - const it = this.observables.keys() + const it = this.observables.keys(); while (true) { - const result = it.next() - if (result.done) break - result.value?.onAccess() + const result = it.next(); + if (result.done) break; + result.value?.onAccess(); } } } onModify(): void { - const set = new Set() - this.collect(true, set) + const set = new Set(); + this.collect(true, set); set.forEach((handler: ObservableHandler) => { - handler._modified = true + handler._modified = true; if (handler.observables.size > 0) { - const it = handler.observables.keys() + const it = handler.observables.keys(); while (true) { - const result = it.next() - if (result.done) break - result.value?.onModify() + const result = it.next(); + if (result.done) break; + result.value?.onModify(); } } - }) + }); } static dropModified(value: Value): boolean { - const handler = ObservableHandler.findIfObject(value) - if (handler === undefined) return false - const result = handler._modified - handler._modified = false - return result + const handler = ObservableHandler.findIfObject(value); + if (handler === undefined) return false; + const result = handler._modified; + handler._modified = false; + return result; } /** Adds the specified `observable` to the handler corresponding to the given `value`. */ static attach(value: Value, observable: Observable): void { - const handler = ObservableHandler.findIfObject(value) - if (handler) handler.observables.add(observable) + const handler = ObservableHandler.findIfObject(value); + if (handler) handler.observables.add(observable); } /** Deletes the specified `observable` from the handler corresponding to the given `value`. */ static detach(value: Value, observable: Observable): void { - const handler = ObservableHandler.findIfObject(value) - if (handler) handler.observables.delete(observable) + const handler = ObservableHandler.findIfObject(value); + if (handler) handler.observables.delete(observable); } /** @returns the handler corresponding to the given `value` if it was installed */ private static findIfObject(value: Value): ObservableHandler | undefined { - const handlers = ObservableHandler.handlers - return handlers !== undefined && value instanceof Object ? handlers.get(getObservableTarget(value as Object)) : undefined + const handlers = ObservableHandler.handlers; + return handlers !== undefined && value instanceof Object + ? handlers.get(getObservableTarget(value as Object)) + : undefined; } /** @@ -117,8 +119,8 @@ export class ObservableHandler implements Observable { * @returns an observable handler or `undefined` if it is not installed */ static find(value: Object): ObservableHandler | undefined { - const handlers = ObservableHandler.handlers - return handlers ? handlers.get(getObservableTarget(value)) : undefined + const handlers = ObservableHandler.handlers; + return handlers ? handlers.get(getObservableTarget(value)) : undefined; } /** @@ -127,304 +129,317 @@ export class ObservableHandler implements Observable { * @throws an error if observable handler cannot be installed */ static installOn(value: Object, observable?: ObservableHandler): void { - let handlers = ObservableHandler.handlers + let handlers = ObservableHandler.handlers; if (handlers === undefined) { - handlers = new WeakMap() - ObservableHandler.handlers = handlers + handlers = new WeakMap(); + ObservableHandler.handlers = handlers; } - observable - ? handlers.set(getObservableTarget(value), observable) - : handlers.delete(getObservableTarget(value)) + observable ? handlers.set(getObservableTarget(value), observable) : handlers.delete(getObservableTarget(value)); } addParent(parent: ObservableHandler) { - const count = parent.children.get(this) ?? 0 - parent.children.set(this, count + 1) - this.parents.add(parent) + const count = parent.children.get(this) ?? 0; + parent.children.set(this, count + 1); + this.parents.add(parent); } removeParent(parent: ObservableHandler) { - const count = parent.children.get(this) ?? 0 + const count = parent.children.get(this) ?? 0; if (count > 1) { - parent.children.set(this, count - 1) - } - else if (count == 1) { - parent.children.delete(this) - this.parents.delete(parent) + parent.children.set(this, count - 1); + } else if (count == 1) { + parent.children.delete(this); + this.parents.delete(parent); } } removeChild(value: Value) { - const child = ObservableHandler.findIfObject(value) - if (child) child.removeParent(this) + const child = ObservableHandler.findIfObject(value); + if (child) child.removeParent(this); } private collect(all: boolean, guards = new Set()) { - if (guards.has(this)) return guards // already collected - guards.add(this) // handler is already guarded - this.parents.forEach(handler => handler.collect(all, guards)) - if (all) this.children.forEach((_count, handler) => handler.collect(all, guards)) - return guards + if (guards.has(this)) return guards; // already collected + guards.add(this); // handler is already guarded + this.parents.forEach((handler) => handler.collect(all, guards)); + if (all) this.children.forEach((_count, handler) => handler.collect(all, guards)); + return guards; } static contains(observable: ObservableHandler, guards?: Set) { - if (observable.observed) return true - if (guards === undefined) guards = new Set() // create if needed - else if (guards.has(observable)) return false // already checked - guards.add(observable) // handler is already guarded + if (observable.observed) return true; + if (guards === undefined) + guards = new Set(); // create if needed + else if (guards.has(observable)) return false; // already checked + guards.add(observable); // handler is already guarded for (const it of observable.parents.keys()) { - if (ObservableHandler.contains(it, guards)) return true + if (ObservableHandler.contains(it, guards)) return true; } - return false + return false; } } /** @internal */ export function observableProxyArray(...value: Value[]): Array { - return observableProxy(value) + return observableProxy(value); } /** @internal */ -export function observableProxy(value: Value, parent?: ObservableHandler, observed?: boolean, strict = true): Value { - if (value instanceof ObservableHandler) return value // do not proxy a marker itself - if (value === null || !(value instanceof Object)) return value // only non-null object can be observable - const observable = ObservableHandler.find(value) +export function observableProxy( + value: Value, + parent?: ObservableHandler, + observed?: boolean, + strict = true +): Value { + if (value instanceof ObservableHandler) return value; // do not proxy a marker itself + if (value === null || !(value instanceof Object)) return value; // only non-null object can be observable + const observable = ObservableHandler.find(value); if (observable) { if (parent) { - if (strict) observable.addParent(parent) - if (observed === undefined) observed = ObservableHandler.contains(parent) + if (strict) observable.addParent(parent); + if (observed === undefined) observed = ObservableHandler.contains(parent); } if (observed) { if (Array.isArray(value)) { for (let index = 0; index < value.length; index++) { - value[index] = observableProxy(value[index], observable, observed, false) + value[index] = observableProxy(value[index], observable, observed, false); } } else { - proxyFields(value, false, observable) + proxyFields(value, false, observable); } } - return value + return value; } if (Array.isArray(value)) { - const handler = new ObservableHandler(parent) - const array = proxyChildrenOnly(value, handler, observed) - copyWithinObservable(array) - fillObservable(array) - popObservable(array) - pushObservable(array) - reverseObservable(array) - shiftObservable(array) - sortObservable(array) - spliceObservable(array) - unshiftObservable(array) - return proxyObject(array, handler) + const handler = new ObservableHandler(parent); + const array = proxyChildrenOnly(value, handler, observed); + copyWithinObservable(array); + fillObservable(array); + popObservable(array); + pushObservable(array); + reverseObservable(array); + shiftObservable(array); + sortObservable(array); + spliceObservable(array); + unshiftObservable(array); + return proxyObject(array, handler); } if (value instanceof Date) { - const valueAsAny = (value as any) - const handler = new ObservableHandler(parent) + const valueAsAny = value as any; + const handler = new ObservableHandler(parent); const setMethods = new Set([ - "setFullYear", "setMonth", "setDate", "setHours", "setMinutes", "setSeconds", - "setMilliseconds", "setTime", "setUTCFullYear", "setUTCMonth", "setUTCDate", - "setUTCHours", "setUTCMinutes", "setUTCSeconds", "setUTCMilliseconds" - ]) + 'setFullYear', + 'setMonth', + 'setDate', + 'setHours', + 'setMinutes', + 'setSeconds', + 'setMilliseconds', + 'setTime', + 'setUTCFullYear', + 'setUTCMonth', + 'setUTCDate', + 'setUTCHours', + 'setUTCMinutes', + 'setUTCSeconds', + 'setUTCMilliseconds', + ]); setMethods.forEach((method: string) => { - const originalMethod = method + 'Original' + const originalMethod = method + 'Original'; if (valueAsAny[originalMethod] !== undefined) { - return + return; } - valueAsAny[originalMethod] = valueAsAny[method] + valueAsAny[originalMethod] = valueAsAny[method]; valueAsAny[method] = function (...args: any[]) { - ObservableHandler.find(this)?.onModify() - return this[originalMethod](...args) - } - }) - return proxyObject(value, handler) + ObservableHandler.find(this)?.onModify(); + return this[originalMethod](...args); + }; + }); + return proxyObject(value, handler); } if (value instanceof Map) { - const handler = new ObservableHandler(parent) - const data = proxyMapValues(value, handler, observed) - setObservable(data) - deleteObservable(data) - clearObservable(data) - return proxyMapOrSet(data, handler) + const handler = new ObservableHandler(parent); + const data = proxyMapValues(value, handler, observed); + setObservable(data); + deleteObservable(data); + clearObservable(data); + return proxyMapOrSet(data, handler); } if (value instanceof Set) { - const handler = new ObservableHandler(parent) - const data = proxySetValues(value, handler, observed) - addObservable(data) - deleteObservable(data) - clearObservable(data) - return proxyMapOrSet(data, handler) - } - const handler = new ObservableHandler(parent, isObserved(value)) - if (handler.observed || observed) proxyFields(value, true, handler) - return proxyObject(value, handler) + const handler = new ObservableHandler(parent); + const data = proxySetValues(value, handler, observed); + addObservable(data); + deleteObservable(data); + clearObservable(data); + return proxyMapOrSet(data, handler); + } + const handler = new ObservableHandler(parent, isObserved(value)); + if (handler.observed || observed) proxyFields(value, true, handler); + return proxyObject(value, handler); } function proxyObject(value: any, observable: ObservableHandler) { - ObservableHandler.installOn(value, observable) + ObservableHandler.installOn(value, observable); return new Proxy(value, { get(target, property, receiver) { - if (property == OBSERVABLE_TARGET) return target - const value: any = Reflect.get(target, property, receiver) - ObservableHandler.find(target)?.onAccess() - return typeof value == "function" - ? value.bind(target) - : value + if (property == OBSERVABLE_TARGET) return target; + const value: any = Reflect.get(target, property, receiver); + ObservableHandler.find(target)?.onAccess(); + return typeof value == 'function' ? value.bind(target) : value; }, set(target, property, value, receiver) { - const old = Reflect.get(target, property, receiver) - if (value === old) return true - const observable = ObservableHandler.find(target) + const old = Reflect.get(target, property, receiver); + if (value === old) return true; + const observable = ObservableHandler.find(target); if (observable) { - observable.onModify() - observable.removeChild(old) - const observed = ObservableHandler.contains(observable) + observable.onModify(); + observable.removeChild(old); + const observed = ObservableHandler.contains(observable); if (observed || Array.isArray(target)) { - value = observableProxy(value, observable, observed) + value = observableProxy(value, observable, observed); } } - return Reflect.set(target, property, value, receiver) + return Reflect.set(target, property, value, receiver); }, deleteProperty(target, property) { - ObservableHandler.find(target)?.onModify() - delete target[property] - return true + ObservableHandler.find(target)?.onModify(); + delete target[property]; + return true; }, - }) + }); } function proxyFields(value: any, strict: boolean, parent?: ObservableHandler) { for (const name of Object.getOwnPropertyNames(value)) { - const descriptor = Object.getOwnPropertyDescriptor(value, name) - if (descriptor?.writable) value[name] = observableProxy(value[name], parent, true, strict) + const descriptor = Object.getOwnPropertyDescriptor(value, name); + if (descriptor?.writable) value[name] = observableProxy(value[name], parent, true, strict); } } function proxyChildrenOnly(array: any[], parent: ObservableHandler, observed?: boolean): any[] { - if (observed === undefined) observed = ObservableHandler.contains(parent) - return array.map(it => observableProxy(it, parent, observed)) + if (observed === undefined) observed = ObservableHandler.contains(parent); + return array.map((it) => observableProxy(it, parent, observed)); } function copyWithinObservable(array: any) { if (array.copyWithinOriginal === undefined) { - array.copyWithinOriginal = array.copyWithin + array.copyWithinOriginal = array.copyWithin; array.copyWithin = function (this, target: number, start: number, end?: number) { - const observable = ObservableHandler.find(this) - observable?.onModify() - return this.copyWithinOriginal(target, start, end) - } + const observable = ObservableHandler.find(this); + observable?.onModify(); + return this.copyWithinOriginal(target, start, end); + }; } } function fillObservable(array: any) { if (array.fillOriginal === undefined) { - array.fillOriginal = array.fill + array.fillOriginal = array.fill; array.fill = function (this, value: any, start?: number, end?: number) { - const observable = ObservableHandler.find(this) - observable?.onModify() - if (observable) value = observableProxy(value, observable) - return this.fillOriginal(value, start, end) - } + const observable = ObservableHandler.find(this); + observable?.onModify(); + if (observable) value = observableProxy(value, observable); + return this.fillOriginal(value, start, end); + }; } } function popObservable(array: any) { if (array.popOriginal === undefined) { - array.popOriginal = array.pop + array.popOriginal = array.pop; array.pop = function (...args: any[]) { - const observable = ObservableHandler.find(this) - observable?.onModify() - const result = this.popOriginal(...args) - if (observable) observable.removeChild(result) - return result - } + const observable = ObservableHandler.find(this); + observable?.onModify(); + const result = this.popOriginal(...args); + if (observable) observable.removeChild(result); + return result; + }; } } function pushObservable(array: any) { if (array.pushOriginal === undefined) { - array.pushOriginal = array.push + array.pushOriginal = array.push; array.push = function (this, ...args: any[]) { - const observable = ObservableHandler.find(this) - observable?.onModify() - if (observable) args = proxyChildrenOnly(args, observable) - return this.pushOriginal(...args) - } + const observable = ObservableHandler.find(this); + observable?.onModify(); + if (observable) args = proxyChildrenOnly(args, observable); + return this.pushOriginal(...args); + }; } } function reverseObservable(array: any) { if (array.reverseOriginal === undefined) { - array.reverseOriginal = array.reverse + array.reverseOriginal = array.reverse; array.reverse = function (this) { - const observable = ObservableHandler.find(this) - observable?.onModify() - return this.reverseOriginal() - } + const observable = ObservableHandler.find(this); + observable?.onModify(); + return this.reverseOriginal(); + }; } } function shiftObservable(array: any) { if (array.shiftOriginal === undefined) { - array.shiftOriginal = array.shift + array.shiftOriginal = array.shift; array.shift = function (this, ...args: any[]) { - const observable = ObservableHandler.find(this) - observable?.onModify() - const result = this.shiftOriginal(...args) - if (observable) observable.removeChild(result) - return result - } + const observable = ObservableHandler.find(this); + observable?.onModify(); + const result = this.shiftOriginal(...args); + if (observable) observable.removeChild(result); + return result; + }; } } function sortObservable(array: any) { if (array.sortOriginal === undefined) { - array.sortOriginal = array.sort + array.sortOriginal = array.sort; array.sort = function (this, compareFn?: (a: any, b: any) => number) { - const observable = ObservableHandler.find(this) - observable?.onModify() - return this.sortOriginal(compareFn) - } + const observable = ObservableHandler.find(this); + observable?.onModify(); + return this.sortOriginal(compareFn); + }; } } function spliceObservable(array: any) { if (array.spliceOriginal === undefined) { - array.spliceOriginal = array.splice + array.spliceOriginal = array.splice; array.splice = function (this, start: number, deleteCount: number, ...items: any[]) { - const observable = ObservableHandler.find(this) - observable?.onModify() - if (observable) items = proxyChildrenOnly(items, observable) - if (deleteCount === undefined) deleteCount = array.length - const result = this.spliceOriginal(start, deleteCount, ...items) + const observable = ObservableHandler.find(this); + observable?.onModify(); + if (observable) items = proxyChildrenOnly(items, observable); + if (deleteCount === undefined) deleteCount = array.length; + const result = this.spliceOriginal(start, deleteCount, ...items); if (observable && Array.isArray(result)) { - result.forEach(it => observable.removeChild(it)) + result.forEach((it) => observable.removeChild(it)); } - return result - } + return result; + }; } } function unshiftObservable(array: any) { if (array.unshiftOriginal === undefined) { - array.unshiftOriginal = array.unshift + array.unshiftOriginal = array.unshift; array.unshift = function (this, ...items: any[]) { - const observable = ObservableHandler.find(this) - observable?.onModify() - if (observable) items = proxyChildrenOnly(items, observable) - return this.unshiftOriginal(...items) - } + const observable = ObservableHandler.find(this); + observable?.onModify(); + if (observable) items = proxyChildrenOnly(items, observable); + return this.unshiftOriginal(...items); + }; } } function proxyMapValues(data: Map, parent: ObservableHandler, observed?: boolean): Map { - if (observed === undefined) observed = ObservableHandler.contains(parent) - const result = new Map() + if (observed === undefined) observed = ObservableHandler.contains(parent); + const result = new Map(); for (const [key, value] of data.entries()) { - result.set(key, observableProxy(value, parent, observed)) + result.set(key, observableProxy(value, parent, observed)); } - return result + return result; } function proxySetValues(data: Set, parent: ObservableHandler, observed?: boolean): Set { @@ -438,106 +453,191 @@ function proxySetValues(data: Set, parent: ObservableHandler, observed?: b } return result */ - return data + return data; } function proxyMapOrSet(value: any, observable: ObservableHandler) { - ObservableHandler.installOn(value, observable) + ObservableHandler.installOn(value, observable); return new Proxy(value, { get(target, property, receiver) { - if (property == OBSERVABLE_TARGET) return target + if (property == OBSERVABLE_TARGET) return target; if (property == 'size') { - ObservableHandler.find(target)?.onAccess() - return target.size + ObservableHandler.find(target)?.onAccess(); + return target.size; } - const value: any = Reflect.get(target, property, receiver) - ObservableHandler.find(target)?.onAccess() - return typeof value == "function" - ? value.bind(target) - : value + const value: any = Reflect.get(target, property, receiver); + ObservableHandler.find(target)?.onAccess(); + return typeof value == 'function' ? value.bind(target) : value; }, - }) + }); } function addObservable(data: any) { if (data.addOriginal === undefined) { - data.addOriginal = data.add + data.addOriginal = data.add; data.add = function (this, value: any) { - const observable = ObservableHandler.find(this) + const observable = ObservableHandler.find(this); if (observable && !this.has(value)) { - observable.onModify() + observable.onModify(); // Improve: check if necessary to replace items of the set with observed objects as // for complex objects add() function won't find original object inside the set of proxies // value = observableProxy(value, observable) } - return this.addOriginal(value) - } + return this.addOriginal(value); + }; } } function setObservable(data: any) { if (data.setOriginal === undefined) { - data.setOriginal = data.set + data.setOriginal = data.set; data.set = function (this, key: any, value: any) { - const observable = ObservableHandler.find(this) + const observable = ObservableHandler.find(this); if (observable) { - observable.onModify() - observable.removeChild(this.get(key)) - value = observableProxy(value, observable) + observable.onModify(); + observable.removeChild(this.get(key)); + value = observableProxy(value, observable); } - return this.setOriginal(key, value) - } + return this.setOriginal(key, value); + }; } } function deleteObservable(data: any) { if (data.deleteOriginal === undefined) { - data.deleteOriginal = data.delete + data.deleteOriginal = data.delete; data.delete = function (this, key: any) { - const observable = ObservableHandler.find(this) + const observable = ObservableHandler.find(this); if (observable) { - observable.onModify() + observable.onModify(); if (this instanceof Map) { - observable.removeChild(this.get(key)) + observable.removeChild(this.get(key)); } else if (this instanceof Set) { - observable.removeChild(key) + observable.removeChild(key); } } - return this.deleteOriginal(key) - } + return this.deleteOriginal(key); + }; } } function clearObservable(data: any) { if (data.clearOriginal === undefined) { - data.clearOriginal = data.clear + data.clearOriginal = data.clear; data.clear = function (this) { - const observable = ObservableHandler.find(this) + const observable = ObservableHandler.find(this); if (observable) { - observable.onModify() - Array.from(this.values()).forEach(it => observable.removeChild(it)) + observable.onModify(); + Array.from(this.values()).forEach((it) => observable.removeChild(it)); } - return this.clearOriginal() - } + return this.clearOriginal(); + }; } } -/** - * Interface for getting the observed properties of a class - */ -export interface TrackableProps { - /** - * Retrieves the set of property names that are being tracked for changes using `@Track` decorator - */ - trackedProperties(): ReadonlySet +function getClassMetadata(value: any): ClassMetadata | undefined { + if (value !== undefined && typeof value.getClassMetadata === 'function') { + return (value as ObservableClass).getClassMetadata(); + } + return undefined; } /** * Interface for getting the observability status of a class */ export interface ObservableClass { + getClassMetadata(): ClassMetadata | undefined; +} + +/** + * Interface for checking the observed properties of a class + */ +export interface TrackableProperties { + isTrackable(propertyName: string): boolean; +} + +/** + * If value is a class, then returns a list of trackable properties + * @param value + */ +export function trackableProperties(value: T): TrackableProperties | undefined { + return getClassMetadata(value); +} + +export class ClassMetadata implements TrackableProperties { + private readonly parent: ClassMetadata | undefined; + private readonly markAsObservedV1: boolean; + private readonly markAsObservedV2: boolean; + private static readonly metadataPropName = '__classMetadata'; + /** - * Indicates whether the class is decorated with `@Observed`. + * Class property names marked with the @Track or @Trace decorator + * @private */ - isObserved(): boolean + private readonly trackableProperties: ReadonlySet | undefined; + + /** + * Contains fields marked with the @Type decorator. + * The key of the map is the property name and the value is the typename of the corresponding field. + * @private + */ + private readonly typedProperties: ReadonlyMap | undefined; + + constructor( + parent: ClassMetadata | undefined, + markAsObservedV1: boolean, + markAsObservedV2: boolean, + trackable: string[] | undefined, + typed: [string, string][] | undefined + ) { + this.parent = parent; + this.markAsObservedV1 = markAsObservedV1; + this.markAsObservedV2 = markAsObservedV2; + if (trackable) { + this.trackableProperties = new Set(trackable); + } + if (typed) { + this.typedProperties = new Map(typed); + } + } + + isObservedV1(value: Object): boolean { + return this.markAsObservedV1; + } + + isObservedV2(value: Object): boolean { + return this.markAsObservedV2; + } + + isTrackable(propertyName: string): boolean { + return (this.trackableProperties?.has(propertyName) || this.parent?.isTrackable(propertyName)) ?? false; + } + + hasTrackableProperties(): boolean { + if (this.trackableProperties) { + return this.trackableProperties!.size > 0; + } + return this.parent?.hasTrackableProperties() ?? false; + } + + getTypenameTypeDecorator(propertyName: string): string | undefined { + if (this.typedProperties) { + return this.typedProperties?.get(propertyName); + } + if (this.parent) { + return this.parent!.getTypenameTypeDecorator(propertyName); + } + return undefined; + } + + private static findClassMetadata(type: any): ClassMetadata | undefined { + let prototype = Object.getPrototypeOf(type); + while (prototype) { + if (prototype.hasOwnProperty(ClassMetadata.metadataPropName) && prototype.__classMetadata !== undefined) { + return prototype.__classMetadata; + } + prototype = Object.getPrototypeOf(prototype); + } + return undefined; + } } diff --git a/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/typescript/performance.ts b/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/typescript/performance.ts index 9451185bcdc..64ee6a75284 100644 --- a/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/typescript/performance.ts +++ b/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/typescript/performance.ts @@ -18,7 +18,7 @@ * January 1, 1970 Universal Coordinated Time (UTC). */ export function timeNow(): number { - return performance.now() + return performance.now(); } /** @@ -26,5 +26,5 @@ export function timeNow(): number { * @returns a string representing a number in fixed-point notation */ export function numberToFixed(value: number, fractionDigits: number): string { - return value.toFixed(fractionDigits) + return value.toFixed(fractionDigits); } diff --git a/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/typescript/primitive.ts b/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/typescript/primitive.ts index 08ffda6e11f..e561639da15 100644 --- a/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/typescript/primitive.ts +++ b/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/typescript/primitive.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2024 Huawei Device Co., Ltd. + * Copyright (c) 2022-2025 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 @@ -13,41 +13,48 @@ * limitations under the License. */ -import { float64, int32, float32, int64 } from "./types" +import { float32, float64, int32, int64 } from './types'; -export function float32To64(value: float32): float64 { - return value +export function float32to64(value: float32): float64 { + return value; // toDouble() } - -export function float64To32(value: float64): float32 { - return value +export function float32toInt32(value: float32): int32 { + return value | 0; // toInt() } - -export function asFloat64(value: string): float64 { - return Number(value) +export function float32toInt64(value: float32): int64 { + return Math.trunc(value); // toLong() } -export function asString(value: float64 | undefined): string | undefined { - return value?.toString() +export function float64to32(value: float64): float32 { + return value; // toFloat() } - -export function float32FromBits(value: int32): float32 { - return value +export function float64toInt32(value: float64): int32 { + return value | 0; // toInt() } - -export function int32BitsFromFloat(value: float32): int32 { - return value +export function float64toInt64(value: float64): int64 { + return Math.trunc(value); // toLong() } -export function float64ToInt(value: float64): int32 { - return value +export function int32toFloat32(value: int32): float32 { + return value; // toFloat() } - -export function float64ToLong(value: float64): int64 { - return value +export function int32toFloat64(value: int32): float64 { + return value; // toDouble() +} +export function int32to64(value: int32): int64 { + return Math.trunc(value); // toLong() } -export function charToInt(value: string): int32 { - return parseInt(value) +export function int64toFloat32(value: int64): float32 { + return value; // toFloat() +} +export function int64toFloat64(value: int64): float64 { + return value; // toDouble() +} +export function int64to32(value: int64): int32 { + return value | 0; // toInt() } +export function asFloat64(value: string): float64 { + return Number(value); +} diff --git a/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/typescript/prop-deep-copy.ts b/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/typescript/prop-deep-copy.ts index 3dd13819df3..3d92bfb8217 100644 --- a/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/typescript/prop-deep-copy.ts +++ b/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/typescript/prop-deep-copy.ts @@ -13,7 +13,7 @@ * limitations under the License. */ -import { getObservableTarget } from "./observable" +import { getObservableTarget } from './observable'; /* When decorating variables of complex types, @@ -23,73 +23,72 @@ import { getObservableTarget } from "./observable" export function propDeepCopy(sourceObject: T): T { if (!sourceObject || typeof sourceObject !== 'object') { - return sourceObject + return sourceObject; } - const copiedObjects = new Map() - return recursiveDeepCopy(sourceObject) as T + const copiedObjects = new Map(); + return recursiveDeepCopy(sourceObject) as T; function recursiveDeepCopy(sourceObject: Object): Object { if (!sourceObject || typeof sourceObject !== 'object') { - return sourceObject + return sourceObject; } - const storedObject = copiedObjects.get(sourceObject) + const storedObject = copiedObjects.get(sourceObject); if (storedObject !== undefined) { - return storedObject + return storedObject; } - const copy: any = copyDeepTrackable(sourceObject) + const copy: any = copyDeepTrackable(sourceObject); - const objectToCopyFrom = getObservableTarget(sourceObject) - Object.keys(objectToCopyFrom) - .forEach((key) => { - const property = objectToCopyFrom[key as keyof Object] + const objectToCopyFrom = getObservableTarget(sourceObject); + Object.keys(objectToCopyFrom).forEach((key) => { + const property = objectToCopyFrom[key as keyof Object]; - if (typeof property === "function") { - Reflect.set(copy, key, property) - copy[key] = copy[key].bind(copy) - return - } - Reflect.set(copy, key, recursiveDeepCopy(property)); - }) + if (typeof property === 'function') { + Reflect.set(copy, key, property); + copy[key] = copy[key].bind(copy); + return; + } + Reflect.set(copy, key, recursiveDeepCopy(property)); + }); - return copy + return copy; } function copyDeepTrackable(sourceObject: T): T { if (sourceObject instanceof Set) { - const copy = new Set() - Object.setPrototypeOf(copy, Object.getPrototypeOf(sourceObject)) - copiedObjects.set(sourceObject, copy) + const copy = new Set(); + Object.setPrototypeOf(copy, Object.getPrototypeOf(sourceObject)); + copiedObjects.set(sourceObject, copy); for (const setKey of sourceObject.keys()) { - copy.add(recursiveDeepCopy(setKey)) + copy.add(recursiveDeepCopy(setKey)); } - return copy as T + return copy as T; } if (sourceObject instanceof Map) { - const copy = new Map() - Object.setPrototypeOf(copy, Object.getPrototypeOf(sourceObject)) - copiedObjects.set(sourceObject, copy) + const copy = new Map(); + Object.setPrototypeOf(copy, Object.getPrototypeOf(sourceObject)); + copiedObjects.set(sourceObject, copy); for (const mapKey of sourceObject.keys()) { - copy.set(mapKey, recursiveDeepCopy(sourceObject.get(mapKey))) + copy.set(mapKey, recursiveDeepCopy(sourceObject.get(mapKey))); } - return copy as T + return copy as T; } if (sourceObject instanceof Date) { - const copy = new Date() - copy.setTime(sourceObject.getTime()) - Object.setPrototypeOf(copy, Object.getPrototypeOf(sourceObject)) - copiedObjects.set(sourceObject, copy) - return copy as T + const copy = new Date(); + copy.setTime(sourceObject.getTime()); + Object.setPrototypeOf(copy, Object.getPrototypeOf(sourceObject)); + copiedObjects.set(sourceObject, copy); + return copy as T; } if (sourceObject instanceof Object) { - const copy = Array.isArray(sourceObject) ? [] : {} - Object.setPrototypeOf(copy, Object.getPrototypeOf(sourceObject)) - copiedObjects.set(sourceObject, copy) - return copy as T + const copy = Array.isArray(sourceObject) ? [] : {}; + Object.setPrototypeOf(copy, Object.getPrototypeOf(sourceObject)); + copiedObjects.set(sourceObject, copy); + return copy as T; } - return sourceObject + return sourceObject; } } diff --git a/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/typescript/reflection.ts b/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/typescript/reflection.ts index 7961ab53e33..9768e144ff1 100644 --- a/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/typescript/reflection.ts +++ b/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/typescript/reflection.ts @@ -1,4 +1,3 @@ - /* * Copyright (c) 2022-2023 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); @@ -13,8 +12,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { className } from "./ts-reflection" +import { className } from './ts-reflection'; export function lcClassName(object: Object) { - return className(object).toLowerCase() + return className(object).toLowerCase(); } diff --git a/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/typescript/strings.ts b/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/typescript/strings.ts index 3fd4a2a78db..264154f9958 100644 --- a/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/typescript/strings.ts +++ b/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/typescript/strings.ts @@ -13,7 +13,7 @@ * limitations under the License. */ -import { int32 } from "./types" +import { int32 } from './types'; interface SystemTextEncoder { encode(input?: string): Uint8Array; @@ -25,181 +25,184 @@ interface WithStreamOption { } interface SystemTextDecoder { - decode( - input?: ArrayBuffer | null, - options?: WithStreamOption - ): string; + decode(input?: ArrayBuffer | null, options?: WithStreamOption): string; } export class CustomTextEncoder { - static readonly HeaderLen: int32 = Int32Array.BYTES_PER_ELEMENT + static readonly HeaderLen: int32 = Int32Array.BYTES_PER_ELEMENT; - constructor(encoder: SystemTextEncoder|undefined = ((typeof TextEncoder != "undefined") ? new TextEncoder() : undefined)) { - this.encoder = encoder + constructor( + encoder: SystemTextEncoder | undefined = typeof TextEncoder != 'undefined' ? new TextEncoder() : undefined + ) { + this.encoder = encoder; } - private readonly encoder: SystemTextEncoder|undefined + private readonly encoder: SystemTextEncoder | undefined; public static stringLength(input: string): int32 { - let length = 0 + let length = 0; for (let i = 0; i < input.length; i++) { - length++ - let cp = input.codePointAt(i)! + length++; + let cp = input.codePointAt(i)!; if (cp >= 0x10000) { - i++ + i++; } } - return length + return length; } encodedLength(input: string): int32 { - let length = 0 + let length = 0; for (let i = 0; i < input.length; i++) { - let cp = input.codePointAt(i)! + let cp = input.codePointAt(i)!; if (cp < 0x80) { - length += 1 + length += 1; } else if (cp < 0x800) { - length += 2 + length += 2; } else if (cp < 0x10000) { - length += 3 + length += 3; } else { - length += 4 - i++ + length += 4; + i++; } } - return length + return length; } private addLength(array: Uint8Array, offset: int32, len: int32): void { - array[offset] = (len & 0xff) - array[offset + 1] = ((len >> 8) & 0xff) - array[offset + 2] = ((len >> 16) & 0xff) - array[offset + 3] = ((len >> 24) & 0xff) + array[offset] = len & 0xff; + array[offset + 1] = (len >> 8) & 0xff; + array[offset + 2] = (len >> 16) & 0xff; + array[offset + 3] = (len >> 24) & 0xff; } static getHeaderLength(array: Uint8Array, offset: int32 = 0): int32 { - return (array[offset] | (array[offset + 1] << 8) | (array[offset + 2] << 16) | (array[offset + 3] << 24)) + return array[offset] | (array[offset + 1] << 8) | (array[offset + 2] << 16) | (array[offset + 3] << 24); } // Produces array of bytes with encoded string headed by 4 bytes (little endian) size information: // [s0][s1][s2][s3] [c_0] ... [c_size-1] encode(input: string | undefined, addLength: boolean = true): Uint8Array { - let headerLen = addLength ? CustomTextEncoder.HeaderLen : 0 - let result: Uint8Array + let headerLen = addLength ? CustomTextEncoder.HeaderLen : 0; + let result: Uint8Array; if (!input) { - result = new Uint8Array(headerLen) + result = new Uint8Array(headerLen); } else if (this.encoder !== undefined) { - result = this.encoder!.encode('s'.repeat(headerLen) + input) + result = this.encoder!.encode('s'.repeat(headerLen) + input); } else { - let length = this.encodedLength(input) - result = new Uint8Array(length + headerLen) - this.encodeInto(input, result, headerLen) + let length = this.encodedLength(input); + result = new Uint8Array(length + headerLen); + this.encodeInto(input, result, headerLen); } if (addLength) { - this.addLength(result, 0, result.length - headerLen) + this.addLength(result, 0, result.length - headerLen); } - return result + return result; } // Produces encoded array of strings with size information. encodeArray(strings: Array): Uint8Array { - let totalBytes = CustomTextEncoder.HeaderLen - let lengths = new Int32Array(strings.length) + let totalBytes = CustomTextEncoder.HeaderLen; + let lengths = new Int32Array(strings.length); for (let i = 0; i < lengths.length; i++) { - let len = this.encodedLength(strings[i]) - lengths[i] = len - totalBytes += len + CustomTextEncoder.HeaderLen + let len = this.encodedLength(strings[i]); + lengths[i] = len; + totalBytes += len + CustomTextEncoder.HeaderLen; } - let array = new Uint8Array(totalBytes) - let position = 0 - this.addLength(array, position, lengths.length) - position += CustomTextEncoder.HeaderLen + let array = new Uint8Array(totalBytes); + let position = 0; + this.addLength(array, position, lengths.length); + position += CustomTextEncoder.HeaderLen; for (let i = 0; i < lengths.length; i++) { - this.addLength(array, position, lengths[i]) - position += CustomTextEncoder.HeaderLen - this.encodeInto(strings[i], array, position) - position += lengths[i] + this.addLength(array, position, lengths[i]); + position += CustomTextEncoder.HeaderLen; + this.encodeInto(strings[i], array, position); + position += lengths[i]; } - return array + return array; } encodeInto(input: string, result: Uint8Array, position: int32): Uint8Array { if (this.encoder !== undefined) { - this.encoder!.encodeInto(input, result.subarray(position, result.length)) - return result + this.encoder!.encodeInto(input, result.subarray(position, result.length)); + return result; } - let index = position + let index = position; for (let stringPosition = 0; stringPosition < input.length; stringPosition++) { - let cp = input.codePointAt(stringPosition)! + let cp = input.codePointAt(stringPosition)!; if (cp < 0x80) { - result[index++] = (cp | 0) + result[index++] = cp | 0; } else if (cp < 0x800) { - result[index++] = ((cp >> 6) | 0xc0) - result[index++] = ((cp & 0x3f) | 0x80) + result[index++] = (cp >> 6) | 0xc0; + result[index++] = (cp & 0x3f) | 0x80; } else if (cp < 0x10000) { - result[index++] = ((cp >> 12) | 0xe0) - result[index++] = (((cp >> 6) & 0x3f) | 0x80) - result[index++] = ((cp & 0x3f) | 0x80) + result[index++] = (cp >> 12) | 0xe0; + result[index++] = ((cp >> 6) & 0x3f) | 0x80; + result[index++] = (cp & 0x3f) | 0x80; } else { - result[index++] = ((cp >> 18) | 0xf0) - result[index++] = (((cp >> 12) & 0x3f) | 0x80) - result[index++] = (((cp >> 6) & 0x3f) | 0x80) - result[index++] = ((cp & 0x3f) | 0x80) - stringPosition++ + result[index++] = (cp >> 18) | 0xf0; + result[index++] = ((cp >> 12) & 0x3f) | 0x80; + result[index++] = ((cp >> 6) & 0x3f) | 0x80; + result[index++] = (cp & 0x3f) | 0x80; + stringPosition++; } } - result[index] = 0 - return result + result[index] = 0; + return result; } } export class CustomTextDecoder { - static cpArrayMaxSize = 128 - constructor(decoder: SystemTextDecoder|undefined = ((typeof TextDecoder != "undefined") ? new TextDecoder() : undefined)) { - this.decoder = decoder + static cpArrayMaxSize = 128; + constructor( + decoder: SystemTextDecoder | undefined = typeof TextDecoder != 'undefined' ? new TextDecoder() : undefined + ) { + this.decoder = decoder; } - private readonly decoder: SystemTextDecoder|undefined + private readonly decoder: SystemTextDecoder | undefined; decode(input: Uint8Array): string { if (this.decoder !== undefined) { - return this.decoder!.decode(input) + return this.decoder!.decode(input); } - const cpSize = Math.min(CustomTextDecoder.cpArrayMaxSize, input.length) - let codePoints = new Int32Array(cpSize) + const cpSize = Math.min(CustomTextDecoder.cpArrayMaxSize, input.length); + let codePoints = new Int32Array(cpSize); let cpIndex = 0; - let index = 0 - let result = "" + let index = 0; + let result = ''; while (index < input.length) { - let elem = input[index] - let lead = elem & 0xff - let count = 0 - let value = 0 + let elem = input[index]; + let lead = elem & 0xff; + let count = 0; + let value = 0; if (lead < 0x80) { - count = 1 - value = elem - } else if ((lead >> 5) == 0x6) { - value = ((elem << 6) & 0x7ff) + (input[index + 1] & 0x3f) - count = 2 - } else if ((lead >> 4) == 0xe) { - value = ((elem << 12) & 0xffff) + ((input[index + 1] << 6) & 0xfff) + - (input[index + 2] & 0x3f) - count = 3 - } else if ((lead >> 3) == 0x1e) { - value = ((elem << 18) & 0x1fffff) + ((input[index + 1] << 12) & 0x3ffff) + - ((input[index + 2] << 6) & 0xfff) + (input[index + 3] & 0x3f) - count = 4 + count = 1; + value = elem; + } else if (lead >> 5 == 0x6) { + value = ((elem << 6) & 0x7ff) + (input[index + 1] & 0x3f); + count = 2; + } else if (lead >> 4 == 0xe) { + value = ((elem << 12) & 0xffff) + ((input[index + 1] << 6) & 0xfff) + (input[index + 2] & 0x3f); + count = 3; + } else if (lead >> 3 == 0x1e) { + value = + ((elem << 18) & 0x1fffff) + + ((input[index + 1] << 12) & 0x3ffff) + + ((input[index + 2] << 6) & 0xfff) + + (input[index + 3] & 0x3f); + count = 4; } - codePoints[cpIndex++] = value + codePoints[cpIndex++] = value; if (cpIndex == cpSize) { - cpIndex = 0 - result += String.fromCodePoint(...codePoints) + cpIndex = 0; + result += String.fromCodePoint(...codePoints); } - index += count + index += count; } if (cpIndex > 0) { - result += String.fromCodePoint(...codePoints.slice(0, cpIndex)) + result += String.fromCodePoint(...codePoints.slice(0, cpIndex)); } - return result + return result; } } diff --git a/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/typescript/ts-reflection.ts b/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/typescript/ts-reflection.ts index 8afbf82663e..68d76e32e6c 100644 --- a/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/typescript/ts-reflection.ts +++ b/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/typescript/ts-reflection.ts @@ -14,22 +14,22 @@ */ export function className(object?: Object): string { - return object?.constructor.name ?? "" + return object?.constructor.name ?? ''; } export function isFunction(object?: Object): boolean { - return typeof object === 'function' + return typeof object === 'function'; } // Improve: this is to match arkts counterpart -export function functionOverValue(value: Value|(()=>Value)): boolean { - return typeof value === 'function' +export function functionOverValue(value: Value | (() => Value)): boolean { + return typeof value === 'function'; } export function refEqual(a: Value, b: Value): boolean { - return a === b + return a === b; } export function isNotPrimitive(value: Object): boolean { - return true + return true; } diff --git a/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/typescript/types.ts b/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/typescript/types.ts index 07796d8d418..9ca487641c4 100644 --- a/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/typescript/types.ts +++ b/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/typescript/types.ts @@ -14,12 +14,47 @@ */ /// -export type uint8 = int -export type int8 = int -export type int16 = int -export type int32 = int -export type uint32 = int -export type int64 = long -export type uint64 = long -export type float32 = float -export type float64 = double +export type uint8 = int; +export type int8 = int; +export type int16 = int; +export type int32 = int; +export type uint32 = int; +export type int64 = long; +export type uint64 = long; +export type float32 = float; +export type float64 = double; + +export {}; +declare global { + export interface Number { + toByte(): int; + toShort(): int; + toInt(): int; + toLong(): long; + toFloat(): float; + toDouble(): double; + } +} +Number.prototype.toByte = function () { + return this as Number | 0 as number; +}; + +Number.prototype.toShort = function () { + return this as Number | 0 as number; +}; + +Number.prototype.toInt = function () { + return this as Number | 0 as number; +}; + +Number.prototype.toLong = function () { + return this as Number | 0 as number; +}; + +Number.prototype.toFloat = function () { + return this as number; +}; + +Number.prototype.toDouble = function () { + return this as number; +}; diff --git a/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/typescript/utils.ts b/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/typescript/utils.ts index c51ff17f4c2..4574ef04d42 100644 --- a/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/typescript/utils.ts +++ b/frameworks/bridge/arkts_frontend/koala_projects/incremental/compat/src/typescript/utils.ts @@ -13,34 +13,75 @@ * limitations under the License. */ -export function errorAsString(error: Error): string { - return error.stack ?? error.toString() +import { AtomicRef } from './atomic'; + +export function errorAsString(error: any): string { + if (error instanceof Error) { + return error.stack ?? error.toString(); + } + return JSON.stringify(error); } export function unsafeCast(value: unknown): T { - return value as unknown as T + return value as unknown as T; } export function scheduleCoroutine(): void {} export function memoryStats(): string { - return `none` + return `none`; } -export function launchJob(job: () => void): Promise { - return new Promise(resolve => setTimeout(() => { - resolve() - job() - }, 0) - ) +export function launchJob(task: () => void): Promise { + return new Promise((resolve, reject) => { + try { + task(); + resolve(undefined); + } catch (error) { + reject(error); + } + }); } -export class CoroutineLocalValue { - private value: T | undefined = undefined - get(): T | undefined { - return this.value +export class WorkerLocalValue { + private ref?: AtomicRef; + + /** + * @param init - a factory function to provide initial worker-local value if needed + */ + constructor(private init?: () => T) {} + + /** + * @returns the worker-local value for current worker + * @throws `Error` when value not initialized and no `init` function provided + */ + get(): T { + const ref = this.ref; + if (ref) return ref.value; + const init = this.init; + if (!init) throw new Error('WorkerLocalValue not initialized: call set() first or provide init() function.'); + const value = init(); + this.ref = new AtomicRef(value); + return value; } - set(value: T | undefined) { - this.value = value + + /** + * Updates the worker-local value for current worker. + * @param value - new value to store + */ + set(value: T) { + const ref = this.ref; + if (ref) { + ref.value = value; + } else { + this.ref = new AtomicRef(value); + } + } + + /** + * Deletes the worker-local value for current worker. + */ + delete() { + this.ref = undefined; } } diff --git a/frameworks/bridge/arkts_frontend/koala_projects/incremental/runtime/src/animation/AnimatedState.ts b/frameworks/bridge/arkts_frontend/koala_projects/incremental/runtime/src/animation/AnimatedState.ts index d117e493e2f..01ce6e1717b 100644 --- a/frameworks/bridge/arkts_frontend/koala_projects/incremental/runtime/src/animation/AnimatedState.ts +++ b/frameworks/bridge/arkts_frontend/koala_projects/incremental/runtime/src/animation/AnimatedState.ts @@ -26,12 +26,9 @@ import { ComputableState, MutableState, State, StateContext } from "../states/St * which value is changed according to the given animation. */ export interface AnimatedState extends State { - getAnimation(): TimeAnimation - setAnimation(value: TimeAnimation): void + animation: TimeAnimation readonly running: boolean - getPaused(): boolean - setPaused(value: boolean): void - + paused: boolean } /** @@ -54,8 +51,7 @@ export function animatedState(animation: TimeAnimation, startNow: boolean * to change the current value to the target one. */ export interface MutableAnimatedState extends MutableState { - getAnimation(): TimeAnimation - setAnimation(value: TimeAnimation): void + animation: TimeAnimation readonly running: boolean } @@ -141,22 +137,22 @@ class AnimatedStateImpl implements Disposable, AnimatedState { return this.runningState.value } - getPaused(): boolean { + get paused(): boolean { return this.pausedState.value } - setPaused(paused: boolean) { + set paused(paused: boolean) { this.pausedState.value = paused } - getAnimation(): TimeAnimation { + get animation(): TimeAnimation { return this.myAnimation } - setAnimation(animation: TimeAnimation): void { + set animation(animation: TimeAnimation) { if (this.myAnimation === animation) return // nothing to change this.myAnimation = animation - if (!this.getPaused()) animation.onStart(this.timeProvider()) + if (!this.paused) animation.onStart(this.timeProvider()) } constructor(myAnimation: TimeAnimation, startNow: boolean, timeProvider: (() => int64)|undefined) { @@ -180,22 +176,22 @@ class AnimatedStateImpl implements Disposable, AnimatedState { const paused = this.pausedState.value if (this.pausedState.modified) { if (paused) { - this.getAnimation().onPause(time) + this.animation.onPause(time) } else { - this.getAnimation().onStart(time) + this.animation.onStart(time) } } // compute value from the time provided - let newValue = this.getAnimation().getValue(time) + let newValue = this.animation.getValue(time) this.action?.(newValue) return newValue } finally { // update running state if needed - this.runningState.value = this.getAnimation().running + this.runningState.value = this.animation.running } }) if (startNow) { - this.getAnimation().onStart(this.timeProvider()) + this.animation.onStart(this.timeProvider()) } } @@ -233,19 +229,19 @@ class MutableAnimatedStateImpl implements MutableAnimatedState { } set value(value: Value) { - this.animatedState.setAnimation(this.animationProvider(this.animatedState.value, value)) + this.animatedState.animation = this.animationProvider(this.animatedState.value, value) } get running(): boolean { return this.animatedState.running } - getAnimation(): TimeAnimation { - return this.animatedState.getAnimation() + get animation(): TimeAnimation { + return this.animatedState.animation } - setAnimation(animation: TimeAnimation): void { - this.animatedState.setAnimation(animation) + set animation(animation: TimeAnimation) { + this.animatedState.animation = animation } } @@ -268,7 +264,7 @@ class StateAnimatorImpl implements StateAnimator { set parameter(parameter: P) { if (refEqual(this.parameterState.value, parameter)) return // nothing to change this.parameterState.value = parameter - this.animatedState.setAnimation(this.animationProvider(parameter, this.animatedState.value)) + this.animatedState.animation = this.animationProvider(parameter, this.animatedState.value) } get value(): V { diff --git a/frameworks/bridge/arkts_frontend/koala_projects/incremental/runtime/src/animation/Easing.ts b/frameworks/bridge/arkts_frontend/koala_projects/incremental/runtime/src/animation/Easing.ts index 12cee45fc2f..d8e989072a6 100644 --- a/frameworks/bridge/arkts_frontend/koala_projects/incremental/runtime/src/animation/Easing.ts +++ b/frameworks/bridge/arkts_frontend/koala_projects/incremental/runtime/src/animation/Easing.ts @@ -13,7 +13,7 @@ * limitations under the License. */ -import { float64, uint32 } from "@koalaui/common" +import { float64, uint32, float64toInt32 } from "@koalaui/common" import { EasingSupport } from "./EasingSupport" /** @@ -99,7 +99,7 @@ export class Easing { if (value < 1) { value *= easing.length const index = Math.floor(value) - if (index < easing.length) return easing[index as uint32](value - index) + if (index < easing.length) return easing[float64toInt32(index)](value - index) } return easing[easing.length - 1](1) } diff --git a/frameworks/bridge/arkts_frontend/koala_projects/incremental/runtime/src/animation/EasingSupport.ts b/frameworks/bridge/arkts_frontend/koala_projects/incremental/runtime/src/animation/EasingSupport.ts index 26921809755..4e3a7fa2ddf 100644 --- a/frameworks/bridge/arkts_frontend/koala_projects/incremental/runtime/src/animation/EasingSupport.ts +++ b/frameworks/bridge/arkts_frontend/koala_projects/incremental/runtime/src/animation/EasingSupport.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2024 Huawei Device Co., Ltd. + * Copyright (c) 2022-2025 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 @@ -13,7 +13,7 @@ * limitations under the License. */ -import { float64, isFiniteNumber, uint32 } from "@koalaui/common" +import { float64, int32toFloat64, int64to32, isFiniteNumber, uint32 } from "@koalaui/common" export class EasingSupport { private x: Float64Array @@ -29,20 +29,20 @@ export class EasingSupport { this.x[last] = xSupplier(1) this.y[last] = ySupplier(1) for (let i = 1; i < last; i++) { - const value = (i as float64) / last + const value = int32toFloat64(i) / last this.x[i] = xSupplier(value) this.y[i] = ySupplier(value) } } convert(value: float64): float64 { - let last = (this.x.length - 1) as uint32 - let left = 0 as uint32 + let last = this.x.length - 1 + let left = 0 if (value < this.x[left]) return this.y[left] let right = last if (value > this.x[right]) return this.y[right] while (left <= right) { - const center = ((left + right) >>> 1) as uint32 + const center = int64to32((left + right) >>> 1) if (value < this.x[center]) right = center - 1 else if (value > this.x[center]) left = center + 1 else return this.y[center] diff --git a/frameworks/bridge/arkts_frontend/koala_projects/incremental/runtime/src/animation/TimeAnimation.ts b/frameworks/bridge/arkts_frontend/koala_projects/incremental/runtime/src/animation/TimeAnimation.ts index d916fdd1952..7eb8566cfda 100644 --- a/frameworks/bridge/arkts_frontend/koala_projects/incremental/runtime/src/animation/TimeAnimation.ts +++ b/frameworks/bridge/arkts_frontend/koala_projects/incremental/runtime/src/animation/TimeAnimation.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2024 Huawei Device Co., Ltd. + * Copyright (c) 2022-2025 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 @@ -13,7 +13,7 @@ * limitations under the License. */ -import { float64, float64ToLong, int32, int64, isFiniteNumber, uint32 } from "@koalaui/common" +import { float64, float64toInt64, int32, int64, isFiniteNumber, uint32 } from "@koalaui/common" import { AnimationRange, NumberAnimationRange } from "./AnimationRange" import { Easing, EasingCurve } from "./Easing" import { scheduleCallback } from "../states/GlobalStateManager" @@ -322,7 +322,7 @@ class PeriodicAnimationImpl implements TimeAnimation { let result = this.state let passedTime = currentTime - startTime if (passedTime > this.period) { - result += float64ToLong(Math.floor(passedTime / this.period)) + result += float64toInt64(Math.floor(passedTime / this.period)) passedTime = passedTime % this.period // tune start time for long animations this.startTime = currentTime - passedTime @@ -514,19 +514,19 @@ class AnimationImpl implements TimeAnimation { if (this.lastState < 0) { // tune start time on direction change this.running = true - this.startTime = time + (this.lastState * this.duration) as int64 + this.startTime = time + float64toInt64(this.lastState * this.duration) } } else { // set start time to continue animation from the current state this.running = true - this.startTime = time - ( + this.startTime = time - float64toInt64( this.lastState < 0 ? (2 + this.lastState) * this.duration : this.lastState > 0 ? this.lastState * this.duration : (0 - this.delay) // add delay for the state 0 only - ) as int64 + ) } } @@ -539,7 +539,7 @@ class AnimationImpl implements TimeAnimation { if (this.lastState > 0) { // tune start time on direction change this.running = true - this.startTime = time - (this.duration * (2 - this.lastState)) as int64 + this.startTime = time - float64toInt64(this.duration * (2 - this.lastState)) } } else { diff --git a/frameworks/bridge/arkts_frontend/koala_projects/incremental/runtime/src/animation/memo.ts b/frameworks/bridge/arkts_frontend/koala_projects/incremental/runtime/src/animation/memo.ts index a22078ca5f2..9c0ec505ada 100644 --- a/frameworks/bridge/arkts_frontend/koala_projects/incremental/runtime/src/animation/memo.ts +++ b/frameworks/bridge/arkts_frontend/koala_projects/incremental/runtime/src/animation/memo.ts @@ -77,7 +77,7 @@ export function rememberNumberTransition(on: boolean, duration: uint32, easing: /** @memo */ export function rememberTransition(on: boolean, duration: uint32, easing: EasingCurve, compute: AnimationRange, initial: boolean = on): AnimatedState { const state = rememberAnimatedState((): TimeAnimation => transition(duration, easing, compute, initial ? 1 : 0), on) - RunEffect(!on, (paused: boolean): void => { state.setPaused(paused) }) + RunEffect(!on, (paused: boolean): void => { state.paused = paused }) return state } diff --git a/frameworks/bridge/arkts_frontend/koala_projects/incremental/runtime/src/common/MarkableQueue.ts b/frameworks/bridge/arkts_frontend/koala_projects/incremental/runtime/src/common/MarkableQueue.ts index 28abd48f6b8..ed8e662868f 100644 --- a/frameworks/bridge/arkts_frontend/koala_projects/incremental/runtime/src/common/MarkableQueue.ts +++ b/frameworks/bridge/arkts_frontend/koala_projects/incremental/runtime/src/common/MarkableQueue.ts @@ -38,9 +38,15 @@ export function markableQueue(reversed: boolean = false): MarkableQueue { } class DefaultQueue implements MarkableQueue { - private readonly last = new AtomicRef(new Block()) - private readonly first = new AtomicRef(this.last.value) - private readonly marker = new AtomicRef(undefined) + private readonly last: AtomicRef + private readonly first: AtomicRef + private readonly marker: AtomicRef + + constructor() { + this.last = new AtomicRef(new Block()) + this.first = new AtomicRef(this.last.value) + this.marker = new AtomicRef(undefined) + } setMarker(): void { const marker = new Block() diff --git a/frameworks/bridge/arkts_frontend/koala_projects/incremental/runtime/src/common/RuntimeProfiler.ts b/frameworks/bridge/arkts_frontend/koala_projects/incremental/runtime/src/common/RuntimeProfiler.ts index f44142e0f59..b1998baeb20 100644 --- a/frameworks/bridge/arkts_frontend/koala_projects/incremental/runtime/src/common/RuntimeProfiler.ts +++ b/frameworks/bridge/arkts_frontend/koala_projects/incremental/runtime/src/common/RuntimeProfiler.ts @@ -13,7 +13,7 @@ * limitations under the License. */ -import { int32 } from "@koalaui/common" +import { float64toInt32, int32 } from "@koalaui/common" /** * Adds statistics for constructing/disposing of the TreeNode instances. @@ -21,8 +21,17 @@ import { int32 } from "@koalaui/common" */ const DEBUG_WITH_NODE_STATS = false +export interface RuntimeTracer { + /** called on enter to the specified section */ + begin(name: string): void + /** called to log the specified message */ + log(message: string): void + /** called on exit from the specified section */ + end(name: string): void +} + export class RuntimeProfiler { - private static readonly map: Map > | undefined = DEBUG_WITH_NODE_STATS + private static readonly map: Map> | undefined = DEBUG_WITH_NODE_STATS ? new Map>() : undefined @@ -43,6 +52,7 @@ export class RuntimeProfiler { if (!set.delete(node)) console.log("node is already disposed") } + public static tracer: RuntimeTracer | undefined = undefined public static instance: RuntimeProfiler | undefined = undefined private invalidations = 0 @@ -131,7 +141,7 @@ export class RuntimeProfiler { `layouts: ${this.layouts}`, `FPS: ${this.lastFPS}`, ) - RuntimeProfiler.map?.forEach((set:Set, kind:int32) => { + RuntimeProfiler.map?.forEach((set: Set, kind: int32) => { if (set.size > 0) array.push(kind + ":" + set.size) }) return array.join("\n") @@ -143,13 +153,13 @@ export class RuntimeProfiler { node() { this.nodes++ } realDraw() { this.realDraws++ } cachedDraw() { this.cachedDraws++ } - layout() { this.layouts++ } + layout() { this.layouts++ } measure() { this.measures++ } frame(ms: number) { if (ms - this.lastTime <= 1000) { this.frames++ } else { - this.lastFPS = Math.round(this.frames * 1000 / (ms - this.lastTime)) as int32 + this.lastFPS = float64toInt32(Math.round(this.frames * 1000 / (ms - this.lastTime))) this.frames = 1 this.lastTime = ms } diff --git a/frameworks/bridge/arkts_frontend/koala_projects/incremental/runtime/src/common/Unique.ts b/frameworks/bridge/arkts_frontend/koala_projects/incremental/runtime/src/common/Unique.ts index 000d9d52f9b..35ec465f5bc 100644 --- a/frameworks/bridge/arkts_frontend/koala_projects/incremental/runtime/src/common/Unique.ts +++ b/frameworks/bridge/arkts_frontend/koala_projects/incremental/runtime/src/common/Unique.ts @@ -13,7 +13,7 @@ * limitations under the License. */ -import { int32 } from "@koalaui/common" +import { int32, int64to32, float64toInt32 } from "@koalaui/common" export type UID = int32 @@ -295,19 +295,19 @@ class Entry implements Unique { } } -function found(array: Array, uid: UID, index: int): U | undefined { +function found(array: Array, uid: UID, index: number): U | undefined { if (index < array.length) { - const element = array[index] + const element = array[float64toInt32(index)] if (element.uid == uid) return element } return undefined } -function find(array: Array, uid: UID): int { +function find(array: Array, uid: UID): number { let left = 0 let right = array.length while (left < right) { - const center = ((left + right) >>> 1) as int32 + const center = int64to32((left + right) >>> 1) if (array[center].uid < uid) left = center + 1 else right = center } diff --git a/frameworks/bridge/arkts_frontend/koala_projects/incremental/runtime/src/index.ts b/frameworks/bridge/arkts_frontend/koala_projects/incremental/runtime/src/index.ts index eba70ddd09c..e694a191c6c 100644 --- a/frameworks/bridge/arkts_frontend/koala_projects/incremental/runtime/src/index.ts +++ b/frameworks/bridge/arkts_frontend/koala_projects/incremental/runtime/src/index.ts @@ -13,7 +13,7 @@ * limitations under the License. */ -export { observableProxy, ObservableClass, TrackableProps } from "@koalaui/compat" +export { observableProxy, ObservableClass, ClassMetadata, TrackableProperties, trackableProperties } from "@koalaui/common" export { AnimatedState, @@ -67,7 +67,10 @@ export { transition, } from "./animation/TimeAnimation" -export { RuntimeProfiler } from "./common/RuntimeProfiler" +export { + RuntimeProfiler, + RuntimeTracer, +} from "./common/RuntimeProfiler" export { memoBind, @@ -120,6 +123,7 @@ export { testRoot, testTick, testUpdate, + toRootHierarchy, } from "./memo/testing" export { @@ -135,6 +139,7 @@ export { mutableState, scheduleCallback, updateStateManager, + globalMutableState, } from "./states/GlobalStateManager" export { ArrayState, @@ -144,13 +149,13 @@ export { ControlledScope, createStateManager, Equivalent, - InternalScope, + IncrementalScope, MutableState, State, StateContext, StateManager, - StateManagerImpl, - StateImpl, + StateManagerImpl, /* Improve:HQ private as public*/ + StateImpl, /* Improve:HQ private as public */ ValueTracker, } from "./states/State" @@ -172,4 +177,3 @@ export { PrimeNumbers } from "./tree/PrimeNumbers" export { ReadonlyTreeNode } from "./tree/ReadonlyTreeNode" export { TreeNode } from "./tree/TreeNode" export { TreePath } from "./tree/TreePath" - diff --git a/frameworks/bridge/arkts_frontend/koala_projects/incremental/runtime/src/memo/bind.ts b/frameworks/bridge/arkts_frontend/koala_projects/incremental/runtime/src/memo/bind.ts index 4ceca7f74ca..2a1eb6880e4 100644 --- a/frameworks/bridge/arkts_frontend/koala_projects/incremental/runtime/src/memo/bind.ts +++ b/frameworks/bridge/arkts_frontend/koala_projects/incremental/runtime/src/memo/bind.ts @@ -23,7 +23,7 @@ export function memoBind( { return ( /** @memo */ - () => { item(value) } + (): void => { item(value) } ) } @@ -37,7 +37,7 @@ export function memoBind2( { return ( /** @memo */ - () => { item(value1, value2) } + (): void => { item(value1, value2) } ) } @@ -52,7 +52,7 @@ export function memoPartialBind2_1( { return ( /** @memo */ - (arg2: T2) => { item(value1, arg2) } + (arg2: T2): void => { item(value1, arg2) } ) } @@ -67,6 +67,6 @@ export function memoPartialBind3_2( { return ( /** @memo */ - (arg2: T2, arg3: T3) => { item(value1, arg2, arg3) } + (arg2: T2, arg3: T3): void => { item(value1, arg2, arg3) } ) } diff --git a/frameworks/bridge/arkts_frontend/koala_projects/incremental/runtime/src/memo/changeListener.ts b/frameworks/bridge/arkts_frontend/koala_projects/incremental/runtime/src/memo/changeListener.ts index b21a0dbd5f2..a93b1cc5079 100644 --- a/frameworks/bridge/arkts_frontend/koala_projects/incremental/runtime/src/memo/changeListener.ts +++ b/frameworks/bridge/arkts_frontend/koala_projects/incremental/runtime/src/memo/changeListener.ts @@ -25,7 +25,7 @@ import { __context, __id } from "../internals" * @param listener - a function to perform if the given value has changed */ /** @memo:intrinsic */ -export function OnChange(value: Value, listener: (value: Value) => void): void { +export function OnChange(value: Value, listener: (value: Value) => void) { watch(__context(), __id(), false, value, listener) } @@ -36,7 +36,7 @@ export function OnChange(value: Value, listener: (value: Value) => void): * @param effect - a function to perform if the given value has changed or initialized */ /** @memo:intrinsic */ -export function RunEffect(value: Value, effect: (value: Value) => void): void { +export function RunEffect(value: Value, effect: (value: Value) => void) { watch(__context(), __id(), true, value, effect) } diff --git a/frameworks/bridge/arkts_frontend/koala_projects/incremental/runtime/src/memo/entry.ts b/frameworks/bridge/arkts_frontend/koala_projects/incremental/runtime/src/memo/entry.ts index be8dc460e5f..dc837aac939 100644 --- a/frameworks/bridge/arkts_frontend/koala_projects/incremental/runtime/src/memo/entry.ts +++ b/frameworks/bridge/arkts_frontend/koala_projects/incremental/runtime/src/memo/entry.ts @@ -13,7 +13,7 @@ * limitations under the License. */ -import { KoalaCallsiteKey, KoalaCallsiteKeys } from "@koalaui/common" +import { KoalaCallsiteKey } from "@koalaui/common" import { RuntimeProfiler } from "../common/RuntimeProfiler" import { GlobalStateManager } from "../states/GlobalStateManager" import { ComputableState, StateContext, StateManager } from "../states/State" @@ -34,10 +34,7 @@ export function memoRoot( ): ComputableState { return manager.updatableNode(node, (context: StateContext) => { RuntimeProfiler.instance?.buildRootEnter() - const frozen = manager.frozen - manager.frozen = true // states are frozen during recomposition - memoEntry1(context, KoalaCallsiteKeys.empty, update, node) - manager.frozen = frozen + memoEntry1(context, 0, update, node) RuntimeProfiler.instance?.buildRootExit() }) } diff --git a/frameworks/bridge/arkts_frontend/koala_projects/incremental/runtime/src/memo/node.ts b/frameworks/bridge/arkts_frontend/koala_projects/incremental/runtime/src/memo/node.ts index 9c6d2be6de8..d406658e81f 100644 --- a/frameworks/bridge/arkts_frontend/koala_projects/incremental/runtime/src/memo/node.ts +++ b/frameworks/bridge/arkts_frontend/koala_projects/incremental/runtime/src/memo/node.ts @@ -16,7 +16,7 @@ import { className, uint32 } from "@koalaui/common" import { __context, __id } from "../internals" import { IncrementalNode } from "../tree/IncrementalNode" -import { memoEntry } from "./entry" +import { memoEntry1 } from "./entry" /** * @param create - the node constructor is invoked only once, @@ -36,11 +36,12 @@ export function NodeAttach( if (scope.unchanged) { scope.cached } else try { - if (!reuseKey) + if (!reuseKey) { update(__context().node as Node) - else + } else { // reset ID addition to 0 to simplify the reuse process later - memoEntry(__context(), 0, () => { update(__context().node as Node) }) + memoEntry1(__context(), 0, update, __context().node as Node) + } } finally { scope.recache() } diff --git a/frameworks/bridge/arkts_frontend/koala_projects/incremental/runtime/src/memo/remember.ts b/frameworks/bridge/arkts_frontend/koala_projects/incremental/runtime/src/memo/remember.ts index f44f75acd6a..f5640c0f067 100644 --- a/frameworks/bridge/arkts_frontend/koala_projects/incremental/runtime/src/memo/remember.ts +++ b/frameworks/bridge/arkts_frontend/koala_projects/incremental/runtime/src/memo/remember.ts @@ -167,8 +167,7 @@ function applyPromiseToState(promise: Promise, state: MutableState /** @memo */ export function rememberMutableAsyncState(compute: () => Promise, initial?: Value, onError?: (error: Error) => void): MutableState { const result = rememberMutableState(initial) - const callback = () => { applyPromiseToState(compute(), result, onError) } - once(callback) + once(() => { applyPromiseToState(compute(), result, onError) }) return result } diff --git a/frameworks/bridge/arkts_frontend/koala_projects/incremental/runtime/src/memo/repeat.ts b/frameworks/bridge/arkts_frontend/koala_projects/incremental/runtime/src/memo/repeat.ts index 01f9b92967b..2c5925547ac 100644 --- a/frameworks/bridge/arkts_frontend/koala_projects/incremental/runtime/src/memo/repeat.ts +++ b/frameworks/bridge/arkts_frontend/koala_projects/incremental/runtime/src/memo/repeat.ts @@ -34,7 +34,7 @@ export function Repeat( count: int32, /** @memo */ action: (index: int32) => void -): void { +) { for (let i = 0; i < count; i++) { memoEntry1(__context(), i, action, i) } @@ -59,7 +59,7 @@ export function RepeatWithKey( key: (index: int32) => KoalaCallsiteKey, /** @memo */ action: (index: int32) => void -): void { +) { for (let i = 0; i < count; i++) { memoEntry1(__context(), key(i), action, i) } @@ -81,7 +81,7 @@ export function RepeatByArray( key: (element: T, index: int32) => KoalaCallsiteKey, /** @memo */ action: (element: T, index: int32) => void -): void { +) { const length = array.length for (let i = 0; i < length; i++) { const e: T = array[i] @@ -106,7 +106,7 @@ export function RepeatRange( key: (element: T, index: int32) => KoalaCallsiteKey, /** @memo */ action: (element: T, index: int32) => void -): void { +) { for (let i: int32 = start; i < end; i++) { const e: T = element(i) memoEntry2(__context(), key(e, i), action, e, i) diff --git a/frameworks/bridge/arkts_frontend/koala_projects/incremental/runtime/src/memo/testing.ts b/frameworks/bridge/arkts_frontend/koala_projects/incremental/runtime/src/memo/testing.ts index 0d5fba4148d..c4815ec9fc7 100644 --- a/frameworks/bridge/arkts_frontend/koala_projects/incremental/runtime/src/memo/testing.ts +++ b/frameworks/bridge/arkts_frontend/koala_projects/incremental/runtime/src/memo/testing.ts @@ -13,10 +13,11 @@ * limitations under the License. */ -import { KoalaCallsiteKey, uint32 } from "@koalaui/common" +import { KoalaCallsiteKey, uint32, unsafeCast } from "@koalaui/common" import { GlobalStateManager } from "../states/GlobalStateManager" import { ComputableState, State, StateManager } from "../states/State" import { IncrementalNode } from "../tree/IncrementalNode" +import { ReadonlyTreeNode, toNodeHierarchy } from "../tree/ReadonlyTreeNode" import { Disposable } from "../states/Disposable" import { memoRoot } from "./entry" import { NodeAttach } from "./node" @@ -44,8 +45,8 @@ export class TestNode extends IncrementalNode { static attach( /** @memo */ content: (node: TestNode) => void - ) { - NodeAttach(():TestNode => new TestNode(), content) + ): void { + NodeAttach((): TestNode => new TestNode(), content) } } @@ -89,7 +90,7 @@ export class ReusableTestNode extends TestNode { export function testRoot( /** @memo */ content: (node: TestNode) => void -): State { +): ComputableState { const root = TestNode.create(content) root.value return root @@ -107,3 +108,9 @@ export function testTick(root: State, withCa testUpdate(withCallbacks) root.value } + +/** @internal */ +export function toRootHierarchy(root: ComputableState, isScopeTree: boolean = false): string { + const node: ReadonlyTreeNode = root.value // recompute if needed + return toNodeHierarchy(isScopeTree ? unsafeCast(root) : node) +} diff --git a/frameworks/bridge/arkts_frontend/koala_projects/incremental/runtime/src/states/GlobalStateManager.ts b/frameworks/bridge/arkts_frontend/koala_projects/incremental/runtime/src/states/GlobalStateManager.ts index 7c1285cf61f..e8e9ef13482 100644 --- a/frameworks/bridge/arkts_frontend/koala_projects/incremental/runtime/src/states/GlobalStateManager.ts +++ b/frameworks/bridge/arkts_frontend/koala_projects/incremental/runtime/src/states/GlobalStateManager.ts @@ -13,19 +13,17 @@ * limitations under the License. */ -import { CoroutineLocalValue, KoalaCallsiteKey } from "@koalaui/common" -import { ArrayState, Equivalent, MutableState, StateManager, ValueTracker, createStateManager } from "./State" +import { ArrayState, Equivalent, MutableState, StateManager, StateManagerLocal, ValueTracker, createStateManager } from "./State" /** * This class provides an access to the global state manager of the application. * @internal */ export class GlobalStateManager { - private static localManager = new CoroutineLocalValue() private static sharedManager: StateManager | undefined = undefined private static get current(): StateManager | undefined { - return GlobalStateManager.GetLocalManager() ?? GlobalStateManager.sharedManager + return StateManagerLocal.get() ?? GlobalStateManager.sharedManager } /** @@ -51,26 +49,22 @@ export class GlobalStateManager { /** * Get state manager by coroutine id. + * Improve: can be removed after migration to multi-threaded implementation. + * @deprecated * @internal */ static GetLocalManager(): StateManager | undefined { - return GlobalStateManager.localManager.get() + return StateManagerLocal.get() } /** * Store state manager by coroutine id. + * Improve: can be removed after migration to multi-threaded implementation. + * @deprecated * @internal */ static SetLocalManager(manager: StateManager | undefined): void { - GlobalStateManager.localManager.set(manager) - } - - /** - * @return callsite key for a current context or `undefined` for global context - * @internal - */ - public static getCurrentScopeId(): KoalaCallsiteKey | undefined { - return GlobalStateManager.instance.currentScopeId + StateManagerLocal.set(manager) } } @@ -131,3 +125,15 @@ export function mutableState(value: T, equivalent?: Equivalent, tracker?: export function arrayState(array?: ReadonlyArray, equivalent?: Equivalent): ArrayState { return GlobalStateManager.instance.arrayState(array, undefined, equivalent) } + +/** + * Creates new mutable state in the global state manager. + * This state is valid until it is manually detached from the manager. + * Note that this state will not be automatically disconnected, + * even if this function is called in memo-context. + * Always call {@link Disposable.dispose} when the state is not needed to prevent memory leaks. + * @see #mutableState + */ +export function globalMutableState(value: Value): MutableState { + return GlobalStateManager.instance.mutableState(value, true) +} diff --git a/frameworks/bridge/arkts_frontend/koala_projects/incremental/runtime/src/states/State.ts b/frameworks/bridge/arkts_frontend/koala_projects/incremental/runtime/src/states/State.ts index 7ea2ed110ef..3ee4e51f1e7 100644 --- a/frameworks/bridge/arkts_frontend/koala_projects/incremental/runtime/src/states/State.ts +++ b/frameworks/bridge/arkts_frontend/koala_projects/incremental/runtime/src/states/State.ts @@ -13,14 +13,28 @@ * limitations under the License. */ -import { Array_from_set, className, float64ToInt, int32, KoalaCallsiteKey, KoalaCallsiteKeys, Observable, ObservableHandler, refEqual, uint32 } from "@koalaui/common" +import { + Array_from_set, + className, + WorkerLocalValue, + float64toInt32, + int32, + KoalaCallsiteKey, + launchJob, + Observable, + ObservableHandler, + refEqual, + TrackableProperties, + trackableProperties, + uint32, +} from "@koalaui/common" import { Dependency, ScopeToStates, StateToScopes } from "./Dependency" import { Disposable, disposeContent, disposeContentBackward } from "./Disposable" import { Changes, Journal } from "./Journal" import { markableQueue } from "../common/MarkableQueue" import { RuntimeProfiler } from "../common/RuntimeProfiler" import { IncrementalNode } from "../tree/IncrementalNode" -import { ReadonlyTreeNode } from "../tree/ReadonlyTreeNode" +import { ReadonlyTreeNode, toNodeHierarchy } from "../tree/ReadonlyTreeNode" export const CONTEXT_ROOT_SCOPE = "ohos.koala.context.root.scope" export const CONTEXT_ROOT_NODE = "ohos.koala.context.root.node" @@ -39,6 +53,8 @@ export function createStateManager(): StateManager { return new StateManagerImpl() } +export const StateManagerLocal = new WorkerLocalValue(() => undefined) + /** * State manager, core of incremental runtime engine. * @@ -46,9 +62,28 @@ export function createStateManager(): StateManager { * applications. */ export interface StateManager extends StateContext { + /** + * Improve:HQ unclear usage of this non-unique identifier + * @internal + */ readonly currentScopeId: KoalaCallsiteKey | undefined + /** + * Improve: can be removed after migration to multi-threaded implementation. + * @deprecated + * @internal + */ contextData: object | undefined + /** + * Improve: can be removed after migration to multi-threaded implementation. + * @deprecated + * @internal + */ isDebugMode: boolean + /** + * Improve: can be removed after migration to multi-threaded implementation. + * @deprecated + * @internal + */ setThreadChecker(callback: () => boolean): void syncChanges(): void @@ -76,6 +111,21 @@ export interface State { readonly value: Value } +/** + * Extended interface to the state object to get extra info. + * @internal + */ +export interface StateEx { + /** + * @return `true` if at least one incremental scope depends on this state + */ + hasDependencies(): boolean + /** + * @return a string representation of this state, useful for debugging + */ + toString(): string +} + /** * Individual mutable state, wrapping a value of type `Value`. */ @@ -94,10 +144,10 @@ export interface MutableState extends Disposable, State { * Individual mutable state, wrapping an array of elements with the specified type. */ export interface ArrayState extends State> { - length: int - at(index: int): Item - get(index: int): Item - set(index: int, item: Item): void + length: number + at(index: number): Item + get(index: number): Item + set(index: number, item: Item): void copyWithin(target: number, start: number, end?: number): Array fill(value: Item, start?: number, end?: number): Array pop(): Item | undefined @@ -105,7 +155,7 @@ export interface ArrayState extends State> { reverse(): Array shift(): Item | undefined sort(comparator?: (a: Item, b: Item) => number): Array - splice(start: int, deleteCount: int | undefined, ...items: Item[]): Array + splice(start: number, deleteCount: number | undefined, ...items: Item[]): Array unshift(...items: Item[]): number } @@ -139,16 +189,7 @@ export interface StateContext { namedState(name: string, create: () => Value, global?: boolean, equivalent?: Equivalent, tracker?: ValueTracker): MutableState stateBy(name: string, global?: boolean): MutableState | undefined valueBy(name: string, global?: boolean): Value - /** @internal */ - scope( - id: KoalaCallsiteKey, - paramCount?: int32, - create?: () => IncrementalNode, - compute?: () => Value, - cleanup?: (value: Value | undefined) => void, - once?: boolean, - reuseKey?: string - ): InternalScope + scope(id: KoalaCallsiteKey, paramCount: int32): IncrementalScope /** @internal */ scopeEx( id: KoalaCallsiteKey, @@ -158,8 +199,26 @@ export interface StateContext { cleanup?: (value: Value | undefined) => void, once?: boolean, reuseKey?: string - ): InternalScope + ): IncrementalScope controlledScope(id: KoalaCallsiteKey, invalidate: () => void): ControlledScope + /** + * Improve: can be removed after migration to multi-threaded implementation. + * @deprecated + * @internal + */ + fork(builder: (manager: StateContext) => void, complete: () => void): StateContext + /** + * Improve: can be removed after migration to multi-threaded implementation. + * @deprecated + * @internal + */ + merge(main: StateContext, rootNode: ComputableState, compute: () => void): void + /** + * Improve: can be removed after migration to multi-threaded implementation. + * @deprecated + * @internal + */ + terminate(rootScope: ComputableState): void } /** @@ -181,7 +240,7 @@ export interface ValueTracker { } /** @internal */ -export interface InternalScope { +export interface IncrementalScope { /** @returns true if internal value can be returned as is */ readonly unchanged: boolean /** @returns internal value if it is already computed */ @@ -189,8 +248,9 @@ export interface InternalScope { /** @returns internal value updated after the computation */ recache(newValue?: Value): Value /** @returns internal state for parameter */ - param(index: int32, value: V, equivalent?: Equivalent, name?: string, contextLocal?: boolean): State + param(index: int32, value: V): State paramEx(index: int32, value: V, equivalent?: Equivalent, name?: string, contextLocal?: boolean): State + forceCompleteRerender(): void } /** @@ -207,7 +267,7 @@ export interface ControlledScope { // IMPLEMENTATION DETAILS: DO NOT USE IT DIRECTLY -interface ManagedState extends Disposable { +interface ManagedState extends Disposable, StateEx { /** * `true` - global state is added to the manager and is valid until it is disposed, * `false` - local state is added to the current scope and is valid as long as this scope is valid. @@ -217,8 +277,7 @@ interface ManagedState extends Disposable { updateStateSnapshot(changes?: Changes): void } -interface ManagedScope extends Disposable, Dependency, ReadonlyTreeNode { - hasDependencies(): boolean +interface ManagedScope extends Disposable, StateEx, Dependency, ReadonlyTreeNode { readonly id: KoalaCallsiteKey readonly node: IncrementalNode | undefined readonly nodeRef: IncrementalNode | undefined @@ -245,11 +304,13 @@ interface ManagedScope extends Disposable, Dependency, ReadonlyTreeNode { ): ScopeImpl increment(count: uint32, skip: boolean): void invalidateRecursively(predicate?: (scope: ManagedScope) => boolean): void + invalidate(): void + getCascadeParent(): ManagedScope | undefined } -export class StateImpl implements Observable, ManagedState, MutableState { +export /* Improve:HQ private as public*/ class StateImpl implements Observable, ManagedState, MutableState { protected manager: StateManagerImpl | undefined = undefined - public dependencies: StateToScopes | undefined = undefined + public /* Improve:HQ private as public*/ dependencies: StateToScopes | undefined = undefined protected snapshot: Value protected myModified: boolean = false protected myUpdated: boolean = true @@ -275,6 +336,7 @@ export class StateImpl implements Observable, ManagedState, MutableState< this.manager = manager this.dependencies = new StateToScopes() this.snapshot = initial + this.trackedScopes.setTrackedProperties(trackableProperties(initial)) ObservableHandler.attach(initial, this) manager.addCreatedState(this) } @@ -291,27 +353,29 @@ export class StateImpl implements Observable, ManagedState, MutableState< get value(): Value { this.onAccess() const manager = this.manager - return manager === undefined || manager.frozen ? this.snapshot : this.current(manager.journal) + if (!manager || manager.frozen) return this.snapshot + if (manager.current?.nodeRef) return this.snapshot + return this.current(manager.journal) } set value(value: Value) { - let actualValue = value this.checkSetProhibited() const tracker = this.tracker - if (tracker) actualValue = tracker.onUpdate(value) + if (tracker) value = tracker.onUpdate(value) + this.trackedScopes.setTrackedProperties(trackableProperties(value)) const manager = this.manager if (manager) { manager.updateNeeded = true - manager.journal.addChange(this, actualValue) + manager.journal.addChange(this, value) this.myUpdated = false } else { - this.applyStateSnapshot(actualValue) + this.applyStateSnapshot(value) } } onAccess(propertyName?: string): void { const dependency = this.manager?.dependency - if (dependency && propertyName) { + if (propertyName) { this.trackedScopes.register(propertyName, dependency?.states) } this.dependencies?.register(dependency) @@ -395,6 +459,10 @@ export class StateImpl implements Observable, ManagedState, MutableState< this.trackedScopes.clear() } + hasDependencies(): boolean { + return this.dependencies?.empty == false + } + toString(): string { let str = this.global ? "GlobalState" : "LocalState" if (this.name !== undefined) str += "(" + this.name + ")" @@ -408,7 +476,7 @@ export class StateImpl implements Observable, ManagedState, MutableState< class ArrayStateImpl extends StateImpl> implements ArrayState { constructor(manager: StateManagerImpl, initial: Array, global: boolean, equivalent?: Equivalent) { super(manager, initial, global, (oldArray: Array, newArray: Array): boolean => { - let i: int = oldArray.length + let i = float64toInt32(oldArray.length) if (i != newArray.length) return false while (0 < i--) { if (isModified(oldArray[i], newArray[i], equivalent)) return false @@ -423,25 +491,25 @@ class ArrayStateImpl extends StateImpl> implements ArrayState< this.myModified = modified } - get length(): int { + get length(): number { return this.value.length } - set length(value: int) { - this.mutable.length = value + set length(value: number) { + this.mutable.length = float64toInt32(value) } - at(index: int): Item { + at(index: number): Item { const array = this.value - return array[index < 0 ? array.length + index : index] + return array[float64toInt32(index < 0 ? array.length + index : index)] } - get(index: int): Item { - return this.value[index] + get(index: number): Item { + return this.value[float64toInt32(index)] } - set(index: int, item: Item): void { - this.mutable[index] = item + set(index: number, item: Item): void { + this.mutable[float64toInt32(index)] = item } copyWithin(target: number, start: number, end?: number): Array { @@ -472,9 +540,9 @@ class ArrayStateImpl extends StateImpl> implements ArrayState< return this.mutable.sort(comparator) } - splice(start: int, deleteCount: int | undefined, ...items: Item[]): Array { + splice(start: number, deleteCount: number | undefined, ...items: Item[]): Array { const array = this.mutable - return array.splice(start, deleteCount ?? array.length, ...items) + return array.splice(float64toInt32(start), deleteCount != undefined ? float64toInt32(deleteCount) : array.length, ...items) } unshift(...items: Item[]): number { @@ -495,7 +563,7 @@ class ArrayStateImpl extends StateImpl> implements ArrayState< } } -class ParameterImpl implements MutableState { +class ParameterImpl implements StateEx, MutableState { private manager: StateManagerImpl | undefined = undefined private dependencies: StateToScopes | undefined = undefined private name: string | undefined = undefined @@ -531,8 +599,8 @@ class ParameterImpl implements MutableState { update(value: Value, equivalent?: Equivalent): void { const isModified = ObservableHandler.dropModified(this._value) if (!refEqual(this._value, value)) { - this._value = value this._modified = isModified || (equivalent?.(this._value, value) != true) + this._value = value } else { this._modified = isModified } @@ -551,6 +619,10 @@ class ParameterImpl implements MutableState { this.dependencies = this.dependencies?.clear() } + hasDependencies(): boolean { + return this.dependencies?.empty == false + } + toString(): string { let str = "Parameter" if (this.name !== undefined) str += "(" + this.name + ")" @@ -560,7 +632,7 @@ class ParameterImpl implements MutableState { } } -export class StateManagerImpl implements StateManager { +export /* Improve:HQ private as public*/ class StateManagerImpl implements StateManager { private stateCreating: string | undefined = undefined private readonly statesNamed: Map = new Map() private readonly statesCreated: Set = new Set() @@ -575,6 +647,8 @@ export class StateManagerImpl implements StateManager { contextData: object | undefined = undefined isDebugMode: boolean = false private threadCheckerCallback?: () => boolean + private childManager: Array = new Array() + private parentManager: StateManagerImpl | undefined = undefined get currentScopeId(): KoalaCallsiteKey | undefined { return this.current?.id @@ -602,6 +676,9 @@ export class StateManagerImpl implements StateManager { } syncChanges(): void { + this.childManager.forEach((manager: StateManagerImpl) => { + manager.syncChanges() + }) this.journal.setMarker() } @@ -610,6 +687,9 @@ export class StateManagerImpl implements StateManager { } updateSnapshot(): uint32 { + this.childManager.forEach((manager: StateManagerImpl) => { + manager.updateSnapshot() + }) RuntimeProfiler.instance?.updateSnapshotEnter() this.checkForStateComputing() // optimization: all states are valid and not modified @@ -617,8 +697,7 @@ export class StateManagerImpl implements StateManager { let modified: uint32 = 0 // try to update snapshot for every state, except for parameter states const changes = this.journal.getChanges() - // Improve: use compat here (toInt cast) - const created = float64ToInt(this.statesCreated.size) // amount of created states to update + const created = float64toInt32(this.statesCreated.size) // amount of created states to update if (created > 0) { const it = this.statesCreated.keys() while (true) { @@ -647,7 +726,7 @@ export class StateManagerImpl implements StateManager { updatableNode(node: Node, update: (context: StateContext) => void, cleanup?: () => void): ComputableState { this.checkForStateComputing() - const scope = ScopeImpl.create(KoalaCallsiteKeys.empty, 0, (): Node => { + const scope = ScopeImpl.create(0, 0, (): Node => { update(this) return node }, cleanup === undefined ? undefined : (value: Node | undefined): void => { @@ -657,15 +736,17 @@ export class StateManagerImpl implements StateManager { scope.node = node scope.nodeRef = node scope.dependencies = new StateToScopes() + this.current = scope // to attach named states to this scope scope.setNamedState(CONTEXT_ROOT_SCOPE, new StateImpl>(this, scope, false)) scope.setNamedState(CONTEXT_ROOT_NODE, new StateImpl(this, node, false)) + this.current = undefined return scope } computableState(compute: (context: StateContext) => Value, cleanup?: (context: StateContext, value: Value | undefined) => void): ComputableState { if (this.current?.once == false) throw new Error("computable state created in memo-context without remember") this.checkForStateCreating() - const scope = ScopeImpl.create(KoalaCallsiteKeys.empty, 0, (): Value => compute(this), cleanup === undefined ? undefined : (value: Value | undefined): void => { + const scope = ScopeImpl.create(0, 0, (): Value => compute(this), cleanup === undefined ? undefined : (value: Value | undefined): void => { cleanup?.(this, value) }) scope.manager = this @@ -721,18 +802,18 @@ export class StateManagerImpl implements StateManager { return this.external } - scope(id: KoalaCallsiteKey, paramCount?: int32, create?: () => IncrementalNode, compute?: () => Value, cleanup?: (value: Value | undefined) => void, once?: boolean, reuseKey?: string): InternalScope { - return this.scopeEx(id, paramCount ?? 0, create, compute, cleanup, once, reuseKey); + scope(id: KoalaCallsiteKey, paramCount: int32): IncrementalScope { + return this.scopeEx(id, paramCount) } - scopeEx(id: KoalaCallsiteKey, paramCount: int32, create?: () => IncrementalNode, compute?: () => Value, cleanup?: (value: Value | undefined) => void, once?: boolean, reuseKey?: string): InternalScope { + scopeEx(id: KoalaCallsiteKey, paramCount: int32, create?: () => IncrementalNode, compute?: () => Value, cleanup?: (value: Value | undefined) => void, once?: boolean, reuseKey?: string): IncrementalScope { const counters = RuntimeProfiler.instance if (counters) { create ? counters.build() : counters.compute() } const scope = this.current if (scope) return scope.getChildScope(id, paramCount, create, compute, cleanup, once, reuseKey) - throw new Error("prohibited to create scope(" + KoalaCallsiteKeys.asString(id) + ") for the top level") + throw new Error("prohibited to create scope(" + id.toString(16) + ") for the top level") } controlledScope(id: KoalaCallsiteKey, invalidate: () => void): ControlledScope { @@ -792,7 +873,7 @@ export class StateManagerImpl implements StateManager { if (state) return state.value const scope = this.current throw new Error(scope - ? ("state(" + name + ") is not defined in scope(" + KoalaCallsiteKeys.asString(scope.id) + ")") + ? ("state(" + name + ") is not defined in scope(" + scope.id.toString(16) + ")") : ("global state(" + name + ") is not defined")) } @@ -826,14 +907,14 @@ export class StateManagerImpl implements StateManager { if (name === undefined) return const scope = this.current throw new Error(scope - ? ("prohibited when creating state(" + name + ") in scope(" + KoalaCallsiteKeys.asString(scope.id) + ")") + ? ("prohibited when creating state(" + name + ") in scope(" + scope.id.toString(16) + ")") : ("prohibited when creating global state(" + name + ")")) } private checkForStateComputing(): void { this.checkForStateCreating() const scope = this.current - if (scope) throw new Error("prohibited when computing scope(" + KoalaCallsiteKeys.asString(scope.id) + ")") + if (scope) throw new Error("prohibited when computing scope(" + scope.id.toString(16) + ")") } setThreadChecker(callback: () => boolean): void { @@ -848,9 +929,72 @@ export class StateManagerImpl implements StateManager { } } } + + addChild(child: StateManagerImpl) { + this.childManager.push(child) + } + + removeChild(child: StateManagerImpl) { + this.childManager = this.childManager.filter(item => item !== child) + } + + fork(builder: (manager: StateContext) => void, complete: () => void): StateContext { + let context = new StateManagerImpl(); + context.parentManager = this + context.contextData = this.contextData + const task = () => { + RuntimeProfiler.tracer?.begin("parallel task") + const old = StateManagerLocal.get() + StateManagerLocal.set(context) + builder(context); + StateManagerLocal.set(old) + context.current = undefined + if (complete) { + complete(); + } + RuntimeProfiler.tracer?.end("parallel task") + return undefined + } + launchJob(task).then(() => { }).catch((err: Error) => { + console.error('parallel run in taskpool error :', err); + console.error(err.stack); + }) + return context; + } + + merge(main: StateContext, rootScope: ComputableState, compute: () => void): void { + const mainContext = main as StateManagerImpl + RuntimeProfiler.tracer?.begin("merge") + mainContext.childManager.push(this) + const current = rootScope as ScopeImpl + const scope = main!.scopeEx(0, 1, () => { + return current.nodeRef! + }) as ScopeImpl + compute() + if (scope.unchanged) { + scope.cached + RuntimeProfiler.tracer?.end("merge") + return + } + current.cascadeParent = scope + scope.recache() + RuntimeProfiler.tracer?.end("merge") + } + + terminate(rootScope: ComputableState): void { + RuntimeProfiler.tracer?.begin("sub manager terminate") + const root = rootScope as ScopeImpl + const cascadeScope = root.cascadeParent as ScopeImpl + cascadeScope.node = undefined + cascadeScope.nodeRef = undefined + root.dispose(); + this.parentManager?.removeChild(this); + this.parentManager = undefined; + RuntimeProfiler.tracer?.end("sub manager terminate") + } } -class ScopeImpl implements ManagedScope, InternalScope, ComputableState { +class ScopeImpl implements ManagedScope, IncrementalScope, ComputableState { recomputeNeeded: boolean = true manager: StateManagerImpl | undefined = undefined dependencies: StateToScopes | undefined = undefined @@ -879,6 +1023,7 @@ class ScopeImpl implements ManagedScope, InternalScope, Computable private _nodeRef: IncrementalNode | undefined = undefined private _reuseKey?: string /** need to store on Scope because not obtainable in every @method recache */ nodeCount: uint32 = 0 + cascadeParent: ManagedScope | undefined = undefined // Constructor with (compute?: () => Value, cleanup?: (value: Value | undefined) => void) // signature causes es2panda recheck crash, so I have introduced a create @@ -900,10 +1045,6 @@ class ScopeImpl implements ManagedScope, InternalScope, Computable return instance } - hasDependencies(): boolean { - return this.dependencies?.empty == false - } - get id(): KoalaCallsiteKey { return this._id } @@ -932,6 +1073,14 @@ class ScopeImpl implements ManagedScope, InternalScope, Computable return this.parentScope } + get firstChild(): ManagedScope | undefined { + return this.child + } + + get nextSibling(): ManagedScope | undefined { + return this.next + } + get reuseKey(): string | undefined { return this._reuseKey } @@ -973,7 +1122,7 @@ class ScopeImpl implements ManagedScope, InternalScope, Computable getChildScope(id: KoalaCallsiteKey, paramCount: int32, create?: () => IncrementalNode, compute?: () => Value, cleanup?: (value: Value | undefined) => void, once?: boolean, reuseKey?: string): ScopeImpl { const manager = this.manager - if (manager === undefined) throw new Error("prohibited to create scope(" + KoalaCallsiteKeys.asString(id) + ") within the disposed scope(" + KoalaCallsiteKeys.asString(this.id) + ")") + if (manager === undefined) throw new Error("prohibited to create scope(" + id.toString(16) + ") within the disposed scope(" + this.id.toString(16) + ")") manager.checkForStateCreating() const inc = this.incremental const next = inc ? inc.next : this.child @@ -984,7 +1133,7 @@ class ScopeImpl implements ManagedScope, InternalScope, Computable return child as ScopeImpl } } - if (once != true && this.once) throw new Error("prohibited to create scope(" + KoalaCallsiteKeys.asString(id) + ") within the remember scope(" + KoalaCallsiteKeys.asString(this.id) + ")") + if (once != true && this.once) throw new Error("prohibited to create scope(" + id.toString(16) + ") within the remember scope(" + this.id.toString(16) + ")") let reused = reuseKey ? this.nodeRef?.reuse(reuseKey, id) : undefined const scope = reused ? reused as ScopeImpl : ScopeImpl.create(id, paramCount, compute, cleanup, reuseKey) scope.manager = manager @@ -1092,8 +1241,8 @@ class ScopeImpl implements ManagedScope, InternalScope, Computable return this.myValue as Value } - param(index: int32, value: V, equivalent?: Equivalent, name?: string, contextLocal?: boolean): State { - return this.paramEx(index, value, equivalent, name, contextLocal) + param(index: int32, value: V): State { + return this.paramEx(index, value) } paramEx(index: int32, value: V, equivalent?: Equivalent, name?: string, contextLocal?: boolean): State { @@ -1120,11 +1269,14 @@ class ScopeImpl implements ManagedScope, InternalScope, Computable return this.myModified } - private invalidate() { + invalidate() { const current = this.manager?.current // parameters can update snapshot during recomposition let scope: ManagedScope = this while (true) { - if (scope === current) break // parameters should not invalidate whole hierarchy + if (scope === current) { + scope.getCascadeParent()?.invalidate(); + break // parameters should not invalidate whole hierarchy + } if (!scope.recomputeNeeded) RuntimeProfiler.instance?.invalidation() else if (current === undefined) break // all parent scopes were already invalidated scope.recomputeNeeded = true @@ -1135,6 +1287,7 @@ class ScopeImpl implements ManagedScope, InternalScope, Computable // if (this.myRecomputeNeeded && !parent.myRecomputeNeeded) console.log("parent of invalid scope is valid unexpectedly") scope = parent } else { + scope.getCascadeParent()?.invalidate(); // mark top-level computable state as dirty if it has dependencies. // they will be recomputed during the snapshot updating. // we do not recompute other computable states and updatable nodes. @@ -1146,6 +1299,10 @@ class ScopeImpl implements ManagedScope, InternalScope, Computable } } + getCascadeParent(): ManagedScope | undefined { + return this.cascadeParent + } + private recycleOrDispose(child: ManagedScope): void { const key = child.reuseKey const node = this._nodeRef @@ -1178,10 +1335,12 @@ class ScopeImpl implements ManagedScope, InternalScope, Computable } catch (cause) { error = cause as Error } - this.node?.dispose() // dispose parent before its children + // dispose parent after its children to allow recycling a child tree + if (this.node) this.node!.disposing = true for (let child = this.child; child; child = child!.next) { this.recycleOrDispose(child!!) } + this.node?.dispose() this.child = undefined this.parentScope = undefined this.node = undefined @@ -1202,8 +1361,12 @@ class ScopeImpl implements ManagedScope, InternalScope, Computable if (error) throw error } + hasDependencies(): boolean { + return this.dependencies?.empty == false + } + toString(): string { - let str: string = KoalaCallsiteKeys.asString(this.id) + let str: string = this.id.toString(16) if (this.once) str += " remember..." if (this.node) str += " " + className(this.node) if (this === this.manager?.current) str += " (*)" @@ -1211,11 +1374,7 @@ class ScopeImpl implements ManagedScope, InternalScope, Computable } toHierarchy(): string { - let str = "" - for (let node = this.parent; node; node = node!.parent) str += " " - str += this.toString() - for (let node = this.child; node; node = node!.next) str += "\n" + node!.toHierarchy() - return str + return toNodeHierarchy(this) } } @@ -1276,6 +1435,16 @@ class TrackedScope { class TrackedScopes { private trackedScopes = new Map() + private trackableProperties: TrackableProperties | undefined + + /** + * Set the class tracked properties + * @param trackedProperties + */ + setTrackedProperties(trackedProperties: TrackableProperties | undefined): void { + this.trackedScopes.clear() + this.trackableProperties = trackedProperties + } /** * Registers or updates a tracking scope for a property. @@ -1284,17 +1453,21 @@ class TrackedScopes { * Pass undefined to remove tracking. */ register(propertyName: string, dependency: ScopeToStates | undefined): void { + if (!this.trackableProperties?.isTrackable(propertyName)) { + return + } + if (!dependency) { - this.trackedScopes.delete(propertyName); - return; + this.trackedScopes.delete(propertyName) + return } - const existing = this.trackedScopes.get(propertyName); + const existing = this.trackedScopes.get(propertyName) if (existing && existing.dependency === dependency) { - return; + return } - this.trackedScopes.set(propertyName, new TrackedScope(dependency)); + this.trackedScopes.set(propertyName, new TrackedScope(dependency)) } /** @@ -1327,9 +1500,11 @@ class TrackedScopes { } /** - * Clears all tracked scopes + * Clears all modified scopes */ clear() { - this.trackedScopes.clear() + this.trackedScopes.forEach(it => { + it.isModified = false + }) } -} +} \ No newline at end of file diff --git a/frameworks/bridge/arkts_frontend/koala_projects/incremental/runtime/src/tree/IncrementalNode.ts b/frameworks/bridge/arkts_frontend/koala_projects/incremental/runtime/src/tree/IncrementalNode.ts index 198c7073b04..76dc80df3a1 100644 --- a/frameworks/bridge/arkts_frontend/koala_projects/incremental/runtime/src/tree/IncrementalNode.ts +++ b/frameworks/bridge/arkts_frontend/koala_projects/incremental/runtime/src/tree/IncrementalNode.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2024 Huawei Device Co., Ltd. + * Copyright (c) 2022-2025 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 @@ -15,13 +15,14 @@ import { className, KoalaCallsiteKey, uint32 } from "@koalaui/common" import { Disposable } from "../states/Disposable" -import { ReadonlyTreeNode } from "./ReadonlyTreeNode" +import { ReadonlyTreeNode, toNodeHierarchy } from "./ReadonlyTreeNode" /** * This is a node implementation for a tree, which is represented as a pair of bidirectional lists. * It allows nodes to be added or removed incrementally using the state manager. */ export class IncrementalNode implements Disposable, ReadonlyTreeNode { + private _disposing = false private _disposed = false private _child: IncrementalNode | undefined = undefined private _prev: IncrementalNode | undefined = undefined @@ -84,6 +85,20 @@ export class IncrementalNode implements Disposable, ReadonlyTreeNode { return false } + /** + * @returns `true` if this node is removing from the hierarchy + */ + get disposing(): boolean { + return this._disposing + } + + /** + * This method is called to mark this node as being removed + */ + set disposing(value: boolean) { + this._disposing = value + } + /** * @returns `true` if this node should no longer be used */ @@ -143,11 +158,7 @@ export class IncrementalNode implements Disposable, ReadonlyTreeNode { * @returns text representation of a tree hierarchy starting from this node */ toHierarchy(): string { - let str = "" - for (let node = this._parent; node; node = node!._parent) str += " " - str += this.toString() - for (let node = this._child; node; node = node!._next) str += "\n" + node!.toHierarchy() - return str + return toNodeHierarchy(this) } /** @@ -212,11 +223,7 @@ export class IncrementalNode implements Disposable, ReadonlyTreeNode { this._parent = parent if (next) next._prev = this if (prev) prev._next = this - else parent._child = this; - - // TODO: this is to workaround ast dumper bug #24055 - if (0) {} else {} - + else parent._child = this parent.onChildInserted?.(this) } } diff --git a/frameworks/bridge/arkts_frontend/koala_projects/incremental/runtime/src/tree/ReadonlyTreeNode.ts b/frameworks/bridge/arkts_frontend/koala_projects/incremental/runtime/src/tree/ReadonlyTreeNode.ts index 00e4e7a7d9c..ae052fdf7b3 100644 --- a/frameworks/bridge/arkts_frontend/koala_projects/incremental/runtime/src/tree/ReadonlyTreeNode.ts +++ b/frameworks/bridge/arkts_frontend/koala_projects/incremental/runtime/src/tree/ReadonlyTreeNode.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2023 Huawei Device Co., Ltd. + * Copyright (c) 2022-2025 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 @@ -13,13 +13,62 @@ * limitations under the License. */ +import { int32 } from "@koalaui/common" + export interface ReadonlyTreeNode { /** * @returns a parent tree node if it is exist */ - readonly parent: ReadonlyTreeNode|undefined + readonly parent: ReadonlyTreeNode | undefined + /** + * @returns the first child node contained in this node if it is exist + */ + readonly firstChild: ReadonlyTreeNode | undefined + /** + * @returns the next sibling of this node if it is exist + */ + readonly nextSibling: ReadonlyTreeNode | undefined + /** + * @returns text representation of the node used to generate a tree hierarchy + */ + toString(): string /** * @returns text representation of a tree hierarchy starting from the tree node */ toHierarchy(): string } + +export function toNodeHierarchy(node: ReadonlyTreeNode, withParents: boolean = true, withChildren: boolean = true): string { + const array = new Array() + visitHierarchy((node: ReadonlyTreeNode, depth: int32) => { + array.push(" ".repeat(depth) + node.toString()) + }, node, withParents, withChildren) + return array.join("\n") +} + +type NodeAcceptor = (node: ReadonlyTreeNode, depth: int32) => void + +function visitHierarchy(accept: NodeAcceptor, node: ReadonlyTreeNode, withParents: boolean, withChildren: boolean) { + const depth = withParents ? visitParents(accept, node) : 0 + accept(node, depth) + if (withChildren) { + visitChildren(accept, node, depth + 1, true) + } +} + +function visitParents(accept: NodeAcceptor, node: ReadonlyTreeNode): int32 { + let parent = node.parent + if (!parent) return 0 + const depth = visitParents(accept, parent) + accept(parent, depth) + return depth + 1 +} + +function visitChildren(accept: NodeAcceptor, node: ReadonlyTreeNode, depth: int32, recursively: boolean) { + for (let child = node.firstChild; child; child = child!.nextSibling) { + accept(child!, depth) + if (recursively) { + visitChildren(accept, child!, depth + 1, recursively) + } + } +} diff --git a/frameworks/bridge/arkts_frontend/koala_projects/incremental/runtime/src/tree/TreeNode.ts b/frameworks/bridge/arkts_frontend/koala_projects/incremental/runtime/src/tree/TreeNode.ts index 16bc265bebf..3248278f9ee 100644 --- a/frameworks/bridge/arkts_frontend/koala_projects/incremental/runtime/src/tree/TreeNode.ts +++ b/frameworks/bridge/arkts_frontend/koala_projects/incremental/runtime/src/tree/TreeNode.ts @@ -13,10 +13,10 @@ * limitations under the License. */ -import { className, float64, float64ToInt, int32, uint32 } from "@koalaui/common" +import { className, float64, float64toInt32, int32, uint32 } from "@koalaui/common" import { RuntimeProfiler } from "../common/RuntimeProfiler" import { Disposable } from "../states/Disposable" -import { ReadonlyTreeNode } from "./ReadonlyTreeNode" +import { ReadonlyTreeNode, toNodeHierarchy } from "./ReadonlyTreeNode" /** * A tree node used to build an incremental runtime and a component tree. @@ -59,6 +59,20 @@ export class TreeNode implements Disposable, ReadonlyTreeNode { return this.myParent } + /** + * Returns the first child node contained in this node if it is exist. + */ + get firstChild(): TreeNode | undefined { + return this.childAt(0) + } + + /** + * @returns the next sibling of this node if it is exist + */ + get nextSibling(): TreeNode | undefined { + return this.parent?.childAt(this.index + 1) + } + /** * Returns the depth of this node relative to the root node. * The root node returns 0. @@ -77,7 +91,7 @@ export class TreeNode implements Disposable, ReadonlyTreeNode { * Returns the number of children of this node. */ get childrenCount(): uint32 { - return float64ToInt(this.myChildren.length) + return float64toInt32(this.myChildren.length) } /** @@ -133,25 +147,22 @@ export class TreeNode implements Disposable, ReadonlyTreeNode { /** * Performs the specified action for each child node. */ - forEach(action: (node: TreeNode, index: float64) => void): void { - // must be int32, but ArkTS array.forEach requires index to be float64 - this.myChildren.forEach((n, i) => action(n, i)) + forEach(action: (node: TreeNode, index: int32) => void): void { + this.myChildren.forEach(action) } /** * Determines whether all child nodes satisfy the specified predicate. */ - every(predicate: (node: TreeNode, index: float64) => boolean): boolean { - // must be int32, but ArkTS array.every requires index to be float64 - return this.myChildren.every((n, i) => predicate(n, i)) + every(predicate: (node: TreeNode, index: int32) => boolean): boolean { + return this.myChildren.every(predicate) } /** * Determines whether any child node satisfies the specified predicate. */ - some(predicate: (node: TreeNode, index: float64) => boolean): boolean { - // must be int32, but ArkTS array.some requires index to be float64 - return this.myChildren.some((n, i) => predicate(n, i)) + some(predicate: (node: TreeNode, index: int32) => boolean): boolean { + return this.myChildren.some(predicate) } /** @@ -224,7 +235,7 @@ export class TreeNode implements Disposable, ReadonlyTreeNode { */ removeChild(node: TreeNode): boolean { if (node.myParent !== this) return false // not in hierarchy - const index: int32 = this.myIndicesValid ? node.index : float64ToInt(this.myChildren.indexOf(node)) + const index: int32 = this.myIndicesValid ? node.index : float64toInt32(this.myChildren.indexOf(node)) return undefined !== this.removeChildAt(index) } @@ -273,33 +284,6 @@ export class TreeNode implements Disposable, ReadonlyTreeNode { } toHierarchy(): string { - return this.collectNodes().map((node: TreeNode) => " ".repeat(node.depth) + node.toString()).join("\n") - } - - collectNodes(): Array { - const array = new Array() - this.collectParentsTo(array) - array.push(this) - this.collectChildrenTo(array, true) - return array - } - - collectParentsTo(array: Array): void { - const index = float64ToInt(array.length) - let parent = this.myParent - while (parent !== undefined) { - array.splice(index, 0, parent!) - parent = parent!.myParent - } - } - - collectChildrenTo(array: Array, deep: boolean = false): void { - const children = this.myChildren - const length = children.length - for (let i = 0; i < length; i++) { - const current = children[i] - array.push(current) - if (deep) current.collectChildrenTo(array, deep) - } + return toNodeHierarchy(this) } } -- Gitee