-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
## Summary This pull request features running classes on the Worklet runtime. Classes aren't supported by Hermes and require polyfilling. - Class instances can be created in worklets. - Class instances cannot be sent between runtimes - afaik it's impossible to create host objects with a prototype. - Static class methods aren't supported - a feature for the future perhaps. Current implementation is a bit naive. 1. We create worklets from all required polyfills. 1. We create __the whole class__ (not the class instance!) in the worklet. 1. We create the instance of this ad-hoc made class. We could could put the class in the global scope, however it doesn't seem exactly necessary at the moment. You can obtain a worklet class explicitly by adding a ClassProperty `__workletClass` to it ```ts // Explicit WorkletClass class Clazz { __workletClass = true; } ``` or implicitly by placing it in a file with a worklet directive. ```ts 'worklet'; // Implicit WorkletClass class Clazz {} ``` ## Test plan - [x] Add unit tests - [x] Add runtime tests suite
- Loading branch information
Showing
20 changed files
with
2,897 additions
and
221 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
138 changes: 138 additions & 0 deletions
138
apps/common-app/src/examples/RuntimeTests/tests/plugin/workletClasses.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,138 @@ | ||
import React, { useEffect } from 'react'; | ||
import { View } from 'react-native'; | ||
import { useSharedValue, runOnUI } from 'react-native-reanimated'; | ||
import { render, wait, describe, getRegisteredValue, registerValue, test, expect } from '../../ReJest/RuntimeTestsApi'; | ||
|
||
const SHARED_VALUE_REF = 'SHARED_VALUE_REF'; | ||
|
||
class WorkletClass { | ||
__workletClass = true; | ||
value = 0; | ||
getOne() { | ||
return 1; | ||
} | ||
|
||
getTwo() { | ||
return this.getOne() + 1; | ||
} | ||
|
||
getIncremented() { | ||
return ++this.value; | ||
} | ||
} | ||
|
||
describe('Test worklet classes', () => { | ||
test('class works on React runtime', async () => { | ||
const ExampleComponent = () => { | ||
const output = useSharedValue<number | null>(null); | ||
registerValue(SHARED_VALUE_REF, output); | ||
const clazz = new WorkletClass(); | ||
|
||
output.value = clazz.getTwo() + clazz.getIncremented() + clazz.getIncremented(); | ||
|
||
return <View />; | ||
}; | ||
await render(<ExampleComponent />); | ||
await wait(100); | ||
const sharedValue = await getRegisteredValue(SHARED_VALUE_REF); | ||
expect(sharedValue.onJS).toBe(5); | ||
}); | ||
|
||
test('constructor works on Worklet runtime', async () => { | ||
const ExampleComponent = () => { | ||
useEffect(() => { | ||
runOnUI(() => { | ||
const clazz = new WorkletClass(); | ||
clazz.getOne(); | ||
})(); | ||
}); | ||
|
||
return <View />; | ||
}; | ||
await render(<ExampleComponent />); | ||
await wait(100); | ||
// TODO: assert no crash here | ||
}); | ||
|
||
test('class methods work on Worklet runtime', async () => { | ||
const ExampleComponent = () => { | ||
const output = useSharedValue<number | null>(null); | ||
registerValue(SHARED_VALUE_REF, output); | ||
|
||
useEffect(() => { | ||
runOnUI(() => { | ||
const clazz = new WorkletClass(); | ||
output.value = clazz.getOne(); | ||
})(); | ||
}); | ||
|
||
return <View />; | ||
}; | ||
await render(<ExampleComponent />); | ||
await wait(100); | ||
const sharedValue = await getRegisteredValue(SHARED_VALUE_REF); | ||
expect(sharedValue.onUI).toBe(1); | ||
}); | ||
|
||
test('class instance methods preserve binding', async () => { | ||
const ExampleComponent = () => { | ||
const output = useSharedValue<number | null>(null); | ||
registerValue(SHARED_VALUE_REF, output); | ||
|
||
useEffect(() => { | ||
runOnUI(() => { | ||
const clazz = new WorkletClass(); | ||
output.value = clazz.getTwo(); | ||
})(); | ||
}); | ||
|
||
return <View />; | ||
}; | ||
await render(<ExampleComponent />); | ||
await wait(100); | ||
const sharedValue = await getRegisteredValue(SHARED_VALUE_REF); | ||
expect(sharedValue.onUI).toBe(2); | ||
}); | ||
|
||
test('class instances preserve state', async () => { | ||
const ExampleComponent = () => { | ||
const output = useSharedValue<number | null>(null); | ||
registerValue(SHARED_VALUE_REF, output); | ||
|
||
useEffect(() => { | ||
runOnUI(() => { | ||
const clazz = new WorkletClass(); | ||
output.value = clazz.getIncremented() + clazz.getIncremented(); | ||
})(); | ||
}); | ||
|
||
return <View />; | ||
}; | ||
await render(<ExampleComponent />); | ||
await wait(100); | ||
const sharedValue = await getRegisteredValue(SHARED_VALUE_REF); | ||
expect(sharedValue.onUI).toBe(3); | ||
}); | ||
|
||
test('instanceof operator works on Worklet runtime', async () => { | ||
const ExampleComponent = () => { | ||
const output = useSharedValue<boolean | null>(null); | ||
registerValue(SHARED_VALUE_REF, output); | ||
|
||
useEffect(() => { | ||
runOnUI(() => { | ||
const clazz = new WorkletClass(); | ||
output.value = clazz instanceof WorkletClass; | ||
})(); | ||
}); | ||
|
||
return <View />; | ||
}; | ||
await render(<ExampleComponent />); | ||
await wait(100); | ||
const sharedValue = await getRegisteredValue(SHARED_VALUE_REF); | ||
expect(sharedValue.onUI).toBe(true); | ||
}); | ||
|
||
// TODO: Add a test that throws when class is sent from React to Worklet runtime. | ||
}); |
Oops, something went wrong.