Skip to content

Commit

Permalink
refactor(react): stop polluting lynx with lynxWorkletJsImpl(1/2)
Browse files Browse the repository at this point in the history
  • Loading branch information
Yradex committed Mar 5, 2025
1 parent 0ff7f50 commit 8c97b1b
Show file tree
Hide file tree
Showing 13 changed files with 88 additions and 66 deletions.
2 changes: 2 additions & 0 deletions .changeset/rotten-poets-fetch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
---
---
5 changes: 5 additions & 0 deletions packages/react/runtime/__test__/worklet/impl.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// LICENSE file in the root directory of this source tree.
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';

import { clearConfigCacheForTesting } from '../../src/worklet/functionality';
import { destroyWorklet, lynxWorkletJsImpl, removeJsWorklets, runJSFunction } from '../../src/worklet/jsImpl';

beforeEach(() => {
Expand Down Expand Up @@ -32,23 +33,27 @@ describe('WorkletImpl', () => {
});

it('should not listen when not enableRunOnBackground', () => {
clearConfigCacheForTesting();
SystemInfo.lynxSdkVersion = '2.15';
expect(lynxWorkletJsImpl()).toEqual(expect.any(Object));
expect(lynx.getCoreContext().addEventListener).toHaveBeenCalledTimes(0);
expect(lynxWorkletJsImpl()).toEqual(expect.any(Object));
expect(lynx.getCoreContext().addEventListener).toHaveBeenCalledTimes(0);
destroyWorklet();
expect(lynx.getCoreContext().removeEventListener).toHaveBeenCalledTimes(0);
clearConfigCacheForTesting();
});

it('should not listen when not enableRunOnBackground 2', () => {
clearConfigCacheForTesting();
SystemInfo.lynxSdkVersion = undefined;
expect(lynxWorkletJsImpl()).toEqual(expect.any(Object));
expect(lynx.getCoreContext().addEventListener).toHaveBeenCalledTimes(0);
expect(lynxWorkletJsImpl()).toEqual(expect.any(Object));
expect(lynx.getCoreContext().addEventListener).toHaveBeenCalledTimes(0);
destroyWorklet();
expect(lynx.getCoreContext().removeEventListener).toHaveBeenCalledTimes(0);
clearConfigCacheForTesting();
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// LICENSE file in the root directory of this source tree.
import { afterEach, beforeEach, describe, expect, it } from 'vitest';

import { clearConfigCacheForTesting } from '../../src/worklet/functionality';
import { destroyWorklet } from '../../src/worklet/jsImpl';
import { runOnBackground } from '../../src/worklet/runOnBackground';
import { globalEnvManager } from '../utils/envManager';
Expand Down Expand Up @@ -46,6 +47,7 @@ describe('runOnBackground', () => {
});

it('should throw when native capabilities not fulfilled', () => {
clearConfigCacheForTesting();
globalEnvManager.switchToMainThread();
SystemInfo.lynxSdkVersion = '2.15';
const worklet = {
Expand All @@ -54,6 +56,7 @@ describe('runOnBackground', () => {
expect(() => {
runOnBackground(worklet)(1, ['args']);
}).toThrowError('runOnBackground requires Lynx sdk version 2.16.');
clearConfigCacheForTesting();
});

it('should throw when _error exists', () => {
Expand Down
14 changes: 0 additions & 14 deletions packages/react/runtime/__test__/worklet/transform.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,6 @@ describe('WorkletJsFnTransform', () => {
expect(result._jsFnId).toEqual(2);
});

it('should not transform js fn when native capabilities not available', () => {
const getCoreContext = lynx.getCoreContext;
lynx.getCoreContext = undefined;

const fn = vi.fn();
let result = transformToWorklet(fn);
expect(result._jsFnId).toEqual(0);

result = transformToWorklet(fn);
expect(result._jsFnId).toEqual(0);

lynx.getCoreContext = getCoreContext;
});

it('should raise error when argument is not a function', () => {
const x = transformToWorklet(1);
expect(x._error).toMatch('Argument of runOnBackground should be a function, but got [number] instead');
Expand Down
19 changes: 11 additions & 8 deletions packages/react/runtime/__test__/worklet/workletRef.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,16 @@
// Licensed under the Apache License Version 2.0 that can be found in the
// LICENSE file in the root directory of this source tree.
*/
import { render } from 'preact';
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest';
import { destroyWorklet } from '../../src/worklet/jsImpl';
import { globalEnvManager } from '../utils/envManager';
import { MainThreadRef, useMainThreadRef } from '../../src/worklet/workletRef';

import { injectUpdatePatch, replaceCommitHook } from '../../src/lifecycle/patchUpdate';
import { __root } from '../../src/root';
import { render } from 'preact';
import { setupPage } from '../../src/snapshot';
import { injectUpdatePatch, replaceCommitHook } from '../../src/lifecycle/patchUpdate';
import { clearConfigCacheForTesting } from '../../src/worklet/functionality';
import { destroyWorklet } from '../../src/worklet/jsImpl';
import { MainThreadRef, useMainThreadRef } from '../../src/worklet/workletRef';
import { globalEnvManager } from '../utils/envManager';

beforeAll(() => {
setupPage(__CreatePage('0', 0));
Expand All @@ -21,6 +23,7 @@ beforeAll(() => {
beforeEach(() => {
globalEnvManager.resetEnv();
SystemInfo.lynxSdkVersion = '999.999';
clearConfigCacheForTesting();
});

afterEach(() => {
Expand Down Expand Up @@ -50,7 +53,7 @@ describe('WorkletRef in js', () => {
it('to json', () => {
globalEnvManager.switchToBackground();
const ref = new MainThreadRef(1);
expect(JSON.stringify(ref)).toMatchInlineSnapshot(`"{"_wvid":1}"`);
expect(JSON.stringify(ref)).toMatchInlineSnapshot(`"{"_wvid":2}"`);
});

it('should send init value to the main thread', () => {
Expand Down Expand Up @@ -84,7 +87,7 @@ describe('WorkletRef in js', () => {
[
"rLynxChange",
{
"data": "{"workletRefInitValuePatch":[[1,233]]}",
"data": "{"workletRefInitValuePatch":[[3,233]]}",
"patchOptions": {
"commitTaskId": 1,
"reloadVersion": 0,
Expand Down Expand Up @@ -154,7 +157,7 @@ describe('WorkletRef in js', () => {
});

it('should not send init value to the main thread when native capabilities not fulfilled', () => {
lynx.getCoreContext = undefined;
SystemInfo.lynxSdkVersion = '2.13';
const Comp = () => {
const ref = useMainThreadRef(233);
return <view></view>;
Expand Down
27 changes: 25 additions & 2 deletions packages/react/runtime/src/worklet/functionality.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,32 @@

import { isSdkVersionGt } from '../utils.js';

let mtsEnabled: boolean | undefined;
let runOnBackgroundEnabled: boolean | undefined;

/**
* @internal
*/
export function enableRunOnBackground(): boolean {
return isSdkVersionGt(2, 15);
function isMtsEnabled(): boolean {
if (mtsEnabled === undefined) {
mtsEnabled = isSdkVersionGt(2, 13);
}
return mtsEnabled;
}

/**
* @internal
*/
function isRunOnBackgroundEnabled(): boolean {
if (runOnBackgroundEnabled === undefined) {
runOnBackgroundEnabled = isSdkVersionGt(2, 15);
}
return runOnBackgroundEnabled;
}

function clearConfigCacheForTesting(): void {
mtsEnabled = undefined;
runOnBackgroundEnabled = undefined;
}

export { isMtsEnabled, isRunOnBackgroundEnabled, clearConfigCacheForTesting };
32 changes: 14 additions & 18 deletions packages/react/runtime/src/worklet/jsImpl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,16 @@
import { WorkletEvents } from '@lynx-js/react/worklet-runtime/bindings';

import { WorkletExecIdMap } from './execMap.js';
import { enableRunOnBackground } from './functionality.js';
import { isRunOnBackgroundEnabled } from './functionality.js';
import type { RunOnBackgroundData } from './runOnBackground.js';
import { takeWorkletRefInitValuePatch } from './workletRefPool.js';

interface LynxWorkletJsImpl {
_workletExecIdMap?: WorkletExecIdMap;
_workletJsFnLastId: number;
_workletRefLastId: number;
_workletRefInitValueSet: Set<number>;
_workletRefInitValuePatch: [number, unknown][];
}

let impl: LynxWorkletJsImpl | undefined;

/**
* @internal
*/
Expand Down Expand Up @@ -50,15 +49,10 @@ function initWorklet(): boolean {
return false;
}

lynx.lynxWorkletJsImpl = {
_workletJsFnLastId: 0,
_workletRefLastId: 0,
_workletRefInitValueSet: new Set<number>(),
_workletRefInitValuePatch: [],
};
impl = {};

if (enableRunOnBackground()) {
lynx.lynxWorkletJsImpl._workletExecIdMap = new WorkletExecIdMap();
if (isRunOnBackgroundEnabled()) {
impl._workletExecIdMap = new WorkletExecIdMap();
lynx.getCoreContext().addEventListener(WorkletEvents.runOnBackground, runJSFunction);
lynx.getCoreContext().addEventListener(WorkletEvents.releaseBackgroundWorkletCtx, removeJsWorklets);
}
Expand All @@ -67,21 +61,23 @@ function initWorklet(): boolean {
}

export function destroyWorklet(): void {
if (!lynx.lynxWorkletJsImpl) {
takeWorkletRefInitValuePatch();

if (!impl) {
return;
}

lynx.lynxWorkletJsImpl = undefined;
impl = undefined;

if (enableRunOnBackground()) {
if (isRunOnBackgroundEnabled()) {
lynx.getCoreContext?.().removeEventListener(WorkletEvents.runOnBackground, runJSFunction);
lynx.getCoreContext?.().removeEventListener(WorkletEvents.releaseBackgroundWorkletCtx, removeJsWorklets);
}
}

export function lynxWorkletJsImpl(shouldInit: boolean = true): LynxWorkletJsImpl | undefined {
if (lynx.lynxWorkletJsImpl || (shouldInit && initWorklet())) {
return lynx.lynxWorkletJsImpl;
if (impl || (shouldInit && initWorklet())) {
return impl;
}
return undefined;
}
4 changes: 2 additions & 2 deletions packages/react/runtime/src/worklet/runOnBackground.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import type { JsFnHandle } from '@lynx-js/react/worklet-runtime/bindings';
import { WorkletEvents } from '@lynx-js/react/worklet-runtime/bindings';

import { enableRunOnBackground } from './functionality.js';
import { isRunOnBackgroundEnabled } from './functionality.js';

/**
* @internal
Expand All @@ -22,7 +22,7 @@ interface RunOnBackgroundData {
* @public
*/
function runOnBackground<Fn extends (...args: any[]) => any>(f: Fn): (...args: Parameters<Fn>) => void {
if (!enableRunOnBackground()) {
if (!isRunOnBackgroundEnabled()) {
throw new Error('runOnBackground requires Lynx sdk version 2.16.');
}
if (__JS__) {
Expand Down
6 changes: 3 additions & 3 deletions packages/react/runtime/src/worklet/transformToWorklet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@
// LICENSE file in the root directory of this source tree.
import type { JsFnHandle } from '@lynx-js/react/worklet-runtime/bindings';

import { lynxWorkletJsImpl } from './jsImpl.js';
let lastId = 0;

/**
* transform args of `runOnBackground()`.
*
* @internal
*/
export function transformToWorklet(obj: (...args: any[]) => any): JsFnHandle {
const impl = lynxWorkletJsImpl();
const id = impl ? ++impl._workletJsFnLastId : 0;
const id = ++lastId;
if (typeof obj !== 'function') {
// We save the error message in the object, so that we can throw it later when the function is called on the main thread.
return {
Expand Down
6 changes: 3 additions & 3 deletions packages/react/runtime/src/worklet/workletRef.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@ import type { RefObject } from 'react';
import type { WorkletRefImpl } from '@lynx-js/react/worklet-runtime/bindings';
import { WorkletEvents } from '@lynx-js/react/worklet-runtime/bindings';

import { lynxWorkletJsImpl } from './jsImpl.js';
import { addWorkletRefInitValue } from './workletRefPool.js';
import { useMemo } from '../hooks/react.js';

let lastId = 0;

abstract class WorkletRef<T> {
/**
* @internal
Expand All @@ -33,8 +34,7 @@ abstract class WorkletRef<T> {
*/
protected constructor(initValue: T, type: string) {
if (__JS__) {
const impl = lynxWorkletJsImpl();
this._id = impl ? ++impl._workletRefLastId : 0;
this._id = ++lastId;
this._initValue = initValue;
this._type = type;
addWorkletRefInitValue(this._id, initValue);
Expand Down
28 changes: 16 additions & 12 deletions packages/react/runtime/src/worklet/workletRefPool.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,29 @@
// Copyright 2024 The Lynx Authors. All rights reserved.
// Licensed under the Apache License Version 2.0 that can be found in the
// LICENSE file in the root directory of this source tree.
import { lynxWorkletJsImpl } from './jsImpl.js';

import { isMtsEnabled } from './functionality.js';

let initValuePatch: [number, unknown][] = [];
const initValueIdSet = new Set<number>();

/**
* @internal
*/
export function addWorkletRefInitValue(id: number, value: unknown): void {
const impl = lynxWorkletJsImpl();
if (!impl) {
if (!isMtsEnabled()) {
return;
}

impl._workletRefInitValueSet.add(id);
impl._workletRefInitValuePatch.push([id, value]);
initValueIdSet.add(id);
initValuePatch.push([id, value]);
}

/**
* @internal
*/
export function takeWorkletRefInitValuePatch(): [number, unknown][] {
const impl = lynxWorkletJsImpl(false);
if (!impl) {
return [];
}

const res = impl._workletRefInitValuePatch;
impl._workletRefInitValuePatch = [];
const res = initValuePatch;
initValuePatch = [];
return res;
}
4 changes: 2 additions & 2 deletions packages/react/worklet-runtime/src/jsFunctionLifecycle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@ class JsFunctionLifecycleManager {
}
}

function enableRunOnBackground(): boolean {
function isRunOnBackgroundEnabled(): boolean {
return isSdkVersionGt(2, 15);
}

export { JsFunctionLifecycleManager, enableRunOnBackground };
export { JsFunctionLifecycleManager, isRunOnBackgroundEnabled };
4 changes: 2 additions & 2 deletions packages/react/worklet-runtime/src/workletRuntime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import { Element } from './api/element.js';
import type { ClosureValueType, Worklet, WorkletRefImpl } from './bindings/types.js';
import { delayExecUntilJsReady, initEventDelay } from './delayWorkletEvent.js';
import { enableRunOnBackground, JsFunctionLifecycleManager } from './jsFunctionLifecycle.js';
import { isRunOnBackgroundEnabled, JsFunctionLifecycleManager } from './jsFunctionLifecycle.js';
import { profile } from './utils/profile.js';
import { getFromWorkletRefMap, initWorkletRef } from './workletRef.js';

Expand All @@ -15,7 +15,7 @@ function initWorklet(): void {
_refImpl: initWorkletRef(),
};

if (enableRunOnBackground()) {
if (isRunOnBackgroundEnabled()) {
globalThis.lynxWorkletImpl._jsFunctionLifecycleManager = new JsFunctionLifecycleManager();
}

Expand Down

0 comments on commit 8c97b1b

Please sign in to comment.